2
0
mirror of https://gitlab.com/ita1024/waf.git synced 2024-11-29 21:41:44 +01:00
waf/waflib/Tools/ifort.py

503 lines
15 KiB
Python

#! /usr/bin/env python
# encoding: utf-8
# DC 2008
# Thomas Nagy 2010 (ita)
import re
from waflib import Utils
from waflib.Tools import fc, fc_config, fc_scan, ar
from waflib.Configure import conf
@conf
def find_ifort(conf):
fc = conf.find_program('ifort', var='FC')
conf.get_ifort_version(fc)
conf.env.FC_NAME = 'IFORT'
@conf
def ifort_modifier_win32(self):
v = self.env
v.IFORT_WIN32 = True
v.FCSTLIB_MARKER = ''
v.FCSHLIB_MARKER = ''
v.FCLIB_ST = v.FCSTLIB_ST = '%s.lib'
v.FCLIBPATH_ST = v.STLIBPATH_ST = '/LIBPATH:%s'
v.FCINCPATH_ST = '/I%s'
v.FCDEFINES_ST = '/D%s'
v.fcprogram_PATTERN = v.fcprogram_test_PATTERN = '%s.exe'
v.fcshlib_PATTERN = '%s.dll'
v.fcstlib_PATTERN = v.implib_PATTERN = '%s.lib'
v.FCLNK_TGT_F = '/out:'
v.FC_TGT_F = ['/c', '/o', '']
v.FCFLAGS_fcshlib = ''
v.LINKFLAGS_fcshlib = '/DLL'
v.AR_TGT_F = '/out:'
v.IMPLIB_ST = '/IMPLIB:%s'
v.append_value('LINKFLAGS', '/subsystem:console')
if v.IFORT_MANIFEST:
v.append_value('LINKFLAGS', ['/MANIFEST'])
@conf
def ifort_modifier_darwin(conf):
fc_config.fortran_modifier_darwin(conf)
@conf
def ifort_modifier_platform(conf):
dest_os = conf.env.DEST_OS or Utils.unversioned_sys_platform()
ifort_modifier_func = getattr(conf, 'ifort_modifier_' + dest_os, None)
if ifort_modifier_func:
ifort_modifier_func()
@conf
def get_ifort_version(conf, fc):
"""get the compiler version"""
version_re = re.compile(r"\bIntel\b.*\bVersion\s*(?P<major>\d*)\.(?P<minor>\d*)",re.I).search
if Utils.is_win32:
cmd = fc
else:
cmd = fc + ['-logo']
out, err = fc_config.getoutput(conf, cmd, stdin=False)
match = version_re(out) or version_re(err)
if not match:
conf.fatal('cannot determine ifort version.')
k = match.groupdict()
conf.env['FC_VERSION'] = (k['major'], k['minor'])
def configure(conf):
if Utils.is_win32:
compiler, version, path, includes, libdirs, arch = conf.detect_ifort(True)
v = conf.env
v.DEST_CPU = arch
v.PATH = path
v.INCLUDES = includes
v.LIBPATH = libdirs
v.MSVC_COMPILER = compiler
try:
v.MSVC_VERSION = float(version)
except Exception:
raise
v.MSVC_VERSION = float(version[:-3])
conf.find_ifort_win32()
conf.ifort_modifier_win32()
else:
conf.find_ifort()
conf.find_program('xiar', var='AR')
conf.find_ar()
conf.fc_flags()
conf.fc_add_flags()
conf.ifort_modifier_platform()
import os, re
from waflib import Task, Logs, Errors
from waflib.TaskGen import after_method, feature
from waflib.Configure import conf
from waflib.Tools import ccroot, ar
all_ifort_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
"""List of icl platforms"""
@conf
def gather_ifort_versions(conf, versions):
# some logic to try and list installed fortran compilers
version_pattern = re.compile('^...?.?\....?.?')
try:
all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran')
except WindowsError:
try:
all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\Fortran')
except WindowsError:
return
index = 0
while 1:
try:
version = Utils.winreg.EnumKey(all_versions, index)
except WindowsError:
break
index += 1
if not version_pattern.match(version):
continue
targets = {}
for target,arch in all_ifort_platforms:
if target=='intel64': targetDir='EM64T_NATIVE'
else: targetDir=target
try:
Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir)
icl_version=Utils.winreg.OpenKey(all_versions,version)
path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
except WindowsError:
pass
else:
batch_file=os.path.join(path,'bin','iclvars.bat')
if os.path.isfile(batch_file):
targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
for target,arch in all_ifort_platforms:
try:
icl_version = Utils.winreg.OpenKey(all_versions, version+'\\'+target)
path,type = Utils.winreg.QueryValueEx(icl_version,'ProductDir')
except WindowsError:
continue
else:
batch_file=os.path.join(path,'bin','iclvars.bat')
if os.path.isfile(batch_file):
targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
major = version[0:2]
versions['intel ' + major] = targets
@conf
def setup_ifort(conf, versiondict):
"""
Checks installed compilers and targets and returns the first combination from the user's
options, env, or the global supported lists that checks.
:param versiondict: dict(platform -> dict(architecture -> configuration))
:type versiondict: dict(string -> dict(string -> target_compiler)
:return: the compiler, revision, path, include dirs, library paths and target architecture
:rtype: tuple of strings
"""
platforms = Utils.to_list(conf.env['MSVC_TARGETS']) or [i for i,j in all_ifort_platforms]
desired_versions = conf.env['MSVC_VERSIONS'] or list(reversed(list(versiondict.keys())))
for version in desired_versions:
try:
targets = versiondict[version]
except KeyError:
continue
for arch in platforms:
try:
cfg = targets[arch]
except KeyError:
continue
cfg.evaluate()
if cfg.is_valid:
compiler,revision = version.rsplit(' ', 1)
return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu
conf.fatal('ifort: Impossible to find a valid architecture for building %r - %r' % (desired_versions, list(versiondict.keys())))
@conf
def get_ifort_version_win32(conf, compiler, version, target, vcvars):
# FIXME hack
try:
conf.msvc_cnt += 1
except AttributeError:
conf.msvc_cnt = 1
batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
batfile.write("""@echo off
set INCLUDE=
set LIB=
call "%s" %s
echo PATH=%%PATH%%
echo INCLUDE=%%INCLUDE%%
echo LIB=%%LIB%%;%%LIBPATH%%
""" % (vcvars,target))
sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
batfile.delete()
lines = sout.splitlines()
if not lines[0]:
lines.pop(0)
MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
for line in lines:
if line.startswith('PATH='):
path = line[5:]
MSVC_PATH = path.split(';')
elif line.startswith('INCLUDE='):
MSVC_INCDIR = [i for i in line[8:].split(';') if i]
elif line.startswith('LIB='):
MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
if None in (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR):
conf.fatal('ifort: Could not find a valid architecture for building (get_ifort_version_win32)')
# Check if the compiler is usable at all.
# The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
env = dict(os.environ)
env.update(PATH = path)
compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
fc = conf.find_program(compiler_name, path_list=MSVC_PATH)
# delete CL if exists. because it could contain parameters wich can change cl's behaviour rather catastrophically.
if 'CL' in env:
del(env['CL'])
try:
conf.cmd_and_log(fc + ['/help'], env=env)
except UnicodeError:
st = Utils.ex_stack()
if conf.logger:
conf.logger.error(st)
conf.fatal('ifort: Unicode error - check the code page?')
except Exception as e:
Logs.debug('ifort: get_ifort_version: %r %r %r -> failure %s', compiler, version, target, str(e))
conf.fatal('ifort: cannot run the compiler in get_ifort_version (run with -v to display errors)')
else:
Logs.debug('ifort: get_ifort_version: %r %r %r -> OK', compiler, version, target)
finally:
conf.env[compiler_name] = ''
return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
class target_compiler(object):
"""
Wrap a compiler configuration; call evaluate() to determine
whether the configuration is usable.
"""
def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
"""
:param ctx: configuration context to use to eventually get the version environment
:param compiler: compiler name
:param cpu: target cpu
:param version: compiler version number
:param bat_target: ?
:param bat: path to the batch file to run
:param callback: optional function to take the realized environment variables tup and map it (e.g. to combine other constant paths)
"""
self.conf = ctx
self.name = None
self.is_valid = False
self.is_done = False
self.compiler = compiler
self.cpu = cpu
self.version = version
self.bat_target = bat_target
self.bat = bat
self.callback = callback
def evaluate(self):
if self.is_done:
return
self.is_done = True
try:
vs = self.conf.get_msvc_version(self.compiler, self.version, self.bat_target, self.bat)
except Errors.ConfigurationError:
self.is_valid = False
return
if self.callback:
vs = self.callback(self, vs)
self.is_valid = True
(self.bindirs, self.incdirs, self.libdirs) = vs
def __str__(self):
return str((self.bindirs, self.incdirs, self.libdirs))
def __repr__(self):
return repr((self.bindirs, self.incdirs, self.libdirs))
@conf
def detect_ifort(self):
# Save installed versions only if lazy detection is disabled.
return self.setup_ifort(self.get_ifort_versions(False))
@conf
def get_ifort_versions(self, eval_and_save=True):
"""
:return: platforms to compiler configurations
:rtype: dict
"""
dct = {}
self.gather_ifort_versions(dct)
return dct
def _get_prog_names(self, compiler):
if compiler=='intel':
compiler_name = 'ifort'
linker_name = 'XILINK'
lib_name = 'XILIB'
else:
# assumes CL.exe
compiler_name = 'CL'
linker_name = 'LINK'
lib_name = 'LIB'
return compiler_name, linker_name, lib_name
@conf
def find_ifort_win32(conf):
# the autodetection is supposed to be performed before entering in this method
v = conf.env
path = v['PATH']
compiler = v['MSVC_COMPILER']
version = v['MSVC_VERSION']
compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
v.IFORT_MANIFEST = (compiler == 'intel' and version >= 11)
# compiler
fc = conf.find_program(compiler_name, var='FC', path_list=path)
# before setting anything, check if the compiler is really intel fortran
env = dict(conf.environ)
if path: env.update(PATH = ';'.join(path))
if not conf.cmd_and_log(fc + ['/nologo', '/help'], env=env):
conf.fatal('not intel fortran compiler could not be identified')
v['FC_NAME'] = 'IFORT'
# linker
if not v['LINK_FC']:
conf.find_program(linker_name, var='LINK_FC', path_list=path, mandatory=True)
# staticlib linker
if not v['AR']:
conf.find_program(lib_name, path_list=path, var='AR', mandatory=True)
v['ARFLAGS'] = ['/nologo']
# manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
if v.IFORT_MANIFEST:
conf.find_program('MT', path_list=path, var='MT')
v['MTFLAGS'] = ['/nologo']
try:
conf.load('winres')
except Errors.WafError:
Logs.warn('Resource compiler not found. Compiling resource file is disabled')
#######################################################################################################
##### conf above, build below
@after_method('apply_link')
@feature('fc')
def apply_flags_ifort(self):
"""
Add additional flags implied by msvc, such as subsystems and pdb files::
def build(bld):
bld.stlib(source='main.c', target='bar', subsystem='gruik')
"""
if not self.env.IFORT_WIN32 or not getattr(self, 'link_task', None):
return
is_static = isinstance(self.link_task, ccroot.stlink_task)
subsystem = getattr(self, 'subsystem', '')
if subsystem:
subsystem = '/subsystem:%s' % subsystem
flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
self.env.append_value(flags, subsystem)
if not is_static:
for f in self.env.LINKFLAGS:
d = f.lower()
if d[1:] == 'debug':
pdbnode = self.link_task.outputs[0].change_ext('.pdb')
self.link_task.outputs.append(pdbnode)
if getattr(self, 'install_task', None):
self.pdb_install_task = self.add_install_files(install_to=self.install_task.install_to, install_from=pdbnode)
break
# split the manifest file processing from the link task, like for the rc processing
@feature('fcprogram', 'fcshlib', 'fcprogram_test')
@after_method('apply_link')
def apply_manifest_ifort(self):
if self.env.IFORT_WIN32 and getattr(self, 'link_task', None):
# it seems ifort.exe cannot be called for linking
self.link_task.env.FC = self.env.LINK_FC
if self.env.IFORT_WIN32 and self.env.IFORT_MANIFEST and getattr(self, 'link_task', None):
out_node = self.link_task.outputs[0]
man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
self.link_task.outputs.append(man_node)
self.link_task.do_manifest = True
def exec_mf(self):
"""
Create the manifest file
"""
env = self.env
mtool = env['MT']
if not mtool:
return 0
self.do_manifest = False
outfile = self.outputs[0].abspath()
manifest = None
for out_node in self.outputs:
if out_node.name.endswith('.manifest'):
manifest = out_node.abspath()
break
if manifest is None:
# Should never get here. If we do, it means the manifest file was
# never added to the outputs list, thus we don't have a manifest file
# to embed, so we just return.
return 0
# embedding mode. Different for EXE's and DLL's.
# see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx
mode = ''
if 'fcprogram' in self.generator.features or 'fcprogram_test' in self.generator.features:
mode = '1'
elif 'fcshlib' in self.generator.features:
mode = '2'
Logs.debug('ifort: embedding manifest in mode %r', mode)
lst = [] + mtool
lst.extend(Utils.to_list(env['MTFLAGS']))
lst.extend(['-manifest', manifest])
lst.append('-outputresource:%s;%s' % (outfile, mode))
return super(self.__class__, self).exec_command(lst)
def exec_command_ifort(self, *k, **kw):
"""
Change the command-line execution for msvc programs.
Instead of quoting all the paths and keep using the shell, we can just join the options msvc is interested in
"""
if self.env['PATH']:
env = dict(self.env.env or os.environ)
env.update(PATH = ';'.join(self.env['PATH']))
kw['env'] = env
ret = super(self.__class__, self).exec_command(k[0], **kw)
if not ret and getattr(self, 'do_manifest', None):
ret = self.exec_mf()
return ret
def wrap_class(class_name):
"""
Manifest file processing and @response file workaround for command-line length limits on Windows systems
The indicated task class is replaced by a subclass to prevent conflicts in case the class is wrapped more than once
"""
cls = Task.classes.get(class_name)
if not cls:
return None
derived_class = type(class_name, (cls,), {})
def exec_command(self, *k, **kw):
if self.env.IFORT_WIN32:
return self.exec_command_ifort(*k, **kw)
else:
return super(self.__class__, self).exec_command(*k, **kw)
# Chain-up monkeypatch needed since exec_command() is in base class API
derived_class.exec_command = exec_command
# No chain-up behavior needed since the following methods aren't in
# base class API
derived_class.exec_command_ifort = exec_command_ifort
derived_class.exec_mf = exec_mf
if hasattr(cls, 'hcode'):
derived_class.hcode = cls.hcode
return derived_class
for k in 'fc fcprogram fcprogram_test fcshlib fcstlib'.split():
wrap_class(k)