third_party:waf: update to upstream 2.0.4 release
[amitay/samba.git] / third_party / waf / waflib / Tools / msvc.py
1 #! /usr/bin/env python
2 # encoding: utf-8
3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
4
5 #!/usr/bin/env python
6 # encoding: utf-8
7 # Carlos Rafael Giani, 2006 (dv)
8 # Tamas Pal, 2007 (folti)
9 # Nicolas Mercier, 2009
10 # Matt Clarkson, 2012
11
12 """
13 Microsoft Visual C++/Intel C++ compiler support
14
15 Usage::
16
17         $ waf configure --msvc_version="msvc 10.0,msvc 9.0" --msvc_target="x64"
18
19 or::
20
21         def configure(conf):
22                 conf.env.MSVC_VERSIONS = ['msvc 10.0', 'msvc 9.0', 'msvc 8.0', 'msvc 7.1', 'msvc 7.0', 'msvc 6.0', 'wsdk 7.0', 'intel 11', 'PocketPC 9.0', 'Smartphone 8.0']
23                 conf.env.MSVC_TARGETS = ['x64']
24                 conf.load('msvc')
25
26 or::
27
28         def configure(conf):
29                 conf.load('msvc', funs='no_autodetect')
30                 conf.check_lib_msvc('gdi32')
31                 conf.check_libs_msvc('kernel32 user32')
32         def build(bld):
33                 tg = bld.program(source='main.c', target='app', use='KERNEL32 USER32 GDI32')
34
35 Platforms and targets will be tested in the order they appear;
36 the first good configuration will be used.
37
38 To force testing all the configurations that are not used, use the ``--no-msvc-lazy`` option
39 or set ``conf.env.MSVC_LAZY_AUTODETECT=False``.
40
41 Supported platforms: ia64, x64, x86, x86_amd64, x86_ia64, x86_arm, amd64_x86, amd64_arm
42
43 Compilers supported:
44
45 * msvc       => Visual Studio, versions 6.0 (VC 98, VC .NET 2002) to 15 (Visual Studio 2017)
46 * wsdk       => Windows SDK, versions 6.0, 6.1, 7.0, 7.1, 8.0
47 * icl        => Intel compiler, versions 9, 10, 11, 13
48 * winphone   => Visual Studio to target Windows Phone 8 native (version 8.0 for now)
49 * Smartphone => Compiler/SDK for Smartphone devices (armv4/v4i)
50 * PocketPC   => Compiler/SDK for PocketPC devices (armv4/v4i)
51
52 To use WAF in a VS2008 Make file project (see http://code.google.com/p/waf/issues/detail?id=894)
53 You may consider to set the environment variable "VS_UNICODE_OUTPUT" to nothing before calling waf.
54 So in your project settings use something like 'cmd.exe /C "set VS_UNICODE_OUTPUT=& set PYTHONUNBUFFERED=true & waf build"'.
55 cmd.exe  /C  "chcp 1252 & set PYTHONUNBUFFERED=true && set && waf  configure"
56 Setting PYTHONUNBUFFERED gives the unbuffered output.
57 """
58
59 import os, sys, re, traceback
60 from waflib import Utils, Logs, Options, Errors
61 from waflib.TaskGen import after_method, feature
62
63 from waflib.Configure import conf
64 from waflib.Tools import ccroot, c, cxx, ar
65
66 g_msvc_systemlibs = '''
67 aclui activeds ad1 adptif adsiid advapi32 asycfilt authz bhsupp bits bufferoverflowu cabinet
68 cap certadm certidl ciuuid clusapi comctl32 comdlg32 comsupp comsuppd comsuppw comsuppwd comsvcs
69 credui crypt32 cryptnet cryptui d3d8thk daouuid dbgeng dbghelp dciman32 ddao35 ddao35d
70 ddao35u ddao35ud delayimp dhcpcsvc dhcpsapi dlcapi dnsapi dsprop dsuiext dtchelp
71 faultrep fcachdll fci fdi framedyd framedyn gdi32 gdiplus glauxglu32 gpedit gpmuuid
72 gtrts32w gtrtst32hlink htmlhelp httpapi icm32 icmui imagehlp imm32 iphlpapi iprop
73 kernel32 ksguid ksproxy ksuser libcmt libcmtd libcpmt libcpmtd loadperf lz32 mapi
74 mapi32 mgmtapi minidump mmc mobsync mpr mprapi mqoa mqrt msacm32 mscms mscoree
75 msdasc msimg32 msrating mstask msvcmrt msvcurt msvcurtd mswsock msxml2 mtx mtxdm
76 netapi32 nmapinmsupp npptools ntdsapi ntdsbcli ntmsapi ntquery odbc32 odbcbcp
77 odbccp32 oldnames ole32 oleacc oleaut32 oledb oledlgolepro32 opends60 opengl32
78 osptk parser pdh penter pgobootrun pgort powrprof psapi ptrustm ptrustmd ptrustu
79 ptrustud qosname rasapi32 rasdlg rassapi resutils riched20 rpcndr rpcns4 rpcrt4 rtm
80 rtutils runtmchk scarddlg scrnsave scrnsavw secur32 sensapi setupapi sfc shell32
81 shfolder shlwapi sisbkup snmpapi sporder srclient sti strsafe svcguid tapi32 thunk32
82 traffic unicows url urlmon user32 userenv usp10 uuid uxtheme vcomp vcompd vdmdbg
83 version vfw32 wbemuuid  webpost wiaguid wininet winmm winscard winspool winstrm
84 wintrust wldap32 wmiutils wow32 ws2_32 wsnmp32 wsock32 wst wtsapi32 xaswitch xolehlp
85 '''.split()
86 """importlibs provided by MSVC/Platform SDK. Do NOT search them"""
87
88 all_msvc_platforms = [  ('x64', 'amd64'), ('x86', 'x86'), ('ia64', 'ia64'),
89                                                 ('x86_amd64', 'amd64'), ('x86_ia64', 'ia64'), ('x86_arm', 'arm'), ('x86_arm64', 'arm64'),
90                                                 ('amd64_x86', 'x86'), ('amd64_arm', 'arm'), ('amd64_arm64', 'arm64') ]
91 """List of msvc platforms"""
92
93 all_wince_platforms = [ ('armv4', 'arm'), ('armv4i', 'arm'), ('mipsii', 'mips'), ('mipsii_fp', 'mips'), ('mipsiv', 'mips'), ('mipsiv_fp', 'mips'), ('sh4', 'sh'), ('x86', 'cex86') ]
94 """List of wince platforms"""
95
96 all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
97 """List of icl platforms"""
98
99 def options(opt):
100         opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default='')
101         opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='')
102         opt.add_option('--no-msvc-lazy', action='store_false', help = 'lazily check msvc target environments', default=True, dest='msvc_lazy')
103
104 @conf
105 def setup_msvc(conf, versiondict):
106         """
107         Checks installed compilers and targets and returns the first combination from the user's
108         options, env, or the global supported lists that checks.
109
110         :param versiondict: dict(platform -> dict(architecture -> configuration))
111         :type versiondict: dict(string -> dict(string -> target_compiler)
112         :return: the compiler, revision, path, include dirs, library paths and target architecture
113         :rtype: tuple of strings
114         """
115         platforms = getattr(Options.options, 'msvc_targets', '').split(',')
116         if platforms == ['']:
117                 platforms=Utils.to_list(conf.env.MSVC_TARGETS) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms]
118         desired_versions = getattr(Options.options, 'msvc_version', '').split(',')
119         if desired_versions == ['']:
120                 desired_versions = conf.env.MSVC_VERSIONS or list(reversed(sorted(versiondict.keys())))
121
122         # Override lazy detection by evaluating after the fact.
123         lazy_detect = getattr(Options.options, 'msvc_lazy', True)
124         if conf.env.MSVC_LAZY_AUTODETECT is False:
125                 lazy_detect = False
126
127         if not lazy_detect:
128                 for val in versiondict.values():
129                         for arch in list(val.keys()):
130                                 cfg = val[arch]
131                                 cfg.evaluate()
132                                 if not cfg.is_valid:
133                                         del val[arch]
134                 conf.env.MSVC_INSTALLED_VERSIONS = versiondict
135
136         for version in desired_versions:
137                 Logs.debug('msvc: detecting %r - %r', version, desired_versions)
138                 try:
139                         targets = versiondict[version]
140                 except KeyError:
141                         continue
142
143                 seen = set()
144                 for arch in platforms:
145                         if arch in seen:
146                                 continue
147                         else:
148                                 seen.add(arch)
149                         try:
150                                 cfg = targets[arch]
151                         except KeyError:
152                                 continue
153
154                         cfg.evaluate()
155                         if cfg.is_valid:
156                                 compiler,revision = version.rsplit(' ', 1)
157                                 return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu
158         conf.fatal('msvc: Impossible to find a valid architecture for building %r - %r' % (desired_versions, list(versiondict.keys())))
159
160 @conf
161 def get_msvc_version(conf, compiler, version, target, vcvars):
162         """
163         Checks that an installed compiler actually runs and uses vcvars to obtain the
164         environment needed by the compiler.
165
166         :param compiler: compiler type, for looking up the executable name
167         :param version: compiler version, for debugging only
168         :param target: target architecture
169         :param vcvars: batch file to run to check the environment
170         :return: the location of the compiler executable, the location of include dirs, and the library paths
171         :rtype: tuple of strings
172         """
173         Logs.debug('msvc: get_msvc_version: %r %r %r', compiler, version, target)
174
175         try:
176                 conf.msvc_cnt += 1
177         except AttributeError:
178                 conf.msvc_cnt = 1
179         batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
180         batfile.write("""@echo off
181 set INCLUDE=
182 set LIB=
183 call "%s" %s
184 echo PATH=%%PATH%%
185 echo INCLUDE=%%INCLUDE%%
186 echo LIB=%%LIB%%;%%LIBPATH%%
187 """ % (vcvars,target))
188         sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
189         lines = sout.splitlines()
190
191         if not lines[0]:
192                 lines.pop(0)
193
194         MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
195         for line in lines:
196                 if line.startswith('PATH='):
197                         path = line[5:]
198                         MSVC_PATH = path.split(';')
199                 elif line.startswith('INCLUDE='):
200                         MSVC_INCDIR = [i for i in line[8:].split(';') if i]
201                 elif line.startswith('LIB='):
202                         MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
203         if None in (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR):
204                 conf.fatal('msvc: Could not find a valid architecture for building (get_msvc_version_3)')
205
206         # Check if the compiler is usable at all.
207         # The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
208         env = dict(os.environ)
209         env.update(PATH = path)
210         compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
211         cxx = conf.find_program(compiler_name, path_list=MSVC_PATH)
212
213         # delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
214         if 'CL' in env:
215                 del(env['CL'])
216
217         try:
218                 conf.cmd_and_log(cxx + ['/help'], env=env)
219         except UnicodeError:
220                 st = traceback.format_exc()
221                 if conf.logger:
222                         conf.logger.error(st)
223                 conf.fatal('msvc: Unicode error - check the code page?')
224         except Exception as e:
225                 Logs.debug('msvc: get_msvc_version: %r %r %r -> failure %s', compiler, version, target, str(e))
226                 conf.fatal('msvc: cannot run the compiler in get_msvc_version (run with -v to display errors)')
227         else:
228                 Logs.debug('msvc: get_msvc_version: %r %r %r -> OK', compiler, version, target)
229         finally:
230                 conf.env[compiler_name] = ''
231
232         return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
233
234 def gather_wince_supported_platforms():
235         """
236         Checks SmartPhones SDKs
237
238         :param versions: list to modify
239         :type versions: list
240         """
241         supported_wince_platforms = []
242         try:
243                 ce_sdk = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Windows CE Tools\\SDKs')
244         except OSError:
245                 try:
246                         ce_sdk = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows CE Tools\\SDKs')
247                 except OSError:
248                         ce_sdk = ''
249         if not ce_sdk:
250                 return supported_wince_platforms
251
252         index = 0
253         while 1:
254                 try:
255                         sdk_device = Utils.winreg.EnumKey(ce_sdk, index)
256                         sdk = Utils.winreg.OpenKey(ce_sdk, sdk_device)
257                 except OSError:
258                         break
259                 index += 1
260                 try:
261                         path,type = Utils.winreg.QueryValueEx(sdk, 'SDKRootDir')
262                 except OSError:
263                         try:
264                                 path,type = Utils.winreg.QueryValueEx(sdk,'SDKInformation')
265                         except OSError:
266                                 continue
267                         path,xml = os.path.split(path)
268                 path = str(path)
269                 path,device = os.path.split(path)
270                 if not device:
271                         path,device = os.path.split(path)
272                 platforms = []
273                 for arch,compiler in all_wince_platforms:
274                         if os.path.isdir(os.path.join(path, device, 'Lib', arch)):
275                                 platforms.append((arch, compiler, os.path.join(path, device, 'Include', arch), os.path.join(path, device, 'Lib', arch)))
276                 if platforms:
277                         supported_wince_platforms.append((device, platforms))
278         return supported_wince_platforms
279
280 def gather_msvc_detected_versions():
281         #Detected MSVC versions!
282         version_pattern = re.compile('^(\d\d?\.\d\d?)(Exp)?$')
283         detected_versions = []
284         for vcver,vcvar in (('VCExpress','Exp'), ('VisualStudio','')):
285                 prefix = 'SOFTWARE\\Wow6432node\\Microsoft\\' + vcver
286                 try:
287                         all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, prefix)
288                 except OSError:
289                         prefix = 'SOFTWARE\\Microsoft\\' + vcver
290                         try:
291                                 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, prefix)
292                         except OSError:
293                                 continue
294
295                 index = 0
296                 while 1:
297                         try:
298                                 version = Utils.winreg.EnumKey(all_versions, index)
299                         except OSError:
300                                 break
301                         index += 1
302                         match = version_pattern.match(version)
303                         if match:
304                                 versionnumber = float(match.group(1))
305                         else:
306                                 continue
307                         detected_versions.append((versionnumber, version+vcvar, prefix+'\\'+version))
308         def fun(tup):
309                 return tup[0]
310
311         detected_versions.sort(key = fun)
312         return detected_versions
313
314 class target_compiler(object):
315         """
316         Wrap a compiler configuration; call evaluate() to determine
317         whether the configuration is usable.
318         """
319         def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
320                 """
321                 :param ctx: configuration context to use to eventually get the version environment
322                 :param compiler: compiler name
323                 :param cpu: target cpu
324                 :param version: compiler version number
325                 :param bat_target: ?
326                 :param bat: path to the batch file to run
327                 """
328                 self.conf = ctx
329                 self.name = None
330                 self.is_valid = False
331                 self.is_done = False
332
333                 self.compiler = compiler
334                 self.cpu = cpu
335                 self.version = version
336                 self.bat_target = bat_target
337                 self.bat = bat
338                 self.callback = callback
339
340         def evaluate(self):
341                 if self.is_done:
342                         return
343                 self.is_done = True
344                 try:
345                         vs = self.conf.get_msvc_version(self.compiler, self.version, self.bat_target, self.bat)
346                 except Errors.ConfigurationError:
347                         self.is_valid = False
348                         return
349                 if self.callback:
350                         vs = self.callback(self, vs)
351                 self.is_valid = True
352                 (self.bindirs, self.incdirs, self.libdirs) = vs
353
354         def __str__(self):
355                 return str((self.compiler, self.cpu, self.version, self.bat_target, self.bat))
356
357         def __repr__(self):
358                 return repr((self.compiler, self.cpu, self.version, self.bat_target, self.bat))
359
360 @conf
361 def gather_wsdk_versions(conf, versions):
362         """
363         Use winreg to add the msvc versions to the input list
364
365         :param versions: list to modify
366         :type versions: list
367         """
368         version_pattern = re.compile('^v..?.?\...?.?')
369         try:
370                 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows')
371         except OSError:
372                 try:
373                         all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows')
374                 except OSError:
375                         return
376         index = 0
377         while 1:
378                 try:
379                         version = Utils.winreg.EnumKey(all_versions, index)
380                 except OSError:
381                         break
382                 index += 1
383                 if not version_pattern.match(version):
384                         continue
385                 try:
386                         msvc_version = Utils.winreg.OpenKey(all_versions, version)
387                         path,type = Utils.winreg.QueryValueEx(msvc_version,'InstallationFolder')
388                 except OSError:
389                         continue
390                 if path and os.path.isfile(os.path.join(path, 'bin', 'SetEnv.cmd')):
391                         targets = {}
392                         for target,arch in all_msvc_platforms:
393                                 targets[target] = target_compiler(conf, 'wsdk', arch, version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd'))
394                         versions['wsdk ' + version[1:]] = targets
395
396 @conf
397 def gather_msvc_targets(conf, versions, version, vc_path):
398         #Looking for normal MSVC compilers!
399         targets = {}
400
401         if os.path.isfile(os.path.join(vc_path, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat')):
402                 for target,realtarget in all_msvc_platforms[::-1]:
403                         targets[target] = target_compiler(conf, 'msvc', realtarget, version, target, os.path.join(vc_path, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat'))
404         elif os.path.isfile(os.path.join(vc_path, 'vcvarsall.bat')):
405                 for target,realtarget in all_msvc_platforms[::-1]:
406                         targets[target] = target_compiler(conf, 'msvc', realtarget, version, target, os.path.join(vc_path, 'vcvarsall.bat'))
407         elif os.path.isfile(os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')):
408                 targets['x86'] = target_compiler(conf, 'msvc', 'x86', version, 'x86', os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat'))
409         elif os.path.isfile(os.path.join(vc_path, 'Bin', 'vcvars32.bat')):
410                 targets['x86'] = target_compiler(conf, 'msvc', 'x86', version, '', os.path.join(vc_path, 'Bin', 'vcvars32.bat'))
411         if targets:
412                 versions['msvc %s' % version] = targets
413
414 @conf
415 def gather_wince_targets(conf, versions, version, vc_path, vsvars, supported_platforms):
416         #Looking for Win CE compilers!
417         for device,platforms in supported_platforms:
418                 targets = {}
419                 for platform,compiler,include,lib in platforms:
420                         winCEpath = os.path.join(vc_path, 'ce')
421                         if not os.path.isdir(winCEpath):
422                                 continue
423
424                         if os.path.isdir(os.path.join(winCEpath, 'lib', platform)):
425                                 bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)]
426                                 incdirs = [os.path.join(winCEpath, 'include'), os.path.join(winCEpath, 'atlmfc', 'include'), include]
427                                 libdirs = [os.path.join(winCEpath, 'lib', platform), os.path.join(winCEpath, 'atlmfc', 'lib', platform), lib]
428                                 def combine_common(obj, compiler_env):
429                                         # TODO this is likely broken, remove in waf 2.1
430                                         (common_bindirs,_1,_2) = compiler_env
431                                         return (bindirs + common_bindirs, incdirs, libdirs)
432                                 targets[platform] = target_compiler(conf, 'msvc', platform, version, 'x86', vsvars, combine_common)
433                 if targets:
434                         versions[device + ' ' + version] = targets
435
436 @conf
437 def gather_winphone_targets(conf, versions, version, vc_path, vsvars):
438         #Looking for WinPhone compilers
439         targets = {}
440         for target,realtarget in all_msvc_platforms[::-1]:
441                 targets[target] = target_compiler(conf, 'winphone', realtarget, version, target, vsvars)
442         if targets:
443                 versions['winphone ' + version] = targets
444
445 @conf
446 def gather_vswhere_versions(conf, versions):
447         try:
448                 import json
449         except ImportError:
450                 Logs.error('Visual Studio 2017 detection requires Python 2.6')
451                 return
452
453         prg_path = os.environ.get('ProgramFiles(x86)', os.environ.get('ProgramFiles', 'C:\\Program Files (x86)'))
454
455         vswhere = os.path.join(prg_path, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe')
456         args = [vswhere, '-products', '*', '-legacy', '-format', 'json']
457         try:
458                 txt = conf.cmd_and_log(args)
459         except Errors.WafError as e:
460                 Logs.debug('msvc: vswhere.exe failed %s', e)
461                 return
462
463         if sys.version_info[0] < 3:
464                 try:
465                         txt = txt.decode(sys.stdout.encoding or 'cp1252')
466                 except UnicodeError:
467                         txt = txt.decode('utf-8', 'replace')
468         arr = json.loads(txt)
469         arr.sort(key=lambda x: x['installationVersion'])
470         for entry in arr:
471                 ver = entry['installationVersion']
472                 ver = str('.'.join(ver.split('.')[:2]))
473                 path = str(os.path.abspath(entry['installationPath']))
474                 if os.path.exists(path) and ('msvc %s' % ver) not in versions:
475                         conf.gather_msvc_targets(versions, ver, path)
476
477 @conf
478 def gather_msvc_versions(conf, versions):
479         vc_paths = []
480         for (v,version,reg) in gather_msvc_detected_versions():
481                 try:
482                         try:
483                                 msvc_version = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, reg + "\\Setup\\VC")
484                         except OSError:
485                                 msvc_version = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, reg + "\\Setup\\Microsoft Visual C++")
486                         path,type = Utils.winreg.QueryValueEx(msvc_version, 'ProductDir')
487                 except OSError:
488                         try:
489                                 msvc_version = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432node\\Microsoft\\VisualStudio\\SxS\\VS7")
490                                 path,type = Utils.winreg.QueryValueEx(msvc_version, version)
491                         except OSError:
492                                 continue
493                         else:
494                                 vc_paths.append((version, os.path.abspath(str(path))))
495                         continue
496                 else:
497                         vc_paths.append((version, os.path.abspath(str(path))))
498
499         wince_supported_platforms = gather_wince_supported_platforms()
500
501         for version,vc_path in vc_paths:
502                 vs_path = os.path.dirname(vc_path)
503                 vsvars = os.path.join(vs_path, 'Common7', 'Tools', 'vsvars32.bat')
504                 if wince_supported_platforms and os.path.isfile(vsvars):
505                         conf.gather_wince_targets(versions, version, vc_path, vsvars, wince_supported_platforms)
506
507         # WP80 works with 11.0Exp and 11.0, both of which resolve to the same vc_path.
508         # Stop after one is found.
509         for version,vc_path in vc_paths:
510                 vs_path = os.path.dirname(vc_path)
511                 vsvars = os.path.join(vs_path, 'VC', 'WPSDK', 'WP80', 'vcvarsphoneall.bat')
512                 if os.path.isfile(vsvars):
513                         conf.gather_winphone_targets(versions, '8.0', vc_path, vsvars)
514                         break
515
516         for version,vc_path in vc_paths:
517                 vs_path = os.path.dirname(vc_path)
518                 conf.gather_msvc_targets(versions, version, vc_path)
519
520 @conf
521 def gather_icl_versions(conf, versions):
522         """
523         Checks ICL compilers
524
525         :param versions: list to modify
526         :type versions: list
527         """
528         version_pattern = re.compile('^...?.?\....?.?')
529         try:
530                 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\C++')
531         except OSError:
532                 try:
533                         all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\C++')
534                 except OSError:
535                         return
536         index = 0
537         while 1:
538                 try:
539                         version = Utils.winreg.EnumKey(all_versions, index)
540                 except OSError:
541                         break
542                 index += 1
543                 if not version_pattern.match(version):
544                         continue
545                 targets = {}
546                 for target,arch in all_icl_platforms:
547                         if target=='intel64':
548                                 targetDir='EM64T_NATIVE'
549                         else:
550                                 targetDir=target
551                         try:
552                                 Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir)
553                                 icl_version=Utils.winreg.OpenKey(all_versions,version)
554                                 path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
555                         except OSError:
556                                 pass
557                         else:
558                                 batch_file=os.path.join(path,'bin','iclvars.bat')
559                                 if os.path.isfile(batch_file):
560                                         targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
561                 for target,arch in all_icl_platforms:
562                         try:
563                                 icl_version = Utils.winreg.OpenKey(all_versions, version+'\\'+target)
564                                 path,type = Utils.winreg.QueryValueEx(icl_version,'ProductDir')
565                         except OSError:
566                                 continue
567                         else:
568                                 batch_file=os.path.join(path,'bin','iclvars.bat')
569                                 if os.path.isfile(batch_file):
570                                         targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
571                 major = version[0:2]
572                 versions['intel ' + major] = targets
573
574 @conf
575 def gather_intel_composer_versions(conf, versions):
576         """
577         Checks ICL compilers that are part of Intel Composer Suites
578
579         :param versions: list to modify
580         :type versions: list
581         """
582         version_pattern = re.compile('^...?.?\...?.?.?')
583         try:
584                 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Suites')
585         except OSError:
586                 try:
587                         all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Suites')
588                 except OSError:
589                         return
590         index = 0
591         while 1:
592                 try:
593                         version = Utils.winreg.EnumKey(all_versions, index)
594                 except OSError:
595                         break
596                 index += 1
597                 if not version_pattern.match(version):
598                         continue
599                 targets = {}
600                 for target,arch in all_icl_platforms:
601                         if target=='intel64':
602                                 targetDir='EM64T_NATIVE'
603                         else:
604                                 targetDir=target
605                         try:
606                                 try:
607                                         defaults = Utils.winreg.OpenKey(all_versions,version+'\\Defaults\\C++\\'+targetDir)
608                                 except OSError:
609                                         if targetDir == 'EM64T_NATIVE':
610                                                 defaults = Utils.winreg.OpenKey(all_versions,version+'\\Defaults\\C++\\EM64T')
611                                         else:
612                                                 raise
613                                 uid,type = Utils.winreg.QueryValueEx(defaults, 'SubKey')
614                                 Utils.winreg.OpenKey(all_versions,version+'\\'+uid+'\\C++\\'+targetDir)
615                                 icl_version=Utils.winreg.OpenKey(all_versions,version+'\\'+uid+'\\C++')
616                                 path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
617                         except OSError:
618                                 pass
619                         else:
620                                 batch_file=os.path.join(path,'bin','iclvars.bat')
621                                 if os.path.isfile(batch_file):
622                                         targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
623                                 # The intel compilervar_arch.bat is broken when used with Visual Studio Express 2012
624                                 # http://software.intel.com/en-us/forums/topic/328487
625                                 compilervars_warning_attr = '_compilervars_warning_key'
626                                 if version[0:2] == '13' and getattr(conf, compilervars_warning_attr, True):
627                                         setattr(conf, compilervars_warning_attr, False)
628                                         patch_url = 'http://software.intel.com/en-us/forums/topic/328487'
629                                         compilervars_arch = os.path.join(path, 'bin', 'compilervars_arch.bat')
630                                         for vscomntool in ('VS110COMNTOOLS', 'VS100COMNTOOLS'):
631                                                 if vscomntool in os.environ:
632                                                         vs_express_path = os.environ[vscomntool] + r'..\IDE\VSWinExpress.exe'
633                                                         dev_env_path = os.environ[vscomntool] + r'..\IDE\devenv.exe'
634                                                         if (r'if exist "%VS110COMNTOOLS%..\IDE\VSWinExpress.exe"' in Utils.readf(compilervars_arch) and
635                                                                 not os.path.exists(vs_express_path) and not os.path.exists(dev_env_path)):
636                                                                 Logs.warn(('The Intel compilervar_arch.bat only checks for one Visual Studio SKU '
637                                                                 '(VSWinExpress.exe) but it does not seem to be installed at %r. '
638                                                                 'The intel command line set up will fail to configure unless the file %r'
639                                                                 'is patched. See: %s') % (vs_express_path, compilervars_arch, patch_url))
640                 major = version[0:2]
641                 versions['intel ' + major] = targets
642
643 @conf
644 def detect_msvc(self):
645         return self.setup_msvc(self.get_msvc_versions())
646
647 @conf
648 def get_msvc_versions(self):
649         """
650         :return: platform to compiler configurations
651         :rtype: dict
652         """
653         dct = Utils.ordered_iter_dict()
654         self.gather_icl_versions(dct)
655         self.gather_intel_composer_versions(dct)
656         self.gather_wsdk_versions(dct)
657         self.gather_msvc_versions(dct)
658         self.gather_vswhere_versions(dct)
659         Logs.debug('msvc: detected versions %r', list(dct.keys()))
660         return dct
661
662 @conf
663 def find_lt_names_msvc(self, libname, is_static=False):
664         """
665         Win32/MSVC specific code to glean out information from libtool la files.
666         this function is not attached to the task_gen class. Returns a triplet:
667         (library absolute path, library name without extension, whether the library is static)
668         """
669         lt_names=[
670                 'lib%s.la' % libname,
671                 '%s.la' % libname,
672         ]
673
674         for path in self.env.LIBPATH:
675                 for la in lt_names:
676                         laf=os.path.join(path,la)
677                         dll=None
678                         if os.path.exists(laf):
679                                 ltdict = Utils.read_la_file(laf)
680                                 lt_libdir=None
681                                 if ltdict.get('libdir', ''):
682                                         lt_libdir = ltdict['libdir']
683                                 if not is_static and ltdict.get('library_names', ''):
684                                         dllnames=ltdict['library_names'].split()
685                                         dll=dllnames[0].lower()
686                                         dll=re.sub('\.dll$', '', dll)
687                                         return (lt_libdir, dll, False)
688                                 elif ltdict.get('old_library', ''):
689                                         olib=ltdict['old_library']
690                                         if os.path.exists(os.path.join(path,olib)):
691                                                 return (path, olib, True)
692                                         elif lt_libdir != '' and os.path.exists(os.path.join(lt_libdir,olib)):
693                                                 return (lt_libdir, olib, True)
694                                         else:
695                                                 return (None, olib, True)
696                                 else:
697                                         raise self.errors.WafError('invalid libtool object file: %s' % laf)
698         return (None, None, None)
699
700 @conf
701 def libname_msvc(self, libname, is_static=False):
702         lib = libname.lower()
703         lib = re.sub('\.lib$','',lib)
704
705         if lib in g_msvc_systemlibs:
706                 return lib
707
708         lib=re.sub('^lib','',lib)
709
710         if lib == 'm':
711                 return None
712
713         (lt_path, lt_libname, lt_static) = self.find_lt_names_msvc(lib, is_static)
714
715         if lt_path != None and lt_libname != None:
716                 if lt_static:
717                         # file existence check has been made by find_lt_names
718                         return os.path.join(lt_path,lt_libname)
719
720         if lt_path != None:
721                 _libpaths = [lt_path] + self.env.LIBPATH
722         else:
723                 _libpaths = self.env.LIBPATH
724
725         static_libs=[
726                 'lib%ss.lib' % lib,
727                 'lib%s.lib' % lib,
728                 '%ss.lib' % lib,
729                 '%s.lib' %lib,
730                 ]
731
732         dynamic_libs=[
733                 'lib%s.dll.lib' % lib,
734                 'lib%s.dll.a' % lib,
735                 '%s.dll.lib' % lib,
736                 '%s.dll.a' % lib,
737                 'lib%s_d.lib' % lib,
738                 '%s_d.lib' % lib,
739                 '%s.lib' %lib,
740                 ]
741
742         libnames=static_libs
743         if not is_static:
744                 libnames=dynamic_libs + static_libs
745
746         for path in _libpaths:
747                 for libn in libnames:
748                         if os.path.exists(os.path.join(path, libn)):
749                                 Logs.debug('msvc: lib found: %s', os.path.join(path,libn))
750                                 return re.sub('\.lib$', '',libn)
751
752         #if no lib can be found, just return the libname as msvc expects it
753         self.fatal('The library %r could not be found' % libname)
754         return re.sub('\.lib$', '', libname)
755
756 @conf
757 def check_lib_msvc(self, libname, is_static=False, uselib_store=None):
758         """
759         Ideally we should be able to place the lib in the right env var, either STLIB or LIB,
760         but we don't distinguish static libs from shared libs.
761         This is ok since msvc doesn't have any special linker flag to select static libs (no env.STLIB_MARKER)
762         """
763         libn = self.libname_msvc(libname, is_static)
764
765         if not uselib_store:
766                 uselib_store = libname.upper()
767
768         if False and is_static: # disabled
769                 self.env['STLIB_' + uselib_store] = [libn]
770         else:
771                 self.env['LIB_' + uselib_store] = [libn]
772
773 @conf
774 def check_libs_msvc(self, libnames, is_static=False):
775         for libname in Utils.to_list(libnames):
776                 self.check_lib_msvc(libname, is_static)
777
778 def configure(conf):
779         """
780         Configuration methods to call for detecting msvc
781         """
782         conf.autodetect(True)
783         conf.find_msvc()
784         conf.msvc_common_flags()
785         conf.cc_load_tools()
786         conf.cxx_load_tools()
787         conf.cc_add_flags()
788         conf.cxx_add_flags()
789         conf.link_add_flags()
790         conf.visual_studio_add_flags()
791
792 @conf
793 def no_autodetect(conf):
794         conf.env.NO_MSVC_DETECT = 1
795         configure(conf)
796
797 @conf
798 def autodetect(conf, arch=False):
799         v = conf.env
800         if v.NO_MSVC_DETECT:
801                 return
802
803         compiler, version, path, includes, libdirs, cpu = conf.detect_msvc()
804         if arch:
805                 v.DEST_CPU = cpu
806
807         v.PATH = path
808         v.INCLUDES = includes
809         v.LIBPATH = libdirs
810         v.MSVC_COMPILER = compiler
811         try:
812                 v.MSVC_VERSION = float(version)
813         except ValueError:
814                 v.MSVC_VERSION = float(version[:-3])
815
816 def _get_prog_names(conf, compiler):
817         if compiler == 'intel':
818                 compiler_name = 'ICL'
819                 linker_name = 'XILINK'
820                 lib_name = 'XILIB'
821         else:
822                 # assumes CL.exe
823                 compiler_name = 'CL'
824                 linker_name = 'LINK'
825                 lib_name = 'LIB'
826         return compiler_name, linker_name, lib_name
827
828 @conf
829 def find_msvc(conf):
830         """Due to path format limitations, limit operation only to native Win32. Yeah it sucks."""
831         if sys.platform == 'cygwin':
832                 conf.fatal('MSVC module does not work under cygwin Python!')
833
834         # the autodetection is supposed to be performed before entering in this method
835         v = conf.env
836         path = v.PATH
837         compiler = v.MSVC_COMPILER
838         version = v.MSVC_VERSION
839
840         compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
841         v.MSVC_MANIFEST = (compiler == 'msvc' and version >= 8) or (compiler == 'wsdk' and version >= 6) or (compiler == 'intel' and version >= 11)
842
843         # compiler
844         cxx = conf.find_program(compiler_name, var='CXX', path_list=path)
845
846         # before setting anything, check if the compiler is really msvc
847         env = dict(conf.environ)
848         if path:
849                 env.update(PATH = ';'.join(path))
850         if not conf.cmd_and_log(cxx + ['/nologo', '/help'], env=env):
851                 conf.fatal('the msvc compiler could not be identified')
852
853         # c/c++ compiler
854         v.CC = v.CXX = cxx
855         v.CC_NAME = v.CXX_NAME = 'msvc'
856
857         # linker
858         if not v.LINK_CXX:
859                 conf.find_program(linker_name, path_list=path, errmsg='%s was not found (linker)' % linker_name, var='LINK_CXX')
860
861         if not v.LINK_CC:
862                 v.LINK_CC = v.LINK_CXX
863
864         # staticlib linker
865         if not v.AR:
866                 stliblink = conf.find_program(lib_name, path_list=path, var='AR')
867                 if not stliblink:
868                         return
869                 v.ARFLAGS = ['/nologo']
870
871         # manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
872         if v.MSVC_MANIFEST:
873                 conf.find_program('MT', path_list=path, var='MT')
874                 v.MTFLAGS = ['/nologo']
875
876         try:
877                 conf.load('winres')
878         except Errors.ConfigurationError:
879                 Logs.warn('Resource compiler not found. Compiling resource file is disabled')
880
881 @conf
882 def visual_studio_add_flags(self):
883         """visual studio flags found in the system environment"""
884         v = self.env
885         if self.environ.get('INCLUDE'):
886                 v.prepend_value('INCLUDES', [x for x in self.environ['INCLUDE'].split(';') if x]) # notice the 'S'
887         if self.environ.get('LIB'):
888                 v.prepend_value('LIBPATH', [x for x in self.environ['LIB'].split(';') if x])
889
890 @conf
891 def msvc_common_flags(conf):
892         """
893         Setup the flags required for executing the msvc compiler
894         """
895         v = conf.env
896
897         v.DEST_BINFMT = 'pe'
898         v.append_value('CFLAGS', ['/nologo'])
899         v.append_value('CXXFLAGS', ['/nologo'])
900         v.append_value('LINKFLAGS', ['/nologo'])
901         v.DEFINES_ST   = '/D%s'
902
903         v.CC_SRC_F     = ''
904         v.CC_TGT_F     = ['/c', '/Fo']
905         v.CXX_SRC_F    = ''
906         v.CXX_TGT_F    = ['/c', '/Fo']
907
908         if (v.MSVC_COMPILER == 'msvc' and v.MSVC_VERSION >= 8) or (v.MSVC_COMPILER == 'wsdk' and v.MSVC_VERSION >= 6):
909                 v.CC_TGT_F = ['/FC'] + v.CC_TGT_F
910                 v.CXX_TGT_F = ['/FC'] + v.CXX_TGT_F
911
912         v.CPPPATH_ST = '/I%s' # template for adding include paths
913
914         v.AR_TGT_F = v.CCLNK_TGT_F = v.CXXLNK_TGT_F = '/OUT:'
915
916         # CRT specific flags
917         v.CFLAGS_CRT_MULTITHREADED     = v.CXXFLAGS_CRT_MULTITHREADED     = ['/MT']
918         v.CFLAGS_CRT_MULTITHREADED_DLL = v.CXXFLAGS_CRT_MULTITHREADED_DLL = ['/MD']
919
920         v.CFLAGS_CRT_MULTITHREADED_DBG     = v.CXXFLAGS_CRT_MULTITHREADED_DBG     = ['/MTd']
921         v.CFLAGS_CRT_MULTITHREADED_DLL_DBG = v.CXXFLAGS_CRT_MULTITHREADED_DLL_DBG = ['/MDd']
922
923         v.LIB_ST            = '%s.lib'
924         v.LIBPATH_ST        = '/LIBPATH:%s'
925         v.STLIB_ST          = '%s.lib'
926         v.STLIBPATH_ST      = '/LIBPATH:%s'
927
928         if v.MSVC_MANIFEST:
929                 v.append_value('LINKFLAGS', ['/MANIFEST'])
930
931         v.CFLAGS_cshlib     = []
932         v.CXXFLAGS_cxxshlib = []
933         v.LINKFLAGS_cshlib  = v.LINKFLAGS_cxxshlib = ['/DLL']
934         v.cshlib_PATTERN    = v.cxxshlib_PATTERN = '%s.dll'
935         v.implib_PATTERN    = '%s.lib'
936         v.IMPLIB_ST         = '/IMPLIB:%s'
937
938         v.LINKFLAGS_cstlib  = []
939         v.cstlib_PATTERN    = v.cxxstlib_PATTERN = '%s.lib'
940
941         v.cprogram_PATTERN  = v.cxxprogram_PATTERN = '%s.exe'
942
943
944 #######################################################################################################
945 ##### conf above, build below
946
947 @after_method('apply_link')
948 @feature('c', 'cxx')
949 def apply_flags_msvc(self):
950         """
951         Add additional flags implied by msvc, such as subsystems and pdb files::
952
953                 def build(bld):
954                         bld.stlib(source='main.c', target='bar', subsystem='gruik')
955         """
956         if self.env.CC_NAME != 'msvc' or not getattr(self, 'link_task', None):
957                 return
958
959         is_static = isinstance(self.link_task, ccroot.stlink_task)
960
961         subsystem = getattr(self, 'subsystem', '')
962         if subsystem:
963                 subsystem = '/subsystem:%s' % subsystem
964                 flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
965                 self.env.append_value(flags, subsystem)
966
967         if not is_static:
968                 for f in self.env.LINKFLAGS:
969                         d = f.lower()
970                         if d[1:] == 'debug':
971                                 pdbnode = self.link_task.outputs[0].change_ext('.pdb')
972                                 self.link_task.outputs.append(pdbnode)
973
974                                 if getattr(self, 'install_task', None):
975                                         self.pdb_install_task = self.add_install_files(
976                                                 install_to=self.install_task.install_to, install_from=pdbnode)
977                                 break
978
979 @feature('cprogram', 'cshlib', 'cxxprogram', 'cxxshlib')
980 @after_method('apply_link')
981 def apply_manifest(self):
982         """
983         Special linker for MSVC with support for embedding manifests into DLL's
984         and executables compiled by Visual Studio 2005 or probably later. Without
985         the manifest file, the binaries are unusable.
986         See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx
987         """
988         if self.env.CC_NAME == 'msvc' and self.env.MSVC_MANIFEST and getattr(self, 'link_task', None):
989                 out_node = self.link_task.outputs[0]
990                 man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
991                 self.link_task.outputs.append(man_node)
992                 self.env.DO_MANIFEST = True
993
994 def make_winapp(self, family):
995         append = self.env.append_unique
996         append('DEFINES', 'WINAPI_FAMILY=%s' % family)
997         append('CXXFLAGS', ['/ZW', '/TP'])
998         for lib_path in self.env.LIBPATH:
999                 append('CXXFLAGS','/AI%s'%lib_path)
1000
1001 @feature('winphoneapp')
1002 @after_method('process_use')
1003 @after_method('propagate_uselib_vars')
1004 def make_winphone_app(self):
1005         """
1006         Insert configuration flags for windows phone applications (adds /ZW, /TP...)
1007         """
1008         make_winapp(self, 'WINAPI_FAMILY_PHONE_APP')
1009         self.env.append_unique('LINKFLAGS', ['/NODEFAULTLIB:ole32.lib', 'PhoneAppModelHost.lib'])
1010
1011 @feature('winapp')
1012 @after_method('process_use')
1013 @after_method('propagate_uselib_vars')
1014 def make_windows_app(self):
1015         """
1016         Insert configuration flags for windows applications (adds /ZW, /TP...)
1017         """
1018         make_winapp(self, 'WINAPI_FAMILY_DESKTOP_APP')