|
from __future__ import division, absolute_import, print_function |
|
|
|
import re |
|
import os |
|
import sys |
|
import warnings |
|
import platform |
|
import tempfile |
|
from subprocess import Popen, PIPE, STDOUT |
|
|
|
from numpy.distutils.cpuinfo import cpu |
|
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 |
|
|
|
compilers = ['GnuFCompiler', 'Gnu95FCompiler'] |
|
|
|
TARGET_R = re.compile("Target: ([a-zA-Z0-9_\-]*)") |
|
|
|
|
|
def is_win64(): |
|
return sys.platform == "win32" and platform.architecture()[0] == "64bit" |
|
|
|
if is_win64(): |
|
|
|
_EXTRAFLAGS = [] |
|
else: |
|
_EXTRAFLAGS = [] |
|
|
|
class GnuFCompiler(FCompiler): |
|
compiler_type = 'gnu' |
|
compiler_aliases = ('g77',) |
|
description = 'GNU Fortran 77 compiler' |
|
|
|
def gnu_version_match(self, version_string): |
|
"""Handle the different versions of GNU fortran compilers""" |
|
m = re.search(r'GNU Fortran', version_string) |
|
if not m: |
|
return None |
|
m = re.search(r'GNU Fortran\s+95.*?([0-9-.]+)', version_string) |
|
if m: |
|
return ('gfortran', m.group(1)) |
|
m = re.search(r'GNU Fortran.*?\-?([0-9-.]+)', version_string) |
|
if m: |
|
v = m.group(1) |
|
if v.startswith('0') or v.startswith('2') or v.startswith('3'): |
|
|
|
return ('g77', v) |
|
else: |
|
|
|
|
|
return ('gfortran', v) |
|
|
|
def version_match(self, version_string): |
|
v = self.gnu_version_match(version_string) |
|
if not v or v[0] != 'g77': |
|
return None |
|
return v[1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
possible_executables = ['g77', 'f77'] |
|
executables = { |
|
'version_cmd' : [None, "--version"], |
|
'compiler_f77' : [None, "-g", "-Wall", "-fno-second-underscore"], |
|
'compiler_f90' : None, |
|
'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 |
|
|
|
|
|
|
|
if os.name != 'nt' and sys.platform != 'cygwin': |
|
pic_flags = ['-fPIC'] |
|
|
|
|
|
if sys.platform == 'win32': |
|
for key in ['version_cmd', 'compiler_f77', 'linker_so', 'linker_exe']: |
|
executables[key].append('-mno-cygwin') |
|
|
|
g2c = 'g2c' |
|
|
|
suggested_f90_compiler = 'gnu95' |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def get_flags_linker_so(self): |
|
opt = self.linker_so[1:] |
|
if sys.platform=='darwin': |
|
target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) |
|
|
|
|
|
|
|
|
|
|
|
if not target: |
|
|
|
|
|
|
|
|
|
|
|
|
|
import distutils.sysconfig as sc |
|
g = {} |
|
filename = sc.get_makefile_filename() |
|
sc.parse_makefile(filename, g) |
|
target = g.get('MACOSX_DEPLOYMENT_TARGET', '10.3') |
|
os.environ['MACOSX_DEPLOYMENT_TARGET'] = target |
|
if target == '10.3': |
|
s = 'Env. variable MACOSX_DEPLOYMENT_TARGET set to 10.3' |
|
warnings.warn(s) |
|
|
|
opt.extend(['-undefined', 'dynamic_lookup', '-bundle']) |
|
else: |
|
opt.append("-shared") |
|
if sys.platform.startswith('sunos'): |
|
|
|
|
|
|
|
|
|
|
|
|
|
opt.append('-mimpure-text') |
|
return opt |
|
|
|
def get_libgcc_dir(self): |
|
status, output = exec_command(self.compiler_f77 + |
|
['-print-libgcc-file-name'], |
|
use_tee=0) |
|
if not status: |
|
return os.path.dirname(output) |
|
return None |
|
|
|
def get_library_dirs(self): |
|
opt = [] |
|
if sys.platform[:5] != 'linux': |
|
d = self.get_libgcc_dir() |
|
if d: |
|
|
|
if sys.platform == 'win32' and not d.startswith('/usr/lib'): |
|
d = os.path.normpath(d) |
|
if not os.path.exists(os.path.join(d, "lib%s.a" % self.g2c)): |
|
d2 = os.path.abspath(os.path.join(d, |
|
'../../../../lib')) |
|
if os.path.exists(os.path.join(d2, "lib%s.a" % self.g2c)): |
|
opt.append(d2) |
|
opt.append(d) |
|
return opt |
|
|
|
def get_libraries(self): |
|
opt = [] |
|
d = self.get_libgcc_dir() |
|
if d is not None: |
|
g2c = self.g2c + '-pic' |
|
f = self.static_lib_format % (g2c, self.static_lib_extension) |
|
if not os.path.isfile(os.path.join(d, f)): |
|
g2c = self.g2c |
|
else: |
|
g2c = self.g2c |
|
|
|
if g2c is not None: |
|
opt.append(g2c) |
|
c_compiler = self.c_compiler |
|
if sys.platform == 'win32' and c_compiler and \ |
|
c_compiler.compiler_type=='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 |
|
|
|
def get_flags_debug(self): |
|
return ['-g'] |
|
|
|
def get_flags_opt(self): |
|
v = self.get_version() |
|
if v and v<='3.3.3': |
|
|
|
|
|
opt = ['-O2'] |
|
else: |
|
opt = ['-O3'] |
|
opt.append('-funroll-loops') |
|
return opt |
|
|
|
def _c_arch_flags(self): |
|
""" Return detected arch flags from CFLAGS """ |
|
from distutils import sysconfig |
|
try: |
|
cflags = sysconfig.get_config_vars()['CFLAGS'] |
|
except KeyError: |
|
return [] |
|
arch_re = re.compile(r"-arch\s+(\w+)") |
|
arch_flags = [] |
|
for arch in arch_re.findall(cflags): |
|
arch_flags += ['-arch', arch] |
|
return arch_flags |
|
|
|
def get_flags_arch(self): |
|
return [] |
|
|
|
def runtime_library_dir_option(self, dir): |
|
return '-Wl,-rpath="%s"' % dir |
|
|
|
class Gnu95FCompiler(GnuFCompiler): |
|
compiler_type = 'gnu95' |
|
compiler_aliases = ('gfortran',) |
|
description = 'GNU Fortran 95 compiler' |
|
|
|
def version_match(self, version_string): |
|
v = self.gnu_version_match(version_string) |
|
if not v or v[0] != 'gfortran': |
|
return None |
|
v = v[1] |
|
if v>='4.': |
|
|
|
pass |
|
else: |
|
|
|
if sys.platform == 'win32': |
|
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>", "--version"], |
|
'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' |
|
|
|
g2c = 'gfortran' |
|
|
|
def _universal_flags(self, cmd): |
|
"""Return a list of -arch flags for every supported architecture.""" |
|
if not sys.platform == 'darwin': |
|
return [] |
|
arch_flags = [] |
|
|
|
c_archs = self._c_arch_flags() |
|
if "i386" in c_archs: |
|
c_archs[c_archs.index("i386")] = "i686" |
|
|
|
|
|
for arch in ["ppc", "i686", "x86_64", "ppc64"]: |
|
if _can_target(cmd, arch) and arch in c_archs: |
|
arch_flags.extend(["-arch", arch]) |
|
return arch_flags |
|
|
|
def get_flags(self): |
|
flags = GnuFCompiler.get_flags(self) |
|
arch_flags = self._universal_flags(self.compiler_f90) |
|
if arch_flags: |
|
flags[:0] = arch_flags |
|
return flags |
|
|
|
def get_flags_linker_so(self): |
|
flags = GnuFCompiler.get_flags_linker_so(self) |
|
arch_flags = self._universal_flags(self.linker_so) |
|
if arch_flags: |
|
flags[:0] = arch_flags |
|
return flags |
|
|
|
def get_library_dirs(self): |
|
opt = GnuFCompiler.get_library_dirs(self) |
|
if sys.platform == 'win32': |
|
c_compiler = self.c_compiler |
|
if c_compiler and c_compiler.compiler_type == "msvc": |
|
target = self.get_target() |
|
if target: |
|
d = os.path.normpath(self.get_libgcc_dir()) |
|
root = os.path.join(d, os.pardir, os.pardir, os.pardir, os.pardir) |
|
mingwdir = os.path.normpath(os.path.join(root, target, "lib")) |
|
full = os.path.join(mingwdir, "libmingwex.a") |
|
if os.path.exists(full): |
|
opt.append(mingwdir) |
|
return opt |
|
|
|
def get_libraries(self): |
|
opt = GnuFCompiler.get_libraries(self) |
|
if sys.platform == 'darwin': |
|
opt.remove('cc_dynamic') |
|
if sys.platform == 'win32': |
|
c_compiler = self.c_compiler |
|
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") |
|
|
|
if is_win64(): |
|
c_compiler = self.c_compiler |
|
if c_compiler and c_compiler.compiler_type == "msvc": |
|
return [] |
|
else: |
|
raise NotImplementedError("Only MS compiler supported with gfortran on win64") |
|
return opt |
|
|
|
def get_target(self): |
|
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): |
|
if is_win64(): |
|
return ['-O0'] |
|
else: |
|
return GnuFCompiler.get_flags_opt(self) |
|
|
|
def _can_target(cmd, arch): |
|
"""Return true is the command supports the -arch flag for the given |
|
architecture.""" |
|
newcmd = cmd[:] |
|
fid, filename = tempfile.mkstemp(suffix=".f") |
|
try: |
|
d = os.path.dirname(filename) |
|
output = os.path.splitext(filename)[0] + ".o" |
|
try: |
|
newcmd.extend(["-arch", arch, "-c", filename]) |
|
p = Popen(newcmd, stderr=STDOUT, stdout=PIPE, cwd=d) |
|
p.communicate() |
|
return p.returncode == 0 |
|
finally: |
|
if os.path.exists(output): |
|
os.remove(output) |
|
finally: |
|
os.remove(filename) |
|
return False |
|
|
|
if __name__ == '__main__': |
|
from distutils import log |
|
log.set_verbosity(2) |
|
|
|
compiler = GnuFCompiler() |
|
compiler.customize() |
|
print(compiler.get_version()) |
|
|
|
try: |
|
compiler = Gnu95FCompiler() |
|
compiler.customize() |
|
print(compiler.get_version()) |
|
except Exception: |
|
msg = get_exception() |
|
print(msg) |
|
|