ifort.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. #! /usr/bin/env python
  2. # encoding: utf-8
  3. # DC 2008
  4. # Thomas Nagy 2016-2018 (ita)
  5. import os, re, traceback
  6. from waflib import Utils, Logs, Errors
  7. from waflib.Tools import fc, fc_config, fc_scan, ar, ccroot
  8. from waflib.Configure import conf
  9. from waflib.TaskGen import after_method, feature
  10. @conf
  11. def find_ifort(conf):
  12. fc = conf.find_program(['ifx', 'ifort'], var='FC')
  13. conf.get_ifort_version(fc)
  14. conf.env.FC_NAME = 'IFORT'
  15. @conf
  16. def ifort_modifier_win32(self):
  17. v = self.env
  18. v.IFORT_WIN32 = True
  19. v.FCSTLIB_MARKER = ''
  20. v.FCSHLIB_MARKER = ''
  21. v.FCLIB_ST = v.FCSTLIB_ST = '%s.lib'
  22. v.FCLIBPATH_ST = v.STLIBPATH_ST = '/LIBPATH:%s'
  23. v.FCINCPATH_ST = '/I%s'
  24. v.FCDEFINES_ST = '/D%s'
  25. v.fcprogram_PATTERN = v.fcprogram_test_PATTERN = '%s.exe'
  26. v.fcshlib_PATTERN = '%s.dll'
  27. v.fcstlib_PATTERN = v.implib_PATTERN = '%s.lib'
  28. v.FCLNK_TGT_F = '/out:'
  29. v.FC_TGT_F = ['/c', '/o', '']
  30. v.FCFLAGS_fcshlib = ''
  31. v.LINKFLAGS_fcshlib = '/DLL'
  32. v.AR_TGT_F = '/out:'
  33. v.IMPLIB_ST = '/IMPLIB:%s'
  34. v.append_value('LINKFLAGS', '/subsystem:console')
  35. if v.IFORT_MANIFEST:
  36. v.append_value('LINKFLAGS', ['/MANIFEST'])
  37. @conf
  38. def ifort_modifier_darwin(conf):
  39. fc_config.fortran_modifier_darwin(conf)
  40. @conf
  41. def ifort_modifier_platform(conf):
  42. dest_os = conf.env.DEST_OS or Utils.unversioned_sys_platform()
  43. ifort_modifier_func = getattr(conf, 'ifort_modifier_' + dest_os, None)
  44. if ifort_modifier_func:
  45. ifort_modifier_func()
  46. @conf
  47. def get_ifort_version(conf, fc):
  48. """
  49. Detects the compiler version and sets ``conf.env.FC_VERSION``
  50. """
  51. version_re = re.compile(r"\bIntel\b.*\bVersion\s*(?P<major>\d*)\.(?P<minor>\d*)",re.I).search
  52. if Utils.is_win32:
  53. cmd = fc
  54. else:
  55. cmd = fc + ['-logo']
  56. out, err = fc_config.getoutput(conf, cmd, stdin=False)
  57. match = version_re(out) or version_re(err)
  58. if not match:
  59. conf.fatal('cannot determine ifort version.')
  60. k = match.groupdict()
  61. conf.env.FC_VERSION = (k['major'], k['minor'])
  62. def configure(conf):
  63. """
  64. Detects the Intel Fortran compilers
  65. """
  66. if Utils.is_win32:
  67. compiler, version, path, includes, libdirs, arch = conf.detect_ifort()
  68. v = conf.env
  69. v.DEST_CPU = arch
  70. v.PATH = path
  71. v.INCLUDES = includes
  72. v.LIBPATH = libdirs
  73. v.MSVC_COMPILER = compiler
  74. try:
  75. v.MSVC_VERSION = float(version)
  76. except ValueError:
  77. v.MSVC_VERSION = float(version[:-3])
  78. conf.find_ifort_win32()
  79. conf.ifort_modifier_win32()
  80. else:
  81. conf.find_ifort()
  82. conf.find_program('xiar', var='AR')
  83. conf.find_ar()
  84. conf.fc_flags()
  85. conf.fc_add_flags()
  86. conf.ifort_modifier_platform()
  87. all_ifort_platforms = [('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86')]
  88. """List of icl platforms"""
  89. @conf
  90. def gather_ifort_versions(conf, versions):
  91. """
  92. List compiler versions by looking up registry keys
  93. """
  94. version_pattern = re.compile(r'^...?.?\....?.?')
  95. try:
  96. all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran')
  97. except OSError:
  98. try:
  99. all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\Fortran')
  100. except OSError:
  101. return
  102. index = 0
  103. while 1:
  104. try:
  105. version = Utils.winreg.EnumKey(all_versions, index)
  106. except OSError:
  107. break
  108. index += 1
  109. if not version_pattern.match(version):
  110. continue
  111. targets = {}
  112. for target,arch in all_ifort_platforms:
  113. if target=='intel64':
  114. targetDir='EM64T_NATIVE'
  115. else:
  116. targetDir=target
  117. try:
  118. Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir)
  119. icl_version=Utils.winreg.OpenKey(all_versions,version)
  120. path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
  121. except OSError:
  122. pass
  123. else:
  124. batch_file = os.path.join(path, 'bin', 'ifortvars.bat')
  125. if os.path.isfile(batch_file):
  126. targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
  127. else:
  128. batch_file = os.path.join(path, 'env', 'vars.bat')
  129. if os.path.isfile(batch_file):
  130. targets[target] = target_compiler(conf, 'oneapi', arch, version, target, batch_file)
  131. for target,arch in all_ifort_platforms:
  132. try:
  133. icl_version = Utils.winreg.OpenKey(all_versions, version+'\\'+target)
  134. path,type = Utils.winreg.QueryValueEx(icl_version,'ProductDir')
  135. except OSError:
  136. continue
  137. else:
  138. batch_file=os.path.join(path,'bin','ifortvars.bat')
  139. if os.path.isfile(batch_file):
  140. targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
  141. major = version[0:2]
  142. versions['intel ' + major] = targets
  143. @conf
  144. def setup_ifort(conf, versiondict):
  145. """
  146. Checks installed compilers and targets and returns the first combination from the user's
  147. options, env, or the global supported lists that checks.
  148. :param versiondict: dict(platform -> dict(architecture -> configuration))
  149. :type versiondict: dict(string -> dict(string -> target_compiler)
  150. :return: the compiler, revision, path, include dirs, library paths and target architecture
  151. :rtype: tuple of strings
  152. """
  153. platforms = Utils.to_list(conf.env.MSVC_TARGETS) or [i for i,j in all_ifort_platforms]
  154. desired_versions = conf.env.MSVC_VERSIONS or list(reversed(list(versiondict.keys())))
  155. for version in desired_versions:
  156. try:
  157. targets = versiondict[version]
  158. except KeyError:
  159. continue
  160. for arch in platforms:
  161. try:
  162. cfg = targets[arch]
  163. except KeyError:
  164. continue
  165. cfg.evaluate()
  166. if cfg.is_valid:
  167. compiler,revision = version.rsplit(' ', 1)
  168. return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu
  169. conf.fatal('ifort: Impossible to find a valid architecture for building %r - %r' % (desired_versions, list(versiondict.keys())))
  170. @conf
  171. def get_ifort_version_win32(conf, compiler, version, target, vcvars):
  172. # FIXME hack
  173. try:
  174. conf.msvc_cnt += 1
  175. except AttributeError:
  176. conf.msvc_cnt = 1
  177. batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
  178. batfile.write("""@echo off
  179. set INCLUDE=
  180. set LIB=
  181. call "%s" %s
  182. echo PATH=%%PATH%%
  183. echo INCLUDE=%%INCLUDE%%
  184. echo LIB=%%LIB%%;%%LIBPATH%%
  185. """ % (vcvars,target))
  186. sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
  187. batfile.delete()
  188. lines = sout.splitlines()
  189. if not lines[0]:
  190. lines.pop(0)
  191. MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
  192. for line in lines:
  193. if line.startswith('PATH='):
  194. path = line[5:]
  195. MSVC_PATH = path.split(';')
  196. elif line.startswith('INCLUDE='):
  197. MSVC_INCDIR = [i for i in line[8:].split(';') if i]
  198. elif line.startswith('LIB='):
  199. MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
  200. if None in (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR):
  201. conf.fatal('ifort: Could not find a valid architecture for building (get_ifort_version_win32)')
  202. # Check if the compiler is usable at all.
  203. # The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
  204. env = dict(os.environ)
  205. env.update(PATH = path)
  206. compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
  207. fc = conf.find_program(compiler_name, path_list=MSVC_PATH)
  208. # delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
  209. if 'CL' in env:
  210. del(env['CL'])
  211. try:
  212. conf.cmd_and_log(fc + ['/help'], env=env)
  213. except UnicodeError:
  214. st = traceback.format_exc()
  215. if conf.logger:
  216. conf.logger.error(st)
  217. conf.fatal('ifort: Unicode error - check the code page?')
  218. except Exception as e:
  219. Logs.debug('ifort: get_ifort_version: %r %r %r -> failure %s', compiler, version, target, str(e))
  220. conf.fatal('ifort: cannot run the compiler in get_ifort_version (run with -v to display errors)')
  221. else:
  222. Logs.debug('ifort: get_ifort_version: %r %r %r -> OK', compiler, version, target)
  223. finally:
  224. conf.env[compiler_name] = ''
  225. return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
  226. class target_compiler(object):
  227. """
  228. Wraps a compiler configuration; call evaluate() to determine
  229. whether the configuration is usable.
  230. """
  231. def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
  232. """
  233. :param ctx: configuration context to use to eventually get the version environment
  234. :param compiler: compiler name
  235. :param cpu: target cpu
  236. :param version: compiler version number
  237. :param bat_target: ?
  238. :param bat: path to the batch file to run
  239. :param callback: optional function to take the realized environment variables tup and map it (e.g. to combine other constant paths)
  240. """
  241. self.conf = ctx
  242. self.name = None
  243. self.is_valid = False
  244. self.is_done = False
  245. self.compiler = compiler
  246. self.cpu = cpu
  247. self.version = version
  248. self.bat_target = bat_target
  249. self.bat = bat
  250. self.callback = callback
  251. def evaluate(self):
  252. if self.is_done:
  253. return
  254. self.is_done = True
  255. try:
  256. vs = self.conf.get_ifort_version_win32(self.compiler, self.version, self.bat_target, self.bat)
  257. except Errors.ConfigurationError:
  258. self.is_valid = False
  259. return
  260. if self.callback:
  261. vs = self.callback(self, vs)
  262. self.is_valid = True
  263. (self.bindirs, self.incdirs, self.libdirs) = vs
  264. def __str__(self):
  265. return str((self.bindirs, self.incdirs, self.libdirs))
  266. def __repr__(self):
  267. return repr((self.bindirs, self.incdirs, self.libdirs))
  268. @conf
  269. def detect_ifort(self):
  270. return self.setup_ifort(self.get_ifort_versions(False))
  271. @conf
  272. def get_ifort_versions(self, eval_and_save=True):
  273. """
  274. :return: platforms to compiler configurations
  275. :rtype: dict
  276. """
  277. dct = {}
  278. self.gather_ifort_versions(dct)
  279. return dct
  280. def _get_prog_names(self, compiler):
  281. if compiler == 'oneapi':
  282. compiler_name = 'ifx'
  283. linker_name = 'XILINK'
  284. lib_name = 'XILIB'
  285. elif compiler == 'intel':
  286. compiler_name = 'ifort'
  287. linker_name = 'XILINK'
  288. lib_name = 'XILIB'
  289. else:
  290. # assumes CL.exe
  291. compiler_name = 'CL'
  292. linker_name = 'LINK'
  293. lib_name = 'LIB'
  294. return compiler_name, linker_name, lib_name
  295. @conf
  296. def find_ifort_win32(conf):
  297. # the autodetection is supposed to be performed before entering in this method
  298. v = conf.env
  299. path = v.PATH
  300. compiler = v.MSVC_COMPILER
  301. version = v.MSVC_VERSION
  302. compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
  303. v.IFORT_MANIFEST = (compiler == 'intel' and version >= 11)
  304. # compiler
  305. fc = conf.find_program(compiler_name, var='FC', path_list=path)
  306. # before setting anything, check if the compiler is really intel fortran
  307. env = dict(conf.environ)
  308. if path:
  309. env.update(PATH = ';'.join(path))
  310. if not conf.cmd_and_log(fc + ['/nologo', '/help'], env=env):
  311. conf.fatal('not intel fortran compiler could not be identified')
  312. v.FC_NAME = 'IFORT'
  313. if not v.LINK_FC:
  314. conf.find_program(linker_name, var='LINK_FC', path_list=path, mandatory=True)
  315. if not v.AR:
  316. conf.find_program(lib_name, path_list=path, var='AR', mandatory=True)
  317. v.ARFLAGS = ['/nologo']
  318. # manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
  319. if v.IFORT_MANIFEST:
  320. conf.find_program('MT', path_list=path, var='MT')
  321. v.MTFLAGS = ['/nologo']
  322. try:
  323. conf.load('winres')
  324. except Errors.WafError:
  325. Logs.warn('Resource compiler not found. Compiling resource file is disabled')
  326. #######################################################################################################
  327. ##### conf above, build below
  328. @after_method('apply_link')
  329. @feature('fc')
  330. def apply_flags_ifort(self):
  331. """
  332. Adds additional flags implied by msvc, such as subsystems and pdb files::
  333. def build(bld):
  334. bld.stlib(source='main.c', target='bar', subsystem='gruik')
  335. """
  336. if not self.env.IFORT_WIN32 or not getattr(self, 'link_task', None):
  337. return
  338. is_static = isinstance(self.link_task, ccroot.stlink_task)
  339. subsystem = getattr(self, 'subsystem', '')
  340. if subsystem:
  341. subsystem = '/subsystem:%s' % subsystem
  342. flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
  343. self.env.append_value(flags, subsystem)
  344. if not is_static:
  345. for f in self.env.LINKFLAGS:
  346. d = f.lower()
  347. if d[1:] == 'debug':
  348. pdbnode = self.link_task.outputs[0].change_ext('.pdb')
  349. self.link_task.outputs.append(pdbnode)
  350. if getattr(self, 'install_task', None):
  351. self.pdb_install_task = self.add_install_files(install_to=self.install_task.install_to, install_from=pdbnode)
  352. break
  353. @feature('fcprogram', 'fcshlib', 'fcprogram_test')
  354. @after_method('apply_link')
  355. def apply_manifest_ifort(self):
  356. """
  357. Enables manifest embedding in Fortran DLLs when using ifort on Windows
  358. See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx
  359. """
  360. if self.env.IFORT_WIN32 and getattr(self, 'link_task', None):
  361. # it seems ifort.exe cannot be called for linking
  362. self.link_task.env.FC = self.env.LINK_FC
  363. if self.env.IFORT_WIN32 and self.env.IFORT_MANIFEST and getattr(self, 'link_task', None):
  364. out_node = self.link_task.outputs[0]
  365. man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
  366. self.link_task.outputs.append(man_node)
  367. self.env.DO_MANIFEST = True