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.
25 MS Compilers: Visual C/C++ detection and configuration.
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
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)
44 from pathlib
import Path
45 from string
import digits
as string_digits
46 from subprocess
import PIPE
48 from collections
import (
53 from functools
import cmp_to_key
58 from SCons
.Tool
import find_program_path
65 from .sdk
import get_installed_sdks
69 from .MSVC
.Exceptions
import (
74 MSVCToolsetVersionNotFound
,
79 class MSVCUnsupportedHostArch(VisualCException
):
82 class MSVCUnsupportedTargetArch(VisualCException
):
85 class MSVCScriptNotFound(MSVCUserError
):
88 class MSVCUseScriptError(MSVCUserError
):
91 class MSVCUseSettingsError(MSVCUserError
):
94 class VSWhereUserError(MSVCUserError
):
99 class UnsupportedVersion(VisualCException
):
102 class BatchFileExecutionError(VisualCException
):
105 # undefined object for dict.get() in case key exists and value is None
108 # powershell error sending telemetry for arm32 process on arm64 host (VS2019+):
109 # True: force VSCMD_SKIP_SENDTELEMETRY=1 (if necessary)
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
= {
126 "ia64" : "ia64", # deprecated
127 "itanium" : "ia64", # deprecated
135 # The msvc batch files report errors via stdout. The following
136 # regular expression attempts to match known msvc error messages
138 re_script_output_error
= re
.compile(
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+
144 r
'\!ERROR\!', # 2015-2015
145 r
'\[ERROR\:', # 2017+
146 r
'\[ERROR\]', # 2017+
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
160 # HostTargetConfig description:
163 # Name used for identification.
166 # Defined list of compatible architectures for each host architecture.
169 # Defined list of target architectures for each host architecture.
172 # Defined list of default target architectures for each host architecture.
175 # Derived list of all host/target combination tuples.
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.
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", [
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
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
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
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
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(
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
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
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(
331 host_all_hosts
= OrderedDict([
332 ('amd64', ['amd64', 'x86']),
334 ('arm64', ['arm64', 'amd64', 'x86']),
339 'amd64': ['amd64', 'x86', 'arm64', 'arm'],
340 'x86': ['x86', 'amd64', 'arm', 'arm64'],
341 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
346 'amd64': ['amd64', 'x86'],
348 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
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(
381 host_all_hosts
= OrderedDict([
382 ('amd64', ['amd64', 'x86']),
384 ('arm64', ['amd64', 'x86']),
389 'amd64': ['amd64', 'x86', 'arm64', 'arm'],
390 'x86': ['x86', 'amd64', 'arm', 'arm64'],
391 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
396 'amd64': ['amd64', 'x86'],
398 'arm64': ['arm64', 'amd64', 'arm', 'x86'],
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(
439 host_all_hosts
= OrderedDict([
440 ('amd64', ['amd64', 'x86']),
442 ('arm64', ['amd64', 'x86']),
448 'amd64': ['amd64', 'x86', 'arm'],
449 'x86': ['x86', 'amd64', 'arm', 'ia64'],
450 'arm64': ['amd64', 'x86', 'arm'],
456 'amd64': ['amd64', 'x86'],
458 'arm64': ['amd64', 'arm', 'x86'],
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(
489 host_all_hosts
= OrderedDict([
490 ('amd64', ['amd64', 'x86']),
492 ('arm64', ['amd64', 'x86']),
497 'amd64': ['amd64', 'x86'],
498 'x86': ['x86', 'amd64', 'ia64'],
499 'arm64': ['amd64', 'x86'],
504 'amd64': ['amd64', 'x86'],
506 'arm64': ['amd64', 'x86'],
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
519 _LE2003_HOST_TARGET_CFG
= _host_target_config_factory(
523 host_all_hosts
= OrderedDict([
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'.
556 string representing the version number, could contain non
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"
574 host_platform
= "x86"
577 host
=_ARCH_TO_CANONICAL
[host_platform
]
579 msg
= "Unrecognized host architecture %s"
580 raise MSVCUnsupportedHostArch(msg
% repr(host_platform
)) from None
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:
593 arch
= common
.read_reg(
594 r
'SYSTEM\CurrentControlSet\Control\Session Manager\Environment\PROCESSOR_ARCHITECTURE'
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
))
642 host_platform
= get_host_platform(host_arch
)
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
))
652 target_platform
= _ARCH_TO_CANONICAL
[target_arch
.lower()]
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
)
660 target_host_map
= host_target_cfg
.target_host_map
663 host_target_list
= target_host_map
[target_platform
][host_platform
]
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
)
674 target_platform
= None
677 host_targets_map
= host_target_cfg
.host_all_targets_map
679 host_targets_map
= host_target_cfg
.host_def_targets_map
682 host_target_list
= host_targets_map
[host_platform
]
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
717 _check_skip_sendtelemetry
= False
719 if not _check_skip_sendtelemetry
:
722 msvc_version
= env
.get('MSVC_VERSION') if env
else None
724 msvc_version
= msvc_default_version(env
)
729 vernum
= float(get_msvc_version_numeric(msvc_version
))
730 if vernum
< 14.2: # VS2019
733 # arm32 process, arm64 host, VS2019+
736 # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the
737 # MSVC_VERSION documentation in Tool/msvc.xml.
752 # VS2017 and later: use a single vswhere json query to find all installations
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
)
769 for vc_ver
in vc_ver_list
:
770 _VSWHERE_SUPPORTED_VCVER
.add(vc_ver
)
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
= {
799 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')
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')
807 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VisualStudio\12.0\Setup\VC\ProductDir'),
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'),
814 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VisualStudio\11.0\Setup\VC\ProductDir'),
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'),
821 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'),
824 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VCExpress\10.0\Setup\VC\ProductDir'),
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
836 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'),
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'),
843 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'),
846 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'),
849 (SCons
.Util
.HKEY_LOCAL_MACHINE
, r
'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'),
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
863 MSVC
.Kind
.IDE_PROGRAM_MSDEV_COM
, # MSDEV.COM
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(".")
918 raise ValueError("Unrecognized version %s (%s)" % (msvc_version
,msvc_version_numeric
))
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', [
947 class VSWhereBinary(_VSWhereBinary
, MSVC
.Util
.AutoInitialize
):
949 _UNDEFINED_VSWHERE_BINARY
= None
951 _cache_vswhere_paths
= {}
954 def _initialize(cls
):
955 vswhere_binary
= cls(
959 cls
._UNDEFINED
_VSWHERE
_BINARY
= vswhere_binary
960 for symbol
in (None, ''):
961 cls
._cache
_vswhere
_paths
[symbol
] = vswhere_binary
964 def factory(cls
, vswhere_exe
):
967 return cls
._UNDEFINED
_VSWHERE
_BINARY
969 vswhere_binary
= cls
._cache
_vswhere
_paths
.get(vswhere_exe
)
971 return vswhere_binary
973 vswhere_norm
= MSVC
.Util
.normalize_path(vswhere_exe
)
975 vswhere_binary
= cls
._cache
_vswhere
_paths
.get(vswhere_norm
)
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
):
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
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)
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
1023 def _initialize(cls
):
1024 cls
.debug_extra
= common
.debug_extra(cls
)
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
):
1033 vswhere_binary
= VSWhereBinary
.factory(vswhere_exe
)
1034 vswhere_binaries
.append(vswhere_binary
)
1036 "insert exegroup=%s, vswhere_binary=%s",
1037 label
, vswhere_binary
, extra
=cls
.debug_extra
1039 return vswhere_binaries
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
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
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
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
:
1088 vswhere_binary
= binary
1090 return vswhere_binary
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
:
1102 vswhere_norm_set
.add(vswhere_binary
.vswhere_norm
)
1103 vswhere_exe_list
.append(vswhere_binary
.vswhere_exe
)
1104 return vswhere_exe_list
1107 def is_frozen(cls
) -> bool:
1108 rval
= bool(cls
.vswhere_frozen_flag
)
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
1120 def freeze_vswhere_executable(cls
):
1121 vswhere_binary
= cls
.freeze_vswhere_binary()
1122 vswhere_exe
= vswhere_binary
.vswhere_exe
1126 def get_vswhere_executable(cls
):
1127 if cls
.vswhere_frozen_flag
:
1128 vswhere_binary
= cls
.vswhere_frozen_binary
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
)
1136 def _vswhere_priority_group(cls
, priority
):
1138 group
= cls
.priority_default
1140 group
= cls
.priority_map
.get(priority
)
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
)
1149 def register_vswhere_executable(cls
, vswhere_exe
, priority
=None):
1151 vswhere_binary
= cls
.UNDEFINED_VSWHERE_BINARY
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
)
1190 "insert exegroup=user[%s], vswhere_binary=%s",
1191 cls
.priority_indmap
[group
], vswhere_binary
, extra
=cls
.debug_extra
1194 return vswhere_binary
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
1203 def vswhere_freeze_env(cls
, env
):
1206 # no environment, VSWHERE undefined
1208 write_vswhere
= False
1209 elif not env
.get('VSWHERE'):
1210 # environment, VSWHERE undefined/none/empty
1212 write_vswhere
= True
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
1223 "env['VSWHERE']=%s",
1224 repr(frozen_binary
.vswhere_exe
), extra
=cls
.debug_extra
1227 return frozen_binary
, vswhere_binary
1231 def vswhere_register_executable(vswhere_exe
, priority
=None, freeze
=False):
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
)
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())
1243 def vswhere_get_executable():
1245 vswhere_exe
= _VSWhereExecutable
.get_vswhere_executable()
1248 def vswhere_freeze_executable():
1250 vswhere_exe
= _VSWhereExecutable
.freeze_vswhere_executable()
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()
1263 _MSVCInstance
= namedtuple('_MSVCInstance', [
1266 'vc_version_numeric',
1270 'vc_component_rank',
1271 'vc_component_suffix',
1274 class MSVCInstance(_MSVCInstance
):
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
1289 class _VSWhere(MSVC
.Util
.AutoInitialize
):
1296 cls
.seen_vswhere
= set()
1297 cls
.seen_root
= set()
1299 cls
.msvc_instances
= []
1303 def _initialize(cls
):
1304 cls
.debug_extra
= common
.debug_extra(cls
)
1308 def _filter_vswhere_binary(cls
, vswhere_binary
):
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
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')
1324 def _vswhere_query_json_output(cls
, vswhere_exe
, vswhere_args
):
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
)
1337 cp
= subprocess
.run(vswhere_cmd
, stdout
=PIPE
, stderr
=PIPE
, check
=True)
1338 except OSError as e
:
1340 debug("%s: %s", type(e
).__name
__, errmsg
, extra
=cls
.debug_extra
)
1342 except Exception as e
:
1344 debug("%s: %s", type(e
).__name
__, errmsg
, extra
=cls
.debug_extra
)
1348 debug("no vswhere information returned", extra
=cls
.debug_extra
)
1351 vswhere_output
= cp
.stdout
.decode('utf8', errors
='replace')
1352 if not vswhere_output
:
1353 debug("no vswhere information output", extra
=cls
.debug_extra
)
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
)
1362 vswhere_json
= vswhere_output_json
1366 'vswhere_json=%s, vswhere_exe=%s',
1367 bool(vswhere_json
), repr(vswhere_exe
), extra
=cls
.debug_extra
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
:
1381 debug('vswhere_norm=%s', repr(vswhere_norm
), extra
=cls
.debug_extra
)
1383 vswhere_json
= cls
._vswhere
_query
_json
_output
(
1385 ['-all', '-products', '*']
1388 if not vswhere_json
:
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
):
1401 vc_path
= os
.path
.join(installation_path
, 'VC')
1402 if not os
.path
.exists(vc_path
):
1405 vc_root
= MSVC
.Util
.normalize_path(vc_path
)
1406 if vc_root
in cls
.seen_root
:
1408 cls
.seen_root
.add(vc_root
)
1410 installation_version
= instance
.get('installationVersion')
1411 if not installation_version
:
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
)
1419 vc_version
= _VSWHERE_VSMAJOR_TO_VCVERSION
[vs_major
]
1421 product_id
= instance
.get('productId')
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
)
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
]
1437 vc_version_scons
= vc_version
+ scons_suffix
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(
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
)
1460 cls
.msvc_instances
= sorted(
1462 key
=cmp_to_key(MSVCInstance
.msvc_instances_default_order
)
1467 for msvc_instance
in cls
.msvc_instances
:
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
:
1482 key
= (msvc_instance
.vc_version
, msvc_instance
.vc_release
)
1483 cls
.msvc_map
.setdefault(key
,[]).append(msvc_instance
)
1485 cls
._new
_roots
_discovered
()
1488 'new_roots=%s, msvc_instances=%s',
1489 new_roots
, len(cls
.msvc_instances
), extra
=cls
.debug_extra
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.
1500 msvc_version: MSVC version to search for
1501 vswhere_exe: vswhere executable or None
1504 MSVC install dir or None
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
)
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
))
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
)
1526 key
= (msvc_version
, is_release
)
1528 msvc_instances
= msvc_map
.get(key
, UNDEFINED
)
1529 if msvc_instances
== UNDEFINED
:
1531 'msvc instances lookup failed: msvc_version=%s, is_release=%s',
1532 repr(msvc_version
), repr(is_release
)
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
])
1548 debug('save kind: msvc_version=%s, pdir=%s', repr(msvc_version
), repr(vcdir
))
1549 save_pdir_kind
.append((vcdir
, vckind_t
))
1551 debug('skip kind: msvc_version=%s, pdir=%s', repr(msvc_version
), repr(vcdir
))
1558 if not pdir
and not kind_t
:
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
1569 _cache_pdir_registry_queries
= {}
1571 def _find_vc_pdir_registry(msvc_version
):
1572 """ Find the MSVC product directory using the registry.
1575 msvc_version: MSVC version to search for
1578 MSVC install dir or None
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
))
1592 regkeys
= _VCVER_TO_PRODUCT_DIR
[msvc_version
]
1594 debug("Unknown version of MSVC: %s", msvc_version
)
1595 raise UnsupportedVersion("Unknown version %s" % msvc_version
) from None
1599 is_win64
= common
.is_win64()
1605 for hkroot
, key
in regkeys
:
1607 if not hkroot
or not key
:
1611 msregkeys
= [root
+ 'Wow6432Node\\' + key
, root
+ key
]
1613 msregkeys
= [root
+ key
]
1616 for msregkey
in msregkeys
:
1617 debug('trying VC registry key %s', repr(msregkey
))
1619 vcdir
= common
.read_reg(msregkey
, hkroot
)
1626 debug('no VC registry key %s', repr(key
))
1629 is_vcforpython
= 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
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
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
))
1648 vckind_t
= MSVC
.Kind
.msvc_version_pdir_registry_kind(msvc_version
, vcdir
, _VCVER_KIND_DETECT
[msvc_version
], is_vcforpython
)
1651 debug('save kind: msvc_version=%s, pdir=%s', repr(msvc_version
), repr(vcdir
))
1652 save_pdir_kind
.append((vcdir
, vckind_t
))
1654 debug('skip kind: msvc_version=%s, pdir=%s', repr(msvc_version
), repr(vcdir
))
1661 if not pdir
and not kind_t
:
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
1672 def _find_vc_pdir(msvc_version
, vswhere_exe
):
1673 """Find the MSVC product directory for the given version.
1677 msvc version (major.minor, e.g. 10.0)
1680 vswhere executable or None
1683 str: Path found in registry, or None
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
)
1696 debug('VC found: %s, dir=%s', repr(msvc_version
), repr(pdir
))
1699 elif msvc_version
in _VCVER_TO_PRODUCT_DIR
:
1701 pdir
= _find_vc_pdir_registry(msvc_version
)
1703 debug('VC found: %s, dir=%s', repr(msvc_version
), repr(pdir
))
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
))
1714 def find_vc_pdir(msvc_version
, env
=None):
1715 """Find the MSVC product directory for the given version.
1719 msvc version (major.minor, e.g. 10.0)
1721 optional to look up VSWHERE variable
1724 str: Path found in registry, or None
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
))
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)
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
)
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
)
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
1799 batfilename
= os
.path
.join(pdir
, os
.pardir
, "vcvarsall.bat")
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
)
1815 if clexe
and not os
.path
.exists(clexe
):
1816 debug("%s not found: %s", _CL_EXE_NAME
, clexe
)
1819 if depbat
and not os
.path
.exists(depbat
):
1820 debug("dependency batch file not found: %s", depbat
)
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
)
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
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
1865 a construction environment, usually if this is passed its
1866 because there is a desired TARGET_ARCH to be used when searching
1869 the path to the VC dir in the MSVC installation
1871 msvc version (major.minor, e.g. 10.0)
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
)
1895 with
open(default_toolset_file
) as f
:
1896 vc_specific_version
= f
.readlines()[0].strip()
1898 debug('failed to read %s', default_toolset_file
)
1901 debug('failed to find MSVC version in %s', default_toolset_file
)
1904 if vernum_int
>= 143:
1905 # 14.3 (VS2022) and later
1906 host_target_batchfile_clpathcomps
= _GE2022_HOST_TARGET_BATCHFILE_CLPATHCOMPS
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
)
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
)
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
)
1932 debug('%s found: %s', _CL_EXE_NAME
, cl_path
)
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
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
1952 batfile_path
= os
.path
.join(vc_dir
, "vcvarsall.bat")
1955 if not os
.path
.exists(batfile_path
):
1956 debug("batch file not found: %s", batfile_path
)
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
)
1971 _
, batfile
, cl_path_comps
= batcharg_batchfile_clpathcomps
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
)
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
)
1984 debug('%s found: %s', _CL_EXE_NAME
, cl_path
)
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
)
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
)
2007 # version not supported return false
2008 debug('unsupported MSVC version: %s', str(vernum
))
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
2024 del env
['TARGET_ARCH']
2025 debug("delete env['TARGET_ARCH']")
2027 installed_versions
= []
2030 debug('trying to find VC %s', ver
)
2032 VC_DIR
= find_vc_pdir(ver
, env
)
2034 debug('found VC %s', ver
)
2035 if _check_files_exist_in_vc_dir(env
, VC_DIR
, ver
):
2036 installed_versions
.append(ver
)
2038 debug('no compiler found %s', ver
)
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
2045 except VisualCException
as e
:
2046 debug('did not find VC %s: caught exception %s', ver
, str(e
))
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()
2060 _VSWhereExecutable
.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
))
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."""
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
):
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:
2124 toolsdir
= cache_data
["VCToolsInstallDir"]
2126 # we write this value, so should not happen
2130 toolpath
= Path(toolsdir
[0])
2131 if not toolpath
.exists():
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
)
2140 olines
= stdout
.splitlines()
2142 # process stdout: batch file errors (not necessarily first line)
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
)
2151 script_errmsg
= '\n'.join(script_errlog
)
2153 have_cl
, _
= _check_cl_exists_in_script_env(cache_data
)
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
)
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
)
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 ")
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
))
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')
2197 debug('using default installed MSVC version %s', repr(msvc_version
))
2199 debug('using specified MSVC version %s', repr(msvc_version
))
2203 def msvc_setup_env_once(env
, tool
=None) -> None:
2205 has_run
= env
["MSVC_SETUP_RUN"]
2210 MSVC
.SetupEnvDefault
.register_setup(env
, msvc_exists
)
2212 env
["MSVC_SETUP_RUN"] = True
2214 req_tools
= MSVC
.SetupEnvDefault
.register_iserror(env
, tool
, msvc_exists
)
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
2234 # Find the product directory
2237 pdir
= find_vc_pdir(version
, env
)
2238 except UnsupportedVersion
:
2239 # Unsupported msvc version (raise MSVCArgumentError?)
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
2249 version_installed
= False
2253 # Query all candidate sdk (host, target, sdk_pdir) after vc_script pass if necessary
2256 for host_arch
, target_arch
, in host_target_list
:
2257 # Set to current arch.
2258 env
['TARGET_ARCH'] = target_arch
2261 # Try to locate a batch file for this host/target platform combo
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
:
2268 debug('Caught exception while looking for batch file (%s)', msg
)
2269 version_installed
= False
2272 # Save (host, target, sdk_pdir) platform combo for sdk queries
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
)
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
))
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
)
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
)
2299 have_cl
, _
= _check_cl_exists_in_script_env(d
)
2301 debug('skip cl.exe not found vc_script:%s, vc_script_args:%s', repr(vc_script
), arg
)
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
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
)
2316 # Try to use the sdk batch file for this (host, target, sdk_pdir) combo
2317 debug('trying sdk_script:%s', repr(sdk_script
))
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
)
2325 have_cl
, _
= _check_cl_exists_in_script_env(d
)
2327 debug('skip cl.exe not found sdk_script:%s', repr(sdk_script
))
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
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
2345 installed_vcs
= get_installed_vcs(env
)
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
)
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
)
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
2384 def msvc_setup_env(env
):
2387 _VSWhereExecutable
.vswhere_freeze_env(env
)
2389 version
= get_default_version(env
)
2391 if not msvc_setup_env_user(env
):
2392 MSVC
.SetupEnvDefault
.set_nodefault()
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
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
)
2410 d
= msvc_find_valid_batch_script(env
,version
)
2411 debug('use_script 2 %s', 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
)
2419 debug('use_settings %s', d
)
2421 debug('MSVC_USE_SCRIPT set to False')
2422 warn_msg
= "MSVC_USE_SCRIPT set to False, assuming environment " \
2424 SCons
.Warnings
.warn(SCons
.Warnings
.VisualCMissingWarning
, warn_msg
)
2427 found_cl_path
= None
2428 found_cl_envpath
= None
2431 for k
, v
in d
.items():
2432 if not seen_path
and k
== 'PATH':
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'."
2445 warn_msg
+= f
" SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {CONFIG_CACHE} if out of date."
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."
2451 SCons
.Warnings
.warn(SCons
.Warnings
.VisualCMissingWarning
, warn_msg
)
2453 def msvc_exists(env
=None, version
=None):
2454 vcs
= get_installed_vcs(env
)
2458 rval
= version
in vcs
2459 debug('version=%s, return=%s', repr(version
), rval
)
2462 def msvc_setup_env_user(env
=None):
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
]:
2476 debug('key=%s, return=%s', repr(key
), 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
]):
2483 debug('key=%s, return=%s', repr(key
), 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:
2490 debug('key=%s, return=%s', repr(key
), rval
)
2493 debug('return=%s', rval
)
2496 def msvc_setup_env_tool(env
=None, version
=None, tool
=None):
2497 MSVC
.SetupEnvDefault
.register_tool(env
, tool
, msvc_exists
)
2499 if not rval
and msvc_exists(env
, version
):
2501 if not rval
and msvc_setup_env_user(env
):
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
))
2511 version
= msvc_default_version()
2514 debug('no msvc versions detected')
2517 version_def
= MSVC
.Util
.msvc_extended_version_components(version
)
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
)
2525 def msvc_toolset_versions(msvc_version
=None, full
: bool=True, sxs
: bool=False, vswhere_exe
=None):
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
)
2535 if not msvc_version
:
2536 msvc_version
= msvc_default_version()
2538 if not msvc_version
:
2539 debug('no msvc versions detected')
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
)
2548 debug('VC folder not found for version %s', repr(msvc_version
))
2551 rval
= MSVC
.ScriptArguments
._msvc
_toolset
_versions
_internal
(msvc_version
, vc_dir
, full
=full
, sxs
=sxs
)
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
)
2561 if not msvc_version
:
2562 msvc_version
= msvc_default_version()
2564 if not msvc_version
:
2565 debug('no msvc versions detected')
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
)
2574 debug('VC folder not found for version %s', repr(msvc_version
))
2577 rval
= MSVC
.ScriptArguments
._msvc
_toolset
_versions
_spectre
_internal
(msvc_version
, vc_dir
)
2580 _InstalledVersionToolset
= namedtuple('_InstalledVersionToolset', [
2582 'toolset_version_def',
2585 _InstalledVCSToolsetsComponents
= namedtuple('_InstalledVCSToolsetComponents', [
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
)
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
)
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
)
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
:
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(
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
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.
2669 The version specification may be an msvc version or a toolset
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.
2679 A vswhere executable location or None.
2682 tuple: A tuple containing the msvc version and the msvc toolset version.
2683 The msvc toolset version may be None.
2686 MSVCToolsetVersionNotFound: when the specified version is not found.
2687 MSVCArgumentError: when argument validation fails.
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
)
2697 msvc_toolset_version
= None
2699 with MSVC
.Policy
.msvc_notfound_policy_contextmanager('suppress'):
2701 vcs
= get_installed_vcs()
2704 version
= msvc_default_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
)
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
:
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:
2742 force_toolset_msvc_version
= False
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
)
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
2773 query_msvc_toolset_version
= version_def
.msvc_toolset_version
2776 query_version_list
= MSVC
.Config
.MSVC_VERSION_TOOLSET_SEARCH_MAP
[msvc_version
]
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
:
2786 seen_msvc_version
.add(query_msvc_version
)
2788 vc_dir
= _find_vc_pdir(query_msvc_version
, vswhere_exe
)
2792 if query_msvc_version
.startswith('14.0'):
2793 # VS2015 does not support toolset version argument
2794 msvc_toolset_version
= None
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
2802 toolset_vcvars
= MSVC
.ScriptArguments
._msvc
_toolset
_internal
(query_msvc_version
, query_msvc_toolset_version
, vc_dir
)
2804 msvc_toolset_version
= toolset_vcvars
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
:
2814 msvc_toolset_version
= query_msvc_toolset_version
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)