Este artigo detalha a implementação de compiladores Fortran dentro do subsistema numpy.distutils, focando em como diferentes compiladores e suas configurações são gerenciadso.
# .\numpy\numpy\distutils\fcompiler\sun.py
from numpy.distutils.ccompiler import simple_version_match
from numpy.distutils.fcompiler import FCompiler
compilers = ['SunFCompiler']
class SunFCompiler(FCompiler):
compiler_type = 'sun'
description = 'Sun or Forte Fortran 95 Compiler'
version_match = simple_version_match(
start=r'f9[05]: (Sun|Forte|WorkShop).*Fortran 95')
executables = {
'version_cmd' : ["<F90>", "-V"],
'compiler_f77' : ["f90"],
'compiler_fix' : ["f90", "-fixed"],
'compiler_f90' : ["f90"],
'linker_so' : ["<F90>", "-Bdynamic", "-G"],
'archiver' : ["ar", "-cr"],
'ranlib' : ["ranlib"]
}
module_dir_switch = '-moddir='
module_include_switch = '-M'
pic_flags = ['-xcode=pic32']
def get_flags_f77(self):
ret = ["-ftrap=%none"]
if (self.get_version() or '') >= '7':
ret.append("-f77")
else:
ret.append("-fixed")
return ret
def get_opt(self):
return ['-fast', '-dalign']
def get_arch(self):
return ['-xtarget=generic']
def get_libraries(self):
opt = []
opt.extend(['fsu', 'sunmath', 'mvec'])
return opt
def runtime_library_dir_option(self, dir):
return '-R%s' % dir
if __name__ == '__main__':
from distutils import log
log.set_verbosity(2)
from numpy.distutils import customized_fcompiler
print(customized_fcompiler(compiler='sun').get_version())
O arquivo sun.py define o compilador SunFCompiler, que é usado para compilar código Fortran com compiladores Sun ou Forte. Ele especifica os comandos executáveis, flags de compilação e opções de biblioteca específicas para este compilador.
# .\numpy\numpy\distutils\fcompiler\vast.py
import os
from numpy.distutils.fcompiler.gnu import GnuFCompiler
compilers = ['VastFCompiler']
class VastFCompiler(GnuFCompiler):
compiler_type = 'vast'
compiler_aliases = ()
description = 'Pacific-Sierra Research Fortran 90 Compiler'
version_pattern = (r'\s*Pacific-Sierra Research vf90 '
r'(Personal|Professional)\s+(?P<version>[^\s]*)')
object_switch = ' && function _mvfile { mv -v `basename $1` $1 ; } && _mvfile '
executables = {
'version_cmd' : ["vf90", "-v"],
'compiler_f77' : ["g77"],
'compiler_fix' : ["f90", "-Wv,-ya"],
'compiler_f90' : ["f90"],
'linker_so' : ["<F90>"],
'archiver' : ["ar", "-cr"],
'ranlib' : ["ranlib"]
}
module_dir_switch = None
module_include_switch = None
def find_executables(self):
pass
def get_version_cmd(self):
f90 = self.compiler_f90[0]
d, b = os.path.split(f90)
vf90 = os.path.join(d, 'v'+b)
return vf90
def get_flags_arch(self):
vast_version = self.get_version()
gnu = GnuFCompiler()
gnu.customize(None)
self.version = gnu.get_version()
opt = GnuFCompiler.get_flags_arch(self)
self.version = vast_version
return opt
if __name__ == '__main__':
from distutils import log
log.set_verbosity(2)
from numpy.distutils import customized_fcompiler
print(customized_fcompiler(compiler='vast').get_version())
O arquivo vast.py implementa o compilador VastFCompiler, que é uma variante do compilador GNU Fortran. Ele lida com a particularidade de que o compilador VAST F90 não suporta o uso conjunto de -o e -c, movendo arquivos objeto criados no diretório atual para o diretório de compilação.
# .\numpy\numpy\distutils\fcompiler\__init__.py
"""
Contains FCompiler, an abstract base class that defines the interface
for the numpy.distutils Fortran compiler abstraction model.
Terminology:
To be consistent, where the term 'executable' is used, it means the single
file, like 'gcc', that is executed, and should be a string. In contrast,
'command' means the entire command line, like ['gcc', '-c', 'file.c'], and
should be a list.
But note that FCompiler.executables is actually a dictionary of commands.
"""
__all__ = ['FCompiler', 'new_fcompiler', 'show_fcompilers',
'dummy_fortran_file']
import os
import sys
import re
from pathlib import Path
from distutils.sysconfig import get_python_lib
from distutils.fancy_getopt import FancyGetopt
from distutils.errors import DistutilsModuleError, \
DistutilsExecError, CompileError, LinkError, DistutilsPlatformError
from distutils.util import split_quoted, strtobool
from numpy.distutils.ccompiler import CCompiler, gen_lib_options
from numpy.distutils import log
from numpy.distutils.misc_util import is_string, all_strings, is_sequence, \
make_temp_file, get_shared_lib_extension
from numpy.distutils.exec_command import find_executable
from numpy.distutils import _shell_utils
from .environment import EnvironmentConfig
__metaclass__ = type
FORTRAN_COMMON_FIXED_EXTENSIONS = ['.for', '.ftn', '.f77', '.f']
class CompilerNotFound(Exception):
pass
def flaglist(s):
if is_string(s):
return split_quoted(s)
else:
return s
def str2bool(s):
if is_string(s):
return strtobool(s)
return bool(s)
def is_sequence_of_strings(seq):
return is_sequence(seq) and all_strings(seq)
class FCompiler(CCompiler):
"""Abstract base class to define the interface that must be implemented
by real Fortran compiler classes.
Methods that subclasses may redefine:
update_executables(), find_executables(), 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()
DON'T call these methods (except get_version) after
constructing a compiler instance or inside any other method.
All methods, except update_executables() and find_executables(),
may call the 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
"""
distutils_vars = EnvironmentConfig(
distutils_section='config_fc',
noopt = (None, None, 'noopt', str2bool, False),
noarch = (None, None, 'noarch', str2bool, False),
debug = (None, None, 'debug', str2bool, False),
verbose = (None, None, 'verbose', str2bool, False),
)
command_vars = EnvironmentConfig(
distutils_section='config_fc',
compiler_f77 = ('exe.compiler_f77', 'F77', 'f77exec', None, False),
compiler_f90 = ('exe.compiler_f90', 'F90', 'f90exec', None, False),
compiler_fix = ('exe.compiler_fix', 'F90', 'f90exec', None, False),
version_cmd = ('exe.version_cmd', None, None, None, False),
linker_so = ('exe.linker_so', 'LDSHARED', 'ldshared', None, False),
linker_exe = ('exe.linker_exe', 'LD', 'ld', None, False),
archiver = (None, 'AR', 'ar', None, False),
ranlib = (None, 'RANLIB', 'ranlib', None, False),
)
flag_vars = EnvironmentConfig(
distutils_section='config_fc',
f77 = ('flags.f77', 'F77FLAGS', 'f77flags', flaglist, True),
f90 = ('flags.f90', 'F90FLAGS', 'f90flags', flaglist, True),
free = ('flags.free', 'FREEFLAGS', 'freeflags', flaglist, True),
fix = ('flags.fix', None, None, flaglist, False),
opt = ('flags.opt', 'FOPT', 'opt', flaglist, True),
opt_f77 = ('flags.opt_f77', None, None, flaglist, False),
opt_f90 = ('flags.opt_f90', None, None, flaglist, False),
arch = ('flags.arch', 'FARCH', 'arch', flaglist, False),
arch_f77 = ('flags.arch_f77', None, None, flaglist, False),
arch_f90 = ('flags.arch_f90', None, None, flaglist, False),
debug = ('flags.debug', 'FDEBUG', 'fdebug', flaglist, True),
debug_f77 = ('flags.debug_f77', None, None, flaglist, False),
debug_f90 = ('flags.debug_f90', None, None, flaglist, False),
flags = ('self.get_flags', 'FFLAGS', 'fflags', flaglist, True),
linker_so = ('flags.linker_so', 'LDFLAGS', 'ldflags', flaglist, True),
linker_exe = ('flags.linker_exe', 'LDFLAGS', 'ldflags', flaglist, True),
ar = ('flags.ar', 'ARFLAGS', 'arflags', flaglist, True),
)
language_map = {'.f': 'f77',
'.for': 'f77',
'.F': 'f77',
'.ftn': 'f77',
'.f77': 'f77',
'.f90': 'f90',
'.F90': 'f90',
'.f95': 'f90',
}
language_order = ['f90', 'f77']
compiler_type = None
compiler_aliases = ()
version_pattern = None
possible_executables = []
executables = {
'version_cmd': ["f77", "-v"],
'compiler_f77': ["f77"],
'compiler_f90': ["f90"],
'compiler_fix': ["f90", "-fixed"],
'linker_so': ["f90", "-shared"],
'linker_exe': ["f90"],
'archiver': ["ar", "-cr"],
'ranlib': None,
}
suggested_f90_compiler = None
compile_switch = "-c"
object_switch = "-o "
library_switch = "-o "
module_dir_switch = None
module_include_switch = '-I'
pic_flags = []
src_extensions = ['.for', '.ftn', '.f77', '.f', '.f90', '.f95', '.F', '.F90', '.FOR']
obj_extension = ".o"
shared_lib_extension = get_shared_lib_extension()
static_lib_extension = ".a"
static_lib_format = "lib%s%s"
shared_lib_format = "%s%s"
exe_extension = ""
_exe_cache = {}
_executable_keys = ['version_cmd', 'compiler_f77', 'compiler_f90',
'compiler_fix', 'linker_so', 'linker_exe', 'archiver',
'ranlib']
c_compiler = None
extra_f77_compile_args = []
extra_f90_compile_args = []
def __init__(self, *args, **kw):
CCompiler.__init__(self, *args, **kw)
self.distutils_vars = self.distutils_vars.clone(self._environment_hook)
self.command_vars = self.command_vars.clone(self._environment_hook)
self.flag_vars = self.flag_vars.clone(self._environment_hook)
self.executables = self.executables.copy()
for e in self._executable_keys:
if e not in self.executables:
self.executables[e] = None
self._is_customised = False
def __copy__(self):
obj = self.__new__(self.__class__)
obj.__dict__.update(self.__dict__)
obj.distutils_vars = obj.distutils_vars.clone(obj._environment_hook)
obj.command_vars = obj.command_vars.clone(obj._environment_hook)
obj.flag_vars = obj.flag_vars.clone(obj._environment_hook)
obj.executables = obj.executables.copy()
return obj
def copy(self):
return self.__copy__()
def _command_property(key):
def fget(self):
assert self._is_customised
return self.executables[key]
return property(fget=fget)
version_cmd = _command_property('version_cmd')
compiler_f77 = _command_property('compiler_f77')
compiler_f90 = _command_property('compiler_f90')
compiler_fix = _command_property('compiler_fix')
linker_so = _command_property('linker_so')
linker_exe = _command_property('linker_exe')
archiver = _command_property('archiver')
ranlib = _command_property('ranlib')
def set_executable(self, key, value):
self.set_command(key, value)
def set_commands(self, **kw):
for k, v in kw.items():
self.set_command(k, v)
def set_command(self, key, value):
if not key in self._executable_keys:
raise ValueError(
"unknown executable '%s' for class %s" %
(key, self.__class__.__name__))
if is_string(value):
value = split_quoted(value)
assert value is None or is_sequence_of_strings(value[1:]), (key, value)
self.executables[key] = value
######################################################################
## 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 update_executables(self):
pass
def get_flags(self):
return [] + self.pic_flags
def _get_command_flags(self, key):
cmd = self.executables.get(key, None)
if cmd is None:
return []
return cmd[1:]
def get_flags_f77(self):
return self._get_command_flags('compiler_f77')
def get_flags_f90(self):
return self._get_command_flags('compiler_f90')
def get_flags_free(self):
return []
def get_flags_fix(self):
return self._get_command_flags('compiler_fix')
def get_flags_linker_so(self):
return self._get_command_flags('linker_so')
def get_flags_linker_exe(self):
return self._get_command_flags('linker_exe')
def get_flags_ar(self):
return self._get_command_flags('archiver')
def get_flags_opt(self):
return []
def get_flags_arch(self):
return []
def get_flags_debug(self):
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):
return self.libraries[:]
def get_library_dirs(self):
return self.library_dirs[:]
def get_version(self, force=False, ok_status=[0]):
assert self._is_customised
version = CCompiler.get_version(self, force=force, ok_status=ok_status)
if version is None:
raise CompilerNotFound()
return version
############################################################
## Public methods:
def dump_properties(self):
props = []
for key in list(self.executables.keys()) + \
['version', 'libraries', 'library_dirs',
'object_switch', 'compile_switch']:
if hasattr(self, key):
v = getattr(self, key)
props.append((key, None, '= '+repr(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 _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
src_flags = {}
if Path(src).suffix.lower() in FORTRAN_COMMON_FIXED_EXTENSIONS \
and not has_f90_header(src):
flavor = ':f77'
compiler = self.compiler_f77
src_flags = get_f77flags(src)
extra_compile_args = self.extra_f77_compile_args or []
elif is_free_format(src):
flavor = ':f90'
compiler = self.compiler_f90
if compiler is None:
raise DistutilsExecError('f90 not supported by %s needed for %s'\
% (self.__class__.__name__, src))
extra_compile_args = self.extra_f90_compile_args or []
else:
flavor = ':fix'
compiler = self.compiler_fix
if compiler is None:
raise DistutilsExecError('f90 (fixed) not supported by %s needed for %s'\
% (self.__class__.__name__, src))
extra_compile_args = self.extra_f90_compile_args or []
if self.object_switch[-1]==' ':
o_args = [self.object_switch.strip(), obj]
else:
o_args = [self.object_switch.strip()+obj]
assert self.compile_switch.strip()
s_args = [self.compile_switch, src]
if extra_compile_args:
log.info('extra %s options: %r' \
% (flavor[1:], ' '.join(extra_compile_args)))
extra_flags = src_flags.get(self.compiler_type, [])
if extra_flags:
log.info('using compile options from source: %r' \
% ' '.join(extra_flags))
command = compiler + cc_args + extra_flags + s_args + o_args \
+ extra_postargs + extra_compile_args
display = '%s: %s' % (os.path.basename(compiler[0]) + flavor,
src)
try:
self.spawn(command, display=display)
except DistutilsExecError as e:
msg = str(e)
raise CompileError(msg) from None
def module_options(self, module_dirs, module_build_dir):
options = []
if self.module_dir_switch is not None:
if self.module_dir_switch[-1]==' ':
options.extend([self.module_dir_switch.strip(), module_build_dir])
else:
options.append(self.module_dir_switch.strip()+module_build_dir)
else:
print('XXX: module_build_dir=%r option ignored' % (module_build_dir))
print('XXX: Fix module_dir_switch for ', self.__class__.__name__)
if self.module_include_switch is not None:
for d in [module_build_dir]+module_dirs:
options.append('%s%s' % (self.module_include_switch, d))
else:
print('XXX: module_dirs=%r option ignored' % (module_dirs))
print('XXX: Fix module_include_switch for ', self.__class__.__name__)
return options
def library_option(self, lib):
return "-l" + lib
def library_dir_option(self, dir):
return "-L" + dir
def link(self, objects, output_filename, output_dir=None,
libraries=None, library_dirs=None, runtime_library_dirs=None,
extra_preargs=None, extra_postargs=None, debug=False,
target_desc=CCompiler.EXECUTABLE):
objects, output_dir = self._fix_object_args(objects, output_dir)
libraries, library_dirs, runtime_library_dirs = \
self._fix_lib_args(libraries, library_dirs, runtime_library_dirs)
lib_opts = gen_lib_options(self, library_dirs, runtime_library_dirs,
libraries)
if is_string(output_dir):
output_filename = os.path.join(output_dir, output_filename)
elif output_dir is not None:
raise TypeError("'output_dir' must be a string or None")
if self._need_link(objects, output_filename):
if self.library_switch[-1]==' ':
o_args = [self.library_switch.strip(), output_filename]
else:
o_args = [self.library_switch.strip()+output_filename]
if is_string(self.objects):
ld_args = objects + [self.objects]
else:
ld_args = objects + self.objects
ld_args = ld_args + lib_opts + o_args
if debug:
ld_args[:0] = ['-g']
if extra_preargs:
ld_args[:0] = extra_preargs
if extra_postargs:
ld_args.extend(extra_postargs)
self.mkpath(os.path.dirname(output_filename))
if target_desc == CCompiler.EXECUTABLE:
linker = self.linker_exe[:]
else:
linker = self.linker_so[:]
command = linker + ld_args
try:
self.spawn(command)
except DistutilsExecError as e:
msg = str(e)
raise LinkError(msg) from None
else:
log.debug("skipping %s (up-to-date)", output_filename)
def _environment_hook(self, name, hook_name):
if hook_name is None:
return None
if is_string(hook_name):
if hook_name.startswith('self.'):
hook_name = hook_name[5:]
hook = getattr(self, hook_name)
return hook()
elif hook_name.startswith('exe.'):
hook_name = hook_name[4:]
var = self.executables[hook_name]
if var:
return var[0]
else:
return None
elif hook_name.startswith('flags.'):
hook_name = hook_name[6:]
hook = getattr(self, 'get_flags_' + hook_name)
return hook()
else:
return hook_name()
def can_ccompiler_link(self, ccompiler):
return True
def wrap_unlinkable_objects(self, objects, output_dir, extra_dll_dir):
raise NotImplementedError()
## class FCompiler
_default_compilers = (
('win32', ('gnu', 'intelv', 'absoft', 'compaqv', 'intelev', 'gnu95', 'g95',
'intelvem', 'intelem', 'flang')),
('cygwin.*', ('gnu', 'intelv', 'absoft', 'compaqv', 'intelev', 'gnu95', 'g95')),
('linux.*', ('arm', 'gnu95', 'intel', 'lahey', 'pg', 'nv', 'absoft', 'nag',
'vast', 'compaq', 'intele', 'intelem', 'gnu', 'g95',
'pathf95', 'nagfor', 'fujitsu')),
('darwin.*', ('gnu95', 'nag', 'nagfor', 'absoft', 'ibm', 'intel', 'gnu',
'g95', 'pg')),
('sunos.*', ('sun', 'gnu', 'gnu95', 'g95')),
('irix.*', ('mips', 'gnu', 'gnu95',)),
('aix.*', ('ibm', 'gnu', 'gnu95',)),
('posix', ('gnu', 'gnu95',)),
('nt', ('gnu', 'gnu95',)),
('mac', ('gnu95', 'gnu', 'pg')),
)
fcompiler_class = None
fcompiler_aliases = None
def load_all_fcompiler_classes():
from glob import glob
global fcompiler_class, fcompiler_aliases
if fcompiler_class is not None:
return
pys = os.path.join(os.path.dirname(__file__), '*.py')
fcompiler_class = {}
fcompiler_aliases = {}
for fname in glob(pys):
module_name, ext = os.path.splitext(os.path.basename(fname))
module_name = 'numpy.distutils.fcompiler.' + module_name
__import__(module_name)
module = sys.modules[module_name]
if hasattr(module, 'compilers'):
for cname in module.compilers:
klass = getattr(module, cname)
desc = (klass.compiler_type, klass, klass.description)
fcompiler_class[klass.compiler_type] = desc
for alias in klass.compiler_aliases:
if alias in fcompiler_aliases:
raise ValueError("alias %r defined for both %s and %s"
% (alias, klass.__name__,
fcompiler_aliases[alias][1].__name__))
fcompiler_aliases[alias] = desc
def _find_existing_fcompiler(compiler_types,
osname=None, platform=None,
requiref90=False,
c_compiler=None):
from numpy.distutils.core import get_distribution
dist = get_distribution(always=True)
for compiler_type in compiler_types:
v = None
try:
c = new_fcompiler(plat=platform, compiler=compiler_type,
c_compiler=c_compiler)
c.customize(dist)
v = c.get_version()
if requiref90 and c.compiler_f90 is None:
v = None
new_compiler = c.suggested_f90_compiler
if new_compiler:
log.warn('Trying %r compiler as suggested by %r '
'compiler for f90 support.' % (compiler_type,
new_compiler))
c = new_fcompiler(plat=platform, compiler=new_compiler,
c_compiler=c_compiler)
c.customize(dist)
v = c.get_version()
if v is not None:
compiler_type = new_compiler
if requiref90 and c.compiler_f90 is None:
raise ValueError('%s does not support compiling f90 codes, '
'skipping.' % (c.__class__.__name__))
except DistutilsModuleError:
log.debug("_find_existing_fcompiler: compiler_type='%s' raised DistutilsModuleError", compiler_type)
except CompilerNotFound:
log.debug("_find_existing_fcompiler: compiler_type='%s' not found", compiler_type)
if v is not None:
return compiler_type
return None
def available_fcompilers_for_platform(osname=None, platform=None):
if osname is None:
osname = os.name
if platform is None:
platform = sys.platform
matching_compiler_types = []
for pattern, compiler_type in _default_compilers:
if re.match(pattern, platform) or re.match(pattern, osname):
for ct in compiler_type:
if ct not in matching_compiler_types:
matching_compiler_types.append(ct)
if not matching_compiler_types:
matching_compiler_types.append('gnu')
return matching_compiler_types
def get_default_fcompiler(osname=None, platform=None, requiref90=False,
c_compiler=None):
matching_compiler_types = available_fcompilers_for_platform(osname,
platform)
log.info("get_default_fcompiler: matching types: '%s'",
matching_compiler_types)
compiler_type = _find_existing_fcompiler(matching_compiler_types,
osname=osname,
platform=platform,
requiref90=requiref90,
c_compiler=c_compiler)
return compiler_type
failed_fcompilers = set()
def new_fcompiler(plat=None,
compiler=None,
verbose=0,
dry_run=0,
force=0,
requiref90=False,
c_compiler=None):
global failed_fcompilers
fcompiler_key = (plat, compiler)
if fcompiler_key in failed_fcompilers:
return None
load_all_fcompiler_classes()
if plat is None:
plat = os.name
if compiler is None:
compiler = get_default_fcompiler(plat, requiref90=requiref90,
c_compiler=c_compiler)
if compiler in fcompiler_class:
module_name, klass, long_description = fcompiler_class[compiler]
elif compiler in fcompiler_aliases:
module_name, klass, long_description = fcompiler_aliases[compiler]
else:
msg = "don't know how to compile Fortran code on platform '%s'" % plat
if compiler is not None:
msg = msg + " with '%s' compiler." % compiler
msg = msg + " Supported compilers are: %s)" \
% (','.join(fcompiler_class.keys()))
log.warn(msg)
failed_fcompilers.add(fcompiler_key)
return None
compiler = klass(verbose=verbose, dry_run=dry_run, force=force)
compiler.c_compiler = c_compiler
return compiler
def show_fcompilers(dist=None):
if dist is None:
from distutils.dist import Distribution
from numpy.distutils.command.config_compiler import config_fc
dist = Distribution()
dist.script_name = os.path.basename(sys.argv[0])
dist.script_args = ['config_fc'] + sys.argv[1:]
try:
dist.script_args.remove('--help-fcompiler')
except ValueError:
pass
dist.cmdclass['config_fc'] = config_fc
dist.parse_config_files()
dist.parse_command_line()
compilers = []
compilers_na = []
compilers_ni = []
if not fcompiler_class:
load_all_fcompiler_classes()
platform_compilers = available_fcompilers_for_platform()
for compiler in platform_compilers:
v = None
log.set_verbosity(-2)
try:
c = new_fcompiler(compiler=compiler, verbose=dist.verbose)
c.customize(dist)
v = c.get_version()
except (DistutilsModuleError, CompilerNotFound) as e:
log.debug("show_fcompilers: %s not found" % (compiler,))
log.debug(repr(e))
if v is None:
compilers_na.append(("fcompiler="+compiler, None,
fcompiler_class[compiler][2]))
else:
c.dump_properties()
compilers.append(("fcompiler="+compiler, None,
fcompiler_class[compiler][2] + ' (%s)' % v))
compilers_ni = list(set(fcompiler_class.keys()) - set(platform_compilers))
compilers_ni = [("fcompiler="+fc, None, fcompiler_class[fc][2])
for fc in compilers_ni]
compilers.sort()
compilers_na.sort()
compilers_ni.sort()
pretty_printer = FancyGetopt(compilers)
pretty_printer.print_help("Fortran compilers found:")
pretty_printer = FancyGetopt(compilers_na)
pretty_printer.print_help("Compilers available for this "
"platform, but not found:")
if compilers_ni:
pretty_printer = FancyGetopt(compilers_ni)
pretty_printer.print_help("Compilers not available on this platform:")
print("For compiler details, run 'config_fc --verbose' setup command.")
def dummy_fortran_file():
fo, name = make_temp_file(suffix='.f')
fo.write(" subroutine dummy()\n end\n")
fo.close()
return name[:-2]
_has_f_header = re.compile(r'-\*-\s*fortran\s*-\*-', re.I).search
_has_f90_header = re.compile(r'-\*-\s*f90\s*-\*-', re.I).search
_has_fix_header = re.compile(r'-\*-\s*fix\s*-\*-', re.I).search
_free_f90_start = re.compile(r'[^c*!]\s*[^\\d\\t]', re.I).match
def is_free_format(file):
result = 0
with open(file, encoding='latin1') as f:
line = f.readline()
n = 10000
if _has_f_header(line) or _has_fix_header(line):
n = 0
elif _has_f90_header(line):
n = 0
result = 1
while n > 0 and line:
line = line.rstrip()
if line and line[0] != '!':
n -= 1
if (line[0] != '\t' and _free_f90_start(line[:5])) or line[-1:] == '&':
result = 1
break
line = f.readline()
return result
def has_f90_header(src):
with open(src, encoding='latin1') as f:
line = f.readline()
return _has_f90_header(line) or _has_fix_header(line)
_f77flags_re = re.compile(r'(c|)f77flags\s*\(\s*(?P<fcname>\w+)\s*\)\s*=\s*(?P<fflags>.*)', re.I)
def get_f77flags(src):
flags = {}
with open(src, encoding='latin1') as f:
i = 0
for line in f:
i += 1
if i > 20:
break
m = _f77flags_re.match(line)
if not m:
continue
fcname = m.group('fcname').strip()
fflags = m.group('fflags').strip()
flags[fcname] = split_quoted(fflags)
return flags
# TODO: implement get_f90flags and use it in _compile similarly to get_f77flags
if __name__ == '__main__':
show_fcompilers()
O arquivo __init__.py é o ponto central para a abstração de compiladores Fortran. Ele define a classe base abstrata FCompiler, que estabelece a interface comum para todos os compiladores Fortran. Funções como new_fcompiler e show_fcompilers são fornecidas para criar instâncias de compiladores e listar os compiladores disponíveis.
# .\numpy\numpy\distutils\fcompiler\fujitsuccompiler.py
from distutils.unixccompiler import UnixCCompiler
class FujitsuCCompiler(UnixCCompiler):
"""
Fujitsu compiler.
"""
compiler_type = 'fujitsu'
cc_exe = 'fcc'
cxx_exe = 'FCC'
def __init__(self, verbose=0, dry_run=0, force=0):
UnixCCompiler.__init__(self, verbose, dry_run, force)
cc_compiler = self.cc_exe
self.set_executables(
compiler=cc_compiler +
' -O3 -Nclang -fPIC',
compiler_so=cc_compiler +
' -O3 -Nclang -fPIC',
compiler_cxx=cxx_compiler +
' -O3 -Nclang -fPIC',
linker_exe=cc_compiler +
' -lfj90i -lfj90f -lfjsrcinfo -lelf -shared',
linker_so=cc_compiler +
' -lfj90i -lfj90f -lfjsrcinfo -lelf -shared'
)
O arquivo fujitsuccompiler.py define o compilador FujitsuCCompiler, herdando de UnixCCompiler. Ele configura os comandos e flags para usar o compilador Fortran da Fujitsu.
# .\numpy\numpy\distutils\intelccompiler.py
import platform
from distutils.unixccompiler import UnixCCompiler
from numpy.distutils.exec_command import find_executable
from numpy.distutils.ccompiler import simple_version_match
if platform.system() == 'Windows':
from numpy.distutils.msvc9compiler import MSVCCompiler
class IntelCCompiler(UnixCCompiler):
"""A modified Intel compiler compatible with a GCC-built Python."""
compiler_type = 'intel'
cc_exe = 'icc'
cc_args = 'fPIC'
def __init__(self, verbose=0, dry_run=0, force=0):
UnixCCompiler.__init__(self, verbose, dry_run, force)
v = self.get_version()
mpopt = 'openmp' if v and v < '15' else 'qopenmp'
self.cc_exe = ('icc -fPIC -fp-model strict -O3 '
'-fomit-frame-pointer -{}').format(mpopt)
compiler = self.cc_exe
if platform.system() == 'Darwin':
shared_flag = '-Wl,-undefined,dynamic_lookup'
else:
shared_flag = '-shared'
self.set_executables(compiler=compiler,
compiler_so=compiler,
compiler_cxx=compiler,
archiver='xiar' + ' cru',
linker_exe=compiler + ' -shared-intel',
linker_so=compiler + ' ' + shared_flag +
' -shared-intel')
class IntelItaniumCCompiler(IntelCCompiler):
compiler_type = 'intele'
for cc_exe in map(find_executable, ['icc', 'ecc']):
if cc_exe:
break
class IntelEM64TCCompiler(UnixCCompiler):
"""
A modified Intel x86_64 compiler compatible with a 64bit GCC-built Python.
"""
compiler_type = 'intelem'
cc_exe = 'icc -m64'
cc_args = '-fPIC'
def __init__(self, verbose=0, dry_run=0, force=0):
UnixCCompiler.__init__(self, verbose, dry_run, force)
v = self.get_version()
mpopt = 'openmp' if v and v < '15' else 'qopenmp'
self.cc_exe = ('icc -std=c99 -m64 -fPIC -fp-model strict -O3 '
'-fomit-frame-pointer -{}').format(mpopt)
compiler = self.cc_exe
if platform.system() == 'Darwin':
shared_flag = '-Wl,-undefined,dynamic_lookup'
else:
shared_flag = '-shared'
self.set_executables(compiler=compiler,
compiler_so=compiler,
compiler_cxx=compiler,
archiver='xiar' + ' cru',
linker_exe=compiler + ' -shared-intel',
linker_so=compiler + ' ' + shared_flag +
' -shared-intel')
if platform.system() == 'Windows':
class IntelCCompilerW(MSVCCompiler):
"""
A modified Intel compiler compatible with an MSVC-built Python.
"""
compiler_type = 'intelw'
compiler_cxx = 'icl'
def __init__(self, verbose=0, dry_run=0, force=0):
MSVCCompiler.__init__(self, verbose, dry_run, force)
version_match = simple_version_match(start=r'Intel\(R\).*?32,')
self.__version = version_match
def initialize(self, plat_name=None):
MSVCCompiler.initialize(self, plat_name)
self.cc = self.find_exe('icl.exe')
self.lib = self.find_exe('xilib')
self.linker = self.find_exe('xilink')
self.compile_options = ['/nologo', '/O3', '/MD', '/W3',
'/Qstd=c99']
self.compile_options_debug = ['/nologo', '/Od', '/MDd', '/W3',
'/Qstd=c99', '/Z7', '/D_DEBUG']
class IntelEM64TCCompilerW(IntelCCompilerW):
"""
A modified Intel x86_64 compiler compatible with
a 64bit MSVC-built Python.
"""
compiler_type = 'intelemw'
def __init__(self, verbose=0, dry_run=0, force=0):
MSVCCompiler.__init__(self, verbose, dry_run, force)
version_match = simple_version_match(start=r'Intel\(R\).*?64,')
self.__version = version_match
O arquivo intelccompiler.py fornece implementações para compilar código com os compiladores Entel (icc e icl), tanto para sistemas baseados em Unix quanto em Windows. Ele inclui classes como IntelCCompiler, IntelItaniumCCompiler e IntelEM64TCCompiler, com suporte específico para diferentes arquiteturas e sistemas operacionais.
# .\numpy\numpy\distutils\from_template.py
"""
process_file(filename)
takes templated file .xxx.src and produces .xxx file where .xxx
is .pyf .f90 or .f using the following template rules:
'<..>' denotes a template.
All function and subroutine blocks in a source file with names that
contain '<..>' will be replicated according to the rules in '<..>'.
The number of comma-separated words in '<..>' will determine the number of
replicates.
'<..>' may have two different forms, named and short. For example,
named:
<p> where anywhere inside a block '</p><p>' will be replaced with
'd', 's', 'z', and 'c' for each replicate of the block.
<_c> is already defined: <_c=s,d,c,z>
<_t> is already defined: <_t=real,double precision,complex,double complex>
short:
<s>, a short form of the named, useful when no </s></p><p> appears inside
a block.
In general, '<..>' contains a comma separated list of arbitrary
expressions. If these expression must contain a comma|leftarrow|rightarrow,
then prepend the comma|leftarrow|rightarrow with a backslash.
If an expression matches '\\<index>' then it will be replaced
by <index>-th expression.
Note that all '<..>' forms in a block must have the same number of
comma-separated entries.
Predefined named template rules:
<prefix>
<ftype complex="" precision="">
<ftypereal precision="">
<ctype>
<ctypereal>
"""
__all__ = ['process_str', 'process_file']
import os
import sys
import re
routine_start_re = re.compile(r"(\n|\A)(( (\$|\*))|)\s*(subroutine|function)\b", re.I)
routine_end_re = re.compile(r"\n\s*end\s*(subroutine|function)\b.*(\n|\Z)", re.I)
function_start_re = re.compile(r"\n (\$|\*)\s*function\b", re.I)
def parse_structure(astr):
""" Return a list of tuples for each function or subroutine each
tuple is the start and end of a subroutine or function to be
expanded.
"""
spanlist = []
ind = 0
while True:
m = routine_start_re.search(astr, ind)
if m is None:
break
start = m.start()
if function_start_re.match(astr, start, m.end()):
while True:
i = astr.rfind('\n', ind, start)
if i == -1:
break
start = i
if astr[i:i+7] != '\n $':
break
start += 1
m = routine_end_re.search(astr, m.end())
ind = end = m and m.end()-1 or len(astr)
spanlist.append((start, end))
return spanlist
template_re = re.compile(r"<\s*(\w[\w\d]*)\s*>")
named_re = re.compile(r"<\s*(\w[\w\d]*)\s*=\s*(.*?)\s*>")
list_re = re.compile(r"<\s*((.*?))\s*>")
def find_repl_patterns(astr):
reps = named_re.findall(astr)
names = {}
for rep in reps:
name = rep[0].strip() or unique_key(names)
repl = rep[1].replace(r'\,', '@comma@')
thelist = conv(repl)
names[name] = thelist
return names
def find_and_remove_repl_patterns(astr):
names = find_repl_patterns(astr)
astr = re.subn(named_re, '', astr)[0]
return astr, names
item_re = re.compile(r"\A\\(?P<index>\d+)\Z")
def conv(astr):
b = astr.split(',')
l = [x.strip() for x in b]
for i in range(len(l)):
m = item_re.match(l[i])
if m:
j = int(m.group('index'))
l[i] = l[j]
return ','.join(l)
def unique_key(adict):
""" Obtain a unique key given a dictionary."""
allkeys = list(adict.keys())
done = False
n = 1
while not done:
newkey = '__l%s' % (n)
if newkey in allkeys:
n += 1
else:
done = True
return newkey
template_name_re = re.compile(r"\A\s*(\w[\w\d]*)\s*\Z")
def expand_sub(substr, names):
substr = substr.replace(r'\>', '@rightarrow@')
substr = substr.replace(r'\<', '@leftarrow@')
lnames = find_repl_patterns(substr)
substr = named_re.sub(r"<\1>", substr)
def listrepl(mobj):
thelist = conv(mobj.group(1).replace(r'\,', '@comma@'))
if template_name_re.match(thelist):
return "<%s>" % (thelist)
name = None
for key in lnames.keys():
if lnames[key] == thelist:
name = key
if name is None:
name = unique_key(lnames)
lnames[name] = thelist
return "<%s>" % name
substr = list_re.sub(listrepl, substr)
numsubs = None
base_rule = None
rules = {}
for r in template_re.findall(substr):
if r not in rules:
thelist = lnames.get(r, names.get(r, None))
if thelist is None:
raise ValueError('No replicates found for <%s>' % (r))
if r not in names and not thelist.startswith('_'):
names[r] = thelist
rule = [i.replace('@comma@', ',') for i in thelist.split(',')]
num = len(rule)
if numsubs is None:
numsubs = num
rules[r] = rule
base_rule = r
elif num == numsubs:
rules[r] = rule
else:
print("Mismatch in number of replacements (base <%s=%s>)"
" for <%s=%s>. Ignoring." %
(base_rule, ','.join(rules[base_rule]), r, thelist))
if not rules:
return substr
def namerepl(mobj):
name = mobj.group(1)
return rules.get(name, (k+1)*[name])[k]
newstr = ''
for k in range(numsubs):
newstr += template_re.sub(namerepl, substr) + '\n\n'
newstr = newstr.replace('@rightarrow@', '>')
newstr = newstr.replace('@leftarrow@', '<')
return newstr
def process_str(allstr):
newstr = allstr
writestr = ''
struct = parse_structure(newstr)
oldend = 0
names = {}
names.update(_special_names)
for sub in struct:
cleanedstr, defs = find_and_remove_repl_patterns(newstr[oldend:sub[0]])
writestr += cleanedstr
names.update(defs)
writestr += expand_sub(newstr[sub[0]:sub[1]], names)
oldend = sub[1]
writestr += newstr[oldend:]
return writestr
include_src_re = re.compile(r"(\n|\A)\s*include\s*['\"](?P<name>[\w\d./\\]+\.src)['\"]", re.I)
def resolve_includes(source):
d = os.path.dirname(source)
with open(source) as fid:
lines = []
for line in fid:
m = include_src_re.match(line)
if m:
fn = m.group('name')
if not os.path.isabs(fn):
fn = os.path.join(d, fn)
if os.path.isfile(fn):
lines.extend(resolve_includes(fn))
else:
lines.append(line)
else:
lines.append(line)
return lines
def process_file(source):
lines = resolve_includes(source)
return process_str(''.join(lines))
_special_names = find_repl_patterns('''
<_c=s,d,c,z>
<_t=real,double precision,complex,double complex>
<prefix>
<ftype complex="" precision="">
<ctype>
<ftypereal precision="">
<ctypereal>
''')
def main():
try:
file = sys.argv[1]
except IndexError:
fid = sys.stdin
outfile = sys.stdout
else:
fid = open(file, 'r')
(base, ext) = os.path.splitext(file)
newname = base
outfile = open(newname, 'w')
allstr = fid.read()
writestr = process_str(allstr)
outfile.write(writestr)
if __name__ == "__main__":
main()
</ctypereal></ftypereal></ctype></ftype></prefix></ctypereal></ctype></ftypereal></ftype></prefix></index></index></p>
O arquivo from_template.py é uma ferramenta para processar arquivos de template Fortran. Ele permite a geração de múltiplas versões de código a partir de um único arquivo template, substituindo placeholders definidos entre colchetes (<...>). Isso é útil para criar código que precisa ser compilado com diferentes tipos de dados (real, double precision, complex) ou com diferentes convenções de chamada.
# .\numpy\numpy\distutils\lib2def.py
import re
import sys
import subprocess
__doc__ = """This module generates a DEF file from the symbols in
an MSVC-compiled DLL import library. It correctly discriminates between
data and functions. The data is collected from the output of the program
nm(1).
Usage:
python lib2def.py [libname.lib] [output.def]
or
python lib2def.py [libname.lib] > output.def
libname.lib defaults to python<py_ver>.lib and output.def defaults to stdout
Author: Robert Kern <kernr@mail.ncifcrf.gov>
Last Update: April 30, 1999
"""
__version__ = '0.1a'
py_ver = "%d%d" % tuple(sys.version_info[:2])
DEFAULT_NM = ['nm', '-Cs']
DEF_HEADER = """LIBRARY python%s.dll
;CODE PRELOAD MOVEABLE DISCARDABLE
;DATA PRELOAD SINGLE
EXPORTS
""" % py_ver
FUNC_RE = re.compile(r"^(.*) in python%s\.dll" % py_ver, re.MULTILINE)
DATA_RE = re.compile(r"^_imp__(.*) in python%s\.dll" % py_ver, re.MULTILINE)
def parse_cmd():
"""Parses the command-line arguments.
libfile,deffile = parse_cmd()"""
if len(sys.argv) == 3:
if sys.argv[1][-4:] == '.lib' and sys.argv[2][-4:] == '.def':
libfile, deffile = sys.argv[1:]
elif sys.argv[1][-4:] == '.def' and sys.argv[2][-4:] == '.lib':
deffile, libfile = sys.argv[1:]
else:
print("I'm assuming that your first argument is the library")
print("and the second is the DEF file.")
elif len(sys.argv) == 2:
if sys.argv[1][-4:] == '.def':
deffile = sys.argv[1]
libfile = 'python%s.lib' % py_ver
elif sys.argv[1][-4:] == '.lib':
deffile = None
libfile = sys.argv[1]
else:
libfile = 'python%s.lib' % py_ver
deffile = None
return libfile, deffile
def getnm(nm_cmd=['nm', '-Cs', 'python%s.lib' % py_ver], shell=True):
"""Returns the output of nm_cmd via a pipe.
nm_output = getnm(nm_cmd = 'nm -Cs py_lib')"""
p = subprocess.Popen(nm_cmd, shell=shell, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, text=True)
nm_output, nm_err = p.communicate()
if p.returncode != 0:
raise RuntimeError('failed to run "%s": "%s"' % (
' '.join(nm_cmd), nm_err))
return nm_output
def parse_nm(nm_output):
"""Returns a tuple of lists: dlist for the list of data
symbols and flist for the list of function symbols.
dlist,flist = parse_nm(nm_output)"""
data = DATA_RE.findall(nm_output)
func = FUNC_RE.findall(nm_output)
flist = []
for sym in data:
if sym in func and (sym[:2] == 'Py' or sym[:3] == '_Py' or sym[:4] == 'init'):
flist.append(sym)
dlist = []
for sym in data:
if sym not in flist and (sym[:2] == 'Py' or sym[:3] == '_Py'):
dlist.append(sym)
dlist.sort()
flist.sort()
return dlist, flist
def output_def(dlist, flist, header, file = sys.stdout):
"""Outputs the final DEF file to stdout or a file."""
for data_sym in dlist:
header = header + '\t%s DATA\n' % data_sym
header = header + '\n'
for func_sym in flist:
header = header + '\t%s\n' % func_sym
file.write(header)
if __name__ == '__main__':
libfile, deffile = parse_cmd()
if deffile is None:
deffile = sys.stdout
else:
deffile = open(deffile, 'w')
nm_cmd = DEFAULT_NM + [str(libfile)]
nm_output = getnm(nm_cmd, shell=False)
dlist, flist = parse_nm(nm_output)
output_def(dlist, flist, DEF_HEADER, deffile)
O script lib2def.py é uma ferramenta auxiliar que gera arquivos .def (definition files) a partir de bibliotecas de importação de DLLs compiladas com MSVC. Ele usa o utilitário nm para extrair os símbolos de dados e funções, permitindo a correta definição de exports para DLLs.
O arquivo line_endings.py contém funções para converter fim de linha entre os formatos DOS (CRLF) e Unix (LF) em arquivos. Isso é útil para garantir a consistência nos finais de linha em diferentes sistemas operacionais.
Finalmente, o arquivo fujitsuccompiler.py fornece a implementação para o compilador Fujitsu.
Esses arquivos demonstram como o NumPy gerencia a complexidade de interagir com uma variedade de compiladores Fortran em diferentes plataformas, abstraindo as diferenças e fornecendo uma interface consistente para o processo de construção.