# MIT License # # Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ Batch file argument functions for Microsoft Visual C/C++. """ import os import re import enum from collections import ( namedtuple, ) from ..common import ( CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS, debug, ) from . import Util from . import Config from . import Registry from . import WinSDK from . import Kind from .Exceptions import ( MSVCInternalError, MSVCSDKVersionNotFound, MSVCToolsetVersionNotFound, MSVCSpectreLibsNotFound, MSVCArgumentError, ) from . import Dispatcher Dispatcher.register_modulename(__name__) # Script argument: boolean True _ARGUMENT_BOOLEAN_TRUE_LEGACY = (True, '1') # MSVC_UWP_APP _ARGUMENT_BOOLEAN_TRUE = (True,) # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last? re_sdk_version_100 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$') re_sdk_version_81 = re.compile(r'^8[.]1$') re_sdk_dispatch_map = { '10.0': re_sdk_version_100, '8.1': re_sdk_version_81, } def _verify_re_sdk_dispatch_map(): debug('') for sdk_version in Config.MSVC_SDK_VERSIONS: if sdk_version in re_sdk_dispatch_map: continue err_msg = f'sdk version {sdk_version} not in re_sdk_dispatch_map' raise MSVCInternalError(err_msg) return None # SxS version bugfix _msvc_sxs_bugfix_map = {} _msvc_sxs_bugfix_folder = {} _msvc_sxs_bugfix_version = {} for msvc_version, sxs_version, sxs_bugfix in [ # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526 # special handling of the 16.8 SxS toolset, use VC\Auxiliary\Build\14.28 directory and SxS files # if SxS version 14.28 not present/installed, fallback selection of toolset VC\Tools\MSVC\14.28.nnnnn. ('14.2', '14.28.16.8', '14.28') ]: _msvc_sxs_bugfix_map.setdefault(msvc_version, []).append((sxs_version, sxs_bugfix)) _msvc_sxs_bugfix_folder[(msvc_version, sxs_bugfix)] = sxs_version _msvc_sxs_bugfix_version[(msvc_version, sxs_version)] = sxs_bugfix # MSVC_SCRIPT_ARGS re_vcvars_uwp = re.compile(r'(?:(?(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE) re_vcvars_sdk = re.compile(r'(?:(?(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE) re_vcvars_toolset = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:(?!\S)|$)', re.IGNORECASE) re_vcvars_spectre = re.compile(r'(?:(?(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:(?!\S)|$)',re.IGNORECASE) # Force default sdk argument _MSVC_FORCE_DEFAULT_SDK = False # Force default toolset argument _MSVC_FORCE_DEFAULT_TOOLSET = False # Force default arguments _MSVC_FORCE_DEFAULT_ARGUMENTS = False def _msvc_force_default_sdk(force: bool=True) -> None: global _MSVC_FORCE_DEFAULT_SDK _MSVC_FORCE_DEFAULT_SDK = force debug('_MSVC_FORCE_DEFAULT_SDK=%s', repr(force)) def _msvc_force_default_toolset(force: bool=True) -> None: global _MSVC_FORCE_DEFAULT_TOOLSET _MSVC_FORCE_DEFAULT_TOOLSET = force debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force)) def msvc_force_default_arguments(force=None): global _MSVC_FORCE_DEFAULT_ARGUMENTS prev_policy = _MSVC_FORCE_DEFAULT_ARGUMENTS if force is not None: _MSVC_FORCE_DEFAULT_ARGUMENTS = force _msvc_force_default_sdk(force) _msvc_force_default_toolset(force) return prev_policy if CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS: msvc_force_default_arguments(force=True) # UWP SDK 8.1 and SDK 10: # # https://stackoverflow.com/questions/46659238/build-windows-app-compatible-for-8-1-and-10 # VS2019 - UWP (Except for Win10Mobile) # VS2017 - UWP # VS2015 - UWP, Win8.1 StoreApp, WP8/8.1 StoreApp # VS2013 - Win8/8.1 StoreApp, WP8/8.1 StoreApp # SPECTRE LIBS (msvc documentation): # "There are no versions of Spectre-mitigated libraries for Universal Windows (UWP) apps or # components. App-local deployment of such libraries isn't possible." # MSVC batch file arguments: # # VS2022: UWP, SDK, TOOLSET, SPECTRE # VS2019: UWP, SDK, TOOLSET, SPECTRE # VS2017: UWP, SDK, TOOLSET, SPECTRE # VS2015: UWP, SDK # # MSVC_SCRIPT_ARGS: VS2015+ # # MSVC_UWP_APP: VS2015+ # MSVC_SDK_VERSION: VS2015+ # MSVC_TOOLSET_VERSION: VS2017+ # MSVC_SPECTRE_LIBS: VS2017+ @enum.unique class SortOrder(enum.IntEnum): UWP = 1 # MSVC_UWP_APP SDK = 2 # MSVC_SDK_VERSION TOOLSET = 3 # MSVC_TOOLSET_VERSION SPECTRE = 4 # MSVC_SPECTRE_LIBS USER = 5 # MSVC_SCRIPT_ARGS VS2019 = Config.MSVS_VERSION_INTERNAL['2019'] VS2017 = Config.MSVS_VERSION_INTERNAL['2017'] VS2015 = Config.MSVS_VERSION_INTERNAL['2015'] MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [ 'version', # full version (e.g., '14.1Exp', '14.32.31326') 'vs_def', ]) TOOLSET_VERSION_ARGS_DEFINITION = namedtuple('ToolsetVersionArgsDefinition', [ 'version', # full version (e.g., '14.1Exp', '14.32.31326') 'vc_buildtools_def', 'is_user', ]) def _msvc_version(version): verstr = Util.get_msvc_version_prefix(version) vs_def = Config.MSVC_VERSION_INTERNAL[verstr] version_args = MSVC_VERSION_ARGS_DEFINITION( version = version, vs_def = vs_def, ) return version_args def _toolset_version(version, is_user=False): vc_series = Util.get_msvc_version_prefix(version) vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL[vc_series] vc_buildtools_def = Config.VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries] version_args = TOOLSET_VERSION_ARGS_DEFINITION( version = version, vc_buildtools_def = vc_buildtools_def, is_user = is_user, ) return version_args def _msvc_script_argument_uwp(env, msvc, arglist, target_arch): uwp_app = env['MSVC_UWP_APP'] debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app)) if not uwp_app: return None if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY: return None if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2015', repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) raise MSVCArgumentError(err_msg) is_supported, is_target = Kind.msvc_version_uwp_is_supported(msvc.version, target_arch, env) if not is_supported: _, kind_str = Kind.get_msvc_version_kind(msvc.version) debug( 'invalid: msvc_version constraint: %s %s %s', repr(msvc.version), repr(kind_str), repr(target_arch) ) if is_target and target_arch: err_msg = "MSVC_UWP_APP ({}) TARGET_ARCH ({}) is not supported for MSVC_VERSION {} ({})".format( repr(uwp_app), repr(target_arch), repr(msvc.version), repr(kind_str) ) else: err_msg = "MSVC_UWP_APP ({}) is not supported for MSVC_VERSION {} ({})".format( repr(uwp_app), repr(msvc.version), repr(kind_str) ) raise MSVCArgumentError(err_msg) # VS2017+ rewrites uwp => store for 14.0 toolset uwp_arg = msvc.vs_def.vc_uwp # store/uwp may not be fully installed argpair = (SortOrder.UWP, uwp_arg) arglist.append(argpair) return uwp_arg def _user_script_argument_uwp(env, uwp, user_argstr) -> bool: matches = [m for m in re_vcvars_uwp.finditer(user_argstr)] if not matches: return False if len(matches) > 1: debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) err_msg = f"multiple uwp declarations: MSVC_SCRIPT_ARGS={user_argstr!r}" raise MSVCArgumentError(err_msg) if not uwp: return True env_argstr = env.get('MSVC_UWP_APP','') debug('multiple uwp declarations: MSVC_UWP_APP=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr)) err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format( repr(env_argstr), repr(user_argstr) ) raise MSVCArgumentError(err_msg) def _msvc_script_argument_sdk_constraints(msvc, sdk_version, env): if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc_version constraint: %s < %s VS2015', repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) return err_msg if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env): _, kind_str = Kind.get_msvc_version_kind(msvc.version) debug('invalid: msvc_version constraint: %s %s', repr(msvc.version), repr(kind_str)) err_msg = "MSVC_SDK_VERSION ({}) is not supported for MSVC_VERSION {} ({})".format( repr(sdk_version), repr(msvc.version), repr(kind_str) ) return err_msg for msvc_sdk_version in msvc.vs_def.vc_sdk_versions: re_sdk_version = re_sdk_dispatch_map[msvc_sdk_version] if re_sdk_version.match(sdk_version): debug('valid: sdk_version=%s', repr(sdk_version)) return None debug('invalid: method exit: sdk_version=%s', repr(sdk_version)) err_msg = f"MSVC_SDK_VERSION ({sdk_version!r}) is not supported" return err_msg def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def): if sdk_version == '8.1' and platform_def.is_uwp: vc_buildtools_def = toolset.vc_buildtools_def if toolset else msvc.vs_def.vc_buildtools_def if vc_buildtools_def.msvc_version_numeric > VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015', repr(vc_buildtools_def.msvc_version_numeric), repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) if toolset and toolset.is_user: err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset {} MSVC_VERSION {} > {} VS2015".format( repr(sdk_version), repr(platform_def.vc_platform), repr(toolset.version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) else: err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format( repr(sdk_version), repr(platform_def.vc_platform), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) return err_msg return None def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist): sdk_version = env['MSVC_SDK_VERSION'] debug( 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s', repr(msvc.version), repr(sdk_version), repr(platform_def.vc_platform) ) if not sdk_version: return None err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version, env) if err_msg: raise MSVCArgumentError(err_msg) sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def) if sdk_version not in sdk_list: err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format( repr(sdk_version), repr(platform_def.vc_platform) ) raise MSVCSDKVersionNotFound(err_msg) err_msg = _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def) if err_msg: raise MSVCArgumentError(err_msg) argpair = (SortOrder.SDK, sdk_version) arglist.append(argpair) return sdk_version def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk: bool=False): if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: return None if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env): return None sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def) if not len(sdk_list): return None sdk_default = sdk_list[0] debug( 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s', repr(msvc.version), repr(sdk_default), repr(platform_def.vc_platform) ) if force_sdk: argpair = (SortOrder.SDK, sdk_default) arglist.append(argpair) return sdk_default def _user_script_argument_sdk(env, sdk_version, user_argstr): matches = [m for m in re_vcvars_sdk.finditer(user_argstr)] if not matches: return None if len(matches) > 1: debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) err_msg = f"multiple sdk version declarations: MSVC_SCRIPT_ARGS={user_argstr!r}" raise MSVCArgumentError(err_msg) if not sdk_version: user_sdk = matches[0].group('sdk') return user_sdk env_argstr = env.get('MSVC_SDK_VERSION','') debug('multiple sdk version declarations: MSVC_SDK_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr)) err_msg = "multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}".format( repr(env_argstr), repr(user_argstr) ) raise MSVCArgumentError(err_msg) _toolset_have140_cache = None def _msvc_have140_toolset(): global _toolset_have140_cache if _toolset_have140_cache is None: suffix = Registry.vstudio_sxs_vc7('14.0') vcinstalldirs = [record[0] for record in Registry.microsoft_query_paths(suffix)] debug('vc140 toolset: paths=%s', repr(vcinstalldirs)) _toolset_have140_cache = True if vcinstalldirs else False return _toolset_have140_cache def _reset_have140_cache() -> None: global _toolset_have140_cache debug('reset: cache') _toolset_have140_cache = None def _msvc_read_toolset_file(msvc, filename): toolset_version = None try: with open(filename) as f: toolset_version = f.readlines()[0].strip() debug( 'msvc_version=%s, filename=%s, toolset_version=%s', repr(msvc.version), repr(filename), repr(toolset_version) ) except OSError: debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) except IndexError: debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) return toolset_version def _msvc_sxs_toolset_folder(msvc, sxs_folder): if Util.is_toolset_sxs(sxs_folder): return sxs_folder, sxs_folder for vc_buildseries_def in msvc.vs_def.vc_buildtools_def.vc_buildseries_list: key = (vc_buildseries_def.vc_version, sxs_folder) sxs_version = _msvc_sxs_bugfix_folder.get(key) if sxs_version: return sxs_folder, sxs_version debug('sxs folder: ignore version=%s', repr(sxs_folder)) return None, None def _msvc_read_toolset_folders(msvc, vc_dir): toolsets_sxs = {} toolsets_full = [] build_dir = os.path.join(vc_dir, "Auxiliary", "Build") if os.path.exists(build_dir): for sxs_folder, sxs_path in Util.listdir_dirs(build_dir): sxs_folder, sxs_version = _msvc_sxs_toolset_folder(msvc, sxs_folder) if not sxs_version: continue filename = f'Microsoft.VCToolsVersion.{sxs_folder}.txt' filepath = os.path.join(sxs_path, filename) debug('sxs toolset: check file=%s', repr(filepath)) if os.path.exists(filepath): toolset_version = _msvc_read_toolset_file(msvc, filepath) if not toolset_version: continue toolsets_sxs[sxs_version] = toolset_version debug( 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', repr(msvc.version), repr(sxs_version), repr(toolset_version) ) toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") if os.path.exists(toolset_dir): for toolset_version, toolset_path in Util.listdir_dirs(toolset_dir): binpath = os.path.join(toolset_path, "bin") debug('toolset: check binpath=%s', repr(binpath)) if os.path.exists(binpath): toolsets_full.append(toolset_version) debug( 'toolset: msvc_version=%s, toolset_version=%s', repr(msvc.version), repr(toolset_version) ) vcvars140 = os.path.join(vc_dir, "..", "Common7", "Tools", "vsdevcmd", "ext", "vcvars", "vcvars140.bat") if os.path.exists(vcvars140) and _msvc_have140_toolset(): toolset_version = '14.0' toolsets_full.append(toolset_version) debug( 'toolset: msvc_version=%s, toolset_version=%s', repr(msvc.version), repr(toolset_version) ) toolsets_full.sort(reverse=True) # SxS bugfix fixup (if necessary) if msvc.version in _msvc_sxs_bugfix_map: for sxs_version, sxs_bugfix in _msvc_sxs_bugfix_map[msvc.version]: if sxs_version in toolsets_sxs: # have SxS version (folder/file mapping exists) continue for toolset_version in toolsets_full: if not toolset_version.startswith(sxs_bugfix): continue debug( 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', repr(msvc.version), repr(sxs_version), repr(toolset_version) ) # SxS compatible bugfix version (equivalent to toolset search) toolsets_sxs[sxs_version] = toolset_version break debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full)) return toolsets_sxs, toolsets_full def _msvc_read_toolset_default(msvc, vc_dir): build_dir = os.path.join(vc_dir, "Auxiliary", "Build") # VS2019+ filename = f"Microsoft.VCToolsVersion.{msvc.vs_def.vc_buildtools_def.vc_buildtools}.default.txt" filepath = os.path.join(build_dir, filename) debug('default toolset: check file=%s', repr(filepath)) if os.path.exists(filepath): toolset_buildtools = _msvc_read_toolset_file(msvc, filepath) if toolset_buildtools: return toolset_buildtools # VS2017+ filename = "Microsoft.VCToolsVersion.default.txt" filepath = os.path.join(build_dir, filename) debug('default toolset: check file=%s', repr(filepath)) if os.path.exists(filepath): toolset_default = _msvc_read_toolset_file(msvc, filepath) if toolset_default: return toolset_default return None _toolset_version_cache = {} _toolset_default_cache = {} def _reset_toolset_cache() -> None: global _toolset_version_cache global _toolset_default_cache debug('reset: toolset cache') _toolset_version_cache = {} _toolset_default_cache = {} def _msvc_version_toolsets(msvc, vc_dir): if msvc.version in _toolset_version_cache: toolsets_sxs, toolsets_full = _toolset_version_cache[msvc.version] else: toolsets_sxs, toolsets_full = _msvc_read_toolset_folders(msvc, vc_dir) _toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full return toolsets_sxs, toolsets_full def _msvc_default_toolset(msvc, vc_dir): if msvc.version in _toolset_default_cache: toolset_default = _toolset_default_cache[msvc.version] else: toolset_default = _msvc_read_toolset_default(msvc, vc_dir) _toolset_default_cache[msvc.version] = toolset_default return toolset_default def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) if toolset_version in toolsets_full: # full toolset version provided toolset_vcvars = toolset_version return toolset_vcvars if Util.is_toolset_sxs(toolset_version): # SxS version provided sxs_version = toolsets_sxs.get(toolset_version, None) if sxs_version and sxs_version in toolsets_full: # SxS full toolset version toolset_vcvars = sxs_version return toolset_vcvars return None for toolset_full in toolsets_full: if toolset_full.startswith(toolset_version): toolset_vcvars = toolset_full return toolset_vcvars return None def _msvc_script_argument_toolset_constraints(msvc, toolset_version): if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2017', repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), repr(VS2017.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version) ) return err_msg toolset_series = Util.get_msvc_version_prefix(toolset_version) if not toolset_series: debug('invalid: msvc version: toolset_version=%s', repr(toolset_version)) err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( repr(toolset_version) ) return err_msg toolset_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(toolset_series) if not toolset_buildseries_def: debug('invalid: msvc version: toolset_version=%s', repr(toolset_version)) err_msg = 'MSVC_TOOLSET_VERSION {} build series {} is not supported'.format( repr(toolset_version), repr(toolset_series) ) return err_msg toolset_buildtools_def = Config.VC_BUILDTOOLS_MAP[toolset_buildseries_def.vc_buildseries] toolset_verstr = toolset_buildtools_def.msvc_version toolset_vernum = toolset_buildtools_def.msvc_version_numeric if toolset_vernum < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: toolset version constraint: %s < %s VS2015', repr(toolset_vernum), repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} < {} VS2015".format( repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.msvc_version) ) return err_msg if toolset_vernum > msvc.vs_def.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: toolset version constraint: toolset %s > %s msvc', repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} > {} MSVC_VERSION".format( repr(toolset_version), repr(toolset_verstr), repr(msvc.version) ) return err_msg if toolset_vernum == VS2015.vc_buildtools_def.msvc_version_numeric: # tooset = 14.0 if Util.is_toolset_full(toolset_version): if not Util.is_toolset_140(toolset_version): debug( 'invalid: toolset version 14.0 constraint: %s != 14.0', repr(toolset_version) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} != '14.0'".format( repr(toolset_version), repr(toolset_version) ) return err_msg return None if Util.is_toolset_full(toolset_version): debug('valid: toolset full: toolset_version=%s', repr(toolset_version)) return None if Util.is_toolset_sxs(toolset_version): debug('valid: toolset sxs: toolset_version=%s', repr(toolset_version)) return None debug('invalid: method exit: toolset_version=%s', repr(toolset_version)) err_msg = f"MSVC_TOOLSET_VERSION ({toolset_version!r}) format is not supported" return err_msg def _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir): err_msg = _msvc_script_argument_toolset_constraints(msvc, toolset_version) if err_msg: raise MSVCArgumentError(err_msg) if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'): new_toolset_version = '14.0' debug( 'rewrite toolset_version=%s => toolset_version=%s', repr(toolset_version), repr(new_toolset_version) ) toolset_version = new_toolset_version toolset_vcvars = _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version) debug( 'toolset: toolset_version=%s, toolset_vcvars=%s', repr(toolset_version), repr(toolset_vcvars) ) if not toolset_vcvars: err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format( repr(toolset_version), repr(msvc.version) ) raise MSVCToolsetVersionNotFound(err_msg) return toolset_vcvars def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist): toolset_version = env['MSVC_TOOLSET_VERSION'] debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version)) if not toolset_version: return None toolset_vcvars = _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir) # toolset may not be installed for host/target argpair = (SortOrder.TOOLSET, f'-vcvars_ver={toolset_vcvars}') arglist.append(argpair) return toolset_vcvars def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset: bool=False): if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: return None toolset_default = _msvc_default_toolset(msvc, vc_dir) if not toolset_default: return None debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default)) if force_toolset: argpair = (SortOrder.TOOLSET, f'-vcvars_ver={toolset_default}') arglist.append(argpair) return toolset_default def _user_script_argument_toolset(env, toolset_version, user_argstr): matches = [m for m in re_vcvars_toolset.finditer(user_argstr)] if not matches: return None if len(matches) > 1: debug('multiple toolset version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) err_msg = f"multiple toolset version declarations: MSVC_SCRIPT_ARGS={user_argstr!r}" raise MSVCArgumentError(err_msg) if not toolset_version: user_toolset = matches[0].group('toolset') return user_toolset env_argstr = env.get('MSVC_TOOLSET_VERSION','') debug('multiple toolset version declarations: MSVC_TOOLSET_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr)) err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format( repr(env_argstr), repr(user_argstr) ) raise MSVCArgumentError(err_msg) def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def): if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2017', repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), repr(VS2017.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version) ) return err_msg if toolset: if toolset.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: toolset version constraint: %s < %s VS2017', repr(toolset.vc_buildtools_def.msvc_version_numeric), repr(VS2017.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format( repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.msvc_version) ) return err_msg if platform_def.is_uwp: debug( 'invalid: spectre_libs=%s and platform_type=%s', repr(spectre_libs), repr(platform_def.vc_platform) ) err_msg = "MSVC_SPECTRE_LIBS ({}) are not supported for platform type ({})".format( repr(spectre_libs), repr(platform_def.vc_platform) ) return err_msg return None def _msvc_toolset_version_spectre_path(vc_dir, toolset_version): spectre_dir = os.path.join(vc_dir, "Tools", "MSVC", toolset_version, "lib", "spectre") return spectre_dir def _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist): spectre_libs = env['MSVC_SPECTRE_LIBS'] debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs)) if not spectre_libs: return None if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE: return None err_msg = _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def) if err_msg: raise MSVCArgumentError(err_msg) if toolset: spectre_dir = _msvc_toolset_version_spectre_path(vc_dir, toolset.version) if not os.path.exists(spectre_dir): debug( 'spectre libs: msvc_version=%s, toolset_version=%s, spectre_dir=%s', repr(msvc.version), repr(toolset.version), repr(spectre_dir) ) err_msg = "Spectre libraries not found for MSVC_VERSION {} toolset version {}".format( repr(msvc.version), repr(toolset.version) ) raise MSVCSpectreLibsNotFound(err_msg) spectre_arg = 'spectre' # spectre libs may not be installed for host/target argpair = (SortOrder.SPECTRE, f'-vcvars_spectre_libs={spectre_arg}') arglist.append(argpair) return spectre_arg def _user_script_argument_spectre(env, spectre, user_argstr): matches = [m for m in re_vcvars_spectre.finditer(user_argstr)] if not matches: return None if len(matches) > 1: debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) err_msg = f"multiple spectre declarations: MSVC_SCRIPT_ARGS={user_argstr!r}" raise MSVCArgumentError(err_msg) if not spectre: return None env_argstr = env.get('MSVC_SPECTRE_LIBS','') debug('multiple spectre declarations: MSVC_SPECTRE_LIBS=%s, MSVC_SCRIPT_ARGS=%s', repr(env_argstr), repr(user_argstr)) err_msg = "multiple spectre declarations: MSVC_SPECTRE_LIBS={} and MSVC_SCRIPT_ARGS={}".format( repr(env_argstr), repr(user_argstr) ) raise MSVCArgumentError(err_msg) def _msvc_script_argument_user(env, msvc, arglist): # subst None -> empty string script_args = env.subst('$MSVC_SCRIPT_ARGS') debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args)) if not script_args: return None if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric: debug( 'invalid: msvc version constraint: %s < %s VS2015', repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric), repr(VS2015.vc_buildtools_def.msvc_version_numeric) ) err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version) ) raise MSVCArgumentError(err_msg) # user arguments are not validated argpair = (SortOrder.USER, script_args) arglist.append(argpair) return script_args def _msvc_process_construction_variables(env) -> bool: for cache_variable in [ _MSVC_FORCE_DEFAULT_TOOLSET, _MSVC_FORCE_DEFAULT_SDK, ]: if cache_variable: return True for env_variable in [ 'MSVC_UWP_APP', 'MSVC_TOOLSET_VERSION', 'MSVC_SDK_VERSION', 'MSVC_SPECTRE_LIBS', ]: if env.get(env_variable, None) is not None: return True return False def msvc_script_arguments_has_uwp(env): if not _msvc_process_construction_variables(env): return False uwp_app = env.get('MSVC_UWP_APP') is_uwp = bool(uwp_app and uwp_app in _ARGUMENT_BOOLEAN_TRUE_LEGACY) debug('is_uwp=%s', is_uwp) return is_uwp def msvc_script_arguments(env, version, vc_dir, arg=None): arguments = [arg] if arg else [] arglist = [] arglist_reverse = False msvc = _msvc_version(version) if 'MSVC_SCRIPT_ARGS' in env: user_argstr = _msvc_script_argument_user(env, msvc, arglist) else: user_argstr = None if _msvc_process_construction_variables(env): target_arch = env.get('TARGET_ARCH') # MSVC_UWP_APP if 'MSVC_UWP_APP' in env: uwp = _msvc_script_argument_uwp(env, msvc, arglist, target_arch) else: uwp = None if user_argstr: user_uwp = _user_script_argument_uwp(env, uwp, user_argstr) else: user_uwp = None is_uwp = True if uwp else False platform_def = WinSDK.get_msvc_platform(is_uwp) # MSVC_TOOLSET_VERSION if 'MSVC_TOOLSET_VERSION' in env: toolset_version = _msvc_script_argument_toolset(env, msvc, vc_dir, arglist) else: toolset_version = None if user_argstr: user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr) else: user_toolset = None if not toolset_version and not user_toolset: default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist, _MSVC_FORCE_DEFAULT_TOOLSET) if _MSVC_FORCE_DEFAULT_TOOLSET: toolset_version = default_toolset else: default_toolset = None if user_toolset: toolset = None elif toolset_version: toolset = _toolset_version(toolset_version, is_user=True) elif default_toolset: toolset = _toolset_version(default_toolset) else: toolset = None # MSVC_SDK_VERSION if 'MSVC_SDK_VERSION' in env: sdk_version = _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist) else: sdk_version = None if user_argstr: user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr) else: user_sdk = None if _MSVC_FORCE_DEFAULT_SDK: if not sdk_version and not user_sdk: sdk_version = _msvc_script_default_sdk(env, msvc, platform_def, arglist, _MSVC_FORCE_DEFAULT_SDK) # MSVC_SPECTRE_LIBS if 'MSVC_SPECTRE_LIBS' in env: spectre = _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, arglist) else: spectre = None if user_argstr: _user_script_argument_spectre(env, spectre, user_argstr) if msvc.vs_def.vc_buildtools_def.msvc_version == '14.0': if user_uwp and sdk_version and len(arglist) == 2: # VS2015 toolset argument order issue: SDK store => store SDK arglist_reverse = True if len(arglist) > 1: arglist.sort() if arglist_reverse: arglist.reverse() arguments.extend([argpair[-1] for argpair in arglist]) argstr = ' '.join(arguments).strip() debug('arguments: %s', repr(argstr)) return argstr def _msvc_toolset_internal(msvc_version, toolset_version, vc_dir): msvc = _msvc_version(msvc_version) toolset_vcvars = _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir) return toolset_vcvars def _msvc_toolset_versions_internal(msvc_version, vc_dir, full: bool=True, sxs: bool=False): msvc = _msvc_version(msvc_version) if len(msvc.vs_def.vc_buildtools_all) <= 1: return None toolset_versions = [] toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) if sxs: sxs_versions = list(toolsets_sxs.keys()) sxs_versions.sort(reverse=True) toolset_versions.extend(sxs_versions) if full: toolset_versions.extend(toolsets_full) return toolset_versions def _msvc_version_toolsets_internal(msvc_version, vc_dir): msvc = _msvc_version(msvc_version) toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) return toolsets_sxs, toolsets_full def _msvc_toolset_versions_spectre_internal(msvc_version, vc_dir): msvc = _msvc_version(msvc_version) if len(msvc.vs_def.vc_buildtools_all) <= 1: return None _, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) spectre_toolset_versions = [ toolset_version for toolset_version in toolsets_full if os.path.exists(_msvc_toolset_version_spectre_path(vc_dir, toolset_version)) ] return spectre_toolset_versions def reset() -> None: debug('') _reset_have140_cache() _reset_toolset_cache() def verify() -> None: debug('') _verify_re_sdk_dispatch_map()