1#! /usr/bin/env python 2# encoding: utf-8 3# DC 2008 4# Thomas Nagy 2016-2018 (ita) 5 6import os, re, traceback 7from waflib import Utils, Logs, Errors 8from waflib.Tools import fc, fc_config, fc_scan, ar, ccroot 9from waflib.Configure import conf 10from waflib.TaskGen import after_method, feature 11 12@conf 13def 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 19def 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 46def ifort_modifier_darwin(conf): 47 fc_config.fortran_modifier_darwin(conf) 48 49@conf 50def 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 57def 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 74def 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 102all_ifort_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')] 103"""List of icl platforms""" 104 105@conf 106def gather_ifort_versions(conf, versions): 107 """ 108 List compiler versions by looking up registry keys 109 """ 110 version_pattern = re.compile('^...?.?\....?.?') 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 158def 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 187def 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 195set INCLUDE= 196set LIB= 197call "%s" %s 198echo PATH=%%PATH%% 199echo INCLUDE=%%INCLUDE%% 200echo 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 249class 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 297def detect_ifort(self): 298 return self.setup_ifort(self.get_ifort_versions(False)) 299 300@conf 301def 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 310def _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 323def 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') 367def 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') 399def 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 414