diff options
-rw-r--r-- | numpy/distutils/command/build_clib.py | 78 | ||||
-rw-r--r-- | numpy/distutils/command/build_ext.py | 120 | ||||
-rw-r--r-- | numpy/distutils/fcompiler/__init__.py | 1 | ||||
-rw-r--r-- | numpy/distutils/fcompiler/gnu.py | 209 | ||||
-rw-r--r-- | numpy/distutils/misc_util.py | 21 | ||||
-rw-r--r-- | numpy/distutils/system_info.py | 65 |
6 files changed, 360 insertions, 134 deletions
diff --git a/numpy/distutils/command/build_clib.py b/numpy/distutils/command/build_clib.py index 1c868cf6c..594c3f67f 100644 --- a/numpy/distutils/command/build_clib.py +++ b/numpy/distutils/command/build_clib.py @@ -7,21 +7,22 @@ from glob import glob import shutil from distutils.command.build_clib import build_clib as old_build_clib from distutils.errors import DistutilsSetupError, DistutilsError, \ - DistutilsFileError + DistutilsFileError from numpy.distutils import log from distutils.dep_util import newer_group from numpy.distutils.misc_util import filter_sources, has_f_sources,\ - has_cxx_sources, all_strings, get_lib_source_files, is_sequence, \ - get_numpy_include_dirs + has_cxx_sources, all_strings, get_lib_source_files, is_sequence, \ + get_numpy_include_dirs, get_library_names # Fix Python distutils bug sf #1718574: _l = old_build_clib.user_options for _i in range(len(_l)): if _l[_i][0] in ['build-clib', 'build-temp']: - _l[_i] = (_l[_i][0]+'=',)+_l[_i][1:] + _l[_i] = (_l[_i][0] + '=',) + _l[_i][1:] # + class build_clib(old_build_clib): description = "build C/C++/F libraries used by Python extensions" @@ -32,7 +33,7 @@ class build_clib(old_build_clib): ('inplace', 'i', 'Build in-place'), ('parallel=', 'j', "number of parallel jobs"), - ] + ] boolean_options = old_build_clib.boolean_options + ['inplace'] @@ -75,7 +76,8 @@ class build_clib(old_build_clib): for (lib_name, build_info) in self.libraries: l = build_info.get('language', None) - if l and l not in languages: languages.append(l) + if l and l not in languages: + languages.append(l) from distutils.ccompiler import new_compiler self.compiler = new_compiler(compiler=self.compiler, @@ -94,11 +96,11 @@ class build_clib(old_build_clib): if self.have_f_sources(): from numpy.distutils.fcompiler import new_fcompiler self._f_compiler = new_fcompiler(compiler=self.fcompiler, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force, - requiref90='f90' in languages, - c_compiler=self.compiler) + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force, + requiref90='f90' in languages, + c_compiler=self.compiler) if self._f_compiler is not None: self._f_compiler.customize(self.distribution) @@ -114,10 +116,10 @@ class build_clib(old_build_clib): self.build_libraries(self.libraries) if self.inplace: - for l in self.distribution.installed_libraries: + for l in self.distribution.installed_libraries: libname = self.compiler.library_filename(l.name) source = os.path.join(self.build_clib, libname) - target = os.path.join(l.target_dir, libname) + target = os.path.join(l.target_dir, libname) self.mkpath(l.target_dir) shutil.copy(source, target) @@ -129,6 +131,11 @@ class build_clib(old_build_clib): return filenames def build_libraries(self, libraries): + library_names = get_library_names() + library_order = {v: k for k, v in enumerate(library_names)} + libraries = sorted( + libraries, key=lambda library: library_order[library[0]]) + for (lib_name, build_info) in libraries: self.build_a_library(build_info, lib_name, libraries) @@ -140,21 +147,25 @@ class build_clib(old_build_clib): sources = build_info.get('sources') if sources is None or not is_sequence(sources): raise DistutilsSetupError(("in 'libraries' option (library '%s'), " + - "'sources' must be present and must be " + - "a list of source filenames") % lib_name) + "'sources' must be present and must be " + + "a list of source filenames") % lib_name) sources = list(sources) c_sources, cxx_sources, f_sources, fmodule_sources \ - = filter_sources(sources) + = filter_sources(sources) requiref90 = not not fmodule_sources or \ - build_info.get('language', 'c')=='f90' + build_info.get('language', 'c') == 'f90' # save source type information so that build_ext can use it. source_languages = [] - if c_sources: source_languages.append('c') - if cxx_sources: source_languages.append('c++') - if requiref90: source_languages.append('f90') - elif f_sources: source_languages.append('f77') + if c_sources: + source_languages.append('c') + if cxx_sources: + source_languages.append('c++') + if requiref90: + source_languages.append('f90') + elif f_sources: + source_languages.append('f77') build_info['source_languages'] = source_languages lib_file = compiler.library_filename(lib_name, @@ -168,8 +179,8 @@ class build_clib(old_build_clib): config_fc = build_info.get('config_fc', {}) if fcompiler is not None and config_fc: - log.info('using additional config_fc from setup script '\ - 'for fortran compiler: %s' \ + log.info('using additional config_fc from setup script ' + 'for fortran compiler: %s' % (config_fc,)) from numpy.distutils.fcompiler import new_fcompiler fcompiler = new_fcompiler(compiler=fcompiler.compiler_type, @@ -186,12 +197,14 @@ class build_clib(old_build_clib): # check availability of Fortran compilers if (f_sources or fmodule_sources) and fcompiler is None: - raise DistutilsError("library %s has Fortran sources"\ - " but no Fortran compiler found" % (lib_name)) + raise DistutilsError("library %s has Fortran sources" + " but no Fortran compiler found" % (lib_name)) if fcompiler is not None: - fcompiler.extra_f77_compile_args = build_info.get('extra_f77_compile_args') or [] - fcompiler.extra_f90_compile_args = build_info.get('extra_f90_compile_args') or [] + fcompiler.extra_f77_compile_args = build_info.get( + 'extra_f77_compile_args') or [] + fcompiler.extra_f90_compile_args = build_info.get( + 'extra_f90_compile_args') or [] macros = build_info.get('macros') include_dirs = build_info.get('include_dirs') @@ -203,9 +216,10 @@ class build_clib(old_build_clib): # where compiled F90 module files are: module_dirs = build_info.get('module_dirs') or [] module_build_dir = os.path.dirname(lib_file) - if requiref90: self.mkpath(module_build_dir) + if requiref90: + self.mkpath(module_build_dir) - if compiler.compiler_type=='msvc': + if compiler.compiler_type == 'msvc': # this hack works around the msvc compiler attributes # problem, msvc uses its own convention :( c_sources += cxx_sources @@ -239,7 +253,7 @@ class build_clib(old_build_clib): if requiref90: if fcompiler.module_dir_switch is None: existing_modules = glob('*.mod') - extra_postargs += fcompiler.module_options(\ + extra_postargs += fcompiler.module_options( module_dirs, module_build_dir) if fmodule_sources: @@ -257,14 +271,14 @@ class build_clib(old_build_clib): if f in existing_modules: continue t = os.path.join(module_build_dir, f) - if os.path.abspath(f)==os.path.abspath(t): + if os.path.abspath(f) == os.path.abspath(t): continue if os.path.isfile(t): os.remove(t) try: self.move_file(f, module_build_dir) except DistutilsFileError: - log.warn('failed to move %r to %r' \ + log.warn('failed to move %r to %r' % (f, module_build_dir)) if f_sources: diff --git a/numpy/distutils/command/build_ext.py b/numpy/distutils/command/build_ext.py index cf5bfa04f..cf0747d2f 100644 --- a/numpy/distutils/command/build_ext.py +++ b/numpy/distutils/command/build_ext.py @@ -5,24 +5,26 @@ from __future__ import division, absolute_import, print_function import os import sys +import shutil from glob import glob from distutils.dep_util import newer_group from distutils.command.build_ext import build_ext as old_build_ext from distutils.errors import DistutilsFileError, DistutilsSetupError,\ - DistutilsError + DistutilsError from distutils.file_util import copy_file from numpy.distutils import log from numpy.distutils.exec_command import exec_command -from numpy.distutils.system_info import combine_paths +from numpy.distutils.system_info import combine_paths, system_info from numpy.distutils.misc_util import filter_sources, has_f_sources, \ - has_cxx_sources, get_ext_source_files, \ - get_numpy_include_dirs, is_sequence, get_build_architecture, \ - msvc_version + has_cxx_sources, get_ext_source_files, \ + get_numpy_include_dirs, is_sequence, get_build_architecture, \ + msvc_version from numpy.distutils.command.config_compiler import show_fortran_compilers + class build_ext (old_build_ext): description = "build C/C++/F extensions (compile/link to build directory)" @@ -32,12 +34,12 @@ class build_ext (old_build_ext): "specify the Fortran compiler type"), ('parallel=', 'j', "number of parallel jobs"), - ] + ] help_options = old_build_ext.help_options + [ ('help-fcompiler', None, "list available Fortran compilers", show_fortran_compilers), - ] + ] def initialize_options(self): old_build_ext.initialize_options(self) @@ -80,11 +82,13 @@ class build_ext (old_build_ext): if self.distribution.has_c_libraries(): if self.inplace: if self.distribution.have_run.get('build_clib'): - log.warn('build_clib already run, it is too late to ' \ - 'ensure in-place build of build_clib') - build_clib = self.distribution.get_command_obj('build_clib') + log.warn('build_clib already run, it is too late to ' + 'ensure in-place build of build_clib') + build_clib = self.distribution.get_command_obj( + 'build_clib') else: - build_clib = self.distribution.get_command_obj('build_clib') + build_clib = self.distribution.get_command_obj( + 'build_clib') build_clib.inplace = 1 build_clib.ensure_finalized() build_clib.run() @@ -120,8 +124,8 @@ class build_ext (old_build_ext): if build_clib is not None: for libname, build_info in build_clib.libraries or []: if libname in clibs and clibs[libname] != build_info: - log.warn('library %r defined more than once,'\ - ' overwriting build_info\n%s... \nwith\n%s...' \ + log.warn('library %r defined more than once,' + ' overwriting build_info\n%s... \nwith\n%s...' % (libname, repr(clibs[libname])[:300], repr(build_info)[:300])) clibs[libname] = build_info # .. and distribution libraries: @@ -177,7 +181,7 @@ class build_ext (old_build_ext): elif 'f77' in ext_languages: ext_language = 'f77' else: - ext_language = 'c' # default + ext_language = 'c' # default if l and l != ext_language and ext.language: log.warn('resetting extension %r language from %r to %r.' % (ext.name, l, ext_language)) @@ -192,9 +196,9 @@ class build_ext (old_build_ext): # Initialize C++ compiler: if need_cxx_compiler: self._cxx_compiler = new_compiler(compiler=compiler_type, - verbose=self.verbose, - dry_run=self.dry_run, - force=self.force) + verbose=self.verbose, + dry_run=self.dry_run, + force=self.force) compiler = self._cxx_compiler compiler.customize(self.distribution, need_cxx=need_cxx_compiler) compiler.customize_cmd(self) @@ -234,7 +238,7 @@ class build_ext (old_build_ext): dry_run=self.dry_run, force=self.force, requiref90=True, - c_compiler = self.compiler) + c_compiler=self.compiler) fcompiler = self._f90_compiler if fcompiler: ctype = fcompiler.compiler_type @@ -252,6 +256,18 @@ class build_ext (old_build_ext): # Build extensions self.build_extensions() + shared_libs = system_info.shared_libs + if shared_libs: + runtime_lib_dir = os.path.join( + self.build_lib, self.distribution.get_name(), '_lib') + try: + os.makedirs(runtime_lib_dir) + except OSError: + pass + + for runtime_lib in shared_libs: + if runtime_lib: + copy_file(runtime_lib, runtime_lib_dir) def swig_sources(self, sources): # Do nothing. Swig sources have beed handled in build_src command. @@ -295,11 +311,9 @@ class build_ext (old_build_ext): macros.append((undef,)) c_sources, cxx_sources, f_sources, fmodule_sources = \ - filter_sources(ext.sources) - + filter_sources(ext.sources) - - if self.compiler.compiler_type=='msvc': + if self.compiler.compiler_type == 'msvc': if cxx_sources: # Needed to compile kiva.agg._agg extension. extra_args.append('/Zm1000') @@ -309,32 +323,34 @@ class build_ext (old_build_ext): cxx_sources = [] # Set Fortran/C++ compilers for compilation and linking. - if ext.language=='f90': + if ext.language == 'f90': fcompiler = self._f90_compiler - elif ext.language=='f77': + elif ext.language == 'f77': fcompiler = self._f77_compiler - else: # in case ext.language is c++, for instance + else: # in case ext.language is c++, for instance fcompiler = self._f90_compiler or self._f77_compiler if fcompiler is not None: - fcompiler.extra_f77_compile_args = (ext.extra_f77_compile_args or []) if hasattr(ext, 'extra_f77_compile_args') else [] - fcompiler.extra_f90_compile_args = (ext.extra_f90_compile_args or []) if hasattr(ext, 'extra_f90_compile_args') else [] + fcompiler.extra_f77_compile_args = (ext.extra_f77_compile_args or []) if hasattr( + ext, 'extra_f77_compile_args') else [] + fcompiler.extra_f90_compile_args = (ext.extra_f90_compile_args or []) if hasattr( + ext, 'extra_f90_compile_args') else [] cxx_compiler = self._cxx_compiler # check for the availability of required compilers if cxx_sources and cxx_compiler is None: - raise DistutilsError("extension %r has C++ sources" \ - "but no C++ compiler found" % (ext.name)) + raise DistutilsError("extension %r has C++ sources" + "but no C++ compiler found" % (ext.name)) if (f_sources or fmodule_sources) and fcompiler is None: - raise DistutilsError("extension %r has Fortran sources " \ - "but no Fortran compiler found" % (ext.name)) + raise DistutilsError("extension %r has Fortran sources " + "but no Fortran compiler found" % (ext.name)) if ext.language in ['f77', 'f90'] and fcompiler is None: - self.warn("extension %r has Fortran libraries " \ - "but no Fortran linker found, using default linker" % (ext.name)) - if ext.language=='c++' and cxx_compiler is None: - self.warn("extension %r has C++ libraries " \ - "but no C++ linker found, using default linker" % (ext.name)) + self.warn("extension %r has Fortran libraries " + "but no Fortran linker found, using default linker" % (ext.name)) + if ext.language == 'c++' and cxx_compiler is None: + self.warn("extension %r has C++ libraries " + "but no C++ linker found, using default linker" % (ext.name)) - kws = {'depends':ext.depends} + kws = {'depends': ext.depends} output_dir = self.build_temp include_dirs = ext.include_dirs + get_numpy_include_dirs() @@ -387,7 +403,7 @@ class build_ext (old_build_ext): if f in existing_modules: continue t = os.path.join(module_build_dir, f) - if os.path.abspath(f)==os.path.abspath(t): + if os.path.abspath(f) == os.path.abspath(t): continue if os.path.isfile(t): os.remove(t) @@ -419,11 +435,12 @@ class build_ext (old_build_ext): if self.compiler.compiler_type in ('msvc', 'intelw', 'intelemw'): # expand libraries with fcompiler libraries as we are # not using fcompiler linker - self._libs_with_msvc_and_fortran(fcompiler, libraries, library_dirs) + self._libs_with_msvc_and_fortran( + fcompiler, libraries, library_dirs) elif ext.language in ['f77', 'f90'] and fcompiler is not None: linker = fcompiler.link_shared_object - if ext.language=='c++' and cxx_compiler is not None: + if ext.language == 'c++' and cxx_compiler is not None: linker = cxx_compiler.link_shared_object linker(objects, ext_filename, @@ -440,23 +457,27 @@ class build_ext (old_build_ext): build_src = self.get_finalized_command("build_src").build_src build_clib = self.get_finalized_command("build_clib").build_clib objects = self.compiler.compile([os.path.join(build_src, - "gfortran_vs2003_hack.c")], - output_dir=self.build_temp) - self.compiler.create_static_lib(objects, "_gfortran_workaround", output_dir=build_clib, debug=self.debug) + "gfortran_vs2003_hack.c")], + output_dir=self.build_temp) + self.compiler.create_static_lib( + objects, "_gfortran_workaround", output_dir=build_clib, debug=self.debug) def _libs_with_msvc_and_fortran(self, fcompiler, c_libraries, c_library_dirs): - if fcompiler is None: return + if fcompiler is None: + return for libname in c_libraries: - if libname.startswith('msvc'): continue + if libname.startswith('msvc'): + continue fileexists = False for libdir in c_library_dirs or []: libfile = os.path.join(libdir, '%s.lib' % (libname)) if os.path.isfile(libfile): fileexists = True break - if fileexists: continue + if fileexists: + continue # make g77-compiled static libs available to MSVC fileexists = False for libdir in c_library_dirs: @@ -470,7 +491,8 @@ class build_ext (old_build_ext): c_library_dirs.append(self.build_temp) fileexists = True break - if fileexists: continue + if fileexists: + continue log.warn('could not find library %r in directories %s' % (libname, c_library_dirs)) @@ -498,14 +520,14 @@ class build_ext (old_build_ext): if self.build_temp not in c_library_dirs: c_library_dirs.append(self.build_temp) - def get_source_files (self): + def get_source_files(self): self.check_extensions_list(self.extensions) filenames = [] for ext in self.extensions: filenames.extend(get_ext_source_files(ext)) return filenames - def get_outputs (self): + def get_outputs(self): self.check_extensions_list(self.extensions) outputs = [] diff --git a/numpy/distutils/fcompiler/__init__.py b/numpy/distutils/fcompiler/__init__.py index 86b080e93..9d465e9d8 100644 --- a/numpy/distutils/fcompiler/__init__.py +++ b/numpy/distutils/fcompiler/__init__.py @@ -430,6 +430,7 @@ class FCompiler(CCompiler): raise CompilerNotFound() return version + ############################################################ ## Public methods: diff --git a/numpy/distutils/fcompiler/gnu.py b/numpy/distutils/fcompiler/gnu.py index 4649fd743..0b2c1c6de 100644 --- a/numpy/distutils/fcompiler/gnu.py +++ b/numpy/distutils/fcompiler/gnu.py @@ -6,37 +6,44 @@ import sys import warnings import platform import tempfile +import random +import string from subprocess import Popen, PIPE, STDOUT - +from copy import copy from numpy.distutils.fcompiler import FCompiler from numpy.distutils.exec_command import exec_command from numpy.distutils.misc_util import msvc_runtime_library from numpy.distutils.compat import get_exception +from numpy.distutils.system_info import system_info compilers = ['GnuFCompiler', 'Gnu95FCompiler'] TARGET_R = re.compile(r"Target: ([a-zA-Z0-9_\-]*)") # XXX: handle cross compilation + + def is_win64(): return sys.platform == "win32" and platform.architecture()[0] == "64bit" + if is_win64(): #_EXTRAFLAGS = ["-fno-leading-underscore"] _EXTRAFLAGS = [] else: _EXTRAFLAGS = [] + class GnuFCompiler(FCompiler): compiler_type = 'gnu' - compiler_aliases = ('g77',) + compiler_aliases = ('g77', ) description = 'GNU Fortran 77 compiler' def gnu_version_match(self, version_string): """Handle the different versions of GNU fortran compilers""" # Strip warning(s) that may be emitted by gfortran while version_string.startswith('gfortran: warning'): - version_string = version_string[version_string.find('\n')+1:] + version_string = version_string[version_string.find('\n') + 1:] # Gfortran versions from after 2010 will output a simple string # (usually "x.y", "x.y.z" or "x.y.z-q") for ``-dumpversion``; older @@ -83,15 +90,15 @@ class GnuFCompiler(FCompiler): possible_executables = ['g77', 'f77'] executables = { - 'version_cmd' : [None, "-dumpversion"], - 'compiler_f77' : [None, "-g", "-Wall", "-fno-second-underscore"], - 'compiler_f90' : None, # Use --fcompiler=gnu95 for f90 codes - 'compiler_fix' : None, - 'linker_so' : [None, "-g", "-Wall"], - 'archiver' : ["ar", "-cr"], - 'ranlib' : ["ranlib"], - 'linker_exe' : [None, "-g", "-Wall"] - } + 'version_cmd': [None, "-dumpversion"], + 'compiler_f77': [None, "-g", "-Wall", "-fno-second-underscore"], + 'compiler_f90': None, # Use --fcompiler=gnu95 for f90 codes + 'compiler_fix': None, + 'linker_so': [None, "-g", "-Wall"], + 'archiver': ["ar", "-cr"], + 'ranlib': ["ranlib"], + 'linker_exe': [None, "-g", "-Wall"] + } module_dir_switch = None module_include_switch = None @@ -129,8 +136,8 @@ class GnuFCompiler(FCompiler): try: get_makefile_filename = sc.get_makefile_filename except AttributeError: - pass # i.e. PyPy - else: + pass # i.e. PyPy + else: filename = get_makefile_filename() sc.parse_makefile(filename, g) target = g.get('MACOSX_DEPLOYMENT_TARGET', '10.3') @@ -153,9 +160,8 @@ class GnuFCompiler(FCompiler): return opt def get_libgcc_dir(self): - status, output = exec_command(self.compiler_f77 + - ['-print-libgcc-file-name'], - use_tee=0) + status, output = exec_command( + self.compiler_f77 + ['-print-libgcc-file-name'], use_tee=0) if not status: return os.path.dirname(output) return None @@ -170,7 +176,7 @@ class GnuFCompiler(FCompiler): d = os.path.normpath(d) path = os.path.join(d, "lib%s.a" % self.g2c) if not os.path.exists(path): - root = os.path.join(d, *((os.pardir,)*4)) + root = os.path.join(d, *((os.pardir, ) * 4)) d2 = os.path.abspath(os.path.join(root, 'lib')) path = os.path.join(d2, "lib%s.a" % self.g2c) if os.path.exists(path): @@ -193,13 +199,10 @@ class GnuFCompiler(FCompiler): opt.append(g2c) c_compiler = self.c_compiler if sys.platform == 'win32' and c_compiler and \ - c_compiler.compiler_type == 'msvc': + c_compiler.compiler_type == 'msvc': # the following code is not needed (read: breaks) when using MinGW # in case want to link F77 compiled code with MSVC opt.append('gcc') - runtime_lib = msvc_runtime_library() - if runtime_lib: - opt.append(runtime_lib) if sys.platform == 'darwin': opt.append('cc_dynamic') return opt @@ -241,7 +244,7 @@ class GnuFCompiler(FCompiler): class Gnu95FCompiler(GnuFCompiler): compiler_type = 'gnu95' - compiler_aliases = ('gfortran',) + compiler_aliases = ('gfortran', ) description = 'GNU Fortran 95 compiler' def version_match(self, version_string): @@ -256,25 +259,29 @@ class Gnu95FCompiler(GnuFCompiler): # use -mno-cygwin flag for gfortran when Python is not # Cygwin-Python if sys.platform == 'win32': - for key in ['version_cmd', 'compiler_f77', 'compiler_f90', - 'compiler_fix', 'linker_so', 'linker_exe']: + for key in [ + 'version_cmd', 'compiler_f77', 'compiler_f90', + 'compiler_fix', 'linker_so', 'linker_exe' + ]: self.executables[key].append('-mno-cygwin') return v possible_executables = ['gfortran', 'f95'] executables = { - 'version_cmd' : ["<F90>", "-dumpversion"], - 'compiler_f77' : [None, "-Wall", "-g", "-ffixed-form", - "-fno-second-underscore"] + _EXTRAFLAGS, - 'compiler_f90' : [None, "-Wall", "-g", - "-fno-second-underscore"] + _EXTRAFLAGS, - 'compiler_fix' : [None, "-Wall", "-g","-ffixed-form", - "-fno-second-underscore"] + _EXTRAFLAGS, - 'linker_so' : ["<F90>", "-Wall", "-g"], - 'archiver' : ["ar", "-cr"], - 'ranlib' : ["ranlib"], - 'linker_exe' : [None, "-Wall"] - } + 'version_cmd': ["<F90>", "-dumpversion"], + 'compiler_f77': + [None, "-Wall", "-g", "-ffixed-form", "-fno-second-underscore" + ] + _EXTRAFLAGS, + 'compiler_f90': + [None, "-Wall", "-g", "-fno-second-underscore"] + _EXTRAFLAGS, + 'compiler_fix': + [None, "-Wall", "-g", "-ffixed-form", "-fno-second-underscore" + ] + _EXTRAFLAGS, + 'linker_so': ["<F90>", "-Wall", "-g"], + 'archiver': ["ar", "-cr"], + 'ranlib': ["ranlib"], + 'linker_exe': [None, "-Wall"] + } module_dir_switch = '-J' module_include_switch = '-I' @@ -298,6 +305,8 @@ class Gnu95FCompiler(GnuFCompiler): return arch_flags def get_flags(self): + if self.c_compiler.compiler_type == "msvc" and not is_win64(): + return ['-O0'] flags = GnuFCompiler.get_flags(self) arch_flags = self._universal_flags(self.compiler_f90) if arch_flags: @@ -319,7 +328,7 @@ class Gnu95FCompiler(GnuFCompiler): target = self.get_target() if target: d = os.path.normpath(self.get_libgcc_dir()) - root = os.path.join(d, *((os.pardir,)*4)) + root = os.path.join(d, *((os.pardir, ) * 4)) path = os.path.join(root, "lib") mingwdir = os.path.normpath(path) if os.path.exists(os.path.join(mingwdir, "libmingwex.a")): @@ -335,32 +344,125 @@ class Gnu95FCompiler(GnuFCompiler): if c_compiler and c_compiler.compiler_type == "msvc": if "gcc" in opt: i = opt.index("gcc") - opt.insert(i+1, "mingwex") - opt.insert(i+1, "mingw32") - # XXX: fix this mess, does not work for mingw - if is_win64(): - c_compiler = self.c_compiler - if c_compiler and c_compiler.compiler_type == "msvc": - return [] - else: - pass + opt.insert(i + 1, "mingwex") + opt.insert(i + 1, "mingw32") + c_compiler = self.c_compiler + if c_compiler and c_compiler.compiler_type == "msvc": + return [] + else: + pass return opt def get_target(self): - status, output = exec_command(self.compiler_f77 + - ['-v'], - use_tee=0) + status, output = exec_command(self.compiler_f77 + ['-v'], use_tee=0) if not status: m = TARGET_R.search(output) if m: return m.group(1) return "" - def get_flags_opt(self): + @staticmethod + def _generate_id(size=6, chars=string.ascii_uppercase + string.digits): + return ''.join(random.choice(chars) for _ in range(size)) + + def link_wrapper_lib(self, + objects=[], + libraries=[], + library_dirs=[], + output_dir=None, + debug=False): + """Create a wrapper shared library for the given objects + + Return an MSVC-compatible lib + """ + + c_compiler = self.c_compiler + if c_compiler.compiler_type != "msvc": + raise ValueError("This method only supports MSVC") + if is_win64(): - return ['-O0'] + tag = 'win_amd64' else: - return GnuFCompiler.get_flags_opt(self) + tag = 'win32' + + root_name = self._generate_id() + '.gfortran-' + tag + dll_name = root_name + '.dll' + dll_path = os.path.join(output_dir, dll_name) + def_path = root_name + '.def' + lib_path = os.path.join(output_dir, root_name + '.lib') + + self.link_shared_object( + objects, + dll_name, + output_dir=output_dir, + extra_postargs=list(system_info.shared_libs) + [ + '-Wl,--output-def,' + def_path, + '-Wl,--export-all-symbols', + '-Wl,--enable-auto-import', + '-static', + '-mlong-double-64', + ] + ['-l' + library for library in libraries] + + ['-L' + lib_dir for lib_dir in library_dirs], + debug=debug) + + system_info.shared_libs.add(dll_path) + + # No PowerPC! + if is_win64(): + specifier = '/MACHINE:X64' + else: + specifier = '/MACHINE:X86' + + # MSVC specific code + lib_args = ['/def:' + def_path, '/OUT:' + lib_path, specifier] + if not c_compiler.initialized: + c_compiler.initialize() + c_compiler.spawn([c_compiler.lib] + lib_args) + + return lib_path + + def compile(self, + sources, + output_dir, + macros=[], + include_dirs=[], + debug=False, + extra_postargs=[], + depends=[], + **kwargs): + c_compiler = self.c_compiler + if c_compiler and c_compiler.compiler_type == "msvc": + # MSVC cannot link objects compiled by GNU fortran + # so we need to contain the damage here. Immediately + # compile a DLL and return the lib for the DLL as + # the object. Also keep track of previous DLLs that + # we have compiled so that we can link against them. + objects = GnuFCompiler.compile( + self, + sources, + output_dir=output_dir, + macros=macros, + include_dirs=include_dirs, + debug=debug, + extra_postargs=extra_postargs, + depends=depends) + + lib_path = self.link_wrapper_lib(objects, output_dir=output_dir) + + # Return the lib that we created as the "object" + return [lib_path] + else: + return GnuFCompiler.compile( + self, + sources, + output_dir, + macros=macros, + include_dirs=include_dirs, + debug=debug, + extra_postargs=extra_postargs, + depends=depends, + **kwargs) + def _can_target(cmd, arch): """Return true if the architecture supports the -arch flag""" @@ -382,6 +484,7 @@ def _can_target(cmd, arch): os.remove(filename) return False + if __name__ == '__main__': from distutils import log log.set_verbosity(2) diff --git a/numpy/distutils/misc_util.py b/numpy/distutils/misc_util.py index 30d72f50e..3221e4da3 100644 --- a/numpy/distutils/misc_util.py +++ b/numpy/distutils/misc_util.py @@ -33,6 +33,13 @@ def clean_up_temporary_directory(): atexit.register(clean_up_temporary_directory) +# stores the order in which the libraries were added +_ldata = [] +def get_library_names(): + """Get the library names in order""" + global _ldata + + return _ldata from numpy.distutils.compat import get_exception from numpy.compat import basestring @@ -1558,6 +1565,10 @@ class Configuration(object): name = name #+ '__OF__' + self.name build_info['sources'] = sources + global _ldata + # Track the library order + _ldata += [name] + # Sometimes, depends is not set up to an empty list by default, and if # depends is not given to add_library, distutils barfs (#1134) if not 'depends' in build_info: @@ -2283,6 +2294,16 @@ def generate_config_py(target): f.write('# This file is generated by %s\n' % (os.path.abspath(sys.argv[0]))) f.write('# It contains system_info results at the time of building this package.\n') f.write('__all__ = ["get_info","show"]\n\n') + + if system_info.shared_libs: + f.write(""" + +import os + +os.environ["PATH"] += os.pathsep + os.path.join(os.path.dirname(__file__), '_lib') + +""") + for k, i in system_info.saved_results.items(): f.write('%s=%r\n' % (k, i)) f.write(r''' diff --git a/numpy/distutils/system_info.py b/numpy/distutils/system_info.py index 2c1103d27..05eedfd3f 100644 --- a/numpy/distutils/system_info.py +++ b/numpy/distutils/system_info.py @@ -126,6 +126,7 @@ import os import re import copy import warnings +import atexit from glob import glob from functools import reduce if sys.version_info[0] < 3: @@ -469,6 +470,8 @@ class system_info(object): verbosity = 1 saved_results = {} + shared_libs = set() + notfounderror = NotFoundError def __init__(self, @@ -1766,10 +1769,70 @@ class openblas_lapack_info(openblas_info): _lib_names = ['openblas'] notfounderror = BlasNotFoundError + def create_msvc_openblas_lib(self, info): + from numpy.distutils.fcompiler import new_fcompiler + + try: + c = distutils.ccompiler.new_compiler() + if c.compiler_type != "msvc": + return False + + f = new_fcompiler(compiler="gnu95", c_compiler=c) + f.customize('') + + libraries=info['libraries'] + library_dirs=info['library_dirs'] + + # For each gfortran-compatible static library, + # we need to generate a dynamic library with + # no dependencies. + + # First, find the full path to each library directory + library_paths = [] + for library in libraries: + for library_dir in library_dirs: + # MinGW static ext will be .a + fullpath = os.path.join(library_dir, library + '.a') + if os.path.isfile(fullpath): + library_paths.append(fullpath) + break + else: + return False + + + tmpdir = tempfile.mkdtemp() + # Next, convert each library to MSVC format + for library, library_path in zip(libraries, library_paths): + mingw_lib = os.path.join(tmpdir, 'lib'+library+'.a') + shutil.copy(library_path, mingw_lib) + msvc_lib = f.link_wrapper_lib(['-Wl,--whole-archive', + mingw_lib, + '-Wl,--no-whole-archive', + ], + output_dir=tmpdir) + + msvc_lib_path = library + '.lib' + + # Now copy + print('copying ' + msvc_lib + ' -> ' + msvc_lib_path) + shutil.copy(msvc_lib, msvc_lib_path) + + atexit.register(os.remove, msvc_lib_path) + + system_info.shared_libs.add('') + + return True + except: + return False + def check_embedded_lapack(self, info): res = False c = distutils.ccompiler.new_compiler() c.customize('') + + if c.compiler_type == "msvc" and not self.create_msvc_openblas_lib(info): + return False + tmpdir = tempfile.mkdtemp() s = """void zungqr(); int main(int argc, const char *argv[]) @@ -1784,6 +1847,8 @@ class openblas_lapack_info(openblas_info): extra_args = info['extra_link_args'] except Exception: extra_args = [] + if sys.version_info < (3, 5) and sys.version_info > (3, 0) and c.compiler_type == "msvc": + extra_args.append("/MANIFEST") try: with open(src, 'wt') as f: f.write(s) |