Chris@87
|
1 from __future__ import division, absolute_import, print_function
|
Chris@87
|
2
|
Chris@87
|
3 import re
|
Chris@87
|
4 import os
|
Chris@87
|
5 import sys
|
Chris@87
|
6 import warnings
|
Chris@87
|
7 import platform
|
Chris@87
|
8 import tempfile
|
Chris@87
|
9 from subprocess import Popen, PIPE, STDOUT
|
Chris@87
|
10
|
Chris@87
|
11 from numpy.distutils.cpuinfo import cpu
|
Chris@87
|
12 from numpy.distutils.fcompiler import FCompiler
|
Chris@87
|
13 from numpy.distutils.exec_command import exec_command
|
Chris@87
|
14 from numpy.distutils.misc_util import msvc_runtime_library
|
Chris@87
|
15 from numpy.distutils.compat import get_exception
|
Chris@87
|
16
|
Chris@87
|
17 compilers = ['GnuFCompiler', 'Gnu95FCompiler']
|
Chris@87
|
18
|
Chris@87
|
19 TARGET_R = re.compile("Target: ([a-zA-Z0-9_\-]*)")
|
Chris@87
|
20
|
Chris@87
|
21 # XXX: handle cross compilation
|
Chris@87
|
22 def is_win64():
|
Chris@87
|
23 return sys.platform == "win32" and platform.architecture()[0] == "64bit"
|
Chris@87
|
24
|
Chris@87
|
25 if is_win64():
|
Chris@87
|
26 #_EXTRAFLAGS = ["-fno-leading-underscore"]
|
Chris@87
|
27 _EXTRAFLAGS = []
|
Chris@87
|
28 else:
|
Chris@87
|
29 _EXTRAFLAGS = []
|
Chris@87
|
30
|
Chris@87
|
31 class GnuFCompiler(FCompiler):
|
Chris@87
|
32 compiler_type = 'gnu'
|
Chris@87
|
33 compiler_aliases = ('g77',)
|
Chris@87
|
34 description = 'GNU Fortran 77 compiler'
|
Chris@87
|
35
|
Chris@87
|
36 def gnu_version_match(self, version_string):
|
Chris@87
|
37 """Handle the different versions of GNU fortran compilers"""
|
Chris@87
|
38 m = re.search(r'GNU Fortran', version_string)
|
Chris@87
|
39 if not m:
|
Chris@87
|
40 return None
|
Chris@87
|
41 m = re.search(r'GNU Fortran\s+95.*?([0-9-.]+)', version_string)
|
Chris@87
|
42 if m:
|
Chris@87
|
43 return ('gfortran', m.group(1))
|
Chris@87
|
44 m = re.search(r'GNU Fortran.*?\-?([0-9-.]+)', version_string)
|
Chris@87
|
45 if m:
|
Chris@87
|
46 v = m.group(1)
|
Chris@87
|
47 if v.startswith('0') or v.startswith('2') or v.startswith('3'):
|
Chris@87
|
48 # the '0' is for early g77's
|
Chris@87
|
49 return ('g77', v)
|
Chris@87
|
50 else:
|
Chris@87
|
51 # at some point in the 4.x series, the ' 95' was dropped
|
Chris@87
|
52 # from the version string
|
Chris@87
|
53 return ('gfortran', v)
|
Chris@87
|
54
|
Chris@87
|
55 def version_match(self, version_string):
|
Chris@87
|
56 v = self.gnu_version_match(version_string)
|
Chris@87
|
57 if not v or v[0] != 'g77':
|
Chris@87
|
58 return None
|
Chris@87
|
59 return v[1]
|
Chris@87
|
60
|
Chris@87
|
61 # 'g77 --version' results
|
Chris@87
|
62 # SunOS: GNU Fortran (GCC 3.2) 3.2 20020814 (release)
|
Chris@87
|
63 # Debian: GNU Fortran (GCC) 3.3.3 20040110 (prerelease) (Debian)
|
Chris@87
|
64 # GNU Fortran (GCC) 3.3.3 (Debian 20040401)
|
Chris@87
|
65 # GNU Fortran 0.5.25 20010319 (prerelease)
|
Chris@87
|
66 # 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
|
67 # GNU Fortran (GCC) 3.4.2 (mingw-special)
|
Chris@87
|
68
|
Chris@87
|
69 possible_executables = ['g77', 'f77']
|
Chris@87
|
70 executables = {
|
Chris@87
|
71 'version_cmd' : [None, "--version"],
|
Chris@87
|
72 'compiler_f77' : [None, "-g", "-Wall", "-fno-second-underscore"],
|
Chris@87
|
73 'compiler_f90' : None, # Use --fcompiler=gnu95 for f90 codes
|
Chris@87
|
74 'compiler_fix' : None,
|
Chris@87
|
75 'linker_so' : [None, "-g", "-Wall"],
|
Chris@87
|
76 'archiver' : ["ar", "-cr"],
|
Chris@87
|
77 'ranlib' : ["ranlib"],
|
Chris@87
|
78 'linker_exe' : [None, "-g", "-Wall"]
|
Chris@87
|
79 }
|
Chris@87
|
80 module_dir_switch = None
|
Chris@87
|
81 module_include_switch = None
|
Chris@87
|
82
|
Chris@87
|
83 # Cygwin: f771: warning: -fPIC ignored for target (all code is
|
Chris@87
|
84 # position independent)
|
Chris@87
|
85 if os.name != 'nt' and sys.platform != 'cygwin':
|
Chris@87
|
86 pic_flags = ['-fPIC']
|
Chris@87
|
87
|
Chris@87
|
88 # use -mno-cygwin for g77 when Python is not Cygwin-Python
|
Chris@87
|
89 if sys.platform == 'win32':
|
Chris@87
|
90 for key in ['version_cmd', 'compiler_f77', 'linker_so', 'linker_exe']:
|
Chris@87
|
91 executables[key].append('-mno-cygwin')
|
Chris@87
|
92
|
Chris@87
|
93 g2c = 'g2c'
|
Chris@87
|
94
|
Chris@87
|
95 suggested_f90_compiler = 'gnu95'
|
Chris@87
|
96
|
Chris@87
|
97 #def get_linker_so(self):
|
Chris@87
|
98 # # win32 linking should be handled by standard linker
|
Chris@87
|
99 # # Darwin g77 cannot be used as a linker.
|
Chris@87
|
100 # #if re.match(r'(darwin)', sys.platform):
|
Chris@87
|
101 # # return
|
Chris@87
|
102 # return FCompiler.get_linker_so(self)
|
Chris@87
|
103
|
Chris@87
|
104 def get_flags_linker_so(self):
|
Chris@87
|
105 opt = self.linker_so[1:]
|
Chris@87
|
106 if sys.platform=='darwin':
|
Chris@87
|
107 target = os.environ.get('MACOSX_DEPLOYMENT_TARGET', None)
|
Chris@87
|
108 # If MACOSX_DEPLOYMENT_TARGET is set, we simply trust the value
|
Chris@87
|
109 # and leave it alone. But, distutils will complain if the
|
Chris@87
|
110 # environment's value is different from the one in the Python
|
Chris@87
|
111 # Makefile used to build Python. We let disutils handle this
|
Chris@87
|
112 # error checking.
|
Chris@87
|
113 if not target:
|
Chris@87
|
114 # If MACOSX_DEPLOYMENT_TARGET is not set in the environment,
|
Chris@87
|
115 # we try to get it first from the Python Makefile and then we
|
Chris@87
|
116 # fall back to setting it to 10.3 to maximize the set of
|
Chris@87
|
117 # versions we can work with. This is a reasonable default
|
Chris@87
|
118 # even when using the official Python dist and those derived
|
Chris@87
|
119 # from it.
|
Chris@87
|
120 import distutils.sysconfig as sc
|
Chris@87
|
121 g = {}
|
Chris@87
|
122 filename = sc.get_makefile_filename()
|
Chris@87
|
123 sc.parse_makefile(filename, g)
|
Chris@87
|
124 target = g.get('MACOSX_DEPLOYMENT_TARGET', '10.3')
|
Chris@87
|
125 os.environ['MACOSX_DEPLOYMENT_TARGET'] = target
|
Chris@87
|
126 if target == '10.3':
|
Chris@87
|
127 s = 'Env. variable MACOSX_DEPLOYMENT_TARGET set to 10.3'
|
Chris@87
|
128 warnings.warn(s)
|
Chris@87
|
129
|
Chris@87
|
130 opt.extend(['-undefined', 'dynamic_lookup', '-bundle'])
|
Chris@87
|
131 else:
|
Chris@87
|
132 opt.append("-shared")
|
Chris@87
|
133 if sys.platform.startswith('sunos'):
|
Chris@87
|
134 # SunOS often has dynamically loaded symbols defined in the
|
Chris@87
|
135 # static library libg2c.a The linker doesn't like this. To
|
Chris@87
|
136 # ignore the problem, use the -mimpure-text flag. It isn't
|
Chris@87
|
137 # the safest thing, but seems to work. 'man gcc' says:
|
Chris@87
|
138 # ".. Instead of using -mimpure-text, you should compile all
|
Chris@87
|
139 # source code with -fpic or -fPIC."
|
Chris@87
|
140 opt.append('-mimpure-text')
|
Chris@87
|
141 return opt
|
Chris@87
|
142
|
Chris@87
|
143 def get_libgcc_dir(self):
|
Chris@87
|
144 status, output = exec_command(self.compiler_f77 +
|
Chris@87
|
145 ['-print-libgcc-file-name'],
|
Chris@87
|
146 use_tee=0)
|
Chris@87
|
147 if not status:
|
Chris@87
|
148 return os.path.dirname(output)
|
Chris@87
|
149 return None
|
Chris@87
|
150
|
Chris@87
|
151 def get_library_dirs(self):
|
Chris@87
|
152 opt = []
|
Chris@87
|
153 if sys.platform[:5] != 'linux':
|
Chris@87
|
154 d = self.get_libgcc_dir()
|
Chris@87
|
155 if d:
|
Chris@87
|
156 # if windows and not cygwin, libg2c lies in a different folder
|
Chris@87
|
157 if sys.platform == 'win32' and not d.startswith('/usr/lib'):
|
Chris@87
|
158 d = os.path.normpath(d)
|
Chris@87
|
159 if not os.path.exists(os.path.join(d, "lib%s.a" % self.g2c)):
|
Chris@87
|
160 d2 = os.path.abspath(os.path.join(d,
|
Chris@87
|
161 '../../../../lib'))
|
Chris@87
|
162 if os.path.exists(os.path.join(d2, "lib%s.a" % self.g2c)):
|
Chris@87
|
163 opt.append(d2)
|
Chris@87
|
164 opt.append(d)
|
Chris@87
|
165 return opt
|
Chris@87
|
166
|
Chris@87
|
167 def get_libraries(self):
|
Chris@87
|
168 opt = []
|
Chris@87
|
169 d = self.get_libgcc_dir()
|
Chris@87
|
170 if d is not None:
|
Chris@87
|
171 g2c = self.g2c + '-pic'
|
Chris@87
|
172 f = self.static_lib_format % (g2c, self.static_lib_extension)
|
Chris@87
|
173 if not os.path.isfile(os.path.join(d, f)):
|
Chris@87
|
174 g2c = self.g2c
|
Chris@87
|
175 else:
|
Chris@87
|
176 g2c = self.g2c
|
Chris@87
|
177
|
Chris@87
|
178 if g2c is not None:
|
Chris@87
|
179 opt.append(g2c)
|
Chris@87
|
180 c_compiler = self.c_compiler
|
Chris@87
|
181 if sys.platform == 'win32' and c_compiler and \
|
Chris@87
|
182 c_compiler.compiler_type=='msvc':
|
Chris@87
|
183 # the following code is not needed (read: breaks) when using MinGW
|
Chris@87
|
184 # in case want to link F77 compiled code with MSVC
|
Chris@87
|
185 opt.append('gcc')
|
Chris@87
|
186 runtime_lib = msvc_runtime_library()
|
Chris@87
|
187 if runtime_lib:
|
Chris@87
|
188 opt.append(runtime_lib)
|
Chris@87
|
189 if sys.platform == 'darwin':
|
Chris@87
|
190 opt.append('cc_dynamic')
|
Chris@87
|
191 return opt
|
Chris@87
|
192
|
Chris@87
|
193 def get_flags_debug(self):
|
Chris@87
|
194 return ['-g']
|
Chris@87
|
195
|
Chris@87
|
196 def get_flags_opt(self):
|
Chris@87
|
197 v = self.get_version()
|
Chris@87
|
198 if v and v<='3.3.3':
|
Chris@87
|
199 # With this compiler version building Fortran BLAS/LAPACK
|
Chris@87
|
200 # with -O3 caused failures in lib.lapack heevr,syevr tests.
|
Chris@87
|
201 opt = ['-O2']
|
Chris@87
|
202 else:
|
Chris@87
|
203 opt = ['-O3']
|
Chris@87
|
204 opt.append('-funroll-loops')
|
Chris@87
|
205 return opt
|
Chris@87
|
206
|
Chris@87
|
207 def _c_arch_flags(self):
|
Chris@87
|
208 """ Return detected arch flags from CFLAGS """
|
Chris@87
|
209 from distutils import sysconfig
|
Chris@87
|
210 try:
|
Chris@87
|
211 cflags = sysconfig.get_config_vars()['CFLAGS']
|
Chris@87
|
212 except KeyError:
|
Chris@87
|
213 return []
|
Chris@87
|
214 arch_re = re.compile(r"-arch\s+(\w+)")
|
Chris@87
|
215 arch_flags = []
|
Chris@87
|
216 for arch in arch_re.findall(cflags):
|
Chris@87
|
217 arch_flags += ['-arch', arch]
|
Chris@87
|
218 return arch_flags
|
Chris@87
|
219
|
Chris@87
|
220 def get_flags_arch(self):
|
Chris@87
|
221 return []
|
Chris@87
|
222
|
Chris@87
|
223 def runtime_library_dir_option(self, dir):
|
Chris@87
|
224 return '-Wl,-rpath="%s"' % dir
|
Chris@87
|
225
|
Chris@87
|
226 class Gnu95FCompiler(GnuFCompiler):
|
Chris@87
|
227 compiler_type = 'gnu95'
|
Chris@87
|
228 compiler_aliases = ('gfortran',)
|
Chris@87
|
229 description = 'GNU Fortran 95 compiler'
|
Chris@87
|
230
|
Chris@87
|
231 def version_match(self, version_string):
|
Chris@87
|
232 v = self.gnu_version_match(version_string)
|
Chris@87
|
233 if not v or v[0] != 'gfortran':
|
Chris@87
|
234 return None
|
Chris@87
|
235 v = v[1]
|
Chris@87
|
236 if v>='4.':
|
Chris@87
|
237 # gcc-4 series releases do not support -mno-cygwin option
|
Chris@87
|
238 pass
|
Chris@87
|
239 else:
|
Chris@87
|
240 # use -mno-cygwin flag for gfortran when Python is not Cygwin-Python
|
Chris@87
|
241 if sys.platform == 'win32':
|
Chris@87
|
242 for key in ['version_cmd', 'compiler_f77', 'compiler_f90',
|
Chris@87
|
243 'compiler_fix', 'linker_so', 'linker_exe']:
|
Chris@87
|
244 self.executables[key].append('-mno-cygwin')
|
Chris@87
|
245 return v
|
Chris@87
|
246
|
Chris@87
|
247 # 'gfortran --version' results:
|
Chris@87
|
248 # XXX is the below right?
|
Chris@87
|
249 # Debian: GNU Fortran 95 (GCC 4.0.3 20051023 (prerelease) (Debian 4.0.2-3))
|
Chris@87
|
250 # GNU Fortran 95 (GCC) 4.1.2 20061115 (prerelease) (Debian 4.1.1-21)
|
Chris@87
|
251 # OS X: GNU Fortran 95 (GCC) 4.1.0
|
Chris@87
|
252 # GNU Fortran 95 (GCC) 4.2.0 20060218 (experimental)
|
Chris@87
|
253 # GNU Fortran (GCC) 4.3.0 20070316 (experimental)
|
Chris@87
|
254
|
Chris@87
|
255 possible_executables = ['gfortran', 'f95']
|
Chris@87
|
256 executables = {
|
Chris@87
|
257 'version_cmd' : ["<F90>", "--version"],
|
Chris@87
|
258 'compiler_f77' : [None, "-Wall", "-g", "-ffixed-form",
|
Chris@87
|
259 "-fno-second-underscore"] + _EXTRAFLAGS,
|
Chris@87
|
260 'compiler_f90' : [None, "-Wall", "-g",
|
Chris@87
|
261 "-fno-second-underscore"] + _EXTRAFLAGS,
|
Chris@87
|
262 'compiler_fix' : [None, "-Wall", "-g","-ffixed-form",
|
Chris@87
|
263 "-fno-second-underscore"] + _EXTRAFLAGS,
|
Chris@87
|
264 'linker_so' : ["<F90>", "-Wall", "-g"],
|
Chris@87
|
265 'archiver' : ["ar", "-cr"],
|
Chris@87
|
266 'ranlib' : ["ranlib"],
|
Chris@87
|
267 'linker_exe' : [None, "-Wall"]
|
Chris@87
|
268 }
|
Chris@87
|
269
|
Chris@87
|
270 module_dir_switch = '-J'
|
Chris@87
|
271 module_include_switch = '-I'
|
Chris@87
|
272
|
Chris@87
|
273 g2c = 'gfortran'
|
Chris@87
|
274
|
Chris@87
|
275 def _universal_flags(self, cmd):
|
Chris@87
|
276 """Return a list of -arch flags for every supported architecture."""
|
Chris@87
|
277 if not sys.platform == 'darwin':
|
Chris@87
|
278 return []
|
Chris@87
|
279 arch_flags = []
|
Chris@87
|
280 # get arches the C compiler gets.
|
Chris@87
|
281 c_archs = self._c_arch_flags()
|
Chris@87
|
282 if "i386" in c_archs:
|
Chris@87
|
283 c_archs[c_archs.index("i386")] = "i686"
|
Chris@87
|
284 # check the arches the Fortran compiler supports, and compare with
|
Chris@87
|
285 # arch flags from C compiler
|
Chris@87
|
286 for arch in ["ppc", "i686", "x86_64", "ppc64"]:
|
Chris@87
|
287 if _can_target(cmd, arch) and arch in c_archs:
|
Chris@87
|
288 arch_flags.extend(["-arch", arch])
|
Chris@87
|
289 return arch_flags
|
Chris@87
|
290
|
Chris@87
|
291 def get_flags(self):
|
Chris@87
|
292 flags = GnuFCompiler.get_flags(self)
|
Chris@87
|
293 arch_flags = self._universal_flags(self.compiler_f90)
|
Chris@87
|
294 if arch_flags:
|
Chris@87
|
295 flags[:0] = arch_flags
|
Chris@87
|
296 return flags
|
Chris@87
|
297
|
Chris@87
|
298 def get_flags_linker_so(self):
|
Chris@87
|
299 flags = GnuFCompiler.get_flags_linker_so(self)
|
Chris@87
|
300 arch_flags = self._universal_flags(self.linker_so)
|
Chris@87
|
301 if arch_flags:
|
Chris@87
|
302 flags[:0] = arch_flags
|
Chris@87
|
303 return flags
|
Chris@87
|
304
|
Chris@87
|
305 def get_library_dirs(self):
|
Chris@87
|
306 opt = GnuFCompiler.get_library_dirs(self)
|
Chris@87
|
307 if sys.platform == 'win32':
|
Chris@87
|
308 c_compiler = self.c_compiler
|
Chris@87
|
309 if c_compiler and c_compiler.compiler_type == "msvc":
|
Chris@87
|
310 target = self.get_target()
|
Chris@87
|
311 if target:
|
Chris@87
|
312 d = os.path.normpath(self.get_libgcc_dir())
|
Chris@87
|
313 root = os.path.join(d, os.pardir, os.pardir, os.pardir, os.pardir)
|
Chris@87
|
314 mingwdir = os.path.normpath(os.path.join(root, target, "lib"))
|
Chris@87
|
315 full = os.path.join(mingwdir, "libmingwex.a")
|
Chris@87
|
316 if os.path.exists(full):
|
Chris@87
|
317 opt.append(mingwdir)
|
Chris@87
|
318 return opt
|
Chris@87
|
319
|
Chris@87
|
320 def get_libraries(self):
|
Chris@87
|
321 opt = GnuFCompiler.get_libraries(self)
|
Chris@87
|
322 if sys.platform == 'darwin':
|
Chris@87
|
323 opt.remove('cc_dynamic')
|
Chris@87
|
324 if sys.platform == 'win32':
|
Chris@87
|
325 c_compiler = self.c_compiler
|
Chris@87
|
326 if c_compiler and c_compiler.compiler_type == "msvc":
|
Chris@87
|
327 if "gcc" in opt:
|
Chris@87
|
328 i = opt.index("gcc")
|
Chris@87
|
329 opt.insert(i+1, "mingwex")
|
Chris@87
|
330 opt.insert(i+1, "mingw32")
|
Chris@87
|
331 # XXX: fix this mess, does not work for mingw
|
Chris@87
|
332 if is_win64():
|
Chris@87
|
333 c_compiler = self.c_compiler
|
Chris@87
|
334 if c_compiler and c_compiler.compiler_type == "msvc":
|
Chris@87
|
335 return []
|
Chris@87
|
336 else:
|
Chris@87
|
337 raise NotImplementedError("Only MS compiler supported with gfortran on win64")
|
Chris@87
|
338 return opt
|
Chris@87
|
339
|
Chris@87
|
340 def get_target(self):
|
Chris@87
|
341 status, output = exec_command(self.compiler_f77 +
|
Chris@87
|
342 ['-v'],
|
Chris@87
|
343 use_tee=0)
|
Chris@87
|
344 if not status:
|
Chris@87
|
345 m = TARGET_R.search(output)
|
Chris@87
|
346 if m:
|
Chris@87
|
347 return m.group(1)
|
Chris@87
|
348 return ""
|
Chris@87
|
349
|
Chris@87
|
350 def get_flags_opt(self):
|
Chris@87
|
351 if is_win64():
|
Chris@87
|
352 return ['-O0']
|
Chris@87
|
353 else:
|
Chris@87
|
354 return GnuFCompiler.get_flags_opt(self)
|
Chris@87
|
355
|
Chris@87
|
356 def _can_target(cmd, arch):
|
Chris@87
|
357 """Return true is the command supports the -arch flag for the given
|
Chris@87
|
358 architecture."""
|
Chris@87
|
359 newcmd = cmd[:]
|
Chris@87
|
360 fid, filename = tempfile.mkstemp(suffix=".f")
|
Chris@87
|
361 try:
|
Chris@87
|
362 d = os.path.dirname(filename)
|
Chris@87
|
363 output = os.path.splitext(filename)[0] + ".o"
|
Chris@87
|
364 try:
|
Chris@87
|
365 newcmd.extend(["-arch", arch, "-c", filename])
|
Chris@87
|
366 p = Popen(newcmd, stderr=STDOUT, stdout=PIPE, cwd=d)
|
Chris@87
|
367 p.communicate()
|
Chris@87
|
368 return p.returncode == 0
|
Chris@87
|
369 finally:
|
Chris@87
|
370 if os.path.exists(output):
|
Chris@87
|
371 os.remove(output)
|
Chris@87
|
372 finally:
|
Chris@87
|
373 os.remove(filename)
|
Chris@87
|
374 return False
|
Chris@87
|
375
|
Chris@87
|
376 if __name__ == '__main__':
|
Chris@87
|
377 from distutils import log
|
Chris@87
|
378 log.set_verbosity(2)
|
Chris@87
|
379
|
Chris@87
|
380 compiler = GnuFCompiler()
|
Chris@87
|
381 compiler.customize()
|
Chris@87
|
382 print(compiler.get_version())
|
Chris@87
|
383
|
Chris@87
|
384 try:
|
Chris@87
|
385 compiler = Gnu95FCompiler()
|
Chris@87
|
386 compiler.customize()
|
Chris@87
|
387 print(compiler.get_version())
|
Chris@87
|
388 except Exception:
|
Chris@87
|
389 msg = get_exception()
|
Chris@87
|
390 print(msg)
|