waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

ifort.py (12473B)


      1 #! /usr/bin/env python
      2 # encoding: utf-8
      3 # DC 2008
      4 # Thomas Nagy 2016-2018 (ita)
      5 
      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
     11 
     12 @conf
     13 def find_ifort(conf):
     14 	fc = conf.find_program('ifort', var='FC')
     15 	conf.get_ifort_version(fc)
     16 	conf.env.FC_NAME = 'IFORT'
     17 
     18 @conf
     19 def ifort_modifier_win32(self):
     20 	v = self.env
     21 	v.IFORT_WIN32 = True
     22 	v.FCSTLIB_MARKER = ''
     23 	v.FCSHLIB_MARKER = ''
     24 
     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'
     29 
     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'
     33 
     34 	v.FCLNK_TGT_F = '/out:'
     35 	v.FC_TGT_F = ['/c', '/o', '']
     36 	v.FCFLAGS_fcshlib = ''
     37 	v.LINKFLAGS_fcshlib = '/DLL'
     38 	v.AR_TGT_F = '/out:'
     39 	v.IMPLIB_ST = '/IMPLIB:%s'
     40 
     41 	v.append_value('LINKFLAGS', '/subsystem:console')
     42 	if v.IFORT_MANIFEST:
     43 		v.append_value('LINKFLAGS', ['/MANIFEST'])
     44 
     45 @conf
     46 def ifort_modifier_darwin(conf):
     47 	fc_config.fortran_modifier_darwin(conf)
     48 
     49 @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:
     54 		ifort_modifier_func()
     55 
     56 @conf
     57 def get_ifort_version(conf, fc):
     58 	"""
     59 	Detects the compiler version and sets ``conf.env.FC_VERSION``
     60 	"""
     61 	version_re = re.compile(r"\bIntel\b.*\bVersion\s*(?P<major>\d*)\.(?P<minor>\d*)",re.I).search
     62 	if Utils.is_win32:
     63 		cmd = fc
     64 	else:
     65 		cmd = fc + ['-logo']
     66 
     67 	out, err = fc_config.getoutput(conf, cmd, stdin=False)
     68 	match = version_re(out) or version_re(err)
     69 	if not match:
     70 		conf.fatal('cannot determine ifort version.')
     71 	k = match.groupdict()
     72 	conf.env.FC_VERSION = (k['major'], k['minor'])
     73 
     74 def configure(conf):
     75 	"""
     76 	Detects the Intel Fortran compilers
     77 	"""
     78 	if Utils.is_win32:
     79 		compiler, version, path, includes, libdirs, arch = conf.detect_ifort()
     80 		v = conf.env
     81 		v.DEST_CPU = arch
     82 		v.PATH = path
     83 		v.INCLUDES = includes
     84 		v.LIBPATH = libdirs
     85 		v.MSVC_COMPILER = compiler
     86 		try:
     87 			v.MSVC_VERSION = float(version)
     88 		except ValueError:
     89 			v.MSVC_VERSION = float(version[:-3])
     90 
     91 		conf.find_ifort_win32()
     92 		conf.ifort_modifier_win32()
     93 	else:
     94 		conf.find_ifort()
     95 		conf.find_program('xiar', var='AR')
     96 		conf.find_ar()
     97 		conf.fc_flags()
     98 		conf.fc_add_flags()
     99 		conf.ifort_modifier_platform()
    100 
    101 
    102 all_ifort_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
    103 """List of icl platforms"""
    104 
    105 @conf
    106 def gather_ifort_versions(conf, versions):
    107 	"""
    108 	List compiler versions by looking up registry keys
    109 	"""
    110 	version_pattern = re.compile(r'^...?.?\....?.?')
    111 	try:
    112 		all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran')
    113 	except OSError:
    114 		try:
    115 			all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\Fortran')
    116 		except OSError:
    117 			return
    118 	index = 0
    119 	while 1:
    120 		try:
    121 			version = Utils.winreg.EnumKey(all_versions, index)
    122 		except OSError:
    123 			break
    124 		index += 1
    125 		if not version_pattern.match(version):
    126 			continue
    127 		targets = {}
    128 		for target,arch in all_ifort_platforms:
    129 			if target=='intel64':
    130 				targetDir='EM64T_NATIVE'
    131 			else:
    132 				targetDir=target
    133 			try:
    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')
    137 			except OSError:
    138 				pass
    139 			else:
    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)
    143 
    144 		for target,arch in all_ifort_platforms:
    145 			try:
    146 				icl_version = Utils.winreg.OpenKey(all_versions, version+'\\'+target)
    147 				path,type = Utils.winreg.QueryValueEx(icl_version,'ProductDir')
    148 			except OSError:
    149 				continue
    150 			else:
    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)
    154 		major = version[0:2]
    155 		versions['intel ' + major] = targets
    156 
    157 @conf
    158 def setup_ifort(conf, versiondict):
    159 	"""
    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.
    162 
    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
    167 	"""
    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:
    171 		try:
    172 			targets = versiondict[version]
    173 		except KeyError:
    174 			continue
    175 		for arch in platforms:
    176 			try:
    177 				cfg = targets[arch]
    178 			except KeyError:
    179 				continue
    180 			cfg.evaluate()
    181 			if cfg.is_valid:
    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())))
    185 
    186 @conf
    187 def get_ifort_version_win32(conf, compiler, version, target, vcvars):
    188 	# FIXME hack
    189 	try:
    190 		conf.msvc_cnt += 1
    191 	except AttributeError:
    192 		conf.msvc_cnt = 1
    193 	batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
    194 	batfile.write("""@echo off
    195 set INCLUDE=
    196 set LIB=
    197 call "%s" %s
    198 echo PATH=%%PATH%%
    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()])
    203 	batfile.delete()
    204 	lines = sout.splitlines()
    205 
    206 	if not lines[0]:
    207 		lines.pop(0)
    208 
    209 	MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
    210 	for line in lines:
    211 		if line.startswith('PATH='):
    212 			path = line[5:]
    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)')
    220 
    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)
    227 
    228 	# delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
    229 	if 'CL' in env:
    230 		del(env['CL'])
    231 
    232 	try:
    233 		conf.cmd_and_log(fc + ['/help'], env=env)
    234 	except UnicodeError:
    235 		st = traceback.format_exc()
    236 		if conf.logger:
    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)')
    242 	else:
    243 		Logs.debug('ifort: get_ifort_version: %r %r %r -> OK', compiler, version, target)
    244 	finally:
    245 		conf.env[compiler_name] = ''
    246 
    247 	return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
    248 
    249 class target_compiler(object):
    250 	"""
    251 	Wraps a compiler configuration; call evaluate() to determine
    252 	whether the configuration is usable.
    253 	"""
    254 	def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
    255 		"""
    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
    260 		:param bat_target: ?
    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)
    263 		"""
    264 		self.conf = ctx
    265 		self.name = None
    266 		self.is_valid = False
    267 		self.is_done = False
    268 
    269 		self.compiler = compiler
    270 		self.cpu = cpu
    271 		self.version = version
    272 		self.bat_target = bat_target
    273 		self.bat = bat
    274 		self.callback = callback
    275 
    276 	def evaluate(self):
    277 		if self.is_done:
    278 			return
    279 		self.is_done = True
    280 		try:
    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
    284 			return
    285 		if self.callback:
    286 			vs = self.callback(self, vs)
    287 		self.is_valid = True
    288 		(self.bindirs, self.incdirs, self.libdirs) = vs
    289 
    290 	def __str__(self):
    291 		return str((self.bindirs, self.incdirs, self.libdirs))
    292 
    293 	def __repr__(self):
    294 		return repr((self.bindirs, self.incdirs, self.libdirs))
    295 
    296 @conf
    297 def detect_ifort(self):
    298 	return self.setup_ifort(self.get_ifort_versions(False))
    299 
    300 @conf
    301 def get_ifort_versions(self, eval_and_save=True):
    302 	"""
    303 	:return: platforms to compiler configurations
    304 	:rtype: dict
    305 	"""
    306 	dct = {}
    307 	self.gather_ifort_versions(dct)
    308 	return dct
    309 
    310 def _get_prog_names(self, compiler):
    311 	if compiler=='intel':
    312 		compiler_name = 'ifort'
    313 		linker_name = 'XILINK'
    314 		lib_name = 'XILIB'
    315 	else:
    316 		# assumes CL.exe
    317 		compiler_name = 'CL'
    318 		linker_name = 'LINK'
    319 		lib_name = 'LIB'
    320 	return compiler_name, linker_name, lib_name
    321 
    322 @conf
    323 def find_ifort_win32(conf):
    324 	# the autodetection is supposed to be performed before entering in this method
    325 	v = conf.env
    326 	path = v.PATH
    327 	compiler = v.MSVC_COMPILER
    328 	version = v.MSVC_VERSION
    329 
    330 	compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
    331 	v.IFORT_MANIFEST = (compiler == 'intel' and version >= 11)
    332 
    333 	# compiler
    334 	fc = conf.find_program(compiler_name, var='FC', path_list=path)
    335 
    336 	# before setting anything, check if the compiler is really intel fortran
    337 	env = dict(conf.environ)
    338 	if path:
    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')
    342 
    343 	v.FC_NAME = 'IFORT'
    344 
    345 	if not v.LINK_FC:
    346 		conf.find_program(linker_name, var='LINK_FC', path_list=path, mandatory=True)
    347 
    348 	if not v.AR:
    349 		conf.find_program(lib_name, path_list=path, var='AR', mandatory=True)
    350 		v.ARFLAGS = ['/nologo']
    351 
    352 	# manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
    353 	if v.IFORT_MANIFEST:
    354 		conf.find_program('MT', path_list=path, var='MT')
    355 		v.MTFLAGS = ['/nologo']
    356 
    357 	try:
    358 		conf.load('winres')
    359 	except Errors.WafError:
    360 		Logs.warn('Resource compiler not found. Compiling resource file is disabled')
    361 
    362 #######################################################################################################
    363 ##### conf above, build below
    364 
    365 @after_method('apply_link')
    366 @feature('fc')
    367 def apply_flags_ifort(self):
    368 	"""
    369 	Adds additional flags implied by msvc, such as subsystems and pdb files::
    370 
    371 		def build(bld):
    372 			bld.stlib(source='main.c', target='bar', subsystem='gruik')
    373 	"""
    374 	if not self.env.IFORT_WIN32 or not getattr(self, 'link_task', None):
    375 		return
    376 
    377 	is_static = isinstance(self.link_task, ccroot.stlink_task)
    378 
    379 	subsystem = getattr(self, 'subsystem', '')
    380 	if subsystem:
    381 		subsystem = '/subsystem:%s' % subsystem
    382 		flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
    383 		self.env.append_value(flags, subsystem)
    384 
    385 	if not is_static:
    386 		for f in self.env.LINKFLAGS:
    387 			d = f.lower()
    388 			if d[1:] == 'debug':
    389 				pdbnode = self.link_task.outputs[0].change_ext('.pdb')
    390 				self.link_task.outputs.append(pdbnode)
    391 
    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)
    394 
    395 				break
    396 
    397 @feature('fcprogram', 'fcshlib', 'fcprogram_test')
    398 @after_method('apply_link')
    399 def apply_manifest_ifort(self):
    400 	"""
    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
    403 	"""
    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
    407 
    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
    413