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