Skip to content

Commit da72610

Browse files
committed
Rework experimental msvc_query_version_toolset function.
Changes: * Add context manager functions to temporary change the policy for msvc not found and msvc script errors within a context block. * Change msvc_query_version_toolset function behavior: raise exceptions if not found and for argument validation errors; otherwise return msvc version and toolset version. * Additional logging of exception messages . * Update tests for 14.3X/14.4X toolsets for VS 2022.
1 parent 40e6f1d commit da72610

File tree

5 files changed

+180
-94
lines changed

5 files changed

+180
-94
lines changed

SCons/Tool/MSCommon/MSVC/Policy.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@
3838
namedtuple,
3939
)
4040

41+
from contextlib import (
42+
contextmanager,
43+
)
44+
4145
import SCons.Warnings
4246

4347
from ..common import (
@@ -215,6 +219,18 @@ def msvc_notfound_handler(env, msg):
215219
else:
216220
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
217221

222+
@contextmanager
223+
def msvc_notfound_policy_contextmanager(MSVC_NOTFOUND_POLICY=None):
224+
""" Temporarily change the MSVC not found policy within a context.
225+
226+
Args:
227+
MSVC_NOTFOUND_POLICY:
228+
string representing the policy behavior
229+
when MSVC is not found or None
230+
"""
231+
prev_policy = msvc_set_notfound_policy(MSVC_NOTFOUND_POLICY)
232+
yield
233+
msvc_set_notfound_policy(prev_policy)
218234

219235
def _msvc_scripterror_policy_lookup(symbol):
220236

@@ -299,3 +315,16 @@ def msvc_scripterror_handler(env, msg):
299315
else:
300316
SCons.Warnings.warn(MSVCScriptExecutionWarning, msg)
301317

318+
@contextmanager
319+
def msvc_scripterror_policy_contextmanager(MSVC_SCRIPTERROR_POLICY=None):
320+
""" Temporarily change the msvc batch execution errors policy within a context.
321+
322+
Args:
323+
MSVC_SCRIPTERROR_POLICY:
324+
string representing the policy behavior
325+
when msvc batch file execution errors are detected or None
326+
"""
327+
prev_policy = msvc_set_scripterror_policy(MSVC_SCRIPTERROR_POLICY)
328+
yield
329+
msvc_set_scripterror_policy(prev_policy)
330+

SCons/Tool/MSCommon/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@
6363
msvc_get_notfound_policy,
6464
msvc_set_scripterror_policy,
6565
msvc_get_scripterror_policy,
66+
msvc_notfound_policy_contextmanager,
67+
msvc_scripterror_policy_contextmanager,
6668
)
6769

6870
from .MSVC.Exceptions import ( # noqa: F401

SCons/Tool/MSCommon/vc.py

Lines changed: 105 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -2572,28 +2572,24 @@ def msvc_toolset_versions_spectre(msvc_version=None, vswhere_exe=None):
25722572

25732573
def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_exe=None):
25742574
"""
2575-
Returns an msvc version and a toolset version given a version
2575+
Return an msvc version and a toolset version given a version
25762576
specification.
25772577
25782578
This is an EXPERIMENTAL proxy for using a toolset version to perform
25792579
msvc instance selection. This function will be removed when
25802580
toolset version is taken into account during msvc instance selection.
25812581
2582-
Search for an installed Visual Studio instance that supports the
2583-
specified version.
2582+
This function searches for an installed Visual Studio instance that
2583+
contains the requested version. A component suffix (e.g., Exp) is not
2584+
supported for toolset version specifications (e.g., 14.39).
25842585
2585-
When the specified version contains a component suffix (e.g., Exp),
2586-
the msvc version is returned and the toolset version is None. No
2587-
search if performed.
2586+
An MSVCArgumentError is raised when argument validation fails. An
2587+
MSVCToolsetVersionNotFound exception is raised when the requested
2588+
version is not found.
25882589
2589-
When the specified version does not contain a component suffix, the
2590-
version is treated as a toolset version specification. A search is
2591-
performed for the first msvc instance that contains the toolset
2592-
version.
2593-
2594-
Only Visual Studio 2017 and later support toolset arguments. For
2595-
Visual Studio 2015 and earlier, the msvc version is returned and
2596-
the toolset version is None.
2590+
For Visual Studio 2015 and earlier, the msvc version is returned and
2591+
the toolset version is None. For Visual Studio 2017 and later, the
2592+
selected toolset version is returned.
25972593
25982594
Args:
25992595
@@ -2623,106 +2619,132 @@ def msvc_query_version_toolset(version=None, prefer_newest: bool=True, vswhere_e
26232619
msvc_version = None
26242620
msvc_toolset_version = None
26252621

2626-
if not version:
2627-
version = msvc_default_version()
2622+
with MSVC.Policy.msvc_notfound_policy_contextmanager('suppress'):
26282623

2629-
if not version:
2630-
debug('no msvc versions detected')
2631-
return msvc_version, msvc_toolset_version
2624+
_VSWhereExecutable.vswhere_freeze_executable(vswhere_exe)
26322625

2633-
version_def = MSVC.Util.msvc_extended_version_components(version)
2626+
vcs = get_installed_vcs()
26342627

2635-
if not version_def:
2636-
msg = f'Unsupported msvc version {version!r}'
2637-
raise MSVCArgumentError(msg)
2628+
if not version:
2629+
version = msvc_default_version()
26382630

2639-
if version_def.msvc_suffix:
2640-
if version_def.msvc_verstr != version_def.msvc_toolset_version:
2641-
# toolset version with component suffix
2642-
msg = f'Unsupported toolset version {version!r}'
2643-
raise MSVCArgumentError(msg)
2631+
if not version:
2632+
msg = f'No versions of the MSVC compiler were found'
2633+
debug(f'MSVCToolsetVersionNotFound: {msg}')
2634+
raise MSVCToolsetVersionNotFound(msg)
26442635

2645-
if version_def.msvc_vernum > 14.0:
2646-
# VS2017 and later
2647-
force_toolset_msvc_version = False
2648-
else:
2649-
# VS2015 and earlier
2650-
force_toolset_msvc_version = True
2651-
extended_version = version_def.msvc_verstr + '0.00000'
2652-
if not extended_version.startswith(version_def.msvc_toolset_version):
2653-
# toolset not equivalent to msvc version
2654-
msg = 'Unsupported toolset version {} (expected {})'.format(
2655-
repr(version), repr(extended_version)
2656-
)
2636+
version_def = MSVC.Util.msvc_extended_version_components(version)
2637+
2638+
if not version_def:
2639+
msg = f'Unsupported MSVC version {version!r}'
2640+
debug(f'MSVCArgumentError: {msg}')
26572641
raise MSVCArgumentError(msg)
26582642

2659-
msvc_version = version_def.msvc_version
2643+
msvc_version = version_def.msvc_version
26602644

2661-
if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP:
2662-
# VS2013 and earlier
2663-
debug(
2664-
'ignore: msvc_version=%s, msvc_toolset_version=%s',
2665-
repr(msvc_version), repr(msvc_toolset_version)
2666-
)
2667-
return msvc_version, msvc_toolset_version
2645+
if version_def.msvc_suffix:
26682646

2669-
if force_toolset_msvc_version:
2670-
query_msvc_toolset_version = version_def.msvc_verstr
2671-
else:
2672-
query_msvc_toolset_version = version_def.msvc_toolset_version
2647+
if version_def.msvc_verstr != version_def.msvc_toolset_version:
2648+
# toolset version with component suffix
2649+
msg = f'Unsupported MSVC toolset version {version!r}'
2650+
debug(f'MSVCArgumentError: {msg}')
2651+
raise MSVCArgumentError(msg)
26732652

2674-
if prefer_newest:
2675-
query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
2676-
else:
2677-
query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \
2678-
MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
2653+
if msvc_version not in vcs:
2654+
msg = f'MSVC version {msvc_version!r} not found'
2655+
debug(f'MSVCToolsetVersionNotFound: {msg}')
2656+
raise MSVCToolsetVersionNotFound(msg)
26792657

2680-
seen_msvc_version = set()
2681-
for query_msvc_version in query_version_list:
2658+
if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP:
2659+
debug(
2660+
'suffix: msvc_version=%s, msvc_toolset_version=%s',
2661+
repr(msvc_version), repr(msvc_toolset_version)
2662+
)
2663+
return msvc_version, msvc_toolset_version
26822664

2683-
if query_msvc_version in seen_msvc_version:
2684-
continue
2685-
seen_msvc_version.add(query_msvc_version)
2665+
if version_def.msvc_vernum > 14.0:
2666+
# VS2017 and later
2667+
force_toolset_msvc_version = False
2668+
else:
2669+
# VS2015 and earlier
2670+
force_toolset_msvc_version = True
2671+
extended_version = version_def.msvc_verstr + '0.00000'
2672+
if not extended_version.startswith(version_def.msvc_toolset_version):
2673+
# toolset not equivalent to msvc version
2674+
msg = 'Unsupported MSVC toolset version {} (expected {})'.format(
2675+
repr(version), repr(extended_version)
2676+
)
2677+
debug(f'MSVCArgumentError: {msg}')
2678+
raise MSVCArgumentError(msg)
26862679

2687-
vc_dir = _find_vc_pdir(query_msvc_version, vswhere_exe)
2688-
if not vc_dir:
2689-
continue
2680+
if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP:
2681+
2682+
# VS2013 and earlier
2683+
2684+
if msvc_version not in vcs:
2685+
msg = f'MSVC version {version!r} not found'
2686+
debug(f'MSVCToolsetVersionNotFound: {msg}')
2687+
raise MSVCToolsetVersionNotFound(msg)
26902688

2691-
if query_msvc_version.startswith('14.0'):
2692-
# VS2015 does not support toolset version argument
2693-
msvc_toolset_version = None
26942689
debug(
2695-
'found: msvc_version=%s, msvc_toolset_version=%s',
2696-
repr(query_msvc_version), repr(msvc_toolset_version)
2690+
'earlier: msvc_version=%s, msvc_toolset_version=%s',
2691+
repr(msvc_version), repr(msvc_toolset_version)
26972692
)
2698-
return query_msvc_version, msvc_toolset_version
2693+
return msvc_version, msvc_toolset_version
26992694

2700-
try:
2701-
toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir)
2702-
if toolset_vcvars:
2703-
msvc_toolset_version = toolset_vcvars
2695+
if force_toolset_msvc_version:
2696+
query_msvc_toolset_version = version_def.msvc_verstr
2697+
else:
2698+
query_msvc_toolset_version = version_def.msvc_toolset_version
2699+
2700+
if prefer_newest:
2701+
query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
2702+
else:
2703+
query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \
2704+
MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version]
2705+
2706+
seen_msvc_version = set()
2707+
for query_msvc_version in query_version_list:
2708+
2709+
if query_msvc_version in seen_msvc_version:
2710+
continue
2711+
seen_msvc_version.add(query_msvc_version)
2712+
2713+
vc_dir = _find_vc_pdir(query_msvc_version, vswhere_exe)
2714+
if not vc_dir:
2715+
continue
2716+
2717+
if query_msvc_version.startswith('14.0'):
2718+
# VS2015 does not support toolset version argument
2719+
msvc_toolset_version = None
27042720
debug(
27052721
'found: msvc_version=%s, msvc_toolset_version=%s',
27062722
repr(query_msvc_version), repr(msvc_toolset_version)
27072723
)
27082724
return query_msvc_version, msvc_toolset_version
27092725

2710-
except MSVCToolsetVersionNotFound:
2711-
pass
2726+
try:
2727+
toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir)
2728+
if toolset_vcvars:
2729+
msvc_toolset_version = toolset_vcvars
2730+
debug(
2731+
'found: msvc_version=%s, msvc_toolset_version=%s',
2732+
repr(query_msvc_version), repr(msvc_toolset_version)
2733+
)
2734+
return query_msvc_version, msvc_toolset_version
27122735

2713-
msvc_toolset_version = query_msvc_toolset_version
2736+
except MSVCToolsetVersionNotFound:
2737+
pass
2738+
2739+
msvc_toolset_version = query_msvc_toolset_version
27142740

27152741
debug(
27162742
'not found: msvc_version=%s, msvc_toolset_version=%s',
27172743
repr(msvc_version), repr(msvc_toolset_version)
27182744
)
27192745

2720-
if version_def.msvc_verstr == msvc_toolset_version:
2721-
msg = f'MSVC version {version!r} was not found'
2722-
MSVC.Policy.msvc_notfound_handler(None, msg)
2723-
return msvc_version, msvc_toolset_version
2724-
27252746
msg = f'MSVC toolset version {version!r} not found'
2747+
debug(f'MSVCToolsetVersionNotFound: {msg}')
27262748
raise MSVCToolsetVersionNotFound(msg)
27272749

27282750

SCons/Tool/MSCommon/vcTests.py

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -234,8 +234,18 @@ class Data:
234234
HAVE_MSVC = False
235235
DEFAULT_VERSION_DEF = None
236236

237+
INSTALLED_VCS = MSCommon.vc.get_installed_vcs()
237238
INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components()
238239

240+
@classmethod
241+
def query_version_list(cls, vcver):
242+
# VS 2022 (14.3) can have either/both toolset versions 14.3X and 14.4X
243+
if vcver == '14.3' or (vcver is None and cls.DEFAULT_VERSION == '14.3'):
244+
vcver_list = ['14.4', '14.3']
245+
else:
246+
vcver_list = [vcver]
247+
return vcver_list
248+
239249
@classmethod
240250
def _msvc_toolset_notfound_list(cls, toolset_seen, toolset_list):
241251
new_toolset_list = []
@@ -454,15 +464,26 @@ class MsvcQueryVersionToolsetTests(unittest.TestCase):
454464

455465
def run_valid_default_msvc(self, have_msvc) -> None:
456466
for prefer_newest in (True, False):
457-
msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
458-
version=None, prefer_newest=prefer_newest
459-
)
460-
expect = (have_msvc and msvc_version) or (not have_msvc and not msvc_version)
461-
self.assertTrue(expect, "unexpected msvc_version {} for for msvc version {}".format(
467+
if not have_msvc:
468+
with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound):
469+
msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
470+
version=None, prefer_newest=prefer_newest
471+
)
472+
continue
473+
msvc_version = msvc_toolset_version = None
474+
for vcver in Data.query_version_list(None):
475+
try:
476+
msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
477+
version=vcver, prefer_newest=prefer_newest
478+
)
479+
break
480+
except MSCommon.vc.MSVCToolsetVersionNotFound:
481+
pass
482+
self.assertTrue(msvc_version, "unexpected msvc_version {} for for msvc version {}".format(
462483
repr(msvc_version), repr(None)
463484
))
464485
version_def = MSCommon.msvc_version_components(msvc_version)
465-
if have_msvc and version_def.msvc_vernum > 14.0:
486+
if version_def.msvc_vernum > 14.0:
466487
# VS2017 and later for toolset version
467488
self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(
468489
repr(None)
@@ -477,11 +498,24 @@ def test_valid_default_msvc(self) -> None:
477498

478499
def test_valid_vcver(self) -> None:
479500
for symbol in MSCommon.vc._VCVER:
501+
have_msvc = bool(symbol in Data.INSTALLED_VCS)
480502
version_def = MSCommon.msvc_version_components(symbol)
481503
for prefer_newest in (True, False):
482-
msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
483-
version=symbol, prefer_newest=prefer_newest
484-
)
504+
if not have_msvc:
505+
with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound):
506+
msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
507+
version=symbol, prefer_newest=prefer_newest
508+
)
509+
continue
510+
msvc_version = msvc_toolset_version = None
511+
for vcver in Data.query_version_list(symbol):
512+
try:
513+
msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset(
514+
version=vcver, prefer_newest=prefer_newest
515+
)
516+
break
517+
except:
518+
pass
485519
self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(symbol)))
486520
if version_def.msvc_vernum > 14.0:
487521
# VS2017 and later for toolset version

test/MSVC/no_msvc.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,7 @@ def exists(env):
7575

7676
# test no msvc's and msvc_query_version_toolset() call
7777
test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py', 'SConstruct')
78-
test.run(arguments='-Q -s')
79-
test.must_contain_all(test.stdout(), 'msvc_version=None, msvc_toolset_version=None')
78+
test.run(arguments='-Q -s', status=2, stderr=r"^.*MSVCToolsetVersionNotFound.+", match=TestSCons.match_re_dotall)
8079

8180
test.pass_test()
8281

0 commit comments

Comments
 (0)