summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxoviat <xoviat@users.noreply.github.com>2017-07-22 15:30:07 -0500
committerPauli Virtanen <pav@iki.fi>2017-09-02 16:56:22 +0300
commitf3c8a0ab23966cae9992dae74da96807a44bc0d8 (patch)
tree8d00247115affdcb9d8f5b1b8c88586946b9143b
parent11593aa176d491beb0cc5ffcc393956a5435a2bf (diff)
downloadnumpy-f3c8a0ab23966cae9992dae74da96807a44bc0d8.tar.gz
distutils: gnu: patch fcompile
This allows mingw's gfortran to work with MSVC. DLLs are autogenerated using heuristics that should work with most cases. In addition, a libopenblas DLL is compiled from the static lib for use with MSVC. All generated DLLs have randomized names so that no clashes will occur.
-rw-r--r--numpy/distutils/command/build_clib.py78
-rw-r--r--numpy/distutils/command/build_ext.py120
-rw-r--r--numpy/distutils/fcompiler/__init__.py1
-rw-r--r--numpy/distutils/fcompiler/gnu.py209
-rw-r--r--numpy/distutils/misc_util.py21
-rw-r--r--numpy/distutils/system_info.py65
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)