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	default_ver = ''
103	vsver = os.getenv('VSCMD_VER')
104	if vsver:
105		m = re.match(r'(^\d+\.\d+).*', vsver)
106		if m:
107			default_ver = 'msvc %s' % m.group(1)
108	opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default=default_ver)
109	opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='')
110	opt.add_option('--no-msvc-lazy', action='store_false', help = 'lazily check msvc target environments', default=True, dest='msvc_lazy')
111
112@conf
113def setup_msvc(conf, versiondict):
114	"""
115	Checks installed compilers and targets and returns the first combination from the user's
116	options, env, or the global supported lists that checks.
117
118	:param versiondict: dict(platform -> dict(architecture -> configuration))
119	:type versiondict: dict(string -> dict(string -> target_compiler)
120	:return: the compiler, revision, path, include dirs, library paths and target architecture
121	:rtype: tuple of strings
122	"""
123	platforms = getattr(Options.options, 'msvc_targets', '').split(',')
124	if platforms == ['']:
125		platforms=Utils.to_list(conf.env.MSVC_TARGETS) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms]
126	desired_versions = getattr(Options.options, 'msvc_version', '').split(',')
127	if desired_versions == ['']:
128		desired_versions = conf.env.MSVC_VERSIONS or list(reversed(sorted(versiondict.keys())))
129
130	# Override lazy detection by evaluating after the fact.
131	lazy_detect = getattr(Options.options, 'msvc_lazy', True)
132	if conf.env.MSVC_LAZY_AUTODETECT is False:
133		lazy_detect = False
134
135	if not lazy_detect:
136		for val in versiondict.values():
137			for arch in list(val.keys()):
138				cfg = val[arch]
139				cfg.evaluate()
140				if not cfg.is_valid:
141					del val[arch]
142		conf.env.MSVC_INSTALLED_VERSIONS = versiondict
143
144	for version in desired_versions:
145		Logs.debug('msvc: detecting %r - %r', version, desired_versions)
146		try:
147			targets = versiondict[version]
148		except KeyError:
149			continue
150
151		seen = set()
152		for arch in platforms:
153			if arch in seen:
154				continue
155			else:
156				seen.add(arch)
157			try:
158				cfg = targets[arch]
159			except KeyError:
160				continue
161
162			cfg.evaluate()
163			if cfg.is_valid:
164				compiler,revision = version.rsplit(' ', 1)
165				return compiler,revision,cfg.bindirs,cfg.incdirs,cfg.libdirs,cfg.cpu
166	conf.fatal('msvc: Impossible to find a valid architecture for building %r - %r' % (desired_versions, list(versiondict.keys())))
167
168@conf
169def get_msvc_version(conf, compiler, version, target, vcvars):
170	"""
171	Checks that an installed compiler actually runs and uses vcvars to obtain the
172	environment needed by the compiler.
173
174	:param compiler: compiler type, for looking up the executable name
175	:param version: compiler version, for debugging only
176	:param target: target architecture
177	:param vcvars: batch file to run to check the environment
178	:return: the location of the compiler executable, the location of include dirs, and the library paths
179	:rtype: tuple of strings
180	"""
181	Logs.debug('msvc: get_msvc_version: %r %r %r', compiler, version, target)
182
183	try:
184		conf.msvc_cnt += 1
185	except AttributeError:
186		conf.msvc_cnt = 1
187	batfile = conf.bldnode.make_node('waf-print-msvc-%d.bat' % conf.msvc_cnt)
188	batfile.write("""@echo off
189set INCLUDE=
190set LIB=
191call "%s" %s
192echo PATH=%%PATH%%
193echo INCLUDE=%%INCLUDE%%
194echo LIB=%%LIB%%;%%LIBPATH%%
195""" % (vcvars,target))
196	sout = conf.cmd_and_log(['cmd.exe', '/E:on', '/V:on', '/C', batfile.abspath()])
197	lines = sout.splitlines()
198
199	if not lines[0]:
200		lines.pop(0)
201
202	MSVC_PATH = MSVC_INCDIR = MSVC_LIBDIR = None
203	for line in lines:
204		if line.startswith('PATH='):
205			path = line[5:]
206			MSVC_PATH = path.split(';')
207		elif line.startswith('INCLUDE='):
208			MSVC_INCDIR = [i for i in line[8:].split(';') if i]
209		elif line.startswith('LIB='):
210			MSVC_LIBDIR = [i for i in line[4:].split(';') if i]
211	if None in (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR):
212		conf.fatal('msvc: Could not find a valid architecture for building (get_msvc_version_3)')
213
214	# Check if the compiler is usable at all.
215	# The detection may return 64-bit versions even on 32-bit systems, and these would fail to run.
216	env = dict(os.environ)
217	env.update(PATH = path)
218	compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
219	cxx = conf.find_program(compiler_name, path_list=MSVC_PATH)
220
221	# delete CL if exists. because it could contain parameters which can change cl's behaviour rather catastrophically.
222	if 'CL' in env:
223		del(env['CL'])
224
225	try:
226		conf.cmd_and_log(cxx + ['/help'], env=env)
227	except UnicodeError:
228		st = traceback.format_exc()
229		if conf.logger:
230			conf.logger.error(st)
231		conf.fatal('msvc: Unicode error - check the code page?')
232	except Exception as e:
233		Logs.debug('msvc: get_msvc_version: %r %r %r -> failure %s', compiler, version, target, str(e))
234		conf.fatal('msvc: cannot run the compiler in get_msvc_version (run with -v to display errors)')
235	else:
236		Logs.debug('msvc: get_msvc_version: %r %r %r -> OK', compiler, version, target)
237	finally:
238		conf.env[compiler_name] = ''
239
240	return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR)
241
242def gather_wince_supported_platforms():
243	"""
244	Checks SmartPhones SDKs
245
246	:param versions: list to modify
247	:type versions: list
248	"""
249	supported_wince_platforms = []
250	try:
251		ce_sdk = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Windows CE Tools\\SDKs')
252	except OSError:
253		try:
254			ce_sdk = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows CE Tools\\SDKs')
255		except OSError:
256			ce_sdk = ''
257	if not ce_sdk:
258		return supported_wince_platforms
259
260	index = 0
261	while 1:
262		try:
263			sdk_device = Utils.winreg.EnumKey(ce_sdk, index)
264			sdk = Utils.winreg.OpenKey(ce_sdk, sdk_device)
265		except OSError:
266			break
267		index += 1
268		try:
269			path,type = Utils.winreg.QueryValueEx(sdk, 'SDKRootDir')
270		except OSError:
271			try:
272				path,type = Utils.winreg.QueryValueEx(sdk,'SDKInformation')
273			except OSError:
274				continue
275			path,xml = os.path.split(path)
276		path = str(path)
277		path,device = os.path.split(path)
278		if not device:
279			path,device = os.path.split(path)
280		platforms = []
281		for arch,compiler in all_wince_platforms:
282			if os.path.isdir(os.path.join(path, device, 'Lib', arch)):
283				platforms.append((arch, compiler, os.path.join(path, device, 'Include', arch), os.path.join(path, device, 'Lib', arch)))
284		if platforms:
285			supported_wince_platforms.append((device, platforms))
286	return supported_wince_platforms
287
288def gather_msvc_detected_versions():
289	#Detected MSVC versions!
290	version_pattern = re.compile(r'^(\d\d?\.\d\d?)(Exp)?$')
291	detected_versions = []
292	for vcver,vcvar in (('VCExpress','Exp'), ('VisualStudio','')):
293		prefix = 'SOFTWARE\\Wow6432node\\Microsoft\\' + vcver
294		try:
295			all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, prefix)
296		except OSError:
297			prefix = 'SOFTWARE\\Microsoft\\' + vcver
298			try:
299				all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, prefix)
300			except OSError:
301				continue
302
303		index = 0
304		while 1:
305			try:
306				version = Utils.winreg.EnumKey(all_versions, index)
307			except OSError:
308				break
309			index += 1
310			match = version_pattern.match(version)
311			if match:
312				versionnumber = float(match.group(1))
313			else:
314				continue
315			detected_versions.append((versionnumber, version+vcvar, prefix+'\\'+version))
316	def fun(tup):
317		return tup[0]
318
319	detected_versions.sort(key = fun)
320	return detected_versions
321
322class target_compiler(object):
323	"""
324	Wrap a compiler configuration; call evaluate() to determine
325	whether the configuration is usable.
326	"""
327	def __init__(self, ctx, compiler, cpu, version, bat_target, bat, callback=None):
328		"""
329		:param ctx: configuration context to use to eventually get the version environment
330		:param compiler: compiler name
331		:param cpu: target cpu
332		:param version: compiler version number
333		:param bat_target: ?
334		:param bat: path to the batch file to run
335		"""
336		self.conf = ctx
337		self.name = None
338		self.is_valid = False
339		self.is_done = False
340
341		self.compiler = compiler
342		self.cpu = cpu
343		self.version = version
344		self.bat_target = bat_target
345		self.bat = bat
346		self.callback = callback
347
348	def evaluate(self):
349		if self.is_done:
350			return
351		self.is_done = True
352		try:
353			vs = self.conf.get_msvc_version(self.compiler, self.version, self.bat_target, self.bat)
354		except Errors.ConfigurationError:
355			self.is_valid = False
356			return
357		if self.callback:
358			vs = self.callback(self, vs)
359		self.is_valid = True
360		(self.bindirs, self.incdirs, self.libdirs) = vs
361
362	def __str__(self):
363		return str((self.compiler, self.cpu, self.version, self.bat_target, self.bat))
364
365	def __repr__(self):
366		return repr((self.compiler, self.cpu, self.version, self.bat_target, self.bat))
367
368@conf
369def gather_wsdk_versions(conf, versions):
370	"""
371	Use winreg to add the msvc versions to the input list
372
373	:param versions: list to modify
374	:type versions: list
375	"""
376	version_pattern = re.compile(r'^v..?.?\...?.?')
377	try:
378		all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows')
379	except OSError:
380		try:
381			all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows')
382		except OSError:
383			return
384	index = 0
385	while 1:
386		try:
387			version = Utils.winreg.EnumKey(all_versions, index)
388		except OSError:
389			break
390		index += 1
391		if not version_pattern.match(version):
392			continue
393		try:
394			msvc_version = Utils.winreg.OpenKey(all_versions, version)
395			path,type = Utils.winreg.QueryValueEx(msvc_version,'InstallationFolder')
396		except OSError:
397			continue
398		if path and os.path.isfile(os.path.join(path, 'bin', 'SetEnv.cmd')):
399			targets = {}
400			for target,arch in all_msvc_platforms:
401				targets[target] = target_compiler(conf, 'wsdk', arch, version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd'))
402			versions['wsdk ' + version[1:]] = targets
403
404@conf
405def gather_msvc_targets(conf, versions, version, vc_path):
406	#Looking for normal MSVC compilers!
407	targets = {}
408
409	if os.path.isfile(os.path.join(vc_path, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat')):
410		for target,realtarget in all_msvc_platforms[::-1]:
411			targets[target] = target_compiler(conf, 'msvc', realtarget, version, target, os.path.join(vc_path, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat'))
412	elif os.path.isfile(os.path.join(vc_path, 'vcvarsall.bat')):
413		for target,realtarget in all_msvc_platforms[::-1]:
414			targets[target] = target_compiler(conf, 'msvc', realtarget, version, target, os.path.join(vc_path, 'vcvarsall.bat'))
415	elif os.path.isfile(os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')):
416		targets['x86'] = target_compiler(conf, 'msvc', 'x86', version, 'x86', os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat'))
417	elif os.path.isfile(os.path.join(vc_path, 'Bin', 'vcvars32.bat')):
418		targets['x86'] = target_compiler(conf, 'msvc', 'x86', version, '', os.path.join(vc_path, 'Bin', 'vcvars32.bat'))
419	if targets:
420		versions['msvc %s' % version] = targets
421
422@conf
423def gather_wince_targets(conf, versions, version, vc_path, vsvars, supported_platforms):
424	#Looking for Win CE compilers!
425	for device,platforms in supported_platforms:
426		targets = {}
427		for platform,compiler,include,lib in platforms:
428			winCEpath = os.path.join(vc_path, 'ce')
429			if not os.path.isdir(winCEpath):
430				continue
431
432			if os.path.isdir(os.path.join(winCEpath, 'lib', platform)):
433				bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)]
434				incdirs = [os.path.join(winCEpath, 'include'), os.path.join(winCEpath, 'atlmfc', 'include'), include]
435				libdirs = [os.path.join(winCEpath, 'lib', platform), os.path.join(winCEpath, 'atlmfc', 'lib', platform), lib]
436				def combine_common(obj, compiler_env):
437					# TODO this is likely broken, remove in waf 2.1
438					(common_bindirs,_1,_2) = compiler_env
439					return (bindirs + common_bindirs, incdirs, libdirs)
440				targets[platform] = target_compiler(conf, 'msvc', platform, version, 'x86', vsvars, combine_common)
441		if targets:
442			versions[device + ' ' + version] = targets
443
444@conf
445def gather_winphone_targets(conf, versions, version, vc_path, vsvars):
446	#Looking for WinPhone compilers
447	targets = {}
448	for target,realtarget in all_msvc_platforms[::-1]:
449		targets[target] = target_compiler(conf, 'winphone', realtarget, version, target, vsvars)
450	if targets:
451		versions['winphone ' + version] = targets
452
453@conf
454def gather_vswhere_versions(conf, versions):
455	try:
456		import json
457	except ImportError:
458		Logs.error('Visual Studio 2017 detection requires Python 2.6')
459		return
460
461	prg_path = os.environ.get('ProgramFiles(x86)', os.environ.get('ProgramFiles', 'C:\\Program Files (x86)'))
462
463	vswhere = os.path.join(prg_path, 'Microsoft Visual Studio', 'Installer', 'vswhere.exe')
464	args = [vswhere, '-products', '*', '-legacy', '-format', 'json']
465	try:
466		txt = conf.cmd_and_log(args)
467	except Errors.WafError as e:
468		Logs.debug('msvc: vswhere.exe failed %s', e)
469		return
470
471	if sys.version_info[0] < 3:
472		txt = txt.decode(Utils.console_encoding())
473
474	arr = json.loads(txt)
475	arr.sort(key=lambda x: x['installationVersion'])
476	for entry in arr:
477		ver = entry['installationVersion']
478		ver = str('.'.join(ver.split('.')[:2]))
479		path = str(os.path.abspath(entry['installationPath']))
480		if os.path.exists(path) and ('msvc %s' % ver) not in versions:
481			conf.gather_msvc_targets(versions, ver, path)
482
483@conf
484def gather_msvc_versions(conf, versions):
485	vc_paths = []
486	for (v,version,reg) in gather_msvc_detected_versions():
487		try:
488			try:
489				msvc_version = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, reg + "\\Setup\\VC")
490			except OSError:
491				msvc_version = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, reg + "\\Setup\\Microsoft Visual C++")
492			path,type = Utils.winreg.QueryValueEx(msvc_version, 'ProductDir')
493		except OSError:
494			try:
495				msvc_version = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, "SOFTWARE\\Wow6432node\\Microsoft\\VisualStudio\\SxS\\VS7")
496				path,type = Utils.winreg.QueryValueEx(msvc_version, version)
497			except OSError:
498				continue
499			else:
500				vc_paths.append((version, os.path.abspath(str(path))))
501			continue
502		else:
503			vc_paths.append((version, os.path.abspath(str(path))))
504
505	wince_supported_platforms = gather_wince_supported_platforms()
506
507	for version,vc_path in vc_paths:
508		vs_path = os.path.dirname(vc_path)
509		vsvars = os.path.join(vs_path, 'Common7', 'Tools', 'vsvars32.bat')
510		if wince_supported_platforms and os.path.isfile(vsvars):
511			conf.gather_wince_targets(versions, version, vc_path, vsvars, wince_supported_platforms)
512
513	# WP80 works with 11.0Exp and 11.0, both of which resolve to the same vc_path.
514	# Stop after one is found.
515	for version,vc_path in vc_paths:
516		vs_path = os.path.dirname(vc_path)
517		vsvars = os.path.join(vs_path, 'VC', 'WPSDK', 'WP80', 'vcvarsphoneall.bat')
518		if os.path.isfile(vsvars):
519			conf.gather_winphone_targets(versions, '8.0', vc_path, vsvars)
520			break
521
522	for version,vc_path in vc_paths:
523		vs_path = os.path.dirname(vc_path)
524		conf.gather_msvc_targets(versions, version, vc_path)
525
526@conf
527def gather_icl_versions(conf, versions):
528	"""
529	Checks ICL compilers
530
531	:param versions: list to modify
532	:type versions: list
533	"""
534	version_pattern = re.compile(r'^...?.?\....?.?')
535	try:
536		all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\C++')
537	except OSError:
538		try:
539			all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\C++')
540		except OSError:
541			return
542	index = 0
543	while 1:
544		try:
545			version = Utils.winreg.EnumKey(all_versions, index)
546		except OSError:
547			break
548		index += 1
549		if not version_pattern.match(version):
550			continue
551		targets = {}
552		for target,arch in all_icl_platforms:
553			if target=='intel64':
554				targetDir='EM64T_NATIVE'
555			else:
556				targetDir=target
557			try:
558				Utils.winreg.OpenKey(all_versions,version+'\\'+targetDir)
559				icl_version=Utils.winreg.OpenKey(all_versions,version)
560				path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
561			except OSError:
562				pass
563			else:
564				batch_file=os.path.join(path,'bin','iclvars.bat')
565				if os.path.isfile(batch_file):
566					targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
567		for target,arch in all_icl_platforms:
568			try:
569				icl_version = Utils.winreg.OpenKey(all_versions, version+'\\'+target)
570				path,type = Utils.winreg.QueryValueEx(icl_version,'ProductDir')
571			except OSError:
572				continue
573			else:
574				batch_file=os.path.join(path,'bin','iclvars.bat')
575				if os.path.isfile(batch_file):
576					targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
577		major = version[0:2]
578		versions['intel ' + major] = targets
579
580@conf
581def gather_intel_composer_versions(conf, versions):
582	"""
583	Checks ICL compilers that are part of Intel Composer Suites
584
585	:param versions: list to modify
586	:type versions: list
587	"""
588	version_pattern = re.compile(r'^...?.?\...?.?.?')
589	try:
590		all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Suites')
591	except OSError:
592		try:
593			all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Suites')
594		except OSError:
595			return
596	index = 0
597	while 1:
598		try:
599			version = Utils.winreg.EnumKey(all_versions, index)
600		except OSError:
601			break
602		index += 1
603		if not version_pattern.match(version):
604			continue
605		targets = {}
606		for target,arch in all_icl_platforms:
607			if target=='intel64':
608				targetDir='EM64T_NATIVE'
609			else:
610				targetDir=target
611			try:
612				try:
613					defaults = Utils.winreg.OpenKey(all_versions,version+'\\Defaults\\C++\\'+targetDir)
614				except OSError:
615					if targetDir == 'EM64T_NATIVE':
616						defaults = Utils.winreg.OpenKey(all_versions,version+'\\Defaults\\C++\\EM64T')
617					else:
618						raise
619				uid,type = Utils.winreg.QueryValueEx(defaults, 'SubKey')
620				Utils.winreg.OpenKey(all_versions,version+'\\'+uid+'\\C++\\'+targetDir)
621				icl_version=Utils.winreg.OpenKey(all_versions,version+'\\'+uid+'\\C++')
622				path,type=Utils.winreg.QueryValueEx(icl_version,'ProductDir')
623			except OSError:
624				pass
625			else:
626				batch_file=os.path.join(path,'bin','iclvars.bat')
627				if os.path.isfile(batch_file):
628					targets[target] = target_compiler(conf, 'intel', arch, version, target, batch_file)
629				# The intel compilervar_arch.bat is broken when used with Visual Studio Express 2012
630				# http://software.intel.com/en-us/forums/topic/328487
631				compilervars_warning_attr = '_compilervars_warning_key'
632				if version[0:2] == '13' and getattr(conf, compilervars_warning_attr, True):
633					setattr(conf, compilervars_warning_attr, False)
634					patch_url = 'http://software.intel.com/en-us/forums/topic/328487'
635					compilervars_arch = os.path.join(path, 'bin', 'compilervars_arch.bat')
636					for vscomntool in ('VS110COMNTOOLS', 'VS100COMNTOOLS'):
637						if vscomntool in os.environ:
638							vs_express_path = os.environ[vscomntool] + r'..\IDE\VSWinExpress.exe'
639							dev_env_path = os.environ[vscomntool] + r'..\IDE\devenv.exe'
640							if (r'if exist "%VS110COMNTOOLS%..\IDE\VSWinExpress.exe"' in Utils.readf(compilervars_arch) and
641								not os.path.exists(vs_express_path) and not os.path.exists(dev_env_path)):
642								Logs.warn(('The Intel compilervar_arch.bat only checks for one Visual Studio SKU '
643								'(VSWinExpress.exe) but it does not seem to be installed at %r. '
644								'The intel command line set up will fail to configure unless the file %r'
645								'is patched. See: %s') % (vs_express_path, compilervars_arch, patch_url))
646		major = version[0:2]
647		versions['intel ' + major] = targets
648
649@conf
650def detect_msvc(self):
651	return self.setup_msvc(self.get_msvc_versions())
652
653@conf
654def get_msvc_versions(self):
655	"""
656	:return: platform to compiler configurations
657	:rtype: dict
658	"""
659	dct = Utils.ordered_iter_dict()
660	self.gather_icl_versions(dct)
661	self.gather_intel_composer_versions(dct)
662	self.gather_wsdk_versions(dct)
663	self.gather_msvc_versions(dct)
664	self.gather_vswhere_versions(dct)
665	Logs.debug('msvc: detected versions %r', list(dct.keys()))
666	return dct
667
668@conf
669def find_lt_names_msvc(self, libname, is_static=False):
670	"""
671	Win32/MSVC specific code to glean out information from libtool la files.
672	this function is not attached to the task_gen class. Returns a triplet:
673	(library absolute path, library name without extension, whether the library is static)
674	"""
675	lt_names=[
676		'lib%s.la' % libname,
677		'%s.la' % libname,
678	]
679
680	for path in self.env.LIBPATH:
681		for la in lt_names:
682			laf=os.path.join(path,la)
683			dll=None
684			if os.path.exists(laf):
685				ltdict = Utils.read_la_file(laf)
686				lt_libdir=None
687				if ltdict.get('libdir', ''):
688					lt_libdir = ltdict['libdir']
689				if not is_static and ltdict.get('library_names', ''):
690					dllnames=ltdict['library_names'].split()
691					dll=dllnames[0].lower()
692					dll=re.sub(r'\.dll$', '', dll)
693					return (lt_libdir, dll, False)
694				elif ltdict.get('old_library', ''):
695					olib=ltdict['old_library']
696					if os.path.exists(os.path.join(path,olib)):
697						return (path, olib, True)
698					elif lt_libdir != '' and os.path.exists(os.path.join(lt_libdir,olib)):
699						return (lt_libdir, olib, True)
700					else:
701						return (None, olib, True)
702				else:
703					raise self.errors.WafError('invalid libtool object file: %s' % laf)
704	return (None, None, None)
705
706@conf
707def libname_msvc(self, libname, is_static=False):
708	lib = libname.lower()
709	lib = re.sub(r'\.lib$','',lib)
710
711	if lib in g_msvc_systemlibs:
712		return lib
713
714	lib=re.sub('^lib','',lib)
715
716	if lib == 'm':
717		return None
718
719	(lt_path, lt_libname, lt_static) = self.find_lt_names_msvc(lib, is_static)
720
721	if lt_path != None and lt_libname != None:
722		if lt_static:
723			# file existence check has been made by find_lt_names
724			return os.path.join(lt_path,lt_libname)
725
726	if lt_path != None:
727		_libpaths = [lt_path] + self.env.LIBPATH
728	else:
729		_libpaths = self.env.LIBPATH
730
731	static_libs=[
732		'lib%ss.lib' % lib,
733		'lib%s.lib' % lib,
734		'%ss.lib' % lib,
735		'%s.lib' %lib,
736		]
737
738	dynamic_libs=[
739		'lib%s.dll.lib' % lib,
740		'lib%s.dll.a' % lib,
741		'%s.dll.lib' % lib,
742		'%s.dll.a' % lib,
743		'lib%s_d.lib' % lib,
744		'%s_d.lib' % lib,
745		'%s.lib' %lib,
746		]
747
748	libnames=static_libs
749	if not is_static:
750		libnames=dynamic_libs + static_libs
751
752	for path in _libpaths:
753		for libn in libnames:
754			if os.path.exists(os.path.join(path, libn)):
755				Logs.debug('msvc: lib found: %s', os.path.join(path,libn))
756				return re.sub(r'\.lib$', '',libn)
757
758	#if no lib can be found, just return the libname as msvc expects it
759	self.fatal('The library %r could not be found' % libname)
760	return re.sub(r'\.lib$', '', libname)
761
762@conf
763def check_lib_msvc(self, libname, is_static=False, uselib_store=None):
764	"""
765	Ideally we should be able to place the lib in the right env var, either STLIB or LIB,
766	but we don't distinguish static libs from shared libs.
767	This is ok since msvc doesn't have any special linker flag to select static libs (no env.STLIB_MARKER)
768	"""
769	libn = self.libname_msvc(libname, is_static)
770
771	if not uselib_store:
772		uselib_store = libname.upper()
773
774	if False and is_static: # disabled
775		self.env['STLIB_' + uselib_store] = [libn]
776	else:
777		self.env['LIB_' + uselib_store] = [libn]
778
779@conf
780def check_libs_msvc(self, libnames, is_static=False):
781	for libname in Utils.to_list(libnames):
782		self.check_lib_msvc(libname, is_static)
783
784def configure(conf):
785	"""
786	Configuration methods to call for detecting msvc
787	"""
788	conf.autodetect(True)
789	conf.find_msvc()
790	conf.msvc_common_flags()
791	conf.cc_load_tools()
792	conf.cxx_load_tools()
793	conf.cc_add_flags()
794	conf.cxx_add_flags()
795	conf.link_add_flags()
796	conf.visual_studio_add_flags()
797
798@conf
799def no_autodetect(conf):
800	conf.env.NO_MSVC_DETECT = 1
801	configure(conf)
802
803@conf
804def autodetect(conf, arch=False):
805	v = conf.env
806	if v.NO_MSVC_DETECT:
807		return
808
809	compiler, version, path, includes, libdirs, cpu = conf.detect_msvc()
810	if arch:
811		v.DEST_CPU = cpu
812
813	v.PATH = path
814	v.INCLUDES = includes
815	v.LIBPATH = libdirs
816	v.MSVC_COMPILER = compiler
817	try:
818		v.MSVC_VERSION = float(version)
819	except ValueError:
820		v.MSVC_VERSION = float(version[:-3])
821
822def _get_prog_names(conf, compiler):
823	if compiler == 'intel':
824		compiler_name = 'ICL'
825		linker_name = 'XILINK'
826		lib_name = 'XILIB'
827	else:
828		# assumes CL.exe
829		compiler_name = 'CL'
830		linker_name = 'LINK'
831		lib_name = 'LIB'
832	return compiler_name, linker_name, lib_name
833
834@conf
835def find_msvc(conf):
836	"""Due to path format limitations, limit operation only to native Win32. Yeah it sucks."""
837	if sys.platform == 'cygwin':
838		conf.fatal('MSVC module does not work under cygwin Python!')
839
840	# the autodetection is supposed to be performed before entering in this method
841	v = conf.env
842	path = v.PATH
843	compiler = v.MSVC_COMPILER
844	version = v.MSVC_VERSION
845
846	compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler)
847	v.MSVC_MANIFEST = (compiler == 'msvc' and version >= 8) or (compiler == 'wsdk' and version >= 6) or (compiler == 'intel' and version >= 11)
848
849	# compiler
850	cxx = conf.find_program(compiler_name, var='CXX', path_list=path)
851
852	# before setting anything, check if the compiler is really msvc
853	env = dict(conf.environ)
854	if path:
855		env.update(PATH = ';'.join(path))
856	if not conf.cmd_and_log(cxx + ['/nologo', '/help'], env=env):
857		conf.fatal('the msvc compiler could not be identified')
858
859	# c/c++ compiler
860	v.CC = v.CXX = cxx
861	v.CC_NAME = v.CXX_NAME = 'msvc'
862
863	# linker
864	if not v.LINK_CXX:
865		conf.find_program(linker_name, path_list=path, errmsg='%s was not found (linker)' % linker_name, var='LINK_CXX')
866
867	if not v.LINK_CC:
868		v.LINK_CC = v.LINK_CXX
869
870	# staticlib linker
871	if not v.AR:
872		stliblink = conf.find_program(lib_name, path_list=path, var='AR')
873		if not stliblink:
874			return
875		v.ARFLAGS = ['/nologo']
876
877	# manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later
878	if v.MSVC_MANIFEST:
879		conf.find_program('MT', path_list=path, var='MT')
880		v.MTFLAGS = ['/nologo']
881
882	try:
883		conf.load('winres')
884	except Errors.ConfigurationError:
885		Logs.warn('Resource compiler not found. Compiling resource file is disabled')
886
887@conf
888def visual_studio_add_flags(self):
889	"""visual studio flags found in the system environment"""
890	v = self.env
891	if self.environ.get('INCLUDE'):
892		v.prepend_value('INCLUDES', [x for x in self.environ['INCLUDE'].split(';') if x]) # notice the 'S'
893	if self.environ.get('LIB'):
894		v.prepend_value('LIBPATH', [x for x in self.environ['LIB'].split(';') if x])
895
896@conf
897def msvc_common_flags(conf):
898	"""
899	Setup the flags required for executing the msvc compiler
900	"""
901	v = conf.env
902
903	v.DEST_BINFMT = 'pe'
904	v.append_value('CFLAGS', ['/nologo'])
905	v.append_value('CXXFLAGS', ['/nologo'])
906	v.append_value('LINKFLAGS', ['/nologo'])
907	v.DEFINES_ST   = '/D%s'
908
909	v.CC_SRC_F     = ''
910	v.CC_TGT_F     = ['/c', '/Fo']
911	v.CXX_SRC_F    = ''
912	v.CXX_TGT_F    = ['/c', '/Fo']
913
914	if (v.MSVC_COMPILER == 'msvc' and v.MSVC_VERSION >= 8) or (v.MSVC_COMPILER == 'wsdk' and v.MSVC_VERSION >= 6):
915		v.CC_TGT_F = ['/FC'] + v.CC_TGT_F
916		v.CXX_TGT_F = ['/FC'] + v.CXX_TGT_F
917
918	v.CPPPATH_ST = '/I%s' # template for adding include paths
919
920	v.AR_TGT_F = v.CCLNK_TGT_F = v.CXXLNK_TGT_F = '/OUT:'
921
922	# CRT specific flags
923	v.CFLAGS_CRT_MULTITHREADED     = v.CXXFLAGS_CRT_MULTITHREADED     = ['/MT']
924	v.CFLAGS_CRT_MULTITHREADED_DLL = v.CXXFLAGS_CRT_MULTITHREADED_DLL = ['/MD']
925
926	v.CFLAGS_CRT_MULTITHREADED_DBG     = v.CXXFLAGS_CRT_MULTITHREADED_DBG     = ['/MTd']
927	v.CFLAGS_CRT_MULTITHREADED_DLL_DBG = v.CXXFLAGS_CRT_MULTITHREADED_DLL_DBG = ['/MDd']
928
929	v.LIB_ST            = '%s.lib'
930	v.LIBPATH_ST        = '/LIBPATH:%s'
931	v.STLIB_ST          = '%s.lib'
932	v.STLIBPATH_ST      = '/LIBPATH:%s'
933
934	if v.MSVC_MANIFEST:
935		v.append_value('LINKFLAGS', ['/MANIFEST'])
936
937	v.CFLAGS_cshlib     = []
938	v.CXXFLAGS_cxxshlib = []
939	v.LINKFLAGS_cshlib  = v.LINKFLAGS_cxxshlib = ['/DLL']
940	v.cshlib_PATTERN    = v.cxxshlib_PATTERN = '%s.dll'
941	v.implib_PATTERN    = '%s.lib'
942	v.IMPLIB_ST         = '/IMPLIB:%s'
943
944	v.LINKFLAGS_cstlib  = []
945	v.cstlib_PATTERN    = v.cxxstlib_PATTERN = '%s.lib'
946
947	v.cprogram_PATTERN  = v.cxxprogram_PATTERN = '%s.exe'
948
949	v.def_PATTERN       = '/def:%s'
950
951
952#######################################################################################################
953##### conf above, build below
954
955@after_method('apply_link')
956@feature('c', 'cxx')
957def apply_flags_msvc(self):
958	"""
959	Add additional flags implied by msvc, such as subsystems and pdb files::
960
961		def build(bld):
962			bld.stlib(source='main.c', target='bar', subsystem='gruik')
963	"""
964	if self.env.CC_NAME != 'msvc' or not getattr(self, 'link_task', None):
965		return
966
967	is_static = isinstance(self.link_task, ccroot.stlink_task)
968
969	subsystem = getattr(self, 'subsystem', '')
970	if subsystem:
971		subsystem = '/subsystem:%s' % subsystem
972		flags = is_static and 'ARFLAGS' or 'LINKFLAGS'
973		self.env.append_value(flags, subsystem)
974
975	if not is_static:
976		for f in self.env.LINKFLAGS:
977			d = f.lower()
978			if d[1:] in ('debug', 'debug:full', 'debug:fastlink'):
979				pdbnode = self.link_task.outputs[0].change_ext('.pdb')
980				self.link_task.outputs.append(pdbnode)
981
982				if getattr(self, 'install_task', None):
983					self.pdb_install_task = self.add_install_files(
984						install_to=self.install_task.install_to, install_from=pdbnode)
985				break
986
987@feature('cprogram', 'cshlib', 'cxxprogram', 'cxxshlib')
988@after_method('apply_link')
989def apply_manifest(self):
990	"""
991	Special linker for MSVC with support for embedding manifests into DLL's
992	and executables compiled by Visual Studio 2005 or probably later. Without
993	the manifest file, the binaries are unusable.
994	See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx
995	"""
996	if self.env.CC_NAME == 'msvc' and self.env.MSVC_MANIFEST and getattr(self, 'link_task', None):
997		out_node = self.link_task.outputs[0]
998		man_node = out_node.parent.find_or_declare(out_node.name + '.manifest')
999		self.link_task.outputs.append(man_node)
1000		self.env.DO_MANIFEST = True
1001
1002def make_winapp(self, family):
1003	append = self.env.append_unique
1004	append('DEFINES', 'WINAPI_FAMILY=%s' % family)
1005	append('CXXFLAGS', ['/ZW', '/TP'])
1006	for lib_path in self.env.LIBPATH:
1007		append('CXXFLAGS','/AI%s'%lib_path)
1008
1009@feature('winphoneapp')
1010@after_method('process_use')
1011@after_method('propagate_uselib_vars')
1012def make_winphone_app(self):
1013	"""
1014	Insert configuration flags for windows phone applications (adds /ZW, /TP...)
1015	"""
1016	make_winapp(self, 'WINAPI_FAMILY_PHONE_APP')
1017	self.env.append_unique('LINKFLAGS', ['/NODEFAULTLIB:ole32.lib', 'PhoneAppModelHost.lib'])
1018
1019@feature('winapp')
1020@after_method('process_use')
1021@after_method('propagate_uselib_vars')
1022def make_windows_app(self):
1023	"""
1024	Insert configuration flags for windows applications (adds /ZW, /TP...)
1025	"""
1026	make_winapp(self, 'WINAPI_FAMILY_DESKTOP_APP')
1027