summaryrefslogtreecommitdiff
path: root/numpy/distutils/fcompiler
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 /numpy/distutils/fcompiler
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.
Diffstat (limited to 'numpy/distutils/fcompiler')
-rw-r--r--numpy/distutils/fcompiler/__init__.py1
-rw-r--r--numpy/distutils/fcompiler/gnu.py209
2 files changed, 157 insertions, 53 deletions
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)