Merge pull request #4655 from bdbaddog/fix_new_ninja_package
[scons.git] / SCons / Tool / MSCommon / MSVC / ScriptArguments.py
blob2c766fed4ed5b5c86c27aa3b907268573c3d90e4
1 # MIT License
3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 """
25 Batch file argument functions for Microsoft Visual C/C++.
26 """
28 import os
29 import re
30 import enum
32 from collections import (
33 namedtuple,
36 from ..common import (
37 CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS,
38 debug,
41 from . import Util
42 from . import Config
43 from . import Registry
44 from . import WinSDK
45 from . import Kind
47 from .Exceptions import (
48 MSVCInternalError,
49 MSVCSDKVersionNotFound,
50 MSVCToolsetVersionNotFound,
51 MSVCSpectreLibsNotFound,
52 MSVCArgumentError,
55 from . import Dispatcher
56 Dispatcher.register_modulename(__name__)
59 # Script argument: boolean True
60 _ARGUMENT_BOOLEAN_TRUE_LEGACY = (True, '1') # MSVC_UWP_APP
61 _ARGUMENT_BOOLEAN_TRUE = (True,)
63 # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last?
64 re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$')
65 re_sdk_version_81 = re.compile(r'^8[.]1$')
67 re_sdk_dispatch_map = {
68 '10.0': re_sdk_version_100,
69 '8.1': re_sdk_version_81,
72 def _verify_re_sdk_dispatch_map():
73 debug('')
74 for sdk_version in Config.MSVC_SDK_VERSIONS:
75 if sdk_version in re_sdk_dispatch_map:
76 continue
77 err_msg = f'sdk version {sdk_version} not in re_sdk_dispatch_map'
78 raise MSVCInternalError(err_msg)
79 return None
81 # SxS version bugfix
82 _msvc_sxs_bugfix_map = {}
83 _msvc_sxs_bugfix_folder = {}
84 _msvc_sxs_bugfix_version = {}
86 for msvc_version, sxs_version, sxs_bugfix in [
87 # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526
88 # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files
89 # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn.
90 ('14.2', '14.28.16.8', '14.28')
92 _msvc_sxs_bugfix_map.setdefault(msvc_version, []).append((sxs_version, sxs_bugfix))
93 _msvc_sxs_bugfix_folder[(msvc_version, sxs_bugfix)] = sxs_version
94 _msvc_sxs_bugfix_version[(msvc_version, sxs_version)] = sxs_bugfix
96 # MSVC_SCRIPT_ARGS
97 re_vcvars_uwp = re.compile(r'(?:(?<!\S)|^)(?P<uwp>(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
98 re_vcvars_sdk = re.compile(r'(?:(?<!\S)|^)(?P<sdk>(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
99 re_vcvars_toolset = re.compile(r'(?:(?<!\S)|^)(?P<toolset_arg>(?:[-]{1,2}|[/])vcvars_ver[=](?P<toolset>\S*))(?:(?!\S)|$)', re.IGNORECASE)
100 re_vcvars_spectre = re.compile(r'(?:(?<!\S)|^)(?P<spectre_arg>(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P<spectre>\S*))(?:(?!\S)|$)',re.IGNORECASE)
102 # Force default sdk argument
103 _MSVC_FORCE_DEFAULT_SDK = False
105 # Force default toolset argument
106 _MSVC_FORCE_DEFAULT_TOOLSET = False
108 # Force default arguments
109 _MSVC_FORCE_DEFAULT_ARGUMENTS = False
111 def _msvc_force_default_sdk(force: bool=True) -> None:
112 global _MSVC_FORCE_DEFAULT_SDK
113 _MSVC_FORCE_DEFAULT_SDK = force
114 debug('_MSVC_FORCE_DEFAULT_SDK=%s', repr(force))
116 def _msvc_force_default_toolset(force: bool=True) -> None:
117 global _MSVC_FORCE_DEFAULT_TOOLSET
118 _MSVC_FORCE_DEFAULT_TOOLSET = force
119 debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force))
121 def msvc_force_default_arguments(force=None):
122 global _MSVC_FORCE_DEFAULT_ARGUMENTS
123 prev_policy = _MSVC_FORCE_DEFAULT_ARGUMENTS
124 if force is not None:
125 _MSVC_FORCE_DEFAULT_ARGUMENTS = force
126 _msvc_force_default_sdk(force)
127 _msvc_force_default_toolset(force)
128 return prev_policy
130 if CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS:
131 msvc_force_default_arguments(force=True)
133 # UWP SDK 8.1 and SDK 10:
135 # https://stackoverflow.com/questions/46659238/build-windows-app-compatible-for-8-1-and-10
136 # VS2019 - UWP (Except for Win10Mobile)
137 # VS2017 - UWP
138 # VS2015 - UWP, Win8.1 StoreApp, WP8/8.1 StoreApp
139 # VS2013 - Win8/8.1 StoreApp, WP8/8.1 StoreApp
141 # SPECTRE LIBS (msvc documentation):
142 # "There are no versions of Spectre-mitigated libraries for Universal Windows (UWP) apps or
143 # components. App-local deployment of such libraries isn't possible."
145 # MSVC batch file arguments:
147 # VS2022: UWP, SDK, TOOLSET, SPECTRE
148 # VS2019: UWP, SDK, TOOLSET, SPECTRE
149 # VS2017: UWP, SDK, TOOLSET, SPECTRE
150 # VS2015: UWP, SDK
152 # MSVC_SCRIPT_ARGS: VS2015+
154 # MSVC_UWP_APP: VS2015+
155 # MSVC_SDK_VERSION: VS2015+
156 # MSVC_TOOLSET_VERSION: VS2017+
157 # MSVC_SPECTRE_LIBS: VS2017+
159 @enum.unique
160 class SortOrder(enum.IntEnum):
161 UWP = 1 # MSVC_UWP_APP
162 SDK = 2 # MSVC_SDK_VERSION
163 TOOLSET = 3 # MSVC_TOOLSET_VERSION
164 SPECTRE = 4 # MSVC_SPECTRE_LIBS
165 USER = 5 # MSVC_SCRIPT_ARGS
167 VS2019 = Config.MSVS_VERSION_INTERNAL['2019']
168 VS2017 = Config.MSVS_VERSION_INTERNAL['2017']
169 VS2015 = Config.MSVS_VERSION_INTERNAL['2015']
171 MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
172 'version', # full version (e.g., '14.1Exp', '14.32.31326')
173 'vs_def',
176 TOOLSET_VERSION_ARGS_DEFINITION = namedtuple('ToolsetVersionArgsDefinition', [
177 'version', # full version (e.g., '14.1Exp', '14.32.31326')
178 'vc_buildtools_def',
179 'is_user',
182 def _msvc_version(version):
184 verstr = Util.get_msvc_version_prefix(version)
185 vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
187 version_args = MSVC_VERSION_ARGS_DEFINITION(
188 version = version,
189 vs_def = vs_def,
192 return version_args
194 def _toolset_version(version, is_user=False):
196 vc_series = Util.get_msvc_version_prefix(version)
198 vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL[vc_series]
199 vc_buildtools_def = Config.VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries]
201 version_args = TOOLSET_VERSION_ARGS_DEFINITION(
202 version = version,
203 vc_buildtools_def = vc_buildtools_def,
204 is_user = is_user,
207 return version_args
209 def _msvc_script_argument_uwp(env, msvc, arglist, target_arch):
211 uwp_app = env['MSVC_UWP_APP']
212 debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
214 if not uwp_app:
215 return None
217 if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY:
218 return None
220 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
221 debug(
222 'invalid: msvc version constraint: %s < %s VS2015',
223 repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
224 repr(VS2015.vc_buildtools_def.msvc_version_numeric)
226 err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
227 repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
229 raise MSVCArgumentError(err_msg)
231 is_supported, is_target = Kind.msvc_version_uwp_is_supported(msvc.version, target_arch, env)
232 if not is_supported:
233 _, kind_str = Kind.get_msvc_version_kind(msvc.version)
234 debug(
235 'invalid: msvc_version constraint: %s %s %s',
236 repr(msvc.version), repr(kind_str), repr(target_arch)
238 if is_target and target_arch:
239 err_msg = "MSVC_UWP_APP ({}) TARGET_ARCH ({}) is not supported for MSVC_VERSION {} ({})".format(
240 repr(uwp_app), repr(target_arch), repr(msvc.version), repr(kind_str)
242 else:
243 err_msg = "MSVC_UWP_APP ({}) is not supported for MSVC_VERSION {} ({})".format(
244 repr(uwp_app), repr(msvc.version), repr(kind_str)
246 raise MSVCArgumentError(err_msg)
248 # VS2017+ rewrites uwp => store for 14.0 toolset
249 uwp_arg = msvc.vs_def.vc_uwp
251 # store/uwp may not be fully installed
252 argpair = (SortOrder.UWP, uwp_arg)
253 arglist.append(argpair)
255 return uwp_arg
257 def _user_script_argument_uwp(env, uwp, user_argstr) -> bool:
259 matches = [m for m in re_vcvars_uwp.finditer(user_argstr)]
260 if not matches:
261 return False
263 if len(matches) > 1:
264 debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
265 err_msg = f"multiple uwp declarations: MSVC_SCRIPT_ARGS={user_argstr!r}"
266 raise MSVCArgumentError(err_msg)
268 if not uwp:
269 return True
271 env_argstr = env.get('MSVC_UWP_APP','')
272 debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
274 err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format(
275 repr(env_argstr), repr(user_argstr)
278 raise MSVCArgumentError(err_msg)
280 def _msvc_script_argument_sdk_constraints(msvc, sdk_version, env):
282 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
283 debug(
284 'invalid: msvc_version constraint: %s < %s VS2015',
285 repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
286 repr(VS2015.vc_buildtools_def.msvc_version_numeric)
288 err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
289 repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
291 return err_msg
293 if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env):
294 _, kind_str = Kind.get_msvc_version_kind(msvc.version)
295 debug('invalid: msvc_version constraint: %s %s', repr(msvc.version), repr(kind_str))
296 err_msg = "MSVC_SDK_VERSION ({}) is not supported for MSVC_VERSION {} ({})".format(
297 repr(sdk_version), repr(msvc.version), repr(kind_str)
299 return err_msg
301 for msvc_sdk_version in msvc.vs_def.vc_sdk_versions:
302 re_sdk_version = re_sdk_dispatch_map[msvc_sdk_version]
303 if re_sdk_version.match(sdk_version):
304 debug('valid: sdk_version=%s', repr(sdk_version))
305 return None
307 debug('invalid: method exit: sdk_version=%s', repr(sdk_version))
308 err_msg = f"MSVC_SDK_VERSION ({sdk_version!r}) is not supported"
309 return err_msg
311 def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def):
313 if sdk_version == '8.1' and platform_def.is_uwp:
315 vc_buildtools_def = toolset.vc_buildtools_def if toolset else msvc.vs_def.vc_buildtools_def
317 if vc_buildtools_def.msvc_version_numeric > VS2015.vc_buildtools_def.msvc_version_numeric:
318 debug(
319 'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015',
320 repr(vc_buildtools_def.msvc_version_numeric),
321 repr(VS2015.vc_buildtools_def.msvc_version_numeric)
323 if toolset and toolset.is_user:
324 err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset {} MSVC_VERSION {} > {} VS2015".format(
325 repr(sdk_version), repr(platform_def.vc_platform),
326 repr(toolset.version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
328 else:
329 err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format(
330 repr(sdk_version), repr(platform_def.vc_platform),
331 repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
333 return err_msg
335 return None
337 def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist):
339 sdk_version = env['MSVC_SDK_VERSION']
340 debug(
341 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s',
342 repr(msvc.version), repr(sdk_version), repr(platform_def.vc_platform)
345 if not sdk_version:
346 return None
348 err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version, env)
349 if err_msg:
350 raise MSVCArgumentError(err_msg)
352 sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
354 if sdk_version not in sdk_list:
355 err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format(
356 repr(sdk_version), repr(platform_def.vc_platform)
358 raise MSVCSDKVersionNotFound(err_msg)
360 err_msg = _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def)
361 if err_msg:
362 raise MSVCArgumentError(err_msg)
364 argpair = (SortOrder.SDK, sdk_version)
365 arglist.append(argpair)
367 return sdk_version
369 def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk: bool=False):
371 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
372 return None
374 if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env):
375 return None
377 sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
378 if not len(sdk_list):
379 return None
381 sdk_default = sdk_list[0]
383 debug(
384 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s',
385 repr(msvc.version), repr(sdk_default), repr(platform_def.vc_platform)
388 if force_sdk:
389 argpair = (SortOrder.SDK, sdk_default)
390 arglist.append(argpair)
392 return sdk_default
394 def _user_script_argument_sdk(env, sdk_version, user_argstr):
396 matches = [m for m in re_vcvars_sdk.finditer(user_argstr)]
397 if not matches:
398 return None
400 if len(matches) > 1:
401 debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
402 err_msg = f"multiple sdk version declarations: MSVC_SCRIPT_ARGS={user_argstr!r}"
403 raise MSVCArgumentError(err_msg)
405 if not sdk_version:
406 user_sdk = matches[0].group('sdk')
407 return user_sdk
409 env_argstr = env.get('MSVC_SDK_VERSION','')
410 debug('multiple sdk version declarations: MSVC_SDK_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
412 err_msg = "multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
413 repr(env_argstr), repr(user_argstr)
416 raise MSVCArgumentError(err_msg)
418 _toolset_have140_cache = None
420 def _msvc_have140_toolset():
421 global _toolset_have140_cache
423 if _toolset_have140_cache is None:
424 suffix = Registry.vstudio_sxs_vc7('14.0')
425 vcinstalldirs = [record[0] for record in Registry.microsoft_query_paths(suffix)]
426 debug('vc140 toolset: paths=%s', repr(vcinstalldirs))
427 _toolset_have140_cache = True if vcinstalldirs else False
429 return _toolset_have140_cache
431 def _reset_have140_cache() -> None:
432 global _toolset_have140_cache
433 debug('reset: cache')
434 _toolset_have140_cache = None
436 def _msvc_read_toolset_file(msvc, filename):
437 toolset_version = None
438 try:
439 with open(filename) as f:
440 toolset_version = f.readlines()[0].strip()
441 debug(
442 'msvc_version=%s, filename=%s, toolset_version=%s',
443 repr(msvc.version), repr(filename), repr(toolset_version)
445 except OSError:
446 debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
447 except IndexError:
448 debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename))
449 return toolset_version
451 def _msvc_sxs_toolset_folder(msvc, sxs_folder):
453 if Util.is_toolset_sxs(sxs_folder):
454 return sxs_folder, sxs_folder
456 for vc_buildseries_def in msvc.vs_def.vc_buildtools_def.vc_buildseries_list:
457 key = (vc_buildseries_def.vc_version, sxs_folder)
458 sxs_version = _msvc_sxs_bugfix_folder.get(key)
459 if sxs_version:
460 return sxs_folder, sxs_version
462 debug('sxs folder: ignore version=%s', repr(sxs_folder))
463 return None, None
465 def _msvc_read_toolset_folders(msvc, vc_dir):
467 toolsets_sxs = {}
468 toolsets_full = []
470 build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
471 if os.path.exists(build_dir):
472 for sxs_folder, sxs_path in Util.listdir_dirs(build_dir):
473 sxs_folder, sxs_version = _msvc_sxs_toolset_folder(msvc, sxs_folder)
474 if not sxs_version:
475 continue
476 filename = f'Microsoft.VCToolsVersion.{sxs_folder}.txt'
477 filepath = os.path.join(sxs_path, filename)
478 debug('sxs toolset: check file=%s', repr(filepath))
479 if os.path.exists(filepath):
480 toolset_version = _msvc_read_toolset_file(msvc, filepath)
481 if not toolset_version:
482 continue
483 toolsets_sxs[sxs_version] = toolset_version
484 debug(
485 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
486 repr(msvc.version), repr(sxs_version), repr(toolset_version)
489 toolset_dir = os.path.join(vc_dir, "Tools", "MSVC")
490 if os.path.exists(toolset_dir):
491 for toolset_version, toolset_path in Util.listdir_dirs(toolset_dir):
492 binpath = os.path.join(toolset_path, "bin")
493 debug('toolset: check binpath=%s', repr(binpath))
494 if os.path.exists(binpath):
495 toolsets_full.append(toolset_version)
496 debug(
497 'toolset: msvc_version=%s, toolset_version=%s',
498 repr(msvc.version), repr(toolset_version)
501 vcvars140 = os.path.join(vc_dir, "..", "Common7", "Tools", "vsdevcmd", "ext", "vcvars", "vcvars140.bat")
502 if os.path.exists(vcvars140) and _msvc_have140_toolset():
503 toolset_version = '14.0'
504 toolsets_full.append(toolset_version)
505 debug(
506 'toolset: msvc_version=%s, toolset_version=%s',
507 repr(msvc.version), repr(toolset_version)
510 toolsets_full.sort(reverse=True)
512 # SxS bugfix fixup (if necessary)
513 if msvc.version in _msvc_sxs_bugfix_map:
514 for sxs_version, sxs_bugfix in _msvc_sxs_bugfix_map[msvc.version]:
515 if sxs_version in toolsets_sxs:
516 # have SxS version (folder/file mapping exists)
517 continue
518 for toolset_version in toolsets_full:
519 if not toolset_version.startswith(sxs_bugfix):
520 continue
521 debug(
522 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s',
523 repr(msvc.version), repr(sxs_version), repr(toolset_version)
525 # SxS compatible bugfix version (equivalent to toolset search)
526 toolsets_sxs[sxs_version] = toolset_version
527 break
529 debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full))
531 return toolsets_sxs, toolsets_full
533 def _msvc_read_toolset_default(msvc, vc_dir):
535 build_dir = os.path.join(vc_dir, "Auxiliary", "Build")
537 # VS2019+
538 filename = f"Microsoft.VCToolsVersion.{msvc.vs_def.vc_buildtools_def.vc_buildtools}.default.txt"
539 filepath = os.path.join(build_dir, filename)
541 debug('default toolset: check file=%s', repr(filepath))
542 if os.path.exists(filepath):
543 toolset_buildtools = _msvc_read_toolset_file(msvc, filepath)
544 if toolset_buildtools:
545 return toolset_buildtools
547 # VS2017+
548 filename = "Microsoft.VCToolsVersion.default.txt"
549 filepath = os.path.join(build_dir, filename)
551 debug('default toolset: check file=%s', repr(filepath))
552 if os.path.exists(filepath):
553 toolset_default = _msvc_read_toolset_file(msvc, filepath)
554 if toolset_default:
555 return toolset_default
557 return None
559 _toolset_version_cache = {}
560 _toolset_default_cache = {}
562 def _reset_toolset_cache() -> None:
563 global _toolset_version_cache
564 global _toolset_default_cache
565 debug('reset: toolset cache')
566 _toolset_version_cache = {}
567 _toolset_default_cache = {}
569 def _msvc_version_toolsets(msvc, vc_dir):
571 if msvc.version in _toolset_version_cache:
572 toolsets_sxs, toolsets_full = _toolset_version_cache[msvc.version]
573 else:
574 toolsets_sxs, toolsets_full = _msvc_read_toolset_folders(msvc, vc_dir)
575 _toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full
577 return toolsets_sxs, toolsets_full
579 def _msvc_default_toolset(msvc, vc_dir):
581 if msvc.version in _toolset_default_cache:
582 toolset_default = _toolset_default_cache[msvc.version]
583 else:
584 toolset_default = _msvc_read_toolset_default(msvc, vc_dir)
585 _toolset_default_cache[msvc.version] = toolset_default
587 return toolset_default
589 def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
591 toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
593 if toolset_version in toolsets_full:
594 # full toolset version provided
595 toolset_vcvars = toolset_version
596 return toolset_vcvars
598 if Util.is_toolset_sxs(toolset_version):
599 # SxS version provided
600 sxs_version = toolsets_sxs.get(toolset_version, None)
601 if sxs_version and sxs_version in toolsets_full:
602 # SxS full toolset version
603 toolset_vcvars = sxs_version
604 return toolset_vcvars
605 return None
607 for toolset_full in toolsets_full:
608 if toolset_full.startswith(toolset_version):
609 toolset_vcvars = toolset_full
610 return toolset_vcvars
612 return None
614 def _msvc_script_argument_toolset_constraints(msvc, toolset_version):
616 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
617 debug(
618 'invalid: msvc version constraint: %s < %s VS2017',
619 repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
620 repr(VS2017.vc_buildtools_def.msvc_version_numeric)
622 err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
623 repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version)
625 return err_msg
627 toolset_series = Util.get_msvc_version_prefix(toolset_version)
629 if not toolset_series:
630 debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
631 err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
632 repr(toolset_version)
634 return err_msg
636 toolset_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(toolset_series)
637 if not toolset_buildseries_def:
638 debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
639 err_msg = 'MSVC_TOOLSET_VERSION {} build series {} is not supported'.format(
640 repr(toolset_version), repr(toolset_series)
642 return err_msg
644 toolset_buildtools_def = Config.VC_BUILDTOOLS_MAP[toolset_buildseries_def.vc_buildseries]
646 toolset_verstr = toolset_buildtools_def.msvc_version
647 toolset_vernum = toolset_buildtools_def.msvc_version_numeric
649 if toolset_vernum < VS2015.vc_buildtools_def.msvc_version_numeric:
650 debug(
651 'invalid: toolset version constraint: %s < %s VS2015',
652 repr(toolset_vernum), repr(VS2015.vc_buildtools_def.msvc_version_numeric)
654 err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} < {} VS2015".format(
655 repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.msvc_version)
657 return err_msg
659 if toolset_vernum > msvc.vs_def.vc_buildtools_def.msvc_version_numeric:
660 debug(
661 'invalid: toolset version constraint: toolset %s > %s msvc',
662 repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric)
664 err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} > {} MSVC_VERSION".format(
665 repr(toolset_version), repr(toolset_verstr), repr(msvc.version)
667 return err_msg
669 if toolset_vernum == VS2015.vc_buildtools_def.msvc_version_numeric:
670 # tooset = 14.0
671 if Util.is_toolset_full(toolset_version):
672 if not Util.is_toolset_140(toolset_version):
673 debug(
674 'invalid: toolset version 14.0 constraint: %s != 14.0',
675 repr(toolset_version)
677 err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} != '14.0'".format(
678 repr(toolset_version), repr(toolset_version)
680 return err_msg
681 return None
683 if Util.is_toolset_full(toolset_version):
684 debug('valid: toolset full: toolset_version=%s', repr(toolset_version))
685 return None
687 if Util.is_toolset_sxs(toolset_version):
688 debug('valid: toolset sxs: toolset_version=%s', repr(toolset_version))
689 return None
691 debug('invalid: method exit: toolset_version=%s', repr(toolset_version))
692 err_msg = f"MSVC_TOOLSET_VERSION ({toolset_version!r}) format is not supported"
693 return err_msg
695 def _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir):
697 err_msg = _msvc_script_argument_toolset_constraints(msvc, toolset_version)
698 if err_msg:
699 raise MSVCArgumentError(err_msg)
701 if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'):
702 new_toolset_version = '14.0'
703 debug(
704 'rewrite toolset_version=%s => toolset_version=%s',
705 repr(toolset_version), repr(new_toolset_version)
707 toolset_version = new_toolset_version
709 toolset_vcvars = _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version)
710 debug(
711 'toolset: toolset_version=%s, toolset_vcvars=%s',
712 repr(toolset_version), repr(toolset_vcvars)
715 if not toolset_vcvars:
716 err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format(
717 repr(toolset_version), repr(msvc.version)
719 raise MSVCToolsetVersionNotFound(err_msg)
721 return toolset_vcvars
723 def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist):
725 toolset_version = env['MSVC_TOOLSET_VERSION']
726 debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version))
728 if not toolset_version:
729 return None
731 toolset_vcvars = _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir)
733 # toolset may not be installed for host/target
734 argpair = (SortOrder.TOOLSET, f'-vcvars_ver={toolset_vcvars}')
735 arglist.append(argpair)
737 return toolset_vcvars
739 def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset: bool=False):
741 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
742 return None
744 toolset_default = _msvc_default_toolset(msvc, vc_dir)
745 if not toolset_default:
746 return None
748 debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default))
750 if force_toolset:
751 argpair = (SortOrder.TOOLSET, f'-vcvars_ver={toolset_default}')
752 arglist.append(argpair)
754 return toolset_default
756 def _user_script_argument_toolset(env, toolset_version, user_argstr):
758 matches = [m for m in re_vcvars_toolset.finditer(user_argstr)]
759 if not matches:
760 return None
762 if len(matches) > 1:
763 debug('multiple toolset version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
764 err_msg = f"multiple toolset version declarations: MSVC_SCRIPT_ARGS={user_argstr!r}"
765 raise MSVCArgumentError(err_msg)
767 if not toolset_version:
768 user_toolset = matches[0].group('toolset')
769 return user_toolset
771 env_argstr = env.get('MSVC_TOOLSET_VERSION','')
772 debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
774 err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format(
775 repr(env_argstr), repr(user_argstr)
778 raise MSVCArgumentError(err_msg)
780 def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def):
782 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
783 debug(
784 'invalid: msvc version constraint: %s < %s VS2017',
785 repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
786 repr(VS2017.vc_buildtools_def.msvc_version_numeric)
788 err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
789 repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version)
791 return err_msg
793 if toolset:
794 if toolset.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
795 debug(
796 'invalid: toolset version constraint: %s < %s VS2017',
797 repr(toolset.vc_buildtools_def.msvc_version_numeric),
798 repr(VS2017.vc_buildtools_def.msvc_version_numeric)
800 err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format(
801 repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.msvc_version)
803 return err_msg
806 if platform_def.is_uwp:
807 debug(
808 'invalid: spectre_libs=%s and platform_type=%s',
809 repr(spectre_libs), repr(platform_def.vc_platform)
811 err_msg = "MSVC_SPECTRE_LIBS ({}) are not supported for platform type ({})".format(
812 repr(spectre_libs), repr(platform_def.vc_platform)
814 return err_msg
816 return None
818 def _msvc_toolset_version_spectre_path(vc_dir, toolset_version):
819 spectre_dir = os.path.join(vc_dir, "Tools", "MSVC", toolset_version, "lib", "spectre")
820 return spectre_dir
822 def _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist):
824 spectre_libs = env['MSVC_SPECTRE_LIBS']
825 debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs))
827 if not spectre_libs:
828 return None
830 if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE:
831 return None
833 err_msg = _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def)
834 if err_msg:
835 raise MSVCArgumentError(err_msg)
837 if toolset:
838 spectre_dir = _msvc_toolset_version_spectre_path(vc_dir, toolset.version)
839 if not os.path.exists(spectre_dir):
840 debug(
841 'spectre libs: msvc_version=%s, toolset_version=%s, spectre_dir=%s',
842 repr(msvc.version), repr(toolset.version), repr(spectre_dir)
844 err_msg = "Spectre libraries not found for MSVC_VERSION {} toolset version {}".format(
845 repr(msvc.version), repr(toolset.version)
847 raise MSVCSpectreLibsNotFound(err_msg)
849 spectre_arg = 'spectre'
851 # spectre libs may not be installed for host/target
852 argpair = (SortOrder.SPECTRE, f'-vcvars_spectre_libs={spectre_arg}')
853 arglist.append(argpair)
855 return spectre_arg
857 def _user_script_argument_spectre(env, spectre, user_argstr):
859 matches = [m for m in re_vcvars_spectre.finditer(user_argstr)]
860 if not matches:
861 return None
863 if len(matches) > 1:
864 debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr))
865 err_msg = f"multiple spectre declarations: MSVC_SCRIPT_ARGS={user_argstr!r}"
866 raise MSVCArgumentError(err_msg)
868 if not spectre:
869 return None
871 env_argstr = env.get('MSVC_SPECTRE_LIBS','')
872 debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr))
874 err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format(
875 repr(env_argstr), repr(user_argstr)
878 raise MSVCArgumentError(err_msg)
880 def _msvc_script_argument_user(env, msvc, arglist):
882 # subst None -> empty string
883 script_args = env.subst('$MSVC_SCRIPT_ARGS')
884 debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args))
886 if not script_args:
887 return None
889 if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
890 debug(
891 'invalid: msvc version constraint: %s < %s VS2015',
892 repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
893 repr(VS2015.vc_buildtools_def.msvc_version_numeric)
895 err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
896 repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
898 raise MSVCArgumentError(err_msg)
900 # user arguments are not validated
901 argpair = (SortOrder.USER, script_args)
902 arglist.append(argpair)
904 return script_args
906 def _msvc_process_construction_variables(env) -> bool:
908 for cache_variable in [
909 _MSVC_FORCE_DEFAULT_TOOLSET,
910 _MSVC_FORCE_DEFAULT_SDK,
912 if cache_variable:
913 return True
915 for env_variable in [
916 'MSVC_UWP_APP',
917 'MSVC_TOOLSET_VERSION',
918 'MSVC_SDK_VERSION',
919 'MSVC_SPECTRE_LIBS',
921 if env.get(env_variable, None) is not None:
922 return True
924 return False
926 def msvc_script_arguments_has_uwp(env):
928 if not _msvc_process_construction_variables(env):
929 return False
931 uwp_app = env.get('MSVC_UWP_APP')
932 is_uwp = bool(uwp_app and uwp_app in _ARGUMENT_BOOLEAN_TRUE_LEGACY)
934 debug('is_uwp=%s', is_uwp)
935 return is_uwp
937 def msvc_script_arguments(env, version, vc_dir, arg=None):
939 arguments = [arg] if arg else []
941 arglist = []
942 arglist_reverse = False
944 msvc = _msvc_version(version)
946 if 'MSVC_SCRIPT_ARGS' in env:
947 user_argstr = _msvc_script_argument_user(env, msvc, arglist)
948 else:
949 user_argstr = None
951 if _msvc_process_construction_variables(env):
953 target_arch = env.get('TARGET_ARCH')
955 # MSVC_UWP_APP
957 if 'MSVC_UWP_APP' in env:
958 uwp = _msvc_script_argument_uwp(env, msvc, arglist, target_arch)
959 else:
960 uwp = None
962 if user_argstr:
963 user_uwp = _user_script_argument_uwp(env, uwp, user_argstr)
964 else:
965 user_uwp = None
967 is_uwp = True if uwp else False
968 platform_def = WinSDK.get_msvc_platform(is_uwp)
970 # MSVC_TOOLSET_VERSION
972 if 'MSVC_TOOLSET_VERSION' in env:
973 toolset_version = _msvc_script_argument_toolset(env, msvc, vc_dir, arglist)
974 else:
975 toolset_version = None
977 if user_argstr:
978 user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr)
979 else:
980 user_toolset = None
982 if not toolset_version and not user_toolset:
983 default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist, _MSVC_FORCE_DEFAULT_TOOLSET)
984 if _MSVC_FORCE_DEFAULT_TOOLSET:
985 toolset_version = default_toolset
986 else:
987 default_toolset = None
989 if user_toolset:
990 toolset = None
991 elif toolset_version:
992 toolset = _toolset_version(toolset_version, is_user=True)
993 elif default_toolset:
994 toolset = _toolset_version(default_toolset)
995 else:
996 toolset = None
998 # MSVC_SDK_VERSION
1000 if 'MSVC_SDK_VERSION' in env:
1001 sdk_version = _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist)
1002 else:
1003 sdk_version = None
1005 if user_argstr:
1006 user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr)
1007 else:
1008 user_sdk = None
1010 if _MSVC_FORCE_DEFAULT_SDK:
1011 if not sdk_version and not user_sdk:
1012 sdk_version = _msvc_script_default_sdk(env, msvc, platform_def, arglist, _MSVC_FORCE_DEFAULT_SDK)
1014 # MSVC_SPECTRE_LIBS
1016 if 'MSVC_SPECTRE_LIBS' in env:
1017 spectre = _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist)
1018 else:
1019 spectre = None
1021 if user_argstr:
1022 _user_script_argument_spectre(env, spectre, user_argstr)
1024 if msvc.vs_def.vc_buildtools_def.msvc_version == '14.0':
1025 if user_uwp and sdk_version and len(arglist) == 2:
1026 # VS2015 toolset argument order issue: SDK store => store SDK
1027 arglist_reverse = True
1029 if len(arglist) > 1:
1030 arglist.sort()
1031 if arglist_reverse:
1032 arglist.reverse()
1034 arguments.extend([argpair[-1] for argpair in arglist])
1035 argstr = ' '.join(arguments).strip()
1037 debug('arguments: %s', repr(argstr))
1038 return argstr
1040 def _msvc_toolset_internal(msvc_version, toolset_version, vc_dir):
1042 msvc = _msvc_version(msvc_version)
1044 toolset_vcvars = _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir)
1046 return toolset_vcvars
1048 def _msvc_toolset_versions_internal(msvc_version, vc_dir, full: bool=True, sxs: bool=False):
1050 msvc = _msvc_version(msvc_version)
1052 if len(msvc.vs_def.vc_buildtools_all) <= 1:
1053 return None
1055 toolset_versions = []
1057 toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
1059 if sxs:
1060 sxs_versions = list(toolsets_sxs.keys())
1061 sxs_versions.sort(reverse=True)
1062 toolset_versions.extend(sxs_versions)
1064 if full:
1065 toolset_versions.extend(toolsets_full)
1067 return toolset_versions
1069 def _msvc_version_toolsets_internal(msvc_version, vc_dir):
1071 msvc = _msvc_version(msvc_version)
1073 toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
1075 return toolsets_sxs, toolsets_full
1077 def _msvc_toolset_versions_spectre_internal(msvc_version, vc_dir):
1079 msvc = _msvc_version(msvc_version)
1081 if len(msvc.vs_def.vc_buildtools_all) <= 1:
1082 return None
1084 _, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
1086 spectre_toolset_versions = [
1087 toolset_version
1088 for toolset_version in toolsets_full
1089 if os.path.exists(_msvc_toolset_version_spectre_path(vc_dir, toolset_version))
1092 return spectre_toolset_versions
1094 def reset() -> None:
1095 debug('')
1096 _reset_have140_cache()
1097 _reset_toolset_cache()
1099 def verify() -> None:
1100 debug('')
1101 _verify_re_sdk_dispatch_map()