123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- #! /usr/bin/env python
- # encoding: utf-8
- # DC 2008
- # Thomas Nagy 2016-2018 (ita)
- import os, re, traceback
- from waflib import Utils, Logs, Errors
- from waflib.Tools import fc, fc_config, fc_scan, ar, ccroot
- from waflib.Configure import conf
- from waflib.TaskGen import after_method, feature
- @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):
- """
- Detects the compiler version and sets ``conf.env.FC_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):
- """
- Detects the Intel Fortran compilers
- """
- if Utils.is_win32:
- compiler, version, path, includes, libdirs, arch = conf.detect_ifort()
- 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 ValueError:
- 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()
- all_ifort_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
- """List of icl platforms"""
- @conf
- def gather_ifort_versions(conf, versions):
- """
- List compiler versions by looking up registry keys
- """
- version_pattern = re.compile('^...?.?\....?.?')
- try:
- all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran')
- except OSError:
- try:
- all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\Fortran')
- except OSError:
- return
- index = 0
- while 1:
- try:
- version = Utils.winreg.EnumKey(all_versions, index)
- except OSError:
- 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 OSError:
- pass
- else:
- batch_file=os.path.join(path,'bin','ifortvars.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 OSError:
- continue
- else:
- batch_file=os.path.join(path,'bin','ifortvars.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 which 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 = traceback.format_exc()
- 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):
- """
- Wraps 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_ifort_version_win32(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):
- 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'
- if not v.LINK_FC:
- conf.find_program(linker_name, var='LINK_FC', path_list=path, mandatory=True)
- 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):
- """
- Adds 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
- @feature('fcprogram', 'fcshlib', 'fcprogram_test')
- @after_method('apply_link')
- def apply_manifest_ifort(self):
- """
- Enables manifest embedding in Fortran DLLs when using ifort on Windows
- See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx
- """
- 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.env.DO_MANIFEST = True
|