74934f3f6618a698312fe91434a070902a90a9bb
[samba.git] / third_party / waf / waflib / Tools / ifort.py
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('^...?.?\....?.?')
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