third_party/waf: upgrade to waf 2.0.8
[vlendec/samba-autobuild/.git] / third_party / waf / waflib / Tools / msvc.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Carlos Rafael Giani, 2006 (dv)
4 # Tamas Pal, 2007 (folti)
5 # Nicolas Mercier, 2009
6 # Matt Clarkson, 2012
7
8 """
9 Microsoft Visual C++/Intel C++ compiler support
10
11 If you get detection problems, first try any of the following::
12
13         chcp 65001
14         set PYTHONIOENCODING=...
15         set PYTHONLEGACYWINDOWSSTDIO=1
16
17 Usage::
18
19         $ waf configure --msvc_version="msvc 10.0,msvc 9.0" --msvc_target="x64"
20
21 or::
22
23         def configure(conf):
24                 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']
25                 conf.env.MSVC_TARGETS = ['x64']
26                 conf.load('msvc')
27
28 or::
29
30         def configure(conf):
31                 conf.load('msvc', funs='no_autodetect')
32                 conf.check_lib_msvc('gdi32')
33                 conf.check_libs_msvc('kernel32 user32')
34         def build(bld):
35                 tg = bld.program(source='main.c', target='app', use='KERNEL32 USER32 GDI32')
36
37 Platforms and targets will be tested in the order they appear;
38 the first good configuration will be used.
39
40 To force testing all the configurations that are not used, use the ``--no-msvc-lazy`` option
41 or set ``conf.env.MSVC_LAZY_AUTODETECT=False``.
42
43 Supported platforms: ia64, x64, x86, x86_amd64, x86_ia64, x86_arm, amd64_x86, amd64_arm
44
45 Compilers supported:
46
47 * msvc       => Visual Studio, versions 6.0 (VC 98, VC .NET 2002) to 15 (Visual Studio 2017)
48 * wsdk       => Windows SDK, versions 6.0, 6.1, 7.0, 7.1, 8.0
49 * icl        => Intel compiler, versions 9, 10, 11, 13
50 * winphone   => Visual Studio to target Windows Phone 8 native (version 8.0 for now)
51 * Smartphone => Compiler/SDK for Smartphone devices (armv4/v4i)
52 * PocketPC   => Compiler/SDK for PocketPC devices (armv4/v4i)
53
54 To use WAF in a VS2008 Make file project (see http://code.google.com/p/waf/issues/detail?id=894)
55 You may consider to set the environment variable "VS_UNICODE_OUTPUT" to nothing before calling waf.
56 So in your project settings use something like 'cmd.exe /C "set VS_UNICODE_OUTPUT=& set PYTHONUNBUFFERED=true & waf build"'.
57 cmd.exe  /C  "chcp 1252 & set PYTHONUNBUFFERED=true && set && waf  configure"
58 Setting PYTHONUNBUFFERED gives the unbuffered output.
59 """
60
61 import os, sys, re, traceback
62 from waflib import Utils, Logs, Options, Errors
63 from waflib.TaskGen import after_method, feature
64
65 from waflib.Configure import conf
66 from waflib.Tools import ccroot, c, cxx, ar
67
68 g_msvc_systemlibs = '''
69 aclui activeds ad1 adptif adsiid advapi32 asycfilt authz bhsupp bits bufferoverflowu cabinet
70 cap certadm certidl ciuuid clusapi comctl32 comdlg32 comsupp comsuppd comsuppw comsuppwd comsvcs
71 credui crypt32 cryptnet cryptui d3d8thk daouuid dbgeng dbghelp dciman32 ddao35 ddao35d
72 ddao35u ddao35ud delayimp dhcpcsvc dhcpsapi dlcapi dnsapi dsprop dsuiext dtchelp
73 faultrep fcachdll fci fdi framedyd framedyn gdi32 gdiplus glauxglu32 gpedit gpmuuid
74 gtrts32w gtrtst32hlink htmlhelp httpapi icm32 icmui imagehlp imm32 iphlpapi iprop
75 kernel32 ksguid ksproxy ksuser libcmt libcmtd libcpmt libcpmtd loadperf lz32 mapi
76 mapi32 mgmtapi minidump mmc mobsync mpr mprapi mqoa mqrt msacm32 mscms mscoree
77 msdasc msimg32 msrating mstask msvcmrt msvcurt msvcurtd mswsock msxml2 mtx mtxdm
78 netapi32 nmapinmsupp npptools ntdsapi ntdsbcli ntmsapi ntquery odbc32 odbcbcp
79 odbccp32 oldnames ole32 oleacc oleaut32 oledb oledlgolepro32 opends60 opengl32
80 osptk parser pdh penter pgobootrun pgort powrprof psapi ptrustm ptrustmd ptrustu
81 ptrustud qosname rasapi32 rasdlg rassapi resutils riched20 rpcndr rpcns4 rpcrt4 rtm
82 rtutils runtmchk scarddlg scrnsave scrnsavw secur32 sensapi setupapi sfc shell32
83 shfolder shlwapi sisbkup snmpapi sporder srclient sti strsafe svcguid tapi32 thunk32
84 traffic unicows url urlmon user32 userenv usp10 uuid uxtheme vcomp vcompd vdmdbg
85 version vfw32 wbemuuid  webpost wiaguid wininet winmm winscard winspool winstrm
86 wintrust wldap32 wmiutils wow32 ws2_32 wsnmp32 wsock32 wst wtsapi32 xaswitch xolehlp
87 '''.split()
88 """importlibs provided by MSVC/Platform SDK. Do NOT search them"""
89
90 all_msvc_platforms = [  ('x64', 'amd64'), ('x86', 'x86'), ('ia64', 'ia64'),
91                                                 ('x86_amd64', 'amd64'), ('x86_ia64', 'ia64'), ('x86_arm', 'arm'), ('x86_arm64', 'arm64'),
92                                                 ('amd64_x86', 'x86'), ('amd64_arm', 'arm'), ('amd64_arm64', 'arm64') ]
93 """List of msvc platforms"""
94
95 all_wince_platforms = [ ('armv4', 'arm'), ('armv4i', 'arm'), ('mipsii', 'mips'), ('mipsii_fp', 'mips'), ('mipsiv', 'mips'), ('mipsiv_fp', 'mips'), ('sh4', 'sh'), ('x86', 'cex86') ]
96 """List of wince platforms"""
97
98 all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
99 """List of icl platforms"""
100
101 def options(opt):
102         opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default='')
103         opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='')
104         opt.add_option('--no-msvc-lazy', action='store_false', help = 'lazily check msvc target environments', default=True, dest='msvc_lazy')
105
106 @conf
107 def setup_msvc(conf, versiondict):
108         """
109         Checks installed compilers and targets and returns the first combination from the user's
110         options, env, or the global supported lists that checks.
111
112         :param versiondict: dict(platform -> dict(architecture -> configuration))
113         :type versiondict: dict(string -> dict(string -> target_compiler)
114         :return: the compiler, revision, path, include dirs, library paths and target architecture
115         :rtype: tuple of strings
116         """
117         platforms = getattr(Options.options, 'msvc_targets', '').split(',')
118         if platforms == ['']:
119                 platforms=Utils.to_list(conf.env.MSVC_TARGETS) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms]
120         desired_versions = getattr(Options.options, 'msvc_version', '').split(',')
121         if desired_versions == ['']:
122                 desired_versions = conf.env.MSVC_VERSIONS or list(reversed(sorted(versiondict.keys())))
123
124         # Override lazy detection by evaluating after the fact.
125         lazy_detect = getattr(Options.options, 'msvc_lazy', True)
126         if conf.env.MSVC_LAZY_AUTODETECT is False:
127                 lazy_detect = False
128
129         if not lazy_detect:
130                 for val in versiondict.values():
131                         for arch in list(val.keys()):
132                                 cfg = val[arch]
133                                 cfg.evaluate()
134                                 if not cfg.is_valid:
135                                         del val[arch]
136                 conf.env.MSVC_INSTALLED_VERSIONS = versiondict
137
138         for version in desired_versions:
139                 Logs.debug('msvc: detecting %r - %r', version, desired_versions)
140                 try:
141                         targets = versiondict[version]
142                 except KeyError:
143                         continue
144
145                 seen = set()
146                 for arch in platforms:
147                         if arch in seen:
148                                 continue
149                         else:
150                                 seen.add(arch)
151                         try:
152                                 cfg = targets[arch]
153                         except KeyError:
154                                 continue
155
156                         cfg.evaluate()
157                         if cfg.is_valid:
158                                 compiler,revision = version.rsplit(' ', 1)
159                                 return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu
160         conf.fatal('msvc: Impossible to find a valid architecture for building %r - %r' % (desired_versions, list(versiondict.keys())))
161
162 @conf
163 def get_msvc_version(conf, compiler, version, target, vcvars):
164         """
165         Checks that an installed compiler actually runs and uses vcvars to obtain the
166         environment needed by the compiler.
167
168         :param compiler: compiler type, for looking up the executable name
169         :param version: compiler version, for debugging only
170         :param target: target architecture
171         :param vcvars: batch file to run to check the environment
172         :return: the location of the compiler executable, the location of include dirs, and the library paths
173         :rtype: tuple of strings
174         """
175         Logs.debug('msvc: get_msvc_version: %r %r %r', compiler, version, target)
176
177         try:
178                 conf.msvc_cnt += 1
179         except AttributeError:
180                 conf.msvc_cnt = 1
181         batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
182         batfile.write("""@echo off
183 set INCLUDE=
184 set LIB=
185 call "%s" %s
186 echo PATH=%%PATH%%
187 echo INCLUDE=%%INCLUDE%%
188 echo LIB=%%LIB%%;%%LIBPATH%%
189 """ % (vcvars,target))
190         sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
191         lines = sout.splitlines()
192
193         if not lines[0]:
194                 lines.pop(0)
195
196         MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
197         for line in lines:
198                 if line.startswith('PATH='):
199                         path = line[5:]
200                         MSVC_PATH = path.split(';')
201                 elif line.startswith('INCLUDE='):
202                         MSVC_INCDIR = [i for i in line[8:].split(';') if i]
203                 elif line.startswith('LIB='):
204                         MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
205         if None in (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR):
206                 conf.fatal('msvc: Could not find a valid architecture for building (get_msvc_version_3)')
207
208         # Check if the compiler is usable at all.
209         # The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
210         env = dict(os.environ)
211         env.update(PATH = path)
212         compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
213         cxx = conf.find_program(compiler_name, path_list=MSVC_PATH)
214
215         # delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
216         if 'CL' in env:
217                 del(env['CL'])
218
219         try:
220                 conf.cmd_and_log(cxx + ['/help'], env=env)
221         except UnicodeError:
222                 st = traceback.format_exc()
223                 if conf.logger:
224                         conf.logger.error(st)
225                 conf.fatal('msvc: Unicode error - check the code page?')
226         except Exception as e:
227                 Logs.debug('msvc: get_msvc_version: %r %r %r -> failure %s', compiler, version, target, str(e))
228                 conf.fatal('msvc: cannot run the compiler in get_msvc_version (run with -v to display errors)')
229         else:
230                 Logs.debug('msvc: get_msvc_version: %r %r %r -> OK', compiler, version, target)
231         finally:
232                 conf.env[compiler_name] = ''
233
234         return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
235
236 def gather_wince_supported_platforms():
237         """
238         Checks SmartPhones SDKs
239
240         :param versions: list to modify
241         :type versions: list
242         """
243         supported_wince_platforms = []
244         try:
245                 ce_sdk = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Windows CE Tools\\SDKs')
246         except OSError:
247                 try:
248                         ce_sdk = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows CE Tools\\SDKs')
249                 except OSError:
250                         ce_sdk = ''
251         if not ce_sdk:
252                 return supported_wince_platforms
253
254         index = 0
255         while 1:
256                 try:
257                         sdk_device = Utils.winreg.EnumKey(ce_sdk, index)
258                         sdk = Utils.winreg.OpenKey(ce_sdk, sdk_device)
259                 except OSError:
260                         break
261                 index += 1
262                 try:
263                         path,type = Utils.winreg.QueryValueEx(sdk, 'SDKRootDir')
264                 except OSError:
265                         try:
266                                 path,type = Utils.winreg.QueryValueEx(sdk,'SDKInformation')
267                         except OSError:
268                                 continue
269                         path,xml = os.path.split(path)
270                 path = str(path)
271                 path,device = os.path.split(path)
272                 if not device:
273                         path,device = os.path.split(path)
274                 platforms = []
275                 for arch,compiler in all_wince_platforms:
276                         if os.path.isdir(os.path.join(path, device, 'Lib', arch)):
277                                 platforms.append((arch, compiler, os.path.join(path, device, 'Include', arch), os.path.join(path, device, 'Lib', arch)))
278                 if platforms:
279                         supported_wince_platforms.append((device, platforms))
280         return supported_wince_platforms
281
282 def gather_msvc_detected_versions():
283         #Detected MSVC versions!
284         version_pattern = re.compile('^(\d\d?\.\d\d?)(Exp)?$')
285         detected_versions = []
286         for vcver,vcvar in (('VCExpress','Exp'), ('VisualStudio','')):
287                 prefix = 'SOFTWARE\\Wow6432node\\Microsoft\\' + vcver
288                 try:
289                         all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, prefix)
290                 except OSError:
291                         prefix = 'SOFTWARE\\Microsoft\\' + vcver
292                         try:
293                                 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, prefix)
294                         except OSError:
295                                 continue
296
297                 index = 0
298                 while 1:
299                         try:
300                                 version = Utils.winreg.EnumKey(all_versions, index)
301                         except OSError:
302                                 break
303                         index += 1
304                         match = version_pattern.match(version)
305                         if match:
306                                 versionnumber = float(match.group(1))
307                         else:
308                                 continue
309                         detected_versions.append((versionnumber, version+vcvar, prefix+'\\'+version))
310         def fun(tup):
311                 return tup[0]
312
313         detected_versions.sort(key = fun)
314         return detected_versions
315
316 class target_compiler(object):
317         """
318         Wrap a compiler configuration; call evaluate() to determine
319         whether the configuration is usable.
320         """
321         def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
322                 """
323                 :param ctx: configuration context to use to eventually get the version environment
324                 :param compiler: compiler name
325                 :param cpu: target cpu
326                 :param version: compiler version number
327                 :param bat_target: ?
328                 :param bat: path to the batch file to run
329                 """
330                 self.conf = ctx
331                 self.name = None
332                 self.is_valid = False
333                 self.is_done = False
334
335                 self.compiler = compiler
336                 self.cpu = cpu
337                 self.version = version
338                 self.bat_target = bat_target
339                 self.bat = bat
340                 self.callback = callback
341
342         def evaluate(self):
343                 if self.is_done:
344                         return
345                 self.is_done = True
346                 try:
347                         vs = self.conf.get_msvc_version(self.compiler, self.version, self.bat_target, self.bat)
348                 except Errors.ConfigurationError:
349                         self.is_valid = False
350                         return
351                 if self.callback:
352                         vs = self.callback(self, vs)
353                 self.is_valid = True
354                 (self.bindirs, self.incdirs, self.libdirs) = vs
355
356         def __str__(self):
357                 return str((self.compiler, self.cpu, self.version, self.bat_target, self.bat))
358
359         def __repr__(self):
360                 return repr((self.compiler, self.cpu, self.version, self.bat_target, self.bat))
361
362 @conf
363 def gather_wsdk_versions(conf, versions):
364         """
365         Use winreg to add the msvc versions to the input list
366
367         :param versions: list to modify
368         :type versions: list
369         """
370         version_pattern = re.compile('^v..?.?\...?.?')
371         try:
372                 all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows')
373         except OSError:
374                 try:
375                         all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows')
376                 except OSError:
377                         return
378         index = 0
379         while 1:
380                 try:
381                         version = Utils.winreg.EnumKey(all_versions, index)
382                 except OSError:
383                         break
384                 index += 1
385                 if not version_pattern.match(version):
386                         continue
387                 try:
388                         msvc_version = Utils.winreg.OpenKey(all_versions, version)
389                         path,type = Utils.winreg.QueryValueEx(msvc_version,'InstallationFolder')
390                 except OSError:
391                         continue
392                 if path and os.path.isfile(os.path.join(path, 'bin', 'SetEnv.cmd')):
393                         targets = {}
394                         for target,arch in all_msvc_platforms:
395                                 targets[target] = target_compiler(conf, 'wsdk', arch, version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd'))
396                         versions['wsdk ' + version[1:]] = targets
397
398 @conf
399 def gather_msvc_targets(conf, versions, version, vc_path):
400         #Looking for normal MSVC compilers!
401         targets = {}
402
403         if os.path.isfile(os.path.join(vc_path, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat')):
404                 for target,realtarget in all_msvc_platforms[::-1]:
405                         targets[target] = target_compiler(conf, 'msvc', realtarget, version, target, os.path.join(vc_path, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat'))
406         elif os.path.isfile(os.path.join(vc_path, 'vcvarsall.bat')):
407                 for target,realtarget in all_msvc_platforms[::-1]:
408                         targets[target] = target_compiler(conf, 'msvc', realtarget, version, target, os.path.join(vc_path, 'vcvarsall.bat'))
409         elif os.path.isfile(os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')):
410                 targets['x86'] = target_compiler(conf, 'msvc', 'x86', version, 'x86', os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat'))
411         elif os.path.isfile(os.path.join(vc_path, 'Bin', 'vcvars32.bat')):
412                 targets['x86'] = target_compiler(conf, 'msvc', 'x86', version, '', os.path.join(vc_path, 'Bin', 'vcvars32.bat'))
413         if targets:
414                 versions['msvc %s' % version] = targets
415
416 @conf
417 def gather_wince_targets(conf, versions, version, vc_path, vsvars, supported_platforms):
418         #Looking for Win CE compilers!
419         for device,platforms in supported_platforms:
420                 targets = {}
421                 for platform,compiler,include,lib in platforms:
422                         winCEpath = os.path.join(vc_path, 'ce')
423                         if not os.path.isdir(winCEpath):
424                                 continue
425
426                         if os.path.isdir(os.path.join(winCEpath, 'lib', platform)):
427                                 bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)]
428                                 incdirs = [os.path.join(winCEpath, 'include'), os.path.join(winCEpath, 'atlmfc', 'include'), include]
429                                 libdirs = [os.path.join(winCEpath, 'lib', platform), os.path.join(winCEpath, 'atlmfc', 'lib', platform), lib]
430                                 def combine_common(obj, compiler_env):
431                                         # TODO this is likely broken, remove in waf 2.1
432                                         (common_bindirs,_1,_2) = compiler_env
433                                         return (bindirs + common_bindirs, incdirs, libdirs)
434                                 targets[platform] = target_compiler(conf, 'msvc', platform, version, 'x86', vsvars, combine_common)
435                 if targets:
436                         versions[device + ' ' + version] = targets
437
438 @conf
439 def gather_winphone_targets(conf, versions, version, vc_path, vsvars):
440         #Looking for WinPhone compilers
441         targets = {}
442         for target,realtarget in all_msvc_platforms[::-1]:
443                 targets[target] = target_compiler(conf, 'winphone', realtarget, version, target, vsvars)
444         if targets:
445                 versions['winphone ' + version] = targets
446
447 @conf
448 def gather_vswhere_versions(conf, versions):
449         try:
450                 import json
451         except ImportError:
452                 Logs.error('Visual Studio 2017 detection requires Python 2.6')
453                 return
454
455         prg_path = os.environ.get('ProgramFiles(x86)', os.environ.get('ProgramFiles', 'C:\\Program Files (x86)'))
456
457         vswhere = os.path.join(prg_path, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe')
458         args = [vswhere, '-products', '*', '-legacy', '-format', 'json']
459         try:
460                 txt = conf.cmd_and_log(args)
461         except Errors.WafError as e:
462                 Logs.debug('msvc: vswhere.exe failed %s', e)
463                 return
464
465         if sys.version_info[0] < 3:
466                 txt = txt.decode(Utils.console_encoding())
467
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         v.def_PATTERN       = '/def:%s'
944
945
946 #######################################################################################################
947 ##### conf above, build below
948
949 @after_method('apply_link')
950 @feature('c', 'cxx')
951 def apply_flags_msvc(self):
952         """
953         Add additional flags implied by msvc, such as subsystems and pdb files::
954
955                 def build(bld):
956                         bld.stlib(source='main.c', target='bar', subsystem='gruik')
957         """
958         if self.env.CC_NAME != 'msvc' or not getattr(self, 'link_task', None):
959                 return
960
961         is_static = isinstance(self.link_task, ccroot.stlink_task)
962
963         subsystem = getattr(self, 'subsystem', '')
964         if subsystem:
965                 subsystem = '/subsystem:%s' % subsystem
966                 flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
967                 self.env.append_value(flags, subsystem)
968
969         if not is_static:
970                 for f in self.env.LINKFLAGS:
971                         d = f.lower()
972                         if d[1:] == 'debug':
973                                 pdbnode = self.link_task.outputs[0].change_ext('.pdb')
974                                 self.link_task.outputs.append(pdbnode)
975
976                                 if getattr(self, 'install_task', None):
977                                         self.pdb_install_task = self.add_install_files(
978                                                 install_to=self.install_task.install_to, install_from=pdbnode)
979                                 break
980
981 @feature('cprogram', 'cshlib', 'cxxprogram', 'cxxshlib')
982 @after_method('apply_link')
983 def apply_manifest(self):
984         """
985         Special linker for MSVC with support for embedding manifests into DLL's
986         and executables compiled by Visual Studio 2005 or probably later. Without
987         the manifest file, the binaries are unusable.
988         See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx
989         """
990         if self.env.CC_NAME == 'msvc' and self.env.MSVC_MANIFEST and getattr(self, 'link_task', None):
991                 out_node = self.link_task.outputs[0]
992                 man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
993                 self.link_task.outputs.append(man_node)
994                 self.env.DO_MANIFEST = True
995
996 def make_winapp(self, family):
997         append = self.env.append_unique
998         append('DEFINES', 'WINAPI_FAMILY=%s' % family)
999         append('CXXFLAGS', ['/ZW', '/TP'])
1000         for lib_path in self.env.LIBPATH:
1001                 append('CXXFLAGS','/AI%s'%lib_path)
1002
1003 @feature('winphoneapp')
1004 @after_method('process_use')
1005 @after_method('propagate_uselib_vars')
1006 def make_winphone_app(self):
1007         """
1008         Insert configuration flags for windows phone applications (adds /ZW, /TP...)
1009         """
1010         make_winapp(self, 'WINAPI_FAMILY_PHONE_APP')
1011         self.env.append_unique('LINKFLAGS', ['/NODEFAULTLIB:ole32.lib', 'PhoneAppModelHost.lib'])
1012
1013 @feature('winapp')
1014 @after_method('process_use')
1015 @after_method('propagate_uselib_vars')
1016 def make_windows_app(self):
1017         """
1018         Insert configuration flags for windows applications (adds /ZW, /TP...)
1019         """
1020         make_winapp(self, 'WINAPI_FAMILY_DESKTOP_APP')