diff options
author | Pearu Peterson <pearu.peterson@gmail.com> | 2004-01-19 09:00:01 +0000 |
---|---|---|
committer | Pearu Peterson <pearu.peterson@gmail.com> | 2004-01-19 09:00:01 +0000 |
commit | c879ef7d77eb19cb4d795e4550573de9c58955b4 (patch) | |
tree | 1f4fefca21b78e13a10f133a5df586faa799347a /scipy_distutils/fcompiler.py | |
parent | 60612a648d3b108daf597dfbaee57c316fb0775f (diff) | |
download | numpy-c879ef7d77eb19cb4d795e4550573de9c58955b4.tar.gz |
Refactoring build_flib.py (to follow and use standard distutils conventions and tools). It's work in-progress. Sending the output of 'python fcompiler.py --verbose' would be a great help if you have other than intel and gnu compilers installed.
Diffstat (limited to 'scipy_distutils/fcompiler.py')
-rw-r--r-- | scipy_distutils/fcompiler.py | 679 |
1 files changed, 679 insertions, 0 deletions
diff --git a/scipy_distutils/fcompiler.py b/scipy_distutils/fcompiler.py new file mode 100644 index 000000000..b4bf950c8 --- /dev/null +++ b/scipy_distutils/fcompiler.py @@ -0,0 +1,679 @@ +"""scipy_distutils.fcompiler + +Contains FCompiler, an abstract base class that defines the interface +for the Scipy_istutils Fortran compiler abstraction model. + +""" + +import re +import os +import sys + +from distutils.version import StrictVersion +from distutils.ccompiler import CCompiler +# distutils.ccompiler provides the following functions: +# gen_preprocess_options(macros, include_dirs) +# gen_lib_options(compiler, library_dirs, runtime_library_dirs, libraries) +from distutils.errors import DistutilsModuleError,DistutilsArgError +from distutils.core import Command +from distutils.util import split_quoted +from distutils.fancy_getopt import FancyGetopt +from distutils.version import LooseVersion +from distutils import log +from distutils.sysconfig import get_config_var + +from exec_command import find_executable, exec_command + + +class FCompiler(CCompiler): + """ Abstract base class to define the interface that must be implemented + by real Fortran compiler classes. + + Methods that subclasses may redefine: + + get_version_cmd(), get_linker_so(), get_version() + get_flags(), get_flags_opt(), get_flags_arch(), get_flags_debug() + get_flags_f77(), get_flags_opt_f77(), get_flags_arch_f77(), + get_flags_debug_f77(), get_flags_f90(), get_flags_opt_f90(), + get_flags_arch_f90(), get_flags_debug_f90(), + get_flags_fix(), get_flags_linker_so(), get_flags_version() + + DON'T call these methods (except get_version) after + constructing a compiler instance or inside any other method. + All methods, except get_version_cmd() and get_flags_version(), may + call get_version() method. + + After constructing a compiler instance, always call customize(dist=None) + method that finalizes compiler construction and makes the following + attributes available: + compiler_f77 + compiler_f90 + compiler_fix + linker_so + archiver + ranlib + libraries + library_dirs + """ + # CCompiler defines the following attributes: + # compiler_type + # src_extensions + # obj_extension + # static_lib_extension + # shared_lib_extension + # static_lib_format + # shared_lib_format + # exe_extension + # language_map ### REDEFINED + # language_order ### REDEFINED + # and the following public methods: + # set_executables(**args) + # set_executable(key,value) + # define_macro(name, value=None) + # undefine_macro(name) + # add_include_dir(dir) + # set_include_dirs(dirs) + # add_library(libname) + # set_libraries(libnames) + # add_library_dir(dir) + # set_library_dirs(dirs) + # add_runtime_library_dir(dir) + # set_runtime_library_dirs(dirs) + # add_link_object(object) + # set_link_objects(objects) + # + # detect_language(sources) ### USABLE + # + # preprocess(source,output_file=None,macros=None,include_dirs=None, + # extra_preargs=None,extra_postargs=None) + # compile(sources, output_dir=None, macros=None, + # include_dirs=None, debug=0, extra_preargs=None, + # extra_postargs=None, depends=None) + # create_static_lib(objects,output_libname,output_dir=None,debug=0,target_lang=None): + # link(target_desc, objects, output_filename, output_dir=None, + # libraries=None, library_dirs=None, runtime_library_dirs=None, + # export_symbols=None, debug=0, extra_preargs=None, extra_postargs=None, + # build_temp=None, target_lang=None) + # link_shared_lib(objects, output_libname, output_dir=None, + # libraries=None, library_dirs=None, runtime_library_dirs=None, + # export_symbols=None, debug=0, extra_preargs=None, + # extra_postargs=None, build_temp=None, target_lang=None) + # link_shared_object(objects,output_filename,output_dir=None, + # libraries=None,library_dirs=None,runtime_library_dirs=None, + # export_symbols=None,debug=0,extra_preargs=None, + # extra_postargs=None,build_temp=None,target_lang=None) + # link_executable(objects,output_progname,output_dir=None, + # libraries=None,library_dirs=None,runtime_library_dirs=None, + # debug=0,extra_preargs=None,extra_postargs=None,target_lang=None) + # + # library_dir_option(dir) + # runtime_library_dir_option(dir) + # library_option(lib) + # has_function(funcname,includes=None,include_dirs=None, + # libraries=None,library_dirs=None) + # find_library_file(dirs, lib, debug=0) + # + # object_filenames(source_filenames, strip_dir=0, output_dir='') + # shared_object_filename(basename, strip_dir=0, output_dir='') + # executable_filenamee(basename, strip_dir=0, output_dir='') + # library_filename(libname, lib_type='static',strip_dir=0, output_dir=''): + # + # announce(msg, level=1) + # debug_print(msg) + # warn(msg) + # execute(func, args, msg=None, level=1) + # spawn(cmd) + # move_file(src,dst) + # mkpath(name, mode=0777) + # + + src_extensions = ['.for','.ftn','.f77','.f','.f90','.f95'] + + language_map = {'.f':'f77', + '.for':'f77', + '.ftn':'f77', + '.f77':'f77', + '.f90':'f90', + '.f95':'f90'} + language_order = ['f90','f77'] + + version_pattern = None + + executables = { + 'version_cmd' : ["f77","-v"], + 'compiler_f77' : ["f77"], + 'compiler_f90' : ["f90"], + 'compiler_fix' : ["f90","-fixed"], + 'linker_so' : ["f90","-shared"], + 'archiver' : ["ar","-cr"], + 'ranlib' : None, + } + + compile_switch = "-c " # Ending space matters! + object_switch = "-o " # Ending space matters! + object_extension = ".o" + shared_lib_extension = get_config_var('SO') + static_lib_extension = ".a" + static_lib_format = "lib%s%s" + shared_lib_format = "%s%s" + + def __init__(self,verbose=0,dry_run=0,force=0): + # Set the following attributes (see ccompiler.py for explanations): + # output_dir + # macros + # include_dirs + # libraries + # library_dirs + # runtime_library_dirs + # objects + # and call set_executables. + CCompiler.__init__(self,verbose,dry_run,force) + + ## Methods that subclasses may redefine. But don't call these methods! + ## They are private to FCompiler class and may return unexpected + ## results if used elsewhere. So, you have been warned.. + + def get_version_cmd(self): + """ Compiler command to print out version information. """ + f77 = self.executables['compiler_f77'] + if f77 is not None: + f77 = f77[0] + cmd = self.executables['version_cmd'] + if cmd is not None: + cmd = cmd[0] + if cmd==f77: + cmd = self.compiler_f77[0] + else: + f90 = self.executables['compiler_f90'] + if f90 is not None: + f90 = f90[0] + if cmd==f90: + cmd = self.compiler_f90[0] + return cmd + + def get_linker_so(self): + """ Linker command to build shared libraries. """ + f77 = self.executables['compiler_f77'] + if f77 is not None: + f77 = f77[0] + ln = self.executables['linker_so'] + if ln is not None: + ln = ln[0] + if ln==f77: + ln = self.compiler_f77[0] + else: + f90 = self.executables['compiler_f90'] + if f90 is not None: + f90 = f90[0] + if ln==f90: + ln = self.compiler_f90[0] + return ln + + def get_flags(self): + """ List of flags common to all compiler types. """ + return [] + def get_flags_version(self): + """ List of compiler flags to print out version information. """ + if self.executables['version_cmd']: + return self.executables['version_cmd'][1:] + return [] + def get_flags_f77(self): + """ List of Fortran 77 specific flags. """ + if self.executables['compiler_f77']: + return self.executables['compiler_f77'][1:] + return [] + def get_flags_f90(self): + """ List of Fortran 90 specific flags. """ + if self.executables['compiler_f90']: + return self.executables['compiler_f90'][1:] + return [] + def get_flags_fix(self): + """ List of Fortran 90 fixed format specific flags. """ + if self.executables['compiler_fix']: + return self.executables['compiler_fix'][1:] + return [] + def get_flags_linker_so(self): + """ List of linker flags to build a shared library. """ + if self.executables['linker_so']: + return self.executables['linker_so'][1:] + return [] + def get_flags_ar(self): + """ List of archiver flags. """ + if self.executables['archiver']: + return self.executables['archiver'][1:] + return [] + def get_flags_opt(self): + """ List of architecture independent compiler flags. """ + return [] + def get_flags_arch(self): + """ List of architecture dependent compiler flags. """ + return [] + def get_flags_debug(self): + """ List of compiler flags to compile with debugging information. """ + return [] + get_flags_opt_f77 = get_flags_opt_f90 = get_flags_opt + get_flags_arch_f77 = get_flags_arch_f90 = get_flags_arch + get_flags_debug_f77 = get_flags_debug_f90 = get_flags_debug + + def get_libraries(self): + """ List of compiler libraries. """ + return self.libraries[:] + def get_library_dirs(self): + """ List of compiler library directories. """ + return self.library_dirs[:] + + def get_version(self, force=0, ok_status=[0]): + """ Compiler version. Returns None if compiler is not available. """ + if not force and hasattr(self,'version'): + return self.version + + cmd = ' '.join(self.version_cmd) + status, output = self.exec_command(cmd,use_tee=0) + version = None + if status in ok_status: + m = re.match(self.version_pattern,output) + if m: + version = m.group('version') + assert version,`version` + version = LooseVersion(version) + self.version = version + return version + + ############################################################ + + ## Public methods: + + def customize(self, dist=None): + """ Customize Fortran compiler. + + This method gets Fortran compiler specific information from + (i) class definition, (ii) environment, (iii) distutils config + files, and (iv) command line. + + This method should be always called after constructing a + compiler instance. But not in __init__ because Distribution + instance is needed for (iii) and (iv). + """ + if dist is None: + # These hooks are for testing only! + from dist import Distribution + dist = Distribution() + dist.script_name = os.path.basename(sys.argv[0]) + dist.script_args = ['config_fc'] + sys.argv[1:] + dist.cmdclass['config_fc'] = config_fc + dist.parse_config_files() + dist.parse_command_line() + + conf = dist.get_option_dict('config_fc') + + noopt = conf.get('noopt',[None,0])[1] + noarch = conf.get('noarch',[None,noopt])[1] + debug = conf.get('debug',[None,0])[1] + + f77 = self.__get_cmd('compiler_f77','F77',(conf,'f77_exec')) + f90 = self.__get_cmd('compiler_f90','F90',(conf,'f90_exec')) + # Temporarily setting f77,f90 compilers so that + # version_cmd can use their executables. + if f77: + self.set_executables(compiler_f77=[f77]) + if f90: + self.set_executables(compiler_f90=[f90]) + + # Must set version_cmd before others as self.get_flags* + # methods may call self.get_version. + vers_cmd = self.__get_cmd(self.get_version_cmd) + if vers_cmd: + vflags = self.__get_flags(self.get_flags_version) + self.set_executables(version_cmd=[vers_cmd]+vflags) + + if f77: + f77flags = self.__get_flags(self.get_flags_f77,'F77FLAGS', + (conf,'f77flags')) + if f90: + f90flags = self.__get_flags(self.get_flags_f90,'F90FLAGS', + (conf,'f90flags')) + + # XXX Assuming that free format is default for f90 compiler. + fix = self.__get_cmd('compiler_fix','F90',(conf,'f90_exec')) + if fix: + fixflags = self.__get_flags(self.get_flags_fix) + f90flags + + oflags,aflags,dflags = [],[],[] + if not noopt: + oflags = self.__get_flags(self.get_flags_opt,'FOPT',(conf,'opt')) + if f77 and self.get_flags_opt is not self.get_flags_opt_f77: + f77flags += self.__get_flags(self.get_flags_opt_f77) + if f90 and self.get_flags_opt is not self.get_flags_opt_f90: + f90flags += self.__get_flags(self.get_flags_opt_f90) + if fix and self.get_flags_opt is not self.get_flags_opt_f90: + fixflags += self.__get_flags(self.get_flags_opt_f90) + if not noarch: + aflags = self.__get_flags(self.get_flags_arch,'FARCH', + (conf,'arch')) + if f77 and self.get_flags_arch is not self.get_flags_arch_f77: + f77flags += self.__get_flags(self.get_flags_arch_f77) + if f90 and self.get_flags_arch is not self.get_flags_arch_f90: + f90flags += self.__get_flags(self.get_flags_arch_f90) + if fix and self.get_flags_arch is not self.get_flags_arch_f90: + fixflags += self.__get_flags(self.get_flags_arch_f90) + if debug: + dflags = self.__get_flags(self.get_flags_debug,'FDEBUG') + if f77 and self.get_flags_debug is not self.get_flags_debug_f77: + f77flags += self.__get_flags(self.get_flags_debug_f77) + if f90 and self.get_flags_debug is not self.get_flags_debug_f90: + f90flags += self.__get_flags(self.get_flags_debug_f90) + if fix and self.get_flags_debug is not self.get_flags_debug_f90: + fixflags += self.__get_flags(self.get_flags_debug_f90) + + fflags = self.__get_flags(self.get_flags,'FFLAGS') \ + + dflags + oflags + aflags + + if f77: + self.set_executables(compiler_f77=[f77]+f77flags+fflags) + if f90: + self.set_executables(compiler_f90=[f90]+f90flags+fflags) + if fix: + self.set_executables(compiler_fix=[fix]+fixflags+fflags) + + linker_so = self.__get_cmd(self.get_linker_so,'LDSHARED') + if linker_so: + linker_so_flags = self.__get_flags(self.get_flags_linker_so,'LDFLAGS') + self.set_executables(linker_so=[linker_so]+linker_so_flags) + + ar = self.__get_cmd('archiver','AR') + if ar: + arflags = self.__get_flags(self.get_flags_ar,'ARFLAGS') + self.set_executables(archiver=[ar]+arflags) + + ranlib = self.__get_cmd('ranlib','RANLIB') + if ranlib: + self.set_executables(ranlib=[ranlib]) + + self.set_library_dirs(self.get_library_dirs()) + self.set_libraries(self.get_libraries()) + + verbose = conf.get('verbose',[None,0])[1] + if verbose: + self.dump_properties() + + def dump_properties(self): + """ Print out the attributes of a compiler instance. """ + props = [] + for key in self.executables.keys() + \ + ['version','libraries','library_dirs', + 'object_switch','compile_switch']: + if hasattr(self,key): + v = getattr(self,key) + props.append((key, None, '= '+`v`)) + props.sort() + + pretty_printer = FancyGetopt(props) + for l in pretty_printer.generate_help("%s instance properties:" \ + % (self.__class__.__name__)): + if l[:4]==' --': + l = ' ' + l[4:] + print l + + def exec_command(self,*args,**kws): + """ Return status,output of a command. """ + log.info('%s.exec_command(*%s,**%s)' % (self.__class__.__name__,args,kws)) + status, output = exec_command(*args,**kws) + log.info('*****status:%s\n*****output:\n%s\n*****' % (status,output)) + return status, output + + ############################################################ + + ## Private methods: + + def __get_cmd(self, command, envvar=None, confvar=None): + if command is None: + var = None + elif type(command) is type(''): + var = self.executables[command] + if var is not None: + var = var[0] + else: + var = command() + if envvar is not None: + var = os.environ.get(envvar, var) + if confvar is not None: + var = confvar[0].get(confvar[1], [None,var])[1] + return var + + def __get_flags(self, command, envvar=None, confvar=None): + if command is None: + var = [] + elif type(command) is type(''): + var = self.executables[command][1:] + else: + var = command() + if envvar is not None: + var = os.environ.get(envvar, var) + if confvar is not None: + var = confvar[0].get(confvar[1], [None,var])[1] + if type(var) is type(''): + var = split_quoted(var) + return var + + ## class FCompiler + +fcompiler_class = {'gnu':('gnufcompiler','GnuFCompiler', + "GNU Fortran Compiler"), + 'pg':('pgfcompiler','PGroupFCompiler', + "Portland Group Fortran Compiler"), + 'absoft':('absoftfcompiler','AbsoftFCompiler', + "Absoft Corp Fortran Compiler"), + 'mips':('mipsfcompiler','MipsFCompiler', + "MIPSpro Fortran Compiler"), + 'forte':('fortefcompiler','ForteFCompiler', + "Forte Fortran 95 Compiler"), + 'sun':('sunfcompiler','SunFCompiler', + "Sun Fortran Compiler"), + 'intel':('intelfcompiler','IntelFCompiler', + "Intel Fortran Compiler for 32-bit apps"), + 'intelv':('intelfcompiler','IntelVisualFCompiler', + "Intel Visual Fortran Compiler for 32-bit apps"), + 'intelitanium':('intelfcompiler','IntelItaniumFCompiler', + "Intel Fortran Compiler for Itanium apps"), + 'nag':('nagfcompiler','NAGFCompiler', + "NAGWare Fortran 95 Compiler"), + 'compaq':('compaqfcompiler','CompaqFCompiler', + "Compaq Fortran Compiler"), + 'compaqv':('compaqfcompiler','CompaqVisualFCompiler', + "DIGITAL|Compaq Visual Fortran Compiler"), + 'vast':('vastfcompiler','VastFCompiler', + "Pacific-Sierra Research Fortran 90 Compiler"), + 'hpux':('hpuxfcompiler','HPUXFCompiler', + "HP Fortran 90 Compiler"), + 'lahey':('laheyfcompiler','LaheyFCompiler', + "Lahey/Fujitsu Fortran 95 Compiler"), + 'f':('fcompiler','FFCompiler', + "Fortran Company/NAG F Compiler"), + } + +_default_compilers = ( + # Platform mappings + ('win32',('gnu','intelv','absoft','compaqv','intelitanium')), + ('cygwin.*',('gnu','intelv','absoft','compaqv','intelitanium')), + ('linux.*',('gnu','intel','lahey','pg','absoft','nag','vast','compaq', + 'intelitanium')), + ('sunos.*',('forte','gnu','sun')), + ('irix',('mips','gnu')), + # OS mappings + ('posix',('gnu',)), + ('nt',('gnu',)), + ('mac',('gnu',)), + ) + +def get_default_fcompiler(osname=None, platform=None): + """ Determine the default Fortran compiler to use for the given platform. """ + if osname is None: + osname = os.name + if platform is None: + platform = sys.platform + for pattern, compiler in _default_compilers: + if re.match(pattern, platform) is not None or \ + re.match(pattern, osname) is not None: + if type(compiler) is type(()): + return compiler[0] + return compiler + return 'gnu' + +def new_fcompiler(plat=None, + compiler=None, + verbose=0, + dry_run=0, + force=0): + """ Generate an instance of some FCompiler subclass for the supplied + platform/compiler combination. + """ + if plat is None: + plat = os.name + try: + if compiler is None: + compiler = get_default_fcompiler(plat) + (module_name, class_name, long_description) = fcompiler_class[compiler] + except KeyError: + msg = "don't know how to compile Fortran code on platform '%s'" % plat + if compiler is not None: + msg = msg + " with '%s' compiler" % compiler + raise DistutilsPlatformError, msg + + try: + module_name = module_name + __import__ (module_name) + module = sys.modules[module_name] + klass = vars(module)[class_name] + except ImportError: + raise DistutilsModuleError, \ + "can't compile Fortran code: unable to load module '%s'" % \ + module_name + except KeyError: + raise DistutilsModuleError, \ + ("can't compile Fortran code: unable to find class '%s' " + + "in module '%s'") % (class_name, module_name) + + return klass(None, dry_run, force) + + +def show_fcompilers(dist = None): + """ Print list of available compilers (used by the "--help-fcompiler" + option to "config_fc"). + """ + if dist is None: + from dist import Distribution + dist = Distribution() + dist.script_name = os.path.basename(sys.argv[0]) + dist.script_args = ['config_fc'] + sys.argv[1:] + dist.cmdclass['config_fc'] = config_fc + dist.parse_config_files() + dist.parse_command_line() + + compilers = [] + compilers_na = [] + compilers_ni = [] + for compiler in fcompiler_class.keys(): + v = 'N/A' + try: + c = new_fcompiler(compiler=compiler) + c.customize(dist) + v = c.get_version() + except DistutilsModuleError: + pass + except: + print sys.exc_info()[0],sys.exc_info()[1] + if v is None: + compilers_na.append(("fcompiler="+compiler, None, + fcompiler_class[compiler][2])) + elif v=='N/A': + compilers_ni.append(("fcompiler="+compiler, None, + fcompiler_class[compiler][2])) + else: + compilers.append(("fcompiler="+compiler, None, + fcompiler_class[compiler][2] + ' (%s)' % v)) + compilers.sort() + compilers_na.sort() + pretty_printer = FancyGetopt(compilers) + pretty_printer.print_help("List of available Fortran compilers:") + pretty_printer = FancyGetopt(compilers_na) + pretty_printer.print_help("List of unavailable Fortran compilers:") + if compilers_ni: + pretty_printer = FancyGetopt(compilers_ni) + pretty_printer.print_help("List of unimplemented Fortran compilers:") + print "For compiler details, run 'config_fc --verbose' setup command." + +class config_fc(Command): + """ Distutils command to hold user specified options + to Fortran compilers. + + This is used in FCompiler.customize() method. + """ + + user_options = [ + ('fcompiler=',None,"specify Fortran compiler type"), + ('f77-exec=', None, "specify F77 compiler command"), + ('f90-exec=', None, "specify F90 compiler command"), + ('f77flags=',None,"specify F77 compiler flags"), + ('f90flags=',None,"specify F90 compiler flags"), + ('opt=',None,"specify optimization flags"), + ('arch=',None,"specify architecture specific optimization flags"), + ('debug','g',"compile with debugging information"), + ('noopt',None,"compile without optimization"), + ('noarch',None,"compile without arch-dependent optimization"), + ] + + boolean_options = ['debug','noopt','noarch'] + + help_options = [ + ('help-fcompiler', None, + "list available Fortran compilers", show_fcompilers), + ] + +def dummy_fortran_file(): + import tempfile + dummy_name = tempfile.mktemp()+'__dummy' + dummy = open(dummy_name+'.f','w') + dummy.write(" subroutine dummy()\n end\n") + dummy.close() + import atexit + from distutils import log + def rm_file(name=dummy_name,log_threshold=log._global_log.threshold): + save_th = log._global_log.threshold + log.set_threshold(log_threshold) + try: os.remove(name+'.f'); log.debug('removed '+name+'.f') + except OSError: pass + try: os.remove(name+'.o'); log.debug('removed '+name+'.o') + except OSError: pass + log.set_threshold(save_th) + atexit.register(rm_file) + return dummy_name + +is_f_file = re.compile(r'.*[.](for|ftn|f77|f)\Z',re.I).match +_has_f_header = re.compile(r'-[*]-\s*fortran\s*-[*]-',re.I).search +_has_f90_header = re.compile(r'-[*]-\s*f90\s*-[*]-',re.I).search +_free_f90_start = re.compile(r'[^c*][^\s\d\t]',re.I).match +def is_free_format(file): + """Check if file is in free format Fortran.""" + # f90 allows both fixed and free format, assuming fixed unless + # signs of free format are detected. + result = 0 + f = open(file,'r') + line = f.readline() + n = 15 # the number of non-comment lines to scan for hints + if _has_f_header(line): + n = 0 + elif _has_f90_header(line): + n = 0 + result = 1 + while n>0 and line: + if line[0]!='!': + n -= 1 + if _free_f90_start(line[:5]) or line[-2:-1]=='&': + result = 1 + break + line = f.readline() + f.close() + return result + +if __name__ == '__main__': + show_fcompilers() |