4 # Thomas Nagy 2016-2018 (ita)
6 import os, re, traceback
7 from waflib import Utils, Logs, Errors
8 from waflib.Tools import fc, fc_config, fc_scan, ar, ccroot
9 from waflib.Configure import conf
10 from waflib.TaskGen import after_method, feature
14 fc = conf.find_program('ifort', var='FC')
15 conf.get_ifort_version(fc)
16 conf.env.FC_NAME = 'IFORT'
19 def ifort_modifier_win32(self):
25 v.FCLIB_ST = v.FCSTLIB_ST = '%s.lib'
26 v.FCLIBPATH_ST = v.STLIBPATH_ST = '/LIBPATH:%s'
27 v.FCINCPATH_ST = '/I%s'
28 v.FCDEFINES_ST = '/D%s'
30 v.fcprogram_PATTERN = v.fcprogram_test_PATTERN = '%s.exe'
31 v.fcshlib_PATTERN = '%s.dll'
32 v.fcstlib_PATTERN = v.implib_PATTERN = '%s.lib'
34 v.FCLNK_TGT_F = '/out:'
35 v.FC_TGT_F = ['/c', '/o', '']
36 v.FCFLAGS_fcshlib = ''
37 v.LINKFLAGS_fcshlib = '/DLL'
39 v.IMPLIB_ST = '/IMPLIB:%s'
41 v.append_value('LINKFLAGS', '/subsystem:console')
43 v.append_value('LINKFLAGS', ['/MANIFEST'])
46 def ifort_modifier_darwin(conf):
47 fc_config.fortran_modifier_darwin(conf)
50 def ifort_modifier_platform(conf):
51 dest_os = conf.env.DEST_OS or Utils.unversioned_sys_platform()
52 ifort_modifier_func = getattr(conf, 'ifort_modifier_' + dest_os, None)
53 if ifort_modifier_func:
57 def get_ifort_version(conf, fc):
59 Detects the compiler version and sets ``conf.env.FC_VERSION``
61 version_re = re.compile(r"\bIntel\b.*\bVersion\s*(?P<major>\d*)\.(?P<minor>\d*)",re.I).search
67 out, err = fc_config.getoutput(conf, cmd, stdin=False)
68 match = version_re(out) or version_re(err)
70 conf.fatal('cannot determine ifort version.')
72 conf.env.FC_VERSION = (k['major'], k['minor'])
76 Detects the Intel Fortran compilers
79 compiler, version, path, includes, libdirs, arch = conf.detect_ifort()
85 v.MSVC_COMPILER = compiler
87 v.MSVC_VERSION = float(version)
89 v.MSVC_VERSION = float(version[:-3])
91 conf.find_ifort_win32()
92 conf.ifort_modifier_win32()
95 conf.find_program('xiar', var='AR')
99 conf.ifort_modifier_platform()
102 all_ifort_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
103 """List of icl platforms"""
106 def gather_ifort_versions(conf, versions):
108 List compiler versions by looking up registry keys
110 version_pattern = re.compile('^...?.?\....?.?')
112 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran')
115 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\Fortran')
121 version = Utils.winreg.EnumKey(all_versions, index)
125 if not version_pattern.match(version):
128 for target,arch in all_ifort_platforms:
129 if target=='intel64':
130 targetDir='EM64T_NATIVE'
134 Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir)
135 icl_version=Utils.winreg.OpenKey(all_versions,version)
136 path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
140 batch_file=os.path.join(path,'bin','ifortvars.bat')
141 if os.path.isfile(batch_file):
142 targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
144 for target,arch in all_ifort_platforms:
146 icl_version = Utils.winreg.OpenKey(all_versions, version+'\\'+target)
147 path,type = Utils.winreg.QueryValueEx(icl_version,'ProductDir')
151 batch_file=os.path.join(path,'bin','ifortvars.bat')
152 if os.path.isfile(batch_file):
153 targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
155 versions['intel ' + major] = targets
158 def setup_ifort(conf, versiondict):
160 Checks installed compilers and targets and returns the first combination from the user's
161 options, env, or the global supported lists that checks.
163 :param versiondict: dict(platform -> dict(architecture -> configuration))
164 :type versiondict: dict(string -> dict(string -> target_compiler)
165 :return: the compiler, revision, path, include dirs, library paths and target architecture
166 :rtype: tuple of strings
168 platforms = Utils.to_list(conf.env.MSVC_TARGETS) or [i for i,j in all_ifort_platforms]
169 desired_versions = conf.env.MSVC_VERSIONS or list(reversed(list(versiondict.keys())))
170 for version in desired_versions:
172 targets = versiondict[version]
175 for arch in platforms:
182 compiler,revision = version.rsplit(' ', 1)
183 return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu
184 conf.fatal('ifort: Impossible to find a valid architecture for building %r - %r' % (desired_versions, list(versiondict.keys())))
187 def get_ifort_version_win32(conf, compiler, version, target, vcvars):
191 except AttributeError:
193 batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
194 batfile.write("""@echo off
199 echo INCLUDE=%%INCLUDE%%
200 echo LIB=%%LIB%%;%%LIBPATH%%
201 """ % (vcvars,target))
202 sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
204 lines = sout.splitlines()
209 MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
211 if line.startswith('PATH='):
213 MSVC_PATH = path.split(';')
214 elif line.startswith('INCLUDE='):
215 MSVC_INCDIR = [i for i in line[8:].split(';') if i]
216 elif line.startswith('LIB='):
217 MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
218 if None in (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR):
219 conf.fatal('ifort: Could not find a valid architecture for building (get_ifort_version_win32)')
221 # Check if the compiler is usable at all.
222 # The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
223 env = dict(os.environ)
224 env.update(PATH = path)
225 compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
226 fc = conf.find_program(compiler_name, path_list=MSVC_PATH)
228 # delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
233 conf.cmd_and_log(fc + ['/help'], env=env)
235 st = traceback.format_exc()
237 conf.logger.error(st)
238 conf.fatal('ifort: Unicode error - check the code page?')
239 except Exception as e:
240 Logs.debug('ifort: get_ifort_version: %r %r %r -> failure %s', compiler, version, target, str(e))
241 conf.fatal('ifort: cannot run the compiler in get_ifort_version (run with -v to display errors)')
243 Logs.debug('ifort: get_ifort_version: %r %r %r -> OK', compiler, version, target)
245 conf.env[compiler_name] = ''
247 return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
249 class target_compiler(object):
251 Wraps a compiler configuration; call evaluate() to determine
252 whether the configuration is usable.
254 def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
256 :param ctx: configuration context to use to eventually get the version environment
257 :param compiler: compiler name
258 :param cpu: target cpu
259 :param version: compiler version number
261 :param bat: path to the batch file to run
262 :param callback: optional function to take the realized environment variables tup and map it (e.g. to combine other constant paths)
266 self.is_valid = False
269 self.compiler = compiler
271 self.version = version
272 self.bat_target = bat_target
274 self.callback = callback
281 vs = self.conf.get_ifort_version_win32(self.compiler, self.version, self.bat_target, self.bat)
282 except Errors.ConfigurationError:
283 self.is_valid = False
286 vs = self.callback(self, vs)
288 (self.bindirs, self.incdirs, self.libdirs) = vs
291 return str((self.bindirs, self.incdirs, self.libdirs))
294 return repr((self.bindirs, self.incdirs, self.libdirs))
297 def detect_ifort(self):
298 return self.setup_ifort(self.get_ifort_versions(False))
301 def get_ifort_versions(self, eval_and_save=True):
303 :return: platforms to compiler configurations
307 self.gather_ifort_versions(dct)
310 def _get_prog_names(self, compiler):
311 if compiler=='intel':
312 compiler_name = 'ifort'
313 linker_name = 'XILINK'
320 return compiler_name, linker_name, lib_name
323 def find_ifort_win32(conf):
324 # the autodetection is supposed to be performed before entering in this method
327 compiler = v.MSVC_COMPILER
328 version = v.MSVC_VERSION
330 compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
331 v.IFORT_MANIFEST = (compiler == 'intel' and version >= 11)
334 fc = conf.find_program(compiler_name, var='FC', path_list=path)
336 # before setting anything, check if the compiler is really intel fortran
337 env = dict(conf.environ)
339 env.update(PATH = ';'.join(path))
340 if not conf.cmd_and_log(fc + ['/nologo', '/help'], env=env):
341 conf.fatal('not intel fortran compiler could not be identified')
346 conf.find_program(linker_name, var='LINK_FC', path_list=path, mandatory=True)
349 conf.find_program(lib_name, path_list=path, var='AR', mandatory=True)
350 v.ARFLAGS = ['/nologo']
352 # manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
354 conf.find_program('MT', path_list=path, var='MT')
355 v.MTFLAGS = ['/nologo']
359 except Errors.WafError:
360 Logs.warn('Resource compiler not found. Compiling resource file is disabled')
362 #######################################################################################################
363 ##### conf above, build below
365 @after_method('apply_link')
367 def apply_flags_ifort(self):
369 Adds additional flags implied by msvc, such as subsystems and pdb files::
372 bld.stlib(source='main.c', target='bar', subsystem='gruik')
374 if not self.env.IFORT_WIN32 or not getattr(self, 'link_task', None):
377 is_static = isinstance(self.link_task, ccroot.stlink_task)
379 subsystem = getattr(self, 'subsystem', '')
381 subsystem = '/subsystem:%s' % subsystem
382 flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
383 self.env.append_value(flags, subsystem)
386 for f in self.env.LINKFLAGS:
389 pdbnode = self.link_task.outputs[0].change_ext('.pdb')
390 self.link_task.outputs.append(pdbnode)
392 if getattr(self, 'install_task', None):
393 self.pdb_install_task = self.add_install_files(install_to=self.install_task.install_to, install_from=pdbnode)
397 @feature('fcprogram', 'fcshlib', 'fcprogram_test')
398 @after_method('apply_link')
399 def apply_manifest_ifort(self):
401 Enables manifest embedding in Fortran DLLs when using ifort on Windows
402 See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx
404 if self.env.IFORT_WIN32 and getattr(self, 'link_task', None):
405 # it seems ifort.exe cannot be called for linking
406 self.link_task.env.FC = self.env.LINK_FC
408 if self.env.IFORT_WIN32 and self.env.IFORT_MANIFEST and getattr(self, 'link_task', None):
409 out_node = self.link_task.outputs[0]
410 man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
411 self.link_task.outputs.append(man_node)
412 self.env.DO_MANIFEST = True