# Added Fortran compiler support to config. Currently useful only for # try_compile call. try_run works but is untested for most of Fortran # compilers (they must define linker_exe first). # Pearu Peterson import os, signal from distutils.command.config import config as old_config from distutils.command.config import LANG_EXT from distutils import log from distutils.file_util import copy_file from numpy.distutils.exec_command import exec_command LANG_EXT['f77'] = '.f' LANG_EXT['f90'] = '.f90' class config(old_config): old_config.user_options += [ ('fcompiler=', None, "specify the Fortran compiler type"), ] def initialize_options(self): self.fcompiler = None old_config.initialize_options(self) def _check_compiler (self): old_config._check_compiler(self) from numpy.distutils.fcompiler import FCompiler, new_fcompiler if not isinstance(self.fcompiler, FCompiler): self.fcompiler = new_fcompiler(compiler=self.fcompiler, dry_run=self.dry_run, force=1, c_compiler=self.compiler) if self.fcompiler is not None: self.fcompiler.customize(self.distribution) if self.fcompiler.get_version(): self.fcompiler.customize_cmd(self) self.fcompiler.show_customization() def _wrap_method(self,mth,lang,args): from distutils.ccompiler import CompileError from distutils.errors import DistutilsExecError save_compiler = self.compiler if lang in ['f77','f90']: self.compiler = self.fcompiler try: ret = mth(*((self,)+args)) except (DistutilsExecError,CompileError),msg: self.compiler = save_compiler raise CompileError self.compiler = save_compiler return ret def _compile (self, body, headers, include_dirs, lang): return self._wrap_method(old_config._compile,lang, (body, headers, include_dirs, lang)) def _link (self, body, headers, include_dirs, libraries, library_dirs, lang): if self.compiler.compiler_type=='msvc': libraries = (libraries or [])[:] library_dirs = (library_dirs or [])[:] if lang in ['f77','f90']: lang = 'c' # always use system linker when using MSVC compiler if self.fcompiler: for d in self.fcompiler.library_dirs or []: # correct path when compiling in Cygwin but with # normal Win Python if d.startswith('/usr/lib'): s,o = exec_command(['cygpath', '-w', d], use_tee=False) if not s: d = o library_dirs.append(d) for libname in self.fcompiler.libraries or []: if libname not in libraries: libraries.append(libname) for libname in libraries: if libname.startswith('msvc'): continue fileexists = False for libdir in library_dirs or []: libfile = os.path.join(libdir,'%s.lib' % (libname)) if os.path.isfile(libfile): fileexists = True break if fileexists: continue # make g77-compiled static libs available to MSVC fileexists = False for libdir in library_dirs: libfile = os.path.join(libdir,'lib%s.a' % (libname)) if os.path.isfile(libfile): # copy libname.a file to name.lib so that MSVC linker # can find it libfile2 = os.path.join(libdir,'%s.lib' % (libname)) copy_file(libfile, libfile2) self.temp_files.append(libfile2) fileexists = True break if fileexists: continue log.warn('could not find library %r in directories %s' \ % (libname, library_dirs)) return self._wrap_method(old_config._link,lang, (body, headers, include_dirs, libraries, library_dirs, lang)) def check_decl(self, symbol, headers=None, include_dirs=None): self._check_compiler() body = """ int main() { #ifndef %s (void) %s; #endif ; return 0; }""" % (symbol, symbol) return self.try_compile(body, headers, include_dirs) def check_func(self, func, headers=None, include_dirs=None, libraries=None, library_dirs=None, decl=False, call=False, call_args=None): # clean up distutils's config a bit: add void to main(), and # return a value. self._check_compiler() body = [] if decl: body.append("int %s ();" % func) body.append("int main (void) {") if call: if call_args is None: call_args = '' body.append(" %s(%s);" % (func, call_args)) else: body.append(" %s;" % func) body.append(" return 0;") body.append("}") body = '\n'.join(body) + "\n" return self.try_link(body, headers, include_dirs, libraries, library_dirs) def get_output(self, body, headers=None, include_dirs=None, libraries=None, library_dirs=None, lang="c"): """Try to compile, link to an executable, and run a program built from 'body' and 'headers'. Returns the exit status code of the program and its output. """ from distutils.ccompiler import CompileError, LinkError self._check_compiler() exitcode, output = 255, '' try: src, obj, exe = self._link(body, headers, include_dirs, libraries, library_dirs, lang) exe = os.path.join('.', exe) exitstatus, output = exec_command(exe, execute_in='.') if hasattr(os, 'WEXITSTATUS'): exitcode = os.WEXITSTATUS(exitstatus) if os.WIFSIGNALED(exitstatus): sig = os.WTERMSIG(exitstatus) log.error('subprocess exited with signal %d' % (sig,)) if sig == signal.SIGINT: # control-C raise KeyboardInterrupt else: exitcode = exitstatus log.info("success!") except (CompileError, LinkError): log.info("failure.") self._clean() return exitcode, output