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"""
9Microsoft Visual C++/Intel C++ compiler support
10
11If you get detection problems, first try any of the following::
12
13	chcp 65001
14	set PYTHONIOENCODING=...
15	set PYTHONLEGACYWINDOWSSTDIO=1
16
17Usage::
18
19	$ waf configure --msvc_version="msvc 10.0,msvc 9.0" --msvc_target="x64"
20
21or::
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
28or::
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
37Platforms and targets will be tested in the order they appear;
38the first good configuration will be used.
39
40To force testing all the configurations that are not used, use the ``--no-msvc-lazy`` option
41or set ``conf.env.MSVC_LAZY_AUTODETECT=False``.
42
43Supported platforms: ia64, x64, x86, x86_amd64, x86_ia64, x86_arm, amd64_x86, amd64_arm
44
45Compilers 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
54To use WAF in a VS2008 Make file project (see http://code.google.com/p/waf/issues/detail?id=894)
55You may consider to set the environment variable "VS_UNICODE_OUTPUT" to nothing before calling waf.
56So in your project settings use something like 'cmd.exe /C "set VS_UNICODE_OUTPUT=& set PYTHONUNBUFFERED=true & waf build"'.
57cmd.exe  /C  "chcp 1252 & set PYTHONUNBUFFERED=true && set && waf  configure"
58Setting PYTHONUNBUFFERED gives the unbuffered output.
59"""
60
61import os, sys, re, traceback
62from waflib import Utils, Logs, Options, Errors
63from waflib.TaskGen import after_method, feature
64
65from waflib.Configure import conf
66from waflib.Tools import ccroot, c, cxx, ar
67
68g_msvc_systemlibs = '''
69aclui activeds ad1 adptif adsiid advapi32 asycfilt authz bhsupp bits bufferoverflowu cabinet
70cap certadm certidl ciuuid clusapi comctl32 comdlg32 comsupp comsuppd comsuppw comsuppwd comsvcs
71credui crypt32 cryptnet cryptui d3d8thk daouuid dbgeng dbghelp dciman32 ddao35 ddao35d
72ddao35u ddao35ud delayimp dhcpcsvc dhcpsapi dlcapi dnsapi dsprop dsuiext dtchelp
73faultrep fcachdll fci fdi framedyd framedyn gdi32 gdiplus glauxglu32 gpedit gpmuuid
74gtrts32w gtrtst32hlink htmlhelp httpapi icm32 icmui imagehlp imm32 iphlpapi iprop
75kernel32 ksguid ksproxy ksuser libcmt libcmtd libcpmt libcpmtd loadperf lz32 mapi
76mapi32 mgmtapi minidump mmc mobsync mpr mprapi mqoa mqrt msacm32 mscms mscoree
77msdasc msimg32 msrating mstask msvcmrt msvcurt msvcurtd mswsock msxml2 mtx mtxdm
78netapi32 nmapinmsupp npptools ntdsapi ntdsbcli ntmsapi ntquery odbc32 odbcbcp
79odbccp32 oldnames ole32 oleacc oleaut32 oledb oledlgolepro32 opends60 opengl32
80osptk parser pdh penter pgobootrun pgort powrprof psapi ptrustm ptrustmd ptrustu
81ptrustud qosname rasapi32 rasdlg rassapi resutils riched20 rpcndr rpcns4 rpcrt4 rtm
82rtutils runtmchk scarddlg scrnsave scrnsavw secur32 sensapi setupapi sfc shell32
83shfolder shlwapi sisbkup snmpapi sporder srclient sti strsafe svcguid tapi32 thunk32
84traffic unicows url urlmon user32 userenv usp10 uuid uxtheme vcomp vcompd vdmdbg
85version vfw32 wbemuuid  webpost wiaguid wininet winmm winscard winspool winstrm
86wintrust 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
90all_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
95all_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
98all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')]
99"""List of icl platforms"""
100
101def 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
107def 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
163def 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
183set INCLUDE=
184set LIB=
185call "%s" %s
186echo PATH=%%PATH%%
187echo INCLUDE=%%INCLUDE%%
188echo 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
236def 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
282def 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
316class 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
363def 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
399def 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
417def 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
439def 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
448def 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
478def 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
521def 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
575def 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
644def detect_msvc(self):
645	return self.setup_msvc(self.get_msvc_versions())
646
647@conf
648def 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
663def 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
701def 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
757def 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
774def 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
778def 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
793def no_autodetect(conf):
794	conf.env.NO_MSVC_DETECT = 1
795	configure(conf)
796
797@conf
798def 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
816def _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
829def 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
882def 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
891def 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')
951def 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')
983def 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
996def 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')
1006def 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')
1016def make_windows_app(self):
1017	"""
1018	Insert configuration flags for windows applications (adds /ZW, /TP...)
1019	"""
1020	make_winapp(self, 'WINAPI_FAMILY_DESKTOP_APP')
1021