Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import re Chris@87: import os Chris@87: import sys Chris@87: import warnings Chris@87: import platform Chris@87: import tempfile Chris@87: from subprocess import Popen, PIPE, STDOUT Chris@87: Chris@87: from numpy.distutils.cpuinfo import cpu Chris@87: from numpy.distutils.fcompiler import FCompiler Chris@87: from numpy.distutils.exec_command import exec_command Chris@87: from numpy.distutils.misc_util import msvc_runtime_library Chris@87: from numpy.distutils.compat import get_exception Chris@87: Chris@87: compilers = ['GnuFCompiler', 'Gnu95FCompiler'] Chris@87: Chris@87: TARGET_R = re.compile("Target: ([a-zA-Z0-9_\-]*)") Chris@87: Chris@87: # XXX: handle cross compilation Chris@87: def is_win64(): Chris@87: return sys.platform == "win32" and platform.architecture()[0] == "64bit" Chris@87: Chris@87: if is_win64(): Chris@87: #_EXTRAFLAGS = ["-fno-leading-underscore"] Chris@87: _EXTRAFLAGS = [] Chris@87: else: Chris@87: _EXTRAFLAGS = [] Chris@87: Chris@87: class GnuFCompiler(FCompiler): Chris@87: compiler_type = 'gnu' Chris@87: compiler_aliases = ('g77',) Chris@87: description = 'GNU Fortran 77 compiler' Chris@87: Chris@87: def gnu_version_match(self, version_string): Chris@87: """Handle the different versions of GNU fortran compilers""" Chris@87: m = re.search(r'GNU Fortran', version_string) Chris@87: if not m: Chris@87: return None Chris@87: m = re.search(r'GNU Fortran\s+95.*?([0-9-.]+)', version_string) Chris@87: if m: Chris@87: return ('gfortran', m.group(1)) Chris@87: m = re.search(r'GNU Fortran.*?\-?([0-9-.]+)', version_string) Chris@87: if m: Chris@87: v = m.group(1) Chris@87: if v.startswith('0') or v.startswith('2') or v.startswith('3'): Chris@87: # the '0' is for early g77's Chris@87: return ('g77', v) Chris@87: else: Chris@87: # at some point in the 4.x series, the ' 95' was dropped Chris@87: # from the version string Chris@87: return ('gfortran', v) Chris@87: Chris@87: def version_match(self, version_string): Chris@87: v = self.gnu_version_match(version_string) Chris@87: if not v or v[0] != 'g77': Chris@87: return None Chris@87: return v[1] Chris@87: Chris@87: # 'g77 --version' results Chris@87: # SunOS: GNU Fortran (GCC 3.2) 3.2 20020814 (release) Chris@87: # Debian: GNU Fortran (GCC) 3.3.3 20040110 (prerelease) (Debian) Chris@87: # GNU Fortran (GCC) 3.3.3 (Debian 20040401) Chris@87: # GNU Fortran 0.5.25 20010319 (prerelease) Chris@87: # Redhat: GNU Fortran (GCC 3.2.2 20030222 (Red Hat Linux 3.2.2-5)) 3.2.2 20030222 (Red Hat Linux 3.2.2-5) Chris@87: # GNU Fortran (GCC) 3.4.2 (mingw-special) Chris@87: Chris@87: possible_executables = ['g77', 'f77'] Chris@87: executables = { Chris@87: 'version_cmd' : [None, "--version"], Chris@87: 'compiler_f77' : [None, "-g", "-Wall", "-fno-second-underscore"], Chris@87: 'compiler_f90' : None, # Use --fcompiler=gnu95 for f90 codes Chris@87: 'compiler_fix' : None, Chris@87: 'linker_so' : [None, "-g", "-Wall"], Chris@87: 'archiver' : ["ar", "-cr"], Chris@87: 'ranlib' : ["ranlib"], Chris@87: 'linker_exe' : [None, "-g", "-Wall"] Chris@87: } Chris@87: module_dir_switch = None Chris@87: module_include_switch = None Chris@87: Chris@87: # Cygwin: f771: warning: -fPIC ignored for target (all code is Chris@87: # position independent) Chris@87: if os.name != 'nt' and sys.platform != 'cygwin': Chris@87: pic_flags = ['-fPIC'] Chris@87: Chris@87: # use -mno-cygwin for g77 when Python is not Cygwin-Python Chris@87: if sys.platform == 'win32': Chris@87: for key in ['version_cmd', 'compiler_f77', 'linker_so', 'linker_exe']: Chris@87: executables[key].append('-mno-cygwin') Chris@87: Chris@87: g2c = 'g2c' Chris@87: Chris@87: suggested_f90_compiler = 'gnu95' Chris@87: Chris@87: #def get_linker_so(self): Chris@87: # # win32 linking should be handled by standard linker Chris@87: # # Darwin g77 cannot be used as a linker. Chris@87: # #if re.match(r'(darwin)', sys.platform): Chris@87: # # return Chris@87: # return FCompiler.get_linker_so(self) Chris@87: Chris@87: def get_flags_linker_so(self): Chris@87: opt = self.linker_so[1:] Chris@87: if sys.platform=='darwin': Chris@87: target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) Chris@87: # If MACOSX_DEPLOYMENT_TARGET is set, we simply trust the value Chris@87: # and leave it alone. But, distutils will complain if the Chris@87: # environment's value is different from the one in the Python Chris@87: # Makefile used to build Python. We let disutils handle this Chris@87: # error checking. Chris@87: if not target: Chris@87: # If MACOSX_DEPLOYMENT_TARGET is not set in the environment, Chris@87: # we try to get it first from the Python Makefile and then we Chris@87: # fall back to setting it to 10.3 to maximize the set of Chris@87: # versions we can work with. This is a reasonable default Chris@87: # even when using the official Python dist and those derived Chris@87: # from it. Chris@87: import distutils.sysconfig as sc Chris@87: g = {} Chris@87: filename = sc.get_makefile_filename() Chris@87: sc.parse_makefile(filename, g) Chris@87: target = g.get('MACOSX_DEPLOYMENT_TARGET', '10.3') Chris@87: os.environ['MACOSX_DEPLOYMENT_TARGET'] = target Chris@87: if target == '10.3': Chris@87: s = 'Env. variable MACOSX_DEPLOYMENT_TARGET set to 10.3' Chris@87: warnings.warn(s) Chris@87: Chris@87: opt.extend(['-undefined', 'dynamic_lookup', '-bundle']) Chris@87: else: Chris@87: opt.append("-shared") Chris@87: if sys.platform.startswith('sunos'): Chris@87: # SunOS often has dynamically loaded symbols defined in the Chris@87: # static library libg2c.a The linker doesn't like this. To Chris@87: # ignore the problem, use the -mimpure-text flag. It isn't Chris@87: # the safest thing, but seems to work. 'man gcc' says: Chris@87: # ".. Instead of using -mimpure-text, you should compile all Chris@87: # source code with -fpic or -fPIC." Chris@87: opt.append('-mimpure-text') Chris@87: return opt Chris@87: Chris@87: def get_libgcc_dir(self): Chris@87: status, output = exec_command(self.compiler_f77 + Chris@87: ['-print-libgcc-file-name'], Chris@87: use_tee=0) Chris@87: if not status: Chris@87: return os.path.dirname(output) Chris@87: return None Chris@87: Chris@87: def get_library_dirs(self): Chris@87: opt = [] Chris@87: if sys.platform[:5] != 'linux': Chris@87: d = self.get_libgcc_dir() Chris@87: if d: Chris@87: # if windows and not cygwin, libg2c lies in a different folder Chris@87: if sys.platform == 'win32' and not d.startswith('/usr/lib'): Chris@87: d = os.path.normpath(d) Chris@87: if not os.path.exists(os.path.join(d, "lib%s.a" % self.g2c)): Chris@87: d2 = os.path.abspath(os.path.join(d, Chris@87: '../../../../lib')) Chris@87: if os.path.exists(os.path.join(d2, "lib%s.a" % self.g2c)): Chris@87: opt.append(d2) Chris@87: opt.append(d) Chris@87: return opt Chris@87: Chris@87: def get_libraries(self): Chris@87: opt = [] Chris@87: d = self.get_libgcc_dir() Chris@87: if d is not None: Chris@87: g2c = self.g2c + '-pic' Chris@87: f = self.static_lib_format % (g2c, self.static_lib_extension) Chris@87: if not os.path.isfile(os.path.join(d, f)): Chris@87: g2c = self.g2c Chris@87: else: Chris@87: g2c = self.g2c Chris@87: Chris@87: if g2c is not None: Chris@87: opt.append(g2c) Chris@87: c_compiler = self.c_compiler Chris@87: if sys.platform == 'win32' and c_compiler and \ Chris@87: c_compiler.compiler_type=='msvc': Chris@87: # the following code is not needed (read: breaks) when using MinGW Chris@87: # in case want to link F77 compiled code with MSVC Chris@87: opt.append('gcc') Chris@87: runtime_lib = msvc_runtime_library() Chris@87: if runtime_lib: Chris@87: opt.append(runtime_lib) Chris@87: if sys.platform == 'darwin': Chris@87: opt.append('cc_dynamic') Chris@87: return opt Chris@87: Chris@87: def get_flags_debug(self): Chris@87: return ['-g'] Chris@87: Chris@87: def get_flags_opt(self): Chris@87: v = self.get_version() Chris@87: if v and v<='3.3.3': Chris@87: # With this compiler version building Fortran BLAS/LAPACK Chris@87: # with -O3 caused failures in lib.lapack heevr,syevr tests. Chris@87: opt = ['-O2'] Chris@87: else: Chris@87: opt = ['-O3'] Chris@87: opt.append('-funroll-loops') Chris@87: return opt Chris@87: Chris@87: def _c_arch_flags(self): Chris@87: """ Return detected arch flags from CFLAGS """ Chris@87: from distutils import sysconfig Chris@87: try: Chris@87: cflags = sysconfig.get_config_vars()['CFLAGS'] Chris@87: except KeyError: Chris@87: return [] Chris@87: arch_re = re.compile(r"-arch\s+(\w+)") Chris@87: arch_flags = [] Chris@87: for arch in arch_re.findall(cflags): Chris@87: arch_flags += ['-arch', arch] Chris@87: return arch_flags Chris@87: Chris@87: def get_flags_arch(self): Chris@87: return [] Chris@87: Chris@87: def runtime_library_dir_option(self, dir): Chris@87: return '-Wl,-rpath="%s"' % dir Chris@87: Chris@87: class Gnu95FCompiler(GnuFCompiler): Chris@87: compiler_type = 'gnu95' Chris@87: compiler_aliases = ('gfortran',) Chris@87: description = 'GNU Fortran 95 compiler' Chris@87: Chris@87: def version_match(self, version_string): Chris@87: v = self.gnu_version_match(version_string) Chris@87: if not v or v[0] != 'gfortran': Chris@87: return None Chris@87: v = v[1] Chris@87: if v>='4.': Chris@87: # gcc-4 series releases do not support -mno-cygwin option Chris@87: pass Chris@87: else: Chris@87: # use -mno-cygwin flag for gfortran when Python is not Cygwin-Python Chris@87: if sys.platform == 'win32': Chris@87: for key in ['version_cmd', 'compiler_f77', 'compiler_f90', Chris@87: 'compiler_fix', 'linker_so', 'linker_exe']: Chris@87: self.executables[key].append('-mno-cygwin') Chris@87: return v Chris@87: Chris@87: # 'gfortran --version' results: Chris@87: # XXX is the below right? Chris@87: # Debian: GNU Fortran 95 (GCC 4.0.3 20051023 (prerelease) (Debian 4.0.2-3)) Chris@87: # GNU Fortran 95 (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21) Chris@87: # OS X: GNU Fortran 95 (GCC) 4.1.0 Chris@87: # GNU Fortran 95 (GCC) 4.2.0 20060218 (experimental) Chris@87: # GNU Fortran (GCC) 4.3.0 20070316 (experimental) Chris@87: Chris@87: possible_executables = ['gfortran', 'f95'] Chris@87: executables = { Chris@87: 'version_cmd' : ["", "--version"], Chris@87: 'compiler_f77' : [None, "-Wall", "-g", "-ffixed-form", Chris@87: "-fno-second-underscore"] + _EXTRAFLAGS, Chris@87: 'compiler_f90' : [None, "-Wall", "-g", Chris@87: "-fno-second-underscore"] + _EXTRAFLAGS, Chris@87: 'compiler_fix' : [None, "-Wall", "-g","-ffixed-form", Chris@87: "-fno-second-underscore"] + _EXTRAFLAGS, Chris@87: 'linker_so' : ["", "-Wall", "-g"], Chris@87: 'archiver' : ["ar", "-cr"], Chris@87: 'ranlib' : ["ranlib"], Chris@87: 'linker_exe' : [None, "-Wall"] Chris@87: } Chris@87: Chris@87: module_dir_switch = '-J' Chris@87: module_include_switch = '-I' Chris@87: Chris@87: g2c = 'gfortran' Chris@87: Chris@87: def _universal_flags(self, cmd): Chris@87: """Return a list of -arch flags for every supported architecture.""" Chris@87: if not sys.platform == 'darwin': Chris@87: return [] Chris@87: arch_flags = [] Chris@87: # get arches the C compiler gets. Chris@87: c_archs = self._c_arch_flags() Chris@87: if "i386" in c_archs: Chris@87: c_archs[c_archs.index("i386")] = "i686" Chris@87: # check the arches the Fortran compiler supports, and compare with Chris@87: # arch flags from C compiler Chris@87: for arch in ["ppc", "i686", "x86_64", "ppc64"]: Chris@87: if _can_target(cmd, arch) and arch in c_archs: Chris@87: arch_flags.extend(["-arch", arch]) Chris@87: return arch_flags Chris@87: Chris@87: def get_flags(self): Chris@87: flags = GnuFCompiler.get_flags(self) Chris@87: arch_flags = self._universal_flags(self.compiler_f90) Chris@87: if arch_flags: Chris@87: flags[:0] = arch_flags Chris@87: return flags Chris@87: Chris@87: def get_flags_linker_so(self): Chris@87: flags = GnuFCompiler.get_flags_linker_so(self) Chris@87: arch_flags = self._universal_flags(self.linker_so) Chris@87: if arch_flags: Chris@87: flags[:0] = arch_flags Chris@87: return flags Chris@87: Chris@87: def get_library_dirs(self): Chris@87: opt = GnuFCompiler.get_library_dirs(self) Chris@87: if sys.platform == 'win32': Chris@87: c_compiler = self.c_compiler Chris@87: if c_compiler and c_compiler.compiler_type == "msvc": Chris@87: target = self.get_target() Chris@87: if target: Chris@87: d = os.path.normpath(self.get_libgcc_dir()) Chris@87: root = os.path.join(d, os.pardir, os.pardir, os.pardir, os.pardir) Chris@87: mingwdir = os.path.normpath(os.path.join(root, target, "lib")) Chris@87: full = os.path.join(mingwdir, "libmingwex.a") Chris@87: if os.path.exists(full): Chris@87: opt.append(mingwdir) Chris@87: return opt Chris@87: Chris@87: def get_libraries(self): Chris@87: opt = GnuFCompiler.get_libraries(self) Chris@87: if sys.platform == 'darwin': Chris@87: opt.remove('cc_dynamic') Chris@87: if sys.platform == 'win32': Chris@87: c_compiler = self.c_compiler Chris@87: if c_compiler and c_compiler.compiler_type == "msvc": Chris@87: if "gcc" in opt: Chris@87: i = opt.index("gcc") Chris@87: opt.insert(i+1, "mingwex") Chris@87: opt.insert(i+1, "mingw32") Chris@87: # XXX: fix this mess, does not work for mingw Chris@87: if is_win64(): Chris@87: c_compiler = self.c_compiler Chris@87: if c_compiler and c_compiler.compiler_type == "msvc": Chris@87: return [] Chris@87: else: Chris@87: raise NotImplementedError("Only MS compiler supported with gfortran on win64") Chris@87: return opt Chris@87: Chris@87: def get_target(self): Chris@87: status, output = exec_command(self.compiler_f77 + Chris@87: ['-v'], Chris@87: use_tee=0) Chris@87: if not status: Chris@87: m = TARGET_R.search(output) Chris@87: if m: Chris@87: return m.group(1) Chris@87: return "" Chris@87: Chris@87: def get_flags_opt(self): Chris@87: if is_win64(): Chris@87: return ['-O0'] Chris@87: else: Chris@87: return GnuFCompiler.get_flags_opt(self) Chris@87: Chris@87: def _can_target(cmd, arch): Chris@87: """Return true is the command supports the -arch flag for the given Chris@87: architecture.""" Chris@87: newcmd = cmd[:] Chris@87: fid, filename = tempfile.mkstemp(suffix=".f") Chris@87: try: Chris@87: d = os.path.dirname(filename) Chris@87: output = os.path.splitext(filename)[0] + ".o" Chris@87: try: Chris@87: newcmd.extend(["-arch", arch, "-c", filename]) Chris@87: p = Popen(newcmd, stderr=STDOUT, stdout=PIPE, cwd=d) Chris@87: p.communicate() Chris@87: return p.returncode == 0 Chris@87: finally: Chris@87: if os.path.exists(output): Chris@87: os.remove(output) Chris@87: finally: Chris@87: os.remove(filename) Chris@87: return False Chris@87: Chris@87: if __name__ == '__main__': Chris@87: from distutils import log Chris@87: log.set_verbosity(2) Chris@87: Chris@87: compiler = GnuFCompiler() Chris@87: compiler.customize() Chris@87: print(compiler.get_version()) Chris@87: Chris@87: try: Chris@87: compiler = Gnu95FCompiler() Chris@87: compiler.customize() Chris@87: print(compiler.get_version()) Chris@87: except Exception: Chris@87: msg = get_exception() Chris@87: print(msg)