# Added Fortran compiler support to config. Currently useful only for # try_compile call. try_run works but is untested for most of Fortran # compilers (they must define linker_exe first). # Pearu Peterson import os import signal import subprocess import sys import textwrap import warnings from distutils.command.config import config as old_config from distutils.command.config import LANG_EXT from distutils import log from distutils.file_util import copy_file from distutils.ccompiler import CompileError, LinkError import distutils from numpy.distutils.exec_command import filepath_from_subprocess_output from numpy.distutils.mingw32ccompiler import generate_manifest from numpy.distutils.command.autodist import (check_gcc_function_attribute, check_gcc_function_attribute_with_intrinsics, check_gcc_variable_attribute, check_inline, check_restrict, check_compiler_gcc4) LANG_EXT['f77'] = '.f' LANG_EXT['f90'] = '.f90' class config(old_config): old_config.user_options += [ ('fcompiler=', None, "specify the Fortran compiler type"), ] def initialize_options(self): self.fcompiler = None old_config.initialize_options(self) def _check_compiler (self): old_config._check_compiler(self) from numpy.distutils.fcompiler import FCompiler, new_fcompiler if sys.platform == 'win32' and (self.compiler.compiler_type in ('msvc', 'intelw', 'intelemw')): # XXX: hack to circumvent a python 2.6 bug with msvc9compiler: # initialize call query_vcvarsall, which throws an IOError, and # causes an error along the way without much information. We try to # catch it here, hoping it is early enough, and print an helpful # message instead of Error: None. if not self.compiler.initialized: try: self.compiler.initialize() except IOError as e: msg = textwrap.dedent("""\ Could not initialize compiler instance: do you have Visual Studio installed? If you are trying to build with MinGW, please use "python setup.py build -c mingw32" instead. If you have Visual Studio installed, check it is correctly installed, and the right version (VS 2008 for python 2.6, 2.7 and 3.2, VS 2010 for >= 3.3). Original exception was: %s, and the Compiler class was %s ============================================================================""") \ % (e, self.compiler.__class__.__name__) print(textwrap.dedent("""\ ============================================================================""")) raise distutils.errors.DistutilsPlatformError(msg) # After MSVC is initialized, add an explicit /MANIFEST to linker # flags. See issues gh-4245 and gh-4101 for details. Also # relevant are issues 4431 and 16296 on the Python bug tracker. from distutils import msvc9compiler if msvc9compiler.get_build_version() >= 10: for ldflags in [self.compiler.ldflags_shared, self.compiler.ldflags_shared_debug]: if '/MANIFEST' not in ldflags: ldflags.append('/MANIFEST') if not isinstance(self.fcompiler, FCompiler): self.fcompiler = new_fcompiler(compiler=self.fcompiler, dry_run=self.dry_run, force=1, c_compiler=self.compiler) if self.fcompiler is not None: self.fcompiler.customize(self.distribution) if self.fcompiler.get_version(): self.fcompiler.customize_cmd(self) self.fcompiler.show_customization() def _wrap_method(self, mth, lang, args): from distutils.ccompiler import CompileError from distutils.errors import DistutilsExecError save_compiler = self.compiler if lang in ['f77', 'f90']: self.compiler = self.fcompiler try: ret = mth(*((self,)+args)) except (DistutilsExecError, CompileError) as e: str(e) self.compiler = save_compiler raise CompileError self.compiler = save_compiler return ret def _compile (self, body, headers, include_dirs, lang): src, obj = self._wrap_method(old_config._compile, lang, (body, headers, include_dirs, lang)) # _compile in unixcompiler.py sometimes creates .d dependency files. # Clean them up. self.temp_files.append(obj + '.d') return src, obj def _link (self, body, headers, include_dirs, libraries, library_dirs, lang): if self.compiler.compiler_type=='msvc': libraries = (libraries or [])[:] library_dirs = (library_dirs or [])[:] if lang in ['f77', 'f90']: lang = 'c' # always use system linker when using MSVC compiler if self.fcompiler: for d in self.fcompiler.library_dirs or []: # correct path when compiling in Cygwin but with # normal Win Python if d.startswith('/usr/lib'): try: d = subprocess.check_output(['cygpath', '-w', d]) except (OSError, subprocess.CalledProcessError): pass else: d = filepath_from_subprocess_output(d) library_dirs.append(d) for libname in self.fcompiler.libraries or []: if libname not in libraries: libraries.append(libname) for libname in libraries: if libname.startswith('msvc'): continue fileexists = False for libdir in library_dirs or []: libfile = os.path.join(libdir, '%s.lib' % (libname)) if os.path.isfile(libfile): fileexists = True break if fileexists: continue # make g77-compiled static libs available to MSVC fileexists = False for libdir in library_dirs: libfile = os.path.join(libdir, 'lib%s.a' % (libname)) if os.path.isfile(libfile): # copy libname.a file to name.lib so that MSVC linker # can find it libfile2 = os.path.join(libdir, '%s.lib' % (libname)) copy_file(libfile, libfile2) self.temp_files.append(libfile2) fileexists = True break if fileexists: continue log.warn('could not find library %r in directories %s' \ % (libname, library_dirs)) elif self.compiler.compiler_type == 'mingw32': generate_manifest(self) return self._wrap_method(old_config._link, lang, (body, headers, include_dirs, libraries, library_dirs, lang)) def check_header(self, header, include_dirs=None, library_dirs=None, lang='c'): self._check_compiler() return self.try_compile( "/* we need a dummy line to make distutils happy */", [header], include_dirs) def check_decl(self, symbol, headers=None, include_dirs=None): self._check_compiler() body = textwrap.dedent(""" int main(void) { #ifndef %s (void) %s; #endif ; return 0; }""") % (symbol, symbol) return self.try_compile(body, headers, include_dirs) def check_macro_true(self, symbol, headers=None, include_dirs=None): self._check_compiler() body = textwrap.dedent(""" int main(void) { #if %s #else #error false or undefined macro #endif ; return 0; }""") % (symbol,) return self.try_compile(body, headers, include_dirs) def check_type(self, type_name, headers=None, include_dirs=None, library_dirs=None): """Check type availability. Return True if the type can be compiled, False otherwise""" self._check_compiler() # First check the type can be compiled body = textwrap.dedent(r""" int main(void) { if ((%(name)s *) 0) return 0; if (sizeof (%(name)s)) return 0; } """) % {'name': type_name} st = False try: try: self._compile(body % {'type': type_name}, headers, include_dirs, 'c') st = True except distutils.errors.CompileError: st = False finally: self._clean() return st def check_type_size(self, type_name, headers=None, include_dirs=None, library_dirs=None, expected=None): """Check size of a given type.""" self._check_compiler() # First check the type can be compiled body = textwrap.dedent(r""" typedef %(type)s npy_check_sizeof_type; int main (void) { static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) >= 0)]; test_array [0] = 0 ; return 0; } """) self._compile(body % {'type': type_name}, headers, include_dirs, 'c') self._clean() if expected: body = textwrap.dedent(r""" typedef %(type)s npy_check_sizeof_type; int main (void) { static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) == %(size)s)]; test_array [0] = 0 ; return 0; } """) for size in expected: try: self._compile(body % {'type': type_name, 'size': size}, headers, include_dirs, 'c') self._clean() return size except CompileError: pass # this fails to *compile* if size > sizeof(type) body = textwrap.dedent(r""" typedef %(type)s npy_check_sizeof_type; int main (void) { static int test_array [1 - 2 * !(((long) (sizeof (npy_check_sizeof_type))) <= %(size)s)]; test_array [0] = 0 ; return 0; } """) # The principle is simple: we first find low and high bounds of size # for the type, where low/high are looked up on a log scale. Then, we # do a binary search to find the exact size between low and high low = 0 mid = 0 while True: try: self._compile(body % {'type': type_name, 'size': mid}, headers, include_dirs, 'c') self._clean() break except CompileError: #log.info("failure to test for bound %d" % mid) low = mid + 1 mid = 2 * mid + 1 high = mid # Binary search: while low != high: mid = (high - low) // 2 + low try: self._compile(body % {'type': type_name, 'size': mid}, headers, include_dirs, 'c') self._clean() high = mid except CompileError: low = mid + 1 return low def check_func(self, func, headers=None, include_dirs=None, libraries=None, library_dirs=None, decl=False, call=False, call_args=None): # clean up distutils's config a bit: add void to main(), and # return a value. self._check_compiler() body = [] if decl: if type(decl) == str: body.append(decl) else: body.append("int %s (void);" % func) # Handle MSVC intrinsics: force MS compiler to make a function call. # Useful to test for some functions when built with optimization on, to # avoid build error because the intrinsic and our 'fake' test # declaration do not match. body.append("#ifdef _MSC_VER") body.append("#pragma function(%s)" % func) body.append("#endif") body.append("int main (void) {") if call: if call_args is None: call_args = '' body.append(" %s(%s);" % (func, call_args)) else: body.append(" %s;" % func) body.append(" return 0;") body.append("}") body = '\n'.join(body) + "\n" return self.try_link(body, headers, include_dirs, libraries, library_dirs) def check_funcs_once(self, funcs, headers=None, include_dirs=None, libraries=None, library_dirs=None, decl=False, call=False, call_args=None): """Check a list of functions at once. This is useful to speed up things, since all the functions in the funcs list will be put in one compilation unit. Arguments --------- funcs : seq list of functions to test include_dirs : seq list of header paths libraries : seq list of libraries to link the code snippet to library_dirs : seq list of library paths decl : dict for every (key, value), the declaration in the value will be used for function in key. If a function is not in the dictionary, no declaration will be used. call : dict for every item (f, value), if the value is True, a call will be done to the function f. """ self._check_compiler() body = [] if decl: for f, v in decl.items(): if v: body.append("int %s (void);" % f) # Handle MS intrinsics. See check_func for more info. body.append("#ifdef _MSC_VER") for func in funcs: body.append("#pragma function(%s)" % func) body.append("#endif") body.append("int main (void) {") if call: for f in funcs: if f in call and call[f]: if not (call_args and f in call_args and call_args[f]): args = '' else: args = call_args[f] body.append(" %s(%s);" % (f, args)) else: body.append(" %s;" % f) else: for f in funcs: body.append(" %s;" % f) body.append(" return 0;") body.append("}") body = '\n'.join(body) + "\n" return self.try_link(body, headers, include_dirs, libraries, library_dirs) def check_inline(self): """Return the inline keyword recognized by the compiler, empty string otherwise.""" return check_inline(self) def check_restrict(self): """Return the restrict keyword recognized by the compiler, empty string otherwise.""" return check_restrict(self) def check_compiler_gcc4(self): """Return True if the C compiler is gcc >= 4.""" return check_compiler_gcc4(self) def check_gcc_function_attribute(self, attribute, name): return check_gcc_function_attribute(self, attribute, name) def check_gcc_function_attribute_with_intrinsics(self, attribute, name, code, include): return check_gcc_function_attribute_with_intrinsics(self, attribute, name, code, include) def check_gcc_variable_attribute(self, attribute): return check_gcc_variable_attribute(self, attribute) def get_output(self, body, headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c", use_tee=None): """Try to compile, link to an executable, and run a program built from 'body' and 'headers'. Returns the exit status code of the program and its output. """ # 2008-11-16, RemoveMe warnings.warn("\n+++++++++++++++++++++++++++++++++++++++++++++++++\n" "Usage of get_output is deprecated: please do not \n" "use it anymore, and avoid configuration checks \n" "involving running executable on the target machine.\n" "+++++++++++++++++++++++++++++++++++++++++++++++++\n", DeprecationWarning, stacklevel=2) self._check_compiler() exitcode, output = 255, '' try: grabber = GrabStdout() try: src, obj, exe = self._link(body, headers, include_dirs, libraries, library_dirs, lang) grabber.restore() except Exception: output = grabber.data grabber.restore() raise exe = os.path.join('.', exe) try: # specify cwd arg for consistency with # historic usage pattern of exec_command() # also, note that exe appears to be a string, # which exec_command() handled, but we now # use a list for check_output() -- this assumes # that exe is always a single command output = subprocess.check_output([exe], cwd='.') except subprocess.CalledProcessError as exc: exitstatus = exc.returncode output = '' except OSError: # preserve the EnvironmentError exit status # used historically in exec_command() exitstatus = 127 output = '' else: output = filepath_from_subprocess_output(output) if hasattr(os, 'WEXITSTATUS'): exitcode = os.WEXITSTATUS(exitstatus) if os.WIFSIGNALED(exitstatus): sig = os.WTERMSIG(exitstatus) log.error('subprocess exited with signal %d' % (sig,)) if sig == signal.SIGINT: # control-C raise KeyboardInterrupt else: exitcode = exitstatus log.info("success!") except (CompileError, LinkError): log.info("failure.") self._clean() return exitcode, output class GrabStdout: def __init__(self): self.sys_stdout = sys.stdout self.data = '' sys.stdout = self def write (self, data): self.sys_stdout.write(data) self.data += data def flush (self): self.sys_stdout.flush() def restore(self): sys.stdout = self.sys_stdout