Merge pull request #4655 from bdbaddog/fix_new_ninja_package
[scons.git] / SCons / Tool / MSCommon / vc.py
blob032dbf202e6fdd24db8e4bd473bc86b331bf8846
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """
25 MS Compilers: Visual C/C++ detection and configuration.
27 # TODO:
28 # * supported arch for versions: for old versions of batch file without
29 # argument, giving bogus argument cannot be detected, so we have to hardcode
30 # this here
31 # * print warning when msvc version specified but not found
32 # * find out why warning do not print
33 # * test on 64 bits XP + VS 2005 (and VS 6 if possible)
34 # * SDK
35 # * Assembly
36 """
38 import SCons.compat
40 import subprocess
41 import os
42 import platform
43 import sysconfig
44 from pathlib import Path
45 from string import digits as string_digits
46 from subprocess import PIPE
47 import re
48 from collections import (
49 namedtuple,
50 OrderedDict,
52 import json
53 from functools import cmp_to_key
54 import enum
56 import SCons.Util
57 import SCons.Warnings
58 from SCons.Tool import find_program_path
60 from . import common
61 from .common import (
62 CONFIG_CACHE,
63 debug,
65 from .sdk import get_installed_sdks
67 from . import MSVC
69 from .MSVC.Exceptions import (
70 VisualCException,
71 MSVCInternalError,
72 MSVCUserError,
73 MSVCArgumentError,
74 MSVCToolsetVersionNotFound,
77 # external exceptions
79 class MSVCUnsupportedHostArch(VisualCException):
80 pass
82 class MSVCUnsupportedTargetArch(VisualCException):
83 pass
85 class MSVCScriptNotFound(MSVCUserError):
86 pass
88 class MSVCUseScriptError(MSVCUserError):
89 pass
91 class MSVCUseSettingsError(MSVCUserError):
92 pass
94 class VSWhereUserError(MSVCUserError):
95 pass
97 # internal exceptions
99 class UnsupportedVersion(VisualCException):
100 pass
102 class BatchFileExecutionError(VisualCException):
103 pass
105 # undefined object for dict.get() in case key exists and value is None
106 UNDEFINED = object()
108 # powershell error sending telemetry for arm32 process on arm64 host (VS2019+):
109 # True: force VSCMD_SKIP_SENDTELEMETRY=1 (if necessary)
110 # False: do nothing
111 _ARM32_ON_ARM64_SKIP_SENDTELEMETRY = True
113 # MSVC 9.0 preferred query order:
114 # True: VCForPython, VisualStudio
115 # False: VisualStudio, VCForPython
116 _VC90_Prefer_VCForPython = False
118 # Dict to 'canonalize' the arch
119 _ARCH_TO_CANONICAL = {
120 "amd64" : "amd64",
121 "emt64" : "amd64",
122 "i386" : "x86",
123 "i486" : "x86",
124 "i586" : "x86",
125 "i686" : "x86",
126 "ia64" : "ia64", # deprecated
127 "itanium" : "ia64", # deprecated
128 "x86" : "x86",
129 "x86_64" : "amd64",
130 "arm" : "arm",
131 "arm64" : "arm64",
132 "aarch64" : "arm64",
135 # The msvc batch files report errors via stdout. The following
136 # regular expression attempts to match known msvc error messages
137 # written to stdout.
138 re_script_output_error = re.compile(
139 r'^(' + r'|'.join([
140 r'VSINSTALLDIR variable is not set', # 2002-2003
141 r'The specified configuration type is missing', # 2005+
142 r'Error in script usage', # 2005+
143 r'ERROR\:', # 2005+
144 r'\!ERROR\!', # 2015-2015
145 r'\[ERROR\:', # 2017+
146 r'\[ERROR\]', # 2017+
147 r'Syntax\:', # 2017+
148 ]) + r')'
151 # Lists of compatible host/target combinations are derived from a set of defined
152 # constant data structures for each host architecture. The derived data structures
153 # implicitly handle the differences in full versions and express versions of visual
154 # studio. The host/target combination search lists are contructed in order of
155 # preference. The construction of the derived data structures is independent of actual
156 # visual studio installations. The host/target configurations are used in both the
157 # initial msvc detection and when finding a valid batch file for a given host/target
158 # combination.
160 # HostTargetConfig description:
162 # label:
163 # Name used for identification.
165 # host_all_hosts:
166 # Defined list of compatible architectures for each host architecture.
168 # host_all_targets:
169 # Defined list of target architectures for each host architecture.
171 # host_def_targets:
172 # Defined list of default target architectures for each host architecture.
174 # all_pairs:
175 # Derived list of all host/target combination tuples.
177 # host_target_map:
178 # Derived list of all compatible host/target combinations for each
179 # supported host/target combination.
181 # host_all_targets_map:
182 # Derived list of all compatible host/target combinations for each
183 # supported host. This is used in the initial check that cl.exe exists
184 # in the requisite visual studio vc host/target directory for a given host.
186 # host_def_targets_map:
187 # Derived list of default compatible host/target combinations for each
188 # supported host. This is used for a given host when the user does not
189 # request a target archicture.
191 # target_host_map:
192 # Derived list of compatible host/target combinations for each supported
193 # target/host combination. This is used for a given host and target when
194 # the user requests a target architecture.
196 _HOST_TARGET_CONFIG_NT = namedtuple("HostTargetConfig", [
197 # defined
198 "label", # name for debugging/output
199 "host_all_hosts", # host_all_hosts[host] -> host_list
200 "host_all_targets", # host_all_targets[host] -> target_list
201 "host_def_targets", # host_def_targets[host] -> target_list
202 # derived
203 "all_pairs", # host_target_list
204 "host_target_map", # host_target_map[host][target] -> host_target_list
205 "host_all_targets_map", # host_all_targets_map[host][target] -> host_target_list
206 "host_def_targets_map", # host_def_targets_map[host][target] -> host_target_list
207 "target_host_map", # target_host_map[target][host] -> host_target_list
210 def _host_target_config_factory(*, label, host_all_hosts, host_all_targets, host_def_targets):
212 def _make_host_target_map(all_hosts, all_targets):
213 # host_target_map[host][target] -> host_target_list
214 host_target_map = {}
215 for host, host_list in all_hosts.items():
216 host_target_map[host] = {}
217 for host_platform in host_list:
218 for target_platform in all_targets[host_platform]:
219 if target_platform not in host_target_map[host]:
220 host_target_map[host][target_platform] = []
221 host_target_map[host][target_platform].append((host_platform, target_platform))
222 return host_target_map
224 def _make_host_all_targets_map(all_hosts, host_target_map, all_targets):
225 # host_all_target_map[host] -> host_target_list
226 # special host key '_all_' contains all (host,target) combinations
227 all = '_all_'
228 host_all_targets_map = {}
229 host_all_targets_map[all] = []
230 for host, host_list in all_hosts.items():
231 host_all_targets_map[host] = []
232 for host_platform in host_list:
233 # all_targets[host_platform]: all targets for compatible host
234 for target in all_targets[host_platform]:
235 for host_target in host_target_map[host_platform][target]:
236 for host_key in (host, all):
237 if host_target not in host_all_targets_map[host_key]:
238 host_all_targets_map[host_key].append(host_target)
239 return host_all_targets_map
241 def _make_host_def_targets_map(all_hosts, host_target_map, def_targets):
242 # host_def_targets_map[host] -> host_target_list
243 host_def_targets_map = {}
244 for host, host_list in all_hosts.items():
245 host_def_targets_map[host] = []
246 for host_platform in host_list:
247 # def_targets[host]: default targets for true host
248 for target in def_targets[host]:
249 for host_target in host_target_map[host_platform][target]:
250 if host_target not in host_def_targets_map[host]:
251 host_def_targets_map[host].append(host_target)
252 return host_def_targets_map
254 def _make_target_host_map(all_hosts, host_all_targets_map):
255 # target_host_map[target][host] -> host_target_list
256 target_host_map = {}
257 for host_platform in all_hosts.keys():
258 for host_target in host_all_targets_map[host_platform]:
259 _, target = host_target
260 if target not in target_host_map:
261 target_host_map[target] = {}
262 if host_platform not in target_host_map[target]:
263 target_host_map[target][host_platform] = []
264 if host_target not in target_host_map[target][host_platform]:
265 target_host_map[target][host_platform].append(host_target)
266 return target_host_map
268 host_target_map = _make_host_target_map(host_all_hosts, host_all_targets)
269 host_all_targets_map = _make_host_all_targets_map(host_all_hosts, host_target_map, host_all_targets)
270 host_def_targets_map = _make_host_def_targets_map(host_all_hosts, host_target_map, host_def_targets)
271 target_host_map = _make_target_host_map(host_all_hosts, host_all_targets_map)
273 all_pairs = host_all_targets_map['_all_']
274 del host_all_targets_map['_all_']
276 host_target_cfg = _HOST_TARGET_CONFIG_NT(
277 label = label,
278 host_all_hosts = dict(host_all_hosts),
279 host_all_targets = host_all_targets,
280 host_def_targets = host_def_targets,
281 all_pairs = all_pairs,
282 host_target_map = host_target_map,
283 host_all_targets_map = host_all_targets_map,
284 host_def_targets_map = host_def_targets_map,
285 target_host_map = target_host_map,
288 return host_target_cfg
290 # 14.1 (VS2017) and later
292 # Given a (host, target) tuple, return a tuple containing the batch file to
293 # look for and a tuple of path components to find cl.exe. We can't rely on returning
294 # an arg to use for vcvarsall.bat, because that script will run even if given
295 # a host/target pair that isn't installed.
297 # Starting with 14.1 (VS2017), the batch files are located in directory
298 # <VSROOT>/VC/Auxiliary/Build. The batch file name is the first value of the
299 # stored tuple.
301 # The build tools are organized by host and target subdirectories under each toolset
302 # version directory. For example, <VSROOT>/VC/Tools/MSVC/14.31.31103/bin/Hostx64/x64.
303 # The cl path fragment under the toolset version folder is the second value of
304 # the stored tuple.
306 # 14.3 (VS2022) and later
308 _GE2022_HOST_TARGET_BATCHFILE_CLPATHCOMPS = {
310 ('amd64', 'amd64') : ('vcvars64.bat', ('bin', 'Hostx64', 'x64')),
311 ('amd64', 'x86') : ('vcvarsamd64_x86.bat', ('bin', 'Hostx64', 'x86')),
312 ('amd64', 'arm') : ('vcvarsamd64_arm.bat', ('bin', 'Hostx64', 'arm')),
313 ('amd64', 'arm64') : ('vcvarsamd64_arm64.bat', ('bin', 'Hostx64', 'arm64')),
315 ('x86', 'amd64') : ('vcvarsx86_amd64.bat', ('bin', 'Hostx86', 'x64')),
316 ('x86', 'x86') : ('vcvars32.bat', ('bin', 'Hostx86', 'x86')),
317 ('x86', 'arm') : ('vcvarsx86_arm.bat', ('bin', 'Hostx86', 'arm')),
318 ('x86', 'arm64') : ('vcvarsx86_arm64.bat', ('bin', 'Hostx86', 'arm64')),
320 ('arm64', 'amd64') : ('vcvarsarm64_amd64.bat', ('bin', 'Hostarm64', 'arm64_amd64')),
321 ('arm64', 'x86') : ('vcvarsarm64_x86.bat', ('bin', 'Hostarm64', 'arm64_x86')),
322 ('arm64', 'arm') : ('vcvarsarm64_arm.bat', ('bin', 'Hostarm64', 'arm64_arm')),
323 ('arm64', 'arm64') : ('vcvarsarm64.bat', ('bin', 'Hostarm64', 'arm64')),
327 _GE2022_HOST_TARGET_CFG = _host_target_config_factory(
329 label = 'GE2022',
331 host_all_hosts = OrderedDict([
332 ('amd64', ['amd64', 'x86']),
333 ('x86', ['x86']),
334 ('arm64', ['arm64', 'amd64', 'x86']),
335 ('arm', ['x86']),
338 host_all_targets = {
339 'amd64': ['amd64', 'x86', 'arm64', 'arm'],
340 'x86': ['x86', 'amd64', 'arm', 'arm64'],
341 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
342 'arm': [],
345 host_def_targets = {
346 'amd64': ['amd64', 'x86'],
347 'x86': ['x86'],
348 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
349 'arm': ['arm'],
354 # debug("_GE2022_HOST_TARGET_CFG: %s", _GE2022_HOST_TARGET_CFG)
356 # 14.2 (VS2019) to 14.1 (VS2017)
358 _LE2019_HOST_TARGET_BATCHFILE_CLPATHCOMPS = {
360 ('amd64', 'amd64') : ('vcvars64.bat', ('bin', 'Hostx64', 'x64')),
361 ('amd64', 'x86') : ('vcvarsamd64_x86.bat', ('bin', 'Hostx64', 'x86')),
362 ('amd64', 'arm') : ('vcvarsamd64_arm.bat', ('bin', 'Hostx64', 'arm')),
363 ('amd64', 'arm64') : ('vcvarsamd64_arm64.bat', ('bin', 'Hostx64', 'arm64')),
365 ('x86', 'amd64') : ('vcvarsx86_amd64.bat', ('bin', 'Hostx86', 'x64')),
366 ('x86', 'x86') : ('vcvars32.bat', ('bin', 'Hostx86', 'x86')),
367 ('x86', 'arm') : ('vcvarsx86_arm.bat', ('bin', 'Hostx86', 'arm')),
368 ('x86', 'arm64') : ('vcvarsx86_arm64.bat', ('bin', 'Hostx86', 'arm64')),
370 ('arm64', 'amd64') : ('vcvars64.bat', ('bin', 'Hostx64', 'x64')),
371 ('arm64', 'x86') : ('vcvarsamd64_x86.bat', ('bin', 'Hostx64', 'x86')),
372 ('arm64', 'arm') : ('vcvarsamd64_arm.bat', ('bin', 'Hostx64', 'arm')),
373 ('arm64', 'arm64') : ('vcvarsamd64_arm64.bat', ('bin', 'Hostx64', 'arm64')),
377 _LE2019_HOST_TARGET_CFG = _host_target_config_factory(
379 label = 'LE2019',
381 host_all_hosts = OrderedDict([
382 ('amd64', ['amd64', 'x86']),
383 ('x86', ['x86']),
384 ('arm64', ['amd64', 'x86']),
385 ('arm', ['x86']),
388 host_all_targets = {
389 'amd64': ['amd64', 'x86', 'arm64', 'arm'],
390 'x86': ['x86', 'amd64', 'arm', 'arm64'],
391 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
392 'arm': [],
395 host_def_targets = {
396 'amd64': ['amd64', 'x86'],
397 'x86': ['x86'],
398 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
399 'arm': ['arm'],
404 # debug("_LE2019_HOST_TARGET_CFG: %s", _LE2019_HOST_TARGET_CFG)
406 # 14.0 (VS2015) to 10.0 (VS2010)
408 # Given a (host, target) tuple, return a tuple containing the argument for
409 # the batch file and a tuple of the path components to find cl.exe.
411 # In 14.0 (VS2015) and earlier, the original x86 tools are in the tools
412 # bin directory (i.e., <VSROOT>/VC/bin). Any other tools are in subdirectory
413 # named for the the host/target pair or a single name if the host==target.
415 _LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS = {
417 ('amd64', 'amd64') : ('amd64', 'vcvars64.bat', ('bin', 'amd64')),
418 ('amd64', 'x86') : ('amd64_x86', 'vcvarsamd64_x86.bat', ('bin', 'amd64_x86')),
419 ('amd64', 'arm') : ('amd64_arm', 'vcvarsamd64_arm.bat', ('bin', 'amd64_arm')),
421 ('x86', 'amd64') : ('x86_amd64', 'vcvarsx86_amd64.bat', ('bin', 'x86_amd64')),
422 ('x86', 'x86') : ('x86', 'vcvars32.bat', ('bin', )),
423 ('x86', 'arm') : ('x86_arm', 'vcvarsx86_arm.bat', ('bin', 'x86_arm')),
424 ('x86', 'ia64') : ('x86_ia64', 'vcvarsx86_ia64.bat', ('bin', 'x86_ia64')),
426 ('arm64', 'amd64') : ('amd64', 'vcvars64.bat', ('bin', 'amd64')),
427 ('arm64', 'x86') : ('amd64_x86', 'vcvarsamd64_x86.bat', ('bin', 'amd64_x86')),
428 ('arm64', 'arm') : ('amd64_arm', 'vcvarsamd64_arm.bat', ('bin', 'amd64_arm')),
430 ('arm', 'arm') : ('arm', 'vcvarsarm.bat', ('bin', 'arm')),
431 ('ia64', 'ia64') : ('ia64', 'vcvars64.bat', ('bin', 'ia64')),
435 _LE2015_HOST_TARGET_CFG = _host_target_config_factory(
437 label = 'LE2015',
439 host_all_hosts = OrderedDict([
440 ('amd64', ['amd64', 'x86']),
441 ('x86', ['x86']),
442 ('arm64', ['amd64', 'x86']),
443 ('arm', ['arm']),
444 ('ia64', ['ia64']),
447 host_all_targets = {
448 'amd64': ['amd64', 'x86', 'arm'],
449 'x86': ['x86', 'amd64', 'arm', 'ia64'],
450 'arm64': ['amd64', 'x86', 'arm'],
451 'arm': ['arm'],
452 'ia64': ['ia64'],
455 host_def_targets = {
456 'amd64': ['amd64', 'x86'],
457 'x86': ['x86'],
458 'arm64': ['amd64', 'arm', 'x86'],
459 'arm': ['arm'],
460 'ia64': ['ia64'],
465 # debug("_LE2015_HOST_TARGET_CFG: %s", _LE2015_HOST_TARGET_CFG)
467 # 9.0 (VS2008) to 8.0 (VS2005)
469 _LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS = {
471 ('amd64', 'amd64') : ('amd64', 'vcvarsamd64.bat', ('bin', 'amd64')),
472 ('amd64', 'x86') : ('x86', 'vcvars32.bat', ('bin', )),
474 ('x86', 'amd64') : ('x86_amd64', 'vcvarsx86_amd64.bat', ('bin', 'x86_amd64')),
475 ('x86', 'x86') : ('x86', 'vcvars32.bat', ('bin', )),
476 ('x86', 'ia64') : ('x86_ia64', 'vcvarsx86_ia64.bat', ('bin', 'x86_ia64')),
478 ('arm64', 'amd64') : ('amd64', 'vcvarsamd64.bat', ('bin', 'amd64')),
479 ('arm64', 'x86') : ('x86', 'vcvars32.bat', ('bin', )),
481 ('ia64', 'ia64') : ('ia64', 'vcvarsia64.bat', ('bin', 'ia64')),
485 _LE2008_HOST_TARGET_CFG = _host_target_config_factory(
487 label = 'LE2008',
489 host_all_hosts = OrderedDict([
490 ('amd64', ['amd64', 'x86']),
491 ('x86', ['x86']),
492 ('arm64', ['amd64', 'x86']),
493 ('ia64', ['ia64']),
496 host_all_targets = {
497 'amd64': ['amd64', 'x86'],
498 'x86': ['x86', 'amd64', 'ia64'],
499 'arm64': ['amd64', 'x86'],
500 'ia64': ['ia64'],
503 host_def_targets = {
504 'amd64': ['amd64', 'x86'],
505 'x86': ['x86'],
506 'arm64': ['amd64', 'x86'],
507 'ia64': ['ia64'],
512 # debug("_LE2008_HOST_TARGET_CFG: %s", _LE2008_HOST_TARGET_CFG)
514 # 7.1 (VS2003) and earlier
516 # For 7.1 (VS2003) and earlier, there are only x86 targets and the batch files
517 # take no arguments.
519 _LE2003_HOST_TARGET_CFG = _host_target_config_factory(
521 label = 'LE2003',
523 host_all_hosts = OrderedDict([
524 ('amd64', ['x86']),
525 ('x86', ['x86']),
526 ('arm64', ['x86']),
529 host_all_targets = {
530 'amd64': ['x86'],
531 'x86': ['x86'],
532 'arm64': ['x86'],
535 host_def_targets = {
536 'amd64': ['x86'],
537 'x86': ['x86'],
538 'arm64': ['x86'],
543 # debug("_LE2003_HOST_TARGET_CFG: %s", _LE2003_HOST_TARGET_CFG)
545 _CL_EXE_NAME = 'cl.exe'
547 _VSWHERE_EXE = 'vswhere.exe'
549 def get_msvc_version_numeric(msvc_version):
550 """Get the raw version numbers from a MSVC_VERSION string, so it
551 could be cast to float or other numeric values. For example, '14.0Exp'
552 would get converted to '14.0'.
554 Args:
555 msvc_version: str
556 string representing the version number, could contain non
557 digit characters
559 Returns:
560 str: the value converted to a numeric only string
563 return ''.join([x for x in msvc_version if x in string_digits + '.'])
565 def get_host_platform(host_platform):
567 host_platform = host_platform.lower()
569 # Solaris returns i86pc for both 32 and 64 bit architectures
570 if host_platform == 'i86pc':
571 if platform.architecture()[0] == "64bit":
572 host_platform = "amd64"
573 else:
574 host_platform = "x86"
576 try:
577 host =_ARCH_TO_CANONICAL[host_platform]
578 except KeyError:
579 msg = "Unrecognized host architecture %s"
580 raise MSVCUnsupportedHostArch(msg % repr(host_platform)) from None
582 return host
584 _native_host_architecture = None
586 def get_native_host_architecture():
587 """Return the native host architecture."""
588 global _native_host_architecture
590 if _native_host_architecture is None:
592 try:
593 arch = common.read_reg(
594 r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment\PROCESSOR_ARCHITECTURE'
596 except OSError:
597 arch = None
599 if not arch:
600 arch = platform.machine()
602 _native_host_architecture = arch
604 return _native_host_architecture
606 _native_host_platform = None
608 def get_native_host_platform():
609 global _native_host_platform
611 if _native_host_platform is None:
612 arch = get_native_host_architecture()
613 _native_host_platform = get_host_platform(arch)
615 return _native_host_platform
617 def get_host_target(env, msvc_version, all_host_targets: bool=False):
619 vernum = float(get_msvc_version_numeric(msvc_version))
620 vernum_int = int(vernum * 10)
622 if vernum_int >= 143:
623 # 14.3 (VS2022) and later
624 host_target_cfg = _GE2022_HOST_TARGET_CFG
625 elif 143 > vernum_int >= 141:
626 # 14.2 (VS2019) to 14.1 (VS2017)
627 host_target_cfg = _LE2019_HOST_TARGET_CFG
628 elif 141 > vernum_int >= 100:
629 # 14.0 (VS2015) to 10.0 (VS2010)
630 host_target_cfg = _LE2015_HOST_TARGET_CFG
631 elif 100 > vernum_int >= 80:
632 # 9.0 (VS2008) to 8.0 (VS2005)
633 host_target_cfg = _LE2008_HOST_TARGET_CFG
634 else: # 80 > vernum_int
635 # 7.1 (VS2003) and earlier
636 host_target_cfg = _LE2003_HOST_TARGET_CFG
638 host_arch = env.get('HOST_ARCH') if env else None
639 debug("HOST_ARCH:%s", str(host_arch))
641 if host_arch:
642 host_platform = get_host_platform(host_arch)
643 else:
644 host_platform = get_native_host_platform()
646 target_arch = env.get('TARGET_ARCH') if env else None
647 debug("TARGET_ARCH:%s", str(target_arch))
649 if target_arch:
651 try:
652 target_platform = _ARCH_TO_CANONICAL[target_arch.lower()]
653 except KeyError:
654 all_archs = str(list(_ARCH_TO_CANONICAL.keys()))
655 raise MSVCUnsupportedTargetArch(
656 "Unrecognized target architecture %s\n\tValid architectures: %s"
657 % (repr(target_arch), all_archs)
658 ) from None
660 target_host_map = host_target_cfg.target_host_map
662 try:
663 host_target_list = target_host_map[target_platform][host_platform]
664 except KeyError:
665 host_target_list = []
666 warn_msg = "unsupported host, target combination ({}, {}) for MSVC version {}".format(
667 repr(host_platform), repr(target_platform), msvc_version
669 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
670 debug(warn_msg)
672 else:
674 target_platform = None
676 if all_host_targets:
677 host_targets_map = host_target_cfg.host_all_targets_map
678 else:
679 host_targets_map = host_target_cfg.host_def_targets_map
681 try:
682 host_target_list = host_targets_map[host_platform]
683 except KeyError:
684 msg = "Unrecognized host architecture %s for version %s"
685 raise MSVCUnsupportedHostArch(msg % (repr(host_platform), msvc_version)) from None
687 return host_platform, target_platform, host_target_list
689 _arm32_process_arm64_host = None
691 def is_arm32_process_arm64_host():
692 global _arm32_process_arm64_host
694 if _arm32_process_arm64_host is None:
696 host = get_native_host_architecture()
697 host = _ARCH_TO_CANONICAL.get(host.lower(),'')
698 host_isarm64 = host == 'arm64'
700 process = sysconfig.get_platform()
701 process_isarm32 = process == 'win-arm32'
703 _arm32_process_arm64_host = host_isarm64 and process_isarm32
705 return _arm32_process_arm64_host
707 _check_skip_sendtelemetry = None
709 def _skip_sendtelemetry(env):
710 global _check_skip_sendtelemetry
712 if _check_skip_sendtelemetry is None:
714 if _ARM32_ON_ARM64_SKIP_SENDTELEMETRY and is_arm32_process_arm64_host():
715 _check_skip_sendtelemetry = True
716 else:
717 _check_skip_sendtelemetry = False
719 if not _check_skip_sendtelemetry:
720 return False
722 msvc_version = env.get('MSVC_VERSION') if env else None
723 if not msvc_version:
724 msvc_version = msvc_default_version(env)
726 if not msvc_version:
727 return False
729 vernum = float(get_msvc_version_numeric(msvc_version))
730 if vernum < 14.2: # VS2019
731 return False
733 # arm32 process, arm64 host, VS2019+
734 return True
736 # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the
737 # MSVC_VERSION documentation in Tool/msvc.xml.
738 _VCVER = [
739 "14.3",
740 "14.2",
741 "14.1", "14.1Exp",
742 "14.0", "14.0Exp",
743 "12.0", "12.0Exp",
744 "11.0", "11.0Exp",
745 "10.0", "10.0Exp",
746 "9.0", "9.0Exp",
747 "8.0", "8.0Exp",
748 "7.1",
749 "7.0",
750 "6.0"]
752 # VS2017 and later: use a single vswhere json query to find all installations
754 # vswhere query:
755 # map vs major version to vc version (no suffix)
756 # build set of supported vc versions (including suffix)
758 _VSWHERE_VSMAJOR_TO_VCVERSION = {}
759 _VSWHERE_SUPPORTED_VCVER = set()
761 for vs_major, vc_version, vc_ver_list in (
762 ('17', '14.3', None),
763 ('16', '14.2', None),
764 ('15', '14.1', ['14.1Exp']),
766 _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major] = vc_version
767 _VSWHERE_SUPPORTED_VCVER.add(vc_version)
768 if vc_ver_list:
769 for vc_ver in vc_ver_list:
770 _VSWHERE_SUPPORTED_VCVER.add(vc_ver)
772 # vwhere query:
773 # build of set of candidate component ids
774 # preferred ranking: Enterprise, Professional, Community, BuildTools, Express
775 # Ent, Pro, Com, BT, Exp are in the same list
776 # Exp also has it's own list
777 # currently, only the express (Exp) suffix is expected
779 _VSWHERE_COMPONENTID_CANDIDATES = set()
780 _VSWHERE_COMPONENTID_RANKING = {}
781 _VSWHERE_COMPONENTID_SUFFIX = {}
782 _VSWHERE_COMPONENTID_SCONS_SUFFIX = {}
784 for component_id, component_rank, component_suffix, scons_suffix in (
785 ('Enterprise', 140, 'Ent', ''),
786 ('Professional', 130, 'Pro', ''),
787 ('Community', 120, 'Com', ''),
788 ('BuildTools', 110, 'BT', ''),
789 ('WDExpress', 100, 'Exp', 'Exp'),
791 _VSWHERE_COMPONENTID_CANDIDATES.add(component_id)
792 _VSWHERE_COMPONENTID_RANKING[component_id] = component_rank
793 _VSWHERE_COMPONENTID_SUFFIX[component_id] = component_suffix
794 _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id] = scons_suffix
796 # VS2015 and earlier: configure registry queries to probe for installed VC editions
797 _VCVER_TO_PRODUCT_DIR = {
798 '14.0': [
799 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')
801 '14.0Exp': [
802 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\14.0\Setup\VS\ProductDir'),
803 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\14.0\Setup\VC\ProductDir'),
804 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')
806 '12.0': [
807 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'),
809 '12.0Exp': [
810 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\12.0\Setup\VS\ProductDir'),
811 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\12.0\Setup\VC\ProductDir'),
813 '11.0': [
814 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'),
816 '11.0Exp': [
817 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\WDExpress\11.0\Setup\VS\ProductDir'),
818 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\11.0\Setup\VC\ProductDir'),
820 '10.0': [
821 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'),
823 '10.0Exp': [
824 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'),
826 '9.0': [
827 (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root
828 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root
829 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',),
830 ] if _VC90_Prefer_VCForPython else [
831 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir',),
832 (SCons.Util.HKEY_CURRENT_USER, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root
833 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\DevDiv\VCForPython\9.0\installdir',), # vs root
835 '9.0Exp': [
836 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'),
838 '8.0': [
839 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir'),
840 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'),
842 '8.0Exp': [
843 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'),
845 '7.1': [
846 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'),
848 '7.0': [
849 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'),
851 '6.0': [
852 (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'),
856 # detect ide binaries
858 VS2022_VS2002_DEV = (
859 MSVC.Kind.IDE_PROGRAM_DEVENV_COM, # devenv.com
862 VS1998_DEV = (
863 MSVC.Kind.IDE_PROGRAM_MSDEV_COM, # MSDEV.COM
866 VS2017_EXP = (
867 MSVC.Kind.IDE_PROGRAM_WDEXPRESS_EXE, # WDExpress.exe
870 VS2015_VS2012_EXP = (
871 MSVC.Kind.IDE_PROGRAM_WDEXPRESS_EXE, # WDExpress.exe [Desktop]
872 MSVC.Kind.IDE_PROGRAM_VSWINEXPRESS_EXE, # VSWinExpress.exe [Windows]
873 MSVC.Kind.IDE_PROGRAM_VWDEXPRESS_EXE, # VWDExpress.exe [Web]
876 VS2010_VS2005_EXP = (
877 MSVC.Kind.IDE_PROGRAM_VCEXPRESS_EXE,
880 # detect productdir kind
882 _DETECT = MSVC.Kind.VCVER_KIND_DETECT
884 _VCVER_KIND_DETECT = {
886 # 'VCVer': (relpath from pdir to vsroot, path from vsroot to ide binaries, ide binaries)
888 '14.3': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2022
889 '14.2': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2019
890 '14.1': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2017_EXP), # 2017
891 '14.1Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2017_EXP), # 2017
893 '14.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2015
894 '14.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2015
895 '12.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2013
896 '12.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2013
897 '11.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2012
898 '11.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2015_VS2012_EXP), # 2012
900 '10.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2010
901 '10.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2010
902 '9.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2008
903 '9.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2008
904 '8.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2005
905 '8.0Exp': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV + VS2010_VS2005_EXP), # 2005
907 '7.1': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2003
908 '7.0': _DETECT(root='..', path=r'Common7\IDE', programs=VS2022_VS2002_DEV), # 2001
910 '6.0': _DETECT(root='..', path=r'Common\MSDev98\Bin', programs=VS1998_DEV), # 1998
913 def msvc_version_to_maj_min(msvc_version):
914 msvc_version_numeric = get_msvc_version_numeric(msvc_version)
916 t = msvc_version_numeric.split(".")
917 if not len(t) == 2:
918 raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric))
919 try:
920 maj = int(t[0])
921 min = int(t[1])
922 return maj, min
923 except ValueError:
924 raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) from None
927 _VSWHERE_EXEGROUP_MSVS = [os.path.join(p, _VSWHERE_EXE) for p in [
928 # For bug 3333: support default location of vswhere for both
929 # 64 and 32 bit windows installs.
930 # For bug 3542: also accommodate not being on C: drive.
931 os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer"),
932 os.path.expandvars(r"%ProgramFiles%\Microsoft Visual Studio\Installer"),
935 _VSWHERE_EXEGROUP_PKGMGR = [os.path.join(p, _VSWHERE_EXE) for p in [
936 os.path.expandvars(r"%ChocolateyInstall%\bin"),
937 os.path.expandvars(r"%LOCALAPPDATA%\Microsoft\WinGet\Links"),
938 os.path.expanduser(r"~\scoop\shims"),
939 os.path.expandvars(r"%SCOOP%\shims"),
942 _VSWhereBinary = namedtuple('_VSWhereBinary', [
943 'vswhere_exe',
944 'vswhere_norm',
947 class VSWhereBinary(_VSWhereBinary, MSVC.Util.AutoInitialize):
949 _UNDEFINED_VSWHERE_BINARY = None
951 _cache_vswhere_paths = {}
953 @classmethod
954 def _initialize(cls):
955 vswhere_binary = cls(
956 vswhere_exe=None,
957 vswhere_norm=None,
959 cls._UNDEFINED_VSWHERE_BINARY = vswhere_binary
960 for symbol in (None, ''):
961 cls._cache_vswhere_paths[symbol] = vswhere_binary
963 @classmethod
964 def factory(cls, vswhere_exe):
966 if not vswhere_exe:
967 return cls._UNDEFINED_VSWHERE_BINARY
969 vswhere_binary = cls._cache_vswhere_paths.get(vswhere_exe)
970 if vswhere_binary:
971 return vswhere_binary
973 vswhere_norm = MSVC.Util.normalize_path(vswhere_exe)
975 vswhere_binary = cls._cache_vswhere_paths.get(vswhere_norm)
976 if vswhere_binary:
977 return vswhere_binary
979 vswhere_binary = cls(
980 vswhere_exe=vswhere_exe,
981 vswhere_norm=vswhere_norm,
984 cls._cache_vswhere_paths[vswhere_exe] = vswhere_binary
985 cls._cache_vswhere_paths[vswhere_norm] = vswhere_binary
987 return vswhere_binary
989 class _VSWhereExecutable(MSVC.Util.AutoInitialize):
991 debug_extra = None
993 class _VSWhereUserPriority(enum.IntEnum):
994 HIGH = 0 # before msvs locations
995 DEFAULT = enum.auto() # after msvs locations and before pkgmgr locations
996 LOW = enum.auto() # after pkgmgr locations
998 priority_default = _VSWhereUserPriority.DEFAULT
999 priority_map = {}
1000 priority_symbols = []
1001 priority_indmap = {}
1003 for e in _VSWhereUserPriority:
1004 for symbol in (e.name.lower(), e.name.lower().capitalize(), e.name):
1005 priority_map[symbol] = e.value
1006 priority_symbols.append(symbol)
1007 priority_indmap[e.value] = e.name
1009 UNDEFINED_VSWHERE_BINARY = VSWhereBinary.factory(None)
1011 @classmethod
1012 def reset(cls):
1014 cls._vswhere_exegroups = None
1015 cls._vswhere_exegroups_user = None
1016 cls._vswhere_exegroup_msvs = None
1017 cls._vswhere_exegroup_pkgmgr = None
1019 cls.vswhere_frozen_flag = False
1020 cls.vswhere_frozen_binary = None
1022 @classmethod
1023 def _initialize(cls):
1024 cls.debug_extra = common.debug_extra(cls)
1025 cls.reset()
1027 @classmethod
1028 def _vswhere_exegroup_binaries(cls, vswhere_exe_list, label):
1029 vswhere_binaries = []
1030 for vswhere_exe in vswhere_exe_list:
1031 if not os.path.exists(vswhere_exe):
1032 continue
1033 vswhere_binary = VSWhereBinary.factory(vswhere_exe)
1034 vswhere_binaries.append(vswhere_binary)
1035 debug(
1036 "insert exegroup=%s, vswhere_binary=%s",
1037 label, vswhere_binary, extra=cls.debug_extra
1039 return vswhere_binaries
1041 @classmethod
1042 def _get_vswhere_exegroups(cls):
1044 if cls._vswhere_exegroups is None:
1046 cls._vswhere_exegroup_msvs = cls._vswhere_exegroup_binaries(_VSWHERE_EXEGROUP_MSVS, 'MSVS')
1047 cls._vswhere_exegroup_pkgmgr = cls._vswhere_exegroup_binaries(_VSWHERE_EXEGROUP_PKGMGR, 'PKGMGR')
1049 cls._vswhere_exegroups_user = [
1050 [] for e in cls._VSWhereUserPriority
1053 vswhere_exegroups = [
1054 cls._vswhere_exegroups_user[cls._VSWhereUserPriority.HIGH],
1055 cls._vswhere_exegroup_msvs,
1056 cls._vswhere_exegroups_user[cls._VSWhereUserPriority.DEFAULT],
1057 cls._vswhere_exegroup_pkgmgr,
1058 cls._vswhere_exegroups_user[cls._VSWhereUserPriority.LOW],
1061 cls._vswhere_exegroups = vswhere_exegroups
1063 return cls._vswhere_exegroups
1065 @classmethod
1066 def _get_vswhere_exegroups_user(cls):
1068 if cls._vswhere_exegroups_user is None:
1069 cls._get_vswhere_exegroups()
1071 return cls._vswhere_exegroups_user
1073 @classmethod
1074 def _vswhere_exegroup_binary(cls, exegroup):
1075 # first vswhere binary in group or UNDEFINED_VSWHERE_BINARY
1076 vswhere_binary = exegroup[0] if exegroup else cls.UNDEFINED_VSWHERE_BINARY
1077 return vswhere_binary
1079 @classmethod
1080 def _vswhere_current_binary(cls):
1081 # first vswhere binary in priority order or UNDEFINED_VSWHERE_BINARY
1082 vswhere_binary = cls.UNDEFINED_VSWHERE_BINARY
1083 vswhere_exegroups = cls._get_vswhere_exegroups()
1084 for exegroup in vswhere_exegroups:
1085 binary = cls._vswhere_exegroup_binary(exegroup)
1086 if binary == cls.UNDEFINED_VSWHERE_BINARY:
1087 continue
1088 vswhere_binary = binary
1089 break
1090 return vswhere_binary
1092 @classmethod
1093 def _vswhere_all_executables(cls):
1094 # unique vswhere executables in priority order
1095 vswhere_exe_list = []
1096 vswhere_norm_set = set()
1097 vswhere_exegroups = cls._get_vswhere_exegroups()
1098 for exegroup in vswhere_exegroups:
1099 for vswhere_binary in exegroup:
1100 if vswhere_binary.vswhere_norm in vswhere_norm_set:
1101 continue
1102 vswhere_norm_set.add(vswhere_binary.vswhere_norm)
1103 vswhere_exe_list.append(vswhere_binary.vswhere_exe)
1104 return vswhere_exe_list
1106 @classmethod
1107 def is_frozen(cls) -> bool:
1108 rval = bool(cls.vswhere_frozen_flag)
1109 return rval
1111 @classmethod
1112 def freeze_vswhere_binary(cls):
1113 if not cls.vswhere_frozen_flag:
1114 cls.vswhere_frozen_flag = True
1115 cls.vswhere_frozen_binary = cls._vswhere_current_binary()
1116 debug("freeze=%s", cls.vswhere_frozen_binary, extra=cls.debug_extra)
1117 return cls.vswhere_frozen_binary
1119 @classmethod
1120 def freeze_vswhere_executable(cls):
1121 vswhere_binary = cls.freeze_vswhere_binary()
1122 vswhere_exe = vswhere_binary.vswhere_exe
1123 return vswhere_exe
1125 @classmethod
1126 def get_vswhere_executable(cls):
1127 if cls.vswhere_frozen_flag:
1128 vswhere_binary = cls.vswhere_frozen_binary
1129 else:
1130 vswhere_binary = cls._vswhere_current_binary()
1131 vswhere_exe = vswhere_binary.vswhere_exe
1132 debug("vswhere_exe=%s", repr(vswhere_exe), extra=cls.debug_extra)
1133 return vswhere_exe
1135 @classmethod
1136 def _vswhere_priority_group(cls, priority):
1137 if not priority:
1138 group = cls.priority_default
1139 else:
1140 group = cls.priority_map.get(priority)
1141 if group is None:
1142 msg = f'Value specified for vswhere executable priority is not supported: {priority!r}:\n' \
1143 f' Valid values are: {cls.priority_symbols}'
1144 debug(f'VSWhereUserError: {msg}', extra=cls.debug_extra)
1145 raise VSWhereUserError(msg)
1146 return group
1148 @classmethod
1149 def register_vswhere_executable(cls, vswhere_exe, priority=None):
1151 vswhere_binary = cls.UNDEFINED_VSWHERE_BINARY
1153 if not vswhere_exe:
1154 # ignore: None or empty
1155 return vswhere_binary
1157 if not os.path.exists(vswhere_exe):
1158 msg = f'Specified vswhere executable not found: {vswhere_exe!r}.'
1159 debug(f'VSWhereUserError: {msg}', extra=cls.debug_extra)
1160 raise VSWhereUserError(msg)
1162 group = cls._vswhere_priority_group(priority)
1164 vswhere_binary = VSWhereBinary.factory(vswhere_exe)
1166 if cls.vswhere_frozen_flag:
1168 if vswhere_binary.vswhere_norm == cls.vswhere_frozen_binary.vswhere_norm:
1169 # ignore: user executable == frozen executable
1170 return vswhere_binary
1172 msg = 'A different vswhere execuable cannot be requested after initial detetection:\n' \
1173 f' initial vswhere executable: {cls.vswhere_frozen_binary.vswhere_exe!r}\n' \
1174 f' request vswhere executable: {vswhere_binary.vswhere_exe!r}'
1176 debug(f'VSWhereUserError: {msg}', extra=cls.debug_extra)
1177 raise VSWhereUserError(msg)
1179 vswhere_exegroups_user = cls._get_vswhere_exegroups_user()
1181 exegroup = vswhere_exegroups_user[group]
1182 group_binary = cls._vswhere_exegroup_binary(exegroup)
1184 if vswhere_binary.vswhere_norm == group_binary.vswhere_norm:
1185 # ignore: user executable == exegroup[0] executable
1186 return vswhere_binary
1188 exegroup.insert(0, vswhere_binary)
1189 debug(
1190 "insert exegroup=user[%s], vswhere_binary=%s",
1191 cls.priority_indmap[group], vswhere_binary, extra=cls.debug_extra
1194 return vswhere_binary
1196 @classmethod
1197 def vswhere_freeze_executable(cls, vswhere_exe):
1198 vswhere_binary = cls.register_vswhere_executable(vswhere_exe, priority='high')
1199 frozen_binary = cls.freeze_vswhere_binary()
1200 return frozen_binary, vswhere_binary
1202 @classmethod
1203 def vswhere_freeze_env(cls, env):
1205 if env is None:
1206 # no environment, VSWHERE undefined
1207 vswhere_exe = None
1208 write_vswhere = False
1209 elif not env.get('VSWHERE'):
1210 # environment, VSWHERE undefined/none/empty
1211 vswhere_exe = None
1212 write_vswhere = True
1213 else:
1214 # environment, VSWHERE defined
1215 vswhere_exe = env.subst('$VSWHERE')
1216 write_vswhere = False
1218 frozen_binary, vswhere_binary = cls.vswhere_freeze_executable(vswhere_exe)
1220 if write_vswhere and frozen_binary.vswhere_norm != vswhere_binary.vswhere_norm:
1221 env['VSWHERE'] = frozen_binary.vswhere_exe
1222 debug(
1223 "env['VSWHERE']=%s",
1224 repr(frozen_binary.vswhere_exe), extra=cls.debug_extra
1227 return frozen_binary, vswhere_binary
1229 # external use
1231 def vswhere_register_executable(vswhere_exe, priority=None, freeze=False):
1232 debug(
1233 'register vswhere_exe=%s, priority=%s, freeze=%s',
1234 repr(vswhere_exe), repr(priority), repr(freeze)
1236 _VSWhereExecutable.register_vswhere_executable(vswhere_exe, priority=priority)
1237 if freeze:
1238 _VSWhereExecutable.freeze_vswhere_executable()
1239 rval = _VSWhereExecutable.get_vswhere_executable()
1240 debug('current vswhere_exe=%s, is_frozen=%s', repr(rval), _VSWhereExecutable.is_frozen())
1241 return vswhere_exe
1243 def vswhere_get_executable():
1244 debug('')
1245 vswhere_exe = _VSWhereExecutable.get_vswhere_executable()
1246 return vswhere_exe
1248 def vswhere_freeze_executable():
1249 debug('')
1250 vswhere_exe = _VSWhereExecutable.freeze_vswhere_executable()
1251 return vswhere_exe
1253 # internal use only
1254 vswhere_freeze_env = _VSWhereExecutable.vswhere_freeze_env
1256 def msvc_find_vswhere():
1257 """ Find the location of vswhere """
1258 # NB: this gets called from testsuite on non-Windows platforms.
1259 # Whether that makes sense or not, don't break it for those.
1260 vswhere_path = _VSWhereExecutable.get_vswhere_executable()
1261 return vswhere_path
1263 _MSVCInstance = namedtuple('_MSVCInstance', [
1264 'vc_path',
1265 'vc_version',
1266 'vc_version_numeric',
1267 'vc_version_scons',
1268 'vc_release',
1269 'vc_component_id',
1270 'vc_component_rank',
1271 'vc_component_suffix',
1274 class MSVCInstance(_MSVCInstance):
1276 @staticmethod
1277 def msvc_instances_default_order(a, b):
1278 # vc version numeric: descending order
1279 if a.vc_version_numeric != b.vc_version_numeric:
1280 return 1 if a.vc_version_numeric < b.vc_version_numeric else -1
1281 # vc release: descending order (release, preview)
1282 if a.vc_release != b.vc_release:
1283 return 1 if a.vc_release < b.vc_release else -1
1284 # component rank: descending order
1285 if a.vc_component_rank != b.vc_component_rank:
1286 return 1 if a.vc_component_rank < b.vc_component_rank else -1
1287 return 0
1289 class _VSWhere(MSVC.Util.AutoInitialize):
1291 debug_extra = None
1293 @classmethod
1294 def reset(cls):
1296 cls.seen_vswhere = set()
1297 cls.seen_root = set()
1299 cls.msvc_instances = []
1300 cls.msvc_map = {}
1302 @classmethod
1303 def _initialize(cls):
1304 cls.debug_extra = common.debug_extra(cls)
1305 cls.reset()
1307 @classmethod
1308 def _filter_vswhere_binary(cls, vswhere_binary):
1310 vswhere_norm = None
1312 if vswhere_binary.vswhere_norm not in cls.seen_vswhere:
1313 cls.seen_vswhere.add(vswhere_binary.vswhere_norm)
1314 vswhere_norm = vswhere_binary.vswhere_norm
1316 return vswhere_norm
1318 @classmethod
1319 def _new_roots_discovered(cls):
1320 if len(cls.seen_vswhere) > 1:
1321 raise MSVCInternalError(f'vswhere discovered new msvc installations after initial detection')
1323 @classmethod
1324 def _vswhere_query_json_output(cls, vswhere_exe, vswhere_args):
1326 vswhere_json = None
1328 once = True
1329 while once:
1330 once = False
1331 # using break for single exit (unless exception)
1333 vswhere_cmd = [vswhere_exe] + vswhere_args + ['-format', 'json', '-utf8']
1334 debug("running: %s", vswhere_cmd, extra=cls.debug_extra)
1336 try:
1337 cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True)
1338 except OSError as e:
1339 errmsg = str(e)
1340 debug("%s: %s", type(e).__name__, errmsg, extra=cls.debug_extra)
1341 break
1342 except Exception as e:
1343 errmsg = str(e)
1344 debug("%s: %s", type(e).__name__, errmsg, extra=cls.debug_extra)
1345 raise
1347 if not cp.stdout:
1348 debug("no vswhere information returned", extra=cls.debug_extra)
1349 break
1351 vswhere_output = cp.stdout.decode('utf8', errors='replace')
1352 if not vswhere_output:
1353 debug("no vswhere information output", extra=cls.debug_extra)
1354 break
1356 try:
1357 vswhere_output_json = json.loads(vswhere_output)
1358 except json.decoder.JSONDecodeError:
1359 debug("json decode exception loading vswhere output", extra=cls.debug_extra)
1360 break
1362 vswhere_json = vswhere_output_json
1363 break
1365 debug(
1366 'vswhere_json=%s, vswhere_exe=%s',
1367 bool(vswhere_json), repr(vswhere_exe), extra=cls.debug_extra
1370 return vswhere_json
1372 @classmethod
1373 def vswhere_update_msvc_map(cls, vswhere_exe):
1375 frozen_binary, vswhere_binary = _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe)
1377 vswhere_norm = cls._filter_vswhere_binary(frozen_binary)
1378 if not vswhere_norm:
1379 return cls.msvc_map
1381 debug('vswhere_norm=%s', repr(vswhere_norm), extra=cls.debug_extra)
1383 vswhere_json = cls._vswhere_query_json_output(
1384 vswhere_norm,
1385 ['-all', '-products', '*']
1388 if not vswhere_json:
1389 return cls.msvc_map
1391 n_instances = len(cls.msvc_instances)
1393 for instance in vswhere_json:
1395 #print(json.dumps(instance, indent=4, sort_keys=True))
1397 installation_path = instance.get('installationPath')
1398 if not installation_path or not os.path.exists(installation_path):
1399 continue
1401 vc_path = os.path.join(installation_path, 'VC')
1402 if not os.path.exists(vc_path):
1403 continue
1405 vc_root = MSVC.Util.normalize_path(vc_path)
1406 if vc_root in cls.seen_root:
1407 continue
1408 cls.seen_root.add(vc_root)
1410 installation_version = instance.get('installationVersion')
1411 if not installation_version:
1412 continue
1414 vs_major = installation_version.split('.')[0]
1415 if not vs_major in _VSWHERE_VSMAJOR_TO_VCVERSION:
1416 debug('ignore vs_major: %s', vs_major, extra=cls.debug_extra)
1417 continue
1419 vc_version = _VSWHERE_VSMAJOR_TO_VCVERSION[vs_major]
1421 product_id = instance.get('productId')
1422 if not product_id:
1423 continue
1425 component_id = product_id.split('.')[-1]
1426 if component_id not in _VSWHERE_COMPONENTID_CANDIDATES:
1427 debug('ignore component_id: %s', component_id, extra=cls.debug_extra)
1428 continue
1430 component_rank = _VSWHERE_COMPONENTID_RANKING.get(component_id,0)
1431 if component_rank == 0:
1432 raise MSVCInternalError(f'unknown component_rank for component_id: {component_id!r}')
1434 scons_suffix = _VSWHERE_COMPONENTID_SCONS_SUFFIX[component_id]
1436 if scons_suffix:
1437 vc_version_scons = vc_version + scons_suffix
1438 else:
1439 vc_version_scons = vc_version
1441 is_prerelease = True if instance.get('isPrerelease', False) else False
1442 is_release = False if is_prerelease else True
1444 msvc_instance = MSVCInstance(
1445 vc_path = vc_path,
1446 vc_version = vc_version,
1447 vc_version_numeric = float(vc_version),
1448 vc_version_scons = vc_version_scons,
1449 vc_release = is_release,
1450 vc_component_id = component_id,
1451 vc_component_rank = component_rank,
1452 vc_component_suffix = component_suffix,
1455 cls.msvc_instances.append(msvc_instance)
1457 new_roots = bool(len(cls.msvc_instances) > n_instances)
1458 if new_roots:
1460 cls.msvc_instances = sorted(
1461 cls.msvc_instances,
1462 key=cmp_to_key(MSVCInstance.msvc_instances_default_order)
1465 cls.msvc_map = {}
1467 for msvc_instance in cls.msvc_instances:
1469 debug(
1470 'msvc instance: msvc_version=%s, is_release=%s, component_id=%s, vc_path=%s',
1471 repr(msvc_instance.vc_version_scons), msvc_instance.vc_release,
1472 repr(msvc_instance.vc_component_id), repr(msvc_instance.vc_path),
1473 extra=cls.debug_extra
1476 key = (msvc_instance.vc_version_scons, msvc_instance.vc_release)
1477 cls.msvc_map.setdefault(key,[]).append(msvc_instance)
1479 if msvc_instance.vc_version_scons == msvc_instance.vc_version:
1480 continue
1482 key = (msvc_instance.vc_version, msvc_instance.vc_release)
1483 cls.msvc_map.setdefault(key,[]).append(msvc_instance)
1485 cls._new_roots_discovered()
1487 debug(
1488 'new_roots=%s, msvc_instances=%s',
1489 new_roots, len(cls.msvc_instances), extra=cls.debug_extra
1492 return cls.msvc_map
1494 _cache_pdir_vswhere_queries = {}
1496 def _find_vc_pdir_vswhere(msvc_version, vswhere_exe):
1497 """ Find the MSVC product directory using the vswhere program.
1499 Args:
1500 msvc_version: MSVC version to search for
1501 vswhere_exe: vswhere executable or None
1503 Returns:
1504 MSVC install dir or None
1506 Raises:
1507 UnsupportedVersion: if the version is not known by this file
1510 global _cache_pdir_vswhere_queries
1512 msvc_map = _VSWhere.vswhere_update_msvc_map(vswhere_exe)
1513 if not msvc_map:
1514 return None
1516 rval = _cache_pdir_vswhere_queries.get(msvc_version, UNDEFINED)
1517 if rval != UNDEFINED:
1518 debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(rval))
1519 return rval
1521 if msvc_version not in _VSWHERE_SUPPORTED_VCVER:
1522 debug("Unknown version of MSVC: %s", msvc_version)
1523 raise UnsupportedVersion("Unknown version %s" % msvc_version)
1525 is_release = True
1526 key = (msvc_version, is_release)
1528 msvc_instances = msvc_map.get(key, UNDEFINED)
1529 if msvc_instances == UNDEFINED:
1530 debug(
1531 'msvc instances lookup failed: msvc_version=%s, is_release=%s',
1532 repr(msvc_version), repr(is_release)
1534 msvc_instances = []
1536 save_pdir_kind = []
1538 pdir = None
1539 kind_t = None
1541 for msvc_instance in msvc_instances:
1543 vcdir = msvc_instance.vc_path
1545 vckind_t = MSVC.Kind.msvc_version_pdir_vswhere_kind(msvc_version, vcdir, _VCVER_KIND_DETECT[msvc_version])
1546 if vckind_t.skip:
1547 if vckind_t.save:
1548 debug('save kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir))
1549 save_pdir_kind.append((vcdir, vckind_t))
1550 else:
1551 debug('skip kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir))
1552 continue
1554 pdir = vcdir
1555 kind_t = vckind_t
1556 break
1558 if not pdir and not kind_t:
1559 if save_pdir_kind:
1560 pdir, kind_t = save_pdir_kind[0]
1562 MSVC.Kind.msvc_version_register_kind(msvc_version, kind_t)
1564 debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(pdir))
1565 _cache_pdir_vswhere_queries[msvc_version] = pdir
1567 return pdir
1569 _cache_pdir_registry_queries = {}
1571 def _find_vc_pdir_registry(msvc_version):
1572 """ Find the MSVC product directory using the registry.
1574 Args:
1575 msvc_version: MSVC version to search for
1577 Returns:
1578 MSVC install dir or None
1580 Raises:
1581 UnsupportedVersion: if the version is not known by this file
1584 global _cache_pdir_registry_queries
1586 rval = _cache_pdir_registry_queries.get(msvc_version, UNDEFINED)
1587 if rval != UNDEFINED:
1588 debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(rval))
1589 return rval
1591 try:
1592 regkeys = _VCVER_TO_PRODUCT_DIR[msvc_version]
1593 except KeyError:
1594 debug("Unknown version of MSVC: %s", msvc_version)
1595 raise UnsupportedVersion("Unknown version %s" % msvc_version) from None
1597 save_pdir_kind = []
1599 is_win64 = common.is_win64()
1601 pdir = None
1602 kind_t = None
1604 root = 'Software\\'
1605 for hkroot, key in regkeys:
1607 if not hkroot or not key:
1608 continue
1610 if is_win64:
1611 msregkeys = [root + 'Wow6432Node\\' + key, root + key]
1612 else:
1613 msregkeys = [root + key]
1615 vcdir = None
1616 for msregkey in msregkeys:
1617 debug('trying VC registry key %s', repr(msregkey))
1618 try:
1619 vcdir = common.read_reg(msregkey, hkroot)
1620 except OSError:
1621 continue
1622 if vcdir:
1623 break
1625 if not vcdir:
1626 debug('no VC registry key %s', repr(key))
1627 continue
1629 is_vcforpython = False
1631 is_vsroot = False
1632 if msvc_version == '9.0' and key.lower().endswith('\\vcforpython\\9.0\\installdir'):
1633 # Visual C++ for Python registry key is VS installdir (root) not VC productdir
1634 is_vsroot = True
1635 is_vcforpython = True
1636 elif msvc_version in ('14.0Exp', '12.0Exp', '11.0Exp') and key.lower().endswith('\\setup\\vs\\productdir'):
1637 # Visual Studio 2015/2013/2012 Express is VS productdir (root) not VC productdir
1638 is_vsroot = True
1640 if is_vsroot:
1641 vcdir = os.path.join(vcdir, 'VC')
1642 debug('convert vs root to vc dir: %s', repr(vcdir))
1644 if not os.path.exists(vcdir):
1645 debug('reg says dir is %s, but it does not exist. (ignoring)', repr(vcdir))
1646 continue
1648 vckind_t = MSVC.Kind.msvc_version_pdir_registry_kind(msvc_version, vcdir, _VCVER_KIND_DETECT[msvc_version], is_vcforpython)
1649 if vckind_t.skip:
1650 if vckind_t.save:
1651 debug('save kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir))
1652 save_pdir_kind.append((vcdir, vckind_t))
1653 else:
1654 debug('skip kind: msvc_version=%s, pdir=%s', repr(msvc_version), repr(vcdir))
1655 continue
1657 pdir = vcdir
1658 kind_t = vckind_t
1659 break
1661 if not pdir and not kind_t:
1662 if save_pdir_kind:
1663 pdir, kind_t = save_pdir_kind[0]
1665 MSVC.Kind.msvc_version_register_kind(msvc_version, kind_t)
1667 debug('msvc_version=%s, pdir=%s', repr(msvc_version), repr(pdir))
1668 _cache_pdir_registry_queries[msvc_version] = pdir
1670 return pdir
1672 def _find_vc_pdir(msvc_version, vswhere_exe):
1673 """Find the MSVC product directory for the given version.
1675 Args:
1676 msvc_version: str
1677 msvc version (major.minor, e.g. 10.0)
1679 vswhere_exe: str
1680 vswhere executable or None
1682 Returns:
1683 str: Path found in registry, or None
1685 Raises:
1686 UnsupportedVersion: if the version is not known by this file.
1688 UnsupportedVersion inherits from VisualCException.
1692 if msvc_version in _VSWHERE_SUPPORTED_VCVER:
1694 pdir = _find_vc_pdir_vswhere(msvc_version, vswhere_exe)
1695 if pdir:
1696 debug('VC found: %s, dir=%s', repr(msvc_version), repr(pdir))
1697 return pdir
1699 elif msvc_version in _VCVER_TO_PRODUCT_DIR:
1701 pdir = _find_vc_pdir_registry(msvc_version)
1702 if pdir:
1703 debug('VC found: %s, dir=%s', repr(msvc_version), repr(pdir))
1704 return pdir
1706 else:
1708 debug("Unknown version of MSVC: %s", repr(msvc_version))
1709 raise UnsupportedVersion("Unknown version %s" % repr(msvc_version)) from None
1711 debug('no VC found for version %s', repr(msvc_version))
1712 return None
1714 def find_vc_pdir(msvc_version, env=None):
1715 """Find the MSVC product directory for the given version.
1717 Args:
1718 msvc_version: str
1719 msvc version (major.minor, e.g. 10.0)
1720 env:
1721 optional to look up VSWHERE variable
1723 Returns:
1724 str: Path found in registry, or None
1726 Raises:
1727 UnsupportedVersion: if the version is not known by this file.
1729 UnsupportedVersion inherits from VisualCException.
1733 frozen_binary, _ = _VSWhereExecutable.vswhere_freeze_env(env)
1735 pdir = _find_vc_pdir(msvc_version, frozen_binary.vswhere_exe)
1736 debug('pdir=%s', repr(pdir))
1738 return pdir
1740 # register find_vc_pdir function with Kind routines
1741 # in case of unknown msvc_version (just in case)
1742 MSVC.Kind.register_msvc_version_pdir_func(find_vc_pdir)
1744 def _reset_vc_pdir():
1745 debug('reset pdir caches')
1746 global _cache_pdir_vswhere_queries
1747 global _cache_pdir_registry_queries
1748 _cache_pdir_vswhere_queries = {}
1749 _cache_pdir_registry_queries = {}
1751 def find_batch_file(msvc_version, host_arch, target_arch, pdir):
1753 Find the location of the batch script which should set up the compiler
1754 for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress
1756 In newer (2017+) compilers, make use of the fact there are vcvars
1757 scripts named with a host_target pair that calls vcvarsall.bat properly,
1758 so use that and return an empty argument.
1761 # filter out e.g. "Exp" from the version name
1762 vernum = float(get_msvc_version_numeric(msvc_version))
1763 vernum_int = int(vernum * 10)
1765 sdk_pdir = pdir
1767 arg = ''
1768 vcdir = None
1769 clexe = None
1770 depbat = None
1772 if vernum_int >= 143:
1773 # 14.3 (VS2022) and later
1774 batfiledir = os.path.join(pdir, "Auxiliary", "Build")
1775 batfile, _ = _GE2022_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)]
1776 batfilename = os.path.join(batfiledir, batfile)
1777 vcdir = pdir
1778 elif 143 > vernum_int >= 141:
1779 # 14.2 (VS2019) to 14.1 (VS2017)
1780 batfiledir = os.path.join(pdir, "Auxiliary", "Build")
1781 batfile, _ = _LE2019_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)]
1782 batfilename = os.path.join(batfiledir, batfile)
1783 vcdir = pdir
1784 elif 141 > vernum_int >= 100:
1785 # 14.0 (VS2015) to 10.0 (VS2010)
1786 arg, batfile, cl_path_comps = _LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)]
1787 batfilename = os.path.join(pdir, "vcvarsall.bat")
1788 depbat = os.path.join(pdir, *cl_path_comps, batfile)
1789 clexe = os.path.join(pdir, *cl_path_comps, _CL_EXE_NAME)
1790 elif 100 > vernum_int >= 80:
1791 # 9.0 (VS2008) to 8.0 (VS2005)
1792 arg, batfile, cl_path_comps = _LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)]
1793 if vernum_int == 90 and MSVC.Kind.msvc_version_is_vcforpython(msvc_version):
1794 # 9.0 (VS2008) Visual C++ for Python:
1795 # sdk batch files do not point to the VCForPython installation
1796 # vcvarsall batch file is in installdir not productdir (e.g., vc\..\vcvarsall.bat)
1797 # dependency batch files are not called from vcvarsall.bat
1798 sdk_pdir = None
1799 batfilename = os.path.join(pdir, os.pardir, "vcvarsall.bat")
1800 depbat = None
1801 else:
1802 batfilename = os.path.join(pdir, "vcvarsall.bat")
1803 depbat = os.path.join(pdir, *cl_path_comps, batfile)
1804 clexe = os.path.join(pdir, *cl_path_comps, _CL_EXE_NAME)
1805 else: # 80 > vernum_int
1806 # 7.1 (VS2003) and earlier
1807 pdir = os.path.join(pdir, "Bin")
1808 batfilename = os.path.join(pdir, "vcvars32.bat")
1809 clexe = os.path.join(pdir, _CL_EXE_NAME)
1811 if not os.path.exists(batfilename):
1812 debug("batch file not found: %s", batfilename)
1813 batfilename = None
1815 if clexe and not os.path.exists(clexe):
1816 debug("%s not found: %s", _CL_EXE_NAME, clexe)
1817 batfilename = None
1819 if depbat and not os.path.exists(depbat):
1820 debug("dependency batch file not found: %s", depbat)
1821 batfilename = None
1823 return batfilename, arg, vcdir, sdk_pdir
1825 def find_batch_file_sdk(host_arch, target_arch, sdk_pdir):
1827 Find the location of the sdk batch script which should set up the compiler
1828 for any TARGET_ARCH whose compilers were installed by Visual Studio/VCExpress
1831 installed_sdks = get_installed_sdks()
1832 for _sdk in installed_sdks:
1833 sdk_bat_file = _sdk.get_sdk_vc_script(host_arch, target_arch)
1834 if not sdk_bat_file:
1835 debug("sdk batch file not found:%s", _sdk)
1836 else:
1837 sdk_bat_file_path = os.path.join(sdk_pdir, sdk_bat_file)
1838 if os.path.exists(sdk_bat_file_path):
1839 debug('sdk_bat_file_path:%s', sdk_bat_file_path)
1840 return sdk_bat_file_path
1842 return None
1844 __INSTALLED_VCS_RUN = None
1846 def _reset_installed_vcs():
1847 global __INSTALLED_VCS_RUN
1848 debug('reset __INSTALLED_VCS_RUN')
1849 __INSTALLED_VCS_RUN = None
1851 _VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt']
1852 _VC_TOOLS_VERSION_FILE = os.sep.join(_VC_TOOLS_VERSION_FILE_PATH)
1854 def _check_files_exist_in_vc_dir(env, vc_dir, msvc_version) -> bool:
1855 """Return status of finding batch file and cl.exe to use.
1857 Locates required vcvars batch files and cl in the vc_dir depending on
1858 TARGET_ARCH, HOST_ARCH and the msvc version. TARGET_ARCH and HOST_ARCH
1859 can be extracted from the passed env, unless the env is None, in which
1860 case the native platform is assumed for the host and all associated
1861 targets.
1863 Args:
1864 env: Environment
1865 a construction environment, usually if this is passed its
1866 because there is a desired TARGET_ARCH to be used when searching
1867 for a cl.exe
1868 vc_dir: str
1869 the path to the VC dir in the MSVC installation
1870 msvc_version: str
1871 msvc version (major.minor, e.g. 10.0)
1873 Returns:
1874 bool:
1878 # Find the host, target, and all candidate (host, target) platform combinations:
1879 platforms = get_host_target(env, msvc_version, all_host_targets=True)
1880 debug("host_platform %s, target_platform %s host_target_list %s", *platforms)
1881 host_platform, target_platform, host_target_list = platforms
1883 vernum = float(get_msvc_version_numeric(msvc_version))
1884 vernum_int = int(vernum * 10)
1886 # make sure the cl.exe exists meaning the tool is installed
1887 if vernum_int >= 141:
1888 # 14.1 (VS2017) and later
1889 # 2017 and newer allowed multiple versions of the VC toolset to be
1890 # installed at the same time. This changes the layout.
1891 # Just get the default tool version for now
1892 # TODO: support setting a specific minor VC version
1893 default_toolset_file = os.path.join(vc_dir, _VC_TOOLS_VERSION_FILE)
1894 try:
1895 with open(default_toolset_file) as f:
1896 vc_specific_version = f.readlines()[0].strip()
1897 except OSError:
1898 debug('failed to read %s', default_toolset_file)
1899 return False
1900 except IndexError:
1901 debug('failed to find MSVC version in %s', default_toolset_file)
1902 return False
1904 if vernum_int >= 143:
1905 # 14.3 (VS2022) and later
1906 host_target_batchfile_clpathcomps = _GE2022_HOST_TARGET_BATCHFILE_CLPATHCOMPS
1907 else:
1908 # 14.2 (VS2019) to 14.1 (VS2017)
1909 host_target_batchfile_clpathcomps = _LE2019_HOST_TARGET_BATCHFILE_CLPATHCOMPS
1911 for host_platform, target_platform in host_target_list:
1913 debug('host platform %s, target platform %s for version %s', host_platform, target_platform, msvc_version)
1915 batchfile_clpathcomps = host_target_batchfile_clpathcomps.get((host_platform, target_platform), None)
1916 if batchfile_clpathcomps is None:
1917 debug('unsupported host/target platform combo: (%s,%s)', host_platform, target_platform)
1918 continue
1920 batfile, cl_path_comps = batchfile_clpathcomps
1922 batfile_path = os.path.join(vc_dir, "Auxiliary", "Build", batfile)
1923 if not os.path.exists(batfile_path):
1924 debug("batch file not found: %s", batfile_path)
1925 continue
1927 cl_path = os.path.join(vc_dir, 'Tools', 'MSVC', vc_specific_version, *cl_path_comps, _CL_EXE_NAME)
1928 if not os.path.exists(cl_path):
1929 debug("%s not found: %s", _CL_EXE_NAME, cl_path)
1930 continue
1932 debug('%s found: %s', _CL_EXE_NAME, cl_path)
1933 return True
1935 elif 141 > vernum_int >= 80:
1936 # 14.0 (VS2015) to 8.0 (VS2005)
1938 if vernum_int >= 100:
1939 # 14.0 (VS2015) to 10.0 (VS2010)
1940 host_target_batcharg_batchfile_clpathcomps = _LE2015_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS
1941 else:
1942 # 9.0 (VS2008) to 8.0 (VS2005)
1943 host_target_batcharg_batchfile_clpathcomps = _LE2008_HOST_TARGET_BATCHARG_BATCHFILE_CLPATHCOMPS
1945 if vernum_int == 90 and MSVC.Kind.msvc_version_is_vcforpython(msvc_version):
1946 # 9.0 (VS2008) Visual C++ for Python:
1947 # vcvarsall batch file is in installdir not productdir (e.g., vc\..\vcvarsall.bat)
1948 # dependency batch files are not called from vcvarsall.bat
1949 batfile_path = os.path.join(vc_dir, os.pardir, "vcvarsall.bat")
1950 check_depbat = False
1951 else:
1952 batfile_path = os.path.join(vc_dir, "vcvarsall.bat")
1953 check_depbat = True
1955 if not os.path.exists(batfile_path):
1956 debug("batch file not found: %s", batfile_path)
1957 return False
1959 for host_platform, target_platform in host_target_list:
1961 debug('host platform %s, target platform %s for version %s', host_platform, target_platform, msvc_version)
1963 batcharg_batchfile_clpathcomps = host_target_batcharg_batchfile_clpathcomps.get(
1964 (host_platform, target_platform), None
1967 if batcharg_batchfile_clpathcomps is None:
1968 debug('unsupported host/target platform combo: (%s,%s)', host_platform, target_platform)
1969 continue
1971 _, batfile, cl_path_comps = batcharg_batchfile_clpathcomps
1973 if check_depbat:
1974 batfile_path = os.path.join(vc_dir, *cl_path_comps, batfile)
1975 if not os.path.exists(batfile_path):
1976 debug("batch file not found: %s", batfile_path)
1977 continue
1979 cl_path = os.path.join(vc_dir, *cl_path_comps, _CL_EXE_NAME)
1980 if not os.path.exists(cl_path):
1981 debug("%s not found: %s", _CL_EXE_NAME, cl_path)
1982 continue
1984 debug('%s found: %s', _CL_EXE_NAME, cl_path)
1985 return True
1987 elif 80 > vernum_int >= 60:
1988 # 7.1 (VS2003) to 6.0 (VS6)
1990 # quick check for vc_dir/bin and vc_dir/ before walk
1991 # need to check root as the walk only considers subdirectories
1992 for cl_dir in ('bin', ''):
1993 cl_path = os.path.join(vc_dir, cl_dir, _CL_EXE_NAME)
1994 if os.path.exists(cl_path):
1995 debug('%s found %s', _CL_EXE_NAME, cl_path)
1996 return True
1997 # not in bin or root: must be in a subdirectory
1998 for cl_root, cl_dirs, _ in os.walk(vc_dir):
1999 for cl_dir in cl_dirs:
2000 cl_path = os.path.join(cl_root, cl_dir, _CL_EXE_NAME)
2001 if os.path.exists(cl_path):
2002 debug('%s found: %s', _CL_EXE_NAME, cl_path)
2003 return True
2004 return False
2006 else:
2007 # version not supported return false
2008 debug('unsupported MSVC version: %s', str(vernum))
2010 return False
2012 def get_installed_vcs(env=None):
2013 global __INSTALLED_VCS_RUN
2015 _VSWhereExecutable.vswhere_freeze_env(env)
2017 if __INSTALLED_VCS_RUN is not None:
2018 return __INSTALLED_VCS_RUN
2020 save_target_arch = env.get('TARGET_ARCH', UNDEFINED) if env else None
2021 force_target = env and save_target_arch and save_target_arch != UNDEFINED
2023 if force_target:
2024 del env['TARGET_ARCH']
2025 debug("delete env['TARGET_ARCH']")
2027 installed_versions = []
2029 for ver in _VCVER:
2030 debug('trying to find VC %s', ver)
2031 try:
2032 VC_DIR = find_vc_pdir(ver, env)
2033 if VC_DIR:
2034 debug('found VC %s', ver)
2035 if _check_files_exist_in_vc_dir(env, VC_DIR, ver):
2036 installed_versions.append(ver)
2037 else:
2038 debug('no compiler found %s', ver)
2039 else:
2040 debug('return None for ver %s', ver)
2041 except (MSVCUnsupportedTargetArch, MSVCUnsupportedHostArch, VSWhereUserError):
2042 # Allow these exceptions to propagate further as it should cause
2043 # SCons to exit with an error code
2044 raise
2045 except VisualCException as e:
2046 debug('did not find VC %s: caught exception %s', ver, str(e))
2048 if force_target:
2049 env['TARGET_ARCH'] = save_target_arch
2050 debug("restore env['TARGET_ARCH']=%s", save_target_arch)
2052 __INSTALLED_VCS_RUN = installed_versions
2053 debug("__INSTALLED_VCS_RUN=%s", __INSTALLED_VCS_RUN)
2054 return __INSTALLED_VCS_RUN
2056 def reset_installed_vcs() -> None:
2057 """Make it try again to find VC. This is just for the tests."""
2058 _reset_installed_vcs()
2059 _reset_vc_pdir()
2060 _VSWhereExecutable.reset()
2061 _VSWhere.reset()
2062 MSVC._reset()
2064 def msvc_default_version(env=None):
2065 """Get default msvc version."""
2066 vcs = get_installed_vcs(env)
2067 msvc_version = vcs[0] if vcs else None
2068 debug('msvc_version=%s', repr(msvc_version))
2069 return msvc_version
2071 def get_installed_vcs_components(env=None):
2072 """Test suite convenience function: return list of installed msvc version component tuples"""
2073 vcs = get_installed_vcs(env)
2074 msvc_version_component_defs = [MSVC.Util.msvc_version_components(vcver) for vcver in vcs]
2075 return msvc_version_component_defs
2077 def _check_cl_exists_in_script_env(data):
2078 """Find cl.exe in the script environment path."""
2079 cl_path = None
2080 if data and 'PATH' in data:
2081 for p in data['PATH']:
2082 cl_exe = os.path.join(p, _CL_EXE_NAME)
2083 if os.path.exists(cl_exe):
2084 cl_path = cl_exe
2085 break
2086 have_cl = True if cl_path else False
2087 debug('have_cl: %s, cl_path: %s', have_cl, cl_path)
2088 return have_cl, cl_path
2090 # Running these batch files isn't cheap: most of the time spent in
2091 # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'"
2092 # in multiple environments, for example:
2093 # env1 = Environment(tools='msvs')
2094 # env2 = Environment(tools='msvs')
2095 # we can greatly improve the speed of the second and subsequent Environment
2096 # (or Clone) calls by memoizing the environment variables set by vcvars*.bat.
2098 # Updated: by 2018, vcvarsall.bat had gotten so expensive (vs2017 era)
2099 # it was breaking CI builds because the test suite starts scons so many
2100 # times and the existing memo logic only helped with repeated calls
2101 # within the same scons run. Windows builds on the CI system were split
2102 # into chunks to get around single-build time limits.
2103 # With VS2019 it got even slower and an optional persistent cache file
2104 # was introduced. The cache now also stores only the parsed vars,
2105 # not the entire output of running the batch file - saves a bit
2106 # of time not parsing every time.
2108 script_env_cache = None
2110 def script_env(env, script, args=None):
2111 global script_env_cache
2113 if script_env_cache is None:
2114 script_env_cache = common.read_script_env_cache()
2115 cache_key = (script, args if args else None)
2116 cache_data = script_env_cache.get(cache_key, None)
2118 # Brief sanity check: if we got a value for the key,
2119 # see if it has a VCToolsInstallDir entry that is not empty.
2120 # If so, and that path does not exist, invalidate the entry.
2121 # If empty, this is an old compiler, just leave it alone.
2122 if cache_data is not None:
2123 try:
2124 toolsdir = cache_data["VCToolsInstallDir"]
2125 except KeyError:
2126 # we write this value, so should not happen
2127 pass
2128 else:
2129 if toolsdir:
2130 toolpath = Path(toolsdir[0])
2131 if not toolpath.exists():
2132 cache_data = None
2134 if cache_data is None:
2135 skip_sendtelemetry = _skip_sendtelemetry(env)
2136 stdout = common.get_output(script, args, skip_sendtelemetry=skip_sendtelemetry)
2137 cache_data = common.parse_output(stdout)
2139 # debug(stdout)
2140 olines = stdout.splitlines()
2142 # process stdout: batch file errors (not necessarily first line)
2143 script_errlog = []
2144 for line in olines:
2145 if re_script_output_error.match(line):
2146 if not script_errlog:
2147 script_errlog.append('vc script errors detected:')
2148 script_errlog.append(line)
2150 if script_errlog:
2151 script_errmsg = '\n'.join(script_errlog)
2153 have_cl, _ = _check_cl_exists_in_script_env(cache_data)
2155 debug(
2156 'script=%s args=%s have_cl=%s, errors=%s',
2157 repr(script), repr(args), repr(have_cl), script_errmsg
2159 MSVC.Policy.msvc_scripterror_handler(env, script_errmsg)
2161 if not have_cl:
2162 # detected errors, cl.exe not on path
2163 raise BatchFileExecutionError(script_errmsg)
2165 # once we updated cache, give a chance to write out if user wanted
2166 script_env_cache[cache_key] = cache_data
2167 common.write_script_env_cache(script_env_cache)
2169 return cache_data
2171 def get_default_version(env):
2172 msvc_version = env.get('MSVC_VERSION')
2173 msvs_version = env.get('MSVS_VERSION')
2174 debug('msvc_version:%s msvs_version:%s', msvc_version, msvs_version)
2176 if msvs_version and not msvc_version:
2177 SCons.Warnings.warn(
2178 SCons.Warnings.DeprecatedWarning,
2179 "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ")
2180 return msvs_version
2181 elif msvc_version and msvs_version:
2182 if not msvc_version == msvs_version:
2183 SCons.Warnings.warn(
2184 SCons.Warnings.VisualVersionMismatch,
2185 "Requested msvc version (%s) and msvs version (%s) do "
2186 "not match: please use MSVC_VERSION only to request a "
2187 "visual studio version, MSVS_VERSION is deprecated"
2188 % (msvc_version, msvs_version))
2189 return msvs_version
2191 if not msvc_version:
2192 msvc_version = msvc_default_version(env)
2193 if not msvc_version:
2194 #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
2195 debug('No installed VCs')
2196 return None
2197 debug('using default installed MSVC version %s', repr(msvc_version))
2198 else:
2199 debug('using specified MSVC version %s', repr(msvc_version))
2201 return msvc_version
2203 def msvc_setup_env_once(env, tool=None) -> None:
2204 try:
2205 has_run = env["MSVC_SETUP_RUN"]
2206 except KeyError:
2207 has_run = False
2209 if not has_run:
2210 MSVC.SetupEnvDefault.register_setup(env, msvc_exists)
2211 msvc_setup_env(env)
2212 env["MSVC_SETUP_RUN"] = True
2214 req_tools = MSVC.SetupEnvDefault.register_iserror(env, tool, msvc_exists)
2215 if req_tools:
2216 msg = "No versions of the MSVC compiler were found.\n" \
2217 " Visual Studio C/C++ compilers may not be set correctly.\n" \
2218 " Requested tool(s) are: {}".format(req_tools)
2219 MSVC.Policy.msvc_notfound_handler(env, msg)
2221 def msvc_find_valid_batch_script(env, version):
2222 """Find and execute appropriate batch script to set up build env.
2224 The MSVC build environment depends heavily on having the shell
2225 environment set. SCons does not inherit that, and does not count
2226 on that being set up correctly anyway, so it tries to find the right
2227 MSVC batch script, or the right arguments to the generic batch script
2228 vcvarsall.bat, and run that, so we have a valid environment to build in.
2229 There are dragons here: the batch scripts don't fail (see comments
2230 elsewhere), they just leave you with a bad setup, so try hard to
2231 get it right.
2234 # Find the product directory
2235 pdir = None
2236 try:
2237 pdir = find_vc_pdir(version, env)
2238 except UnsupportedVersion:
2239 # Unsupported msvc version (raise MSVCArgumentError?)
2240 pass
2241 debug('product directory: version=%s, pdir=%s', version, pdir)
2243 # Find the host, target, and all candidate (host, target) platform combinations:
2244 platforms = get_host_target(env, version)
2245 debug("host_platform %s, target_platform %s host_target_list %s", *platforms)
2246 host_platform, target_platform, host_target_list = platforms
2248 d = None
2249 version_installed = False
2251 if pdir:
2253 # Query all candidate sdk (host, target, sdk_pdir) after vc_script pass if necessary
2254 sdk_queries = []
2256 for host_arch, target_arch, in host_target_list:
2257 # Set to current arch.
2258 env['TARGET_ARCH'] = target_arch
2259 arg = ''
2261 # Try to locate a batch file for this host/target platform combo
2262 try:
2263 (vc_script, arg, vc_dir, sdk_pdir) = find_batch_file(version, host_arch, target_arch, pdir)
2264 debug('vc_script:%s vc_script_arg:%s', vc_script, arg)
2265 version_installed = True
2266 except VisualCException as e:
2267 msg = str(e)
2268 debug('Caught exception while looking for batch file (%s)', msg)
2269 version_installed = False
2270 continue
2272 # Save (host, target, sdk_pdir) platform combo for sdk queries
2273 if sdk_pdir:
2274 sdk_query = (host_arch, target_arch, sdk_pdir)
2275 if sdk_query not in sdk_queries:
2276 debug('save sdk_query host=%s, target=%s, sdk_pdir=%s', host_arch, target_arch, sdk_pdir)
2277 sdk_queries.append(sdk_query)
2279 if not vc_script:
2280 continue
2282 if not target_platform and MSVC.ScriptArguments.msvc_script_arguments_has_uwp(env):
2283 # no target arch specified and is a store/uwp build
2284 if MSVC.Kind.msvc_version_skip_uwp_target(env, version):
2285 # store/uwp may not be supported for all express targets (prevent constraint error)
2286 debug('skip uwp target arch: version=%s, target=%s', repr(version), repr(target_arch))
2287 continue
2289 # Try to use the located batch file for this host/target platform combo
2290 arg = MSVC.ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg)
2291 debug('trying vc_script:%s, vc_script_args:%s', repr(vc_script), arg)
2292 try:
2293 d = script_env(env, vc_script, args=arg)
2294 except BatchFileExecutionError as e:
2295 debug('failed vc_script:%s, vc_script_args:%s, error:%s', repr(vc_script), arg, e)
2296 vc_script = None
2297 continue
2299 have_cl, _ = _check_cl_exists_in_script_env(d)
2300 if not have_cl:
2301 debug('skip cl.exe not found vc_script:%s, vc_script_args:%s', repr(vc_script), arg)
2302 continue
2304 debug("Found a working script/target: %s/%s", repr(vc_script), arg)
2305 break # We've found a working target_platform, so stop looking
2307 if not d:
2308 for host_arch, target_arch, sdk_pdir in sdk_queries:
2309 # Set to current arch.
2310 env['TARGET_ARCH'] = target_arch
2312 sdk_script = find_batch_file_sdk(host_arch, target_arch, sdk_pdir)
2313 if not sdk_script:
2314 continue
2316 # Try to use the sdk batch file for this (host, target, sdk_pdir) combo
2317 debug('trying sdk_script:%s', repr(sdk_script))
2318 try:
2319 d = script_env(env, sdk_script)
2320 version_installed = True
2321 except BatchFileExecutionError as e:
2322 debug('failed sdk_script:%s, error=%s', repr(sdk_script), e)
2323 continue
2325 have_cl, _ = _check_cl_exists_in_script_env(d)
2326 if not have_cl:
2327 debug('skip cl.exe not found sdk_script:%s', repr(sdk_script))
2328 continue
2330 debug("Found a working script/target: %s", repr(sdk_script))
2331 break # We've found a working script, so stop looking
2333 # If we cannot find a viable installed compiler, reset the TARGET_ARCH
2334 # To it's initial value
2335 if not d:
2336 env['TARGET_ARCH'] = target_platform
2338 if version_installed:
2339 msg = "MSVC version '{}' working host/target script was not found.\n" \
2340 " Host = '{}', Target = '{}'\n" \
2341 " Visual Studio C/C++ compilers may not be set correctly".format(
2342 version, host_platform, target_platform
2344 else:
2345 installed_vcs = get_installed_vcs(env)
2346 if installed_vcs:
2347 msg = "MSVC version '{}' was not found.\n" \
2348 " Visual Studio C/C++ compilers may not be set correctly.\n" \
2349 " Installed versions are: {}".format(version, installed_vcs)
2350 else:
2351 msg = "MSVC version '{}' was not found.\n" \
2352 " No versions of the MSVC compiler were found.\n" \
2353 " Visual Studio C/C++ compilers may not be set correctly".format(version)
2355 MSVC.Policy.msvc_notfound_handler(env, msg)
2357 return d
2359 def get_use_script_use_settings(env):
2361 # use_script use_settings return values action
2362 # value ignored (value, None) use script or bypass detection
2363 # undefined value not None (False, value) use dictionary
2364 # undefined undefined/None (True, None) msvc detection
2366 # None (documentation) or evaluates False (code): bypass detection
2367 # need to distinguish between undefined and None
2368 use_script = env.get('MSVC_USE_SCRIPT', UNDEFINED)
2370 if use_script != UNDEFINED:
2371 # use_script defined, use_settings ignored (not type checked)
2372 return use_script, None
2374 # undefined or None: use_settings ignored
2375 use_settings = env.get('MSVC_USE_SETTINGS', None)
2377 if use_settings is not None:
2378 # use script undefined, use_settings defined and not None (type checked)
2379 return False, use_settings
2381 # use script undefined, use_settings undefined or None
2382 return True, None
2384 def msvc_setup_env(env):
2385 debug('called')
2387 _VSWhereExecutable.vswhere_freeze_env(env)
2389 version = get_default_version(env)
2390 if version is None:
2391 if not msvc_setup_env_user(env):
2392 MSVC.SetupEnvDefault.set_nodefault()
2393 return None
2395 # XXX: we set-up both MSVS version for backward
2396 # compatibility with the msvs tool
2397 env['MSVC_VERSION'] = version
2398 env['MSVS_VERSION'] = version
2399 env['MSVS'] = {}
2401 use_script, use_settings = get_use_script_use_settings(env)
2402 if SCons.Util.is_String(use_script):
2403 use_script = use_script.strip()
2404 if not os.path.exists(use_script):
2405 raise MSVCScriptNotFound(f'Script specified by MSVC_USE_SCRIPT not found: "{use_script}"')
2406 args = env.subst('$MSVC_USE_SCRIPT_ARGS')
2407 debug('use_script 1 %s %s', repr(use_script), repr(args))
2408 d = script_env(env, use_script, args)
2409 elif use_script:
2410 d = msvc_find_valid_batch_script(env,version)
2411 debug('use_script 2 %s', d)
2412 if not d:
2413 return d
2414 elif use_settings is not None:
2415 if not SCons.Util.is_Dict(use_settings):
2416 error_msg = f'MSVC_USE_SETTINGS type error: expected a dictionary, found {type(use_settings).__name__}'
2417 raise MSVCUseSettingsError(error_msg)
2418 d = use_settings
2419 debug('use_settings %s', d)
2420 else:
2421 debug('MSVC_USE_SCRIPT set to False')
2422 warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \
2423 "set correctly."
2424 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
2425 return None
2427 found_cl_path = None
2428 found_cl_envpath = None
2430 seen_path = False
2431 for k, v in d.items():
2432 if not seen_path and k == 'PATH':
2433 seen_path = True
2434 found_cl_path = SCons.Util.WhereIs('cl', v)
2435 found_cl_envpath = SCons.Util.WhereIs('cl', env['ENV'].get(k, []))
2436 env.PrependENVPath(k, v, delete_existing=True)
2437 debug("env['ENV']['%s'] = %s", k, env['ENV'][k])
2439 debug("cl paths: d['PATH']=%s, ENV['PATH']=%s", repr(found_cl_path), repr(found_cl_envpath))
2441 # final check to issue a warning if the requested compiler is not present
2442 if not found_cl_path:
2443 warn_msg = "Could not find requested MSVC compiler 'cl'."
2444 if CONFIG_CACHE:
2445 warn_msg += f" SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {CONFIG_CACHE} if out of date."
2446 else:
2447 warn_msg += " It may need to be installed separately with Visual Studio."
2448 if found_cl_envpath:
2449 warn_msg += " A 'cl' was found on the scons ENV path which may be erroneous."
2450 debug(warn_msg)
2451 SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg)
2453 def msvc_exists(env=None, version=None):
2454 vcs = get_installed_vcs(env)
2455 if version is None:
2456 rval = len(vcs) > 0
2457 else:
2458 rval = version in vcs
2459 debug('version=%s, return=%s', repr(version), rval)
2460 return rval
2462 def msvc_setup_env_user(env=None):
2463 rval = False
2464 if env:
2466 # Intent is to use msvc tools:
2467 # MSVC_VERSION: defined and evaluates True
2468 # MSVS_VERSION: defined and evaluates True
2469 # MSVC_USE_SCRIPT: defined and (is string or evaluates False)
2470 # MSVC_USE_SETTINGS: defined and is not None
2472 # defined and is True
2473 for key in ['MSVC_VERSION', 'MSVS_VERSION']:
2474 if key in env and env[key]:
2475 rval = True
2476 debug('key=%s, return=%s', repr(key), rval)
2477 return rval
2479 # defined and (is string or is False)
2480 for key in ['MSVC_USE_SCRIPT']:
2481 if key in env and (SCons.Util.is_String(env[key]) or not env[key]):
2482 rval = True
2483 debug('key=%s, return=%s', repr(key), rval)
2484 return rval
2486 # defined and is not None
2487 for key in ['MSVC_USE_SETTINGS']:
2488 if key in env and env[key] is not None:
2489 rval = True
2490 debug('key=%s, return=%s', repr(key), rval)
2491 return rval
2493 debug('return=%s', rval)
2494 return rval
2496 def msvc_setup_env_tool(env=None, version=None, tool=None):
2497 MSVC.SetupEnvDefault.register_tool(env, tool, msvc_exists)
2498 rval = False
2499 if not rval and msvc_exists(env, version):
2500 rval = True
2501 if not rval and msvc_setup_env_user(env):
2502 rval = True
2503 return rval
2505 def msvc_sdk_versions(version=None, msvc_uwp_app: bool=False):
2506 debug('version=%s, msvc_uwp_app=%s', repr(version), repr(msvc_uwp_app))
2508 rval = []
2510 if not version:
2511 version = msvc_default_version()
2513 if not version:
2514 debug('no msvc versions detected')
2515 return rval
2517 version_def = MSVC.Util.msvc_extended_version_components(version)
2518 if not version_def:
2519 msg = f'Unsupported version {version!r}'
2520 raise MSVCArgumentError(msg)
2522 rval = MSVC.WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app)
2523 return rval
2525 def msvc_toolset_versions(msvc_version=None, full: bool=True, sxs: bool=False, vswhere_exe=None):
2526 debug(
2527 'msvc_version=%s, full=%s, sxs=%s, vswhere_exe=%s',
2528 repr(msvc_version), repr(full), repr(sxs), repr(vswhere_exe)
2531 _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe)
2533 rval = []
2535 if not msvc_version:
2536 msvc_version = msvc_default_version()
2538 if not msvc_version:
2539 debug('no msvc versions detected')
2540 return rval
2542 if msvc_version not in _VCVER:
2543 msg = f'Unsupported msvc version {msvc_version!r}'
2544 raise MSVCArgumentError(msg)
2546 vc_dir = _find_vc_pdir(msvc_version, vswhere_exe)
2547 if not vc_dir:
2548 debug('VC folder not found for version %s', repr(msvc_version))
2549 return rval
2551 rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs)
2552 return rval
2554 def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None):
2555 debug('msvc_version=%s, vswhere_exe=%s', repr(msvc_version), repr(vswhere_exe))
2557 _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe)
2559 rval = []
2561 if not msvc_version:
2562 msvc_version = msvc_default_version()
2564 if not msvc_version:
2565 debug('no msvc versions detected')
2566 return rval
2568 if msvc_version not in _VCVER:
2569 msg = f'Unsupported msvc version {msvc_version!r}'
2570 raise MSVCArgumentError(msg)
2572 vc_dir = _find_vc_pdir(msvc_version, vswhere_exe)
2573 if not vc_dir:
2574 debug('VC folder not found for version %s', repr(msvc_version))
2575 return rval
2577 rval = MSVC.ScriptArguments._msvc_toolset_versions_spectre_internal(msvc_version, vc_dir)
2578 return rval
2580 _InstalledVersionToolset = namedtuple('_InstalledVersionToolset', [
2581 'msvc_version_def',
2582 'toolset_version_def',
2585 _InstalledVCSToolsetsComponents = namedtuple('_InstalledVCSToolsetComponents', [
2586 'sxs_map',
2587 'toolset_vcs',
2588 'msvc_toolset_component_defs',
2591 def get_installed_vcs_toolsets_components(vswhere_exe=None):
2592 debug('vswhere_exe=%s', repr(vswhere_exe))
2594 _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe)
2596 sxs_map = {}
2597 toolset_vcs = set()
2598 msvc_toolset_component_defs = []
2600 vcs = get_installed_vcs()
2601 for msvc_version in vcs:
2603 vc_dir = _find_vc_pdir(msvc_version, vswhere_exe)
2604 if not vc_dir:
2605 continue
2607 msvc_version_def = MSVC.Util.msvc_version_components(msvc_version)
2608 toolset_vcs.add(msvc_version_def.msvc_version)
2610 if msvc_version_def.msvc_vernum > 14.0:
2612 toolsets_sxs, toolsets_full = MSVC.ScriptArguments._msvc_version_toolsets_internal(
2613 msvc_version, vc_dir
2616 debug('msvc_version=%s, toolset_sxs=%s', repr(msvc_version), repr(toolsets_sxs))
2617 sxs_map.update(toolsets_sxs)
2619 else:
2621 toolsets_full = [msvc_version_def.msvc_verstr]
2623 for toolset_version in toolsets_full:
2624 debug('msvc_version=%s, toolset_version=%s', repr(msvc_version), repr(toolset_version))
2626 toolset_version_def = MSVC.Util.msvc_extended_version_components(toolset_version)
2627 if not toolset_version_def:
2628 continue
2629 toolset_vcs.add(toolset_version_def.msvc_version)
2631 rval = _InstalledVersionToolset(
2632 msvc_version_def=msvc_version_def,
2633 toolset_version_def=toolset_version_def,
2635 msvc_toolset_component_defs.append(rval)
2637 installed_vcs_toolsets_components = _InstalledVCSToolsetsComponents(
2638 sxs_map=sxs_map,
2639 toolset_vcs=toolset_vcs,
2640 msvc_toolset_component_defs=msvc_toolset_component_defs,
2643 return installed_vcs_toolsets_components
2645 def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_exe=None):
2647 Return an msvc version and a toolset version given a version
2648 specification.
2650 This is an EXPERIMENTAL proxy for using a toolset version to perform
2651 msvc instance selection. This function will be removed when
2652 toolset version is taken into account during msvc instance selection.
2654 This function searches for an installed Visual Studio instance that
2655 contains the requested version. A component suffix (e.g., Exp) is not
2656 supported for toolset version specifications (e.g., 14.39).
2658 An MSVCArgumentError is raised when argument validation fails. An
2659 MSVCToolsetVersionNotFound exception is raised when the requested
2660 version is not found.
2662 For Visual Studio 2015 and earlier, the msvc version is returned and
2663 the toolset version is None. For Visual Studio 2017 and later, the
2664 selected toolset version is returned.
2666 Args:
2668 version: str
2669 The version specification may be an msvc version or a toolset
2670 version.
2672 prefer_newest: bool
2673 True: prefer newer Visual Studio instances.
2674 False: prefer the "native" Visual Studio instance first. If
2675 the native Visual Studio instance is not detected, prefer
2676 newer Visual Studio instances.
2678 vswhere_exe: str
2679 A vswhere executable location or None.
2681 Returns:
2682 tuple: A tuple containing the msvc version and the msvc toolset version.
2683 The msvc toolset version may be None.
2685 Raises:
2686 MSVCToolsetVersionNotFound: when the specified version is not found.
2687 MSVCArgumentError: when argument validation fails.
2689 debug(
2690 'version=%s, prefer_newest=%s, vswhere_exe=%s',
2691 repr(version), repr(prefer_newest), repr(vswhere_exe)
2694 _VSWhereExecutable.vswhere_freeze_executable(vswhere_exe)
2696 msvc_version = None
2697 msvc_toolset_version = None
2699 with MSVC.Policy.msvc_notfound_policy_contextmanager('suppress'):
2701 vcs = get_installed_vcs()
2703 if not version:
2704 version = msvc_default_version()
2706 if not version:
2707 msg = f'No versions of the MSVC compiler were found'
2708 debug(f'MSVCToolsetVersionNotFound: {msg}')
2709 raise MSVCToolsetVersionNotFound(msg)
2711 version_def = MSVC.Util.msvc_extended_version_components(version)
2713 if not version_def:
2714 msg = f'Unsupported MSVC version {version!r}'
2715 debug(f'MSVCArgumentError: {msg}')
2716 raise MSVCArgumentError(msg)
2718 msvc_version = version_def.msvc_version
2720 if version_def.msvc_suffix:
2722 if version_def.msvc_verstr != version_def.msvc_toolset_version:
2723 # toolset version with component suffix
2724 msg = f'Unsupported MSVC toolset version {version!r}'
2725 debug(f'MSVCArgumentError: {msg}')
2726 raise MSVCArgumentError(msg)
2728 if msvc_version not in vcs:
2729 msg = f'MSVC version {msvc_version!r} not found'
2730 debug(f'MSVCToolsetVersionNotFound: {msg}')
2731 raise MSVCToolsetVersionNotFound(msg)
2733 if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP:
2734 debug(
2735 'suffix: msvc_version=%s, msvc_toolset_version=%s',
2736 repr(msvc_version), repr(msvc_toolset_version)
2738 return msvc_version, msvc_toolset_version
2740 if version_def.msvc_vernum > 14.0:
2741 # VS2017 and later
2742 force_toolset_msvc_version = False
2743 else:
2744 # VS2015 and earlier
2745 force_toolset_msvc_version = True
2746 extended_version = version_def.msvc_verstr + '0.00000'
2747 if not extended_version.startswith(version_def.msvc_toolset_version):
2748 # toolset not equivalent to msvc version
2749 msg = 'Unsupported MSVC toolset version {} (expected {})'.format(
2750 repr(version), repr(extended_version)
2752 debug(f'MSVCArgumentError: {msg}')
2753 raise MSVCArgumentError(msg)
2755 if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP:
2757 # VS2013 and earlier
2759 if msvc_version not in vcs:
2760 msg = f'MSVC version {version!r} not found'
2761 debug(f'MSVCToolsetVersionNotFound: {msg}')
2762 raise MSVCToolsetVersionNotFound(msg)
2764 debug(
2765 'earlier: msvc_version=%s, msvc_toolset_version=%s',
2766 repr(msvc_version), repr(msvc_toolset_version)
2768 return msvc_version, msvc_toolset_version
2770 if force_toolset_msvc_version:
2771 query_msvc_toolset_version = version_def.msvc_verstr
2772 else:
2773 query_msvc_toolset_version = version_def.msvc_toolset_version
2775 if prefer_newest:
2776 query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
2777 else:
2778 query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \
2779 MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
2781 seen_msvc_version = set()
2782 for query_msvc_version in query_version_list:
2784 if query_msvc_version in seen_msvc_version:
2785 continue
2786 seen_msvc_version.add(query_msvc_version)
2788 vc_dir = _find_vc_pdir(query_msvc_version, vswhere_exe)
2789 if not vc_dir:
2790 continue
2792 if query_msvc_version.startswith('14.0'):
2793 # VS2015 does not support toolset version argument
2794 msvc_toolset_version = None
2795 debug(
2796 'found: msvc_version=%s, msvc_toolset_version=%s',
2797 repr(query_msvc_version), repr(msvc_toolset_version)
2799 return query_msvc_version, msvc_toolset_version
2801 try:
2802 toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir)
2803 if toolset_vcvars:
2804 msvc_toolset_version = toolset_vcvars
2805 debug(
2806 'found: msvc_version=%s, msvc_toolset_version=%s',
2807 repr(query_msvc_version), repr(msvc_toolset_version)
2809 return query_msvc_version, msvc_toolset_version
2811 except MSVCToolsetVersionNotFound:
2812 pass
2814 msvc_toolset_version = query_msvc_toolset_version
2816 debug(
2817 'not found: msvc_version=%s, msvc_toolset_version=%s',
2818 repr(msvc_version), repr(msvc_toolset_version)
2821 msg = f'MSVC toolset version {version!r} not found'
2822 debug(f'MSVCToolsetVersionNotFound: {msg}')
2823 raise MSVCToolsetVersionNotFound(msg)
2826 # internal consistency check (should be last)
2827 MSVC._verify()