123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517 |
- # 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_gcc_version_at_least,
- check_inline,
- check_restrict,
- check_compiler_gcc)
- 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) from e
- # 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
- if self.compiler is None:
- raise CompileError('%s compiler is not set' % (lang,))
- try:
- ret = mth(*((self,)+args))
- except (DistutilsExecError, CompileError) as e:
- self.compiler = save_compiler
- raise CompileError from e
- 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_gcc(self):
- """Return True if the C compiler is gcc"""
- return check_compiler_gcc(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 check_gcc_version_at_least(self, major, minor=0, patchlevel=0):
- """Return True if the GCC version is greater than or equal to the
- specified version."""
- return check_gcc_version_at_least(self, major, minor, patchlevel)
- 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
|