From 3a760ae7934f840a6c029cbac95bdbb74cf11a3e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 12 Jun 2022 13:06:18 -0400 Subject: [PATCH 001/108] Initial support for msvc batch file arguments --- SCons/Tool/MSCommon/vc.py | 491 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 474 insertions(+), 17 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index b542d1571d..ac247d188c 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -91,6 +91,29 @@ class MSVCUseSettingsError(VisualCException): class MSVCVersionNotFound(VisualCException): pass +class MSVCArgumentError(VisualCException): + pass + +class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): + pass + +# VS constants = VS product MSVC version introduced +VS2022 = 14.3 +VS2019 = 14.2 +VS2017 = 14.1 +VS2015 = 14.0 +VS2013 = 12.0 +VS2012 = 11.0 +VS2010 = 10.0 +VS2008 = 9.0 +VS2005 = 8.0 +VS2003 = 7.1 +VS2002 = 7.0 +VS6 = 6.0 + +# Force -vcvars_ver argument for default toolset +_MSVC_TOOLSET_DEFAULT_VCVARSVER = False + # MSVC_NOTFOUND_POLICY definition: # error: raise exception # warning: issue warning and continue @@ -806,11 +829,14 @@ def find_batch_file(env, msvc_version, host_arch, target_arch): vernum = float(msvc_ver_numeric) arg = '' + vcdir = None + if vernum > 14: # 14.1 (VS2017) and later batfiledir = os.path.join(pdir, "Auxiliary", "Build") batfile, _ = _GE2017_HOST_TARGET_BATCHFILE_CLPATHCOMPS[(host_arch, target_arch)] batfilename = os.path.join(batfiledir, batfile) + vcdir = pdir elif 14 >= vernum >= 8: # 14.0 (VS2015) to 8.0 (VS2005) arg, _ = _LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[(host_arch, target_arch)] @@ -833,9 +859,9 @@ def find_batch_file(env, msvc_version, host_arch, target_arch): sdk_bat_file_path = os.path.join(pdir, sdk_bat_file) if os.path.exists(sdk_bat_file_path): debug('sdk_bat_file_path:%s', sdk_bat_file_path) - return (batfilename, arg, sdk_bat_file_path) + return (batfilename, arg, vcdir, sdk_bat_file_path) - return (batfilename, arg, None) + return (batfilename, arg, vcdir, None) __INSTALLED_VCS_RUN = None _VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt'] @@ -994,6 +1020,7 @@ def reset_installed_vcs(): global __INSTALLED_VCS_RUN __INSTALLED_VCS_RUN = None _MSVCSetupEnvDefault.reset() + _MSVCScriptArguments.reset() # Running these batch files isn't cheap: most of the time spent in # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" @@ -1041,16 +1068,39 @@ def script_env(script, args=None): if cache_data is None: stdout = common.get_output(script, args) + cache_data = common.parse_output(stdout) - # Stupid batch files do not set return code: we take a look at the - # beginning of the output for an error message instead olines = stdout.splitlines() - if re_script_output_error.match(olines[0]): - raise BatchFileExecutionError("\n".join(olines[:2])) + #debug(olines) + + # process stdout: batch file errors (not necessarily first line) + script_errlog = [] + for line in olines: + if re_script_output_error.match(line): + if not script_errlog: + script_errlog.append('vc script errors detected:') + script_errlog.append(line) + + if script_errlog: + script_errmsg = '\n'.join(script_errlog) + have_cl = False + if cache_data and 'PATH' in cache_data: + for p in cache_data['PATH']: + if os.path.exists(os.path.join(p, _CL_EXE_NAME)): + have_cl = True + break + if not have_cl: + # detected errors, cl.exe not on path + raise BatchFileExecutionError(script_errmsg) + else: + # detected errors, cl.exe on path + debug('script=%s args=%s errors=%s', repr(script), repr(args), script_errmsg) + # This may be a bad idea (scons environment != vs cmdline environment) + SCons.Warnings.warn(BatchFileExecutionWarning, script_errmsg) + # TODO: errlog/errstr should be added to cache and warning moved to call site - cache_data = common.parse_output(stdout) - script_env_cache[cache_key] = cache_data # once we updated cache, give a chance to write out if user wanted + script_env_cache[cache_key] = cache_data common.write_script_env_cache(script_env_cache) return cache_data @@ -1395,6 +1445,419 @@ def msvc_setup_env_once(env, tool=None): " Requested tool(s) are: {}".format(req_tools) _msvc_notfound_policy_handler(env, msg) +class _MSVCScriptArguments: + + # MSVC batch file arguments: + # + # VS2022: UWP, SDK, TOOLSET, SPECTRE + # VS2019: UWP, SDK, TOOLSET, SPECTRE + # VS2017: UWP, SDK, TOOLSET, SPECTRE + # VS2015: UWP, SDK + # + # MSVC_UWP_APP: VS2015+ + # MSVC_SDK_VERSION: VS2015+ + # MSVC_TOOLSET_VERSION: VS2017+ + # MSVC_SPECTRE_LIBS: VS2017+ + # + # MSVC_SCRIPT_ARGS: VS2015+ + # + + @classmethod + def _msvc_script_argument_uwp(cls, env, version, vernum, arglist): + + uwp_app = env['MSVC_UWP_APP'] + debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(version), repr(uwp_app)) + + if not uwp_app: + return False + + if uwp_app not in (True, '1'): + return False + + if vernum < VS2015: + debug('invalid: msvc_version constraint: msvc_version=%s < VS2015=%s', repr(version), repr(str(VS2015))) + err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(uwp_app), repr(version), repr(str(VS2015)) + ) + raise MSVCArgumentError(err_msg) + + # uwp may not be installed + uwp_arg = 'uwp' if vernum > VS2015 else 'store' + arglist.append(uwp_arg) + return True + + # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last? + re_sdk_version_10 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$') + + @classmethod + def _msvc_script_argument_sdk_constraints(cls, version, msvc_vernum, sdk_version): + + if msvc_vernum < VS2015: + debug('invalid: msvc_version constraint: msvc_version=%s < VS2015=%s', repr(version), repr(str(VS2015))) + err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(sdk_version), repr(version), repr(str(VS2015)) + ) + return err_msg + + if sdk_version == '8.1': + debug('valid: sdk_version=%s', repr(sdk_version)) + return None + + if cls.re_sdk_version_10.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 = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version)) + return err_msg + + @classmethod + def _msvc_script_argument_sdk(cls, env, version, vernum, is_uwp, arglist): + + sdk_version = env['MSVC_SDK_VERSION'] + debug('MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, uwp=%s', repr(version), repr(sdk_version), repr(is_uwp)) + + if not sdk_version: + return False + + err_msg = cls._msvc_script_argument_sdk_constraints(version, vernum, sdk_version) + if err_msg: + raise MSVCArgumentError(err_msg) + + # sdk folder may not exist + arglist.append(sdk_version) + return True + + @classmethod + def _msvc_read_toolset_file(cls, version, 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(version), repr(filename), repr(toolset_version)) + except IOError: + debug('IOError: msvc_version=%s, filename=%s', repr(version), repr(filename)) + except IndexError: + debug('IndexError: msvc_version=%s, filename=%s', repr(version), repr(filename)) + return toolset_version + + @classmethod + def _msvc_read_toolset_folders(cls, version, vc_dir): + + toolsets_sxs = {} + toolsets_full = [] + + build_dir = os.path.join(vc_dir, "Auxiliary", "Build") + sxs_toolsets = [ f.name for f in os.scandir(build_dir) if f.is_dir() ] + for sxs_toolset in sxs_toolsets: + filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) + filepath = os.path.join(build_dir, sxs_toolset, filename) + if os.path.exists(filepath): + toolset_version = cls._msvc_read_toolset_file(version, filepath) + if not toolset_version: + continue + toolsets_sxs[sxs_toolset] = toolset_version + debug('sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', repr(version), repr(sxs_toolset), toolset_version) + + toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") + toolsets = [ f.name for f in os.scandir(toolset_dir) if f.is_dir() ] + for toolset_version in toolsets: + binpath = os.path.join(toolset_dir, toolset_version, "bin") + if os.path.exists(binpath): + toolsets_full.append(toolset_version) + debug('toolset: msvc_version=%s, toolset_version=%s', repr(version), repr(toolset_version)) + + toolsets_full.sort(reverse=True) + debug('msvc_version=%s, toolsets=%s', repr(version), repr(toolsets_full)) + + return toolsets_sxs, toolsets_full + + @classmethod + def _msvc_read_toolset_default(cls, version, vc_dir): + + build_dir = os.path.join(vc_dir, "Auxiliary", "Build") + + version_numeric = get_msvc_version_numeric(version) + + # VS2019+ + buildtools_version = ''.join(version_numeric.split('.')) + filename = "Microsoft.VCToolsVersion.v{}.default.txt".format(buildtools_version) + filepath = os.path.join(build_dir, filename) + + toolset_buildtools = None + if os.path.exists(filepath): + toolset_buildtools = cls._msvc_read_toolset_file(version, filepath) + if toolset_buildtools: + return toolset_buildtools + + # VS2017+ + filename = "Microsoft.VCToolsVersion.default.txt" + filepath = os.path.join(build_dir, filename) + + toolset_default = None + if os.path.exists(filepath): + toolset_default = cls._msvc_read_toolset_file(version, filepath) + if toolset_default: + return toolset_default + + return None + + @classmethod + def _reset_toolset(cls): + debug('reset: toolset cache') + cls._toolset_version_cache = {} + cls._toolset_default_cache = {} + + _toolset_version_cache = {} + _toolset_default_cache = {} + + @classmethod + def _msvc_version_toolsets(cls, version, vc_dir): + + if version in cls._toolset_version_cache: + toolsets_sxs, toolsets_full = cls._toolset_version_cache[version] + else: + toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(version, vc_dir) + cls._toolset_version_cache[version] = toolsets_sxs, toolsets_full + + return toolsets_sxs, toolsets_full + + @classmethod + def _msvc_default_toolset(cls, version, vc_dir): + + if version in cls._toolset_default_cache: + toolset_default = cls._toolset_default_cache[version] + else: + toolset_default = cls._msvc_read_toolset_default(version, vc_dir) + cls._toolset_default_cache[version] = toolset_default + + return toolset_default + + @classmethod + def _msvc_version_toolset_vcvars(cls, version, vernum, vc_dir, toolset_version): + + if toolset_version == '14.0': + return toolset_version + + toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(version, vc_dir) + + if vernum == VS2019 and toolset_version == '14.28.16.8': + # 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. + toolset_version = '14.28' + + if toolset_version in toolsets_sxs: + toolset_vcvars = toolsets_sxs[toolset_version] + return toolset_vcvars + + for toolset_full in toolsets_full: + if toolset_full.startswith(toolset_version): + toolset_vcvars = toolset_full + return toolset_vcvars + + return None + + # capture msvc version + re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) + + re_toolset_full = re.compile(r'''^(?: + (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY + (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ + )$''', re.VERBOSE) + + re_toolset_140 = re.compile(r'''^(?: + (?:14[.]0{1,2})| # 14.0 - 14.00 + (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000 + )$''', re.VERBOSE) + + # valid SxS formats will be matched with re_toolset_full: match 3 '.' format + re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') + + @classmethod + def _msvc_script_argument_toolset_constraints(cls, version, msvc_vernum, toolset_version): + + if msvc_vernum < VS2017: + debug('invalid: msvc_version constraint: msvc_version=%s < VS2017=%s', repr(version), repr(str(VS2017))) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( + repr(toolset_version), repr(version), repr(str(VS2017)) + ) + return err_msg + + m = cls.re_toolset_version.match(toolset_version) + if not m: + debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version)) + err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( + repr(toolset_version) + ) + return err_msg + + toolset_ver = m.group('version') + toolset_vernum = float(toolset_ver) + + if toolset_vernum < VS2015: + debug('invalid: toolset_vernum constraint: toolset_vernum=%s < VS2015=%s', repr(str(toolset_vernum)), repr(str(VS2015))) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( + repr(toolset_version), repr(str(toolset_vernum)), repr(str(VS2015)) + ) + return err_msg + + if toolset_vernum > msvc_vernum: + debug('invalid: toolset_vernum constraint: toolset_vernum=%s > msvc_vernum=%s', repr(str(toolset_vernum)), repr(str(msvc_vernum))) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( + repr(toolset_version), repr(str(toolset_vernum)), repr(version) + ) + return err_msg + + if toolset_vernum == VS2015 and cls.re_toolset_full.match(toolset_version): + if not cls.re_toolset_140.match(toolset_version): + debug('invalid: 14.0 version: toolset_version=%s', repr(toolset_version)) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > '14.0'".format( + repr(toolset_version), repr(toolset_version) + ) + return err_msg + return None + + if cls.re_toolset_full.match(toolset_version): + debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) + return None + + if cls.re_toolset_sxs.match(toolset_version): + debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version)) + return None + + debug('invalid: method exit: toolset_version=%s', repr(toolset_version)) + err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version)) + return err_msg + + @classmethod + def _msvc_script_argument_toolset(cls, env, version, vernum, vc_dir, arglist): + + toolset_version = env['MSVC_TOOLSET_VERSION'] + debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(version), repr(toolset_version)) + + if not toolset_version: + return False + + err_msg = cls._msvc_script_argument_toolset_constraints(version, vernum, toolset_version) + if err_msg: + raise MSVCArgumentError(err_msg) + + if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'): + debug('rewrite 14.0 toolset_version=%s', repr(toolset_version)) + toolset_version = '14.0' + + toolset_vcvars = cls._msvc_version_toolset_vcvars(version, vernum, 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_VERSION {}: MSVC_TOOLSET_VERSION {} not found".format( + repr(version), repr(toolset_version) + ) + raise MSVCArgumentError(err_msg) + + arglist.append('-vcvars_ver={}'.format(toolset_vcvars)) + return True + + @classmethod + def _msvc_script_default_toolset(cls, env, version, vernum, vc_dir, arglist): + + if vernum < VS2017: + return False + + toolset_default = cls._msvc_default_toolset(version, vc_dir) + if not toolset_default: + return False + + debug('MSVC_VERSION=%s, toolset_default=%s', repr(version), repr(toolset_default)) + arglist.append('-vcvars_ver={}'.format(toolset_default)) + return True + + @classmethod + def _msvc_script_argument_spectre(cls, env, version, vernum, arglist): + + spectre_libs = env['MSVC_SPECTRE_LIBS'] + debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(version), repr(spectre_libs)) + + if not spectre_libs: + return False + + if spectre_libs not in (True, '1'): + return False + + if vernum < VS2017: + debug('invalid: msvc_version constraint: msvc_version=%s < VS2017=%s', repr(version), repr(str(VS2017))) + err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( + repr(spectre_libs), repr(version), repr(str(VS2017)) + ) + raise MSVCArgumentError(err_msg) + + # spectre libs may not be installed + arglist.append('-vcvars_spectre_libs=spectre') + return True + + @classmethod + def _msvc_script_argument_user(cls, env, version, vernum, arglist): + + # subst None -> empty string + script_args = env.subst('$MSVC_SCRIPT_ARGS') + debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(version), repr(script_args)) + + if not script_args: + return False + + if vernum < VS2015: + debug('invalid: msvc_version constraint: msvc_version=%s < VS2015=%s', repr(version), repr(str(VS2015))) + err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(script_args), repr(version), repr(str(VS2015)) + ) + raise MSVCArgumentError(err_msg) + + # user arguments are not validated + arglist.append(script_args) + return True + + @classmethod + def msvc_script_arguments(cls, env, version, vc_dir, arg): + + msvc_ver_numeric = get_msvc_version_numeric(version) + vernum = float(msvc_ver_numeric) + + arglist = [arg] + + have_uwp = False + have_sdk = False + have_toolset = False + have_spectre = False + have_user = False + + if 'MSVC_UWP_APP' in env: + have_uwp = cls._msvc_script_argument_uwp(env, version, vernum, arglist) + + if 'MSVC_SDK_VERSION' in env: + have_sdk = cls._msvc_script_argument_sdk(env, version, vernum, have_uwp, arglist) + + if 'MSVC_TOOLSET_VERSION' in env: + have_toolset = cls._msvc_script_argument_toolset(env, version, vernum, vc_dir, arglist) + + if _MSVC_TOOLSET_DEFAULT_VCVARSVER and not have_toolset and vernum >= VS2017: + have_toolset = cls._msvc_script_default_toolset(env, version, vernum, vc_dir, arglist) + + if 'MSVC_SPECTRE_LIBS' in env: + have_spectre = cls._msvc_script_argument_spectre(env, version, vernum, arglist) + + if 'MSVC_SCRIPT_ARGS' in env: + have_user = cls._msvc_script_argument_user(env, version, vernum, arglist) + + argstr = ' '.join(arglist).strip() + debug('arguments: %s', repr(argstr)) + + return argstr + + def reset(cls): + debug('reset') + cl._reset_toolset() + def msvc_find_valid_batch_script(env, version): """Find and execute appropriate batch script to set up build env. @@ -1418,10 +1881,11 @@ def msvc_find_valid_batch_script(env, version): for host_arch, target_arch, in host_target_list: # Set to current arch. env['TARGET_ARCH'] = target_arch + arg = '' # Try to locate a batch file for this host/target platform combo try: - (vc_script, arg, sdk_script) = find_batch_file(env, version, host_arch, target_arch) + (vc_script, arg, vc_dir, sdk_script) = find_batch_file(env, version, host_arch, target_arch) debug('vc_script:%s vc_script_arg:%s sdk_script:%s', vc_script, arg, sdk_script) version_installed = True except VisualCException as e: @@ -1434,14 +1898,7 @@ def msvc_find_valid_batch_script(env, version): debug('use_script 2 %s, args:%s', repr(vc_script), arg) found = None if vc_script: - # Get just version numbers - maj, min = msvc_version_to_maj_min(version) - # VS2015+ - if maj >= 14: - if env.get('MSVC_UWP_APP') == '1': - # Initialize environment variables with store/UWP paths - arg = (arg + ' store').lstrip() - + arg = _MSVCScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) try: d = script_env(vc_script, args=arg) found = vc_script From 49d6e9acd28bf0301efe4ae757615b6042abbcef Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 12 Jun 2022 14:26:55 -0400 Subject: [PATCH 002/108] Fix sider complaints --- SCons/Tool/MSCommon/vc.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index ac247d188c..cfba8166de 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -109,7 +109,7 @@ class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): VS2005 = 8.0 VS2003 = 7.1 VS2002 = 7.0 -VS6 = 6.0 +VS6 = 6.0 # Force -vcvars_ver argument for default toolset _MSVC_TOOLSET_DEFAULT_VCVARSVER = False @@ -1548,7 +1548,7 @@ def _msvc_read_toolset_folders(cls, version, vc_dir): toolsets_full = [] build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - sxs_toolsets = [ f.name for f in os.scandir(build_dir) if f.is_dir() ] + sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()] for sxs_toolset in sxs_toolsets: filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) filepath = os.path.join(build_dir, sxs_toolset, filename) @@ -1560,7 +1560,7 @@ def _msvc_read_toolset_folders(cls, version, vc_dir): debug('sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', repr(version), repr(sxs_toolset), toolset_version) toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") - toolsets = [ f.name for f in os.scandir(toolset_dir) if f.is_dir() ] + toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] for toolset_version in toolsets: binpath = os.path.join(toolset_dir, toolset_version, "bin") if os.path.exists(binpath): @@ -1856,7 +1856,7 @@ def msvc_script_arguments(cls, env, version, vc_dir, arg): def reset(cls): debug('reset') - cl._reset_toolset() + cls._reset_toolset() def msvc_find_valid_batch_script(env, version): """Find and execute appropriate batch script to set up build env. From ef1cf0803b4f69f53f4d9ab6f5d7d21802decaab Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 12 Jun 2022 18:01:51 -0400 Subject: [PATCH 003/108] Convert VS constants to namedtuples. Update debug message content. --- SCons/Tool/MSCommon/vc.py | 108 ++++++++++++++++++++++---------------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index cfba8166de..28aff355c4 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -97,23 +97,42 @@ class MSVCArgumentError(VisualCException): class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): pass -# VS constants = VS product MSVC version introduced -VS2022 = 14.3 -VS2019 = 14.2 -VS2017 = 14.1 -VS2015 = 14.0 -VS2013 = 12.0 -VS2012 = 11.0 -VS2010 = 10.0 -VS2008 = 9.0 -VS2005 = 8.0 -VS2003 = 7.1 -VS2002 = 7.0 -VS6 = 6.0 - # Force -vcvars_ver argument for default toolset _MSVC_TOOLSET_DEFAULT_VCVARSVER = False +# VS constants: VSPRODUCT defined as MSVC_VERSION introduced + +_VS_MSVC_VERSION_DEFINITION = namedtuple('VSMSVCVersionDefinition', [ + 'vernum', + 'symbol', + 'major', + 'minor', +]) + +def _vs_msvc_version(vernum): + symbol = str(vernum) + major, minor = [int(s) for s in symbol.split('.')] + vs_msvc_version = _VS_MSVC_VERSION_DEFINITION( + vernum = vernum, + symbol = symbol, + major = major, + minor = minor + ) + return vs_msvc_version + +VS2022 = _vs_msvc_version(14.3) +VS2019 = _vs_msvc_version(14.2) +VS2017 = _vs_msvc_version(14.1) +VS2015 = _vs_msvc_version(14.0) +VS2013 = _vs_msvc_version(12.0) +VS2012 = _vs_msvc_version(11.0) +VS2010 = _vs_msvc_version(10.0) +VS2008 = _vs_msvc_version(9.0) +VS2005 = _vs_msvc_version(8.0) +VS2003 = _vs_msvc_version(7.1) +VS2002 = _vs_msvc_version(7.0) +VS6 = _vs_msvc_version(6.0) + # MSVC_NOTFOUND_POLICY definition: # error: raise exception # warning: issue warning and continue @@ -1474,15 +1493,15 @@ def _msvc_script_argument_uwp(cls, env, version, vernum, arglist): if uwp_app not in (True, '1'): return False - if vernum < VS2015: - debug('invalid: msvc_version constraint: msvc_version=%s < VS2015=%s', repr(version), repr(str(VS2015))) + if vernum < VS2015.vernum: + debug('invalid: msvc_version constraint: vernum %s < %s VS2015', repr(vernum), repr(VS2015.vernum)) err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(uwp_app), repr(version), repr(str(VS2015)) + repr(uwp_app), repr(version), repr(VS2015.symbol) ) raise MSVCArgumentError(err_msg) # uwp may not be installed - uwp_arg = 'uwp' if vernum > VS2015 else 'store' + uwp_arg = 'uwp' if vernum > VS2015.vernum else 'store' arglist.append(uwp_arg) return True @@ -1492,10 +1511,10 @@ def _msvc_script_argument_uwp(cls, env, version, vernum, arglist): @classmethod def _msvc_script_argument_sdk_constraints(cls, version, msvc_vernum, sdk_version): - if msvc_vernum < VS2015: - debug('invalid: msvc_version constraint: msvc_version=%s < VS2015=%s', repr(version), repr(str(VS2015))) + if msvc_vernum < VS2015.vernum: + debug('invalid: msvc_version constraint: vernum %s < %s VS2015', repr(msvc_vernum), repr(VS2015.vernum)) err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(sdk_version), repr(version), repr(str(VS2015)) + repr(sdk_version), repr(version), repr(VS2015.symbol) ) return err_msg @@ -1641,7 +1660,7 @@ def _msvc_version_toolset_vcvars(cls, version, vernum, vc_dir, toolset_version): toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(version, vc_dir) - if vernum == VS2019 and toolset_version == '14.28.16.8': + if vernum == VS2019.vernum and toolset_version == '14.28.16.8': # 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. @@ -1677,10 +1696,10 @@ def _msvc_version_toolset_vcvars(cls, version, vernum, vc_dir, toolset_version): @classmethod def _msvc_script_argument_toolset_constraints(cls, version, msvc_vernum, toolset_version): - if msvc_vernum < VS2017: - debug('invalid: msvc_version constraint: msvc_version=%s < VS2017=%s', repr(version), repr(str(VS2017))) + if msvc_vernum < VS2017.vernum: + debug('invalid: msvc_version constraint: vernum %s < %s VS2017', repr(msvc_vernum), repr(VS2017.vernum)) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(toolset_version), repr(version), repr(str(VS2017)) + repr(toolset_version), repr(version), repr(VS2017.symbol) ) return err_msg @@ -1695,23 +1714,23 @@ def _msvc_script_argument_toolset_constraints(cls, version, msvc_vernum, toolset toolset_ver = m.group('version') toolset_vernum = float(toolset_ver) - if toolset_vernum < VS2015: - debug('invalid: toolset_vernum constraint: toolset_vernum=%s < VS2015=%s', repr(str(toolset_vernum)), repr(str(VS2015))) + if toolset_vernum < VS2015.vernum: + debug('invalid: toolset_version constraint: toolset vernum %s < %s VS2015', repr(toolset_vernum), repr(VS2015.vernum)) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( - repr(toolset_version), repr(str(toolset_vernum)), repr(str(VS2015)) + repr(toolset_version), repr(toolset_ver), repr(VS2015.symbol) ) return err_msg if toolset_vernum > msvc_vernum: - debug('invalid: toolset_vernum constraint: toolset_vernum=%s > msvc_vernum=%s', repr(str(toolset_vernum)), repr(str(msvc_vernum))) + debug('invalid: toolset_version constraint: toolset vernum %s > %s msvc vernum', repr(toolset_vernum), repr(msvc_vernum)) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( - repr(toolset_version), repr(str(toolset_vernum)), repr(version) + repr(toolset_version), repr(toolset_ver), repr(version) ) return err_msg - if toolset_vernum == VS2015 and cls.re_toolset_full.match(toolset_version): + if toolset_vernum == VS2015.vernum and cls.re_toolset_full.match(toolset_version): if not cls.re_toolset_140.match(toolset_version): - debug('invalid: 14.0 version: toolset_version=%s', repr(toolset_version)) + debug('invalid: 14.0 constraint: toolset version %s > 14.0', repr(toolset_version)) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > '14.0'".format( repr(toolset_version), repr(toolset_version) ) @@ -1762,7 +1781,7 @@ def _msvc_script_argument_toolset(cls, env, version, vernum, vc_dir, arglist): @classmethod def _msvc_script_default_toolset(cls, env, version, vernum, vc_dir, arglist): - if vernum < VS2017: + if vernum < VS2017.vernum: return False toolset_default = cls._msvc_default_toolset(version, vc_dir) @@ -1785,10 +1804,10 @@ def _msvc_script_argument_spectre(cls, env, version, vernum, arglist): if spectre_libs not in (True, '1'): return False - if vernum < VS2017: - debug('invalid: msvc_version constraint: msvc_version=%s < VS2017=%s', repr(version), repr(str(VS2017))) + if vernum < VS2017.vernum: + debug('invalid: msvc_version constraint: vernum %s < %s VS2017', repr(vernum), repr(VS2017.vernum)) err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(spectre_libs), repr(version), repr(str(VS2017)) + repr(spectre_libs), repr(version), repr(VS2017.symbol) ) raise MSVCArgumentError(err_msg) @@ -1806,10 +1825,10 @@ def _msvc_script_argument_user(cls, env, version, vernum, arglist): if not script_args: return False - if vernum < VS2015: - debug('invalid: msvc_version constraint: msvc_version=%s < VS2015=%s', repr(version), repr(str(VS2015))) + if vernum < VS2015.vernum: + debug('invalid: msvc_version constraint: vernum %s < %s VS2015', repr(vernum), repr(VS2015.vernum)) err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(script_args), repr(version), repr(str(VS2015)) + repr(script_args), repr(version), repr(VS2015.symbol) ) raise MSVCArgumentError(err_msg) @@ -1826,28 +1845,25 @@ def msvc_script_arguments(cls, env, version, vc_dir, arg): arglist = [arg] have_uwp = False - have_sdk = False have_toolset = False - have_spectre = False - have_user = False if 'MSVC_UWP_APP' in env: have_uwp = cls._msvc_script_argument_uwp(env, version, vernum, arglist) if 'MSVC_SDK_VERSION' in env: - have_sdk = cls._msvc_script_argument_sdk(env, version, vernum, have_uwp, arglist) + cls._msvc_script_argument_sdk(env, version, vernum, have_uwp, arglist) if 'MSVC_TOOLSET_VERSION' in env: have_toolset = cls._msvc_script_argument_toolset(env, version, vernum, vc_dir, arglist) - if _MSVC_TOOLSET_DEFAULT_VCVARSVER and not have_toolset and vernum >= VS2017: + if _MSVC_TOOLSET_DEFAULT_VCVARSVER and not have_toolset and vernum >= VS2017.vernum: have_toolset = cls._msvc_script_default_toolset(env, version, vernum, vc_dir, arglist) if 'MSVC_SPECTRE_LIBS' in env: - have_spectre = cls._msvc_script_argument_spectre(env, version, vernum, arglist) + cls._msvc_script_argument_spectre(env, version, vernum, arglist) if 'MSVC_SCRIPT_ARGS' in env: - have_user = cls._msvc_script_argument_user(env, version, vernum, arglist) + cls._msvc_script_argument_user(env, version, vernum, arglist) argstr = ' '.join(arglist).strip() debug('arguments: %s', repr(argstr)) From b9c8f0c09786811d595e4a331ba75881e7a3d009 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 13 Jun 2022 11:56:34 -0400 Subject: [PATCH 004/108] Expand data structures and rework version comparisons and messages. --- SCons/Tool/MSCommon/vc.py | 496 ++++++++++++++++++++++++++++---------- 1 file changed, 365 insertions(+), 131 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 28aff355c4..22778263ec 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -97,41 +97,198 @@ class MSVCArgumentError(VisualCException): class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): pass -# Force -vcvars_ver argument for default toolset -_MSVC_TOOLSET_DEFAULT_VCVARSVER = False -# VS constants: VSPRODUCT defined as MSVC_VERSION introduced +class _Const: + + MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ + 'vc_runtime', + 'vc_runtime_numeric', + 'vc_runtime_alias_list', + 'vc_runtime_vsdef_list', + ]) + + MSVC_RUNTIME_DEFINITION_LIST = [] + + MSVC_RUNTIME_INTERNAL = {} + MSVC_RUNTIME_EXTERNAL = {} + + for vc_runtime, vc_runtime_numeric, vc_runtime_alias_list in [ + ('140', 140, ['ucrt']), + ('120', 120, ['msvcr120']), + ('110', 110, ['msvcr110']), + ('100', 100, ['msvcr100']), + ( '90', 90, ['msvcr90']), + ( '80', 80, ['msvcr80']), + ( '71', 71, ['msvcr71']), + ( '70', 70, ['msvcr70']), + ( '60', 60, ['msvcrt']), + ]: + vc_runtime_def = MSVC_RUNTIME_DEFINITION( + vc_runtime = vc_runtime, + vc_runtime_numeric = vc_runtime_numeric, + vc_runtime_alias_list = vc_runtime_alias_list, + vc_runtime_vsdef_list = [], + ) -_VS_MSVC_VERSION_DEFINITION = namedtuple('VSMSVCVersionDefinition', [ - 'vernum', - 'symbol', - 'major', - 'minor', -]) + MSVC_RUNTIME_DEFINITION_LIST.append(vc_runtime_def) + + MSVC_RUNTIME_INTERNAL[vc_runtime] = vc_runtime_def + MSVC_RUNTIME_EXTERNAL[vc_runtime] = vc_runtime_def + + for vc_runtime_alias in vc_runtime_alias_list: + MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def + + MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [ + 'vc_buildtools', + 'vc_buildtools_numeric', + 'vc_version', + 'vc_version_numeric', + 'cl_version', + 'cl_version_numeric', + 'vc_runtime_def', + ]) + + MSVC_BUILDTOOLS_DEFINITION_LIST = [] + + MSVC_BUILDTOOLS_INTERNAL = {} + MSVC_BUILDTOOLS_EXTERNAL = {} + + VC_VERSION_MAP = {} + + for vc_buildtools, vc_version, cl_version, vc_runtime in [ + ('v143', '14.3', '19.3', '140'), + ('v142', '14.2', '19.2', '140'), + ('v141', '14.1', '19.1', '140'), + ('v140', '14.0', '19.0', '140'), + ('v120', '12.0', '18.0', '120'), + ('v110', '11.0', '17.0', '110'), + ('v100', '10.0', '16.0', '100'), + ( 'v90', '9.0', '15.0', '90'), + ( 'v80', '8.0', '14.0', '80'), + ( 'v71', '7.1', '13.1', '71'), + ( 'v70', '7.0', '13.0', '70'), + ( 'v60', '6.0', '12.0', '60'), + ]: + + vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime] + + vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION( + vc_buildtools = vc_buildtools, + vc_buildtools_numeric = int(vc_buildtools[1:]), + vc_version = vc_version, + vc_version_numeric = float(vc_version), + cl_version = cl_version, + cl_version_numeric = float(cl_version), + vc_runtime_def = vc_runtime_def, + ) -def _vs_msvc_version(vernum): - symbol = str(vernum) - major, minor = [int(s) for s in symbol.split('.')] - vs_msvc_version = _VS_MSVC_VERSION_DEFINITION( - vernum = vernum, - symbol = symbol, - major = major, - minor = minor - ) - return vs_msvc_version - -VS2022 = _vs_msvc_version(14.3) -VS2019 = _vs_msvc_version(14.2) -VS2017 = _vs_msvc_version(14.1) -VS2015 = _vs_msvc_version(14.0) -VS2013 = _vs_msvc_version(12.0) -VS2012 = _vs_msvc_version(11.0) -VS2010 = _vs_msvc_version(10.0) -VS2008 = _vs_msvc_version(9.0) -VS2005 = _vs_msvc_version(8.0) -VS2003 = _vs_msvc_version(7.1) -VS2002 = _vs_msvc_version(7.0) -VS6 = _vs_msvc_version(6.0) + MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def) + + MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def + MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def + MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def + + VC_VERSION_MAP[vc_version] = vc_buildtools_def + + MSVS_VERSION_INTERNAL = {} + MSVS_VERSION_EXTERNAL = {} + + MSVC_VERSION_INTERNAL = {} + MSVC_VERSION_EXTERNAL = {} + + MSVS_VERSION_MAJOR_MAP = {} + + CL_VERSION_MAP = {} + + VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [ + 'vs_product', + 'vs_product_alias_list', + 'vs_version', + 'vs_version_major', + 'vs_envvar', + 'vs_express', + 'vs_lookup', + 'vc_sdk_versions', + 'vc_ucrt_versions', + 'vc_uwp', + 'vc_buildtools_def', + 'vc_buildtools_all', + ]) + + VISUALSTUDIO_DEFINITION_LIST = [] + + VS_PRODUCT_ALIAS = { + '1998': ['6'] + } + + # vs_envvar: VisualStudioVersion defined in environment for MSVS 2012 and later + # MSVS 2010 and earlier cl_version -> vs_def is a 1:1 mapping + # SDK attached to product or buildtools? + for vs_product, vs_version, vs_envvar, vs_express, vs_lookup, vc_sdk, vc_ucrt, vc_uwp, vc_buildtools_all in [ + ('2022', '17.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v143', 'v142', 'v141', 'v140']), + ('2019', '16.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v142', 'v141', 'v140']), + ('2017', '15.0', True, True, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v141', 'v140']), + ('2015', '14.0', True, True, 'registry', ['10.0', '8.1'], ['10'], 'store', ['v140']), + ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']), + ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']), + ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']), + ('2008', '9.0', False, True, 'registry', None, None, None, [ 'v90']), + ('2005', '8.0', False, True, 'registry', None, None, None, [ 'v80']), + ('2003', '7.1', False, False, 'registry', None, None, None, [ 'v71']), + ('2002', '7.0', False, False, 'registry', None, None, None, [ 'v70']), + ('1998', '6.0', False, False, 'registry', None, None, None, [ 'v60']), + ]: + + vs_version_major = vs_version.split('.')[0] + + vc_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools_all[0]] + + vs_def = VISUALSTUDIO_DEFINITION( + vs_product = vs_product, + vs_product_alias_list = [], + vs_version = vs_version, + vs_version_major = vs_version_major, + vs_envvar = vs_envvar, + vs_express = vs_express, + vs_lookup = vs_lookup, + vc_sdk_versions = vc_sdk, + vc_ucrt_versions = vc_ucrt, + vc_uwp = vc_uwp, + vc_buildtools_def = vc_buildtools_def, + vc_buildtools_all = vc_buildtools_all, + ) + + VISUALSTUDIO_DEFINITION_LIST.append(vs_def) + + vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def) + + MSVS_VERSION_INTERNAL[vs_product] = vs_def + MSVS_VERSION_EXTERNAL[vs_product] = vs_def + MSVS_VERSION_EXTERNAL[vs_version] = vs_def + + MSVC_VERSION_INTERNAL[vc_buildtools_def.vc_version] = vs_def + MSVC_VERSION_EXTERNAL[vs_product] = vs_def + MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_version] = vs_def + MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def + + if vs_product in VS_PRODUCT_ALIAS: + for vs_product_alias in VS_PRODUCT_ALIAS[vs_product]: + vs_def.vs_product_alias_list.append(vs_product_alias) + MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def + MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def + + MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def + + CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def + + MSVS_VERSION_LEGACY = {} + MSVC_VERSION_LEGACY = {} + + for vdict in (MSVS_VERSION_EXTERNAL, MSVC_VERSION_INTERNAL): + for key, vs_def in vdict.items(): + if key not in MSVS_VERSION_LEGACY: + MSVS_VERSION_LEGACY[key] = vs_def + MSVC_VERSION_LEGACY[key] = vs_def # MSVC_NOTFOUND_POLICY definition: # error: raise exception @@ -1481,11 +1638,33 @@ class _MSVCScriptArguments: # MSVC_SCRIPT_ARGS: VS2015+ # + # Force -vcvars_ver argument for default toolset + MSVC_TOOLSET_DEFAULT_VCVARSVER = False + + VS2019 = _Const.MSVS_VERSION_INTERNAL['2019'] + VS2017 = _Const.MSVS_VERSION_INTERNAL['2017'] + VS2015 = _Const.MSVS_VERSION_INTERNAL['2015'] + + MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [ + 'version', + 'vs_def', + ]) + + @classmethod + def _msvc_version(cls, version): + verstr = get_msvc_version_numeric(version) + vs_def = _Const.MSVC_VERSION_INTERNAL[verstr] + msvc_req = cls.MSVC_VERSION_ARGS_DEFINITION( + version = version, + vs_def = vs_def, + ) + return msvc_req + @classmethod - def _msvc_script_argument_uwp(cls, env, version, vernum, arglist): + def _msvc_script_argument_uwp(cls, env, msvc, arglist): uwp_app = env['MSVC_UWP_APP'] - debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(version), repr(uwp_app)) + debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app)) if not uwp_app: return False @@ -1493,15 +1672,19 @@ def _msvc_script_argument_uwp(cls, env, version, vernum, arglist): if uwp_app not in (True, '1'): return False - if vernum < VS2015.vernum: - debug('invalid: msvc_version constraint: vernum %s < %s VS2015', repr(vernum), repr(VS2015.vernum)) + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(uwp_app), repr(version), repr(VS2015.symbol) + repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) ) raise MSVCArgumentError(err_msg) # uwp may not be installed - uwp_arg = 'uwp' if vernum > VS2015.vernum else 'store' + uwp_arg = msvc.vs_def.vc_uwp arglist.append(uwp_arg) return True @@ -1509,15 +1692,21 @@ def _msvc_script_argument_uwp(cls, env, version, vernum, arglist): re_sdk_version_10 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$') @classmethod - def _msvc_script_argument_sdk_constraints(cls, version, msvc_vernum, sdk_version): + def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version): - if msvc_vernum < VS2015.vernum: - debug('invalid: msvc_version constraint: vernum %s < %s VS2015', repr(msvc_vernum), repr(VS2015.vernum)) + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc_version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(sdk_version), repr(version), repr(VS2015.symbol) + repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) ) return err_msg + # TODO: check sdk against vs_def/vc_buildtools_def + if sdk_version == '8.1': debug('valid: sdk_version=%s', repr(sdk_version)) return None @@ -1531,15 +1720,18 @@ def _msvc_script_argument_sdk_constraints(cls, version, msvc_vernum, sdk_version return err_msg @classmethod - def _msvc_script_argument_sdk(cls, env, version, vernum, is_uwp, arglist): + def _msvc_script_argument_sdk(cls, env, msvc, is_uwp, arglist): sdk_version = env['MSVC_SDK_VERSION'] - debug('MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, uwp=%s', repr(version), repr(sdk_version), repr(is_uwp)) + debug( + 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, uwp=%s', + repr(msvc.version), repr(sdk_version), repr(is_uwp) + ) if not sdk_version: return False - err_msg = cls._msvc_script_argument_sdk_constraints(version, vernum, sdk_version) + err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version) if err_msg: raise MSVCArgumentError(err_msg) @@ -1548,20 +1740,23 @@ def _msvc_script_argument_sdk(cls, env, version, vernum, is_uwp, arglist): return True @classmethod - def _msvc_read_toolset_file(cls, version, filename): + def _msvc_read_toolset_file(cls, 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(version), repr(filename), repr(toolset_version)) + debug( + 'msvc_version=%s, filename=%s, toolset_version=%s', + repr(msvc.version), repr(filename), repr(toolset_version) + ) except IOError: - debug('IOError: msvc_version=%s, filename=%s', repr(version), repr(filename)) + debug('IOError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) except IndexError: - debug('IndexError: msvc_version=%s, filename=%s', repr(version), repr(filename)) + debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) return toolset_version @classmethod - def _msvc_read_toolset_folders(cls, version, vc_dir): + def _msvc_read_toolset_folders(cls, msvc, vc_dir): toolsets_sxs = {} toolsets_full = [] @@ -1572,11 +1767,14 @@ def _msvc_read_toolset_folders(cls, version, vc_dir): filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) filepath = os.path.join(build_dir, sxs_toolset, filename) if os.path.exists(filepath): - toolset_version = cls._msvc_read_toolset_file(version, filepath) + toolset_version = cls._msvc_read_toolset_file(msvc, filepath) if not toolset_version: continue toolsets_sxs[sxs_toolset] = toolset_version - debug('sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', repr(version), repr(sxs_toolset), toolset_version) + debug( + 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', + repr(msvc.version), repr(sxs_toolset), toolset_version + ) toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] @@ -1584,28 +1782,28 @@ def _msvc_read_toolset_folders(cls, version, vc_dir): binpath = os.path.join(toolset_dir, toolset_version, "bin") if os.path.exists(binpath): toolsets_full.append(toolset_version) - debug('toolset: msvc_version=%s, toolset_version=%s', repr(version), repr(toolset_version)) + debug( + 'toolset: msvc_version=%s, toolset_version=%s', + repr(msvc.version), repr(toolset_version) + ) toolsets_full.sort(reverse=True) - debug('msvc_version=%s, toolsets=%s', repr(version), repr(toolsets_full)) + debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full)) return toolsets_sxs, toolsets_full @classmethod - def _msvc_read_toolset_default(cls, version, vc_dir): + def _msvc_read_toolset_default(cls, msvc, vc_dir): build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - version_numeric = get_msvc_version_numeric(version) - # VS2019+ - buildtools_version = ''.join(version_numeric.split('.')) - filename = "Microsoft.VCToolsVersion.v{}.default.txt".format(buildtools_version) + filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools) filepath = os.path.join(build_dir, filename) toolset_buildtools = None if os.path.exists(filepath): - toolset_buildtools = cls._msvc_read_toolset_file(version, filepath) + toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath) if toolset_buildtools: return toolset_buildtools @@ -1615,14 +1813,14 @@ def _msvc_read_toolset_default(cls, version, vc_dir): toolset_default = None if os.path.exists(filepath): - toolset_default = cls._msvc_read_toolset_file(version, filepath) + toolset_default = cls._msvc_read_toolset_file(msvc, filepath) if toolset_default: return toolset_default return None @classmethod - def _reset_toolset(cls): + def _reset_toolsets(cls): debug('reset: toolset cache') cls._toolset_version_cache = {} cls._toolset_default_cache = {} @@ -1631,40 +1829,46 @@ def _reset_toolset(cls): _toolset_default_cache = {} @classmethod - def _msvc_version_toolsets(cls, version, vc_dir): + def _msvc_version_toolsets(cls, msvc, vc_dir): - if version in cls._toolset_version_cache: - toolsets_sxs, toolsets_full = cls._toolset_version_cache[version] + if msvc.version in cls._toolset_version_cache: + toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version] else: - toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(version, vc_dir) - cls._toolset_version_cache[version] = toolsets_sxs, toolsets_full + toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir) + cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full return toolsets_sxs, toolsets_full @classmethod - def _msvc_default_toolset(cls, version, vc_dir): + def _msvc_default_toolset(cls, msvc, vc_dir): - if version in cls._toolset_default_cache: - toolset_default = cls._toolset_default_cache[version] + if msvc.version in cls._toolset_default_cache: + toolset_default = cls._toolset_default_cache[msvc.version] else: - toolset_default = cls._msvc_read_toolset_default(version, vc_dir) - cls._toolset_default_cache[version] = toolset_default + toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir) + cls._toolset_default_cache[msvc.version] = toolset_default return toolset_default @classmethod - def _msvc_version_toolset_vcvars(cls, version, vernum, vc_dir, toolset_version): + def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version): if toolset_version == '14.0': return toolset_version - toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(version, vc_dir) - - if vernum == VS2019.vernum and toolset_version == '14.28.16.8': - # 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. - toolset_version = '14.28' + toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir) + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric: + if toolset_version == '14.28.16.8': + new_toolset_version = '14.28' + # 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. + debug( + 'rewrite toolset_version=%s => toolset_version=%s', + repr(toolset_version), repr(new_toolset_version) + ) + toolset_version = new_toolset_version if toolset_version in toolsets_sxs: toolset_vcvars = toolsets_sxs[toolset_version] @@ -1694,12 +1898,16 @@ def _msvc_version_toolset_vcvars(cls, version, vernum, vc_dir, toolset_version): re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') @classmethod - def _msvc_script_argument_toolset_constraints(cls, version, msvc_vernum, toolset_version): + def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version): - if msvc_vernum < VS2017.vernum: - debug('invalid: msvc_version constraint: vernum %s < %s VS2017', repr(msvc_vernum), repr(VS2017.vernum)) + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2017', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(toolset_version), repr(version), repr(VS2017.symbol) + repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) ) return err_msg @@ -1714,28 +1922,39 @@ def _msvc_script_argument_toolset_constraints(cls, version, msvc_vernum, toolset toolset_ver = m.group('version') toolset_vernum = float(toolset_ver) - if toolset_vernum < VS2015.vernum: - debug('invalid: toolset_version constraint: toolset vernum %s < %s VS2015', repr(toolset_vernum), repr(VS2015.vernum)) + if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: %s < %s VS2015', + repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( - repr(toolset_version), repr(toolset_ver), repr(VS2015.symbol) + repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version) ) return err_msg - if toolset_vernum > msvc_vernum: - debug('invalid: toolset_version constraint: toolset vernum %s > %s msvc vernum', repr(toolset_vernum), repr(msvc_vernum)) + if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: toolset %s > %s msvc', + repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( - repr(toolset_version), repr(toolset_ver), repr(version) + repr(toolset_version), repr(toolset_ver), repr(msvc.version) ) return err_msg - if toolset_vernum == VS2015.vernum and cls.re_toolset_full.match(toolset_version): - if not cls.re_toolset_140.match(toolset_version): - debug('invalid: 14.0 constraint: toolset version %s > 14.0', repr(toolset_version)) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > '14.0'".format( - repr(toolset_version), repr(toolset_version) - ) - return err_msg - return None + if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric: + # tooset = 14.0 + if cls.re_toolset_full.match(toolset_version): + if not cls.re_toolset_140.match(toolset_version): + debug( + 'invalid: toolset version 14.0 constraint: %s != 14.0', + repr(toolset_version) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format( + repr(toolset_version), repr(toolset_version) + ) + return err_msg + return None if cls.re_toolset_full.match(toolset_version): debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) @@ -1750,28 +1969,35 @@ def _msvc_script_argument_toolset_constraints(cls, version, msvc_vernum, toolset return err_msg @classmethod - def _msvc_script_argument_toolset(cls, env, version, vernum, vc_dir, arglist): + def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist): toolset_version = env['MSVC_TOOLSET_VERSION'] - debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(version), repr(toolset_version)) + debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version)) if not toolset_version: return False - err_msg = cls._msvc_script_argument_toolset_constraints(version, vernum, toolset_version) + err_msg = cls._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'): - debug('rewrite 14.0 toolset_version=%s', repr(toolset_version)) - toolset_version = '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 = cls._msvc_version_toolset_vcvars(version, vernum, vc_dir, toolset_version) - debug('toolset: toolset_version=%s, toolset_vcvars=%s', repr(toolset_version), repr(toolset_vcvars)) + toolset_vcvars = cls._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_VERSION {}: MSVC_TOOLSET_VERSION {} not found".format( - repr(version), repr(toolset_version) + err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format( + repr(toolset_version), repr(msvc.version) ) raise MSVCArgumentError(err_msg) @@ -1779,24 +2005,24 @@ def _msvc_script_argument_toolset(cls, env, version, vernum, vc_dir, arglist): return True @classmethod - def _msvc_script_default_toolset(cls, env, version, vernum, vc_dir, arglist): + def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist): - if vernum < VS2017.vernum: + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: return False - toolset_default = cls._msvc_default_toolset(version, vc_dir) + toolset_default = cls._msvc_default_toolset(msvc, vc_dir) if not toolset_default: return False - debug('MSVC_VERSION=%s, toolset_default=%s', repr(version), repr(toolset_default)) + debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default)) arglist.append('-vcvars_ver={}'.format(toolset_default)) return True @classmethod - def _msvc_script_argument_spectre(cls, env, version, vernum, arglist): + def _msvc_script_argument_spectre(cls, env, msvc, arglist): spectre_libs = env['MSVC_SPECTRE_LIBS'] - debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(version), repr(spectre_libs)) + debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs)) if not spectre_libs: return False @@ -1804,10 +2030,14 @@ def _msvc_script_argument_spectre(cls, env, version, vernum, arglist): if spectre_libs not in (True, '1'): return False - if vernum < VS2017.vernum: - debug('invalid: msvc_version constraint: vernum %s < %s VS2017', repr(vernum), repr(VS2017.vernum)) + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2017', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(spectre_libs), repr(version), repr(VS2017.symbol) + repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) ) raise MSVCArgumentError(err_msg) @@ -1816,19 +2046,23 @@ def _msvc_script_argument_spectre(cls, env, version, vernum, arglist): return True @classmethod - def _msvc_script_argument_user(cls, env, version, vernum, arglist): + def _msvc_script_argument_user(cls, env, msvc, arglist): # subst None -> empty string script_args = env.subst('$MSVC_SCRIPT_ARGS') - debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(version), repr(script_args)) + debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args)) if not script_args: return False - if vernum < VS2015.vernum: - debug('invalid: msvc_version constraint: vernum %s < %s VS2015', repr(vernum), repr(VS2015.vernum)) + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) + ) err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(script_args), repr(version), repr(VS2015.symbol) + repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) ) raise MSVCArgumentError(err_msg) @@ -1839,8 +2073,7 @@ def _msvc_script_argument_user(cls, env, version, vernum, arglist): @classmethod def msvc_script_arguments(cls, env, version, vc_dir, arg): - msvc_ver_numeric = get_msvc_version_numeric(version) - vernum = float(msvc_ver_numeric) + msvc = cls._msvc_version(version) arglist = [arg] @@ -1848,31 +2081,32 @@ def msvc_script_arguments(cls, env, version, vc_dir, arg): have_toolset = False if 'MSVC_UWP_APP' in env: - have_uwp = cls._msvc_script_argument_uwp(env, version, vernum, arglist) + have_uwp = cls._msvc_script_argument_uwp(env, msvc, arglist) if 'MSVC_SDK_VERSION' in env: - cls._msvc_script_argument_sdk(env, version, vernum, have_uwp, arglist) + cls._msvc_script_argument_sdk(env, msvc, have_uwp, arglist) if 'MSVC_TOOLSET_VERSION' in env: - have_toolset = cls._msvc_script_argument_toolset(env, version, vernum, vc_dir, arglist) + have_toolset = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist) - if _MSVC_TOOLSET_DEFAULT_VCVARSVER and not have_toolset and vernum >= VS2017.vernum: - have_toolset = cls._msvc_script_default_toolset(env, version, vernum, vc_dir, arglist) + if cls.MSVC_TOOLSET_DEFAULT_VCVARSVER and not have_toolset: + have_toolset = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist) if 'MSVC_SPECTRE_LIBS' in env: - cls._msvc_script_argument_spectre(env, version, vernum, arglist) + cls._msvc_script_argument_spectre(env, msvc, arglist) if 'MSVC_SCRIPT_ARGS' in env: - cls._msvc_script_argument_user(env, version, vernum, arglist) + cls._msvc_script_argument_user(env, msvc, arglist) argstr = ' '.join(arglist).strip() debug('arguments: %s', repr(argstr)) return argstr + @classmethod def reset(cls): debug('reset') - cls._reset_toolset() + cls._reset_toolsets() def msvc_find_valid_batch_script(env, version): """Find and execute appropriate batch script to set up build env. From 74740d2e8c2c500bc961cc9d8a1fe01af88efa37 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 14 Jun 2022 10:28:28 -0400 Subject: [PATCH 005/108] Detect multiple declarations between MSVC_SCRIPT_ARGS and new variables. Sort arguments in preferred order. --- SCons/Tool/MSCommon/vc.py | 253 +++++++++++++++++++++++++++++--------- 1 file changed, 195 insertions(+), 58 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 22778263ec..7c4228d37b 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -117,11 +117,11 @@ class _Const: ('120', 120, ['msvcr120']), ('110', 110, ['msvcr110']), ('100', 100, ['msvcr100']), - ( '90', 90, ['msvcr90']), - ( '80', 80, ['msvcr80']), - ( '71', 71, ['msvcr71']), - ( '70', 70, ['msvcr70']), - ( '60', 60, ['msvcrt']), + ('90', 90, ['msvcr90']), + ('80', 80, ['msvcr80']), + ('71', 71, ['msvcr71']), + ('70', 70, ['msvcr70']), + ('60', 60, ['msvcrt']), ]: vc_runtime_def = MSVC_RUNTIME_DEFINITION( vc_runtime = vc_runtime, @@ -163,11 +163,11 @@ class _Const: ('v120', '12.0', '18.0', '120'), ('v110', '11.0', '17.0', '110'), ('v100', '10.0', '16.0', '100'), - ( 'v90', '9.0', '15.0', '90'), - ( 'v80', '8.0', '14.0', '80'), - ( 'v71', '7.1', '13.1', '71'), - ( 'v70', '7.0', '13.0', '70'), - ( 'v60', '6.0', '12.0', '60'), + ('v90', '9.0', '15.0', '90'), + ('v80', '8.0', '14.0', '80'), + ('v71', '7.1', '13.1', '71'), + ('v70', '7.0', '13.0', '70'), + ('v60', '6.0', '12.0', '60'), ]: vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime] @@ -232,11 +232,11 @@ class _Const: ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']), ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']), ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']), - ('2008', '9.0', False, True, 'registry', None, None, None, [ 'v90']), - ('2005', '8.0', False, True, 'registry', None, None, None, [ 'v80']), - ('2003', '7.1', False, False, 'registry', None, None, None, [ 'v71']), - ('2002', '7.0', False, False, 'registry', None, None, None, [ 'v70']), - ('1998', '6.0', False, False, 'registry', None, None, None, [ 'v60']), + ('2008', '9.0', False, True, 'registry', None, None, None, ['v90']), + ('2005', '8.0', False, True, 'registry', None, None, None, ['v80']), + ('2003', '7.1', False, False, 'registry', None, None, None, ['v71']), + ('2002', '7.0', False, False, 'registry', None, None, None, ['v70']), + ('1998', '6.0', False, False, 'registry', None, None, None, ['v60']), ]: vs_version_major = vs_version.split('.')[0] @@ -1623,6 +1623,9 @@ def msvc_setup_env_once(env, tool=None): class _MSVCScriptArguments: + # Force -vcvars_ver argument for default toolset + MSVC_TOOLSET_DEFAULT_VCVARSVER = True + # MSVC batch file arguments: # # VS2022: UWP, SDK, TOOLSET, SPECTRE @@ -1630,35 +1633,42 @@ class _MSVCScriptArguments: # 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+ - # - # MSVC_SCRIPT_ARGS: VS2015+ - # - # Force -vcvars_ver argument for default toolset - MSVC_TOOLSET_DEFAULT_VCVARSVER = False + class SortOrder: + ARCH = 0 # arch + 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 = _Const.MSVS_VERSION_INTERNAL['2019'] VS2017 = _Const.MSVS_VERSION_INTERNAL['2017'] VS2015 = _Const.MSVS_VERSION_INTERNAL['2015'] MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [ - 'version', + 'version', # fully qualified msvc version (e.g., '14.1Exp') 'vs_def', ]) @classmethod def _msvc_version(cls, version): + verstr = get_msvc_version_numeric(version) vs_def = _Const.MSVC_VERSION_INTERNAL[verstr] - msvc_req = cls.MSVC_VERSION_ARGS_DEFINITION( + + version_args = cls.MSVC_VERSION_ARGS_DEFINITION( version = version, vs_def = vs_def, ) - return msvc_req + + return version_args @classmethod def _msvc_script_argument_uwp(cls, env, msvc, arglist): @@ -1667,10 +1677,10 @@ def _msvc_script_argument_uwp(cls, env, msvc, arglist): debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app)) if not uwp_app: - return False + return None if uwp_app not in (True, '1'): - return False + return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: debug( @@ -1683,10 +1693,14 @@ def _msvc_script_argument_uwp(cls, env, msvc, arglist): ) raise MSVCArgumentError(err_msg) - # uwp may not be installed + # VS2017+ rewrites uwp => store for 14.0 toolset uwp_arg = msvc.vs_def.vc_uwp - arglist.append(uwp_arg) - return True + + # uwp may not be installed + argpair = (cls.SortOrder.UWP, uwp_arg) + arglist.append(argpair) + + return uwp_arg # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last? re_sdk_version_10 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$') @@ -1729,15 +1743,17 @@ def _msvc_script_argument_sdk(cls, env, msvc, is_uwp, arglist): ) if not sdk_version: - return False + return None err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version) if err_msg: raise MSVCArgumentError(err_msg) # sdk folder may not exist - arglist.append(sdk_version) - return True + argpair = (cls.SortOrder.SDK, sdk_version) + arglist.append(argpair) + + return sdk_version @classmethod def _msvc_read_toolset_file(cls, msvc, filename): @@ -1766,6 +1782,7 @@ def _msvc_read_toolset_folders(cls, msvc, vc_dir): for sxs_toolset in sxs_toolsets: filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) filepath = os.path.join(build_dir, sxs_toolset, filename) + debug('sxs toolset: check file=%s', repr(filepath)) if os.path.exists(filepath): toolset_version = cls._msvc_read_toolset_file(msvc, filepath) if not toolset_version: @@ -1780,6 +1797,7 @@ def _msvc_read_toolset_folders(cls, msvc, vc_dir): toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] for toolset_version in toolsets: binpath = os.path.join(toolset_dir, toolset_version, "bin") + debug('toolset: check binpath=%s', repr(binpath)) if os.path.exists(binpath): toolsets_full.append(toolset_version) debug( @@ -1801,6 +1819,7 @@ def _msvc_read_toolset_default(cls, msvc, vc_dir): filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools) filepath = os.path.join(build_dir, filename) + debug('default toolset: check file=%s', repr(filepath)) toolset_buildtools = None if os.path.exists(filepath): toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath) @@ -1811,6 +1830,7 @@ def _msvc_read_toolset_default(cls, msvc, vc_dir): filename = "Microsoft.VCToolsVersion.default.txt" filepath = os.path.join(build_dir, filename) + debug('default toolset: check file=%s', repr(filepath)) toolset_default = None if os.path.exists(filepath): toolset_default = cls._msvc_read_toolset_file(msvc, filepath) @@ -1859,6 +1879,7 @@ def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version): toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir) if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric: + # necessary to detect toolset not found if toolset_version == '14.28.16.8': new_toolset_version = '14.28' # VS2019\Common7\Tools\vsdevcmd\ext\vcvars.bat AzDO Bug#1293526 @@ -1975,7 +1996,7 @@ def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist): debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version)) if not toolset_version: - return False + return None err_msg = cls._msvc_script_argument_toolset_constraints(msvc, toolset_version) if err_msg: @@ -2001,22 +2022,27 @@ def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist): ) raise MSVCArgumentError(err_msg) - arglist.append('-vcvars_ver={}'.format(toolset_vcvars)) - return True + argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars)) + arglist.append(argpair) + + return toolset_vcvars @classmethod def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: - return False + return None toolset_default = cls._msvc_default_toolset(msvc, vc_dir) if not toolset_default: - return False + return None debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default)) - arglist.append('-vcvars_ver={}'.format(toolset_default)) - return True + + argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default)) + arglist.append(argpair) + + return toolset_default @classmethod def _msvc_script_argument_spectre(cls, env, msvc, arglist): @@ -2025,10 +2051,10 @@ def _msvc_script_argument_spectre(cls, env, msvc, arglist): debug('MSVC_VERSION=%s, MSVC_SPECTRE_LIBS=%s', repr(msvc.version), repr(spectre_libs)) if not spectre_libs: - return False + return None if spectre_libs not in (True, '1'): - return False + return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: debug( @@ -2041,9 +2067,13 @@ def _msvc_script_argument_spectre(cls, env, msvc, arglist): ) raise MSVCArgumentError(err_msg) + spectre_arg = 'spectre' + # spectre libs may not be installed - arglist.append('-vcvars_spectre_libs=spectre') - return True + argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg)) + arglist.append(argpair) + + return spectre_arg @classmethod def _msvc_script_argument_user(cls, env, msvc, arglist): @@ -2053,7 +2083,7 @@ def _msvc_script_argument_user(cls, env, msvc, arglist): debug('MSVC_VERSION=%s, MSVC_SCRIPT_ARGS=%s', repr(msvc.version), repr(script_args)) if not script_args: - return False + return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: debug( @@ -2067,40 +2097,147 @@ def _msvc_script_argument_user(cls, env, msvc, arglist): raise MSVCArgumentError(err_msg) # user arguments are not validated - arglist.append(script_args) - return True + argpair = (cls.SortOrder.USER, script_args) + arglist.append(argpair) + + return script_args + + re_vcvars_uwp = re.compile(r'(?:\s|^)(?P(?:uwp|store))(?:\s|$)',re.IGNORECASE) + re_vcvars_sdk = re.compile(r'(?:\s|^)(?P(?:[1-9][0-9]*[.]\S*))(?:\s|$)',re.IGNORECASE) + re_vcvars_spectre = re.compile(r'(?:\s|^)(?P(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:\s|$)',re.IGNORECASE) + re_vcvars_toolset = re.compile(r'(?:\s|^)(?P(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:\s|$)', re.IGNORECASE) + + @classmethod + def _user_script_argument_uwp(cls, env, uwp, user_argstr): + + if not uwp: + return None + + m = cls.re_vcvars_uwp.search(user_argstr) + if not m: + return None + + 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) + + @classmethod + def _user_script_argument_sdk(cls, env, sdk_version, user_argstr): + + if not sdk_version: + return None + + m = cls.re_vcvars_sdk.search(user_argstr) + if not m: + return None + + 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) + + @classmethod + def _user_script_argument_toolset(cls, env, toolset_version, user_argstr): + + m = cls.re_vcvars_toolset.search(user_argstr) + if not m: + return None + + if not toolset_version: + user_toolset = m.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) + + @classmethod + def _user_script_argument_spectre(cls, env, spectre, user_argstr): + + if not spectre: + return None + + m = cls.re_vcvars_spectre.search(user_argstr) + if not m: + 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) @classmethod def msvc_script_arguments(cls, env, version, vc_dir, arg): msvc = cls._msvc_version(version) - arglist = [arg] + argstr = '' + arglist = [] - have_uwp = False - have_toolset = False + if arg: + argpair = (cls.SortOrder.ARCH, arg) + arglist.append(argpair) + + user_argstr = None + user_toolset = None + + uwp = None + sdk_version = None + toolset_version = None + spectre = None + + if 'MSVC_SCRIPT_ARGS' in env: + user_argstr = cls._msvc_script_argument_user(env, msvc, arglist) if 'MSVC_UWP_APP' in env: - have_uwp = cls._msvc_script_argument_uwp(env, msvc, arglist) + uwp = cls._msvc_script_argument_uwp(env, msvc, arglist) + if uwp and user_argstr: + cls._user_script_argument_uwp(env, uwp, user_argstr) if 'MSVC_SDK_VERSION' in env: - cls._msvc_script_argument_sdk(env, msvc, have_uwp, arglist) + is_uwp = True if uwp else False + sdk_version = cls._msvc_script_argument_sdk(env, msvc, is_uwp, arglist) + if sdk_version and user_argstr: + cls._user_script_argument_sdk(env, sdk_version, user_argstr) if 'MSVC_TOOLSET_VERSION' in env: - have_toolset = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist) + toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist) + + if user_argstr: + user_toolset = cls._user_script_argument_toolset(env, toolset_version, user_argstr) - if cls.MSVC_TOOLSET_DEFAULT_VCVARSVER and not have_toolset: - have_toolset = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist) + if cls.MSVC_TOOLSET_DEFAULT_VCVARSVER: + if not toolset_version and not user_toolset: + toolset_version = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist) if 'MSVC_SPECTRE_LIBS' in env: - cls._msvc_script_argument_spectre(env, msvc, arglist) + spectre = cls._msvc_script_argument_spectre(env, msvc, arglist) + if spectre and user_argstr: + cls._user_script_argument_spectre(env, spectre, user_argstr) - if 'MSVC_SCRIPT_ARGS' in env: - cls._msvc_script_argument_user(env, msvc, arglist) + if arglist: + arglist.sort() + argstr = ' '.join([argpair[-1] for argpair in arglist]).strip() - argstr = ' '.join(arglist).strip() debug('arguments: %s', repr(argstr)) - return argstr @classmethod From 7d195b67f954ab1391b0f5233a85dbcaed1aab85 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 14 Jun 2022 10:49:39 -0400 Subject: [PATCH 006/108] Convert SortOrder class to enum --- SCons/Tool/MSCommon/vc.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7c4228d37b..d078a99204 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -51,6 +51,7 @@ namedtuple, OrderedDict, ) +import enum import SCons.Util import SCons.Warnings @@ -1640,13 +1641,14 @@ class _MSVCScriptArguments: # MSVC_TOOLSET_VERSION: VS2017+ # MSVC_SPECTRE_LIBS: VS2017+ - class SortOrder: - ARCH = 0 # arch - UWP = 1 # MSVC_UWP_APP - SDK = 2 # MSVC_SDK_VERSION + @enum.unique + class SortOrder(enum.IntEnum): + ARCH = 0 # arch + 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 + USER = 5 # MSVC_SCRIPT_ARGS VS2019 = _Const.MSVS_VERSION_INTERNAL['2019'] VS2017 = _Const.MSVS_VERSION_INTERNAL['2017'] From 7b8a915bf4b33ec2ee20b0d6763968f39c743344 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 14 Jun 2022 12:05:52 -0400 Subject: [PATCH 007/108] Disable MSVC_TOOLSET_DEFAULT_VCVARSVER --- SCons/Tool/MSCommon/vc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index d078a99204..c885f20e25 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1625,7 +1625,7 @@ def msvc_setup_env_once(env, tool=None): class _MSVCScriptArguments: # Force -vcvars_ver argument for default toolset - MSVC_TOOLSET_DEFAULT_VCVARSVER = True + MSVC_TOOLSET_DEFAULT_VCVARSVER = False # MSVC batch file arguments: # From 5b8d1c4ba03b422f7415bea638cb84c4b974a494 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 14 Jun 2022 12:42:12 -0400 Subject: [PATCH 008/108] Change IOError to OSError --- SCons/Tool/MSCommon/vc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index c885f20e25..0c9fceed6f 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1767,8 +1767,8 @@ def _msvc_read_toolset_file(cls, msvc, filename): 'msvc_version=%s, filename=%s, toolset_version=%s', repr(msvc.version), repr(filename), repr(toolset_version) ) - except IOError: - debug('IOError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) + 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 From 5187917d8a99b86e965ddeb676b0f8fe6c670318 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 16 Jun 2022 11:41:17 -0400 Subject: [PATCH 009/108] Add SDK version support and validate all arguments. --- SCons/Tool/MSCommon/vc.py | 1637 +++++++++++++++++++++++-------------- 1 file changed, 1010 insertions(+), 627 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 0c9fceed6f..c44c698ecd 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -101,6 +101,17 @@ class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): class _Const: + BOOLEAN_KEYS = {} + BOOLEAN_SYMBOLS = {} + + for bool, symbol_list in [ + (False, (0, '0', False, 'False', 'FALSE', 'false', 'No', 'NO', 'no', None, '')), + (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes', )), + ]: + BOOLEAN_KEYS[bool] = symbol_list + for symbol in symbol_list: + BOOLEAN_SYMBOLS[symbol] = bool + MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ 'vc_runtime', 'vc_runtime_numeric', @@ -1197,7 +1208,8 @@ def reset_installed_vcs(): global __INSTALLED_VCS_RUN __INSTALLED_VCS_RUN = None _MSVCSetupEnvDefault.reset() - _MSVCScriptArguments.reset() + _WindowsSDK.reset() + _ScriptArguments.reset() # Running these batch files isn't cheap: most of the time spent in # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" @@ -1622,287 +1634,543 @@ def msvc_setup_env_once(env, tool=None): " Requested tool(s) are: {}".format(req_tools) _msvc_notfound_policy_handler(env, msg) -class _MSVCScriptArguments: - - # Force -vcvars_ver argument for default toolset - MSVC_TOOLSET_DEFAULT_VCVARSVER = False +def msvc_find_valid_batch_script(env, version): + """Find and execute appropriate batch script to set up build env. - # 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+ + The MSVC build environment depends heavily on having the shell + environment set. SCons does not inherit that, and does not count + on that being set up correctly anyway, so it tries to find the right + MSVC batch script, or the right arguments to the generic batch script + vcvarsall.bat, and run that, so we have a valid environment to build in. + There are dragons here: the batch scripts don't fail (see comments + elsewhere), they just leave you with a bad setup, so try hard to + get it right. + """ - @enum.unique - class SortOrder(enum.IntEnum): - ARCH = 0 # arch - 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 + # Find the host, target, and all candidate (host, target) platform combinations: + platforms = get_host_target(env, version) + debug("host_platform %s, target_platform %s host_target_list %s", *platforms) + host_platform, target_platform, host_target_list = platforms - VS2019 = _Const.MSVS_VERSION_INTERNAL['2019'] - VS2017 = _Const.MSVS_VERSION_INTERNAL['2017'] - VS2015 = _Const.MSVS_VERSION_INTERNAL['2015'] + d = None + version_installed = False + for host_arch, target_arch, in host_target_list: + # Set to current arch. + env['TARGET_ARCH'] = target_arch + arg = '' - MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [ - 'version', # fully qualified msvc version (e.g., '14.1Exp') - 'vs_def', - ]) + # Try to locate a batch file for this host/target platform combo + try: + (vc_script, arg, vc_dir, sdk_script) = find_batch_file(env, version, host_arch, target_arch) + debug('vc_script:%s vc_script_arg:%s sdk_script:%s', vc_script, arg, sdk_script) + version_installed = True + except VisualCException as e: + msg = str(e) + debug('Caught exception while looking for batch file (%s)', msg) + version_installed = False + continue - @classmethod - def _msvc_version(cls, version): + # Try to use the located batch file for this host/target platform combo + debug('use_script 2 %s, args:%s', repr(vc_script), arg) + found = None + if vc_script: + arg = _ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) + try: + d = script_env(vc_script, args=arg) + found = vc_script + except BatchFileExecutionError as e: + debug('use_script 3: failed running VC script %s: %s: Error:%s', repr(vc_script), arg, e) + vc_script=None + continue + if not vc_script and sdk_script: + debug('use_script 4: trying sdk script: %s', sdk_script) + try: + d = script_env(sdk_script) + found = sdk_script + except BatchFileExecutionError as e: + debug('use_script 5: failed running SDK script %s: Error:%s', repr(sdk_script), e) + continue + elif not vc_script and not sdk_script: + debug('use_script 6: Neither VC script nor SDK script found') + continue - verstr = get_msvc_version_numeric(version) - vs_def = _Const.MSVC_VERSION_INTERNAL[verstr] + debug("Found a working script/target: %s/%s", repr(found), arg) + break # We've found a working target_platform, so stop looking - version_args = cls.MSVC_VERSION_ARGS_DEFINITION( - version = version, - vs_def = vs_def, - ) + # If we cannot find a viable installed compiler, reset the TARGET_ARCH + # To it's initial value + if not d: + env['TARGET_ARCH'] = target_platform - return version_args + if version_installed: + msg = "MSVC version '{}' working host/target script was not found.\n" \ + " Host = '{}', Target = '{}'\n" \ + " Visual Studio C/C++ compilers may not be set correctly".format( + version, host_platform, target_platform + ) + else: + installed_vcs = get_installed_vcs(env) + if installed_vcs: + msg = "MSVC version '{}' was not found.\n" \ + " Visual Studio C/C++ compilers may not be set correctly.\n" \ + " Installed versions are: {}".format(version, installed_vcs) + else: + msg = "MSVC version '{}' was not found.\n" \ + " No versions of the MSVC compiler were found.\n" \ + " Visual Studio C/C++ compilers may not be set correctly".format(version) - @classmethod - def _msvc_script_argument_uwp(cls, env, msvc, arglist): + _msvc_notfound_policy_handler(env, msg) - uwp_app = env['MSVC_UWP_APP'] - debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app)) + return d - if not uwp_app: - return None +_undefined = None - if uwp_app not in (True, '1'): - return None +def get_use_script_use_settings(env): + global _undefined - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - raise MSVCArgumentError(err_msg) + if _undefined is None: + _undefined = object() - # VS2017+ rewrites uwp => store for 14.0 toolset - uwp_arg = msvc.vs_def.vc_uwp + # use_script use_settings return values action + # value ignored (value, None) use script or bypass detection + # undefined value not None (False, value) use dictionary + # undefined undefined/None (True, None) msvc detection - # uwp may not be installed - argpair = (cls.SortOrder.UWP, uwp_arg) - arglist.append(argpair) + # None (documentation) or evaluates False (code): bypass detection + # need to distinguish between undefined and None + use_script = env.get('MSVC_USE_SCRIPT', _undefined) - return uwp_arg + if use_script != _undefined: + # use_script defined, use_settings ignored (not type checked) + return (use_script, None) - # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last? - re_sdk_version_10 = re.compile(r'^10[.][0-9][.][0-9]{5}[.][0-9]{1}$') + # undefined or None: use_settings ignored + use_settings = env.get('MSVC_USE_SETTINGS', None) - @classmethod - def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version): + if use_settings is not None: + # use script undefined, use_settings defined and not None (type checked) + return (False, use_settings) - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc_version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - return err_msg + # use script undefined, use_settings undefined or None + return (True, None) - # TODO: check sdk against vs_def/vc_buildtools_def +def msvc_setup_env(env): + debug('called') + version = get_default_version(env) + if version is None: + if not msvc_setup_env_user(env): + _MSVCSetupEnvDefault.set_nodefault() + return None - if sdk_version == '8.1': - debug('valid: sdk_version=%s', repr(sdk_version)) - return None + # XXX: we set-up both MSVS version for backward + # compatibility with the msvs tool + env['MSVC_VERSION'] = version + env['MSVS_VERSION'] = version + env['MSVS'] = {} - if cls.re_sdk_version_10.match(sdk_version): - debug('valid: sdk_version=%s', repr(sdk_version)) - return None + use_script, use_settings = get_use_script_use_settings(env) + if SCons.Util.is_String(use_script): + use_script = use_script.strip() + if not os.path.exists(use_script): + raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script)) + args = env.subst('$MSVC_USE_SCRIPT_ARGS') + debug('use_script 1 %s %s', repr(use_script), repr(args)) + d = script_env(use_script, args) + elif use_script: + d = msvc_find_valid_batch_script(env,version) + debug('use_script 2 %s', d) + if not d: + return d + elif use_settings is not None: + if not SCons.Util.is_Dict(use_settings): + error_msg = 'MSVC_USE_SETTINGS type error: expected a dictionary, found {}'.format(type(use_settings).__name__) + raise MSVCUseSettingsError(error_msg) + d = use_settings + debug('use_settings %s', d) + else: + debug('MSVC_USE_SCRIPT set to False') + warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ + "set correctly." + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None - debug('invalid: method exit: sdk_version=%s', repr(sdk_version)) - err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version)) - return err_msg + for k, v in d.items(): + env.PrependENVPath(k, v, delete_existing=True) + debug("env['ENV']['%s'] = %s", k, env['ENV'][k]) - @classmethod - def _msvc_script_argument_sdk(cls, env, msvc, is_uwp, arglist): + # final check to issue a warning if the compiler is not present + if not find_program_path(env, 'cl'): + debug("did not find %s", _CL_EXE_NAME) + if CONFIG_CACHE: + propose = "SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {} if out of date.".format(CONFIG_CACHE) + else: + propose = "It may need to be installed separately with Visual Studio." + warn_msg = "Could not find MSVC compiler 'cl'. {}".format(propose) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - sdk_version = env['MSVC_SDK_VERSION'] - debug( - 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, uwp=%s', - repr(msvc.version), repr(sdk_version), repr(is_uwp) - ) +def msvc_exists(env=None, version=None): + debug('version=%s', repr(version)) + vcs = get_installed_vcs(env) + if version is None: + rval = len(vcs) > 0 + else: + rval = version in vcs + debug('version=%s, return=%s', repr(version), rval) + return rval - if not sdk_version: - return None +def msvc_setup_env_user(env=None): + rval = False + if env: - err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version) - if err_msg: - raise MSVCArgumentError(err_msg) + # Intent is to use msvc tools: + # MSVC_VERSION or MSVS_VERSION: defined and is True + # MSVC_USE_SCRIPT: defined and (is string or is False) + # MSVC_USE_SETTINGS: defined and is not None - # sdk folder may not exist - argpair = (cls.SortOrder.SDK, sdk_version) - arglist.append(argpair) + # defined and is True + for key in ['MSVC_VERSION', 'MSVS_VERSION']: + if key in env and env[key]: + rval = True + debug('key=%s, return=%s', repr(key), rval) + return rval - return sdk_version + # defined and (is string or is False) + for key in ['MSVC_USE_SCRIPT']: + if key in env and (SCons.Util.is_String(env[key]) or not env[key]): + rval = True + debug('key=%s, return=%s', repr(key), rval) + return rval - @classmethod - def _msvc_read_toolset_file(cls, msvc, filename): - toolset_version = None + # defined and is not None + for key in ['MSVC_USE_SETTINGS']: + if key in env and env[key] is not None: + rval = True + debug('key=%s, return=%s', repr(key), rval) + return rval + + debug('return=%s', rval) + return rval + +def msvc_setup_env_tool(env=None, version=None, tool=None): + debug('tool=%s, version=%s', repr(tool), repr(version)) + _MSVCSetupEnvDefault.register_tool(env, tool) + rval = False + if not rval and msvc_exists(env, version): + rval = True + if not rval and msvc_setup_env_user(env): + rval = True + debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval) + return rval + +class _Util: + + @staticmethod + def listdir_dirs(p): + dirs = [] + for dir_name in os.listdir(p): + dir_path = os.path.join(p, dir_name) + if os.path.isdir(dir_path): + dirs.append((dir_name, dir_path)) + return dirs + + @staticmethod + def process_path(p): + if p: + p = os.path.normpath(p) + p = os.path.realpath(p) + p = os.path.normcase(p) + return p + +class _Registry: + + def read_value(hkey, subkey_valname): 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) - ) + rval = common.read_reg(subkey_valname, hkroot=hkey) except OSError: - debug('OSError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) + debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) + return None except IndexError: - debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) - return toolset_version + debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) + return None + debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval)) + return rval @classmethod - def _msvc_read_toolset_folders(cls, msvc, vc_dir): + def registry_query_path(cls, key, val, suffix): + extval = val + '\\' + suffix if suffix else val + qpath = cls.read_value(key, extval) + if qpath and os.path.exists(qpath): + qpath = _Util.process_path(qpath) + else: + qpath = None + return (qpath, key, val, extval) + + REG_SOFTWARE_MICROSOFT = [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'), + (SCons.Util.HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries + (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Microsoft'), + (SCons.Util.HKEY_CURRENT_USER, r'Software\Microsoft'), + ] - toolsets_sxs = {} - toolsets_full = [] + @classmethod + def microsoft_query_paths(cls, suffix, usrval=None): + paths = [] + records = [] + for key, val in cls.REG_SOFTWARE_MICROSOFT: + extval = val + '\\' + suffix if suffix else val + qpath = cls.read_value(key, extval) + if qpath and os.path.exists(qpath): + qpath = _Util.process_path(qpath) + if qpath not in paths: + paths.append(qpath) + records.append((qpath, key, val, extval, usrval)) + return records - build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()] - for sxs_toolset in sxs_toolsets: - filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) - filepath = os.path.join(build_dir, sxs_toolset, filename) - debug('sxs toolset: check file=%s', repr(filepath)) - if os.path.exists(filepath): - toolset_version = cls._msvc_read_toolset_file(msvc, filepath) - if not toolset_version: - continue - toolsets_sxs[sxs_toolset] = toolset_version - debug( - 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', - repr(msvc.version), repr(sxs_toolset), toolset_version - ) + @classmethod + def microsoft_query_keys(cls, suffix, usrval=None): + records = [] + for key, val in cls.REG_SOFTWARE_MICROSOFT: + extval = val + '\\' + suffix if suffix else val + rval = cls.read_value(key, extval) + if rval: + records.append((key, val, extval, usrval)) + return records - toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") - toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] - for toolset_version in toolsets: - binpath = os.path.join(toolset_dir, toolset_version, "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) - ) + @classmethod + def microsoft_sdks(cls, version): + return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder']) - toolsets_full.sort(reverse=True) - debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full)) + @classmethod + def sdk_query_paths(cls, version): + q = cls.microsoft_sdks(version) + return cls.microsoft_query_paths(q) - return toolsets_sxs, toolsets_full + @classmethod + def windows_kits(cls, version): + return r'Windows Kits\Installed Roots\KitsRoot' + version @classmethod - def _msvc_read_toolset_default(cls, msvc, vc_dir): + def windows_kit_query_paths(cls, version): + q = cls.windows_kits(version) + return cls.microsoft_query_paths(q) - build_dir = os.path.join(vc_dir, "Auxiliary", "Build") +class _WindowsSDK: - # VS2019+ - filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools) - filepath = os.path.join(build_dir, filename) + sdk_map_cache = {} + sdk_cache = {} - debug('default toolset: check file=%s', repr(filepath)) - toolset_buildtools = None - if os.path.exists(filepath): - toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath) - if toolset_buildtools: - return toolset_buildtools + @classmethod + def reset(cls): + cls.sdk_map_cache = {} + cls.sdk_cache = {} - # VS2017+ - filename = "Microsoft.VCToolsVersion.default.txt" - filepath = os.path.join(build_dir, filename) + @classmethod + def _new_sdk_map(cls): + sdk_map = { + 'desktop': [], + 'uwp': [], + } + return sdk_map - debug('default toolset: check file=%s', repr(filepath)) - toolset_default = None - if os.path.exists(filepath): - toolset_default = cls._msvc_read_toolset_file(msvc, filepath) - if toolset_default: - return toolset_default + @classmethod + def _sdk_10_layout(cls, version): - return None + folder_prefix = version + '.' + + sdk_map = cls._new_sdk_map() + + sdk_roots = _Registry.sdk_query_paths(version) + + sdk_version_platform_seen = set() + sdk_roots_seen = set() + + for sdk_t in sdk_roots: + + sdk_root = sdk_t[0] + if sdk_root in sdk_roots_seen: + continue + sdk_roots_seen.add(sdk_root) + + if not os.path.exists(sdk_root): + continue + + sdk_include_path = os.path.join(sdk_root, 'include') + if not os.path.exists(sdk_include_path): + continue + + for version_nbr, version_nbr_path in _Util.listdir_dirs(sdk_include_path): + + if not version_nbr.startswith(folder_prefix): + continue + + sdk_inc_path = _Util.process_path(os.path.join(version_nbr_path, 'um')) + if not os.path.exists(sdk_inc_path): + continue + + for platform, sdk_inc_file in [ + ('desktop', 'winsdkver.h'), + ('uwp', 'windows.h'), + ]: + + if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): + continue + + key = (version_nbr, platform) + if key in sdk_version_platform_seen: + continue + sdk_version_platform_seen.add(key) + + sdk_map[platform].append(version_nbr) + + for key, val in sdk_map.items(): + val.sort(reverse=True) + + return sdk_map @classmethod - def _reset_toolsets(cls): - debug('reset: toolset cache') - cls._toolset_version_cache = {} - cls._toolset_default_cache = {} + def _sdk_81_layout(cls, version): - _toolset_version_cache = {} - _toolset_default_cache = {} + version_nbr = version + + sdk_map = cls._new_sdk_map() + + sdk_roots = _Registry.sdk_query_paths(version) + + sdk_version_platform_seen = set() + sdk_roots_seen = set() + + sdk_targets = [] + + for sdk_t in sdk_roots: + + sdk_root = sdk_t[0] + if sdk_root in sdk_roots_seen: + continue + sdk_roots_seen.add(sdk_root) + + # msvc does not check for existence of root or other files + + sdk_inc_path = _Util.process_path(os.path.join(sdk_root, r'include\um')) + if not os.path.exists(sdk_inc_path): + continue + + for platform, sdk_inc_file in [ + ('desktop', 'winsdkver.h'), + ('uwp', 'windows.h'), + ]: + + if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): + continue + + key = (version_nbr, platform) + if key in sdk_version_platform_seen: + continue + sdk_version_platform_seen.add(key) + + sdk_map[platform].append(version_nbr) + + for key, val in sdk_map.items(): + val.sort(reverse=True) + + return sdk_map @classmethod - def _msvc_version_toolsets(cls, msvc, vc_dir): + def _sdk_10(cls, key, reg_version): + if key in cls.sdk_map_cache: + sdk_map = cls.sdk_map_cache[key] + else: + sdk_map = cls._sdk_10_layout(reg_version) + cls.sdk_map_cache[key] = sdk_map + return sdk_map - if msvc.version in cls._toolset_version_cache: - toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version] + @classmethod + def _sdk_81(cls, key, reg_version): + if key in cls.sdk_map_cache: + sdk_map = cls.sdk_map_cache[key] else: - toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir) - cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full + sdk_map = cls._sdk_81_layout(reg_version) + cls.sdk_map_cache[key] = sdk_map + return sdk_map - return toolsets_sxs, toolsets_full + @classmethod + def _combine_sdk_map_list(cls, sdk_map_list): + combined_sdk_map = cls._new_sdk_map() + for sdk_map in sdk_map_list: + for key, val in sdk_map.items(): + combined_sdk_map[key].extend(val) + return combined_sdk_map + + sdk_dispatch_map = None @classmethod - def _msvc_default_toolset(cls, msvc, vc_dir): + def _version_list_sdk_map(cls, version_list): - if msvc.version in cls._toolset_default_cache: - toolset_default = cls._toolset_default_cache[msvc.version] - else: - toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir) - cls._toolset_default_cache[msvc.version] = toolset_default + if not cls.sdk_dispatch_map: + cls.sdk_dispatch_map = { + '10.0': (cls._sdk_10, '10.0'), + '8.1': (cls._sdk_81, '8.1'), + } - return toolset_default + sdk_map_list = [] + for version in version_list: + func, reg_version = cls.sdk_dispatch_map[version] + sdk_map = func(version, reg_version) + sdk_map_list.append(sdk_map) + + combined_sdk_map = cls._combine_sdk_map_list(sdk_map_list) + return combined_sdk_map @classmethod - def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version): + def _sdk_map(cls, version_list): + key = tuple(version_list) + if key in cls.sdk_cache: + sdk_map = cls.sdk_cache[key] + else: + version_numlist = [float(v) for v in version_list] + version_numlist.sort(reverse=True) + key = tuple([str(v) for v in version_numlist]) + sdk_map = cls._version_list_sdk_map(key) + cls.sdk_cache[key] = sdk_map + return sdk_map - if toolset_version == '14.0': - return toolset_version + @classmethod + def _get_sdk_version_list(cls, version_list, platform): + sdk_map = cls._sdk_map(version_list) + sdk_list = sdk_map.get(platform, []) + return sdk_list - toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir) +def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric: - # necessary to detect toolset not found - if toolset_version == '14.28.16.8': - new_toolset_version = '14.28' - # 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. - debug( - 'rewrite toolset_version=%s => toolset_version=%s', - repr(toolset_version), repr(new_toolset_version) - ) - toolset_version = new_toolset_version + sdk_versions = [] - if toolset_version in toolsets_sxs: - toolset_vcvars = toolsets_sxs[toolset_version] - return toolset_vcvars + if not MSVC_VERSION: + vcs = get_installed_vcs() + if not vcs: + return sdk_versions + MSVC_VERSION = vcs[0] - for toolset_full in toolsets_full: - if toolset_full.startswith(toolset_version): - toolset_vcvars = toolset_full - return toolset_vcvars + verstr = get_msvc_version_numeric(MSVC_VERSION) + vs_def = _Const.MSVC_VERSION_EXTERNAL.get(verstr, None) + if not vs_def: + return sdk_versions - return None + is_uwp = True if MSVC_UWP_APP in _Const.BOOLEAN_KEYS[True] else False + platform = 'uwp' if is_uwp else 'desktop' + sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform) + + sdk_versions.extend(sdk_list) + return sdk_versions + +class _ScriptArguments: + + # 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, + } # capture msvc version re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) @@ -1920,223 +2188,210 @@ def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version): # valid SxS formats will be matched with re_toolset_full: match 3 '.' format re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') - @classmethod - def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version): + # 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) - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2017', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) - ) - return err_msg + # Force default sdk argument + MSVC_FORCE_DEFAULT_SDK = False - m = cls.re_toolset_version.match(toolset_version) - if not m: - debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version)) - err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( - repr(toolset_version) - ) - return err_msg + # Force default toolset argument + MSVC_FORCE_DEFAULT_TOOLSET = False - toolset_ver = m.group('version') - toolset_vernum = float(toolset_ver) + # 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+ - if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: toolset version constraint: %s < %s VS2015', - repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( - repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - return err_msg + @enum.unique + class SortOrder(enum.IntEnum): + ARCH = 0 # arch + 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 - if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: toolset version constraint: toolset %s > %s msvc', - repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( - repr(toolset_version), repr(toolset_ver), repr(msvc.version) - ) - return err_msg + VS2019 = _Const.MSVS_VERSION_INTERNAL['2019'] + VS2017 = _Const.MSVS_VERSION_INTERNAL['2017'] + VS2015 = _Const.MSVS_VERSION_INTERNAL['2015'] - if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric: - # tooset = 14.0 - if cls.re_toolset_full.match(toolset_version): - if not cls.re_toolset_140.match(toolset_version): - debug( - 'invalid: toolset version 14.0 constraint: %s != 14.0', - repr(toolset_version) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format( - repr(toolset_version), repr(toolset_version) - ) - return err_msg - return None + MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [ + 'version', # fully qualified msvc version (e.g., '14.1Exp') + 'vs_def', + ]) - if cls.re_toolset_full.match(toolset_version): - debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) - return None + @classmethod + def _msvc_version(cls, version): - if cls.re_toolset_sxs.match(toolset_version): - debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version)) - return None + verstr = get_msvc_version_numeric(version) + vs_def = _Const.MSVC_VERSION_INTERNAL[verstr] - debug('invalid: method exit: toolset_version=%s', repr(toolset_version)) - err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version)) - return err_msg + version_args = cls.MSVC_VERSION_ARGS_DEFINITION( + version = version, + vs_def = vs_def, + ) + + return version_args @classmethod - def _msvc_script_argument_toolset(cls, env, msvc, vc_dir, arglist): + def _msvc_script_argument_uwp(cls, env, msvc, arglist): - toolset_version = env['MSVC_TOOLSET_VERSION'] - debug('MSVC_VERSION=%s, MSVC_TOOLSET_VERSION=%s', repr(msvc.version), repr(toolset_version)) + uwp_app = env['MSVC_UWP_APP'] + debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app)) - if not toolset_version: + if not uwp_app: return None - err_msg = cls._msvc_script_argument_toolset_constraints(msvc, toolset_version) - if err_msg: - raise MSVCArgumentError(err_msg) + if uwp_app not in _Const.BOOLEAN_KEYS[True]: + return None - if toolset_version.startswith('14.0') and len(toolset_version) > len('14.0'): - new_toolset_version = '14.0' + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: debug( - 'rewrite toolset_version=%s => toolset_version=%s', - repr(toolset_version), repr(new_toolset_version) + 'invalid: msvc version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) ) - toolset_version = new_toolset_version - - toolset_vcvars = cls._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) + err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) ) raise MSVCArgumentError(err_msg) - argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars)) + # VS2017+ rewrites uwp => store for 14.0 toolset + uwp_arg = msvc.vs_def.vc_uwp + + # uwp may not be installed + argpair = (cls.SortOrder.UWP, uwp_arg) arglist.append(argpair) - return toolset_vcvars + return uwp_arg @classmethod - def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist): + def _user_script_argument_uwp(cls, env, uwp, user_argstr): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + matches = [m for m in cls.re_vcvars_uwp.finditer(user_argstr)] + if not matches: return None - toolset_default = cls._msvc_default_toolset(msvc, vc_dir) - if not toolset_default: + if len(matches) > 1: + debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) + err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + raise MSVCArgumentError(err_msg) + + if not uwp: return None - debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default)) + 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)) - argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default)) - arglist.append(argpair) + err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format( + repr(env_argstr), repr(user_argstr) + ) - return toolset_default + raise MSVCArgumentError(err_msg) @classmethod - def _msvc_script_argument_spectre(cls, env, msvc, 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 (True, '1'): - return None + def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version): - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: debug( - 'invalid: msvc version constraint: %s < %s VS2017', + 'invalid: msvc_version constraint: %s < %s VS2015', repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) + repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) ) - err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) + err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) ) - raise MSVCArgumentError(err_msg) - - spectre_arg = 'spectre' + return err_msg - # spectre libs may not be installed - argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg)) - arglist.append(argpair) + for msvc_sdk_version in msvc.vs_def.vc_sdk_versions: + re_sdk_version = cls.re_sdk_dispatch_map[msvc_sdk_version] + if re_sdk_version.match(sdk_version): + debug('valid: sdk_version=%s', repr(sdk_version)) + return None - return spectre_arg + debug('invalid: method exit: sdk_version=%s', repr(sdk_version)) + err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version)) + return err_msg @classmethod - def _msvc_script_argument_user(cls, env, msvc, arglist): + def _msvc_script_argument_sdk(cls, env, msvc, platform, 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)) + sdk_version = env['MSVC_SDK_VERSION'] + debug( + 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform=%s', + repr(msvc.version), repr(sdk_version), repr(platform) + ) - if not script_args: + if not sdk_version: return None - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) + err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version) + if err_msg: + raise MSVCArgumentError(err_msg) + + sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform) + + if sdk_version not in sdk_list: + err_msg = "MSVC_SDK_VERSION {} not found for platform {}".format( + repr(sdk_version), repr(platform) ) raise MSVCArgumentError(err_msg) - # user arguments are not validated - argpair = (cls.SortOrder.USER, script_args) + # sdk folder may not exist + argpair = (cls.SortOrder.SDK, sdk_version) arglist.append(argpair) - return script_args - - re_vcvars_uwp = re.compile(r'(?:\s|^)(?P(?:uwp|store))(?:\s|$)',re.IGNORECASE) - re_vcvars_sdk = re.compile(r'(?:\s|^)(?P(?:[1-9][0-9]*[.]\S*))(?:\s|$)',re.IGNORECASE) - re_vcvars_spectre = re.compile(r'(?:\s|^)(?P(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P\S*))(?:\s|$)',re.IGNORECASE) - re_vcvars_toolset = re.compile(r'(?:\s|^)(?P(?:[-]{1,2}|[/])vcvars_ver[=](?P\S*))(?:\s|$)', re.IGNORECASE) + return sdk_version @classmethod - def _user_script_argument_uwp(cls, env, uwp, user_argstr): + def _msvc_script_default_sdk(cls, env, msvc, platform, arglist): - if not uwp: + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: return None - m = cls.re_vcvars_uwp.search(user_argstr) - if not m: + sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform) + if not len(sdk_list): return None - 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)) + sdk_default = sdk_list[0] - err_msg = "multiple uwp declarations: MSVC_UWP_APP={} and MSVC_SCRIPT_ARGS={}".format( - repr(env_argstr), repr(user_argstr) + debug( + 'MSVC_VERSION=%s, sdk_default=%s, platform=%s', + repr(msvc.version), repr(sdk_default), repr(platform) ) - raise MSVCArgumentError(err_msg) + argpair = (cls.SortOrder.SDK, sdk_default) + arglist.append(argpair) + + return sdk_default @classmethod def _user_script_argument_sdk(cls, env, sdk_version, user_argstr): - if not sdk_version: + matches = [m for m in cls.re_vcvars_sdk.finditer(user_argstr)] + if not matches: return None - m = cls.re_vcvars_sdk.search(user_argstr) - if not m: - return None + if len(matches) > 1: + debug('multiple sdk version declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) + err_msg = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + 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)) @@ -2148,33 +2403,374 @@ def _user_script_argument_sdk(cls, env, sdk_version, user_argstr): raise MSVCArgumentError(err_msg) @classmethod - def _user_script_argument_toolset(cls, env, toolset_version, user_argstr): + def _msvc_read_toolset_file(cls, 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 - m = cls.re_vcvars_toolset.search(user_argstr) - if not m: - return None + @classmethod + def _msvc_read_toolset_folders(cls, msvc, vc_dir): - if not toolset_version: - user_toolset = m.group('toolset') - return user_toolset + toolsets_sxs = {} + toolsets_full = [] - 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)) + build_dir = os.path.join(vc_dir, "Auxiliary", "Build") + sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()] + for sxs_toolset in sxs_toolsets: + filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) + filepath = os.path.join(build_dir, sxs_toolset, filename) + debug('sxs toolset: check file=%s', repr(filepath)) + if os.path.exists(filepath): + toolset_version = cls._msvc_read_toolset_file(msvc, filepath) + if not toolset_version: + continue + toolsets_sxs[sxs_toolset] = toolset_version + debug( + 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', + repr(msvc.version), repr(sxs_toolset), toolset_version + ) - err_msg = "multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS={}".format( - repr(env_argstr), repr(user_argstr) - ) + toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") + toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] + for toolset_version in toolsets: + binpath = os.path.join(toolset_dir, toolset_version, "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) + ) - raise MSVCArgumentError(err_msg) + toolsets_full.sort(reverse=True) + debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full)) + + return toolsets_sxs, toolsets_full + + @classmethod + def _msvc_read_toolset_default(cls, msvc, vc_dir): + + build_dir = os.path.join(vc_dir, "Auxiliary", "Build") + + # VS2019+ + filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools) + filepath = os.path.join(build_dir, filename) + + debug('default toolset: check file=%s', repr(filepath)) + toolset_buildtools = None + if os.path.exists(filepath): + toolset_buildtools = cls._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)) + toolset_default = None + if os.path.exists(filepath): + toolset_default = cls._msvc_read_toolset_file(msvc, filepath) + if toolset_default: + return toolset_default + + return None + + @classmethod + def _reset_toolsets(cls): + debug('reset: toolset cache') + cls._toolset_version_cache = {} + cls._toolset_default_cache = {} + + _toolset_version_cache = {} + _toolset_default_cache = {} + + @classmethod + def _msvc_version_toolsets(cls, msvc, vc_dir): + + if msvc.version in cls._toolset_version_cache: + toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version] + else: + toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir) + cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full + + return toolsets_sxs, toolsets_full + + @classmethod + def _msvc_default_toolset(cls, msvc, vc_dir): + + if msvc.version in cls._toolset_default_cache: + toolset_default = cls._toolset_default_cache[msvc.version] + else: + toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir) + cls._toolset_default_cache[msvc.version] = toolset_default + + return toolset_default + + @classmethod + def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version): + + if toolset_version == '14.0': + return toolset_version + + toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir) + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric: + # necessary to detect toolset not found + if toolset_version == '14.28.16.8': + new_toolset_version = '14.28' + # 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. + debug( + 'rewrite toolset_version=%s => toolset_version=%s', + repr(toolset_version), repr(new_toolset_version) + ) + toolset_version = new_toolset_version + + if toolset_version in toolsets_sxs: + toolset_vcvars = toolsets_sxs[toolset_version] + return toolset_vcvars + + for toolset_full in toolsets_full: + if toolset_full.startswith(toolset_version): + toolset_vcvars = toolset_full + return toolset_vcvars + + return None + + @classmethod + def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version): + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2017', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( + repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) + ) + return err_msg + + m = cls.re_toolset_version.match(toolset_version) + if not m: + debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version)) + err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( + repr(toolset_version) + ) + return err_msg + + toolset_ver = m.group('version') + toolset_vernum = float(toolset_ver) + + if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: %s < %s VS2015', + repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( + repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version) + ) + return err_msg + + if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: toolset %s > %s msvc', + repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( + repr(toolset_version), repr(toolset_ver), repr(msvc.version) + ) + return err_msg + + if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric: + # tooset = 14.0 + if cls.re_toolset_full.match(toolset_version): + if not cls.re_toolset_140.match(toolset_version): + debug( + 'invalid: toolset version 14.0 constraint: %s != 14.0', + repr(toolset_version) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format( + repr(toolset_version), repr(toolset_version) + ) + return err_msg + return None + + if cls.re_toolset_full.match(toolset_version): + debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) + return None + + if cls.re_toolset_sxs.match(toolset_version): + debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version)) + return None + + debug('invalid: method exit: toolset_version=%s', repr(toolset_version)) + err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version)) + return err_msg + + @classmethod + def _msvc_script_argument_toolset(cls, 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 + + err_msg = cls._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 = cls._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 MSVCArgumentError(err_msg) + + argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars)) + arglist.append(argpair) + + return toolset_vcvars + + @classmethod + def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist): + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + return None + + toolset_default = cls._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)) + + argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default)) + arglist.append(argpair) + + return toolset_default + + @classmethod + def _user_script_argument_toolset(cls, env, toolset_version, user_argstr): + + matches = [m for m in cls.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 = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + 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) + + @classmethod + def _msvc_script_argument_spectre(cls, env, msvc, 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 (True, '1'): + return None + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2017', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( + repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) + ) + raise MSVCArgumentError(err_msg) + + spectre_arg = 'spectre' + + # spectre libs may not be installed + argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg)) + arglist.append(argpair) + + return spectre_arg + + @classmethod + def _msvc_script_argument_user(cls, 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.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) + ) + raise MSVCArgumentError(err_msg) + + # user arguments are not validated + argpair = (cls.SortOrder.USER, script_args) + arglist.append(argpair) + + return script_args @classmethod def _user_script_argument_spectre(cls, env, spectre, user_argstr): - if not spectre: + matches = [m for m in cls.re_vcvars_spectre.finditer(user_argstr)] + if not matches: return None - m = cls.re_vcvars_spectre.search(user_argstr) - if not m: + if len(matches) > 1: + debug('multiple spectre declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) + err_msg = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + raise MSVCArgumentError(err_msg) + + if not spectre: return None env_argstr = env.get('MSVC_SPECTRE_LIBS','') @@ -2189,55 +2785,70 @@ def _user_script_argument_spectre(cls, env, spectre, user_argstr): @classmethod def msvc_script_arguments(cls, env, version, vc_dir, arg): - msvc = cls._msvc_version(version) - - argstr = '' arglist = [] + msvc = cls._msvc_version(version) + if arg: argpair = (cls.SortOrder.ARCH, arg) arglist.append(argpair) - user_argstr = None - user_toolset = None - - uwp = None - sdk_version = None - toolset_version = None - spectre = None - if 'MSVC_SCRIPT_ARGS' in env: user_argstr = cls._msvc_script_argument_user(env, msvc, arglist) + else: + user_argstr = None if 'MSVC_UWP_APP' in env: uwp = cls._msvc_script_argument_uwp(env, msvc, arglist) - if uwp and user_argstr: - cls._user_script_argument_uwp(env, uwp, user_argstr) + else: + uwp = None + + if user_argstr: + cls._user_script_argument_uwp(env, uwp, user_argstr) + + platform = 'uwp' if uwp else 'desktop' if 'MSVC_SDK_VERSION' in env: - is_uwp = True if uwp else False - sdk_version = cls._msvc_script_argument_sdk(env, msvc, is_uwp, arglist) - if sdk_version and user_argstr: - cls._user_script_argument_sdk(env, sdk_version, user_argstr) + sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform, arglist) + else: + sdk_version = None + + if user_argstr: + user_sdk = cls._user_script_argument_sdk(env, sdk_version, user_argstr) + else: + user_sdk = None + + if cls.MSVC_FORCE_DEFAULT_SDK: + if not sdk_version and not user_sdk: + sdk_version = cls._msvc_script_default_sdk(env, msvc, platform, arglist) if 'MSVC_TOOLSET_VERSION' in env: toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist) + else: + toolset_version = None if user_argstr: user_toolset = cls._user_script_argument_toolset(env, toolset_version, user_argstr) + else: + user_toolset = None - if cls.MSVC_TOOLSET_DEFAULT_VCVARSVER: + if cls.MSVC_FORCE_DEFAULT_TOOLSET: if not toolset_version and not user_toolset: toolset_version = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist) if 'MSVC_SPECTRE_LIBS' in env: spectre = cls._msvc_script_argument_spectre(env, msvc, arglist) - if spectre and user_argstr: - cls._user_script_argument_spectre(env, spectre, user_argstr) + else: + spectre = None + + if user_argstr: + cls._user_script_argument_spectre(env, spectre, user_argstr) if arglist: arglist.sort() argstr = ' '.join([argpair[-1] for argpair in arglist]).strip() + else: + argstr = '' debug('arguments: %s', repr(argstr)) return argstr @@ -2247,231 +2858,3 @@ def reset(cls): debug('reset') cls._reset_toolsets() -def msvc_find_valid_batch_script(env, version): - """Find and execute appropriate batch script to set up build env. - - The MSVC build environment depends heavily on having the shell - environment set. SCons does not inherit that, and does not count - on that being set up correctly anyway, so it tries to find the right - MSVC batch script, or the right arguments to the generic batch script - vcvarsall.bat, and run that, so we have a valid environment to build in. - There are dragons here: the batch scripts don't fail (see comments - elsewhere), they just leave you with a bad setup, so try hard to - get it right. - """ - - # Find the host, target, and all candidate (host, target) platform combinations: - platforms = get_host_target(env, version) - debug("host_platform %s, target_platform %s host_target_list %s", *platforms) - host_platform, target_platform, host_target_list = platforms - - d = None - version_installed = False - for host_arch, target_arch, in host_target_list: - # Set to current arch. - env['TARGET_ARCH'] = target_arch - arg = '' - - # Try to locate a batch file for this host/target platform combo - try: - (vc_script, arg, vc_dir, sdk_script) = find_batch_file(env, version, host_arch, target_arch) - debug('vc_script:%s vc_script_arg:%s sdk_script:%s', vc_script, arg, sdk_script) - version_installed = True - except VisualCException as e: - msg = str(e) - debug('Caught exception while looking for batch file (%s)', msg) - version_installed = False - continue - - # Try to use the located batch file for this host/target platform combo - debug('use_script 2 %s, args:%s', repr(vc_script), arg) - found = None - if vc_script: - arg = _MSVCScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) - try: - d = script_env(vc_script, args=arg) - found = vc_script - except BatchFileExecutionError as e: - debug('use_script 3: failed running VC script %s: %s: Error:%s', repr(vc_script), arg, e) - vc_script=None - continue - if not vc_script and sdk_script: - debug('use_script 4: trying sdk script: %s', sdk_script) - try: - d = script_env(sdk_script) - found = sdk_script - except BatchFileExecutionError as e: - debug('use_script 5: failed running SDK script %s: Error:%s', repr(sdk_script), e) - continue - elif not vc_script and not sdk_script: - debug('use_script 6: Neither VC script nor SDK script found') - continue - - debug("Found a working script/target: %s/%s", repr(found), arg) - break # We've found a working target_platform, so stop looking - - # If we cannot find a viable installed compiler, reset the TARGET_ARCH - # To it's initial value - if not d: - env['TARGET_ARCH'] = target_platform - - if version_installed: - msg = "MSVC version '{}' working host/target script was not found.\n" \ - " Host = '{}', Target = '{}'\n" \ - " Visual Studio C/C++ compilers may not be set correctly".format( - version, host_platform, target_platform - ) - else: - installed_vcs = get_installed_vcs(env) - if installed_vcs: - msg = "MSVC version '{}' was not found.\n" \ - " Visual Studio C/C++ compilers may not be set correctly.\n" \ - " Installed versions are: {}".format(version, installed_vcs) - else: - msg = "MSVC version '{}' was not found.\n" \ - " No versions of the MSVC compiler were found.\n" \ - " Visual Studio C/C++ compilers may not be set correctly".format(version) - - _msvc_notfound_policy_handler(env, msg) - - return d - -_undefined = None - -def get_use_script_use_settings(env): - global _undefined - - if _undefined is None: - _undefined = object() - - # use_script use_settings return values action - # value ignored (value, None) use script or bypass detection - # undefined value not None (False, value) use dictionary - # undefined undefined/None (True, None) msvc detection - - # None (documentation) or evaluates False (code): bypass detection - # need to distinguish between undefined and None - use_script = env.get('MSVC_USE_SCRIPT', _undefined) - - if use_script != _undefined: - # use_script defined, use_settings ignored (not type checked) - return (use_script, None) - - # undefined or None: use_settings ignored - use_settings = env.get('MSVC_USE_SETTINGS', None) - - if use_settings is not None: - # use script undefined, use_settings defined and not None (type checked) - return (False, use_settings) - - # use script undefined, use_settings undefined or None - return (True, None) - -def msvc_setup_env(env): - debug('called') - version = get_default_version(env) - if version is None: - if not msvc_setup_env_user(env): - _MSVCSetupEnvDefault.set_nodefault() - return None - - # XXX: we set-up both MSVS version for backward - # compatibility with the msvs tool - env['MSVC_VERSION'] = version - env['MSVS_VERSION'] = version - env['MSVS'] = {} - - use_script, use_settings = get_use_script_use_settings(env) - if SCons.Util.is_String(use_script): - use_script = use_script.strip() - if not os.path.exists(use_script): - raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script)) - args = env.subst('$MSVC_USE_SCRIPT_ARGS') - debug('use_script 1 %s %s', repr(use_script), repr(args)) - d = script_env(use_script, args) - elif use_script: - d = msvc_find_valid_batch_script(env,version) - debug('use_script 2 %s', d) - if not d: - return d - elif use_settings is not None: - if not SCons.Util.is_Dict(use_settings): - error_msg = 'MSVC_USE_SETTINGS type error: expected a dictionary, found {}'.format(type(use_settings).__name__) - raise MSVCUseSettingsError(error_msg) - d = use_settings - debug('use_settings %s', d) - else: - debug('MSVC_USE_SCRIPT set to False') - warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ - "set correctly." - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - return None - - for k, v in d.items(): - env.PrependENVPath(k, v, delete_existing=True) - debug("env['ENV']['%s'] = %s", k, env['ENV'][k]) - - # final check to issue a warning if the compiler is not present - if not find_program_path(env, 'cl'): - debug("did not find %s", _CL_EXE_NAME) - if CONFIG_CACHE: - propose = "SCONS_CACHE_MSVC_CONFIG caching enabled, remove cache file {} if out of date.".format(CONFIG_CACHE) - else: - propose = "It may need to be installed separately with Visual Studio." - warn_msg = "Could not find MSVC compiler 'cl'. {}".format(propose) - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) - -def msvc_exists(env=None, version=None): - debug('version=%s', repr(version)) - vcs = get_installed_vcs(env) - if version is None: - rval = len(vcs) > 0 - else: - rval = version in vcs - debug('version=%s, return=%s', repr(version), rval) - return rval - -def msvc_setup_env_user(env=None): - rval = False - if env: - - # Intent is to use msvc tools: - # MSVC_VERSION or MSVS_VERSION: defined and is True - # MSVC_USE_SCRIPT: defined and (is string or is False) - # MSVC_USE_SETTINGS: defined and is not None - - # defined and is True - for key in ['MSVC_VERSION', 'MSVS_VERSION']: - if key in env and env[key]: - rval = True - debug('key=%s, return=%s', repr(key), rval) - return rval - - # defined and (is string or is False) - for key in ['MSVC_USE_SCRIPT']: - if key in env and (SCons.Util.is_String(env[key]) or not env[key]): - rval = True - debug('key=%s, return=%s', repr(key), rval) - return rval - - # defined and is not None - for key in ['MSVC_USE_SETTINGS']: - if key in env and env[key] is not None: - rval = True - debug('key=%s, return=%s', repr(key), rval) - return rval - - debug('return=%s', rval) - return rval - -def msvc_setup_env_tool(env=None, version=None, tool=None): - debug('tool=%s, version=%s', repr(tool), repr(version)) - _MSVCSetupEnvDefault.register_tool(env, tool) - rval = False - if not rval and msvc_exists(env, version): - rval = True - if not rval and msvc_setup_env_user(env): - rval = True - debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval) - return rval - From 33bef70b9ccaac0106d5e7f0c37211d224b6a585 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 16 Jun 2022 12:43:22 -0400 Subject: [PATCH 010/108] Rename msvc batch platform (uwp/store/desktop) to platform_type. --- SCons/Tool/MSCommon/vc.py | 50 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 26 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index c44c698ecd..acffc7cead 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -106,7 +106,7 @@ class _Const: for bool, symbol_list in [ (False, (0, '0', False, 'False', 'FALSE', 'false', 'No', 'NO', 'no', None, '')), - (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes', )), + (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes')), ]: BOOLEAN_KEYS[bool] = symbol_list for symbol in symbol_list: @@ -2007,7 +2007,7 @@ def _sdk_10_layout(cls, version): if not os.path.exists(sdk_inc_path): continue - for platform, sdk_inc_file in [ + for platform_type, sdk_inc_file in [ ('desktop', 'winsdkver.h'), ('uwp', 'windows.h'), ]: @@ -2015,12 +2015,12 @@ def _sdk_10_layout(cls, version): if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): continue - key = (version_nbr, platform) + key = (version_nbr, platform_type) if key in sdk_version_platform_seen: continue sdk_version_platform_seen.add(key) - sdk_map[platform].append(version_nbr) + sdk_map[platform_type].append(version_nbr) for key, val in sdk_map.items(): val.sort(reverse=True) @@ -2039,8 +2039,6 @@ def _sdk_81_layout(cls, version): sdk_version_platform_seen = set() sdk_roots_seen = set() - sdk_targets = [] - for sdk_t in sdk_roots: sdk_root = sdk_t[0] @@ -2054,7 +2052,7 @@ def _sdk_81_layout(cls, version): if not os.path.exists(sdk_inc_path): continue - for platform, sdk_inc_file in [ + for platform_type, sdk_inc_file in [ ('desktop', 'winsdkver.h'), ('uwp', 'windows.h'), ]: @@ -2062,12 +2060,12 @@ def _sdk_81_layout(cls, version): if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): continue - key = (version_nbr, platform) + key = (version_nbr, platform_type) if key in sdk_version_platform_seen: continue sdk_version_platform_seen.add(key) - sdk_map[platform].append(version_nbr) + sdk_map[platform_type].append(version_nbr) for key, val in sdk_map.items(): val.sort(reverse=True) @@ -2134,9 +2132,9 @@ def _sdk_map(cls, version_list): return sdk_map @classmethod - def _get_sdk_version_list(cls, version_list, platform): + def _get_sdk_version_list(cls, version_list, platform_type): sdk_map = cls._sdk_map(version_list) - sdk_list = sdk_map.get(platform, []) + sdk_list = sdk_map.get(platform_type, []) return sdk_list def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): @@ -2155,8 +2153,8 @@ def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): return sdk_versions is_uwp = True if MSVC_UWP_APP in _Const.BOOLEAN_KEYS[True] else False - platform = 'uwp' if is_uwp else 'desktop' - sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform) + platform_type = 'uwp' if is_uwp else 'desktop' + sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) sdk_versions.extend(sdk_list) return sdk_versions @@ -2326,12 +2324,12 @@ def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version): return err_msg @classmethod - def _msvc_script_argument_sdk(cls, env, msvc, platform, arglist): + def _msvc_script_argument_sdk(cls, env, msvc, platform_type, arglist): sdk_version = env['MSVC_SDK_VERSION'] debug( - 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform=%s', - repr(msvc.version), repr(sdk_version), repr(platform) + 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform_type=%s', + repr(msvc.version), repr(sdk_version), repr(platform_type) ) if not sdk_version: @@ -2341,11 +2339,11 @@ def _msvc_script_argument_sdk(cls, env, msvc, platform, arglist): if err_msg: raise MSVCArgumentError(err_msg) - sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform) + sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) if sdk_version not in sdk_list: - err_msg = "MSVC_SDK_VERSION {} not found for platform {}".format( - repr(sdk_version), repr(platform) + err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format( + repr(sdk_version), repr(platform_type) ) raise MSVCArgumentError(err_msg) @@ -2356,20 +2354,20 @@ def _msvc_script_argument_sdk(cls, env, msvc, platform, arglist): return sdk_version @classmethod - def _msvc_script_default_sdk(cls, env, msvc, platform, arglist): + def _msvc_script_default_sdk(cls, env, msvc, platform_type, arglist): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: return None - sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform) + sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) if not len(sdk_list): return None sdk_default = sdk_list[0] debug( - 'MSVC_VERSION=%s, sdk_default=%s, platform=%s', - repr(msvc.version), repr(sdk_default), repr(platform) + 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s', + repr(msvc.version), repr(sdk_default), repr(platform_type) ) argpair = (cls.SortOrder.SDK, sdk_default) @@ -2806,10 +2804,10 @@ def msvc_script_arguments(cls, env, version, vc_dir, arg): if user_argstr: cls._user_script_argument_uwp(env, uwp, user_argstr) - platform = 'uwp' if uwp else 'desktop' + platform_type = 'uwp' if uwp else 'desktop' if 'MSVC_SDK_VERSION' in env: - sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform, arglist) + sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform_type, arglist) else: sdk_version = None @@ -2820,7 +2818,7 @@ def msvc_script_arguments(cls, env, version, vc_dir, arg): if cls.MSVC_FORCE_DEFAULT_SDK: if not sdk_version and not user_sdk: - sdk_version = cls._msvc_script_default_sdk(env, msvc, platform, arglist) + sdk_version = cls._msvc_script_default_sdk(env, msvc, platform_type, arglist) if 'MSVC_TOOLSET_VERSION' in env: toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist) From 716f93a5207d8e7c941f3ff38ef2b3b0c923c479 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 16 Jun 2022 13:04:52 -0400 Subject: [PATCH 011/108] Update comments --- SCons/Tool/MSCommon/vc.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index acffc7cead..e29fba4481 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -2269,7 +2269,7 @@ def _msvc_script_argument_uwp(cls, env, msvc, arglist): # VS2017+ rewrites uwp => store for 14.0 toolset uwp_arg = msvc.vs_def.vc_uwp - # uwp may not be installed + # store/uwp may not be fully installed argpair = (cls.SortOrder.UWP, uwp_arg) arglist.append(argpair) @@ -2347,7 +2347,6 @@ def _msvc_script_argument_sdk(cls, env, msvc, platform_type, arglist): ) raise MSVCArgumentError(err_msg) - # sdk folder may not exist argpair = (cls.SortOrder.SDK, sdk_version) arglist.append(argpair) From ac6045d37df4d53d73cbf33323b33141dbb67e58 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 16 Jun 2022 16:07:59 -0400 Subject: [PATCH 012/108] Add dispatch class for reset and verify methods. Verify data structure consistency for msvc sdk versions. --- SCons/Tool/MSCommon/vc.py | 188 +++++++++++++++++++++++++++++--------- 1 file changed, 144 insertions(+), 44 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index e29fba4481..26a2fd605c 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -95,22 +95,55 @@ class MSVCVersionNotFound(VisualCException): class MSVCArgumentError(VisualCException): pass +class MSVCInternalError(VisualCException): + pass + class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): pass +class _Manager: + + classrefs = [] + + @classmethod + def register(cls, classref): + cls.classrefs.append(classref) + + @classmethod + def reset(cls): + debug('reset %s', cls.__name__) + for classref in cls.classrefs: + for method in ['reset', '_reset']: + if not hasattr(classref, method) or not callable(getattr(classref, method, None)): + continue + debug('call %s.%s()', classref.__name__, method) + func = getattr(classref, method) + func() + + @classmethod + def verify(cls): + debug('verify %s', cls.__name__) + for classref in cls.classrefs: + for method in ['verify', '_verify']: + if not hasattr(classref, method) or not callable(getattr(classref, method, None)): + continue + debug('call %s.%s()', classref.__name__, method) + func = getattr(classref, method) + func() + class _Const: - BOOLEAN_KEYS = {} BOOLEAN_SYMBOLS = {} + BOOLEAN_EXTERNAL = {} for bool, symbol_list in [ (False, (0, '0', False, 'False', 'FALSE', 'false', 'No', 'NO', 'no', None, '')), (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes')), ]: - BOOLEAN_KEYS[bool] = symbol_list + BOOLEAN_SYMBOLS[bool] = symbol_list for symbol in symbol_list: - BOOLEAN_SYMBOLS[symbol] = bool + BOOLEAN_EXTERNAL[symbol] = bool MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ 'vc_runtime', @@ -212,6 +245,8 @@ class _Const: CL_VERSION_MAP = {} + MSVC_SDK_VERSIONS = set() + VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [ 'vs_product', 'vs_product_alias_list', @@ -293,6 +328,14 @@ class _Const: CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def + if not vc_sdk: + continue + + MSVC_SDK_VERSIONS.update(vc_sdk) + + # convert string version set to string version list ranked in descending order + MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)] + MSVS_VERSION_LEGACY = {} MSVC_VERSION_LEGACY = {} @@ -1207,9 +1250,7 @@ def reset_installed_vcs(): """Make it try again to find VC. This is just for the tests.""" global __INSTALLED_VCS_RUN __INSTALLED_VCS_RUN = None - _MSVCSetupEnvDefault.reset() - _WindowsSDK.reset() - _ScriptArguments.reset() + _Manager.reset() # Running these batch files isn't cheap: most of the time spent in # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" @@ -1581,6 +1622,8 @@ def register_iserror(cls, env, tool): # return tool list in order presented return tools_found_list +_Manager.register(_MSVCSetupEnvDefault) + def get_default_version(env): msvc_version = env.get('MSVC_VERSION') msvs_version = env.get('MSVS_VERSION') @@ -1956,14 +1999,6 @@ def windows_kit_query_paths(cls, version): class _WindowsSDK: - sdk_map_cache = {} - sdk_cache = {} - - @classmethod - def reset(cls): - cls.sdk_map_cache = {} - cls.sdk_cache = {} - @classmethod def _new_sdk_map(cls): sdk_map = { @@ -2072,6 +2107,15 @@ def _sdk_81_layout(cls, version): return sdk_map + sdk_map_cache = {} + sdk_cache = {} + + @classmethod + def _reset_sdk_cache(cls): + debug('reset %s: sdk cache', cls.__name__) + cls._sdk_map_cache = {} + cls._sdk_cache = {} + @classmethod def _sdk_10(cls, key, reg_version): if key in cls.sdk_map_cache: @@ -2100,14 +2144,29 @@ def _combine_sdk_map_list(cls, sdk_map_list): sdk_dispatch_map = None + @classmethod + def _init_sdk_dispatch_map(cls): + cls.sdk_dispatch_map = { + '10.0': (cls._sdk_10, '10.0'), + '8.1': (cls._sdk_81, '8.1'), + } + + @classmethod + def _verify_sdk_dispatch_map(cls): + debug('%s verify sdk_dispatch_map', cls.__name__) + cls._init_sdk_dispatch_map() + for sdk_version in _Const.MSVC_SDK_VERSIONS: + if sdk_version in cls.sdk_dispatch_map: + continue + err_msg = 'sdk version {} not in {}.sdk_dispatch_map'.format(sdk_version, cls.__name__) + raise MSVCInternalError(err_msg) + return None + @classmethod def _version_list_sdk_map(cls, version_list): if not cls.sdk_dispatch_map: - cls.sdk_dispatch_map = { - '10.0': (cls._sdk_10, '10.0'), - '8.1': (cls._sdk_81, '8.1'), - } + cls._init_sdk_dispatch_map() sdk_map_list = [] for version in version_list: @@ -2132,32 +2191,54 @@ def _sdk_map(cls, version_list): return sdk_map @classmethod - def _get_sdk_version_list(cls, version_list, platform_type): + def get_sdk_version_list(cls, version_list, platform_type): sdk_map = cls._sdk_map(version_list) sdk_list = sdk_map.get(platform_type, []) return sdk_list -def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): + @classmethod + def get_msvc_sdk_version_list(cls, msvc_version=None, msvc_uwp_app=False): + debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app)) + + sdk_versions = [] + + if not msvc_version: + vcs = get_installed_vcs() + if not vcs: + debug('no msvc versions detected') + return sdk_versions + msvc_version = vcs[0] + + verstr = get_msvc_version_numeric(msvc_version) + vs_def = _Const.MSVC_VERSION_EXTERNAL.get(verstr, None) + if not vs_def: + debug('vs_def is not defined') + return sdk_versions - sdk_versions = [] + is_uwp = True if msvc_uwp_app in _Const.BOOLEAN_SYMBOLS[True] else False + platform_type = 'uwp' if is_uwp else 'desktop' + sdk_list = _WindowsSDK.get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) - if not MSVC_VERSION: - vcs = get_installed_vcs() - if not vcs: - return sdk_versions - MSVC_VERSION = vcs[0] + sdk_versions.extend(sdk_list) + debug('sdk_versions=%s', repr(sdk_versions)) - verstr = get_msvc_version_numeric(MSVC_VERSION) - vs_def = _Const.MSVC_VERSION_EXTERNAL.get(verstr, None) - if not vs_def: return sdk_versions - is_uwp = True if MSVC_UWP_APP in _Const.BOOLEAN_KEYS[True] else False - platform_type = 'uwp' if is_uwp else 'desktop' - sdk_list = _WindowsSDK._get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) + @classmethod + def reset(cls): + debug('reset %s', cls.__name__) + cls._reset_sdk_cache() + + @classmethod + def verify(cls): + debug('verify %s', cls.__name__) + cls._verify_sdk_dispatch_map() + +_Manager.register(_WindowsSDK) - sdk_versions.extend(sdk_list) - return sdk_versions +def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): + debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(MSVC_VERSION), repr(MSVC_UWP_APP)) + return _WindowsSDK.get_msvc_sdk_version_list(msvc_version=MSVC_VERSION, msvc_uwp_app=MSVC_UWP_APP) class _ScriptArguments: @@ -2170,6 +2251,16 @@ class _ScriptArguments: '8.1': re_sdk_version_81, } + @classmethod + def _verify_re_sdk_dispatch_map(cls): + debug('%s verify re_sdk_dispatch_map', cls.__name__) + for sdk_version in _Const.MSVC_SDK_VERSIONS: + if sdk_version in cls.re_sdk_dispatch_map: + continue + err_msg = 'sdk version {} not in {}.re_sdk_dispatch_map'.format(sdk_version, cls.__name__) + raise MSVCInternalError(err_msg) + return None + # capture msvc version re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) @@ -2252,7 +2343,7 @@ def _msvc_script_argument_uwp(cls, env, msvc, arglist): if not uwp_app: return None - if uwp_app not in _Const.BOOLEAN_KEYS[True]: + if uwp_app not in _Const.BOOLEAN_SYMBOLS[True]: return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: @@ -2339,7 +2430,7 @@ def _msvc_script_argument_sdk(cls, env, msvc, platform_type, arglist): if err_msg: raise MSVCArgumentError(err_msg) - sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) + sdk_list = _WindowsSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) if sdk_version not in sdk_list: err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format( @@ -2358,7 +2449,7 @@ def _msvc_script_default_sdk(cls, env, msvc, platform_type, arglist): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: return None - sdk_list = _WindowsSDK._get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) + sdk_list = _WindowsSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) if not len(sdk_list): return None @@ -2483,15 +2574,15 @@ def _msvc_read_toolset_default(cls, msvc, vc_dir): return None + _toolset_version_cache = {} + _toolset_default_cache = {} + @classmethod - def _reset_toolsets(cls): - debug('reset: toolset cache') + def _reset_toolset_cache(cls): + debug('reset %s: toolset cache', cls.__name__) cls._toolset_version_cache = {} cls._toolset_default_cache = {} - _toolset_version_cache = {} - _toolset_default_cache = {} - @classmethod def _msvc_version_toolsets(cls, msvc, vc_dir): @@ -2852,6 +2943,15 @@ def msvc_script_arguments(cls, env, version, vc_dir, arg): @classmethod def reset(cls): - debug('reset') - cls._reset_toolsets() + debug('reset %s', cls.__name__) + cls._reset_toolset_cache() + + @classmethod + def verify(cls): + debug('verify %s', cls.__name__) + cls._verify_re_sdk_dispatch_map() + +_Manager.register(_ScriptArguments) +# internal consistency check +_Manager.verify() From 736c7e5a7406dd2dcb5b988a330701f8eed70119 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:02:17 -0400 Subject: [PATCH 013/108] Rename Manager to Dispatcher --- SCons/Tool/MSCommon/vc.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 26a2fd605c..ec508a85c6 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -102,7 +102,7 @@ class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): pass -class _Manager: +class _Dispatcher: classrefs = [] @@ -1250,7 +1250,7 @@ def reset_installed_vcs(): """Make it try again to find VC. This is just for the tests.""" global __INSTALLED_VCS_RUN __INSTALLED_VCS_RUN = None - _Manager.reset() + _Dispatcher.reset() # Running these batch files isn't cheap: most of the time spent in # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" @@ -1622,7 +1622,7 @@ def register_iserror(cls, env, tool): # return tool list in order presented return tools_found_list -_Manager.register(_MSVCSetupEnvDefault) +_Dispatcher.register(_MSVCSetupEnvDefault) def get_default_version(env): msvc_version = env.get('MSVC_VERSION') @@ -2234,7 +2234,7 @@ def verify(cls): debug('verify %s', cls.__name__) cls._verify_sdk_dispatch_map() -_Manager.register(_WindowsSDK) +_Dispatcher.register(_WindowsSDK) def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(MSVC_VERSION), repr(MSVC_UWP_APP)) @@ -2951,7 +2951,7 @@ def verify(cls): debug('verify %s', cls.__name__) cls._verify_re_sdk_dispatch_map() -_Manager.register(_ScriptArguments) +_Dispatcher.register(_ScriptArguments) # internal consistency check -_Manager.verify() +_Dispatcher.verify() From a5cb849e6584f5feb9240f4ca773799ac7a6de57 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:10:21 -0400 Subject: [PATCH 014/108] Remove unnecessary variable initialization --- SCons/Tool/MSCommon/vc.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index ec508a85c6..ae5cf3c417 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -2555,7 +2555,6 @@ def _msvc_read_toolset_default(cls, msvc, vc_dir): filepath = os.path.join(build_dir, filename) debug('default toolset: check file=%s', repr(filepath)) - toolset_buildtools = None if os.path.exists(filepath): toolset_buildtools = cls._msvc_read_toolset_file(msvc, filepath) if toolset_buildtools: @@ -2566,7 +2565,6 @@ def _msvc_read_toolset_default(cls, msvc, vc_dir): filepath = os.path.join(build_dir, filename) debug('default toolset: check file=%s', repr(filepath)) - toolset_default = None if os.path.exists(filepath): toolset_default = cls._msvc_read_toolset_file(msvc, filepath) if toolset_default: From 27cd6c4f48674d065e730dc06249367d1037430d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 17 Jun 2022 08:20:47 -0400 Subject: [PATCH 015/108] Update boolean symbols --- SCons/Tool/MSCommon/vc.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index ae5cf3c417..e193d91680 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -137,12 +137,15 @@ class _Const: BOOLEAN_SYMBOLS = {} BOOLEAN_EXTERNAL = {} - for bool, symbol_list in [ - (False, (0, '0', False, 'False', 'FALSE', 'false', 'No', 'NO', 'no', None, '')), - (True, (1, '1', True, 'True', 'TRUE', 'true', 'Yes', 'YES', 'yes')), + for bool, symbol_list, symbol_case_list in [ + (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')), + (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')), ]: - BOOLEAN_SYMBOLS[bool] = symbol_list - for symbol in symbol_list: + BOOLEAN_SYMBOLS[bool] = list(symbol_list) + for symbol in symbol_case_list: + BOOLEAN_SYMBOLS[bool].extend([symbol, symbol.lower(), symbol.upper()]) + + for symbol in BOOLEAN_SYMBOLS[bool]: BOOLEAN_EXTERNAL[symbol] = bool MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ From 94b23f77472aabaabc372b0facb26c4f384413c5 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 12:11:24 -0400 Subject: [PATCH 016/108] Rename _Const to _Config. Rework msvc sdk version function. Minor cleanup. --- SCons/Tool/MSCommon/__init__.py | 1 + SCons/Tool/MSCommon/vc.py | 68 +++++++++++++++++---------------- 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 9d8a8ffc4e..de78f8459a 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -40,6 +40,7 @@ msvc_find_vswhere, set_msvc_notfound_policy, get_msvc_notfound_policy, + get_msvc_sdk_versions, ) from SCons.Tool.MSCommon.vs import ( diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index e193d91680..3abd60610e 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -132,7 +132,7 @@ def verify(cls): func = getattr(classref, method) func() -class _Const: +class _Config: BOOLEAN_SYMBOLS = {} BOOLEAN_EXTERNAL = {} @@ -1255,6 +1255,12 @@ def reset_installed_vcs(): __INSTALLED_VCS_RUN = None _Dispatcher.reset() +def get_default_installed_msvc(env=None): + vcs = get_installed_vcs(env) + msvc_version = vcs[0] if vcs else None + debug('msvc_version=%s', repr(msvc_version)) + return msvc_version + # Running these batch files isn't cheap: most of the time spent in # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" # in multiple environments, for example: @@ -1648,13 +1654,11 @@ def get_default_version(env): return msvs_version if not msvc_version: - installed_vcs = get_installed_vcs(env) - debug('installed_vcs:%s', installed_vcs) - if not installed_vcs: + msvc_version = get_default_installed_msvc(env) + if not msvc_version: #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) debug('No installed VCs') return None - msvc_version = installed_vcs[0] debug('using default installed MSVC version %s', repr(msvc_version)) else: debug('using specified MSVC version %s', repr(msvc_version)) @@ -1769,13 +1773,9 @@ def msvc_find_valid_batch_script(env, version): return d -_undefined = None +_UNDEFINED = object() def get_use_script_use_settings(env): - global _undefined - - if _undefined is None: - _undefined = object() # use_script use_settings return values action # value ignored (value, None) use script or bypass detection @@ -1784,9 +1784,9 @@ def get_use_script_use_settings(env): # None (documentation) or evaluates False (code): bypass detection # need to distinguish between undefined and None - use_script = env.get('MSVC_USE_SCRIPT', _undefined) + use_script = env.get('MSVC_USE_SCRIPT', _UNDEFINED) - if use_script != _undefined: + if use_script != _UNDEFINED: # use_script defined, use_settings ignored (not type checked) return (use_script, None) @@ -1908,6 +1908,21 @@ def msvc_setup_env_tool(env=None, version=None, tool=None): debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval) return rval +def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): + debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app)) + + rval = [] + + if not msvc_version: + msvc_version = get_default_installed_msvc() + + if not msvc_version: + debug('no msvc versions detected') + return rval + + rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) + return rval + class _Util: @staticmethod @@ -2158,7 +2173,7 @@ def _init_sdk_dispatch_map(cls): def _verify_sdk_dispatch_map(cls): debug('%s verify sdk_dispatch_map', cls.__name__) cls._init_sdk_dispatch_map() - for sdk_version in _Const.MSVC_SDK_VERSIONS: + for sdk_version in _Config.MSVC_SDK_VERSIONS: if sdk_version in cls.sdk_dispatch_map: continue err_msg = 'sdk version {} not in {}.sdk_dispatch_map'.format(sdk_version, cls.__name__) @@ -2205,20 +2220,13 @@ def get_msvc_sdk_version_list(cls, msvc_version=None, msvc_uwp_app=False): sdk_versions = [] - if not msvc_version: - vcs = get_installed_vcs() - if not vcs: - debug('no msvc versions detected') - return sdk_versions - msvc_version = vcs[0] - verstr = get_msvc_version_numeric(msvc_version) - vs_def = _Const.MSVC_VERSION_EXTERNAL.get(verstr, None) + vs_def = _Config.MSVC_VERSION_EXTERNAL.get(verstr, None) if not vs_def: debug('vs_def is not defined') return sdk_versions - is_uwp = True if msvc_uwp_app in _Const.BOOLEAN_SYMBOLS[True] else False + is_uwp = True if msvc_uwp_app in _Config.BOOLEAN_SYMBOLS[True] else False platform_type = 'uwp' if is_uwp else 'desktop' sdk_list = _WindowsSDK.get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) @@ -2239,10 +2247,6 @@ def verify(cls): _Dispatcher.register(_WindowsSDK) -def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False): - debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(MSVC_VERSION), repr(MSVC_UWP_APP)) - return _WindowsSDK.get_msvc_sdk_version_list(msvc_version=MSVC_VERSION, msvc_uwp_app=MSVC_UWP_APP) - class _ScriptArguments: # TODO: verify SDK 10 version folder names 10.0.XXXXX.0 {1,3} last? @@ -2257,7 +2261,7 @@ class _ScriptArguments: @classmethod def _verify_re_sdk_dispatch_map(cls): debug('%s verify re_sdk_dispatch_map', cls.__name__) - for sdk_version in _Const.MSVC_SDK_VERSIONS: + for sdk_version in _Config.MSVC_SDK_VERSIONS: if sdk_version in cls.re_sdk_dispatch_map: continue err_msg = 'sdk version {} not in {}.re_sdk_dispatch_map'.format(sdk_version, cls.__name__) @@ -2315,9 +2319,9 @@ class SortOrder(enum.IntEnum): SPECTRE = 4 # MSVC_SPECTRE_LIBS USER = 5 # MSVC_SCRIPT_ARGS - VS2019 = _Const.MSVS_VERSION_INTERNAL['2019'] - VS2017 = _Const.MSVS_VERSION_INTERNAL['2017'] - VS2015 = _Const.MSVS_VERSION_INTERNAL['2015'] + 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', # fully qualified msvc version (e.g., '14.1Exp') @@ -2328,7 +2332,7 @@ class SortOrder(enum.IntEnum): def _msvc_version(cls, version): verstr = get_msvc_version_numeric(version) - vs_def = _Const.MSVC_VERSION_INTERNAL[verstr] + vs_def = _Config.MSVC_VERSION_INTERNAL[verstr] version_args = cls.MSVC_VERSION_ARGS_DEFINITION( version = version, @@ -2346,7 +2350,7 @@ def _msvc_script_argument_uwp(cls, env, msvc, arglist): if not uwp_app: return None - if uwp_app not in _Const.BOOLEAN_SYMBOLS[True]: + if uwp_app not in _Config.BOOLEAN_SYMBOLS[True]: return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: From dbd301878f8f7bb004658b25a62d57b48ee4c8a3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 12:19:39 -0400 Subject: [PATCH 017/108] Fix windows SDK reference --- SCons/Tool/MSCommon/vc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 3abd60610e..ab05323cde 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1920,7 +1920,7 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): debug('no msvc versions detected') return rval - rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) + rval = _WindowsSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) return rval class _Util: From 010c2ad46c32827b7ed8082ec11b83404a039faa Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 16:53:31 -0400 Subject: [PATCH 018/108] Refactor recent portions of vc.py into MSVC module --- SCons/Tool/MSCommon/MSVC/Config.py | 284 ++++ SCons/Tool/MSCommon/MSVC/Dispatcher.py | 62 + SCons/Tool/MSCommon/MSVC/Exceptions.py | 39 + SCons/Tool/MSCommon/MSVC/NotFound.py | 132 ++ SCons/Tool/MSCommon/MSVC/Registry.py | 110 ++ SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 733 +++++++++ SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py | 235 +++ SCons/Tool/MSCommon/MSVC/Util.py | 55 + SCons/Tool/MSCommon/MSVC/WinSDK.py | 250 +++ SCons/Tool/MSCommon/MSVC/__init__.py | 50 + SCons/Tool/MSCommon/__init__.py | 7 +- SCons/Tool/MSCommon/vc.py | 1638 +------------------ 12 files changed, 1968 insertions(+), 1627 deletions(-) create mode 100644 SCons/Tool/MSCommon/MSVC/Config.py create mode 100644 SCons/Tool/MSCommon/MSVC/Dispatcher.py create mode 100644 SCons/Tool/MSCommon/MSVC/Exceptions.py create mode 100644 SCons/Tool/MSCommon/MSVC/NotFound.py create mode 100644 SCons/Tool/MSCommon/MSVC/Registry.py create mode 100644 SCons/Tool/MSCommon/MSVC/ScriptArguments.py create mode 100644 SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py create mode 100644 SCons/Tool/MSCommon/MSVC/Util.py create mode 100644 SCons/Tool/MSCommon/MSVC/WinSDK.py create mode 100644 SCons/Tool/MSCommon/MSVC/__init__.py diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py new file mode 100644 index 0000000000..476dcb35eb --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -0,0 +1,284 @@ +# 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. + +""" +Constants and initialized data structures for Microsoft Visual C/C++. +""" + +from collections import ( + namedtuple, +) + +from . import Dispatcher + +Dispatcher.register_modulename(__name__) + +UNDEFINED = object() + +BOOLEAN_SYMBOLS = {} +BOOLEAN_EXTERNAL = {} + +for bool, symbol_list, symbol_case_list in [ + (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')), + (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')), +]: + BOOLEAN_SYMBOLS[bool] = list(symbol_list) + for symbol in symbol_case_list: + BOOLEAN_SYMBOLS[bool].extend([symbol, symbol.lower(), symbol.upper()]) + + for symbol in BOOLEAN_SYMBOLS[bool]: + BOOLEAN_EXTERNAL[symbol] = bool + +MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ + 'vc_runtime', + 'vc_runtime_numeric', + 'vc_runtime_alias_list', + 'vc_runtime_vsdef_list', +]) + +MSVC_RUNTIME_DEFINITION_LIST = [] + +MSVC_RUNTIME_INTERNAL = {} +MSVC_RUNTIME_EXTERNAL = {} + +for vc_runtime, vc_runtime_numeric, vc_runtime_alias_list in [ + ('140', 140, ['ucrt']), + ('120', 120, ['msvcr120']), + ('110', 110, ['msvcr110']), + ('100', 100, ['msvcr100']), + ('90', 90, ['msvcr90']), + ('80', 80, ['msvcr80']), + ('71', 71, ['msvcr71']), + ('70', 70, ['msvcr70']), + ('60', 60, ['msvcrt']), +]: + vc_runtime_def = MSVC_RUNTIME_DEFINITION( + vc_runtime = vc_runtime, + vc_runtime_numeric = vc_runtime_numeric, + vc_runtime_alias_list = vc_runtime_alias_list, + vc_runtime_vsdef_list = [], + ) + + MSVC_RUNTIME_DEFINITION_LIST.append(vc_runtime_def) + + MSVC_RUNTIME_INTERNAL[vc_runtime] = vc_runtime_def + MSVC_RUNTIME_EXTERNAL[vc_runtime] = vc_runtime_def + + for vc_runtime_alias in vc_runtime_alias_list: + MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def + +MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [ + 'vc_buildtools', + 'vc_buildtools_numeric', + 'vc_version', + 'vc_version_numeric', + 'cl_version', + 'cl_version_numeric', + 'vc_runtime_def', +]) + +MSVC_BUILDTOOLS_DEFINITION_LIST = [] + +MSVC_BUILDTOOLS_INTERNAL = {} +MSVC_BUILDTOOLS_EXTERNAL = {} + +VC_VERSION_MAP = {} + +for vc_buildtools, vc_version, cl_version, vc_runtime in [ + ('v143', '14.3', '19.3', '140'), + ('v142', '14.2', '19.2', '140'), + ('v141', '14.1', '19.1', '140'), + ('v140', '14.0', '19.0', '140'), + ('v120', '12.0', '18.0', '120'), + ('v110', '11.0', '17.0', '110'), + ('v100', '10.0', '16.0', '100'), + ('v90', '9.0', '15.0', '90'), + ('v80', '8.0', '14.0', '80'), + ('v71', '7.1', '13.1', '71'), + ('v70', '7.0', '13.0', '70'), + ('v60', '6.0', '12.0', '60'), +]: + + vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime] + + vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION( + vc_buildtools = vc_buildtools, + vc_buildtools_numeric = int(vc_buildtools[1:]), + vc_version = vc_version, + vc_version_numeric = float(vc_version), + cl_version = cl_version, + cl_version_numeric = float(cl_version), + vc_runtime_def = vc_runtime_def, + ) + + MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def) + + MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def + MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def + MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def + + VC_VERSION_MAP[vc_version] = vc_buildtools_def + +MSVS_VERSION_INTERNAL = {} +MSVS_VERSION_EXTERNAL = {} + +MSVC_VERSION_INTERNAL = {} +MSVC_VERSION_EXTERNAL = {} + +MSVS_VERSION_MAJOR_MAP = {} + +CL_VERSION_MAP = {} + +MSVC_SDK_VERSIONS = set() + +VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [ + 'vs_product', + 'vs_product_alias_list', + 'vs_version', + 'vs_version_major', + 'vs_envvar', + 'vs_express', + 'vs_lookup', + 'vc_sdk_versions', + 'vc_ucrt_versions', + 'vc_uwp', + 'vc_buildtools_def', + 'vc_buildtools_all', +]) + +VISUALSTUDIO_DEFINITION_LIST = [] + +VS_PRODUCT_ALIAS = { + '1998': ['6'] +} + +# vs_envvar: VisualStudioVersion defined in environment for MSVS 2012 and later +# MSVS 2010 and earlier cl_version -> vs_def is a 1:1 mapping +# SDK attached to product or buildtools? +for vs_product, vs_version, vs_envvar, vs_express, vs_lookup, vc_sdk, vc_ucrt, vc_uwp, vc_buildtools_all in [ + ('2022', '17.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v143', 'v142', 'v141', 'v140']), + ('2019', '16.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v142', 'v141', 'v140']), + ('2017', '15.0', True, True, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v141', 'v140']), + ('2015', '14.0', True, True, 'registry', ['10.0', '8.1'], ['10'], 'store', ['v140']), + ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']), + ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']), + ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']), + ('2008', '9.0', False, True, 'registry', None, None, None, ['v90']), + ('2005', '8.0', False, True, 'registry', None, None, None, ['v80']), + ('2003', '7.1', False, False, 'registry', None, None, None, ['v71']), + ('2002', '7.0', False, False, 'registry', None, None, None, ['v70']), + ('1998', '6.0', False, False, 'registry', None, None, None, ['v60']), +]: + + vs_version_major = vs_version.split('.')[0] + + vc_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools_all[0]] + + vs_def = VISUALSTUDIO_DEFINITION( + vs_product = vs_product, + vs_product_alias_list = [], + vs_version = vs_version, + vs_version_major = vs_version_major, + vs_envvar = vs_envvar, + vs_express = vs_express, + vs_lookup = vs_lookup, + vc_sdk_versions = vc_sdk, + vc_ucrt_versions = vc_ucrt, + vc_uwp = vc_uwp, + vc_buildtools_def = vc_buildtools_def, + vc_buildtools_all = vc_buildtools_all, + ) + + VISUALSTUDIO_DEFINITION_LIST.append(vs_def) + + vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def) + + MSVS_VERSION_INTERNAL[vs_product] = vs_def + MSVS_VERSION_EXTERNAL[vs_product] = vs_def + MSVS_VERSION_EXTERNAL[vs_version] = vs_def + + MSVC_VERSION_INTERNAL[vc_buildtools_def.vc_version] = vs_def + MSVC_VERSION_EXTERNAL[vs_product] = vs_def + MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_version] = vs_def + MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def + + if vs_product in VS_PRODUCT_ALIAS: + for vs_product_alias in VS_PRODUCT_ALIAS[vs_product]: + vs_def.vs_product_alias_list.append(vs_product_alias) + MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def + MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def + + MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def + + CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def + + if not vc_sdk: + continue + + MSVC_SDK_VERSIONS.update(vc_sdk) + +# convert string version set to string version list ranked in descending order +MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)] + +MSVS_VERSION_LEGACY = {} +MSVC_VERSION_LEGACY = {} + +for vdict in (MSVS_VERSION_EXTERNAL, MSVC_VERSION_INTERNAL): + for key, vs_def in vdict.items(): + if key not in MSVS_VERSION_LEGACY: + MSVS_VERSION_LEGACY[key] = vs_def + MSVC_VERSION_LEGACY[key] = vs_def + +# MSVC_NOTFOUND_POLICY definition: +# error: raise exception +# warning: issue warning and continue +# ignore: continue + +MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [ + 'value', + 'symbol', +]) + +MSVC_NOTFOUND_DEFINITION_LIST = [] + +MSVC_NOTFOUND_POLICY_INTERNAL = {} +MSVC_NOTFOUND_POLICY_EXTERNAL = {} + +for policy_value, policy_symbol_list in [ + (True, ['Error', 'Exception']), + (False, ['Warning', 'Warn']), + (None, ['Ignore', 'Suppress']), +]: + + policy_symbol = policy_symbol_list[0].lower() + policy_def = MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol) + + MSVC_NOTFOUND_DEFINITION_LIST.append(vs_def) + + MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def + + for policy_symbol in policy_symbol_list: + MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def + MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def + MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def + diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py new file mode 100644 index 0000000000..ebcd704e00 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py @@ -0,0 +1,62 @@ +# 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. + +""" +Internal method dispatcher for Microsoft Visual C/C++. +""" + +import sys + +from ..common import ( + debug, +) + +_refs = [] + +def register_class(ref): + _refs.append(ref) + +def register_modulename(modname): + module = sys.modules[modname] + _refs.append(module) + +def reset(): + debug('') + for ref in _refs: + for method in ['reset', '_reset']: + if not hasattr(ref, method) or not callable(getattr(ref, method, None)): + continue + debug('call %s.%s()', ref.__name__, method) + func = getattr(ref, method) + func() + +def verify(): + debug('') + for ref in _refs: + for method in ['verify', '_verify']: + if not hasattr(ref, method) or not callable(getattr(ref, method, None)): + continue + debug('call %s.%s()', ref.__name__, method) + func = getattr(ref, method) + func() + diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py new file mode 100644 index 0000000000..7a61ec532a --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py @@ -0,0 +1,39 @@ +# 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. + +""" +Exceptions for Microsoft Visual C/C++. +""" + +class VisualCException(Exception): + pass + +class MSVCVersionNotFound(VisualCException): + pass + +class MSVCInternalError(VisualCException): + pass + +class MSVCArgumentError(VisualCException): + pass + diff --git a/SCons/Tool/MSCommon/MSVC/NotFound.py b/SCons/Tool/MSCommon/MSVC/NotFound.py new file mode 100644 index 0000000000..7abe5ad6ba --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/NotFound.py @@ -0,0 +1,132 @@ +# 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. + +""" +Microsoft Visual C/C++ not found policy. + +Notes: + * As implemented, the default is that a warning is issued. This can + be changed globally via the function set_msvc_notfound_policy and/or + through the environment via the MSVC_NOTFOUND_POLICY variable. +""" + +import SCons.Warnings + +from ..common import ( + debug, +) + +from . import Dispatcher +from . import Config + +from .Exceptions import ( + MSVCVersionNotFound, +) + +Dispatcher.register_modulename(__name__) + +_MSVC_NOTFOUND_POLICY_DEF = Config.MSVC_NOTFOUND_POLICY_INTERNAL['warning'] + +def _msvc_notfound_policy_lookup(symbol): + + try: + notfound_policy_def = Config.MSVC_NOTFOUND_POLICY_EXTERNAL[symbol] + except KeyError: + err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \ + " Valid values are: {}".format( + repr(symbol), + ', '.join([repr(s) for s in Config.MSVC_NOTFOUND_POLICY_EXTERNAL.keys()]) + ) + raise ValueError(err_msg) + + return notfound_policy_def + +def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None): + """ Set the default policy when MSVC is not found. + + Args: + MSVC_NOTFOUND_POLICY: + string representing the policy behavior + when MSVC is not found or None + + Returns: + The previous policy is returned when the MSVC_NOTFOUND_POLICY argument + is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY + argument is None. + + """ + global _MSVC_NOTFOUND_POLICY_DEF + + prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol + + policy = MSVC_NOTFOUND_POLICY + if policy is not None: + _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy) + + debug( + 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', + repr(prev_policy), repr(policy), + repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) + ) + + return prev_policy + +def get_msvc_notfound_policy(): + """Return the active policy when MSVC is not found.""" + debug( + 'policy.symbol=%s, policy.value=%s', + repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) + ) + return _MSVC_NOTFOUND_POLICY_DEF.symbol + +def policy_handler(env, msg): + + if env and 'MSVC_NOTFOUND_POLICY' in env: + # environment setting + notfound_policy_src = 'environment' + policy = env['MSVC_NOTFOUND_POLICY'] + if policy is not None: + # user policy request + notfound_policy_def = _msvc_notfound_policy_lookup(policy) + else: + # active global setting + notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF + else: + # active global setting + notfound_policy_src = 'default' + policy = None + notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF + + debug( + 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', + notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value) + ) + + if notfound_policy_def.value is None: + # ignore + pass + elif notfound_policy_def.value: + raise MSVCVersionNotFound(msg) + else: + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) + diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py new file mode 100644 index 0000000000..848b1254f6 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -0,0 +1,110 @@ +# 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. + +""" +Windows registry functions for Microsoft Visual C/C++. +""" + +import os + +from SCons.Util import ( + HKEY_LOCAL_MACHINE, + HKEY_CURRENT_USER, + HKEY_LOCAL_MACHINE, + HKEY_CURRENT_USER, +) + +from .. common import ( + debug, + read_reg, +) + +from . import Dispatcher +from . import Util + +Dispatcher.register_modulename(__name__) + +def read_value(hkey, subkey_valname): + try: + rval = read_reg(subkey_valname, hkroot=hkey) + except OSError: + debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) + return None + except IndexError: + debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) + return None + debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval)) + return rval + +def registry_query_path(key, val, suffix): + extval = val + '\\' + suffix if suffix else val + qpath = read_value(key, extval) + if qpath and os.path.exists(qpath): + qpath = Util.process_path(qpath) + else: + qpath = None + return (qpath, key, val, extval) + +REG_SOFTWARE_MICROSOFT = [ + (HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'), + (HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries + (HKEY_LOCAL_MACHINE, r'Software\Microsoft'), + (HKEY_CURRENT_USER, r'Software\Microsoft'), +] + +def microsoft_query_paths(suffix, usrval=None): + paths = [] + records = [] + for key, val in REG_SOFTWARE_MICROSOFT: + extval = val + '\\' + suffix if suffix else val + qpath = read_value(key, extval) + if qpath and os.path.exists(qpath): + qpath = Util.process_path(qpath) + if qpath not in paths: + paths.append(qpath) + records.append((qpath, key, val, extval, usrval)) + return records + +def microsoft_query_keys(suffix, usrval=None): + records = [] + for key, val in REG_SOFTWARE_MICROSOFT: + extval = val + '\\' + suffix if suffix else val + rval = read_value(key, extval) + if rval: + records.append((key, val, extval, usrval)) + return records + +def microsoft_sdks(version): + return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder']) + +def sdk_query_paths(version): + q = microsoft_sdks(version) + return microsoft_query_paths(q) + +def windows_kits(version): + return r'Windows Kits\Installed Roots\KitsRoot' + version + +def windows_kit_query_paths(version): + q = windows_kits(version) + return microsoft_query_paths(q) + diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py new file mode 100644 index 0000000000..324f8be8ad --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -0,0 +1,733 @@ +# 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 ( + debug, +) + +from . import Dispatcher +from . import Util +from . import Config +from . import WinSDK + +from .Exceptions import ( + MSVCInternalError, + MSVCArgumentError, +) + +Dispatcher.register_modulename(__name__) + +# 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 = 'sdk version {} not in re_sdk_dispatch_map'.format(sdk_version) + raise MSVCInternalError(err_msg) + return None + +# capture msvc version +re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) + +re_toolset_full = re.compile(r'''^(?: + (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY + (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ +)$''', re.VERBOSE) + +re_toolset_140 = re.compile(r'''^(?: + (?:14[.]0{1,2})| # 14.0 - 14.00 + (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000 +)$''', re.VERBOSE) + +# valid SxS formats will be matched with re_toolset_full: match 3 '.' format +re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') + +# 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 + +# 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): + ARCH = 0 # arch + 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', # fully qualified msvc version (e.g., '14.1Exp') + 'vs_def', +]) + +def _msvc_version(version): + + verstr = Util.get_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 _msvc_script_argument_uwp(env, msvc, arglist): + + 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 Config.BOOLEAN_SYMBOLS[True]: + return None + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2015.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + ) + 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): + + matches = [m for m in re_vcvars_uwp.finditer(user_argstr)] + if not matches: + return None + + if len(matches) > 1: + debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) + err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + raise MSVCArgumentError(err_msg) + + if not uwp: + return None + + 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): + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc_version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2015.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + ) + 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 = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version)) + return err_msg + +def _msvc_script_argument_sdk(env, msvc, platform_type, 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_type) + ) + + if not sdk_version: + return None + + err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version) + if err_msg: + raise MSVCArgumentError(err_msg) + + sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) + + if sdk_version not in sdk_list: + err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format( + repr(sdk_version), repr(platform_type) + ) + raise MSVCArgumentError(err_msg) + + argpair = (SortOrder.SDK, sdk_version) + arglist.append(argpair) + + return sdk_version + +def _msvc_script_default_sdk(env, msvc, platform_type, arglist): + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + return None + + sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) + 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_type) + ) + + 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 = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + 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) + +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_read_toolset_folders(msvc, vc_dir): + + toolsets_sxs = {} + toolsets_full = [] + + build_dir = os.path.join(vc_dir, "Auxiliary", "Build") + sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()] + for sxs_toolset in sxs_toolsets: + filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) + filepath = os.path.join(build_dir, sxs_toolset, 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_toolset] = toolset_version + debug( + 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', + repr(msvc.version), repr(sxs_toolset), toolset_version + ) + + toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") + toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] + for toolset_version in toolsets: + binpath = os.path.join(toolset_dir, toolset_version, "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) + ) + + toolsets_full.sort(reverse=True) + 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 = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools) + 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(): + 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): + + if toolset_version == '14.0': + return toolset_version + + toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric == VS2019.vc_buildtools_def.vc_version_numeric: + # necessary to detect toolset not found + if toolset_version == '14.28.16.8': + new_toolset_version = '14.28' + # 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. + debug( + 'rewrite toolset_version=%s => toolset_version=%s', + repr(toolset_version), repr(new_toolset_version) + ) + toolset_version = new_toolset_version + + if toolset_version in toolsets_sxs: + toolset_vcvars = toolsets_sxs[toolset_version] + return toolset_vcvars + + 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.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2017', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2017.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( + repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version) + ) + return err_msg + + m = re_toolset_version.match(toolset_version) + if not m: + debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version)) + err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( + repr(toolset_version) + ) + return err_msg + + toolset_ver = m.group('version') + toolset_vernum = float(toolset_ver) + + if toolset_vernum < VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: %s < %s VS2015', + repr(toolset_vernum), repr(VS2015.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( + repr(toolset_version), repr(toolset_ver), repr(VS2015.vc_buildtools_def.vc_version) + ) + return err_msg + + if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: toolset %s > %s msvc', + repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( + repr(toolset_version), repr(toolset_ver), repr(msvc.version) + ) + return err_msg + + if toolset_vernum == VS2015.vc_buildtools_def.vc_version_numeric: + # tooset = 14.0 + if re_toolset_full.match(toolset_version): + if not re_toolset_140.match(toolset_version): + debug( + 'invalid: toolset version 14.0 constraint: %s != 14.0', + repr(toolset_version) + ) + err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format( + repr(toolset_version), repr(toolset_version) + ) + return err_msg + return None + + if re_toolset_full.match(toolset_version): + debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) + return None + + if re_toolset_sxs.match(toolset_version): + debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version)) + return None + + debug('invalid: method exit: toolset_version=%s', repr(toolset_version)) + err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version)) + return err_msg + +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 + + 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 MSVCArgumentError(err_msg) + + argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars)) + arglist.append(argpair) + + return toolset_vcvars + +def _msvc_script_default_toolset(env, msvc, vc_dir, arglist): + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_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)) + + argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(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 = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + 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(env, msvc, 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 (True, '1'): + return None + + if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2017', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2017.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( + repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version) + ) + raise MSVCArgumentError(err_msg) + + spectre_arg = 'spectre' + + # spectre libs may not be installed + argpair = (SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg)) + arglist.append(argpair) + + return spectre_arg + +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.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: msvc version constraint: %s < %s VS2015', + repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2015.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( + repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version) + ) + raise MSVCArgumentError(err_msg) + + # user arguments are not validated + argpair = (SortOrder.USER, script_args) + arglist.append(argpair) + + return script_args + +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 = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + 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_arguments(env, version, vc_dir, arg): + + arglist = [] + + msvc = _msvc_version(version) + + if arg: + argpair = (SortOrder.ARCH, arg) + arglist.append(argpair) + + if 'MSVC_SCRIPT_ARGS' in env: + user_argstr = _msvc_script_argument_user(env, msvc, arglist) + else: + user_argstr = None + + if 'MSVC_UWP_APP' in env: + uwp = _msvc_script_argument_uwp(env, msvc, arglist) + else: + uwp = None + + if user_argstr: + _user_script_argument_uwp(env, uwp, user_argstr) + + platform_type = 'uwp' if uwp else 'desktop' + + if 'MSVC_SDK_VERSION' in env: + sdk_version = _msvc_script_argument_sdk(env, msvc, platform_type, 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_type, arglist) + + 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 MSVC_FORCE_DEFAULT_TOOLSET: + if not toolset_version and not user_toolset: + toolset_version = _msvc_script_default_toolset(env, msvc, vc_dir, arglist) + + if 'MSVC_SPECTRE_LIBS' in env: + spectre = _msvc_script_argument_spectre(env, msvc, arglist) + else: + spectre = None + + if user_argstr: + _user_script_argument_spectre(env, spectre, user_argstr) + + if arglist: + arglist.sort() + argstr = ' '.join([argpair[-1] for argpair in arglist]).strip() + else: + argstr = '' + + debug('arguments: %s', repr(argstr)) + return argstr + +def reset(): + debug('') + _reset_toolset_cache() + +def verify(): + debug('') + _verify_re_sdk_dispatch_map() + diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py new file mode 100644 index 0000000000..8a790076a3 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py @@ -0,0 +1,235 @@ +# 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. + +""" +Determine if and/or when an error/warning should be issued when there +are no versions of msvc installed. If there is at least one version of +msvc installed, these routines do (almost) nothing. + +Notes: + * When msvc is the default compiler because there are no compilers + installed, a build may fail due to the cl.exe command not being + recognized. Currently, there is no easy way to detect during + msvc initialization if the default environment will be used later + to build a program and/or library. There is no error/warning + as there are legitimate SCons uses that do not require a c compiler. + * An error is indicated by returning a non-empty tool list from the + function register_iserror. +""" + +import re + +from .. common import ( + debug, +) + +from . import Dispatcher + +Dispatcher.register_modulename(__name__) + +class _Data: + + separator = r';' + + need_init = True + + @classmethod + def reset(cls): + debug('msvc default:init') + cls.n_setup = 0 # number of calls to msvc_setup_env_once + cls.default_ismsvc = False # is msvc the default compiler + cls.default_tools_re_list = [] # list of default tools regular expressions + cls.msvc_tools_init = set() # tools registered via msvc_exists + cls.msvc_tools = None # tools registered via msvc_setup_env_once + cls.msvc_installed = False # is msvc installed (vcs_installed > 0) + cls.msvc_nodefault = False # is there a default version of msvc + cls.need_init = True # reset initialization indicator + +def _initialize(env, msvc_exists_func): + if _Data.need_init: + _Data.reset() + _Data.need_init = False + _Data.msvc_installed = msvc_exists_func(env) + debug('msvc default:msvc_installed=%s', _Data.msvc_installed) + +def register_tool(env, tool, msvc_exists_func): + debug('msvc default:tool=%s', tool) + if _Data.need_init: + _initialize(env, msvc_exists_func) + if _Data.msvc_installed: + return None + if not tool: + return None + if _Data.n_setup == 0: + if tool not in _Data.msvc_tools_init: + _Data.msvc_tools_init.add(tool) + debug('msvc default:tool=%s, msvc_tools_init=%s', tool, _Data.msvc_tools_init) + return None + if tool not in _Data.msvc_tools: + _Data.msvc_tools.add(tool) + debug('msvc default:tool=%s, msvc_tools=%s', tool, _Data.msvc_tools) + +def register_setup(env, msvc_exists_func): + debug('msvc default') + if _Data.need_init: + _initialize(env, msvc_exists_func) + _Data.n_setup += 1 + if not _Data.msvc_installed: + _Data.msvc_tools = set(_Data.msvc_tools_init) + if _Data.n_setup == 1: + tool_list = env.get('TOOLS', None) + if tool_list and tool_list[0] == 'default': + if len(tool_list) > 1 and tool_list[1] in _Data.msvc_tools: + # msvc tools are the default compiler + _Data.default_ismsvc = True + _Data.msvc_nodefault = False + debug( + 'msvc default:n_setup=%d, msvc_installed=%s, default_ismsvc=%s', + _Data.n_setup, _Data.msvc_installed, _Data.default_ismsvc + ) + +def set_nodefault(): + # default msvc version, msvc not installed + _Data.msvc_nodefault = True + debug('msvc default:msvc_nodefault=%s', _Data.msvc_nodefault) + +def register_iserror(env, tool, msvc_exists_func): + + register_tool(env, tool, msvc_exists_func) + + if _Data.msvc_installed: + # msvc installed + return None + + if not _Data.msvc_nodefault: + # msvc version specified + return None + + tool_list = env.get('TOOLS', None) + if not tool_list: + # tool list is empty + return None + + debug( + 'msvc default:n_setup=%s, default_ismsvc=%s, msvc_tools=%s, tool_list=%s', + _Data.n_setup, _Data.default_ismsvc, _Data.msvc_tools, tool_list + ) + + if not _Data.default_ismsvc: + + # Summary: + # * msvc is not installed + # * msvc version not specified (default) + # * msvc is not the default compiler + + # construct tools set + tools_set = set(tool_list) + + else: + + if _Data.n_setup == 1: + # first setup and msvc is default compiler: + # build default tools regex for current tool state + tools = _Data.separator.join(tool_list) + tools_nchar = len(tools) + debug('msvc default:add regex:nchar=%d, tools=%s', tools_nchar, tools) + re_default_tools = re.compile(re.escape(tools)) + _Data.default_tools_re_list.insert(0, (tools_nchar, re_default_tools)) + # early exit: no error for default environment when msvc is not installed + return None + + # Summary: + # * msvc is not installed + # * msvc version not specified (default) + # * environment tools list is not empty + # * default tools regex list constructed + # * msvc tools set constructed + # + # Algorithm using tools string and sets: + # * convert environment tools list to a string + # * iteratively remove default tools sequences via regex + # substition list built from longest sequence (first) + # to shortest sequence (last) + # * build environment tools set with remaining tools + # * compute intersection of environment tools and msvc tools sets + # * if the intersection is: + # empty - no error: default tools and/or no additional msvc tools + # not empty - error: user specified one or more msvc tool(s) + # + # This will not produce an error or warning when there are no + # msvc installed instances nor any other recognized compilers + # and the default environment is needed for a build. The msvc + # compiler is forcibly added to the environment tools list when + # there are no compilers installed on win32. In this case, cl.exe + # will not be found on the path resulting in a failed build. + + # construct tools string + tools = _Data.separator.join(tool_list) + tools_nchar = len(tools) + + debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools) + + # iteratively remove default tool sequences (longest to shortest) + re_nchar_min, re_tools_min = _Data.default_tools_re_list[-1] + if tools_nchar >= re_nchar_min and re_tools_min.search(tools): + # minimum characters satisfied and minimum pattern exists + for re_nchar, re_default_tool in _Data.default_tools_re_list: + if tools_nchar < re_nchar: + # not enough characters for pattern + continue + tools = re_default_tool.sub('', tools).strip(_Data.separator) + tools_nchar = len(tools) + debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools) + if tools_nchar < re_nchar_min or not re_tools_min.search(tools): + # less than minimum characters or minimum pattern does not exist + break + + # construct non-default list(s) tools set + tools_set = {msvc_tool for msvc_tool in tools.split(_Data.separator) if msvc_tool} + + debug('msvc default:tools=%s', tools_set) + if not tools_set: + return None + + # compute intersection of remaining tools set and msvc tools set + tools_found = _Data.msvc_tools.intersection(tools_set) + debug('msvc default:tools_exist=%s', tools_found) + if not tools_found: + return None + + # construct in same order as tools list + tools_found_list = [] + seen_tool = set() + for tool in tool_list: + if tool not in seen_tool: + seen_tool.add(tool) + if tool in tools_found: + tools_found_list.append(tool) + + # return tool list in order presented + return tools_found_list + +def reset(): + debug('') + _Data.reset() + diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py new file mode 100644 index 0000000000..15abdcdb1d --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -0,0 +1,55 @@ +# 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. + +""" +Helper functions for Microsoft Visual C/C++. +""" + +import os +import re + +def listdir_dirs(p): + dirs = [] + for dir_name in os.listdir(p): + dir_path = os.path.join(p, dir_name) + if os.path.isdir(dir_path): + dirs.append((dir_name, dir_path)) + return dirs + +def process_path(p): + if p: + p = os.path.normpath(p) + p = os.path.realpath(p) + p = os.path.normcase(p) + return p + +re_version_prefix = re.compile(r'^(?P[0-9.]+).*') + +def get_version_prefix(version): + m = re_version_prefix.match(version) + if m: + rval = m.group('version') + else: + rval = '' + return rval + diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py new file mode 100644 index 0000000000..e95c72eef5 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -0,0 +1,250 @@ +# 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. + +""" +Windows SDK functions for Microsoft Visual C/C++. +""" + +import os + +from ..common import ( + debug, +) + +from . import Dispatcher +from . import Util +from . import Config +from . import Registry + +from .Exceptions import ( + MSVCInternalError, +) + +Dispatcher.register_modulename(__name__) + +def _new_sdk_map(): + sdk_map = { + 'desktop': [], + 'uwp': [], + } + return sdk_map + +def _sdk_10_layout(version): + + folder_prefix = version + '.' + + sdk_map = _new_sdk_map() + + sdk_roots = Registry.sdk_query_paths(version) + + sdk_version_platform_seen = set() + sdk_roots_seen = set() + + for sdk_t in sdk_roots: + + sdk_root = sdk_t[0] + if sdk_root in sdk_roots_seen: + continue + sdk_roots_seen.add(sdk_root) + + if not os.path.exists(sdk_root): + continue + + sdk_include_path = os.path.join(sdk_root, 'include') + if not os.path.exists(sdk_include_path): + continue + + for version_nbr, version_nbr_path in Util.listdir_dirs(sdk_include_path): + + if not version_nbr.startswith(folder_prefix): + continue + + sdk_inc_path = Util.process_path(os.path.join(version_nbr_path, 'um')) + if not os.path.exists(sdk_inc_path): + continue + + for platform_type, sdk_inc_file in [ + ('desktop', 'winsdkver.h'), + ('uwp', 'windows.h'), + ]: + + if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): + continue + + key = (version_nbr, platform_type) + if key in sdk_version_platform_seen: + continue + sdk_version_platform_seen.add(key) + + sdk_map[platform_type].append(version_nbr) + + for key, val in sdk_map.items(): + val.sort(reverse=True) + + return sdk_map + +def _sdk_81_layout(version): + + version_nbr = version + + sdk_map = _new_sdk_map() + + sdk_roots = Registry.sdk_query_paths(version) + + sdk_version_platform_seen = set() + sdk_roots_seen = set() + + for sdk_t in sdk_roots: + + sdk_root = sdk_t[0] + if sdk_root in sdk_roots_seen: + continue + sdk_roots_seen.add(sdk_root) + + # msvc does not check for existence of root or other files + + sdk_inc_path = Util.process_path(os.path.join(sdk_root, r'include\um')) + if not os.path.exists(sdk_inc_path): + continue + + for platform_type, sdk_inc_file in [ + ('desktop', 'winsdkver.h'), + ('uwp', 'windows.h'), + ]: + + if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): + continue + + key = (version_nbr, platform_type) + if key in sdk_version_platform_seen: + continue + sdk_version_platform_seen.add(key) + + sdk_map[platform_type].append(version_nbr) + + for key, val in sdk_map.items(): + val.sort(reverse=True) + + return sdk_map + +_sdk_map_cache = {} +_sdk_cache = {} + +def _reset_sdk_cache(): + debug('') + _sdk_map_cache = {} + _sdk_cache = {} + +def _sdk_10(key, reg_version): + if key in _sdk_map_cache: + sdk_map = _sdk_map_cache[key] + else: + sdk_map = _sdk_10_layout(reg_version) + _sdk_map_cache[key] = sdk_map + return sdk_map + +def _sdk_81(key, reg_version): + if key in _sdk_map_cache: + sdk_map = _sdk_map_cache[key] + else: + sdk_map = _sdk_81_layout(reg_version) + _sdk_map_cache[key] = sdk_map + return sdk_map + +def _combine_sdk_map_list(sdk_map_list): + combined_sdk_map = _new_sdk_map() + for sdk_map in sdk_map_list: + for key, val in sdk_map.items(): + combined_sdk_map[key].extend(val) + return combined_sdk_map + +_sdk_dispatch_map = { + '10.0': (_sdk_10, '10.0'), + '8.1': (_sdk_81, '8.1'), +} + +def _verify_sdk_dispatch_map(): + debug('') + for sdk_version in Config.MSVC_SDK_VERSIONS: + if sdk_version in _sdk_dispatch_map: + continue + err_msg = 'sdk version {} not in sdk_dispatch_map'.format(sdk_version) + raise MSVCInternalError(err_msg) + return None + +def _version_list_sdk_map(version_list): + + sdk_map_list = [] + for version in version_list: + func, reg_version = _sdk_dispatch_map[version] + sdk_map = func(version, reg_version) + sdk_map_list.append(sdk_map) + + combined_sdk_map = _combine_sdk_map_list(sdk_map_list) + return combined_sdk_map + +def _sdk_map(version_list): + key = tuple(version_list) + if key in _sdk_cache: + sdk_map = _sdk_cache[key] + else: + version_numlist = [float(v) for v in version_list] + version_numlist.sort(reverse=True) + key = tuple([str(v) for v in version_numlist]) + sdk_map = _version_list_sdk_map(key) + _sdk_cache[key] = sdk_map + return sdk_map + +def get_sdk_version_list(version_list, platform_type): + sdk_map = _sdk_map(version_list) + sdk_list = sdk_map.get(platform_type, []) + return sdk_list + +def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False): + debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app)) + + sdk_versions = [] + + verstr = Util.get_version_prefix(msvc_version) + vs_def = Config.MSVC_VERSION_EXTERNAL.get(verstr, None) + if not vs_def: + debug('vs_def is not defined') + return sdk_versions + + is_uwp = True if msvc_uwp_app in Config.BOOLEAN_SYMBOLS[True] else False + platform_type = 'uwp' if is_uwp else 'desktop' + sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) + + sdk_versions.extend(sdk_list) + debug('sdk_versions=%s', repr(sdk_versions)) + + return sdk_versions + +def reset(): + debug('') + _reset_sdk_cache() + +def verify(): + debug('') + _verify_sdk_dispatch_map() + diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py new file mode 100644 index 0000000000..afd993f969 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -0,0 +1,50 @@ +# 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. + +""" +Functions for Microsoft Visual C/C++. +""" + +from . import Exceptions +from . import Util + +from . import Dispatcher as _Dispatcher + +from . import Config +from . import Registry +from . import SetupEnvDefault +from . import NotFound +from . import WinSDK +from . import ScriptArguments + +from .NotFound import ( + set_msvc_notfound_policy, + get_msvc_notfound_policy, +) + +def reset(): + _Dispatcher.reset() + +#reset() # testing +_Dispatcher.verify() + diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index de78f8459a..9f35e94002 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -32,14 +32,17 @@ from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env +from SCons.Tool.MSCommon.MSVC import ( + set_msvc_notfound_policy, + get_msvc_notfound_policy, +) + from SCons.Tool.MSCommon.vc import ( msvc_exists, msvc_setup_env_tool, msvc_setup_env_once, msvc_version_to_maj_min, msvc_find_vswhere, - set_msvc_notfound_policy, - get_msvc_notfound_policy, get_msvc_sdk_versions, ) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index ab05323cde..5a27f44457 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -51,7 +51,6 @@ namedtuple, OrderedDict, ) -import enum import SCons.Util import SCons.Warnings @@ -61,9 +60,10 @@ from .common import CONFIG_CACHE, debug from .sdk import get_installed_sdks - -class VisualCException(Exception): - pass +from . import MSVC +from .MSVC.Exceptions import ( + VisualCException +) class UnsupportedVersion(VisualCException): pass @@ -89,296 +89,10 @@ class MSVCScriptNotFound(VisualCException): class MSVCUseSettingsError(VisualCException): pass -class MSVCVersionNotFound(VisualCException): - pass - -class MSVCArgumentError(VisualCException): - pass - -class MSVCInternalError(VisualCException): - pass - class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): pass -class _Dispatcher: - - classrefs = [] - - @classmethod - def register(cls, classref): - cls.classrefs.append(classref) - - @classmethod - def reset(cls): - debug('reset %s', cls.__name__) - for classref in cls.classrefs: - for method in ['reset', '_reset']: - if not hasattr(classref, method) or not callable(getattr(classref, method, None)): - continue - debug('call %s.%s()', classref.__name__, method) - func = getattr(classref, method) - func() - - @classmethod - def verify(cls): - debug('verify %s', cls.__name__) - for classref in cls.classrefs: - for method in ['verify', '_verify']: - if not hasattr(classref, method) or not callable(getattr(classref, method, None)): - continue - debug('call %s.%s()', classref.__name__, method) - func = getattr(classref, method) - func() - -class _Config: - - BOOLEAN_SYMBOLS = {} - BOOLEAN_EXTERNAL = {} - - for bool, symbol_list, symbol_case_list in [ - (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')), - (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')), - ]: - BOOLEAN_SYMBOLS[bool] = list(symbol_list) - for symbol in symbol_case_list: - BOOLEAN_SYMBOLS[bool].extend([symbol, symbol.lower(), symbol.upper()]) - - for symbol in BOOLEAN_SYMBOLS[bool]: - BOOLEAN_EXTERNAL[symbol] = bool - - MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ - 'vc_runtime', - 'vc_runtime_numeric', - 'vc_runtime_alias_list', - 'vc_runtime_vsdef_list', - ]) - - MSVC_RUNTIME_DEFINITION_LIST = [] - - MSVC_RUNTIME_INTERNAL = {} - MSVC_RUNTIME_EXTERNAL = {} - - for vc_runtime, vc_runtime_numeric, vc_runtime_alias_list in [ - ('140', 140, ['ucrt']), - ('120', 120, ['msvcr120']), - ('110', 110, ['msvcr110']), - ('100', 100, ['msvcr100']), - ('90', 90, ['msvcr90']), - ('80', 80, ['msvcr80']), - ('71', 71, ['msvcr71']), - ('70', 70, ['msvcr70']), - ('60', 60, ['msvcrt']), - ]: - vc_runtime_def = MSVC_RUNTIME_DEFINITION( - vc_runtime = vc_runtime, - vc_runtime_numeric = vc_runtime_numeric, - vc_runtime_alias_list = vc_runtime_alias_list, - vc_runtime_vsdef_list = [], - ) - - MSVC_RUNTIME_DEFINITION_LIST.append(vc_runtime_def) - - MSVC_RUNTIME_INTERNAL[vc_runtime] = vc_runtime_def - MSVC_RUNTIME_EXTERNAL[vc_runtime] = vc_runtime_def - - for vc_runtime_alias in vc_runtime_alias_list: - MSVC_RUNTIME_EXTERNAL[vc_runtime_alias] = vc_runtime_def - - MSVC_BUILDTOOLS_DEFINITION = namedtuple('MSVCBuildtools', [ - 'vc_buildtools', - 'vc_buildtools_numeric', - 'vc_version', - 'vc_version_numeric', - 'cl_version', - 'cl_version_numeric', - 'vc_runtime_def', - ]) - - MSVC_BUILDTOOLS_DEFINITION_LIST = [] - - MSVC_BUILDTOOLS_INTERNAL = {} - MSVC_BUILDTOOLS_EXTERNAL = {} - - VC_VERSION_MAP = {} - - for vc_buildtools, vc_version, cl_version, vc_runtime in [ - ('v143', '14.3', '19.3', '140'), - ('v142', '14.2', '19.2', '140'), - ('v141', '14.1', '19.1', '140'), - ('v140', '14.0', '19.0', '140'), - ('v120', '12.0', '18.0', '120'), - ('v110', '11.0', '17.0', '110'), - ('v100', '10.0', '16.0', '100'), - ('v90', '9.0', '15.0', '90'), - ('v80', '8.0', '14.0', '80'), - ('v71', '7.1', '13.1', '71'), - ('v70', '7.0', '13.0', '70'), - ('v60', '6.0', '12.0', '60'), - ]: - - vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime] - - vc_buildtools_def = MSVC_BUILDTOOLS_DEFINITION( - vc_buildtools = vc_buildtools, - vc_buildtools_numeric = int(vc_buildtools[1:]), - vc_version = vc_version, - vc_version_numeric = float(vc_version), - cl_version = cl_version, - cl_version_numeric = float(cl_version), - vc_runtime_def = vc_runtime_def, - ) - - MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def) - - MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] = vc_buildtools_def - MSVC_BUILDTOOLS_EXTERNAL[vc_buildtools] = vc_buildtools_def - MSVC_BUILDTOOLS_EXTERNAL[vc_version] = vc_buildtools_def - - VC_VERSION_MAP[vc_version] = vc_buildtools_def - - MSVS_VERSION_INTERNAL = {} - MSVS_VERSION_EXTERNAL = {} - - MSVC_VERSION_INTERNAL = {} - MSVC_VERSION_EXTERNAL = {} - - MSVS_VERSION_MAJOR_MAP = {} - - CL_VERSION_MAP = {} - - MSVC_SDK_VERSIONS = set() - - VISUALSTUDIO_DEFINITION = namedtuple('VisualStudioDefinition', [ - 'vs_product', - 'vs_product_alias_list', - 'vs_version', - 'vs_version_major', - 'vs_envvar', - 'vs_express', - 'vs_lookup', - 'vc_sdk_versions', - 'vc_ucrt_versions', - 'vc_uwp', - 'vc_buildtools_def', - 'vc_buildtools_all', - ]) - - VISUALSTUDIO_DEFINITION_LIST = [] - - VS_PRODUCT_ALIAS = { - '1998': ['6'] - } - - # vs_envvar: VisualStudioVersion defined in environment for MSVS 2012 and later - # MSVS 2010 and earlier cl_version -> vs_def is a 1:1 mapping - # SDK attached to product or buildtools? - for vs_product, vs_version, vs_envvar, vs_express, vs_lookup, vc_sdk, vc_ucrt, vc_uwp, vc_buildtools_all in [ - ('2022', '17.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v143', 'v142', 'v141', 'v140']), - ('2019', '16.0', True, False, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v142', 'v141', 'v140']), - ('2017', '15.0', True, True, 'vswhere' , ['10.0', '8.1'], ['10'], 'uwp', ['v141', 'v140']), - ('2015', '14.0', True, True, 'registry', ['10.0', '8.1'], ['10'], 'store', ['v140']), - ('2013', '12.0', True, True, 'registry', None, None, None, ['v120']), - ('2012', '11.0', True, True, 'registry', None, None, None, ['v110']), - ('2010', '10.0', False, True, 'registry', None, None, None, ['v100']), - ('2008', '9.0', False, True, 'registry', None, None, None, ['v90']), - ('2005', '8.0', False, True, 'registry', None, None, None, ['v80']), - ('2003', '7.1', False, False, 'registry', None, None, None, ['v71']), - ('2002', '7.0', False, False, 'registry', None, None, None, ['v70']), - ('1998', '6.0', False, False, 'registry', None, None, None, ['v60']), - ]: - - vs_version_major = vs_version.split('.')[0] - - vc_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools_all[0]] - - vs_def = VISUALSTUDIO_DEFINITION( - vs_product = vs_product, - vs_product_alias_list = [], - vs_version = vs_version, - vs_version_major = vs_version_major, - vs_envvar = vs_envvar, - vs_express = vs_express, - vs_lookup = vs_lookup, - vc_sdk_versions = vc_sdk, - vc_ucrt_versions = vc_ucrt, - vc_uwp = vc_uwp, - vc_buildtools_def = vc_buildtools_def, - vc_buildtools_all = vc_buildtools_all, - ) - - VISUALSTUDIO_DEFINITION_LIST.append(vs_def) - - vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def) - - MSVS_VERSION_INTERNAL[vs_product] = vs_def - MSVS_VERSION_EXTERNAL[vs_product] = vs_def - MSVS_VERSION_EXTERNAL[vs_version] = vs_def - - MSVC_VERSION_INTERNAL[vc_buildtools_def.vc_version] = vs_def - MSVC_VERSION_EXTERNAL[vs_product] = vs_def - MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_version] = vs_def - MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def - - if vs_product in VS_PRODUCT_ALIAS: - for vs_product_alias in VS_PRODUCT_ALIAS[vs_product]: - vs_def.vs_product_alias_list.append(vs_product_alias) - MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def - MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def - - MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def - - CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def - - if not vc_sdk: - continue - - MSVC_SDK_VERSIONS.update(vc_sdk) - - # convert string version set to string version list ranked in descending order - MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)] - - MSVS_VERSION_LEGACY = {} - MSVC_VERSION_LEGACY = {} - - for vdict in (MSVS_VERSION_EXTERNAL, MSVC_VERSION_INTERNAL): - for key, vs_def in vdict.items(): - if key not in MSVS_VERSION_LEGACY: - MSVS_VERSION_LEGACY[key] = vs_def - MSVC_VERSION_LEGACY[key] = vs_def - -# MSVC_NOTFOUND_POLICY definition: -# error: raise exception -# warning: issue warning and continue -# ignore: continue - -_MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [ - 'value', - 'symbol', -]) - -_MSVC_NOTFOUND_POLICY_INTERNAL = {} -_MSVC_NOTFOUND_POLICY_EXTERNAL = {} - -for policy_value, policy_symbol_list in [ - (True, ['Error', 'Exception']), - (False, ['Warning', 'Warn']), - (None, ['Ignore', 'Suppress']), -]: - - policy_symbol = policy_symbol_list[0].lower() - policy_def = _MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol) - - _MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def - - for policy_symbol in policy_symbol_list: - _MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def - _MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def - _MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def - -_MSVC_NOTFOUND_POLICY_DEF = _MSVC_NOTFOUND_POLICY_INTERNAL['warning'] - # Dict to 'canonalize' the arch _ARCH_TO_CANONICAL = { "amd64" : "amd64", @@ -1253,7 +967,7 @@ def reset_installed_vcs(): """Make it try again to find VC. This is just for the tests.""" global __INSTALLED_VCS_RUN __INSTALLED_VCS_RUN = None - _Dispatcher.reset() + MSVC.reset() def get_default_installed_msvc(env=None): vcs = get_installed_vcs(env) @@ -1344,295 +1058,6 @@ def script_env(script, args=None): return cache_data -def _msvc_notfound_policy_lookup(symbol): - - try: - notfound_policy_def = _MSVC_NOTFOUND_POLICY_EXTERNAL[symbol] - except KeyError: - err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \ - " Valid values are: {}".format( - repr(symbol), - ', '.join([repr(s) for s in _MSVC_NOTFOUND_POLICY_EXTERNAL.keys()]) - ) - raise ValueError(err_msg) - - return notfound_policy_def - -def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None): - """ Set the default policy when MSVC is not found. - - Args: - MSVC_NOTFOUND_POLICY: - string representing the policy behavior - when MSVC is not found or None - - Returns: - The previous policy is returned when the MSVC_NOTFOUND_POLICY argument - is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY - argument is None. - - """ - global _MSVC_NOTFOUND_POLICY_DEF - - prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol - - policy = MSVC_NOTFOUND_POLICY - if policy is not None: - _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy) - - debug( - 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', - repr(prev_policy), repr(policy), - repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) - ) - - return prev_policy - -def get_msvc_notfound_policy(): - """Return the active policy when MSVC is not found.""" - debug( - 'policy.symbol=%s, policy.value=%s', - repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) - ) - return _MSVC_NOTFOUND_POLICY_DEF.symbol - -def _msvc_notfound_policy_handler(env, msg): - - if env and 'MSVC_NOTFOUND_POLICY' in env: - # environment setting - notfound_policy_src = 'environment' - policy = env['MSVC_NOTFOUND_POLICY'] - if policy is not None: - # user policy request - notfound_policy_def = _msvc_notfound_policy_lookup(policy) - else: - # active global setting - notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF - else: - # active global setting - notfound_policy_src = 'default' - policy = None - notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF - - debug( - 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', - notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value) - ) - - if notfound_policy_def.value is None: - # ignore - pass - elif notfound_policy_def.value: - raise MSVCVersionNotFound(msg) - else: - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) - -class _MSVCSetupEnvDefault: - """ - Determine if and/or when an error/warning should be issued when there - are no versions of msvc installed. If there is at least one version of - msvc installed, these routines do (almost) nothing. - - Notes: - * When msvc is the default compiler because there are no compilers - installed, a build may fail due to the cl.exe command not being - recognized. Currently, there is no easy way to detect during - msvc initialization if the default environment will be used later - to build a program and/or library. There is no error/warning - as there are legitimate SCons uses that do not require a c compiler. - * As implemented, the default is that a warning is issued. This can - be changed globally via the function set_msvc_notfound_policy and/or - through the environment via the MSVC_NOTFOUND_POLICY variable. - """ - - separator = r';' - - need_init = True - - @classmethod - def reset(cls): - debug('msvc default:init') - cls.n_setup = 0 # number of calls to msvc_setup_env_once - cls.default_ismsvc = False # is msvc the default compiler - cls.default_tools_re_list = [] # list of default tools regular expressions - cls.msvc_tools_init = set() # tools registered via msvc_exists - cls.msvc_tools = None # tools registered via msvc_setup_env_once - cls.msvc_installed = False # is msvc installed (vcs_installed > 0) - cls.msvc_nodefault = False # is there a default version of msvc - cls.need_init = True # reset initialization indicator - - @classmethod - def _initialize(cls, env): - if cls.need_init: - cls.reset() - cls.need_init = False - vcs = get_installed_vcs(env) - cls.msvc_installed = len(vcs) > 0 - debug('msvc default:msvc_installed=%s', cls.msvc_installed) - - @classmethod - def register_tool(cls, env, tool): - debug('msvc default:tool=%s', tool) - if cls.need_init: - cls._initialize(env) - if cls.msvc_installed: - return None - if not tool: - return None - if cls.n_setup == 0: - if tool not in cls.msvc_tools_init: - cls.msvc_tools_init.add(tool) - debug('msvc default:tool=%s, msvc_tools_init=%s', tool, cls.msvc_tools_init) - return None - if tool not in cls.msvc_tools: - cls.msvc_tools.add(tool) - debug('msvc default:tool=%s, msvc_tools=%s', tool, cls.msvc_tools) - - @classmethod - def register_setup(cls, env): - debug('msvc default') - if cls.need_init: - cls._initialize(env) - cls.n_setup += 1 - if not cls.msvc_installed: - cls.msvc_tools = set(cls.msvc_tools_init) - if cls.n_setup == 1: - tool_list = env.get('TOOLS', None) - if tool_list and tool_list[0] == 'default': - if len(tool_list) > 1 and tool_list[1] in cls.msvc_tools: - # msvc tools are the default compiler - cls.default_ismsvc = True - cls.msvc_nodefault = False - debug( - 'msvc default:n_setup=%d, msvc_installed=%s, default_ismsvc=%s', - cls.n_setup, cls.msvc_installed, cls.default_ismsvc - ) - - @classmethod - def set_nodefault(cls): - # default msvc version, msvc not installed - cls.msvc_nodefault = True - debug('msvc default:msvc_nodefault=%s', cls.msvc_nodefault) - - @classmethod - def register_iserror(cls, env, tool): - - cls.register_tool(env, tool) - - if cls.msvc_installed: - # msvc installed - return None - - if not cls.msvc_nodefault: - # msvc version specified - return None - - tool_list = env.get('TOOLS', None) - if not tool_list: - # tool list is empty - return None - - debug( - 'msvc default:n_setup=%s, default_ismsvc=%s, msvc_tools=%s, tool_list=%s', - cls.n_setup, cls.default_ismsvc, cls.msvc_tools, tool_list - ) - - if not cls.default_ismsvc: - - # Summary: - # * msvc is not installed - # * msvc version not specified (default) - # * msvc is not the default compiler - - # construct tools set - tools_set = set(tool_list) - - else: - - if cls.n_setup == 1: - # first setup and msvc is default compiler: - # build default tools regex for current tool state - tools = cls.separator.join(tool_list) - tools_nchar = len(tools) - debug('msvc default:add regex:nchar=%d, tools=%s', tools_nchar, tools) - re_default_tools = re.compile(re.escape(tools)) - cls.default_tools_re_list.insert(0, (tools_nchar, re_default_tools)) - # early exit: no error for default environment when msvc is not installed - return None - - # Summary: - # * msvc is not installed - # * msvc version not specified (default) - # * environment tools list is not empty - # * default tools regex list constructed - # * msvc tools set constructed - # - # Algorithm using tools string and sets: - # * convert environment tools list to a string - # * iteratively remove default tools sequences via regex - # substition list built from longest sequence (first) - # to shortest sequence (last) - # * build environment tools set with remaining tools - # * compute intersection of environment tools and msvc tools sets - # * if the intersection is: - # empty - no error: default tools and/or no additional msvc tools - # not empty - error: user specified one or more msvc tool(s) - # - # This will not produce an error or warning when there are no - # msvc installed instances nor any other recognized compilers - # and the default environment is needed for a build. The msvc - # compiler is forcibly added to the environment tools list when - # there are no compilers installed on win32. In this case, cl.exe - # will not be found on the path resulting in a failed build. - - # construct tools string - tools = cls.separator.join(tool_list) - tools_nchar = len(tools) - - debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools) - - # iteratively remove default tool sequences (longest to shortest) - re_nchar_min, re_tools_min = cls.default_tools_re_list[-1] - if tools_nchar >= re_nchar_min and re_tools_min.search(tools): - # minimum characters satisfied and minimum pattern exists - for re_nchar, re_default_tool in cls.default_tools_re_list: - if tools_nchar < re_nchar: - # not enough characters for pattern - continue - tools = re_default_tool.sub('', tools).strip(cls.separator) - tools_nchar = len(tools) - debug('msvc default:check tools:nchar=%d, tools=%s', tools_nchar, tools) - if tools_nchar < re_nchar_min or not re_tools_min.search(tools): - # less than minimum characters or minimum pattern does not exist - break - - # construct non-default list(s) tools set - tools_set = {msvc_tool for msvc_tool in tools.split(cls.separator) if msvc_tool} - - debug('msvc default:tools=%s', tools_set) - if not tools_set: - return None - - # compute intersection of remaining tools set and msvc tools set - tools_found = cls.msvc_tools.intersection(tools_set) - debug('msvc default:tools_exist=%s', tools_found) - if not tools_found: - return None - - # construct in same order as tools list - tools_found_list = [] - seen_tool = set() - for tool in tool_list: - if tool not in seen_tool: - seen_tool.add(tool) - if tool in tools_found: - tools_found_list.append(tool) - - # return tool list in order presented - return tools_found_list - -_Dispatcher.register(_MSVCSetupEnvDefault) - def get_default_version(env): msvc_version = env.get('MSVC_VERSION') msvs_version = env.get('MSVS_VERSION') @@ -1673,16 +1098,16 @@ def msvc_setup_env_once(env, tool=None): if not has_run: debug('tool=%s', repr(tool)) - _MSVCSetupEnvDefault.register_setup(env) + MSVC.SetupEnvDefault.register_setup(env, msvc_exists) msvc_setup_env(env) env["MSVC_SETUP_RUN"] = True - req_tools = _MSVCSetupEnvDefault.register_iserror(env, tool) + req_tools = MSVC.SetupEnvDefault.register_iserror(env, tool, msvc_exists) if req_tools: msg = "No versions of the MSVC compiler were found.\n" \ " Visual Studio C/C++ compilers may not be set correctly.\n" \ " Requested tool(s) are: {}".format(req_tools) - _msvc_notfound_policy_handler(env, msg) + MSVC.Notfound.policy_handler(env, msg) def msvc_find_valid_batch_script(env, version): """Find and execute appropriate batch script to set up build env. @@ -1724,7 +1149,7 @@ def msvc_find_valid_batch_script(env, version): debug('use_script 2 %s, args:%s', repr(vc_script), arg) found = None if vc_script: - arg = _ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) + arg = MSVC.ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) try: d = script_env(vc_script, args=arg) found = vc_script @@ -1769,7 +1194,7 @@ def msvc_find_valid_batch_script(env, version): " No versions of the MSVC compiler were found.\n" \ " Visual Studio C/C++ compilers may not be set correctly".format(version) - _msvc_notfound_policy_handler(env, msg) + MSVC.NotFound.policy_handler(env, msg) return d @@ -1805,7 +1230,7 @@ def msvc_setup_env(env): version = get_default_version(env) if version is None: if not msvc_setup_env_user(env): - _MSVCSetupEnvDefault.set_nodefault() + MSVC.SetupEnvDefault.set_nodefault() return None # XXX: we set-up both MSVS version for backward @@ -1899,7 +1324,7 @@ def msvc_setup_env_user(env=None): def msvc_setup_env_tool(env=None, version=None, tool=None): debug('tool=%s, version=%s', repr(tool), repr(version)) - _MSVCSetupEnvDefault.register_tool(env, tool) + MSVC.SetupEnvDefault.register_tool(env, tool, msvc_exists) rval = False if not rval and msvc_exists(env, version): rval = True @@ -1920,1043 +1345,6 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): debug('no msvc versions detected') return rval - rval = _WindowsSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) + rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) return rval -class _Util: - - @staticmethod - def listdir_dirs(p): - dirs = [] - for dir_name in os.listdir(p): - dir_path = os.path.join(p, dir_name) - if os.path.isdir(dir_path): - dirs.append((dir_name, dir_path)) - return dirs - - @staticmethod - def process_path(p): - if p: - p = os.path.normpath(p) - p = os.path.realpath(p) - p = os.path.normcase(p) - return p - -class _Registry: - - def read_value(hkey, subkey_valname): - try: - rval = common.read_reg(subkey_valname, hkroot=hkey) - except OSError: - debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) - return None - except IndexError: - debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) - return None - debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval)) - return rval - - @classmethod - def registry_query_path(cls, key, val, suffix): - extval = val + '\\' + suffix if suffix else val - qpath = cls.read_value(key, extval) - if qpath and os.path.exists(qpath): - qpath = _Util.process_path(qpath) - else: - qpath = None - return (qpath, key, val, extval) - - REG_SOFTWARE_MICROSOFT = [ - (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Wow6432Node\Microsoft'), - (SCons.Util.HKEY_CURRENT_USER, r'Software\Wow6432Node\Microsoft'), # SDK queries - (SCons.Util.HKEY_LOCAL_MACHINE, r'Software\Microsoft'), - (SCons.Util.HKEY_CURRENT_USER, r'Software\Microsoft'), - ] - - @classmethod - def microsoft_query_paths(cls, suffix, usrval=None): - paths = [] - records = [] - for key, val in cls.REG_SOFTWARE_MICROSOFT: - extval = val + '\\' + suffix if suffix else val - qpath = cls.read_value(key, extval) - if qpath and os.path.exists(qpath): - qpath = _Util.process_path(qpath) - if qpath not in paths: - paths.append(qpath) - records.append((qpath, key, val, extval, usrval)) - return records - - @classmethod - def microsoft_query_keys(cls, suffix, usrval=None): - records = [] - for key, val in cls.REG_SOFTWARE_MICROSOFT: - extval = val + '\\' + suffix if suffix else val - rval = cls.read_value(key, extval) - if rval: - records.append((key, val, extval, usrval)) - return records - - @classmethod - def microsoft_sdks(cls, version): - return '\\'.join([r'Microsoft SDKs\Windows', 'v' + version, r'InstallationFolder']) - - @classmethod - def sdk_query_paths(cls, version): - q = cls.microsoft_sdks(version) - return cls.microsoft_query_paths(q) - - @classmethod - def windows_kits(cls, version): - return r'Windows Kits\Installed Roots\KitsRoot' + version - - @classmethod - def windows_kit_query_paths(cls, version): - q = cls.windows_kits(version) - return cls.microsoft_query_paths(q) - -class _WindowsSDK: - - @classmethod - def _new_sdk_map(cls): - sdk_map = { - 'desktop': [], - 'uwp': [], - } - return sdk_map - - @classmethod - def _sdk_10_layout(cls, version): - - folder_prefix = version + '.' - - sdk_map = cls._new_sdk_map() - - sdk_roots = _Registry.sdk_query_paths(version) - - sdk_version_platform_seen = set() - sdk_roots_seen = set() - - for sdk_t in sdk_roots: - - sdk_root = sdk_t[0] - if sdk_root in sdk_roots_seen: - continue - sdk_roots_seen.add(sdk_root) - - if not os.path.exists(sdk_root): - continue - - sdk_include_path = os.path.join(sdk_root, 'include') - if not os.path.exists(sdk_include_path): - continue - - for version_nbr, version_nbr_path in _Util.listdir_dirs(sdk_include_path): - - if not version_nbr.startswith(folder_prefix): - continue - - sdk_inc_path = _Util.process_path(os.path.join(version_nbr_path, 'um')) - if not os.path.exists(sdk_inc_path): - continue - - for platform_type, sdk_inc_file in [ - ('desktop', 'winsdkver.h'), - ('uwp', 'windows.h'), - ]: - - if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): - continue - - key = (version_nbr, platform_type) - if key in sdk_version_platform_seen: - continue - sdk_version_platform_seen.add(key) - - sdk_map[platform_type].append(version_nbr) - - for key, val in sdk_map.items(): - val.sort(reverse=True) - - return sdk_map - - @classmethod - def _sdk_81_layout(cls, version): - - version_nbr = version - - sdk_map = cls._new_sdk_map() - - sdk_roots = _Registry.sdk_query_paths(version) - - sdk_version_platform_seen = set() - sdk_roots_seen = set() - - for sdk_t in sdk_roots: - - sdk_root = sdk_t[0] - if sdk_root in sdk_roots_seen: - continue - sdk_roots_seen.add(sdk_root) - - # msvc does not check for existence of root or other files - - sdk_inc_path = _Util.process_path(os.path.join(sdk_root, r'include\um')) - if not os.path.exists(sdk_inc_path): - continue - - for platform_type, sdk_inc_file in [ - ('desktop', 'winsdkver.h'), - ('uwp', 'windows.h'), - ]: - - if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): - continue - - key = (version_nbr, platform_type) - if key in sdk_version_platform_seen: - continue - sdk_version_platform_seen.add(key) - - sdk_map[platform_type].append(version_nbr) - - for key, val in sdk_map.items(): - val.sort(reverse=True) - - return sdk_map - - sdk_map_cache = {} - sdk_cache = {} - - @classmethod - def _reset_sdk_cache(cls): - debug('reset %s: sdk cache', cls.__name__) - cls._sdk_map_cache = {} - cls._sdk_cache = {} - - @classmethod - def _sdk_10(cls, key, reg_version): - if key in cls.sdk_map_cache: - sdk_map = cls.sdk_map_cache[key] - else: - sdk_map = cls._sdk_10_layout(reg_version) - cls.sdk_map_cache[key] = sdk_map - return sdk_map - - @classmethod - def _sdk_81(cls, key, reg_version): - if key in cls.sdk_map_cache: - sdk_map = cls.sdk_map_cache[key] - else: - sdk_map = cls._sdk_81_layout(reg_version) - cls.sdk_map_cache[key] = sdk_map - return sdk_map - - @classmethod - def _combine_sdk_map_list(cls, sdk_map_list): - combined_sdk_map = cls._new_sdk_map() - for sdk_map in sdk_map_list: - for key, val in sdk_map.items(): - combined_sdk_map[key].extend(val) - return combined_sdk_map - - sdk_dispatch_map = None - - @classmethod - def _init_sdk_dispatch_map(cls): - cls.sdk_dispatch_map = { - '10.0': (cls._sdk_10, '10.0'), - '8.1': (cls._sdk_81, '8.1'), - } - - @classmethod - def _verify_sdk_dispatch_map(cls): - debug('%s verify sdk_dispatch_map', cls.__name__) - cls._init_sdk_dispatch_map() - for sdk_version in _Config.MSVC_SDK_VERSIONS: - if sdk_version in cls.sdk_dispatch_map: - continue - err_msg = 'sdk version {} not in {}.sdk_dispatch_map'.format(sdk_version, cls.__name__) - raise MSVCInternalError(err_msg) - return None - - @classmethod - def _version_list_sdk_map(cls, version_list): - - if not cls.sdk_dispatch_map: - cls._init_sdk_dispatch_map() - - sdk_map_list = [] - for version in version_list: - func, reg_version = cls.sdk_dispatch_map[version] - sdk_map = func(version, reg_version) - sdk_map_list.append(sdk_map) - - combined_sdk_map = cls._combine_sdk_map_list(sdk_map_list) - return combined_sdk_map - - @classmethod - def _sdk_map(cls, version_list): - key = tuple(version_list) - if key in cls.sdk_cache: - sdk_map = cls.sdk_cache[key] - else: - version_numlist = [float(v) for v in version_list] - version_numlist.sort(reverse=True) - key = tuple([str(v) for v in version_numlist]) - sdk_map = cls._version_list_sdk_map(key) - cls.sdk_cache[key] = sdk_map - return sdk_map - - @classmethod - def get_sdk_version_list(cls, version_list, platform_type): - sdk_map = cls._sdk_map(version_list) - sdk_list = sdk_map.get(platform_type, []) - return sdk_list - - @classmethod - def get_msvc_sdk_version_list(cls, msvc_version=None, msvc_uwp_app=False): - debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app)) - - sdk_versions = [] - - verstr = get_msvc_version_numeric(msvc_version) - vs_def = _Config.MSVC_VERSION_EXTERNAL.get(verstr, None) - if not vs_def: - debug('vs_def is not defined') - return sdk_versions - - is_uwp = True if msvc_uwp_app in _Config.BOOLEAN_SYMBOLS[True] else False - platform_type = 'uwp' if is_uwp else 'desktop' - sdk_list = _WindowsSDK.get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) - - sdk_versions.extend(sdk_list) - debug('sdk_versions=%s', repr(sdk_versions)) - - return sdk_versions - - @classmethod - def reset(cls): - debug('reset %s', cls.__name__) - cls._reset_sdk_cache() - - @classmethod - def verify(cls): - debug('verify %s', cls.__name__) - cls._verify_sdk_dispatch_map() - -_Dispatcher.register(_WindowsSDK) - -class _ScriptArguments: - - # 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, - } - - @classmethod - def _verify_re_sdk_dispatch_map(cls): - debug('%s verify re_sdk_dispatch_map', cls.__name__) - for sdk_version in _Config.MSVC_SDK_VERSIONS: - if sdk_version in cls.re_sdk_dispatch_map: - continue - err_msg = 'sdk version {} not in {}.re_sdk_dispatch_map'.format(sdk_version, cls.__name__) - raise MSVCInternalError(err_msg) - return None - - # capture msvc version - re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) - - re_toolset_full = re.compile(r'''^(?: - (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY - (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ - )$''', re.VERBOSE) - - re_toolset_140 = re.compile(r'''^(?: - (?:14[.]0{1,2})| # 14.0 - 14.00 - (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000 - )$''', re.VERBOSE) - - # valid SxS formats will be matched with re_toolset_full: match 3 '.' format - re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') - - # 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 - - # 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): - ARCH = 0 # arch - 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', # fully qualified msvc version (e.g., '14.1Exp') - 'vs_def', - ]) - - @classmethod - def _msvc_version(cls, version): - - verstr = get_msvc_version_numeric(version) - vs_def = _Config.MSVC_VERSION_INTERNAL[verstr] - - version_args = cls.MSVC_VERSION_ARGS_DEFINITION( - version = version, - vs_def = vs_def, - ) - - return version_args - - @classmethod - def _msvc_script_argument_uwp(cls, env, msvc, arglist): - - 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 _Config.BOOLEAN_SYMBOLS[True]: - return None - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(uwp_app), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - 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 = (cls.SortOrder.UWP, uwp_arg) - arglist.append(argpair) - - return uwp_arg - - @classmethod - def _user_script_argument_uwp(cls, env, uwp, user_argstr): - - matches = [m for m in cls.re_vcvars_uwp.finditer(user_argstr)] - if not matches: - return None - - if len(matches) > 1: - debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) - err_msg = "multiple uwp declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) - raise MSVCArgumentError(err_msg) - - if not uwp: - return None - - 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) - - @classmethod - def _msvc_script_argument_sdk_constraints(cls, msvc, sdk_version): - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc_version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(sdk_version), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - return err_msg - - for msvc_sdk_version in msvc.vs_def.vc_sdk_versions: - re_sdk_version = cls.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 = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version)) - return err_msg - - @classmethod - def _msvc_script_argument_sdk(cls, env, msvc, platform_type, 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_type) - ) - - if not sdk_version: - return None - - err_msg = cls._msvc_script_argument_sdk_constraints(msvc, sdk_version) - if err_msg: - raise MSVCArgumentError(err_msg) - - sdk_list = _WindowsSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) - - if sdk_version not in sdk_list: - err_msg = "MSVC_SDK_VERSION {} not found for platform type {}".format( - repr(sdk_version), repr(platform_type) - ) - raise MSVCArgumentError(err_msg) - - argpair = (cls.SortOrder.SDK, sdk_version) - arglist.append(argpair) - - return sdk_version - - @classmethod - def _msvc_script_default_sdk(cls, env, msvc, platform_type, arglist): - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - return None - - sdk_list = _WindowsSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) - 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_type) - ) - - argpair = (cls.SortOrder.SDK, sdk_default) - arglist.append(argpair) - - return sdk_default - - @classmethod - def _user_script_argument_sdk(cls, env, sdk_version, user_argstr): - - matches = [m for m in cls.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 = "multiple sdk version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) - 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) - - @classmethod - def _msvc_read_toolset_file(cls, 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 - - @classmethod - def _msvc_read_toolset_folders(cls, msvc, vc_dir): - - toolsets_sxs = {} - toolsets_full = [] - - build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()] - for sxs_toolset in sxs_toolsets: - filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) - filepath = os.path.join(build_dir, sxs_toolset, filename) - debug('sxs toolset: check file=%s', repr(filepath)) - if os.path.exists(filepath): - toolset_version = cls._msvc_read_toolset_file(msvc, filepath) - if not toolset_version: - continue - toolsets_sxs[sxs_toolset] = toolset_version - debug( - 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', - repr(msvc.version), repr(sxs_toolset), toolset_version - ) - - toolset_dir = os.path.join(vc_dir, "Tools", "MSVC") - toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] - for toolset_version in toolsets: - binpath = os.path.join(toolset_dir, toolset_version, "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) - ) - - toolsets_full.sort(reverse=True) - debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full)) - - return toolsets_sxs, toolsets_full - - @classmethod - def _msvc_read_toolset_default(cls, msvc, vc_dir): - - build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - - # VS2019+ - filename = "Microsoft.VCToolsVersion.{}.default.txt".format(msvc.vs_def.vc_buildtools_def.vc_buildtools) - filepath = os.path.join(build_dir, filename) - - debug('default toolset: check file=%s', repr(filepath)) - if os.path.exists(filepath): - toolset_buildtools = cls._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 = cls._msvc_read_toolset_file(msvc, filepath) - if toolset_default: - return toolset_default - - return None - - _toolset_version_cache = {} - _toolset_default_cache = {} - - @classmethod - def _reset_toolset_cache(cls): - debug('reset %s: toolset cache', cls.__name__) - cls._toolset_version_cache = {} - cls._toolset_default_cache = {} - - @classmethod - def _msvc_version_toolsets(cls, msvc, vc_dir): - - if msvc.version in cls._toolset_version_cache: - toolsets_sxs, toolsets_full = cls._toolset_version_cache[msvc.version] - else: - toolsets_sxs, toolsets_full = cls._msvc_read_toolset_folders(msvc, vc_dir) - cls._toolset_version_cache[msvc.version] = toolsets_sxs, toolsets_full - - return toolsets_sxs, toolsets_full - - @classmethod - def _msvc_default_toolset(cls, msvc, vc_dir): - - if msvc.version in cls._toolset_default_cache: - toolset_default = cls._toolset_default_cache[msvc.version] - else: - toolset_default = cls._msvc_read_toolset_default(msvc, vc_dir) - cls._toolset_default_cache[msvc.version] = toolset_default - - return toolset_default - - @classmethod - def _msvc_version_toolset_vcvars(cls, msvc, vc_dir, toolset_version): - - if toolset_version == '14.0': - return toolset_version - - toolsets_sxs, toolsets_full = cls._msvc_version_toolsets(msvc, vc_dir) - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric == cls.VS2019.vc_buildtools_def.vc_version_numeric: - # necessary to detect toolset not found - if toolset_version == '14.28.16.8': - new_toolset_version = '14.28' - # 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. - debug( - 'rewrite toolset_version=%s => toolset_version=%s', - repr(toolset_version), repr(new_toolset_version) - ) - toolset_version = new_toolset_version - - if toolset_version in toolsets_sxs: - toolset_vcvars = toolsets_sxs[toolset_version] - return toolset_vcvars - - for toolset_full in toolsets_full: - if toolset_full.startswith(toolset_version): - toolset_vcvars = toolset_full - return toolset_vcvars - - return None - - @classmethod - def _msvc_script_argument_toolset_constraints(cls, msvc, toolset_version): - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2017', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(toolset_version), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) - ) - return err_msg - - m = cls.re_toolset_version.match(toolset_version) - if not m: - debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version)) - err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format( - repr(toolset_version) - ) - return err_msg - - toolset_ver = m.group('version') - toolset_vernum = float(toolset_ver) - - if toolset_vernum < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: toolset version constraint: %s < %s VS2015', - repr(toolset_vernum), repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( - repr(toolset_version), repr(toolset_ver), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - return err_msg - - if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: toolset version constraint: toolset %s > %s msvc', - repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( - repr(toolset_version), repr(toolset_ver), repr(msvc.version) - ) - return err_msg - - if toolset_vernum == cls.VS2015.vc_buildtools_def.vc_version_numeric: - # tooset = 14.0 - if cls.re_toolset_full.match(toolset_version): - if not cls.re_toolset_140.match(toolset_version): - debug( - 'invalid: toolset version 14.0 constraint: %s != 14.0', - repr(toolset_version) - ) - err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format( - repr(toolset_version), repr(toolset_version) - ) - return err_msg - return None - - if cls.re_toolset_full.match(toolset_version): - debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) - return None - - if cls.re_toolset_sxs.match(toolset_version): - debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version)) - return None - - debug('invalid: method exit: toolset_version=%s', repr(toolset_version)) - err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version)) - return err_msg - - @classmethod - def _msvc_script_argument_toolset(cls, 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 - - err_msg = cls._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 = cls._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 MSVCArgumentError(err_msg) - - argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars)) - arglist.append(argpair) - - return toolset_vcvars - - @classmethod - def _msvc_script_default_toolset(cls, env, msvc, vc_dir, arglist): - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: - return None - - toolset_default = cls._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)) - - argpair = (cls.SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default)) - arglist.append(argpair) - - return toolset_default - - @classmethod - def _user_script_argument_toolset(cls, env, toolset_version, user_argstr): - - matches = [m for m in cls.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 = "multiple toolset version declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) - 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) - - @classmethod - def _msvc_script_argument_spectre(cls, env, msvc, 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 (True, '1'): - return None - - if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2017.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2017', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2017.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( - repr(spectre_libs), repr(msvc.version), repr(cls.VS2017.vc_buildtools_def.vc_version) - ) - raise MSVCArgumentError(err_msg) - - spectre_arg = 'spectre' - - # spectre libs may not be installed - argpair = (cls.SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg)) - arglist.append(argpair) - - return spectre_arg - - @classmethod - def _msvc_script_argument_user(cls, 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.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric: - debug( - 'invalid: msvc version constraint: %s < %s VS2015', - repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric), - repr(cls.VS2015.vc_buildtools_def.vc_version_numeric) - ) - err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format( - repr(script_args), repr(msvc.version), repr(cls.VS2015.vc_buildtools_def.vc_version) - ) - raise MSVCArgumentError(err_msg) - - # user arguments are not validated - argpair = (cls.SortOrder.USER, script_args) - arglist.append(argpair) - - return script_args - - @classmethod - def _user_script_argument_spectre(cls, env, spectre, user_argstr): - - matches = [m for m in cls.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 = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) - 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) - - @classmethod - def msvc_script_arguments(cls, env, version, vc_dir, arg): - - arglist = [] - - msvc = cls._msvc_version(version) - - if arg: - argpair = (cls.SortOrder.ARCH, arg) - arglist.append(argpair) - - if 'MSVC_SCRIPT_ARGS' in env: - user_argstr = cls._msvc_script_argument_user(env, msvc, arglist) - else: - user_argstr = None - - if 'MSVC_UWP_APP' in env: - uwp = cls._msvc_script_argument_uwp(env, msvc, arglist) - else: - uwp = None - - if user_argstr: - cls._user_script_argument_uwp(env, uwp, user_argstr) - - platform_type = 'uwp' if uwp else 'desktop' - - if 'MSVC_SDK_VERSION' in env: - sdk_version = cls._msvc_script_argument_sdk(env, msvc, platform_type, arglist) - else: - sdk_version = None - - if user_argstr: - user_sdk = cls._user_script_argument_sdk(env, sdk_version, user_argstr) - else: - user_sdk = None - - if cls.MSVC_FORCE_DEFAULT_SDK: - if not sdk_version and not user_sdk: - sdk_version = cls._msvc_script_default_sdk(env, msvc, platform_type, arglist) - - if 'MSVC_TOOLSET_VERSION' in env: - toolset_version = cls._msvc_script_argument_toolset(env, msvc, vc_dir, arglist) - else: - toolset_version = None - - if user_argstr: - user_toolset = cls._user_script_argument_toolset(env, toolset_version, user_argstr) - else: - user_toolset = None - - if cls.MSVC_FORCE_DEFAULT_TOOLSET: - if not toolset_version and not user_toolset: - toolset_version = cls._msvc_script_default_toolset(env, msvc, vc_dir, arglist) - - if 'MSVC_SPECTRE_LIBS' in env: - spectre = cls._msvc_script_argument_spectre(env, msvc, arglist) - else: - spectre = None - - if user_argstr: - cls._user_script_argument_spectre(env, spectre, user_argstr) - - if arglist: - arglist.sort() - argstr = ' '.join([argpair[-1] for argpair in arglist]).strip() - else: - argstr = '' - - debug('arguments: %s', repr(argstr)) - return argstr - - @classmethod - def reset(cls): - debug('reset %s', cls.__name__) - cls._reset_toolset_cache() - - @classmethod - def verify(cls): - debug('verify %s', cls.__name__) - cls._verify_re_sdk_dispatch_map() - -_Dispatcher.register(_ScriptArguments) - -# internal consistency check -_Dispatcher.verify() From 14ee60c050d577704004ba8ade7e5347e356f9e9 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 17:08:34 -0400 Subject: [PATCH 019/108] Fix typo in module name --- SCons/Tool/MSCommon/vc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 5a27f44457..2e5d5429cf 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1107,7 +1107,7 @@ def msvc_setup_env_once(env, tool=None): msg = "No versions of the MSVC compiler were found.\n" \ " Visual Studio C/C++ compilers may not be set correctly.\n" \ " Requested tool(s) are: {}".format(req_tools) - MSVC.Notfound.policy_handler(env, msg) + MSVC.NotFound.policy_handler(env, msg) def msvc_find_valid_batch_script(env, version): """Find and execute appropriate batch script to set up build env. From 9619adbcf75cf9f6c851e577f49d92b021012de3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 17:50:03 -0400 Subject: [PATCH 020/108] Cleanup MSCommon/vc imports and move Dispatcher imports and registration --- SCons/Tool/MSCommon/MSVC/Config.py | 2 +- SCons/Tool/MSCommon/MSVC/Dispatcher.py | 1 + SCons/Tool/MSCommon/MSVC/NotFound.py | 3 ++- SCons/Tool/MSCommon/MSVC/Registry.py | 3 ++- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 3 ++- SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py | 2 +- SCons/Tool/MSCommon/MSVC/WinSDK.py | 3 ++- SCons/Tool/MSCommon/MSVC/__init__.py | 7 +------ SCons/Tool/MSCommon/__init__.py | 6 ------ SCons/Tool/MSCommon/vc.py | 6 ++++++ 10 files changed, 18 insertions(+), 18 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 476dcb35eb..8f3a2cc839 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -30,9 +30,9 @@ ) from . import Dispatcher - Dispatcher.register_modulename(__name__) + UNDEFINED = object() BOOLEAN_SYMBOLS = {} diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py index ebcd704e00..0b216caee3 100644 --- a/SCons/Tool/MSCommon/MSVC/Dispatcher.py +++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py @@ -31,6 +31,7 @@ debug, ) + _refs = [] def register_class(ref): diff --git a/SCons/Tool/MSCommon/MSVC/NotFound.py b/SCons/Tool/MSCommon/MSVC/NotFound.py index 7abe5ad6ba..6ade285226 100644 --- a/SCons/Tool/MSCommon/MSVC/NotFound.py +++ b/SCons/Tool/MSCommon/MSVC/NotFound.py @@ -36,15 +36,16 @@ debug, ) -from . import Dispatcher from . import Config from .Exceptions import ( MSVCVersionNotFound, ) +from . import Dispatcher Dispatcher.register_modulename(__name__) + _MSVC_NOTFOUND_POLICY_DEF = Config.MSVC_NOTFOUND_POLICY_INTERNAL['warning'] def _msvc_notfound_policy_lookup(symbol): diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py index 848b1254f6..f9a5eb25d7 100644 --- a/SCons/Tool/MSCommon/MSVC/Registry.py +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -39,11 +39,12 @@ read_reg, ) -from . import Dispatcher from . import Util +from . import Dispatcher Dispatcher.register_modulename(__name__) + def read_value(hkey, subkey_valname): try: rval = read_reg(subkey_valname, hkroot=hkey) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 324f8be8ad..1b02396727 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -37,7 +37,6 @@ debug, ) -from . import Dispatcher from . import Util from . import Config from . import WinSDK @@ -47,8 +46,10 @@ MSVCArgumentError, ) +from . import Dispatcher Dispatcher.register_modulename(__name__) + # 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$') diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py index 8a790076a3..8b9faa9a1f 100644 --- a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py +++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py @@ -44,9 +44,9 @@ ) from . import Dispatcher - Dispatcher.register_modulename(__name__) + class _Data: separator = r';' diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index e95c72eef5..bd8d9b0094 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -31,7 +31,6 @@ debug, ) -from . import Dispatcher from . import Util from . import Config from . import Registry @@ -40,8 +39,10 @@ MSVCInternalError, ) +from . import Dispatcher Dispatcher.register_modulename(__name__) + def _new_sdk_map(): sdk_map = { 'desktop': [], diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index afd993f969..1341415846 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -28,8 +28,6 @@ from . import Exceptions from . import Util -from . import Dispatcher as _Dispatcher - from . import Config from . import Registry from . import SetupEnvDefault @@ -37,10 +35,7 @@ from . import WinSDK from . import ScriptArguments -from .NotFound import ( - set_msvc_notfound_policy, - get_msvc_notfound_policy, -) +from . import Dispatcher as _Dispatcher def reset(): _Dispatcher.reset() diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 9f35e94002..7900a8770c 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -32,18 +32,12 @@ from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env -from SCons.Tool.MSCommon.MSVC import ( - set_msvc_notfound_policy, - get_msvc_notfound_policy, -) - from SCons.Tool.MSCommon.vc import ( msvc_exists, msvc_setup_env_tool, msvc_setup_env_once, msvc_version_to_maj_min, msvc_find_vswhere, - get_msvc_sdk_versions, ) from SCons.Tool.MSCommon.vs import ( diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 2e5d5429cf..a2e8e42213 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -61,10 +61,16 @@ from .sdk import get_installed_sdks from . import MSVC + from .MSVC.Exceptions import ( VisualCException ) +from .MSVC.NotFound import ( + set_msvc_notfound_policy, + get_msvc_notfound_policy, +) + class UnsupportedVersion(VisualCException): pass From a7c56cebc24fd5aaa2dd05a1751412120c946835 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 18:36:54 -0400 Subject: [PATCH 021/108] Fix msvc notfound policy module path for test --- test/fixture/no_msvc/no_msvcs_sconstruct_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py index f5cabf769f..4c76d44d9f 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py @@ -10,7 +10,7 @@ def DummyVsWhere(msvc_version, env): SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere -SCons.Tool.MSCommon.set_msvc_notfound_policy('error') +SCons.Tool.MSCommon.vc.set_msvc_notfound_policy('error') env = SCons.Environment.Environment(MSVC_VERSION='14.3') From f6a7b846bba8477ca8221edd2b76dc3d1d842439 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 20 Jun 2022 21:48:57 -0400 Subject: [PATCH 022/108] Add global for cache reset (classmethod to module omission) and remove duplicate import --- SCons/Tool/MSCommon/MSVC/Registry.py | 2 -- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 2 ++ SCons/Tool/MSCommon/MSVC/WinSDK.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py index f9a5eb25d7..492f3d0b54 100644 --- a/SCons/Tool/MSCommon/MSVC/Registry.py +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -30,8 +30,6 @@ from SCons.Util import ( HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, - HKEY_LOCAL_MACHINE, - HKEY_CURRENT_USER, ) from .. common import ( diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 1b02396727..d0fc6ef9d9 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -373,6 +373,8 @@ def _msvc_read_toolset_default(msvc, vc_dir): _toolset_default_cache = {} def _reset_toolset_cache(): + global _toolset_version_cache + global _toolset_default_cache debug('reset: toolset cache') _toolset_version_cache = {} _toolset_default_cache = {} diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index bd8d9b0094..8338c2798e 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -152,6 +152,8 @@ def _sdk_81_layout(version): _sdk_cache = {} def _reset_sdk_cache(): + global _sdk_map_cache + global _sdk_cache debug('') _sdk_map_cache = {} _sdk_cache = {} From dd328cff200935a7f570396f06b93a3da82278d7 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 08:06:01 -0400 Subject: [PATCH 023/108] Suppress sider imported but unused for namespace. Restrict MSVC_UWP_APP boolean symbols accepted. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 13 ++++++++----- SCons/Tool/MSCommon/MSVC/__init__.py | 16 ++++++++-------- SCons/Tool/MSCommon/vc.py | 4 ++-- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index d0fc6ef9d9..a64d9f4625 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -50,6 +50,9 @@ Dispatcher.register_modulename(__name__) +# MSVC_UWP_APP argument: boolean True +_UWP_ARGUMENT_BOOLEAN_TRUE = (True, '1') + # 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$') @@ -91,10 +94,10 @@ def _verify_re_sdk_dispatch_map(): 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 +_MSVC_FORCE_DEFAULT_SDK = False # Force default toolset argument -MSVC_FORCE_DEFAULT_TOOLSET = False +_MSVC_FORCE_DEFAULT_TOOLSET = False # MSVC batch file arguments: # @@ -148,7 +151,7 @@ def _msvc_script_argument_uwp(env, msvc, arglist): if not uwp_app: return None - if uwp_app not in Config.BOOLEAN_SYMBOLS[True]: + if uwp_app not in _UWP_ARGUMENT_BOOLEAN_TRUE: return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: @@ -691,7 +694,7 @@ def msvc_script_arguments(env, version, vc_dir, arg): else: user_sdk = None - if MSVC_FORCE_DEFAULT_SDK: + if _MSVC_FORCE_DEFAULT_SDK: if not sdk_version and not user_sdk: sdk_version = _msvc_script_default_sdk(env, msvc, platform_type, arglist) @@ -705,7 +708,7 @@ def msvc_script_arguments(env, version, vc_dir, arg): else: user_toolset = None - if MSVC_FORCE_DEFAULT_TOOLSET: + if _MSVC_FORCE_DEFAULT_TOOLSET: if not toolset_version and not user_toolset: toolset_version = _msvc_script_default_toolset(env, msvc, vc_dir, arglist) diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index 1341415846..c07e849492 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -25,15 +25,15 @@ Functions for Microsoft Visual C/C++. """ -from . import Exceptions -from . import Util +from . import Exceptions # noqa: F401 +from . import Util # noqa: F401 -from . import Config -from . import Registry -from . import SetupEnvDefault -from . import NotFound -from . import WinSDK -from . import ScriptArguments +from . import Config # noqa: F401 +from . import Registry # noqa: F401 +from . import SetupEnvDefault # noqa: F401 +from . import NotFound # noqa: F401 +from . import WinSDK # noqa: F401 +from . import ScriptArguments # noqa: F401 from . import Dispatcher as _Dispatcher diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index a2e8e42213..2f1ec11f4f 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -67,8 +67,8 @@ ) from .MSVC.NotFound import ( - set_msvc_notfound_policy, - get_msvc_notfound_policy, + set_msvc_notfound_policy, # noqa: F401 + get_msvc_notfound_policy, # noqa: F401 ) class UnsupportedVersion(VisualCException): From 5dd220bf7d625771acc7f7ca476275795029b560 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 08:59:21 -0400 Subject: [PATCH 024/108] Add internal, undocumented SCONS_CACHE_MSVC_FORCE_DEFAULTS environment variable to force default SDK and toolset arguments. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 22 +++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index a64d9f4625..8bef3f5422 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -34,6 +34,7 @@ ) from ..common import ( + CONFIG_CACHE, debug, ) @@ -50,6 +51,13 @@ Dispatcher.register_modulename(__name__) +# Force default SDK and toolset arguments in cache +_SCONS_CACHE_MSVC_FORCE_DEFAULTS = False +if CONFIG_CACHE: + # SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal and not documented. + if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in Config.BOOLEAN_SYMBOLS[True]: + _SCONS_CACHE_MSVC_FORCE_DEFAULTS = True + # MSVC_UWP_APP argument: boolean True _UWP_ARGUMENT_BOOLEAN_TRUE = (True, '1') @@ -99,6 +107,20 @@ def _verify_re_sdk_dispatch_map(): # Force default toolset argument _MSVC_FORCE_DEFAULT_TOOLSET = False +def _msvc_force_default_sdk(force=True): + global _MSVC_FORCE_DEFAULT_SDK + _MSVC_FORCE_DEFAULT_SDK = force + debug('_MSVC_FORCE_DEFAULT_SDK=%s', repr(force)) + +def _msvc_force_default_toolset(force=True): + global _MSVC_FORCE_DEFAULT_TOOLSET + _MSVC_FORCE_DEFAULT_TOOLSET = force + debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force)) + +if _SCONS_CACHE_MSVC_FORCE_DEFAULTS: + _msvc_force_default_sdk(True) + _msvc_force_default_toolset(True) + # MSVC batch file arguments: # # VS2022: UWP, SDK, TOOLSET, SPECTRE From ac9b54756cedd01c36bfbf1bca7d59f92fd08f15 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 10:08:53 -0400 Subject: [PATCH 025/108] Consider MSVC_TOOLSET_VERSION specification intent to use msvc tools. Update boolean symbols accepted for uwp and spectre. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 9 +++++---- SCons/Tool/MSCommon/vc.py | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 8bef3f5422..2a94650aed 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -58,8 +58,9 @@ if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in Config.BOOLEAN_SYMBOLS[True]: _SCONS_CACHE_MSVC_FORCE_DEFAULTS = True -# MSVC_UWP_APP argument: boolean True -_UWP_ARGUMENT_BOOLEAN_TRUE = (True, '1') +# 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}$') @@ -173,7 +174,7 @@ def _msvc_script_argument_uwp(env, msvc, arglist): if not uwp_app: return None - if uwp_app not in _UWP_ARGUMENT_BOOLEAN_TRUE: + if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY: return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: @@ -610,7 +611,7 @@ def _msvc_script_argument_spectre(env, msvc, arglist): if not spectre_libs: return None - if spectre_libs not in (True, '1'): + if spectre_libs not in _ARGUMENT_BOOLEAN_TRUE: return None if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 2f1ec11f4f..dc97e2fc57 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1300,12 +1300,20 @@ def msvc_setup_env_user(env=None): if env: # Intent is to use msvc tools: - # MSVC_VERSION or MSVS_VERSION: defined and is True - # MSVC_USE_SCRIPT: defined and (is string or is False) - # MSVC_USE_SETTINGS: defined and is not None + # MSVC_VERSION: defined and evaluates True + # MSVS_VERSION: defined and evaluates True + # MSVC_TOOLSET_VERSION: defined and evaluates True + # MSVC_USE_SCRIPT: defined and (is string or evaluates False) + # MSVC_USE_SETTINGS: defined and is not None + + # Arguments possibly present but not considered: + # MSVC_SDK_VERSION + # MSVC_UWP_APP + # MSVC_SPECTRE_LIBS + # MSVC_SCRIPT_ARGS # defined and is True - for key in ['MSVC_VERSION', 'MSVS_VERSION']: + for key in ['MSVC_VERSION', 'MSVS_VERSION', 'MSVC_TOOLSET_VERSION']: if key in env and env[key]: rval = True debug('key=%s, return=%s', repr(key), rval) From 377e8152bbd9cc4556318283c845dd66defe2d8c Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 10:11:29 -0400 Subject: [PATCH 026/108] Comment out BatchFileExecutionWarning definition and invocation. --- SCons/Tool/MSCommon/vc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index dc97e2fc57..7b6034e7f7 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -95,8 +95,8 @@ class MSVCScriptNotFound(VisualCException): class MSVCUseSettingsError(VisualCException): pass -class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): - pass +#class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): +# pass # Dict to 'canonalize' the arch @@ -1055,7 +1055,7 @@ def script_env(script, args=None): # detected errors, cl.exe on path debug('script=%s args=%s errors=%s', repr(script), repr(args), script_errmsg) # This may be a bad idea (scons environment != vs cmdline environment) - SCons.Warnings.warn(BatchFileExecutionWarning, script_errmsg) + #SCons.Warnings.warn(BatchFileExecutionWarning, script_errmsg) # TODO: errlog/errstr should be added to cache and warning moved to call site # once we updated cache, give a chance to write out if user wanted From 016e7f7bdc6b901916da08a361b5cca8c24ee600 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 10:26:36 -0400 Subject: [PATCH 027/108] Update flake8 F401 placement --- SCons/Tool/MSCommon/vc.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7b6034e7f7..f34e9e0c85 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -67,9 +67,9 @@ ) from .MSVC.NotFound import ( - set_msvc_notfound_policy, # noqa: F401 - get_msvc_notfound_policy, # noqa: F401 -) + set_msvc_notfound_policy, + get_msvc_notfound_policy, +) # noqa: F401 class UnsupportedVersion(VisualCException): pass From 56bd50d3a15cbe3c82e84bf703a3f4bf1e5615d5 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 10:32:31 -0400 Subject: [PATCH 028/108] Add comment and import one-by-one for msvc notfound policy and flake8 F401 --- SCons/Tool/MSCommon/vc.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index f34e9e0c85..56f21e82f5 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -66,10 +66,9 @@ VisualCException ) -from .MSVC.NotFound import ( - set_msvc_notfound_policy, - get_msvc_notfound_policy, -) # noqa: F401 +# msvc test(s) expect avaiable via vc +from .MSVC.NotFound import set_msvc_notfound_policy # noqa: F401 +from .MSVC.NotFound import get_msvc_notfound_policy # noqa: F401 class UnsupportedVersion(VisualCException): pass From ad7a59d1621d4154aeb51ba402f77aa18d65e21d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 10:38:05 -0400 Subject: [PATCH 029/108] Fix sider issue --- SCons/Tool/MSCommon/vc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 56f21e82f5..5130727222 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -66,7 +66,7 @@ VisualCException ) -# msvc test(s) expect avaiable via vc +# msvc test(s) expect notfound policy available via vc from .MSVC.NotFound import set_msvc_notfound_policy # noqa: F401 from .MSVC.NotFound import get_msvc_notfound_policy # noqa: F401 From a5938cce33f77237e45ad86a3f43e58f65ba74d3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 11:22:29 -0400 Subject: [PATCH 030/108] Move SCONS_CACHE_MSVC_FORCE_DEFAULTS environment variable query to MSCommon and set boolean if active. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 18 +++++++----------- SCons/Tool/MSCommon/common.py | 5 +++++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 2a94650aed..aa9694682d 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -34,7 +34,7 @@ ) from ..common import ( - CONFIG_CACHE, + CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS, debug, ) @@ -51,13 +51,6 @@ Dispatcher.register_modulename(__name__) -# Force default SDK and toolset arguments in cache -_SCONS_CACHE_MSVC_FORCE_DEFAULTS = False -if CONFIG_CACHE: - # SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal and not documented. - if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in Config.BOOLEAN_SYMBOLS[True]: - _SCONS_CACHE_MSVC_FORCE_DEFAULTS = True - # Script argument: boolean True _ARGUMENT_BOOLEAN_TRUE_LEGACY = (True, '1') # MSVC_UWP_APP _ARGUMENT_BOOLEAN_TRUE = (True,) @@ -118,9 +111,12 @@ def _msvc_force_default_toolset(force=True): _MSVC_FORCE_DEFAULT_TOOLSET = force debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force)) -if _SCONS_CACHE_MSVC_FORCE_DEFAULTS: - _msvc_force_default_sdk(True) - _msvc_force_default_toolset(True) +def msvc_force_default_arguments(force=True): + _msvc_force_default_sdk(force) + _msvc_force_default_toolset(force) + +if CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS: + msvc_force_default_arguments(force=True) # MSVC batch file arguments: # diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index c9f07f5ca3..da8fd55603 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -102,6 +102,11 @@ def debug(x, *args): if CONFIG_CACHE in ('1', 'true', 'True'): CONFIG_CACHE = os.path.join(os.path.expanduser('~'), 'scons_msvc_cache.json') +# SCONS_CACHE_MSVC_FORCE_DEFAULTS is internal-use so undocumented. +CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS = False +if CONFIG_CACHE: + if os.environ.get('SCONS_CACHE_MSVC_FORCE_DEFAULTS') in ('1', 'true', 'True'): + CONFIG_CACHE_FORCE_DEFAULT_ARGUMENTS = True def read_script_env_cache(): """ fetch cached msvc env vars if requested, else return empty dict """ From 52e349c07058d6b7b2b5eb46ae6371427bee9849 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 12:37:03 -0400 Subject: [PATCH 031/108] Remove debug messages that by default are noisy. --- SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py | 2 -- SCons/Tool/MSCommon/vc.py | 7 ++----- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py index 8b9faa9a1f..e1c05bc1b7 100644 --- a/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py +++ b/SCons/Tool/MSCommon/MSVC/SetupEnvDefault.py @@ -73,7 +73,6 @@ def _initialize(env, msvc_exists_func): debug('msvc default:msvc_installed=%s', _Data.msvc_installed) def register_tool(env, tool, msvc_exists_func): - debug('msvc default:tool=%s', tool) if _Data.need_init: _initialize(env, msvc_exists_func) if _Data.msvc_installed: @@ -90,7 +89,6 @@ def register_tool(env, tool, msvc_exists_func): debug('msvc default:tool=%s, msvc_tools=%s', tool, _Data.msvc_tools) def register_setup(env, msvc_exists_func): - debug('msvc default') if _Data.need_init: _initialize(env, msvc_exists_func) _Data.n_setup += 1 diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 5130727222..7ea3583191 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1102,7 +1102,6 @@ def msvc_setup_env_once(env, tool=None): has_run = False if not has_run: - debug('tool=%s', repr(tool)) MSVC.SetupEnvDefault.register_setup(env, msvc_exists) msvc_setup_env(env) env["MSVC_SETUP_RUN"] = True @@ -1285,13 +1284,13 @@ def msvc_setup_env(env): SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) def msvc_exists(env=None, version=None): - debug('version=%s', repr(version)) vcs = get_installed_vcs(env) if version is None: rval = len(vcs) > 0 else: rval = version in vcs - debug('version=%s, return=%s', repr(version), rval) + if not rval: + debug('version=%s, return=%s', repr(version), rval) return rval def msvc_setup_env_user(env=None): @@ -1336,14 +1335,12 @@ def msvc_setup_env_user(env=None): return rval def msvc_setup_env_tool(env=None, version=None, tool=None): - debug('tool=%s, version=%s', repr(tool), repr(version)) MSVC.SetupEnvDefault.register_tool(env, tool, msvc_exists) rval = False if not rval and msvc_exists(env, version): rval = True if not rval and msvc_setup_env_user(env): rval = True - debug('tool=%s, version=%s, return=%s', repr(tool), repr(version), rval) return rval def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): From 7683a2f958287ceee257b0449eed767b0dd275e5 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 21 Jun 2022 12:46:14 -0400 Subject: [PATCH 032/108] Remove MSVC_TOOLSET_VERSION from intent to use msvc tools (attached to default version or specific version). --- SCons/Tool/MSCommon/vc.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7ea3583191..675b8d0a9b 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1304,14 +1304,8 @@ def msvc_setup_env_user(env=None): # MSVC_USE_SCRIPT: defined and (is string or evaluates False) # MSVC_USE_SETTINGS: defined and is not None - # Arguments possibly present but not considered: - # MSVC_SDK_VERSION - # MSVC_UWP_APP - # MSVC_SPECTRE_LIBS - # MSVC_SCRIPT_ARGS - # defined and is True - for key in ['MSVC_VERSION', 'MSVS_VERSION', 'MSVC_TOOLSET_VERSION']: + for key in ['MSVC_VERSION', 'MSVS_VERSION']: if key in env and env[key]: rval = True debug('key=%s, return=%s', repr(key), rval) From 98cf01fd1434463a7af5b3fe5375a9772882dcd2 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 22 Jun 2022 16:53:53 -0400 Subject: [PATCH 033/108] Reorder function declarations --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 46 ++++++++++----------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index aa9694682d..e56dd4a120 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -629,6 +629,29 @@ def _msvc_script_argument_spectre(env, msvc, arglist): 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 = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) + 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 @@ -655,29 +678,6 @@ def _msvc_script_argument_user(env, msvc, arglist): return script_args -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 = "multiple spectre declarations: MSVC_SCRIPT_ARGS={}".format(repr(user_argstr)) - 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_arguments(env, version, vc_dir, arg): arglist = [] From 1de714ae3085d9fd07cb8837f523037885271aa8 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:58:38 -0400 Subject: [PATCH 034/108] Construction variable documentation additions and modifications. --- SCons/Tool/msvc.xml | 553 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 542 insertions(+), 11 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index e8df1288d3..6663aabce6 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -481,9 +481,24 @@ env = Environment(MSVC_VERSION='8.0', MSVC_USE_SETTINGS=msvc_use_settings) -Note: the dictionary content requirements are based on the internal msvc implementation and -therefore may change at any time. The burden is on the user to ensure the dictionary contents -are minimally sufficient to ensure successful builds. +Important usage details: + + + +MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +The dictionary content requirements are based on the internal msvc implementation and +therefore may change at any time. + +The burden is on the user to ensure the dictionary contents are minimally sufficient to +ensure successful builds. + + + @@ -510,21 +525,65 @@ are minimally sufficient to ensure successful builds. -Build libraries for a Universal Windows Platform (UWP) Application. +Build with the Universal Windows Platform (UWP) application Visual C++ libraries. -If &cv-MSVC_UWP_APP; is set, the Visual C++ environment will be set up to point +The valid values for MSVC_UWP_APP are True, +'1', False, '0', +or None. + + + +When MSVC_UWP_APP is enabled (i.e., True or +'1'), the Visual C++ environment will be set up to point to the Windows Store compatible libraries and Visual C++ runtimes. In doing so, any libraries that are built will be able to be used in a UWP App and published to the Windows Store. -This flag will only have an effect with Visual Studio 2015 or later. -This variable must be passed as an argument to the Environment() -constructor; setting it later has no effect. + + + + + +An exception is raised when any of the following conditions are satisfied: + + +MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. + + +MSVC_UWP_APP is enabled and a UWP argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + -Valid values are '1' or '0' +Example - A Visual Studio 2022 build for the Universal Windows Platform: + +env = Environment(MSVC_VERSION='14.3', MSVC_UWP_APP=True) + + + + +Important usage details: + + + +MSVC_UWP_APP must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled +which could result in build failures. + +The burden is on the user to ensure the requisite UWP libraries are installed. + + + @@ -582,7 +641,7 @@ and also before &f-link-env-Tool; is called to ininitialize any of those tools: -Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detected. +Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not detected. @@ -590,7 +649,7 @@ Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detec when the requested msvc version is not detected. - + The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are: @@ -663,14 +722,486 @@ A non-default tool list is specified that does not contain any of the msvc tools + +Important usage details: + + + +MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + + When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue subject to the conditions listed above. The default &scons; behavior may change in the future. + + + + + + +Pass user-defined arguments to the Visual C++ batch file determined via autodetection. + + + +MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support +via construction variables or when there is an issue with the appropriate construction variable validation. +When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;) +rather than MSVC_SCRIPT_ARGS arguments. + + + +The valid values for MSVC_SCRIPT_ARGS are: None, a string, +or a list of strings. + + + +The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened"). +The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined +via autodetection subject to the validation conditions listed below. + + + +MSVC_SCRIPT_ARGS is ignored when the value is None and when the +result from argument conversion is an empty string. The validation conditions below do not apply. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier. + + + +Multiple SDK version arguments (e.g., '10.0.20348.0') are specified +in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument +(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS. +Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS +are not allowed. + + + +Multiple toolset version arguments (e.g., '-vcvars_ver=14.29') +are specified in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument +(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS. +Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and +MSVC_SCRIPT_ARGS are not allowed. + + + +Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre') +are specified in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument +(e.g., '-vcvars_spectre_libs=spectre') is specified in +MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS; +and MSVC_SCRIPT_ARGS are not allowed. + + + +Multiple UWP arguments (e.g., uwp or store) are specified +in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or +store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations +via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed. + + + + + + +Example 1 - A Visual Studio 2022 build with an SDK version and a toolset version +specified with a string argument: + +env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS='10.0.20348.0 -vcvars_ver=14.29.30133') + + + + +Example 2 - A Visual Studio 2022 build with an SDK version and a toolset version +specified with a list argument: + +env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['10.0.20348.0', '-vcvars_ver=14.29.30133']) + + + + +Important usage details: + + + +MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + +Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments +are not validated. + + + + +Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely +to result in build failures for reasons that are not readily apparent and may be difficult to diagnose. + +The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent +and compatible with the version of msvc selected. + + + + + + + +Build with a specific version of the Microsoft Software Development Kit (SDK). + + + +The valid values for MSVC_SDK_VERSION are: None +or a string containing the requested SDK version (e.g., '10.0.20348.0'). + + + +MSVC_SDK_VERSION is ignored when the value is None and when +the value is an empty string. The validation conditions below do not apply. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier. + + + +MSVC_SDK_VERSION is specified and an SDK version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + +The MSVC_SDK_VERSION specified does not match any of the supported formats: + + +'10.0.XXXXX.Y' [SDK 10.0] + + +'8.1' [SDK 8.1] + + + + + +The system folder for the corresponding MSVC_SDK_VERSION version is not found. +The requested SDK version does not appear to be installed. + + + +The MSVC_SDK_VERSION version does not appear to support the requested platform +type (i.e., UWP or Desktop). The requested SDK version +platform type components do not appear to be installed. + + + + + + +Example 1 - A Visual Studio 2022 build with a specific Windows SDK version: + +env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0') + + + + +Example 2 - A Visual Studio 2022 build with a specific SDK version for the Universal Windows Platform: + +env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0', MSVC_UWP_APP=True) + + + + +Important usage details: + + + +MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +Should a SDK 10.0 version be installed that does not follow the naming scheme above, the +SDK version will need to be specified via &cv-link-MSVC_SCRIPT_ARGS; until the version number +validation format can be extended. + + + + +Should an exception be raised indicating that the SDK version is not found, verify that +the requested SDK version is installed with the necessary platform type components. + + + +There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0' +when using the v141 build tools and the target architecture is ARM64. +Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be +employed to specify a different SDK version for the build. + + + + + + + + + + + +Build with a specific Visual C++ toolset version. + + + +Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection +of msvc instances. The MSVC_TOOLSET_VERSION is applied after +an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION; +is not specified. + + + +The valid values for MSVC_TOOLSET_VERSION are: None +or a string containing the requested toolset version (e.g., '14.29'). + + + +MSVC_TOOLSET_VERSION is ignored when the value is None and when +the value is an empty string. The validation conditions below do not apply. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier. + + + +MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + + +The MSVC_TOOLSET_VERSION specified does not match any of the supported formats: + + + + + +'XX.Y' + + + +'XX.YY' + + + +'XX.YY.ZZZZZ' + + + +'XX.YY.Z' to 'XX.YY.ZZZZ' + +[&scons; extension not directly supported by the msvc batch files and may be removed in the future] + + + + +'XX.YY.ZZ.N' [SxS format] + + + +'XX.YY.ZZ.NN' [SxS format] + + + + + + + +The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified +is for Visual Studio 2013 and earlier (e.g., '12.0'). + + + +The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified +is greater than the msvc version selected (e.g., '99.0'). + + + +A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found. +The requested toolset version does not appear to be installed. + + + + + + +Toolset selection details: + + + +SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above. + +In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly +mapped to toolset version 14.16.27023. + + + +When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number: +the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION +prefix is selected. + + + +When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix +(i.e., 'XX.Y') and the major msvc version is that of the latest release of +Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version. + +In the latest release of Visual Studio, the default Visual C++ toolset version is not necessarily the +toolset with the largest version number. + + + + + + +Example environment constructor invocations with toolset version specifications: + +Environment(MSVC_TOOLSET_VERSION='14.2') +Environment(MSVC_TOOLSET_VERSION='14.29') +Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133') +Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11') + + + + +Important usage details: + + + +MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +The existence of the toolset host architecture and target architecture folders are not verified +when MSVC_TOOLSET_VERSION is specified which could result in build failures. + +The burden is on the user to ensure the requisite toolset target architecture build tools are installed. + + + + + + + + + + + +Build with the spectre-mitigated Visual C++ libraries. + + + +The valid values for MSVC_SPECTRE_LIBS are: True, +False, or None. + + + +When MSVC_SPECTRE_LIBS is enabled (i.e., True), +the Visual C++ environment will include the paths to the spectre-mitigated implementations +of the Microsoft Visual C++ libraries. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier. + + + +MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + + + + +Example - A Visual Studio 2022 build with spectre mitigated Visual C++ libraries: + +env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True) + + + + +Important usage details: + + + +MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + +Additional compiler switches (e.g., /Qspectre) are necessary for including +spectre mitigations when building user artifacts. Refer to the Visual Studio documentation for +details. + + + + +The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS +is enabled which could result in build failures. + +The burden is on the user to ensure the requisite libraries with spectre mitigations are installed. + + + + + + + From f99f7cab7bc3af5ef18e3189f531572df4558fe4 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 23 Jun 2022 18:59:01 -0400 Subject: [PATCH 035/108] Construction variable documentation additions and modifications. --- doc/generated/variables.gen | 558 +++++++++++++++++++++++++++++++++++- doc/generated/variables.mod | 8 + 2 files changed, 555 insertions(+), 11 deletions(-) diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 996e35acbd..f1b3fff078 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -4686,7 +4686,7 @@ will be compiled separately. MSVC_NOTFOUND_POLICY -Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detected. +Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not detected. @@ -4694,7 +4694,7 @@ Specify the scons behavior when the Microsoft Visual C/C++ compiler is not detec when the requested msvc version is not detected. - + The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are: @@ -4767,11 +4767,488 @@ A non-default tool list is specified that does not contain any of the msvc tools + +Important usage details: + + + +MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + + When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue subject to the conditions listed above. The default &scons; behavior may change in the future. + + + + + MSVC_SCRIPT_ARGS + + +Pass user-defined arguments to the Visual C++ batch file determined via autodetection. + + + +MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support +via construction variables or when there is an issue with the appropriate construction variable validation. +When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;) +rather than MSVC_SCRIPT_ARGS arguments. + + + +The valid values for MSVC_SCRIPT_ARGS are: None, a string, +or a list of strings. + + + +The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened"). +The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined +via autodetection subject to the validation conditions listed below. + + + +MSVC_SCRIPT_ARGS is ignored when the value is None and when the +result from argument conversion is an empty string. The validation conditions below do not apply. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier. + + + +Multiple SDK version arguments (e.g., '10.0.20348.0') are specified +in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument +(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS. +Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS +are not allowed. + + + +Multiple toolset version arguments (e.g., '-vcvars_ver=14.29') +are specified in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument +(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS. +Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and +MSVC_SCRIPT_ARGS are not allowed. + + + +Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre') +are specified in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument +(e.g., '-vcvars_spectre_libs=spectre') is specified in +MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS; +and MSVC_SCRIPT_ARGS are not allowed. + + + +Multiple UWP arguments (e.g., uwp or store) are specified +in MSVC_SCRIPT_ARGS. + + + +&cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or +store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations +via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed. + + + + + + +Example 1 - A Visual Studio 2022 build with an SDK version and a toolset version +specified with a string argument: + +env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS='10.0.20348.0 -vcvars_ver=14.29.30133') + + + + +Example 2 - A Visual Studio 2022 build with an SDK version and a toolset version +specified with a list argument: + +env = Environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['10.0.20348.0', '-vcvars_ver=14.29.30133']) + + + + +Important usage details: + + + +MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + +Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments +are not validated. + + + + +Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely +to result in build failures for reasons that are not readily apparent and may be difficult to diagnose. + +The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent +and compatible with the version of msvc selected. + + + + + + + + + + MSVC_SDK_VERSION + + +Build with a specific version of the Microsoft Software Development Kit (SDK). + + + +The valid values for MSVC_SDK_VERSION are: None +or a string containing the requested SDK version (e.g., '10.0.20348.0'). + + + +MSVC_SDK_VERSION is ignored when the value is None and when +the value is an empty string. The validation conditions below do not apply. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier. + + + +MSVC_SDK_VERSION is specified and an SDK version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + +The MSVC_SDK_VERSION specified does not match any of the supported formats: + + +'10.0.XXXXX.Y' [SDK 10.0] + + +'8.1' [SDK 8.1] + + + + + +The system folder for the corresponding MSVC_SDK_VERSION version is not found. +The requested SDK version does not appear to be installed. + + + +The MSVC_SDK_VERSION version does not appear to support the requested platform +type (i.e., UWP or Desktop). The requested SDK version +platform type components do not appear to be installed. + + + + + + +Example 1 - A Visual Studio 2022 build with a specific Windows SDK version: + +env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0') + + + + +Example 2 - A Visual Studio 2022 build with a specific SDK version for the Universal Windows Platform: + +env = Environment(MSVC_VERSION='14.3', MSVC_SDK_VERSION='10.0.20348.0', MSVC_UWP_APP=True) + + + + +Important usage details: + + + +MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +Should a SDK 10.0 version be installed that does not follow the naming scheme above, the +SDK version will need to be specified via &cv-link-MSVC_SCRIPT_ARGS; until the version number +validation format can be extended. + + + + +Should an exception be raised indicating that the SDK version is not found, verify that +the requested SDK version is installed with the necessary platform type components. + + + +There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0' +when using the v141 build tools and the target architecture is ARM64. +Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be +employed to specify a different SDK version for the build. + + + + + + + + + + MSVC_SPECTRE_LIBS + + +Build with the spectre-mitigated Visual C++ libraries. + + + +The valid values for MSVC_SPECTRE_LIBS are: True, +False, or None. + + + +When MSVC_SPECTRE_LIBS is enabled (i.e., True), +the Visual C++ environment will include the paths to the spectre-mitigated implementations +of the Microsoft Visual C++ libraries. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier. + + + +MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + + + + +Example - A Visual Studio 2022 build with spectre mitigated Visual C++ libraries: + +env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True) + + + + +Important usage details: + + + +MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + +Additional compiler switches (e.g., /Qspectre) are necessary for including +spectre mitigations when building user artifacts. Refer to the Visual Studio documentation for +details. + + + + +The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS +is enabled which could result in build failures. + +The burden is on the user to ensure the requisite libraries with spectre mitigations are installed. + + + + + + + + + + MSVC_TOOLSET_VERSION + + +Build with a specific Visual C++ toolset version. + + + +Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection +of msvc instances. The MSVC_TOOLSET_VERSION is applied after +an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION; +is not specified. + + + +The valid values for MSVC_TOOLSET_VERSION are: None +or a string containing the requested toolset version (e.g., '14.29'). + + + +MSVC_TOOLSET_VERSION is ignored when the value is None and when +the value is an empty string. The validation conditions below do not apply. + + + +An exception is raised when any of the following conditions are satisfied: + + + +MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier. + + + +MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + + +The MSVC_TOOLSET_VERSION specified does not match any of the supported formats: + + + + + +'XX.Y' + + + +'XX.YY' + + + +'XX.YY.ZZZZZ' + + + +'XX.YY.Z' to 'XX.YY.ZZZZ' + +[&scons; extension not directly supported by the msvc batch files and may be removed in the future] + + + + +'XX.YY.ZZ.N' [SxS format] + + + +'XX.YY.ZZ.NN' [SxS format] + + + + + + + +The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified +is for Visual Studio 2013 and earlier (e.g., '12.0'). + + + +The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified +is greater than the msvc version selected (e.g., '99.0'). + + + +A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found. +The requested toolset version does not appear to be installed. + + + + + + +Toolset selection details: + + + +SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above. + +In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly +mapped to toolset version 14.16.27023. + + + +When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number: +the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION +prefix is selected. + + + +When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix +(i.e., 'XX.Y') and the major msvc version is that of the latest release of +Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version. + +In the latest release of Visual Studio, the default Visual C++ toolset version is not necessarily the +toolset with the largest version number. + + + + + + +Example environment constructor invocations with toolset version specifications: + +Environment(MSVC_TOOLSET_VERSION='14.2') +Environment(MSVC_TOOLSET_VERSION='14.29') +Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133') +Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11') + + + + +Important usage details: + + + +MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +The existence of the toolset host architecture and target architecture folders are not verified +when MSVC_TOOLSET_VERSION is specified which could result in build failures. + +The burden is on the user to ensure the requisite toolset target architecture build tools are installed. + + + + @@ -4879,9 +5356,24 @@ env = Environment(MSVC_VERSION='8.0', MSVC_USE_SETTINGS=msvc_use_settings) -Note: the dictionary content requirements are based on the internal msvc implementation and -therefore may change at any time. The burden is on the user to ensure the dictionary contents -are minimally sufficient to ensure successful builds. +Important usage details: + + + +MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +The dictionary content requirements are based on the internal msvc implementation and +therefore may change at any time. + +The burden is on the user to ensure the dictionary contents are minimally sufficient to +ensure successful builds. + + + @@ -4891,21 +5383,65 @@ are minimally sufficient to ensure successful builds. MSVC_UWP_APP -Build libraries for a Universal Windows Platform (UWP) Application. +Build with the Universal Windows Platform (UWP) application Visual C++ libraries. + + + +The valid values for MSVC_UWP_APP are True, +'1', False, '0', +or None. -If &cv-MSVC_UWP_APP; is set, the Visual C++ environment will be set up to point +When MSVC_UWP_APP is enabled (i.e., True or +'1'), the Visual C++ environment will be set up to point to the Windows Store compatible libraries and Visual C++ runtimes. In doing so, any libraries that are built will be able to be used in a UWP App and published to the Windows Store. -This flag will only have an effect with Visual Studio 2015 or later. -This variable must be passed as an argument to the Environment() -constructor; setting it later has no effect. + + -Valid values are '1' or '0' +An exception is raised when any of the following conditions are satisfied: + + +MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. + + +MSVC_UWP_APP is enabled and a UWP argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP +and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + + + + + +Example - A Visual Studio 2022 build for the Universal Windows Platform: + +env = Environment(MSVC_VERSION='14.3', MSVC_UWP_APP=True) + + + + +Important usage details: + + + +MSVC_UWP_APP must be passed as an argument to the Environment() constructor; +setting it later has no effect. + + + + +The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled +which could result in build failures. + +The burden is on the user to ensure the requisite UWP libraries are installed. + + + diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index c5cd0cdf1e..d3d29f2869 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -321,6 +321,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $MSSDK_VERSION"> $MSVC_BATCH"> $MSVC_NOTFOUND_POLICY"> +$MSVC_SCRIPT_ARGS"> +$MSVC_SDK_VERSION"> +$MSVC_SPECTRE_LIBS"> +$MSVC_TOOLSET_VERSION"> $MSVC_USE_SCRIPT"> $MSVC_USE_SCRIPT_ARGS"> $MSVC_USE_SETTINGS"> @@ -985,6 +989,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $MSSDK_VERSION"> $MSVC_BATCH"> $MSVC_NOTFOUND_POLICY"> +$MSVC_SCRIPT_ARGS"> +$MSVC_SDK_VERSION"> +$MSVC_SPECTRE_LIBS"> +$MSVC_TOOLSET_VERSION"> $MSVC_USE_SCRIPT"> $MSVC_USE_SCRIPT_ARGS"> $MSVC_USE_SETTINGS"> From 623cdba27514874cd1af6e09911153885574113d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 24 Jun 2022 19:21:22 -0400 Subject: [PATCH 036/108] Documentation updates [ci skip] --- SCons/Tool/msvc.xml | 199 +++++++++++++++++++++--------------- doc/generated/variables.gen | 199 +++++++++++++++++++++--------------- 2 files changed, 234 insertions(+), 164 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 6663aabce6..de82db08f0 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -349,8 +349,15 @@ Sets the preferred version of Microsoft Visual C/C++ to use. If &cv-MSVC_VERSION; is not set, SCons will (by default) select the latest version of Visual C/C++ installed on your system. If the specified version isn't installed, tool initialization will fail. -This variable must be passed as an argument to the &f-link-Environment; -constructor; setting it later has no effect. + + + +&cv-MSVC_VERSION; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is +loaded into the environment. @@ -410,7 +417,7 @@ is, if you are sure everything is set correctly already and you don't want &SCons; to change anything. -&cv-MSVC_USE_SCRIPT; overrides &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;. +&cv-MSVC_USE_SCRIPT; ignores &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;. @@ -485,8 +492,12 @@ Important usage details: -MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_USE_SETTINGS; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_USE_SETTINGS; must be set before the first msvc tool is +loaded into the environment. @@ -529,13 +540,13 @@ Build with the Universal Windows Platform (UWP) application Visual C++ libraries -The valid values for MSVC_UWP_APP are True, +The valid values for &cv-MSVC_UWP_APP; are: True, '1', False, '0', or None. -When MSVC_UWP_APP is enabled (i.e., True or +When &cv-MSVC_UWP_APP; is enabled (i.e., True or '1'), the Visual C++ environment will be set up to point to the Windows Store compatible libraries and Visual C++ runtimes. In doing so, any libraries that are built will be able to be used in a UWP App and published @@ -549,11 +560,11 @@ constructor; setting it later has no effect. --> An exception is raised when any of the following conditions are satisfied: -MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. +&cv-MSVC_UWP_APP; is enabled for Visual Studio 2013 and earlier. -MSVC_UWP_APP is enabled and a UWP argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP +&cv-MSVC_UWP_APP; is enabled and a UWP argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via &cv-MSVC_UWP_APP; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. @@ -571,13 +582,17 @@ Important usage details: -MSVC_UWP_APP must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_UWP_APP; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_UWP_APP; must be set before the first msvc tool is +loaded into the environment. -The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled +The existence of the UWP libraries is not verified when &cv-MSVC_UWP_APP; is enabled which could result in build failures. The burden is on the user to ensure the requisite UWP libraries are installed. @@ -645,12 +660,12 @@ Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not det - The MSVC_NOTFOUND_POLICY specifies the &scons; behavior when no msvc versions are detected or + The &cv-MSVC_NOTFOUND_POLICY; specifies the &scons; behavior when no msvc versions are detected or when the requested msvc version is not detected. -The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are: +The valid values for &cv-MSVC_NOTFOUND_POLICY; and the corresponding &scons; behavior are: @@ -691,7 +706,7 @@ Note: in addition to the camel case values shown above, lower case and upper cas -The MSVC_NOTFOUND_POLICY is applied when any of the following conditions are satisfied: +The &cv-MSVC_NOTFOUND_POLICY; is applied when any of the following conditions are satisfied: &cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified), @@ -708,7 +723,7 @@ A non-default tools list is specified that contains one or more of the msvc tool -The MSVC_NOTFOUND_POLICY is ignored when any of the following conditions are satisfied: +The &cv-MSVC_NOTFOUND_POLICY; is ignored when any of the following conditions are satisfied: &cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified). @@ -727,15 +742,19 @@ Important usage details: -MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_NOTFOUND_POLICY; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_NOTFOUND_POLICY; must be set before the first msvc tool is +loaded into the environment. -When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue +When &cv-MSVC_NOTFOUND_POLICY; is not specified, the default &scons; behavior is to issue a warning and continue subject to the conditions listed above. The default &scons; behavior may change in the future. @@ -749,25 +768,25 @@ Pass user-defined arguments to the Visual C++ batch file determined via autodete -MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support +&cv-MSVC_SCRIPT_ARGS; is available for msvc batch file arguments that do not have first-class support via construction variables or when there is an issue with the appropriate construction variable validation. When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;) -rather than MSVC_SCRIPT_ARGS arguments. +rather than &cv-MSVC_SCRIPT_ARGS; arguments. -The valid values for MSVC_SCRIPT_ARGS are: None, a string, +The valid values for &cv-MSVC_SCRIPT_ARGS; are: None, a string, or a list of strings. -The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened"). +The &cv-MSVC_SCRIPT_ARGS; value is converted to a scalar string (i.e., "flattened"). The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined via autodetection subject to the validation conditions listed below. -MSVC_SCRIPT_ARGS is ignored when the value is None and when the +&cv-MSVC_SCRIPT_ARGS; is ignored when the value is None and when the result from argument conversion is an empty string. The validation conditions below do not apply. @@ -776,54 +795,54 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier. +&cv-MSVC_SCRIPT_ARGS; is specified for Visual Studio 2013 and earlier. Multiple SDK version arguments (e.g., '10.0.20348.0') are specified -in MSVC_SCRIPT_ARGS. +in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument -(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS. -Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS +(e.g., '10.0.20348.0') is specified in &cv-MSVC_SCRIPT_ARGS;. +Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and &cv-MSVC_SCRIPT_ARGS; are not allowed. Multiple toolset version arguments (e.g., '-vcvars_ver=14.29') -are specified in MSVC_SCRIPT_ARGS. +are specified in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument -(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS. +(e.g., '-vcvars_ver=14.29') is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and -MSVC_SCRIPT_ARGS are not allowed. +&cv-MSVC_SCRIPT_ARGS; are not allowed. Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre') -are specified in MSVC_SCRIPT_ARGS. +are specified in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument (e.g., '-vcvars_spectre_libs=spectre') is specified in -MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS; -and MSVC_SCRIPT_ARGS are not allowed. +&cv-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS; +and &cv-MSVC_SCRIPT_ARGS; are not allowed. Multiple UWP arguments (e.g., uwp or store) are specified -in MSVC_SCRIPT_ARGS. +in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or -store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations -via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed. +store) is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple UWP declarations +via &cv-link-MSVC_UWP_APP; and &cv-MSVC_SCRIPT_ARGS; are not allowed. @@ -850,18 +869,22 @@ Important usage details: -MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_SCRIPT_ARGS; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SCRIPT_ARGS; must be set before the first msvc tool is +loaded into the environment. -Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments +Other than checking for multiple declarations as described above, &cv-MSVC_SCRIPT_ARGS; arguments are not validated. -Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely +Erroneous, inconsistent, and/or version incompatible &cv-MSVC_SCRIPT_ARGS; arguments are likely to result in build failures for reasons that are not readily apparent and may be difficult to diagnose. The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent @@ -881,12 +904,12 @@ Build with a specific version of the Microsoft Software Development Kit (SDK). -The valid values for MSVC_SDK_VERSION are: None -or a string containing the requested SDK version (e.g., '10.0.20348.0'). +The valid values for &cv-MSVC_SDK_VERSION; are: None +or a string containing the requested SDK version (e.g., '10.0.20348.0'). -MSVC_SDK_VERSION is ignored when the value is None and when +&cv-MSVC_SDK_VERSION; is ignored when the value is None and when the value is an empty string. The validation conditions below do not apply. @@ -895,17 +918,17 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier. +&cv-MSVC_SDK_VERSION; is specified for Visual Studio 2013 and earlier. -MSVC_SDK_VERSION is specified and an SDK version argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION +&cv-MSVC_SDK_VERSION; is specified and an SDK version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via &cv-MSVC_SDK_VERSION; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. -The MSVC_SDK_VERSION specified does not match any of the supported formats: +The &cv-MSVC_SDK_VERSION; specified does not match any of the supported formats: '10.0.XXXXX.Y' [SDK 10.0] @@ -917,12 +940,12 @@ The MSVC_SDK_VERSION specified does not match any of the supporte -The system folder for the corresponding MSVC_SDK_VERSION version is not found. +The system folder for the corresponding &cv-MSVC_SDK_VERSION; version is not found. The requested SDK version does not appear to be installed. -The MSVC_SDK_VERSION version does not appear to support the requested platform +The &cv-MSVC_SDK_VERSION; version does not appear to support the requested platform type (i.e., UWP or Desktop). The requested SDK version platform type components do not appear to be installed. @@ -949,8 +972,12 @@ Important usage details: -MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_SDK_VERSION; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SDK_VERSION; must be set before the first msvc tool is +loaded into the environment. @@ -969,7 +996,7 @@ the requested SDK version is installed with the necessary platform type componen There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0' when using the v141 build tools and the target architecture is ARM64. -Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be +Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be employed to specify a different SDK version for the build. @@ -986,19 +1013,19 @@ Build with a specific Visual C++ toolset version. -Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection -of msvc instances. The MSVC_TOOLSET_VERSION is applied after +Specifying &cv-MSVC_TOOLSET_VERSION; does not affect the autodetection and selection +of msvc instances. The &cv-MSVC_TOOLSET_VERSION; is applied after an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION; is not specified. -The valid values for MSVC_TOOLSET_VERSION are: None -or a string containing the requested toolset version (e.g., '14.29'). +The valid values for &cv-MSVC_TOOLSET_VERSION; are: None +or a string containing the requested toolset version (e.g., '14.29'). -MSVC_TOOLSET_VERSION is ignored when the value is None and when +&cv-MSVC_TOOLSET_VERSION; is ignored when the value is None and when the value is an empty string. The validation conditions below do not apply. @@ -1007,18 +1034,18 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier. +&cv-MSVC_TOOLSET_VERSION; is specified for Visual Studio 2015 and earlier. -MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION +&cv-MSVC_TOOLSET_VERSION; is specified and a toolset version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-MSVC_TOOLSET_VERSION; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. -The MSVC_TOOLSET_VERSION specified does not match any of the supported formats: +The &cv-MSVC_TOOLSET_VERSION; specified does not match any of the supported formats: @@ -1055,17 +1082,17 @@ The MSVC_TOOLSET_VERSION specified does not match any of the supp -The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified -is for Visual Studio 2013 and earlier (e.g., '12.0'). +The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified +is for Visual Studio 2013 and earlier (e.g., '12.0'). -The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified -is greater than the msvc version selected (e.g., '99.0'). +The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified +is greater than the msvc version selected (e.g., '99.0'). -A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found. +A system folder for the corresponding &cv-MSVC_TOOLSET_VERSION; version is not found. The requested toolset version does not appear to be installed. @@ -1084,13 +1111,13 @@ mapped to toolset version 14.16.27023. -When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number: -the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION +When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number: +the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION; prefix is selected. -When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix +When &cv-MSVC_TOOLSET_VERSION; is specified using the major msvc version prefix (i.e., 'XX.Y') and the major msvc version is that of the latest release of Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version. @@ -1116,14 +1143,18 @@ Important usage details: -MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_TOOLSET_VERSION; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_TOOLSET_VERSION; must be set before the first msvc tool is +loaded into the environment. The existence of the toolset host architecture and target architecture folders are not verified -when MSVC_TOOLSET_VERSION is specified which could result in build failures. +when &cv-MSVC_TOOLSET_VERSION; is specified which could result in build failures. The burden is on the user to ensure the requisite toolset target architecture build tools are installed. @@ -1141,12 +1172,12 @@ Build with the spectre-mitigated Visual C++ libraries. -The valid values for MSVC_SPECTRE_LIBS are: True, +The valid values for &cv-MSVC_SPECTRE_LIBS; are: True, False, or None. -When MSVC_SPECTRE_LIBS is enabled (i.e., True), +When &cv-MSVC_SPECTRE_LIBS; is enabled (i.e., True), the Visual C++ environment will include the paths to the spectre-mitigated implementations of the Microsoft Visual C++ libraries. @@ -1156,12 +1187,12 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier. +&cv-MSVC_SPECTRE_LIBS; is enabled for Visual Studio 2015 and earlier. -MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS +&cv-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-MSVC_SPECTRE_LIBS; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. @@ -1180,8 +1211,12 @@ Important usage details: -MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_SPECTRE_LIBS; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SPECTRE_LIBS; must be set before the first msvc tool is +loaded into the environment. @@ -1192,7 +1227,7 @@ details. -The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS +The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures. The burden is on the user to ensure the requisite libraries with spectre mitigations are installed. diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index f1b3fff078..a0c2dc47ef 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -4690,12 +4690,12 @@ Specify the &scons; behavior when the Microsoft Visual C/C++ compiler is not det - The MSVC_NOTFOUND_POLICY specifies the &scons; behavior when no msvc versions are detected or + The &cv-MSVC_NOTFOUND_POLICY; specifies the &scons; behavior when no msvc versions are detected or when the requested msvc version is not detected. -The valid values for MSVC_NOTFOUND_POLICY and the corresponding &scons; behavior are: +The valid values for &cv-MSVC_NOTFOUND_POLICY; and the corresponding &scons; behavior are: @@ -4736,7 +4736,7 @@ Note: in addition to the camel case values shown above, lower case and upper cas -The MSVC_NOTFOUND_POLICY is applied when any of the following conditions are satisfied: +The &cv-MSVC_NOTFOUND_POLICY; is applied when any of the following conditions are satisfied: &cv-MSVC_VERSION; is specified, the default tools list is implicitly defined (i.e., the tools list is not specified), @@ -4753,7 +4753,7 @@ A non-default tools list is specified that contains one or more of the msvc tool -The MSVC_NOTFOUND_POLICY is ignored when any of the following conditions are satisfied: +The &cv-MSVC_NOTFOUND_POLICY; is ignored when any of the following conditions are satisfied: &cv-MSVC_VERSION; is not specified and the default tools list is implicitly defined (i.e., the tools list is not specified). @@ -4772,15 +4772,19 @@ Important usage details: -MSVC_NOTFOUND_POLICY must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_NOTFOUND_POLICY; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_NOTFOUND_POLICY; must be set before the first msvc tool is +loaded into the environment. -When MSVC_NOTFOUND_POLICY is not specified, the default &scons; behavior is to issue a warning and continue +When &cv-MSVC_NOTFOUND_POLICY; is not specified, the default &scons; behavior is to issue a warning and continue subject to the conditions listed above. The default &scons; behavior may change in the future. @@ -4795,25 +4799,25 @@ Pass user-defined arguments to the Visual C++ batch file determined via autodete -MSVC_SCRIPT_ARGS is available for msvc batch file arguments that do not have first-class support +&cv-MSVC_SCRIPT_ARGS; is available for msvc batch file arguments that do not have first-class support via construction variables or when there is an issue with the appropriate construction variable validation. When available, it is recommended to use the appropriate construction variables (e.g., &cv-link-MSVC_TOOLSET_VERSION;) -rather than MSVC_SCRIPT_ARGS arguments. +rather than &cv-MSVC_SCRIPT_ARGS; arguments. -The valid values for MSVC_SCRIPT_ARGS are: None, a string, +The valid values for &cv-MSVC_SCRIPT_ARGS; are: None, a string, or a list of strings. -The MSVC_SCRIPT_ARGS value is converted to a scalar string (i.e., "flattened"). +The &cv-MSVC_SCRIPT_ARGS; value is converted to a scalar string (i.e., "flattened"). The resulting scalar string, if not empty, is passed as an argument to the msvc batch file determined via autodetection subject to the validation conditions listed below. -MSVC_SCRIPT_ARGS is ignored when the value is None and when the +&cv-MSVC_SCRIPT_ARGS; is ignored when the value is None and when the result from argument conversion is an empty string. The validation conditions below do not apply. @@ -4822,54 +4826,54 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_SCRIPT_ARGS is specified for Visual Studio 2013 and earlier. +&cv-MSVC_SCRIPT_ARGS; is specified for Visual Studio 2013 and earlier. Multiple SDK version arguments (e.g., '10.0.20348.0') are specified -in MSVC_SCRIPT_ARGS. +in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_SDK_VERSION; is specified and an SDK version argument -(e.g., '10.0.20348.0') is specified in MSVC_SCRIPT_ARGS. -Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and MSVC_SCRIPT_ARGS +(e.g., '10.0.20348.0') is specified in &cv-MSVC_SCRIPT_ARGS;. +Multiple SDK version declarations via &cv-link-MSVC_SDK_VERSION; and &cv-MSVC_SCRIPT_ARGS; are not allowed. Multiple toolset version arguments (e.g., '-vcvars_ver=14.29') -are specified in MSVC_SCRIPT_ARGS. +are specified in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_TOOLSET_VERSION; is specified and a toolset version argument -(e.g., '-vcvars_ver=14.29') is specified in MSVC_SCRIPT_ARGS. +(e.g., '-vcvars_ver=14.29') is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-link-MSVC_TOOLSET_VERSION; and -MSVC_SCRIPT_ARGS are not allowed. +&cv-MSVC_SCRIPT_ARGS; are not allowed. Multiple spectre library arguments (e.g., '-vcvars_spectre_libs=spectre') -are specified in MSVC_SCRIPT_ARGS. +are specified in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument (e.g., '-vcvars_spectre_libs=spectre') is specified in -MSVC_SCRIPT_ARGS. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS; -and MSVC_SCRIPT_ARGS are not allowed. +&cv-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-link-MSVC_SPECTRE_LIBS; +and &cv-MSVC_SCRIPT_ARGS; are not allowed. Multiple UWP arguments (e.g., uwp or store) are specified -in MSVC_SCRIPT_ARGS. +in &cv-MSVC_SCRIPT_ARGS;. &cv-link-MSVC_UWP_APP; is enabled and a UWP argument (e.g., uwp or -store) is specified in MSVC_SCRIPT_ARGS. Multiple UWP declarations -via &cv-link-MSVC_UWP_APP; and MSVC_SCRIPT_ARGS are not allowed. +store) is specified in &cv-MSVC_SCRIPT_ARGS;. Multiple UWP declarations +via &cv-link-MSVC_UWP_APP; and &cv-MSVC_SCRIPT_ARGS; are not allowed. @@ -4896,18 +4900,22 @@ Important usage details: -MSVC_SCRIPT_ARGS must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_SCRIPT_ARGS; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SCRIPT_ARGS; must be set before the first msvc tool is +loaded into the environment. -Other than checking for multiple declarations as described above, MSVC_SCRIPT_ARGS arguments +Other than checking for multiple declarations as described above, &cv-MSVC_SCRIPT_ARGS; arguments are not validated. -Erroneous, inconsistent, and/or version incompatible MSVC_SCRIPT_ARGS arguments are likely +Erroneous, inconsistent, and/or version incompatible &cv-MSVC_SCRIPT_ARGS; arguments are likely to result in build failures for reasons that are not readily apparent and may be difficult to diagnose. The burden is on the user to ensure that the arguments provided to the msvc batch file are valid, consistent @@ -4928,12 +4936,12 @@ Build with a specific version of the Microsoft Software Development Kit (SDK). -The valid values for MSVC_SDK_VERSION are: None -or a string containing the requested SDK version (e.g., '10.0.20348.0'). +The valid values for &cv-MSVC_SDK_VERSION; are: None +or a string containing the requested SDK version (e.g., '10.0.20348.0'). -MSVC_SDK_VERSION is ignored when the value is None and when +&cv-MSVC_SDK_VERSION; is ignored when the value is None and when the value is an empty string. The validation conditions below do not apply. @@ -4942,17 +4950,17 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_SDK_VERSION is specified for Visual Studio 2013 and earlier. +&cv-MSVC_SDK_VERSION; is specified for Visual Studio 2013 and earlier. -MSVC_SDK_VERSION is specified and an SDK version argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via MSVC_SDK_VERSION +&cv-MSVC_SDK_VERSION; is specified and an SDK version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple SDK version declarations via &cv-MSVC_SDK_VERSION; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. -The MSVC_SDK_VERSION specified does not match any of the supported formats: +The &cv-MSVC_SDK_VERSION; specified does not match any of the supported formats: '10.0.XXXXX.Y' [SDK 10.0] @@ -4964,12 +4972,12 @@ The MSVC_SDK_VERSION specified does not match any of the supporte -The system folder for the corresponding MSVC_SDK_VERSION version is not found. +The system folder for the corresponding &cv-MSVC_SDK_VERSION; version is not found. The requested SDK version does not appear to be installed. -The MSVC_SDK_VERSION version does not appear to support the requested platform +The &cv-MSVC_SDK_VERSION; version does not appear to support the requested platform type (i.e., UWP or Desktop). The requested SDK version platform type components do not appear to be installed. @@ -4996,8 +5004,12 @@ Important usage details: -MSVC_SDK_VERSION must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_SDK_VERSION; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SDK_VERSION; must be set before the first msvc tool is +loaded into the environment. @@ -5016,7 +5028,7 @@ the requested SDK version is installed with the necessary platform type componen There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0' when using the v141 build tools and the target architecture is ARM64. -Should build failures arise with this combination of settings, MSVC_SDK_VERSION may be +Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be employed to specify a different SDK version for the build. @@ -5034,12 +5046,12 @@ Build with the spectre-mitigated Visual C++ libraries. -The valid values for MSVC_SPECTRE_LIBS are: True, +The valid values for &cv-MSVC_SPECTRE_LIBS; are: True, False, or None. -When MSVC_SPECTRE_LIBS is enabled (i.e., True), +When &cv-MSVC_SPECTRE_LIBS; is enabled (i.e., True), the Visual C++ environment will include the paths to the spectre-mitigated implementations of the Microsoft Visual C++ libraries. @@ -5049,12 +5061,12 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_SPECTRE_LIBS is enabled for Visual Studio 2015 and earlier. +&cv-MSVC_SPECTRE_LIBS; is enabled for Visual Studio 2015 and earlier. -MSVC_SPECTRE_LIBS is enabled and a spectre library argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via MSVC_SPECTRE_LIBS +&cv-MSVC_SPECTRE_LIBS; is enabled and a spectre library argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple spectre library declarations via &cv-MSVC_SPECTRE_LIBS; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. @@ -5073,8 +5085,12 @@ Important usage details: -MSVC_SPECTRE_LIBS must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_SPECTRE_LIBS; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SPECTRE_LIBS; must be set before the first msvc tool is +loaded into the environment. @@ -5085,7 +5101,7 @@ details. -The existence of the spectre mitigations libraries is not verified when MSVC_SPECTRE_LIBS +The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures. The burden is on the user to ensure the requisite libraries with spectre mitigations are installed. @@ -5105,19 +5121,19 @@ Build with a specific Visual C++ toolset version. -Specifying MSVC_TOOLSET_VERSION does not affect the autodetection and selection -of msvc instances. The MSVC_TOOLSET_VERSION is applied after +Specifying &cv-MSVC_TOOLSET_VERSION; does not affect the autodetection and selection +of msvc instances. The &cv-MSVC_TOOLSET_VERSION; is applied after an msvc instance is selected. This could be the default version of msvc if &cv-link-MSVC_VERSION; is not specified. -The valid values for MSVC_TOOLSET_VERSION are: None -or a string containing the requested toolset version (e.g., '14.29'). +The valid values for &cv-MSVC_TOOLSET_VERSION; are: None +or a string containing the requested toolset version (e.g., '14.29'). -MSVC_TOOLSET_VERSION is ignored when the value is None and when +&cv-MSVC_TOOLSET_VERSION; is ignored when the value is None and when the value is an empty string. The validation conditions below do not apply. @@ -5126,18 +5142,18 @@ An exception is raised when any of the following conditions are satisfied: -MSVC_TOOLSET_VERSION is specified for Visual Studio 2015 and earlier. +&cv-MSVC_TOOLSET_VERSION; is specified for Visual Studio 2015 and earlier. -MSVC_TOOLSET_VERSION is specified and a toolset version argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via MSVC_TOOLSET_VERSION +&cv-MSVC_TOOLSET_VERSION; is specified and a toolset version argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple toolset version declarations via &cv-MSVC_TOOLSET_VERSION; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. -The MSVC_TOOLSET_VERSION specified does not match any of the supported formats: +The &cv-MSVC_TOOLSET_VERSION; specified does not match any of the supported formats: @@ -5174,17 +5190,17 @@ The MSVC_TOOLSET_VERSION specified does not match any of the supp -The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified -is for Visual Studio 2013 and earlier (e.g., '12.0'). +The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified +is for Visual Studio 2013 and earlier (e.g., '12.0'). -The major msvc version prefix (i.e., 'XX.Y') of the MSVC_TOOLSET_VERSION specified -is greater than the msvc version selected (e.g., '99.0'). +The major msvc version prefix (i.e., 'XX.Y') of the &cv-MSVC_TOOLSET_VERSION; specified +is greater than the msvc version selected (e.g., '99.0'). -A system folder for the corresponding MSVC_TOOLSET_VERSION version is not found. +A system folder for the corresponding &cv-MSVC_TOOLSET_VERSION; version is not found. The requested toolset version does not appear to be installed. @@ -5203,13 +5219,13 @@ mapped to toolset version 14.16.27023. -When MSVC_TOOLSET_VERSION is not an SxS version number or a full toolset version number: -the first toolset version, ranked in descending order, that matches the MSVC_TOOLSET_VERSION +When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number: +the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION; prefix is selected. -When MSVC_TOOLSET_VERSION is specified using the major msvc version prefix +When &cv-MSVC_TOOLSET_VERSION; is specified using the major msvc version prefix (i.e., 'XX.Y') and the major msvc version is that of the latest release of Visual Studio, the selected toolset version may not be the same as the default Visual C++ toolset version. @@ -5235,14 +5251,18 @@ Important usage details: -MSVC_TOOLSET_VERSION must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_TOOLSET_VERSION; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_TOOLSET_VERSION; must be set before the first msvc tool is +loaded into the environment. The existence of the toolset host architecture and target architecture folders are not verified -when MSVC_TOOLSET_VERSION is specified which could result in build failures. +when &cv-MSVC_TOOLSET_VERSION; is specified which could result in build failures. The burden is on the user to ensure the requisite toolset target architecture build tools are installed. @@ -5283,7 +5303,7 @@ is, if you are sure everything is set correctly already and you don't want &SCons; to change anything. -&cv-MSVC_USE_SCRIPT; overrides &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;. +&cv-MSVC_USE_SCRIPT; ignores &cv-link-MSVC_VERSION; and &cv-link-TARGET_ARCH;. @@ -5360,8 +5380,12 @@ Important usage details: -MSVC_USE_SETTINGS must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_USE_SETTINGS; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_USE_SETTINGS; must be set before the first msvc tool is +loaded into the environment. @@ -5387,13 +5411,13 @@ Build with the Universal Windows Platform (UWP) application Visual C++ libraries -The valid values for MSVC_UWP_APP are True, +The valid values for &cv-MSVC_UWP_APP; are: True, '1', False, '0', or None. -When MSVC_UWP_APP is enabled (i.e., True or +When &cv-MSVC_UWP_APP; is enabled (i.e., True or '1'), the Visual C++ environment will be set up to point to the Windows Store compatible libraries and Visual C++ runtimes. In doing so, any libraries that are built will be able to be used in a UWP App and published @@ -5407,11 +5431,11 @@ constructor; setting it later has no effect. --> An exception is raised when any of the following conditions are satisfied: -MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. +&cv-MSVC_UWP_APP; is enabled for Visual Studio 2013 and earlier. -MSVC_UWP_APP is enabled and a UWP argument is specified in -&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via MSVC_UWP_APP +&cv-MSVC_UWP_APP; is enabled and a UWP argument is specified in +&cv-link-MSVC_SCRIPT_ARGS;. Multiple UWP declarations via &cv-MSVC_UWP_APP; and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. @@ -5429,13 +5453,17 @@ Important usage details: -MSVC_UWP_APP must be passed as an argument to the Environment() constructor; -setting it later has no effect. +&cv-MSVC_UWP_APP; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_UWP_APP; must be set before the first msvc tool is +loaded into the environment. -The existence of the UWP libraries is not verified when MSVC_UWP_APP is enabled +The existence of the UWP libraries is not verified when &cv-MSVC_UWP_APP; is enabled which could result in build failures. The burden is on the user to ensure the requisite UWP libraries are installed. @@ -5458,8 +5486,15 @@ Sets the preferred version of Microsoft Visual C/C++ to use. If &cv-MSVC_VERSION; is not set, SCons will (by default) select the latest version of Visual C/C++ installed on your system. If the specified version isn't installed, tool initialization will fail. -This variable must be passed as an argument to the &f-link-Environment; -constructor; setting it later has no effect. + + + +&cv-MSVC_VERSION; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_VERSION; must be set before the first msvc tool is +loaded into the environment. From 607d719e44d58de52f261b95a6c7c8a4dfa5b225 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 25 Jun 2022 19:18:59 -0400 Subject: [PATCH 037/108] Minor documentation update based on stress tests [ci skip] --- SCons/Tool/msvc.xml | 10 ++++++---- doc/generated/variables.gen | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index de82db08f0..ab022524dd 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -994,10 +994,12 @@ the requested SDK version is installed with the necessary platform type componen -There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0' -when using the v141 build tools and the target architecture is ARM64. -Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be -employed to specify a different SDK version for the build. +There is a known issue with the Microsoft libraries when the target architecture is +ARM64 and a Windows 11 SDK (version '10.0.22000.0' and later) is used +with the v141 build tools and older v142 toolsets +(versions '14.28.29333' and earlier). Should build failures arise with these combinations +of settings due to unresolved symbols in the Microsoft libraries, &cv-MSVC_SDK_VERSION; may be employed to +specify a Windows 10 SDK (e.g., '10.0.20348.0') for the build. diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index a0c2dc47ef..c34b70cd11 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -5026,10 +5026,12 @@ the requested SDK version is installed with the necessary platform type componen -There is a known issue with the Microsoft libraries for SDK version '10.0.22000.0' -when using the v141 build tools and the target architecture is ARM64. -Should build failures arise with this combination of settings, &cv-MSVC_SDK_VERSION; may be -employed to specify a different SDK version for the build. +There is a known issue with the Microsoft libraries when the target architecture is +ARM64 and a Windows 11 SDK (version '10.0.22000.0' and later) is used +with the v141 build tools and older v142 toolsets +(versions '14.28.29333' and earlier). Should build failures arise with these combinations +of settings due to unresolved symbols in the Microsoft libraries, &cv-MSVC_SDK_VERSION; may be employed to +specify a Windows 10 SDK (e.g., '10.0.20348.0') for the build. From d98f0faf2c06cda35b3da02a245ad78a32f245a7 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 26 Jun 2022 09:12:39 -0400 Subject: [PATCH 038/108] Update CHANGES.txt and RELEASE.txt --- CHANGES.txt | 23 ++++++++++++++++++++++- RELEASE.txt | 24 +++++++++++++++++++++--- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index bea5838176..c6c673227e 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -31,8 +31,29 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER determination when configuring the build environment. This could lead to build failures when only an MSVC Express instance is installed and the MSVC version is not explicitly specified (issue #2668 and issue #2697). - - Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler + - Added MSVC_USE_SETTINGS construction variable to pass a dictionary to configure the msvc compiler system environment as an alternative to bypassing Visual Studio autodetection entirely. + - Added MSVC_SDK_VERSION construction variable which allows building with a specific Microsoft + SDK version. This variable is used with the msvc batch file determined via autodetection subject + to validation constraints. Refer to the documentation for additional requirements and validation + details. + - Added MSVC_TOOLSET_VERSION construction variable which allows building with a specific toolset + version. This variable is used with the msvc batch file determined via autodetection subject to + validation constraints. This variable does not affect the autodetection and selection of msvc + instances. The toolset version is applied after an msvc instance is selected. This could be the + default version of msvc. Refer to the documentation for additional requirements and validation + details. Addresses issue #3265, issue #3664, and pull request #4149. + - Added MSVC_SPECTRE_LIBS construction variable which allows building with spectre-mitigated + Visual C++ libraries. This variable is used with the msvc batch file determined via autodetection + subject to validation constraints. Refer to the documentation for additional requirements and + validation details. + - Added MSVC_SCRIPT_ARGS construction variable which specifies command line arguments that are + passed to the msvc batch file determined via autodetection subject to validation constraints. + Refer to the documentation for additional requirements and validation details. Addresses + enhancement issue #4106. + - An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. + Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013 + and earlier. Refer to the documentation for additional requirements and validation details. - The imported system environment variable names for MSVC 7.0 and 6.0 have been changed to the names set by their respective installers. Prior to this change, bypassing MSVC detection by specifying the MSVC 7.0 batch file directly would fail due to using an erroneous environment diff --git a/RELEASE.txt b/RELEASE.txt index c86c1d586e..b244fda4ee 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -23,8 +23,23 @@ NEW FUNCTIONALITY variables). This allows the user to customize how (for example) PATH is constructed. Note that these are called for every build command run by SCons. It could have considerable performance impact if not used carefully. -- Added MSVC_USE_SETTINGS variable to pass a dictionary to configure the msvc compiler +- Added MSVC_USE_SETTINGS construction variable to pass a dictionary to configure the msvc compiler system environment as an alternative to bypassing Visual Studio autodetection entirely. +- Added MSVC_SDK_VERSION construction variable which allows building with a specific Microsoft + SDK version. This variable is used with the msvc batch file determined via autodetection. Refer + to the documentation for additional requirements and validation details. +- Added MSVC_TOOLSET_VERSION construction variable which allows building with a specific toolset + version. This variable is used with the msvc batch file determined via autodetection. This + variable does not affect the autodetection and selection of msvc instances. The toolset version + is applied after an msvc instance is selected. This could be the default version of msvc. Refer + to the documentation for additional requirements and validation details. Addresses issue #3265, + issue #3664, and pull request #4149. +- Added MSVC_SPECTRE_LIBS construction variable which allows building with spectre-mitigated + Visual C++ libraries. This variable is used with the msvc batch file determined via autodetection. + Refer to the documentation for additional requirements and validation details. +- Added MSVC_SCRIPT_ARGS construction variable which specifies command line arguments that are + passed to the msvc batch file determined via autodetection. Refer to the documentation for + additional requirements and validation details. Addresses enhancement issue #4106. - Ninja: Added new alias "shutdown-ninja-scons-daemon" to allow ninja to shutdown the daemon. Also added cleanup to test framework to kill ninja scons daemons and clean ip daemon logs. NOTE: Test for this requires python psutil module. It will be skipped if not present. @@ -96,8 +111,8 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY automatically executes ninja. - Add JavaScanner to include JAVACLASSPATH as a dependency when using the Java tool. - The build argument (i.e., x86) is no longer passed to the MSVC 6.0 to 7.1 batch - files. This may improve the effectiveness of the internal msvc cache when using - MSVC detection and when bypassing MSVC detection as the MSVC 6.0 to 7.1 batch files + files. This may improve the effectiveness of the internal msvc cache when using + MSVC detection and when bypassing MSVC detection as the MSVC 6.0 to 7.1 batch files do not expect any arguments. - Propagate the OS and windir environment variables from the system environment to the msvc environment. The OS and windir environment variables are used in the MSVC 6.0 batch file @@ -109,6 +124,9 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY require delayed expansion to be enabled which is currently not supported and is typically not enabled by default on the host system. The batch files may also require environment variables that are not included by default in the msvc environment. +- An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. + Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013 + and earlier. Refer to the documentation for additional requirements and validation details. - Ninja: added option "--skip-ninja-regen" to enable skipping regeneration of the ninja file if scons can determine the ninja file doesnot need to be regenerated, which will also skip restarting the scons daemon. Note this option is could result in incorrect rebuilds From 7a38a4b6547c3776b20f4b1435773a7458a8ebd8 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 26 Jun 2022 10:54:38 -0400 Subject: [PATCH 039/108] Set global lxml etree XSLT maximum traversal depth. Update generated documentation artifacts. --- SCons/Tool/docbook/__init__.py | 12 ++++ doc/generated/functions.gen | 52 +++++++++-------- doc/generated/tools.gen | 17 +++--- doc/generated/variables.gen | 103 ++++++++++++++++++++++----------- doc/generated/variables.mod | 2 + 5 files changed, 117 insertions(+), 69 deletions(-) diff --git a/SCons/Tool/docbook/__init__.py b/SCons/Tool/docbook/__init__.py index 4c3f60cee9..5cf5e61980 100644 --- a/SCons/Tool/docbook/__init__.py +++ b/SCons/Tool/docbook/__init__.py @@ -65,6 +65,18 @@ re_manvolnum = re.compile(r"([^<]*)") re_refname = re.compile(r"([^<]*)") +# +# lxml etree XSLT global max traversal depth +# + +lmxl_xslt_global_max_depth = 3100 + +if has_lxml and lmxl_xslt_global_max_depth: + def __lxml_xslt_set_global_max_depth(max_depth): + from lxml import etree + etree.XSLT.set_global_max_depth(max_depth) + __lxml_xslt_set_global_max_depth(lmxl_xslt_global_max_depth) + # # Helper functions # diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index a2d9acd503..efc4c9edf3 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -1611,45 +1611,49 @@ See the manpage section "Construction Environments" for more details. - Execute(action, [strfunction, varlist]) - env.Execute(action, [strfunction, varlist]) + Execute(action, [actionargs ...]) + env.Execute(action, [actionargs ...]) -Executes an Action object. -The specified +Executes an Action. action may be an Action object -(see manpage section "Action Objects" -for an explanation of behavior), or it may be a command-line string, list of commands, or executable &Python; function, -each of which will be converted +each of which will first be converted into an Action object and then executed. Any additional arguments to &f-Execute; -(strfunction, varlist) are passed on to the &f-link-Action; factory function -which actually creates the Action object. -The exit value of the command -or return value of the &Python; function -will be returned. +which actually creates the Action object +(see the manpage section Action Objects +for a description). Example: + + + +Execute(Copy('file.out', 'file.in')) + + +&f-Execute; performs its action immediately, +as part of the SConscript-reading phase. +There are no sources or targets declared in an +&f-Execute; call, so any objects it manipulates +will not be tracked as part of the &SCons; dependency graph. +In the example above, neither +file.out nor +file.in will be tracked objects. -Note that +&f-Execute; returns the exit value of the command +or return value of the &Python; function. &scons; -will print an error message if the executed +prints an error message if the executed action -fails--that is, -exits with or returns a non-zero value. -&scons; -will +fails (exits with or returns a non-zero value), +however it does not, -however, -automatically terminate the build -if the specified -action -fails. +automatically terminate the build for such a failure. If you want the build to stop in response to a failed &f-Execute; call, @@ -1657,8 +1661,6 @@ you must explicitly check for a non-zero return value: -Execute(Copy('file.out', 'file.in')) - if Execute("mkdir sub/dir/ectory"): # The mkdir failed, don't try to build. Exit(1) diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index ecc301a083..35ded17ceb 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -389,35 +389,35 @@ Sets construction variables for the dvips utility. Set construction variables for generic POSIX Fortran 03 compilers. -Sets: &cv-link-F03;, &cv-link-F03COM;, &cv-link-F03FLAGS;, &cv-link-F03PPCOM;, &cv-link-SHF03;, &cv-link-SHF03COM;, &cv-link-SHF03FLAGS;, &cv-link-SHF03PPCOM;, &cv-link-_F03INCFLAGS;.Uses: &cv-link-F03COMSTR;, &cv-link-F03PPCOMSTR;, &cv-link-SHF03COMSTR;, &cv-link-SHF03PPCOMSTR;. +Sets: &cv-link-F03;, &cv-link-F03COM;, &cv-link-F03FLAGS;, &cv-link-F03PPCOM;, &cv-link-SHF03;, &cv-link-SHF03COM;, &cv-link-SHF03FLAGS;, &cv-link-SHF03PPCOM;, &cv-link-_F03INCFLAGS;.Uses: &cv-link-F03COMSTR;, &cv-link-F03PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF03COMSTR;, &cv-link-SHF03PPCOMSTR;. f08 Set construction variables for generic POSIX Fortran 08 compilers. -Sets: &cv-link-F08;, &cv-link-F08COM;, &cv-link-F08FLAGS;, &cv-link-F08PPCOM;, &cv-link-SHF08;, &cv-link-SHF08COM;, &cv-link-SHF08FLAGS;, &cv-link-SHF08PPCOM;, &cv-link-_F08INCFLAGS;.Uses: &cv-link-F08COMSTR;, &cv-link-F08PPCOMSTR;, &cv-link-SHF08COMSTR;, &cv-link-SHF08PPCOMSTR;. +Sets: &cv-link-F08;, &cv-link-F08COM;, &cv-link-F08FLAGS;, &cv-link-F08PPCOM;, &cv-link-SHF08;, &cv-link-SHF08COM;, &cv-link-SHF08FLAGS;, &cv-link-SHF08PPCOM;, &cv-link-_F08INCFLAGS;.Uses: &cv-link-F08COMSTR;, &cv-link-F08PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF08COMSTR;, &cv-link-SHF08PPCOMSTR;. f77 Set construction variables for generic POSIX Fortran 77 compilers. -Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77FLAGS;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;, &cv-link-_F77INCFLAGS;.Uses: &cv-link-F77COMSTR;, &cv-link-F77PPCOMSTR;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHF77COMSTR;, &cv-link-SHF77PPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANPPCOMSTR;. +Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77FLAGS;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANFLAGS;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;, &cv-link-_F77INCFLAGS;.Uses: &cv-link-F77COMSTR;, &cv-link-F77PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-FORTRANCOMSTR;, &cv-link-FORTRANFLAGS;, &cv-link-FORTRANPPCOMSTR;, &cv-link-SHF77COMSTR;, &cv-link-SHF77PPCOMSTR;, &cv-link-SHFORTRANCOMSTR;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOMSTR;. f90 Set construction variables for generic POSIX Fortran 90 compilers. -Sets: &cv-link-F90;, &cv-link-F90COM;, &cv-link-F90FLAGS;, &cv-link-F90PPCOM;, &cv-link-SHF90;, &cv-link-SHF90COM;, &cv-link-SHF90FLAGS;, &cv-link-SHF90PPCOM;, &cv-link-_F90INCFLAGS;.Uses: &cv-link-F90COMSTR;, &cv-link-F90PPCOMSTR;, &cv-link-SHF90COMSTR;, &cv-link-SHF90PPCOMSTR;. +Sets: &cv-link-F90;, &cv-link-F90COM;, &cv-link-F90FLAGS;, &cv-link-F90PPCOM;, &cv-link-SHF90;, &cv-link-SHF90COM;, &cv-link-SHF90FLAGS;, &cv-link-SHF90PPCOM;, &cv-link-_F90INCFLAGS;.Uses: &cv-link-F90COMSTR;, &cv-link-F90PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF90COMSTR;, &cv-link-SHF90PPCOMSTR;. f95 Set construction variables for generic POSIX Fortran 95 compilers. -Sets: &cv-link-F95;, &cv-link-F95COM;, &cv-link-F95FLAGS;, &cv-link-F95PPCOM;, &cv-link-SHF95;, &cv-link-SHF95COM;, &cv-link-SHF95FLAGS;, &cv-link-SHF95PPCOM;, &cv-link-_F95INCFLAGS;.Uses: &cv-link-F95COMSTR;, &cv-link-F95PPCOMSTR;, &cv-link-SHF95COMSTR;, &cv-link-SHF95PPCOMSTR;. +Sets: &cv-link-F95;, &cv-link-F95COM;, &cv-link-F95FLAGS;, &cv-link-F95PPCOM;, &cv-link-SHF95;, &cv-link-SHF95COM;, &cv-link-SHF95FLAGS;, &cv-link-SHF95PPCOM;, &cv-link-_F95INCFLAGS;.Uses: &cv-link-F95COMSTR;, &cv-link-F95PPCOMSTR;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-SHF95COMSTR;, &cv-link-SHF95PPCOMSTR;. fortran @@ -437,10 +437,8 @@ Set construction variables for the &gXX; C++ compiler. g77 Set construction variables for the &g77; Fortran compiler. -Calls the &t-f77; Tool module -to set variables. - +Sets: &cv-link-F77;, &cv-link-F77COM;, &cv-link-F77FILESUFFIXES;, &cv-link-F77PPCOM;, &cv-link-F77PPFILESUFFIXES;, &cv-link-FORTRAN;, &cv-link-FORTRANCOM;, &cv-link-FORTRANPPCOM;, &cv-link-SHF77;, &cv-link-SHF77COM;, &cv-link-SHF77FLAGS;, &cv-link-SHF77PPCOM;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANCOM;, &cv-link-SHFORTRANFLAGS;, &cv-link-SHFORTRANPPCOM;.Uses: &cv-link-F77FLAGS;, &cv-link-FORTRANCOMMONFLAGS;, &cv-link-FORTRANFLAGS;. gas @@ -516,7 +514,8 @@ environment: gfortran -Sets construction variables for the GNU F95/F2003 GNU compiler. +Sets construction variables for the GNU Fortran compiler. +Calls the &t-link-fortran; Tool module to set variables. Sets: &cv-link-F77;, &cv-link-F90;, &cv-link-F95;, &cv-link-FORTRAN;, &cv-link-SHF77;, &cv-link-SHF77FLAGS;, &cv-link-SHF90;, &cv-link-SHF90FLAGS;, &cv-link-SHF95;, &cv-link-SHF95FLAGS;, &cv-link-SHFORTRAN;, &cv-link-SHFORTRANFLAGS;. diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index c34b70cd11..32db05eb2c 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -2680,6 +2680,17 @@ in the &cv-link-FORTRANFLAGS;, &cv-link-_FORTRANINCFLAGS; &consvars; are included on this command line. + + + + + FORTRANCOMMONFLAGS + + +General user-specified options that are passed to the Fortran compiler. +Similar to &cv-link-FORTRANFLAGS;, +but this variable is applied to all dialects. + @@ -2709,7 +2720,8 @@ default, this is ['.f', '.for', '.ftn'] FORTRANFLAGS -General user-specified options that are passed to the Fortran compiler. +General user-specified options for the FORTRAN dialect +that are passed to the Fortran compiler. Note that this variable does not contain @@ -6578,44 +6590,65 @@ A Python function used to print the command lines as they are executed or options or their equivalents). -The function should take four arguments: +The function must accept four arguments: s, -the command being executed (a string), target, -the target being built (file node, list, or string name(s)), +source and +env. +s +is a string showing the command being executed, +target, +is the target being built (file node, list, or string name(s)), source, -the source(s) used (file node, list, or string name(s)), and -env, -the environment being used. +is the source(s) used (file node, list, or string name(s)), +and env +is the environment being used. -The function must do the printing itself. The default implementation, -used if this variable is not set or is None, is: +The function must do the printing itself. +The default implementation, +used if this variable is not set or is None, +is to just print the string, as in: def print_cmd_line(s, target, source, env): - sys.stdout.write(s + "\n") + sys.stdout.write(s + "\n") -Here's an example of a more interesting function: +Here is an example of a more interesting function: def print_cmd_line(s, target, source, env): - sys.stdout.write("Building %s -> %s...\n" % - (' and '.join([str(x) for x in source]), - ' and '.join([str(x) for x in target]))) -env=Environment(PRINT_CMD_LINE_FUNC=print_cmd_line) -env.Program('foo', 'foo.c') + sys.stdout.write( + "Building %s -> %s...\n" + % ( + ' and '.join([str(x) for x in source]), + ' and '.join([str(x) for x in target]), + ) + ) + +env = Environment(PRINT_CMD_LINE_FUNC=print_cmd_line) +env.Program('foo', ['foo.c', 'bar.c']) -This just prints "Building targetname from sourcename..." instead -of the actual commands. -Such a function could also log the actual commands to a log file, -for example. +This prints: + + + +... +scons: Building targets ... +Building bar.c -> bar.o... +Building foo.c -> foo.o... +Building foo.o and bar.o -> foo... +scons: done building targets. + + + +Another example could be a function that logs the actual commands to a file. @@ -7587,9 +7620,9 @@ targets. def custom_shell_env(env, target, source, shell_env): """customize shell_env if desired""" if str(target[0]) == 'special_target': - shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source) + shell_env['SPECIAL_VAR'] = env.subst('SOME_VAR', target=target, source=source) return shell_env - + env["SHELL_ENV_GENERATORS"] = [custom_shell_env] @@ -7663,7 +7696,7 @@ Options that are passed to the Fortran 03 compiler to generated shared-library objects. You only need to set &cv-link-SHF03FLAGS; if you need to define specific user options for Fortran 03 files. -You should normally set the &cv-link-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -7751,7 +7784,7 @@ Options that are passed to the Fortran 08 compiler to generated shared-library objects. You only need to set &cv-link-SHF08FLAGS; if you need to define specific user options for Fortran 08 files. -You should normally set the &cv-link-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -7839,7 +7872,7 @@ Options that are passed to the Fortran 77 compiler to generated shared-library objects. You only need to set &cv-link-SHF77FLAGS; if you need to define specific user options for Fortran 77 files. -You should normally set the &cv-link-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -7927,7 +7960,7 @@ Options that are passed to the Fortran 90 compiler to generated shared-library objects. You only need to set &cv-link-SHF90FLAGS; if you need to define specific user options for Fortran 90 files. -You should normally set the &cv-link-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -8015,7 +8048,7 @@ Options that are passed to the Fortran 95 compiler to generated shared-library objects. You only need to set &cv-link-SHF95FLAGS; if you need to define specific user options for Fortran 95 files. -You should normally set the &cv-link-SHFORTRANFLAGS; variable, +You should normally set the &cv-link-FORTRANCOMMONFLAGS; variable, which specifies the user-specified options passed to the default Fortran compiler for all Fortran versions. @@ -8389,7 +8422,7 @@ which would be a symlink and point to libtest.so.0.1.2 A command interpreter function that will be called to execute command line -strings. The function must expect the following arguments: +strings. The function must accept five arguments: @@ -8397,18 +8430,18 @@ def spawn(shell, escape, cmd, args, env): -sh -is a string naming the shell program to use. +shell +is a string naming the shell program to use, escape is a function that can be called to escape shell special characters in -the command line. +the command line, cmd -is the path to the command to be executed. +is the path to the command to be executed, args -is the arguments to the command. +holds the arguments to the command and env -is a dictionary of the environment variables -in which the command should be executed. +is a dictionary of environment variables +defining the execution environment in which the command should be executed. diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index d3d29f2869..cc51043c12 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -186,6 +186,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $File"> $FORTRAN"> $FORTRANCOM"> +$FORTRANCOMMONFLAGS"> $FORTRANCOMSTR"> $FORTRANFILESUFFIXES"> $FORTRANFLAGS"> @@ -854,6 +855,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $File"> $FORTRAN"> $FORTRANCOM"> +$FORTRANCOMMONFLAGS"> $FORTRANCOMSTR"> $FORTRANFILESUFFIXES"> $FORTRANFLAGS"> From 20a092ffda8d2492a5745b5aa93c48c14d0d4e76 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 26 Jun 2022 12:26:12 -0400 Subject: [PATCH 040/108] Add blurb for additional MSVC_UWP_APP values accepted. --- CHANGES.txt | 1 + RELEASE.txt | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index c6c673227e..381b061578 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013 and earlier. Refer to the documentation for additional requirements and validation details. + MSVC_UWP_APP was extended to accept True, False, and None in addition to '1' and '0'. - The imported system environment variable names for MSVC 7.0 and 6.0 have been changed to the names set by their respective installers. Prior to this change, bypassing MSVC detection by specifying the MSVC 7.0 batch file directly would fail due to using an erroneous environment diff --git a/RELEASE.txt b/RELEASE.txt index b244fda4ee..12dec62adb 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -127,6 +127,7 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - An exception is raised when MSVC_UWP_APP is enabled for Visual Studio 2013 and earlier. Previous behavior was to silently ignore MSVC_UWP_APP when enabled for Visual Studio 2013 and earlier. Refer to the documentation for additional requirements and validation details. + MSVC_UWP_APP was extended to accept True, False, and None in addition to '1' and '0'. - Ninja: added option "--skip-ninja-regen" to enable skipping regeneration of the ninja file if scons can determine the ninja file doesnot need to be regenerated, which will also skip restarting the scons daemon. Note this option is could result in incorrect rebuilds From 2e2b4640b09a94b0aa2b2f90581295e28ba83650 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 27 Jun 2022 10:40:55 -0400 Subject: [PATCH 041/108] Rework SxS toolset version support and vcvars bug fix handling. Update MSVC_TOOLSET_VERSION documentation. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 81 ++++++++++++++++----- SCons/Tool/msvc.xml | 39 ++++++---- doc/generated/variables.gen | 39 ++++++---- 3 files changed, 113 insertions(+), 46 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index e56dd4a120..6a4ce3e5ec 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -86,9 +86,22 @@ def _verify_re_sdk_dispatch_map(): (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000 )$''', re.VERBOSE) -# valid SxS formats will be matched with re_toolset_full: match 3 '.' format +# SxS toolset version: MM.mm.VV.vv format re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') +# SxS version bugfix +_msvc_sxs_bugfix_folder = set() +_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_folder.add((msvc_version, sxs_bugfix)) + _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) @@ -327,14 +340,42 @@ def _msvc_read_toolset_file(msvc, filename): debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) return toolset_version +def _msvc_sxs_toolset_folder(msvc, sxs_folder): + + if re_toolset_sxs.match(sxs_folder): + return sxs_folder + + key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder) + if key in _msvc_sxs_bugfix_folder: + return sxs_folder + + debug('sxs folder: ignore version=%s', repr(sxs_folder)) + return None + +def _msvc_sxs_toolset_version(msvc, sxs_toolset): + + if not re_toolset_sxs.match(sxs_toolset): + return None, False + + key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_toolset) + sxs_bugfix = _msvc_sxs_bugfix_version.get(key) + if not sxs_bugfix: + return sxs_toolset, False + + debug('sxs bugfix: version=%s => version=%s', repr(sxs_toolset), repr(sxs_bugfix)) + return sxs_bugfix, True + def _msvc_read_toolset_folders(msvc, vc_dir): toolsets_sxs = {} toolsets_full = [] build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - sxs_toolsets = [f.name for f in os.scandir(build_dir) if f.is_dir()] - for sxs_toolset in sxs_toolsets: + sxs_folders = [f.name for f in os.scandir(build_dir) if f.is_dir()] + for sxs_folder in sxs_folders: + sxs_toolset = _msvc_sxs_toolset_folder(msvc, sxs_folder) + if not sxs_toolset: + continue filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) filepath = os.path.join(build_dir, sxs_toolset, filename) debug('sxs toolset: check file=%s', repr(filepath)) @@ -428,23 +469,27 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) - if msvc.vs_def.vc_buildtools_def.vc_version_numeric == VS2019.vc_buildtools_def.vc_version_numeric: - # necessary to detect toolset not found - if toolset_version == '14.28.16.8': - new_toolset_version = '14.28' - # 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. - debug( - 'rewrite toolset_version=%s => toolset_version=%s', - repr(toolset_version), repr(new_toolset_version) - ) - toolset_version = new_toolset_version - - if toolset_version in toolsets_sxs: - toolset_vcvars = toolsets_sxs[toolset_version] + if toolset_version in toolsets_full: + # full toolset version provided + toolset_vcvars = toolset_version return toolset_vcvars + sxs_toolset, sxs_isbugfix = _msvc_sxs_toolset_version(msvc, toolset_version) + if sxs_toolset: + # SxS version provided + sxs_version = toolsets_sxs.get(sxs_toolset, None) + if sxs_version: + # SxS full toolset version + if sxs_version in toolsets_full: + toolset_vcvars = sxs_version + return toolset_vcvars + return None + # SxS version file missing + if not sxs_isbugfix: + return None + # SxS version bugfix: check toolset version + toolset_version = sxs_toolset + for toolset_full in toolsets_full: if toolset_full.startswith(toolset_version): toolset_vcvars = toolset_full diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index ab022524dd..11c6478f1a 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -1105,13 +1105,6 @@ The requested toolset version does not appear to be installed. Toolset selection details: - -SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above. - -In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly -mapped to toolset version 14.16.27023. - - When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number: the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION; @@ -1131,13 +1124,31 @@ toolset with the largest version number. -Example environment constructor invocations with toolset version specifications: - -Environment(MSVC_TOOLSET_VERSION='14.2') -Environment(MSVC_TOOLSET_VERSION='14.29') -Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133') -Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11') - +Example 1 - A default Visual Studio build with a partial toolset version specified: + +env = Environment(MSVC_TOOLSET_VERSION='14.2') + + + + +Example 2 - A default Visual Studio build with a partial toolset version specified: + +env = Environment(MSVC_TOOLSET_VERSION='14.29') + + + + +Example 3 - A Visual Studio 2022 build with a full toolset version specified: + +env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133') + + + + +Example 4 - A Visual Studio 2022 build with an SxS toolset version specified: + +env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11') + diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 32db05eb2c..d5980ff30d 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -5225,13 +5225,6 @@ The requested toolset version does not appear to be installed. Toolset selection details: - -SxS version numbers are not always in three dot format (e.g., 'XX.YY.ZZ.NN') as shown above. - -In Visual Studio 2022 for example, 14.16 is an SxS toolset version that is directly -mapped to toolset version 14.16.27023. - - When &cv-MSVC_TOOLSET_VERSION; is not an SxS version number or a full toolset version number: the first toolset version, ranked in descending order, that matches the &cv-MSVC_TOOLSET_VERSION; @@ -5251,13 +5244,31 @@ toolset with the largest version number. -Example environment constructor invocations with toolset version specifications: - -Environment(MSVC_TOOLSET_VERSION='14.2') -Environment(MSVC_TOOLSET_VERSION='14.29') -Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133') -Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11') - +Example 1 - A default Visual Studio build with a partial toolset version specified: + +env = Environment(MSVC_TOOLSET_VERSION='14.2') + + + + +Example 2 - A default Visual Studio build with a partial toolset version specified: + +env = Environment(MSVC_TOOLSET_VERSION='14.29') + + + + +Example 3 - A Visual Studio 2022 build with a full toolset version specified: + +env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.30133') + + + + +Example 4 - A Visual Studio 2022 build with an SxS toolset version specified: + +env = Environment(MSVC_VERSION='14.3', MSVC_TOOLSET_VERSION='14.29.16.11') + From b94e801aba5a6864644a156b5260ce6d721f4fc3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 27 Jun 2022 14:36:02 -0400 Subject: [PATCH 042/108] Move verify invocation to last line of vc.py. Add verify method to Config module to check that all _VCVER versions are defined locally. Update module docstrings for Dispatcher and MSVC init. --- SCons/Tool/MSCommon/MSVC/Config.py | 15 +++++++++++++++ SCons/Tool/MSCommon/MSVC/Dispatcher.py | 25 ++++++++++++++++++++++--- SCons/Tool/MSCommon/MSVC/__init__.py | 14 ++++++++++++-- SCons/Tool/MSCommon/vc.py | 3 +++ 4 files changed, 52 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 8f3a2cc839..a4cb87463a 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -29,6 +29,12 @@ namedtuple, ) +from . import Util + +from .Exceptions import ( + MSVCInternalError, +) + from . import Dispatcher Dispatcher.register_modulename(__name__) @@ -282,3 +288,12 @@ MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def +def verify(): + from .. import vc + for msvc_version in vc._VCVER: + vc_version = Util.get_version_prefix(msvc_version) + if vc_version in MSVC_VERSION_INTERNAL: + continue + err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version)) + raise MSVCInternalError(err_msg) + diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py index 0b216caee3..dab1e15719 100644 --- a/SCons/Tool/MSCommon/MSVC/Dispatcher.py +++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py @@ -23,6 +23,25 @@ """ Internal method dispatcher for Microsoft Visual C/C++. + +MSVC modules can register their module (register_modulename) and individual +classes (register_class) with the method dispatcher during initialization. MSVC +modules tend to be registered immediately after the Dispatcher import near the +top of the file. Methods in the MSVC modules can be invoked indirectly without +having to hard-code the method calls effectively decoupling the upstream module +with the downstream modules: + +The reset method dispatches calls to all registered objects with a reset method +and/or a _reset method. The reset methods are used to restore data structures +to their initial state for testing purposes. Typically, this involves clearing +cached values. + +The verify method dispatches calls to all registered objects with a verify +method and/or a _verify method. The verify methods are used to check that +initialized data structures distributed across multiple modules are internally +consistent. An exception is raised when a verification constraint violation +is detected. Typically, this verifies that initialized dictionaries support +all of the requisite keys as new versions are added. """ import sys @@ -34,13 +53,13 @@ _refs = [] -def register_class(ref): - _refs.append(ref) - def register_modulename(modname): module = sys.modules[modname] _refs.append(module) +def register_class(ref): + _refs.append(ref) + def reset(): debug('') for ref in _refs: diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index c07e849492..b0ef5dd512 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -23,6 +23,16 @@ """ Functions for Microsoft Visual C/C++. + +The reset method is used to restore MSVC module data structures to their +initial state for testing purposes. + +The verify method is used as a sanity check that MSVC module data structures +are internally consistent. + +Currently: +* reset is invoked from reset_installed_vcs in the vc module. +* verify is invoked from the last line in the vc module. """ from . import Exceptions # noqa: F401 @@ -40,6 +50,6 @@ def reset(): _Dispatcher.reset() -#reset() # testing -_Dispatcher.verify() +def verify(): + _Dispatcher.verify() diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 675b8d0a9b..7c6587938b 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1352,3 +1352,6 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) return rval +# internal consistency check (should be last) +MSVC.verify() + From 90922c1195eef8d75664d59ed8668fd8e5679390 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 27 Jun 2022 15:09:13 -0400 Subject: [PATCH 043/108] Add docstrings to MSVC/Util.py methods. --- SCons/Tool/MSCommon/MSVC/Util.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 15abdcdb1d..ba70b249eb 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -29,6 +29,16 @@ import re def listdir_dirs(p): + """Get a list of qualified subdirectory paths from a windows path. + + Args: + p: str + windows path + + Returns: + List[str]: list of qualified subdirectory paths + + """ dirs = [] for dir_name in os.listdir(p): dir_path = os.path.join(p, dir_name) @@ -37,6 +47,16 @@ def listdir_dirs(p): return dirs def process_path(p): + """Normalize a windows path. + + Args: + p: str + windows path + + Returns: + str: normalized windows path + + """ if p: p = os.path.normpath(p) p = os.path.realpath(p) @@ -46,6 +66,16 @@ def process_path(p): re_version_prefix = re.compile(r'^(?P[0-9.]+).*') def get_version_prefix(version): + """Get the version number prefix from a string. + + Args: + version: str + version string + + Returns: + str: the version number prefix + + """ m = re_version_prefix.match(version) if m: rval = m.group('version') From d5a2a52087c968058fff3f2cc983f75953235e1a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 28 Jun 2022 17:16:49 -0400 Subject: [PATCH 044/108] Additional validation for MSVC_SDK_VERSION and MSVC_SPECTRE_LIBS. Adjust documentation. Add additional exceptions for SDK version not found, toolset version not found, and spectre libraries not found. Add data structure for platform type. --- SCons/Tool/MSCommon/MSVC/Config.py | 27 ++ SCons/Tool/MSCommon/MSVC/Exceptions.py | 11 +- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 262 +++++++++++++++----- SCons/Tool/MSCommon/MSVC/Util.py | 20 ++ SCons/Tool/MSCommon/MSVC/WinSDK.py | 39 +-- SCons/Tool/msvc.xml | 17 +- doc/generated/variables.gen | 17 +- 7 files changed, 314 insertions(+), 79 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index a4cb87463a..3b71cd8f53 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -55,6 +55,33 @@ for symbol in BOOLEAN_SYMBOLS[bool]: BOOLEAN_EXTERNAL[symbol] = bool +MSVC_PLATFORM_DEFINITION = namedtuple('MSVCPlatform', [ + 'vc_platform', + 'is_uwp', +]) + +MSVC_PLATFORM_DEFINITION_LIST = [] + +MSVC_PLATFORM_INTERNAL = {} +MSVC_PLATFORM_EXTERNAL = {} + +for vc_platform, is_uwp in [ + ('Desktop', False), + ('UWP', True), +]: + + vc_platform_def = MSVC_PLATFORM_DEFINITION( + vc_platform = vc_platform, + is_uwp = is_uwp, + ) + + MSVC_PLATFORM_DEFINITION_LIST.append(vc_platform_def) + + MSVC_PLATFORM_INTERNAL[vc_platform] = vc_platform_def + + for symbol in [vc_platform, vc_platform.lower(), vc_platform.upper()]: + MSVC_PLATFORM_EXTERNAL[symbol] = vc_platform_def + MSVC_RUNTIME_DEFINITION = namedtuple('MSVCRuntime', [ 'vc_runtime', 'vc_runtime_numeric', diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py index 7a61ec532a..015fede50e 100644 --- a/SCons/Tool/MSCommon/MSVC/Exceptions.py +++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py @@ -28,10 +28,19 @@ class VisualCException(Exception): pass +class MSVCInternalError(VisualCException): + pass + class MSVCVersionNotFound(VisualCException): pass -class MSVCInternalError(VisualCException): +class MSVCSDKVersionNotFound(VisualCException): + pass + +class MSVCToolsetVersionNotFound(VisualCException): + pass + +class MSVCSpectreLibsNotFound(VisualCException): pass class MSVCArgumentError(VisualCException): diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 6a4ce3e5ec..33e13fd344 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -44,6 +44,9 @@ from .Exceptions import ( MSVCInternalError, + MSVCSDKVersionNotFound, + MSVCToolsetVersionNotFound, + MSVCSpectreLibsNotFound, MSVCArgumentError, ) @@ -131,6 +134,18 @@ def msvc_force_default_arguments(force=True): 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 @@ -159,7 +174,7 @@ class SortOrder(enum.IntEnum): VS2015 = Config.MSVS_VERSION_INTERNAL['2015'] MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [ - 'version', # fully qualified msvc version (e.g., '14.1Exp') + 'version', # full version (e.g., '14.1Exp', '14.32.31326') 'vs_def', ]) @@ -175,6 +190,18 @@ def _msvc_version(version): return version_args +def _toolset_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 _msvc_script_argument_uwp(env, msvc, arglist): uwp_app = env['MSVC_UWP_APP'] @@ -252,12 +279,38 @@ def _msvc_script_argument_sdk_constraints(msvc, sdk_version): err_msg = "MSVC_SDK_VERSION ({}) is not supported".format(repr(sdk_version)) return err_msg -def _msvc_script_argument_sdk(env, msvc, platform_type, arglist): +def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, platform_def): + + if sdk_version == '8.1' and platform_def.is_uwp: + + vs_def = toolset.vs_def if toolset else msvc.vs_def + + if vs_def.vc_buildtools_def.vc_version_numeric > VS2015.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015', + repr(vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2015.vc_buildtools_def.vc_version_numeric) + ) + if toolset and toolset.vs_def != msvc.vs_def: + err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset version {} > {} VS2015".format( + repr(sdk_version), repr(platform_def.vc_platform), + repr(toolset.version), repr(VS2015.vc_buildtools_def.vc_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.vc_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_type) + repr(msvc.version), repr(sdk_version), repr(platform_def.vc_platform) ) if not sdk_version: @@ -267,12 +320,16 @@ def _msvc_script_argument_sdk(env, msvc, platform_type, arglist): if err_msg: raise MSVCArgumentError(err_msg) - sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) + sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, 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_type) + 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) @@ -280,12 +337,12 @@ def _msvc_script_argument_sdk(env, msvc, platform_type, arglist): return sdk_version -def _msvc_script_default_sdk(env, msvc, platform_type, arglist): +def _msvc_script_default_sdk(env, msvc, platform_def, arglist): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: return None - sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_type) + sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_def) if not len(sdk_list): return None @@ -293,7 +350,7 @@ def _msvc_script_default_sdk(env, msvc, platform_type, arglist): debug( 'MSVC_VERSION=%s, sdk_default=%s, platform_type=%s', - repr(msvc.version), repr(sdk_default), repr(platform_type) + repr(msvc.version), repr(sdk_default), repr(platform_def.vc_platform) ) argpair = (SortOrder.SDK, sdk_default) @@ -597,8 +654,9 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist): err_msg = "MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}".format( repr(toolset_version), repr(msvc.version) ) - raise MSVCArgumentError(err_msg) + raise MSVCToolsetVersionNotFound(err_msg) + # toolset may not be installed for host/target argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_vcvars)) arglist.append(argpair) @@ -644,16 +702,7 @@ def _user_script_argument_toolset(env, toolset_version, user_argstr): raise MSVCArgumentError(err_msg) -def _msvc_script_argument_spectre(env, msvc, 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 +def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platform_def): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: debug( @@ -664,11 +713,63 @@ def _msvc_script_argument_spectre(env, msvc, arglist): err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format( repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version) ) + return err_msg + + if toolset: + if toolset.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: + debug( + 'invalid: toolset version constraint: %s < %s VS2017', + repr(toolset.vs_def.vc_buildtools_def.vc_version_numeric), + repr(VS2017.vc_buildtools_def.vc_version_numeric) + ) + err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format( + repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.vc_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_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 = os.path.join(vc_dir, "Tools", "MSVC", toolset.version, "lib", "spectre") + 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 + # spectre libs may not be installed for host/target argpair = (SortOrder.SPECTRE, '-vcvars_spectre_libs={}'.format(spectre_arg)) arglist.append(argpair) @@ -723,66 +824,111 @@ def _msvc_script_argument_user(env, msvc, arglist): return script_args +def _msvc_process_construction_variables(env): + + 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) != None: + return True + + return False + def msvc_script_arguments(env, version, vc_dir, arg): arglist = [] - msvc = _msvc_version(version) - if arg: argpair = (SortOrder.ARCH, arg) arglist.append(argpair) + 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_UWP_APP' in env: - uwp = _msvc_script_argument_uwp(env, msvc, arglist) - else: - uwp = None + if _msvc_process_construction_variables(env): - if user_argstr: - _user_script_argument_uwp(env, uwp, user_argstr) + # MSVC_UWP_APP - platform_type = 'uwp' if uwp else 'desktop' + if 'MSVC_UWP_APP' in env: + uwp = _msvc_script_argument_uwp(env, msvc, arglist) + else: + uwp = None - if 'MSVC_SDK_VERSION' in env: - sdk_version = _msvc_script_argument_sdk(env, msvc, platform_type, arglist) - else: - sdk_version = None + if user_argstr: + _user_script_argument_uwp(env, uwp, user_argstr) - if user_argstr: - user_sdk = _user_script_argument_sdk(env, sdk_version, user_argstr) - else: - user_sdk = None + is_uwp = True if uwp else False + platform_def = WinSDK.get_msvc_platform(is_uwp) - if _MSVC_FORCE_DEFAULT_SDK: - if not sdk_version and not user_sdk: - sdk_version = _msvc_script_default_sdk(env, msvc, platform_type, arglist) + # 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 '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 user_argstr: + user_toolset = _user_script_argument_toolset(env, toolset_version, user_argstr) + else: + user_toolset = None - if _MSVC_FORCE_DEFAULT_TOOLSET: if not toolset_version and not user_toolset: - toolset_version = _msvc_script_default_toolset(env, msvc, vc_dir, arglist) - - if 'MSVC_SPECTRE_LIBS' in env: - spectre = _msvc_script_argument_spectre(env, msvc, arglist) - else: - spectre = None - - if user_argstr: - _user_script_argument_spectre(env, spectre, user_argstr) + default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist) + else: + default_toolset = None + + if _MSVC_FORCE_DEFAULT_TOOLSET: + if default_toolset: + toolset_version = default_toolset + + if user_toolset: + toolset = None + elif toolset_version: + toolset = _toolset_version(toolset_version) + 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_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 arglist: arglist.sort() diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index ba70b249eb..ed87f9dbf4 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -83,3 +83,23 @@ def get_version_prefix(version): rval = '' return rval +re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*') + +def get_msvc_version_prefix(version): + """Get the msvc version number prefix from a string. + + Args: + version: str + version string + + Returns: + str: the msvc version number prefix + + """ + m = re_msvc_version_prefix.match(version) + if m: + rval = m.group('version') + else: + rval = '' + return rval + diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index 8338c2798e..42526c2b72 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -43,10 +43,13 @@ Dispatcher.register_modulename(__name__) +_DESKTOP = Config.MSVC_PLATFORM_INTERNAL['Desktop'] +_UWP = Config.MSVC_PLATFORM_INTERNAL['UWP'] + def _new_sdk_map(): sdk_map = { - 'desktop': [], - 'uwp': [], + _DESKTOP.vc_platform: [], + _UWP.vc_platform: [], } return sdk_map @@ -84,20 +87,20 @@ def _sdk_10_layout(version): if not os.path.exists(sdk_inc_path): continue - for platform_type, sdk_inc_file in [ - ('desktop', 'winsdkver.h'), - ('uwp', 'windows.h'), + for vc_platform, sdk_inc_file in [ + (_DESKTOP.vc_platform, 'winsdkver.h'), + (_UWP.vc_platform, 'windows.h'), ]: if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): continue - key = (version_nbr, platform_type) + key = (version_nbr, vc_platform) if key in sdk_version_platform_seen: continue sdk_version_platform_seen.add(key) - sdk_map[platform_type].append(version_nbr) + sdk_map[vc_platform].append(version_nbr) for key, val in sdk_map.items(): val.sort(reverse=True) @@ -128,20 +131,20 @@ def _sdk_81_layout(version): if not os.path.exists(sdk_inc_path): continue - for platform_type, sdk_inc_file in [ - ('desktop', 'winsdkver.h'), - ('uwp', 'windows.h'), + for vc_platform, sdk_inc_file in [ + (_DESKTOP.vc_platform, 'winsdkver.h'), + (_UWP.vc_platform, 'windows.h'), ]: if not os.path.exists(os.path.join(sdk_inc_path, sdk_inc_file)): continue - key = (version_nbr, platform_type) + key = (version_nbr, vc_platform) if key in sdk_version_platform_seen: continue sdk_version_platform_seen.add(key) - sdk_map[platform_type].append(version_nbr) + sdk_map[vc_platform].append(version_nbr) for key, val in sdk_map.items(): val.sort(reverse=True) @@ -218,9 +221,13 @@ def _sdk_map(version_list): _sdk_cache[key] = sdk_map return sdk_map -def get_sdk_version_list(version_list, platform_type): +def get_msvc_platform(is_uwp=False): + platform_def = _UWP if is_uwp else _DESKTOP + return platform_def + +def get_sdk_version_list(version_list, platform_def): sdk_map = _sdk_map(version_list) - sdk_list = sdk_map.get(platform_type, []) + sdk_list = sdk_map.get(platform_def.vc_platform, []) return sdk_list def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False): @@ -235,8 +242,8 @@ def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False): return sdk_versions is_uwp = True if msvc_uwp_app in Config.BOOLEAN_SYMBOLS[True] else False - platform_type = 'uwp' if is_uwp else 'desktop' - sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_type) + platform_def = get_msvc_platform(is_uwp) + sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_def) sdk_versions.extend(sdk_list) debug('sdk_versions=%s', repr(sdk_versions)) diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 11c6478f1a..9297100f24 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -950,6 +950,13 @@ type (i.e., UWP or Desktop). The requeste platform type components do not appear to be installed. + +The &cv-MSVC_SDK_VERSION; version is 8.1, the platform type is +UWP, and the build tools selected are from Visual Studio 2017 +and later (i.e., &cv-link-MSVC_VERSION; must be '14.0' or &cv-link-MSVC_TOOLSET_VERSION; +must be '14.0'). + + @@ -1209,6 +1216,12 @@ An exception is raised when any of the following conditions are satisfied: and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + +&cv-MSVC_SPECTRE_LIBS; is enabled and the platform type is UWP. There +are no spectre-mitigated libraries for Universal Windows Platform (UWP) applications or +components. + + @@ -1240,8 +1253,8 @@ details. -The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS; -is enabled which could result in build failures. +The existence of the spectre libraries host architecture and target architecture folders are not +verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures. The burden is on the user to ensure the requisite libraries with spectre mitigations are installed. diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index d5980ff30d..80d5b180b8 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -4994,6 +4994,13 @@ type (i.e., UWP or Desktop). The requeste platform type components do not appear to be installed. + +The &cv-MSVC_SDK_VERSION; version is 8.1, the platform type is +UWP, and the build tools selected are from Visual Studio 2017 +and later (i.e., &cv-link-MSVC_VERSION; must be '14.0' or &cv-link-MSVC_TOOLSET_VERSION; +must be '14.0'). + + @@ -5084,6 +5091,12 @@ An exception is raised when any of the following conditions are satisfied: and &cv-link-MSVC_SCRIPT_ARGS; are not allowed. + +&cv-MSVC_SPECTRE_LIBS; is enabled and the platform type is UWP. There +are no spectre-mitigated libraries for Universal Windows Platform (UWP) applications or +components. + + @@ -5115,8 +5128,8 @@ details. -The existence of the spectre mitigations libraries is not verified when &cv-MSVC_SPECTRE_LIBS; -is enabled which could result in build failures. +The existence of the spectre libraries host architecture and target architecture folders are not +verified when &cv-MSVC_SPECTRE_LIBS; is enabled which could result in build failures. The burden is on the user to ensure the requisite libraries with spectre mitigations are installed. From f500d2e6d12d50feec93517c077194adc9a31306 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 28 Jun 2022 17:26:56 -0400 Subject: [PATCH 045/108] Fix sider issue. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 33e13fd344..556cae8d4d 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -839,7 +839,7 @@ def _msvc_process_construction_variables(env): 'MSVC_SDK_VERSION', 'MSVC_SPECTRE_LIBS', ]: - if env.get(env_variable, None) != None: + if env.get(env_variable, None) is not None: return True return False From 8dd2c436317301067f2637829572c36499e24318 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 29 Jun 2022 06:58:30 -0400 Subject: [PATCH 046/108] Use msvc version prefix instead of version prefix for internal dictionary lookup. --- SCons/Tool/MSCommon/MSVC/Config.py | 2 +- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 2 +- SCons/Tool/MSCommon/MSVC/WinSDK.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 3b71cd8f53..fa7dbc14ff 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -318,7 +318,7 @@ def verify(): from .. import vc for msvc_version in vc._VCVER: - vc_version = Util.get_version_prefix(msvc_version) + vc_version = Util.get_msvc_version_prefix(msvc_version) if vc_version in MSVC_VERSION_INTERNAL: continue err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version)) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 556cae8d4d..56a467693d 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -180,7 +180,7 @@ class SortOrder(enum.IntEnum): def _msvc_version(version): - verstr = Util.get_version_prefix(version) + verstr = Util.get_msvc_version_prefix(version) vs_def = Config.MSVC_VERSION_INTERNAL[verstr] version_args = MSVC_VERSION_ARGS_DEFINITION( diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index 42526c2b72..b17f8508c0 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -235,7 +235,7 @@ def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False): sdk_versions = [] - verstr = Util.get_version_prefix(msvc_version) + verstr = Util.get_msvc_version_prefix(msvc_version) vs_def = Config.MSVC_VERSION_EXTERNAL.get(verstr, None) if not vs_def: debug('vs_def is not defined') From a63c1cfdeffbcf7e9214702df4f93e5f015ead10 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 29 Jun 2022 15:20:44 -0400 Subject: [PATCH 047/108] Add 14.0 toolset registry check as done in msvc vsvars140.bat. --- SCons/Tool/MSCommon/MSVC/Registry.py | 3 +++ SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 19 ++++++++++++++++++- SCons/Tool/MSCommon/vc.py | 1 - 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py index 492f3d0b54..9ffa01e681 100644 --- a/SCons/Tool/MSCommon/MSVC/Registry.py +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -107,3 +107,6 @@ def windows_kit_query_paths(version): q = windows_kits(version) return microsoft_query_paths(q) +def vstudio_sxs_vc7(version): + return '\\'.join([r'VisualStudio\SxS\VC7', version]) + diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 56a467693d..4db478a180 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -40,6 +40,7 @@ from . import Util from . import Config +from . import Registry from . import WinSDK from .Exceptions import ( @@ -491,13 +492,16 @@ def _msvc_read_toolset_default(msvc, vc_dir): _toolset_version_cache = {} _toolset_default_cache = {} +_toolset_have140_cache = None def _reset_toolset_cache(): global _toolset_version_cache global _toolset_default_cache + global _toolset_have140_cache debug('reset: toolset cache') _toolset_version_cache = {} _toolset_default_cache = {} + _toolset_have140_cache = None def _msvc_version_toolsets(msvc, vc_dir): @@ -519,10 +523,23 @@ def _msvc_default_toolset(msvc, vc_dir): return toolset_default +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 _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): if toolset_version == '14.0': - return toolset_version + if _msvc_have140_toolset(): + return toolset_version + return None toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7c6587938b..502a71fd0b 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1300,7 +1300,6 @@ def msvc_setup_env_user(env=None): # Intent is to use msvc tools: # MSVC_VERSION: defined and evaluates True # MSVS_VERSION: defined and evaluates True - # MSVC_TOOLSET_VERSION: defined and evaluates True # MSVC_USE_SCRIPT: defined and (is string or evaluates False) # MSVC_USE_SETTINGS: defined and is not None From fa7f870b7768fae625396c0a0dfface959fee65f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 29 Jun 2022 20:44:23 -0400 Subject: [PATCH 048/108] Fix Util.py docstring for listdir_dirs. Minor update to reading sxs toolsets and toolset folders. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 64 ++++++++++----------- SCons/Tool/MSCommon/MSVC/Util.py | 26 +++++---- 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 4db478a180..14c8c6c7cf 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -398,16 +398,16 @@ def _msvc_read_toolset_file(msvc, filename): debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) return toolset_version -def _msvc_sxs_toolset_folder(msvc, sxs_folder): +def _msvc_sxs_toolset_folder(msvc, sxs_version): - if re_toolset_sxs.match(sxs_folder): - return sxs_folder + if re_toolset_sxs.match(sxs_version): + return sxs_version - key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder) + key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_version) if key in _msvc_sxs_bugfix_folder: - return sxs_folder + return sxs_version - debug('sxs folder: ignore version=%s', repr(sxs_folder)) + debug('sxs folder: ignore version=%s', repr(sxs_version)) return None def _msvc_sxs_toolset_version(msvc, sxs_toolset): @@ -429,35 +429,35 @@ def _msvc_read_toolset_folders(msvc, vc_dir): toolsets_full = [] build_dir = os.path.join(vc_dir, "Auxiliary", "Build") - sxs_folders = [f.name for f in os.scandir(build_dir) if f.is_dir()] - for sxs_folder in sxs_folders: - sxs_toolset = _msvc_sxs_toolset_folder(msvc, sxs_folder) - if not sxs_toolset: - continue - filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_toolset) - filepath = os.path.join(build_dir, sxs_toolset, 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: + if os.path.exists(build_dir): + for sxs_version, sxs_path in Util.listdir_dirs(build_dir): + sxs_version = _msvc_sxs_toolset_folder(msvc, sxs_version) + if not sxs_version: continue - toolsets_sxs[sxs_toolset] = toolset_version - debug( - 'sxs toolset: msvc_version=%s, sxs_version=%s, toolset_version=%s', - repr(msvc.version), repr(sxs_toolset), toolset_version - ) + filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_version) + 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") - toolsets = [f.name for f in os.scandir(toolset_dir) if f.is_dir()] - for toolset_version in toolsets: - binpath = os.path.join(toolset_dir, toolset_version, "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) - ) + 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) + ) toolsets_full.sort(reverse=True) debug('msvc_version=%s, toolsets=%s', repr(msvc.version), repr(toolsets_full)) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index ed87f9dbf4..f1983ba3e7 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -29,14 +29,17 @@ import re def listdir_dirs(p): - """Get a list of qualified subdirectory paths from a windows path. + """ + Return a list of tuples for each subdirectory of the given directory path. + Each tuple is comprised of the subdirectory name and the qualified subdirectory path. + Assumes the given directory path exists and is a directory. Args: p: str - windows path + directory path Returns: - List[str]: list of qualified subdirectory paths + list[tuple[str,str]]: a list of tuples """ dirs = [] @@ -47,14 +50,15 @@ def listdir_dirs(p): return dirs def process_path(p): - """Normalize a windows path. + """ + Normalize a system path Args: p: str - windows path + system path Returns: - str: normalized windows path + str: normalized system path """ if p: @@ -66,11 +70,12 @@ def process_path(p): re_version_prefix = re.compile(r'^(?P[0-9.]+).*') def get_version_prefix(version): - """Get the version number prefix from a string. + """ + Get the version number prefix from a string. Args: version: str - version string + version specification Returns: str: the version number prefix @@ -86,11 +91,12 @@ def get_version_prefix(version): re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*') def get_msvc_version_prefix(version): - """Get the msvc version number prefix from a string. + """ + Get the msvc version number prefix from a string. Args: version: str - version string + version specification Returns: str: the msvc version number prefix From 033e876212f949069f2110b04a7552e02cd4ae56 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 1 Jul 2022 12:59:50 -0400 Subject: [PATCH 049/108] Fix: make MSCommon logger to be independent of root logger. --- SCons/Tool/MSCommon/common.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index da8fd55603..aa01abcf0c 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -85,11 +85,11 @@ def filter(self, record): log_handler = logging.StreamHandler(sys.stdout) else: log_handler = logging.FileHandler(filename=LOGFILE) - logging.basicConfig( - format=log_format, - handlers=[log_handler], - level=logging.DEBUG) + log_formatter = logging.Formatter(log_format) + log_handler.setFormatter(log_formatter) logger = logging.getLogger(name=__name__) + logger.setLevel(level=logging.DEBUG) + logger.addHandler(log_handler) logger.addFilter(_Debug_Filter()) debug = logger.debug else: From 8cf60974f5b08e80004b843189747f5d0d226d42 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 4 Jul 2022 23:10:30 -0400 Subject: [PATCH 050/108] Add msvc script error global policy and construction variable. Move msvc not found policy and msvc script error policy to Policy.py. Rework vcvars bugfix handling for SxS toolset 14.28. Add method to return msvc toolsets. Add experimental function to return msvc version and msvc toolset version given a version specification (proxy for selection). Add API.py to manage symbols imported in vc.py. Update documentation. --- CHANGES.txt | 30 +- RELEASE.txt | 30 +- SCons/Tool/MSCommon/MSVC/API.py | 38 +++ SCons/Tool/MSCommon/MSVC/Config.py | 98 ++++--- SCons/Tool/MSCommon/MSVC/Exceptions.py | 14 + SCons/Tool/MSCommon/MSVC/NotFound.py | 133 --------- SCons/Tool/MSCommon/MSVC/Policy.py | 301 ++++++++++++++++++++ SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 173 ++++++----- SCons/Tool/MSCommon/MSVC/Util.py | 46 ++- SCons/Tool/MSCommon/MSVC/Warnings.py | 44 +++ SCons/Tool/MSCommon/MSVC/__init__.py | 16 +- SCons/Tool/MSCommon/vc.py | 224 +++++++++++++-- SCons/Tool/msvc.xml | 125 ++++++++ doc/generated/variables.gen | 126 ++++++++ doc/generated/variables.mod | 2 + 15 files changed, 1112 insertions(+), 288 deletions(-) create mode 100644 SCons/Tool/MSCommon/MSVC/API.py delete mode 100644 SCons/Tool/MSCommon/MSVC/NotFound.py create mode 100644 SCons/Tool/MSCommon/MSVC/Policy.py create mode 100644 SCons/Tool/MSCommon/MSVC/Warnings.py diff --git a/CHANGES.txt b/CHANGES.txt index 381b061578..a4cf13aeff 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -76,19 +76,39 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER present, there is no easy way to detect during msvc initialization if the default environment will be used later to build a program and/or library. There is no error/warning issued for the default tools as there are legitimate SCons uses that do not require a c compiler. - - Added a global policy setting and an environment policy variable for specifying the action to - be taken when an msvc request cannot be satisfied. The available options are "error", + - Added a global policy setting and an environment construction variable for specifying the + action to be taken when an msvc request cannot be satisfied. The available options are "error", "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be set and retrieved via the functions set_msvc_notfound_policy and get_msvc_notfound_policy, - respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment - policy variable introduced is MSVC_NOTFOUND_POLICY. When defined, the environment policy + respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy - is "warning" or "warn", a VisualCMissingWarning warning is issued and the constructed + is "warning" or "warn", an MSVCVersionNotFoundWarning warning is issued and the constructed environment is likely incomplete. When the active policy is "ignore" or "suppress", no action is taken and the constructed environment is likely incomplete. As implemented, the default global policy is "warning". The ability to set the global policy via an SCons command-line option may be added in a future enhancement. + - Added a global policy setting and an environment construction variable for specifying the + action to be taken when msvc script errors are detected. The available options are "error", + "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be + set and retrieved via the functions set_msvc_scripterror_policy and get_msvc_scripterror_policy, + respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + construction variable is MSVC_SCRIPTERROR_POLICY. When defined, the environment construction + variable overrides the global policy setting for a given environment. When the active policy + is "error" or "exception", an MSVCScriptExecutionError exception is raised when msvc batch file + errors are detected. When the active policy is "warning" or "warn", an MSVCScriptExecutionWarning + warning is issued when msvc batch file errors are detected. When the active policy is "ignore" or + "suppress", msvc batch error messages are suppressed. As implemented, the default global policy + is "ignore". The ability to set the global policy via an SCons command-line option may be added + in a future enhancement. + - Experimental: added function find_msvc_version_toolset to SCons.Tool.MSCommon.vc. Given a version + specification, this function will return a msvc version and a toolset version. The toolset version + may be None. The msvc version and toolset version can be used in the environment construction + variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The version specification may be an + msvc version or an msvc toolset version. This is an experimental proxy for using a toolset version + to perform instance selection. This function may be removed when toolset version is taken into + account during msvc instance selection. From William Deegan: - Fix check for unsupported Python version. It was broken. Also now the error message diff --git a/RELEASE.txt b/RELEASE.txt index 12dec62adb..d7a2690adc 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -45,19 +45,39 @@ NEW FUNCTIONALITY NOTE: Test for this requires python psutil module. It will be skipped if not present. - Ninja: Added command line variable NINJA_CMD_ARGS that allows to pass through ninja command line args. This can also be set in your Environment(). -- Added a global policy setting and an environment policy variable for specifying the action to - be taken when an msvc request cannot be satisfied. The available options are "error", +- Added a global policy setting and an environment construction variable for specifying the + action to be taken when an msvc request cannot be satisfied. The available options are "error", "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be set and retrieved via the functions set_msvc_notfound_policy and get_msvc_notfound_policy, - respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment - policy variable introduced is MSVC_NOTFOUND_POLICY. When defined, the environment policy + respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy - is "warning" or "warn", a VisualCMissingWarning warning is issued and the constructed + is "warning" or "warn", an MSVCVersionNotFoundWarning warning is issued and the constructed environment is likely incomplete. When the active policy is "ignore" or "suppress", no action is taken and the constructed environment is likely incomplete. As implemented, the default global policy is "warning". The ability to set the global policy via an SCons command-line option may be added in a future enhancement. +- Added a global policy setting and an environment construction variable for specifying the + action to be taken when msvc script errors are detected. The available options are "error", + "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be + set and retrieved via the functions set_msvc_scripterror_policy and get_msvc_scripterror_policy, + respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + construction variable is MSVC_SCRIPTERROR_POLICY. When defined, the environment construction + variable overrides the global policy setting for a given environment. When the active policy + is "error" or "exception", an MSVCScriptExecutionError exception is raised when msvc batch file + errors are detected. When the active policy is "warning" or "warn", an MSVCScriptExecutionWarning + warning is issued when msvc batch file errors are detected. When the active policy is "ignore" or + "suppress", msvc batch error messages are suppressed. As implemented, the default global policy + is "ignore". The ability to set the global policy via an SCons command-line option may be added + in a future enhancement. +- Experimental: added function find_msvc_version_toolset to SCons.Tool.MSCommon.vc. Given a version + specification, this function will return a msvc version and a toolset version. The toolset version + may be None. The msvc version and toolset version can be used in the environment construction + variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The version specification may be an + msvc version or an msvc toolset version. This is an experimental proxy for using a toolset version + to perform instance selection. This function may be removed when toolset version is taken into + account during msvc instance selection. - Fortran: a new construction variable FORTRANCOMMONFLAGS is added which is applied to all Fortran dialects, to enable global (all-dialect) settings. diff --git a/SCons/Tool/MSCommon/MSVC/API.py b/SCons/Tool/MSCommon/MSVC/API.py new file mode 100644 index 0000000000..cfa4195e4b --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/API.py @@ -0,0 +1,38 @@ +# 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. + +""" +Public API for Microsoft Visual C/C++. +""" + +from .Exceptions import * # noqa: F401 +from .Warnings import * # noqa: F401 + +from .Policy import set_msvc_notfound_policy # noqa: F401 +from .Policy import get_msvc_notfound_policy # noqa: F401 + +from .Policy import set_msvc_scripterror_policy # noqa: F401 +from .Policy import get_msvc_scripterror_policy # noqa: F401 + +from .Util import get_version_elements # noqa: F401 + diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index fa7dbc14ff..60e2910312 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -128,6 +128,7 @@ 'cl_version', 'cl_version_numeric', 'vc_runtime_def', + 'vc_istoolset', ]) MSVC_BUILDTOOLS_DEFINITION_LIST = [] @@ -137,19 +138,19 @@ VC_VERSION_MAP = {} -for vc_buildtools, vc_version, cl_version, vc_runtime in [ - ('v143', '14.3', '19.3', '140'), - ('v142', '14.2', '19.2', '140'), - ('v141', '14.1', '19.1', '140'), - ('v140', '14.0', '19.0', '140'), - ('v120', '12.0', '18.0', '120'), - ('v110', '11.0', '17.0', '110'), - ('v100', '10.0', '16.0', '100'), - ('v90', '9.0', '15.0', '90'), - ('v80', '8.0', '14.0', '80'), - ('v71', '7.1', '13.1', '71'), - ('v70', '7.0', '13.0', '70'), - ('v60', '6.0', '12.0', '60'), +for vc_buildtools, vc_version, cl_version, vc_runtime, vc_istoolset in [ + ('v143', '14.3', '19.3', '140', True), + ('v142', '14.2', '19.2', '140', True), + ('v141', '14.1', '19.1', '140', True), + ('v140', '14.0', '19.0', '140', True), + ('v120', '12.0', '18.0', '120', False), + ('v110', '11.0', '17.0', '110', False), + ('v100', '10.0', '16.0', '100', False), + ('v90', '9.0', '15.0', '90', False), + ('v80', '8.0', '14.0', '80', False), + ('v71', '7.1', '13.1', '71', False), + ('v70', '7.0', '13.0', '70', False), + ('v60', '6.0', '12.0', '60', False), ]: vc_runtime_def = MSVC_RUNTIME_INTERNAL[vc_runtime] @@ -162,6 +163,7 @@ cl_version = cl_version, cl_version_numeric = float(cl_version), vc_runtime_def = vc_runtime_def, + vc_istoolset = vc_istoolset, ) MSVC_BUILDTOOLS_DEFINITION_LIST.append(vc_buildtools_def) @@ -270,6 +272,43 @@ MSVC_SDK_VERSIONS.update(vc_sdk) +# EXPERIMENTAL: msvc version/toolset search lists +# +# VS2017 example: +# +# defaults['14.1'] = ['14.1', '14.1Exp'] +# defaults['14.1Exp'] = ['14.1Exp'] +# +# search['14.1'] = ['14.3', '14.2', '14.1', '14.1Exp'] +# search['14.1Exp'] = ['14.1Exp'] + +MSVC_VERSION_TOOLSET_DEFAULTS_MAP = {} +MSVC_VERSION_TOOLSET_SEARCH_MAP = {} + +# Pass 1: Build defaults lists and setup express versions +for vs_def in VISUALSTUDIO_DEFINITION_LIST: + if not vs_def.vc_buildtools_def.vc_istoolset: + continue + version_key = vs_def.vc_buildtools_def.vc_version + MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key] = [version_key] + MSVC_VERSION_TOOLSET_SEARCH_MAP[version_key] = [] + if vs_def.vs_express: + express_key = version_key + 'Exp' + MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key].append(express_key) + MSVC_VERSION_TOOLSET_DEFAULTS_MAP[express_key] = [express_key] + MSVC_VERSION_TOOLSET_SEARCH_MAP[express_key] = [express_key] + +# Pass 2: Extend search lists (decreasing version order) +for vs_def in VISUALSTUDIO_DEFINITION_LIST: + if not vs_def.vc_buildtools_def.vc_istoolset: + continue + version_key = vs_def.vc_buildtools_def.vc_version + for vc_buildtools in vs_def.vc_buildtools_all: + toolset_buildtools_def = MSVC_BUILDTOOLS_INTERNAL[vc_buildtools] + toolset_vs_def = MSVC_VERSION_INTERNAL[toolset_buildtools_def.vc_version] + buildtools_key = toolset_buildtools_def.vc_version + MSVC_VERSION_TOOLSET_SEARCH_MAP[buildtools_key].extend(MSVC_VERSION_TOOLSET_DEFAULTS_MAP[version_key]) + # convert string version set to string version list ranked in descending order MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)] @@ -282,39 +321,6 @@ MSVS_VERSION_LEGACY[key] = vs_def MSVC_VERSION_LEGACY[key] = vs_def -# MSVC_NOTFOUND_POLICY definition: -# error: raise exception -# warning: issue warning and continue -# ignore: continue - -MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [ - 'value', - 'symbol', -]) - -MSVC_NOTFOUND_DEFINITION_LIST = [] - -MSVC_NOTFOUND_POLICY_INTERNAL = {} -MSVC_NOTFOUND_POLICY_EXTERNAL = {} - -for policy_value, policy_symbol_list in [ - (True, ['Error', 'Exception']), - (False, ['Warning', 'Warn']), - (None, ['Ignore', 'Suppress']), -]: - - policy_symbol = policy_symbol_list[0].lower() - policy_def = MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol) - - MSVC_NOTFOUND_DEFINITION_LIST.append(vs_def) - - MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def - - for policy_symbol in policy_symbol_list: - MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def - MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def - MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def - def verify(): from .. import vc for msvc_version in vc._VCVER: diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py index 015fede50e..6a311d49aa 100644 --- a/SCons/Tool/MSCommon/MSVC/Exceptions.py +++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py @@ -25,12 +25,26 @@ Exceptions for Microsoft Visual C/C++. """ +__all__ = [ + 'VisualCException', + 'MSVCInternalError', + 'MSVCScriptExecutionError', + 'MSVCVersionNotFound', + 'MSVCSDKVersionNotFound', + 'MSVCToolsetVersionNotFound', + 'MSVCSpectreLibsNotFound', + 'MSVCArgumentError', +] + class VisualCException(Exception): pass class MSVCInternalError(VisualCException): pass +class MSVCScriptExecutionError(VisualCException): + pass + class MSVCVersionNotFound(VisualCException): pass diff --git a/SCons/Tool/MSCommon/MSVC/NotFound.py b/SCons/Tool/MSCommon/MSVC/NotFound.py deleted file mode 100644 index 6ade285226..0000000000 --- a/SCons/Tool/MSCommon/MSVC/NotFound.py +++ /dev/null @@ -1,133 +0,0 @@ -# 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. - -""" -Microsoft Visual C/C++ not found policy. - -Notes: - * As implemented, the default is that a warning is issued. This can - be changed globally via the function set_msvc_notfound_policy and/or - through the environment via the MSVC_NOTFOUND_POLICY variable. -""" - -import SCons.Warnings - -from ..common import ( - debug, -) - -from . import Config - -from .Exceptions import ( - MSVCVersionNotFound, -) - -from . import Dispatcher -Dispatcher.register_modulename(__name__) - - -_MSVC_NOTFOUND_POLICY_DEF = Config.MSVC_NOTFOUND_POLICY_INTERNAL['warning'] - -def _msvc_notfound_policy_lookup(symbol): - - try: - notfound_policy_def = Config.MSVC_NOTFOUND_POLICY_EXTERNAL[symbol] - except KeyError: - err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \ - " Valid values are: {}".format( - repr(symbol), - ', '.join([repr(s) for s in Config.MSVC_NOTFOUND_POLICY_EXTERNAL.keys()]) - ) - raise ValueError(err_msg) - - return notfound_policy_def - -def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None): - """ Set the default policy when MSVC is not found. - - Args: - MSVC_NOTFOUND_POLICY: - string representing the policy behavior - when MSVC is not found or None - - Returns: - The previous policy is returned when the MSVC_NOTFOUND_POLICY argument - is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY - argument is None. - - """ - global _MSVC_NOTFOUND_POLICY_DEF - - prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol - - policy = MSVC_NOTFOUND_POLICY - if policy is not None: - _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy) - - debug( - 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', - repr(prev_policy), repr(policy), - repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) - ) - - return prev_policy - -def get_msvc_notfound_policy(): - """Return the active policy when MSVC is not found.""" - debug( - 'policy.symbol=%s, policy.value=%s', - repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) - ) - return _MSVC_NOTFOUND_POLICY_DEF.symbol - -def policy_handler(env, msg): - - if env and 'MSVC_NOTFOUND_POLICY' in env: - # environment setting - notfound_policy_src = 'environment' - policy = env['MSVC_NOTFOUND_POLICY'] - if policy is not None: - # user policy request - notfound_policy_def = _msvc_notfound_policy_lookup(policy) - else: - # active global setting - notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF - else: - # active global setting - notfound_policy_src = 'default' - policy = None - notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF - - debug( - 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', - notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value) - ) - - if notfound_policy_def.value is None: - # ignore - pass - elif notfound_policy_def.value: - raise MSVCVersionNotFound(msg) - else: - SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) - diff --git a/SCons/Tool/MSCommon/MSVC/Policy.py b/SCons/Tool/MSCommon/MSVC/Policy.py new file mode 100644 index 0000000000..870c73c77f --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Policy.py @@ -0,0 +1,301 @@ +# 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. + +""" +Microsoft Visual C/C++ policy handlers. + +Notes: + * The default msvc not found policy is that a warning is issued. This can be + changed globally via the function set_msvc_notfound_policy and/or through + the environment via the MSVC_NOTFOUND_POLICY construction variable. + * The default msvc script error policy is to suppress all msvc batch file + error messages. This can be changed globally via the function + set_msvc_scripterror_policy and/or through the environment via the + MSVC_SCRIPTERROR_POLICY construction variable. +""" + +from collections import ( + namedtuple, +) + +import SCons.Warnings + +from ..common import ( + debug, +) + +from .Exceptions import ( + MSVCVersionNotFound, + MSVCScriptExecutionError, +) + +from .Warnings import ( + MSVCVersionNotFoundWarning, + MSVCScriptExecutionWarning, +) + +from . import Dispatcher +Dispatcher.register_modulename(__name__) + + +# MSVC_NOTFOUND_POLICY definition: +# error: raise exception +# warning: issue warning and continue +# ignore: continue + +MSVC_NOTFOUND_POLICY_DEFINITION = namedtuple('MSVCNotFoundPolicyDefinition', [ + 'value', + 'symbol', +]) + +MSVC_NOTFOUND_DEFINITION_LIST = [] + +MSVC_NOTFOUND_POLICY_INTERNAL = {} +MSVC_NOTFOUND_POLICY_EXTERNAL = {} + +for policy_value, policy_symbol_list in [ + (True, ['Error', 'Exception']), + (False, ['Warning', 'Warn']), + (None, ['Ignore', 'Suppress']), +]: + + policy_symbol = policy_symbol_list[0].lower() + policy_def = MSVC_NOTFOUND_POLICY_DEFINITION(policy_value, policy_symbol) + + MSVC_NOTFOUND_DEFINITION_LIST.append(policy_def) + + MSVC_NOTFOUND_POLICY_INTERNAL[policy_symbol] = policy_def + + for policy_symbol in policy_symbol_list: + MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def + MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol] = policy_def + MSVC_NOTFOUND_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def + +# default definition +_MSVC_NOTFOUND_POLICY_DEF = MSVC_NOTFOUND_POLICY_INTERNAL['warning'] + + +# MSVC_SCRIPTERROR_POLICY definition: +# error: raise exception +# warning: issue warning and continue +# ignore: continue + +MSVC_SCRIPTERROR_POLICY_DEFINITION = namedtuple('MSVCBatchErrorPolicyDefinition', [ + 'value', + 'symbol', +]) + +MSVC_SCRIPTERROR_DEFINITION_LIST = [] + +MSVC_SCRIPTERROR_POLICY_INTERNAL = {} +MSVC_SCRIPTERROR_POLICY_EXTERNAL = {} + +for policy_value, policy_symbol_list in [ + (True, ['Error', 'Exception']), + (False, ['Warning', 'Warn']), + (None, ['Ignore', 'Suppress']), +]: + + policy_symbol = policy_symbol_list[0].lower() + policy_def = MSVC_SCRIPTERROR_POLICY_DEFINITION(policy_value, policy_symbol) + + MSVC_SCRIPTERROR_DEFINITION_LIST.append(policy_def) + + MSVC_SCRIPTERROR_POLICY_INTERNAL[policy_symbol] = policy_def + + for policy_symbol in policy_symbol_list: + MSVC_SCRIPTERROR_POLICY_EXTERNAL[policy_symbol.lower()] = policy_def + MSVC_SCRIPTERROR_POLICY_EXTERNAL[policy_symbol] = policy_def + MSVC_SCRIPTERROR_POLICY_EXTERNAL[policy_symbol.upper()] = policy_def + +# default definition +_MSVC_SCRIPTERROR_POLICY_DEF = MSVC_SCRIPTERROR_POLICY_INTERNAL['ignore'] + + +def _msvc_notfound_policy_lookup(symbol): + + try: + notfound_policy_def = MSVC_NOTFOUND_POLICY_EXTERNAL[symbol] + except KeyError: + err_msg = "Value specified for MSVC_NOTFOUND_POLICY is not supported: {}.\n" \ + " Valid values are: {}".format( + repr(symbol), + ', '.join([repr(s) for s in MSVC_NOTFOUND_POLICY_EXTERNAL.keys()]) + ) + raise ValueError(err_msg) + + return notfound_policy_def + +def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None): + """ Set the default policy when MSVC is not found. + + Args: + MSVC_NOTFOUND_POLICY: + string representing the policy behavior + when MSVC is not found or None + + Returns: + The previous policy is returned when the MSVC_NOTFOUND_POLICY argument + is not None. The active policy is returned when the MSVC_NOTFOUND_POLICY + argument is None. + + """ + global _MSVC_NOTFOUND_POLICY_DEF + + prev_policy = _MSVC_NOTFOUND_POLICY_DEF.symbol + + policy = MSVC_NOTFOUND_POLICY + if policy is not None: + _MSVC_NOTFOUND_POLICY_DEF = _msvc_notfound_policy_lookup(policy) + + debug( + 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', + repr(prev_policy), repr(policy), + repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) + ) + + return prev_policy + +def get_msvc_notfound_policy(): + """Return the active policy when MSVC is not found.""" + debug( + 'policy.symbol=%s, policy.value=%s', + repr(_MSVC_NOTFOUND_POLICY_DEF.symbol), repr(_MSVC_NOTFOUND_POLICY_DEF.value) + ) + return _MSVC_NOTFOUND_POLICY_DEF.symbol + +def msvc_notfound_handler(env, msg): + + if env and 'MSVC_NOTFOUND_POLICY' in env: + # environment setting + notfound_policy_src = 'environment' + policy = env['MSVC_NOTFOUND_POLICY'] + if policy is not None: + # user policy request + notfound_policy_def = _msvc_notfound_policy_lookup(policy) + else: + # active global setting + notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF + else: + # active global setting + notfound_policy_src = 'default' + policy = None + notfound_policy_def = _MSVC_NOTFOUND_POLICY_DEF + + debug( + 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', + notfound_policy_src, repr(policy), repr(notfound_policy_def.symbol), repr(notfound_policy_def.value) + ) + + if notfound_policy_def.value is None: + # ignore + pass + elif notfound_policy_def.value: + raise MSVCVersionNotFound(msg) + else: + SCons.Warnings.warn(MSVCVersionNotFoundWarning, msg) + + +def _msvc_scripterror_policy_lookup(symbol): + + try: + scripterror_policy_def = MSVC_SCRIPTERROR_POLICY_EXTERNAL[symbol] + except KeyError: + err_msg = "Value specified for MSVC_SCRIPTERROR_POLICY is not supported: {}.\n" \ + " Valid values are: {}".format( + repr(symbol), + ', '.join([repr(s) for s in MSVC_SCRIPTERROR_POLICY_EXTERNAL.keys()]) + ) + raise ValueError(err_msg) + + return scripterror_policy_def + +def set_msvc_scripterror_policy(MSVC_SCRIPTERROR_POLICY=None): + """ Set the default policy when msvc batch file execution errors are detected. + + Args: + MSVC_SCRIPTERROR_POLICY: + string representing the policy behavior + when msvc batch file execution errors are detected or None + + Returns: + The previous policy is returned when the MSVC_SCRIPTERROR_POLICY argument + is not None. The active policy is returned when the MSVC_SCRIPTERROR_POLICY + argument is None. + + """ + global _MSVC_SCRIPTERROR_POLICY_DEF + + prev_policy = _MSVC_SCRIPTERROR_POLICY_DEF.symbol + + policy = MSVC_SCRIPTERROR_POLICY + if policy is not None: + _MSVC_SCRIPTERROR_POLICY_DEF = _msvc_scripterror_policy_lookup(policy) + + debug( + 'prev_policy=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', + repr(prev_policy), repr(policy), + repr(_MSVC_SCRIPTERROR_POLICY_DEF.symbol), repr(_MSVC_SCRIPTERROR_POLICY_DEF.value) + ) + + return prev_policy + +def get_msvc_scripterror_policy(): + """Return the active policy when msvc batch file execution errors are detected.""" + debug( + 'policy.symbol=%s, policy.value=%s', + repr(_MSVC_SCRIPTERROR_POLICY_DEF.symbol), repr(_MSVC_SCRIPTERROR_POLICY_DEF.value) + ) + return _MSVC_SCRIPTERROR_POLICY_DEF.symbol + +def msvc_scripterror_handler(env, msg): + + if env and 'MSVC_SCRIPTERROR_POLICY' in env: + # environment setting + scripterror_policy_src = 'environment' + policy = env['MSVC_SCRIPTERROR_POLICY'] + if policy is not None: + # user policy request + scripterror_policy_def = _msvc_scripterror_policy_lookup(policy) + else: + # active global setting + scripterror_policy_def = _MSVC_SCRIPTERROR_POLICY_DEF + else: + # active global setting + scripterror_policy_src = 'default' + policy = None + scripterror_policy_def = _MSVC_SCRIPTERROR_POLICY_DEF + + debug( + 'source=%s, set_policy=%s, policy.symbol=%s, policy.value=%s', + scripterror_policy_src, repr(policy), repr(scripterror_policy_def.symbol), repr(scripterror_policy_def.value) + ) + + if scripterror_policy_def.value is None: + # ignore + pass + elif scripterror_policy_def.value: + raise MSVCScriptExecutionError(msg) + else: + SCons.Warnings.warn(MSVCScriptExecutionWarning, msg) + diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 14c8c6c7cf..d07b78fc80 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -94,7 +94,8 @@ def _verify_re_sdk_dispatch_map(): re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') # SxS version bugfix -_msvc_sxs_bugfix_folder = set() +_msvc_sxs_bugfix_map = {} +_msvc_sxs_bugfix_folder = {} _msvc_sxs_bugfix_version = {} for msvc_version, sxs_version, sxs_bugfix in [ @@ -103,7 +104,8 @@ def _verify_re_sdk_dispatch_map(): # 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_folder.add((msvc_version, sxs_bugfix)) + _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 @@ -383,6 +385,24 @@ def _user_script_argument_sdk(env, sdk_version, 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(): + global _toolset_have140_cache + debug('reset: cache') + _toolset_have140_cache = None + def _msvc_read_toolset_file(msvc, filename): toolset_version = None try: @@ -398,30 +418,18 @@ def _msvc_read_toolset_file(msvc, filename): debug('IndexError: msvc_version=%s, filename=%s', repr(msvc.version), repr(filename)) return toolset_version -def _msvc_sxs_toolset_folder(msvc, sxs_version): +def _msvc_sxs_toolset_folder(msvc, sxs_folder): - if re_toolset_sxs.match(sxs_version): - return sxs_version + if re_toolset_sxs.match(sxs_folder): + return sxs_folder, sxs_folder - key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_version) + key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder) if key in _msvc_sxs_bugfix_folder: - return sxs_version + sxs_version = _msvc_sxs_bugfix_folder[key] + return sxs_folder, sxs_version - debug('sxs folder: ignore version=%s', repr(sxs_version)) - return None - -def _msvc_sxs_toolset_version(msvc, sxs_toolset): - - if not re_toolset_sxs.match(sxs_toolset): - return None, False - - key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_toolset) - sxs_bugfix = _msvc_sxs_bugfix_version.get(key) - if not sxs_bugfix: - return sxs_toolset, False - - debug('sxs bugfix: version=%s => version=%s', repr(sxs_toolset), repr(sxs_bugfix)) - return sxs_bugfix, True + debug('sxs folder: ignore version=%s', repr(sxs_folder)) + return None, None def _msvc_read_toolset_folders(msvc, vc_dir): @@ -430,11 +438,11 @@ def _msvc_read_toolset_folders(msvc, vc_dir): build_dir = os.path.join(vc_dir, "Auxiliary", "Build") if os.path.exists(build_dir): - for sxs_version, sxs_path in Util.listdir_dirs(build_dir): - sxs_version = _msvc_sxs_toolset_folder(msvc, sxs_version) + 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 = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_version) + filename = 'Microsoft.VCToolsVersion.{}.txt'.format(sxs_folder) filepath = os.path.join(sxs_path, filename) debug('sxs toolset: check file=%s', repr(filepath)) if os.path.exists(filepath): @@ -459,7 +467,34 @@ def _msvc_read_toolset_folders(msvc, vc_dir): 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 @@ -492,16 +527,13 @@ def _msvc_read_toolset_default(msvc, vc_dir): _toolset_version_cache = {} _toolset_default_cache = {} -_toolset_have140_cache = None def _reset_toolset_cache(): global _toolset_version_cache global _toolset_default_cache - global _toolset_have140_cache debug('reset: toolset cache') _toolset_version_cache = {} _toolset_default_cache = {} - _toolset_have140_cache = None def _msvc_version_toolsets(msvc, vc_dir): @@ -523,24 +555,8 @@ def _msvc_default_toolset(msvc, vc_dir): return toolset_default -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 _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): - if toolset_version == '14.0': - if _msvc_have140_toolset(): - return toolset_version - return None - toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir) if toolset_version in toolsets_full: @@ -548,21 +564,14 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): toolset_vcvars = toolset_version return toolset_vcvars - sxs_toolset, sxs_isbugfix = _msvc_sxs_toolset_version(msvc, toolset_version) - if sxs_toolset: + if re_toolset_sxs.match(toolset_version): # SxS version provided - sxs_version = toolsets_sxs.get(sxs_toolset, None) - if sxs_version: + sxs_version = toolsets_sxs.get(toolset_version, None) + if sxs_version and sxs_version in toolsets_full: # SxS full toolset version - if sxs_version in toolsets_full: - toolset_vcvars = sxs_version - return toolset_vcvars - return None - # SxS version file missing - if not sxs_isbugfix: - return None - # SxS version bugfix: check toolset version - toolset_version = sxs_toolset + toolset_vcvars = sxs_version + return toolset_vcvars + return None for toolset_full in toolsets_full: if toolset_full.startswith(toolset_version): @@ -641,13 +650,7 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version): err_msg = "MSVC_TOOLSET_VERSION ({}) format is not supported".format(repr(toolset_version)) return err_msg -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 +def _msvc_script_argument_toolset_vcvars(msvc, toolset_version, vc_dir): err_msg = _msvc_script_argument_toolset_constraints(msvc, toolset_version) if err_msg: @@ -673,6 +676,18 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist): ) 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, '-vcvars_ver={}'.format(toolset_vcvars)) arglist.append(argpair) @@ -956,8 +971,38 @@ def msvc_script_arguments(env, version, vc_dir, arg): 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=True, sxs=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 reset(): debug('') + _reset_have140_cache() _reset_toolset_cache() def verify(): diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index f1983ba3e7..11b80ec4ea 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -28,6 +28,10 @@ import os import re +from collections import ( + namedtuple, +) + def listdir_dirs(p): """ Return a list of tuples for each subdirectory of the given directory path. @@ -67,7 +71,7 @@ def process_path(p): p = os.path.normcase(p) return p -re_version_prefix = re.compile(r'^(?P[0-9.]+).*') +re_version_prefix = re.compile(r'^(?P[0-9.]+).*$') def get_version_prefix(version): """ @@ -88,7 +92,7 @@ def get_version_prefix(version): rval = '' return rval -re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*') +re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*$') def get_msvc_version_prefix(version): """ @@ -109,3 +113,41 @@ def get_msvc_version_prefix(version): rval = '' return rval +VERSION_ELEMENTS_DEFINITION = namedtuple('VersionElements', [ + 'vc_version_numstr', # msvc version numeric string ('14.1') + 'vc_toolset_numstr', # toolset version numeric string ('14.16.27023') + 'vc_version_suffix', # component type ('Exp') + 'msvc_version', # msvc version ('14.1Exp') +]) + +re_version_elements = re.compile(r'^(?P(?P[1-9][0-9]?[.][0-9])[0-9.]*)(?P.*)$') + +def get_version_elements(version): + """ + Get the version elements from an msvc version or toolset version. + + Args: + version: str + version specification + + Returns: + None or VersionElements namedtuple: + """ + + m = re_version_elements.match(version) + if not m: + return None + + vc_version_numstr = m.group('msvc_version') + vc_toolset_numstr = m.group('version') + vc_version_suffix = m.group('suffix') + + version_elements_def = VERSION_ELEMENTS_DEFINITION( + vc_version_numstr = vc_version_numstr, + vc_toolset_numstr = vc_toolset_numstr, + vc_version_suffix = vc_version_suffix, + msvc_version = vc_version_numstr + vc_version_suffix, + ) + + return version_elements_def + diff --git a/SCons/Tool/MSCommon/MSVC/Warnings.py b/SCons/Tool/MSCommon/MSVC/Warnings.py new file mode 100644 index 0000000000..9f209725d3 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/Warnings.py @@ -0,0 +1,44 @@ +# 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. + +""" +Warnings for Microsoft Visual C/C++. +""" + +__all__ = [ + 'VisualCWarning', + 'MSVCScriptExecutionWarning', + 'MSVCVersionNotFoundWarning', +] + +import SCons.Warnings + +class VisualCWarning(SCons.Warnings.WarningOnByDefault): + pass + +class MSVCScriptExecutionWarning(VisualCWarning): + pass + +class MSVCVersionNotFoundWarning(VisualCWarning): + pass + diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index b0ef5dd512..d73a7e182e 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -24,15 +24,15 @@ """ Functions for Microsoft Visual C/C++. -The reset method is used to restore MSVC module data structures to their +The _reset method is used to restore MSVC module data structures to their initial state for testing purposes. -The verify method is used as a sanity check that MSVC module data structures +The _verify method is used as a sanity check that MSVC module data structures are internally consistent. Currently: -* reset is invoked from reset_installed_vcs in the vc module. -* verify is invoked from the last line in the vc module. +* _reset is invoked from reset_installed_vcs in the vc module. +* _verify is invoked from the last line in the vc module. """ from . import Exceptions # noqa: F401 @@ -41,15 +41,17 @@ from . import Config # noqa: F401 from . import Registry # noqa: F401 from . import SetupEnvDefault # noqa: F401 -from . import NotFound # noqa: F401 +from . import Policy # noqa: F401 from . import WinSDK # noqa: F401 from . import ScriptArguments # noqa: F401 +from . import API # noqa: F401 + from . import Dispatcher as _Dispatcher -def reset(): +def _reset(): _Dispatcher.reset() -def verify(): +def _verify(): _Dispatcher.verify() diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 502a71fd0b..4834b12ee6 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -62,13 +62,7 @@ from . import MSVC -from .MSVC.Exceptions import ( - VisualCException -) - -# msvc test(s) expect notfound policy available via vc -from .MSVC.NotFound import set_msvc_notfound_policy # noqa: F401 -from .MSVC.NotFound import get_msvc_notfound_policy # noqa: F401 +from .MSVC.API import * class UnsupportedVersion(VisualCException): pass @@ -94,9 +88,6 @@ class MSVCScriptNotFound(VisualCException): class MSVCUseSettingsError(VisualCException): pass -#class BatchFileExecutionWarning(SCons.Warnings.WarningOnByDefault): -# pass - # Dict to 'canonalize' the arch _ARCH_TO_CANONICAL = { @@ -972,7 +963,7 @@ def reset_installed_vcs(): """Make it try again to find VC. This is just for the tests.""" global __INSTALLED_VCS_RUN __INSTALLED_VCS_RUN = None - MSVC.reset() + MSVC._reset() def get_default_installed_msvc(env=None): vcs = get_installed_vcs(env) @@ -1000,7 +991,7 @@ def get_default_installed_msvc(env=None): script_env_cache = None -def script_env(script, args=None): +def script_env(env, script, args=None): global script_env_cache if script_env_cache is None: @@ -1028,8 +1019,8 @@ def script_env(script, args=None): stdout = common.get_output(script, args) cache_data = common.parse_output(stdout) + # debug(stdout) olines = stdout.splitlines() - #debug(olines) # process stdout: batch file errors (not necessarily first line) script_errlog = [] @@ -1041,21 +1032,23 @@ def script_env(script, args=None): if script_errlog: script_errmsg = '\n'.join(script_errlog) + have_cl = False if cache_data and 'PATH' in cache_data: for p in cache_data['PATH']: if os.path.exists(os.path.join(p, _CL_EXE_NAME)): have_cl = True break + + debug( + 'script=%s args=%s have_cl=%s, errors=%s', + repr(script), repr(args), repr(have_cl), script_errmsg + ) + MSVC.Policy.msvc_scripterror_handler(env, script_errmsg) + if not have_cl: # detected errors, cl.exe not on path raise BatchFileExecutionError(script_errmsg) - else: - # detected errors, cl.exe on path - debug('script=%s args=%s errors=%s', repr(script), repr(args), script_errmsg) - # This may be a bad idea (scons environment != vs cmdline environment) - #SCons.Warnings.warn(BatchFileExecutionWarning, script_errmsg) - # TODO: errlog/errstr should be added to cache and warning moved to call site # once we updated cache, give a chance to write out if user wanted script_env_cache[cache_key] = cache_data @@ -1111,7 +1104,7 @@ def msvc_setup_env_once(env, tool=None): msg = "No versions of the MSVC compiler were found.\n" \ " Visual Studio C/C++ compilers may not be set correctly.\n" \ " Requested tool(s) are: {}".format(req_tools) - MSVC.NotFound.policy_handler(env, msg) + MSVC.Policy.msvc_notfound_handler(env, msg) def msvc_find_valid_batch_script(env, version): """Find and execute appropriate batch script to set up build env. @@ -1155,7 +1148,7 @@ def msvc_find_valid_batch_script(env, version): if vc_script: arg = MSVC.ScriptArguments.msvc_script_arguments(env, version, vc_dir, arg) try: - d = script_env(vc_script, args=arg) + d = script_env(env, vc_script, args=arg) found = vc_script except BatchFileExecutionError as e: debug('use_script 3: failed running VC script %s: %s: Error:%s', repr(vc_script), arg, e) @@ -1164,7 +1157,7 @@ def msvc_find_valid_batch_script(env, version): if not vc_script and sdk_script: debug('use_script 4: trying sdk script: %s', sdk_script) try: - d = script_env(sdk_script) + d = script_env(env, sdk_script) found = sdk_script except BatchFileExecutionError as e: debug('use_script 5: failed running SDK script %s: Error:%s', repr(sdk_script), e) @@ -1198,7 +1191,7 @@ def msvc_find_valid_batch_script(env, version): " No versions of the MSVC compiler were found.\n" \ " Visual Studio C/C++ compilers may not be set correctly".format(version) - MSVC.NotFound.policy_handler(env, msg) + MSVC.Policy.msvc_notfound_handler(env, msg) return d @@ -1250,7 +1243,7 @@ def msvc_setup_env(env): raise MSVCScriptNotFound('Script specified by MSVC_USE_SCRIPT not found: "{}"'.format(use_script)) args = env.subst('$MSVC_USE_SCRIPT_ARGS') debug('use_script 1 %s %s', repr(use_script), repr(args)) - d = script_env(use_script, args) + d = script_env(env, use_script, args) elif use_script: d = msvc_find_valid_batch_script(env,version) debug('use_script 2 %s', d) @@ -1336,7 +1329,7 @@ def msvc_setup_env_tool(env=None, version=None, tool=None): rval = True return rval -def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): +def msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app)) rval = [] @@ -1351,6 +1344,185 @@ def get_msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) return rval +def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): + debug('msvc_version=%s, full=%s, sxs=%s', repr(msvc_version), repr(full), repr(sxs)) + + env = None + rval = [] + + if not msvc_version: + msvc_version = get_default_installed_msvc() + + if not msvc_version: + debug('no msvc versions detected') + return rval + + if msvc_version not in _VCVER: + msg = 'Unsupported msvc version {}'.format(repr(msvc_version)) + raise MSVCArgumentError(msg) + + vc_dir = find_vc_pdir(env, msvc_version) + if not vc_dir: + debug('VC folder not found for version %s', repr(msvc_version)) + return + + rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs) + return rval + +def find_msvc_version_toolset(version, prefer_newest=True): + """ + Returns an msvc version and a toolset version given a version + specification. + + This an EXPERIMENTAL proxy for using a toolset version to perform + msvc instance selection. This function will be removed when + toolset version is taken into account during msvc instance selection. + + Search for an installed Visual Studio instance that supports the + specified version. + + When the specified version contains a component suffix (e.g., Exp), + the msvc version is returned and the toolset version is None. No + search if performed. + + When the specified version does not contain a component suffix, the + version is treated as a toolset version specification. A search is + performed for the first msvc instance that contains the toolset + version. + + Only Visual Studio 2017 and later support toolset arguments. For + Visual Studio 2015 and earlier, the msvc version is returned and + the toolset version is None. + + Args: + + version: str + The version specification may be an msvc version or a toolset + version. + + prefer_newest: bool + True: prefer newer Visual Studio instances. + False: prefer the "native" Visual Studio instance first. If + the native Visual Studio instance is not detected, prefer + newer Visual Studio instances. + + Returns: + tuple: A tuple containing the msvc version and the msvc toolset version. + The msvc toolset version may be None. + + Raises: + MSVCToolsetVersionNotFound: when the specified version is not found. + MSVCArgumentError: when argument validation fails. + """ + debug('version=%s, prefer_newest=%s', repr(version), repr(prefer_newest)) + + env = None + msvc_version = version + msvc_toolset_version = None + + if not version: + debug( + 'ignore: msvc_version=%s, msvc_toolset_version=%s', + repr(msvc_version), repr(msvc_toolset_version) + ) + return msvc_version, msvc_toolset_version + + version_elements_def = MSVC.Util.get_version_elements(version) + if not version_elements_def: + msg = 'Unsupported version format {}'.format(repr(version)) + raise MSVCArgumentError(msg) + + if version_elements_def.msvc_version not in _VCVER: + msg = 'Unsupported msvc version {}'.format(repr(version)) + raise MSVCArgumentError(msg) + + if version_elements_def.vc_version_suffix: + if version_elements_def.vc_version_numstr != version_elements_def.vc_toolset_numstr: + # toolset version with component suffix + msg = 'Unsupported toolset version {}'.format(repr(version)) + raise MSVCArgumentError(msg) + + if float(version_elements_def.vc_version_numstr) > 14.0: + # VS2017 and later + force_toolset_msvc_version = False + else: + # VS2015 and earlier + force_toolset_msvc_version = True + extended_version = version_elements_def.vc_version_numstr + '0.00000' + if not extended_version.startswith(version_elements_def.vc_toolset_numstr): + # toolset not equivalent to msvc version + msg = 'Unsupported toolset version {} (expected {})'.format( + repr(version), repr(extended_version) + ) + raise MSVCArgumentError(msg) + + msvc_version = version_elements_def.msvc_version + + if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP: + # VS2013 and earlier + debug( + 'ignore: msvc_version=%s, msvc_toolset_version=%s', + repr(msvc_version), repr(msvc_toolset_version) + ) + return msvc_version, msvc_toolset_version + + if force_toolset_msvc_version: + msvc_toolset_version = version_elements_def.vc_version_numstr + else: + msvc_toolset_version = version_elements_def.vc_toolset_numstr + + if prefer_newest: + query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] + else: + query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_DEFAULTS_MAP[msvc_version] + \ + MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] + + seen_msvc_version = set() + for query_msvc_version in query_version_list: + + if query_msvc_version in seen_msvc_version: + continue + seen_msvc_version.add(query_msvc_version) + + vc_dir = find_vc_pdir(env, query_msvc_version) + if not vc_dir: + continue + + if query_msvc_version.startswith('14.0'): + # VS2015 does not support toolset version argument + msvc_toolset_version = None + debug( + 'found: msvc_version=%s, msvc_toolset_version=%s', + repr(query_msvc_version), repr(msvc_toolset_version) + ) + return query_msvc_version, msvc_toolset_version + + try: + toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, msvc_toolset_version, vc_dir) + if toolset_vcvars: + debug( + 'found: msvc_version=%s, msvc_toolset_version=%s', + repr(query_msvc_version), repr(msvc_toolset_version) + ) + return query_msvc_version, msvc_toolset_version + + except MSVCToolsetVersionNotFound: + pass + + debug( + 'not found: msvc_version=%s, msvc_toolset_version=%s', + repr(msvc_version), repr(msvc_toolset_version) + ) + + if version_elements_def.vc_version_numstr == msvc_toolset_version: + msg = 'MSVC version {} was not found'.format(repr(version)) + MSVC.Policy.msvc_notfound_handler(None, msg) + return msvc_version, msvc_toolset_version + + msg = 'MSVC toolset version {} not found'.format(repr(version)) + raise MSVCToolsetVersionNotFound(msg) + + # internal consistency check (should be last) -MSVC.verify() +MSVC._verify() diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index 9297100f24..c582bb4d65 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -761,6 +761,131 @@ subject to the conditions listed above. The default &scons; behavior may change + + + +Specify the &scons; behavior when Microsoft Visual C/C++ batch file errors are detected. + + + +The &cv-MSVC_SCRIPTERROR_POLICY; specifies the &scons; behavior when msvc batch file errors are +detected. +When &cv-MSVC_SCRIPTERROR_POLICY; is not specified, the default &scons; behavior is to suppress +msvc batch file error messages. + + +The root cause of msvc build failures may be difficult to diagnose. In these situations, setting +the &scons; behavior to issue a warning when msvc batch file errors are detected may +produce additional diagnostic information. + + + +The valid values for &cv-MSVC_SCRIPTERROR_POLICY; and the corresponding &scons; behavior are: + + + + + +'Error' or 'Exception' + + +Raise an exception when msvc batch file errors are detected. + + + + + +'Warning' or 'Warn' + + +Issue a warning when msvc batch file errors are detected. + + + + + +'Ignore' or 'Suppress' + + +Suppress msvc batch file error messages. + + + + + + + +Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well. + + + +Example 1 - A Visual Studio 2022 build with user-defined script arguments: + +env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1']) +env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject']) + + + + +Example 1 - Output fragment: + +... +link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj +LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib' +... + + + + +Example 2 - A Visual Studio 2022 build with user-defined script arguments and the script error policy set +to issue a warning when msvc batch file errors are detected: + +env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1'], MSVC_SCRIPTERROR_POLICY='warn') +env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject']) + + + + +Example 2 - Output fragment: + +... +scons: warning: vc script errors detected: +[ERROR:vcvars.bat] The UWP Application Platform requires a Windows 10 SDK. +[ERROR:vcvars.bat] WindowsSdkDir = "C:\Program Files (x86)\Windows Kits\8.1\" +[ERROR:vcvars.bat] host/target architecture is not supported : { x64 , x64 } +... +link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj +LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib' + + + + +Important usage details: + + + +&cv-MSVC_SCRIPTERROR_POLICY; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SCRIPTERROR_POLICY; must be set before the first msvc tool is +loaded into the environment. + + + +Due to &scons; implementation details, not all Windows system environment variables are propagated +to the environment in which the msvc batch file is executed. Depending on Visual Studio version +and installation options, non-fatal msvc batch file error messages may be generated for ancillary +tools which may not affect builds with the msvc compiler. For this reason, caution is recommended +when setting the script error policy to raise an exception (e.g., 'Error'). + + + + + + + + diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 80d5b180b8..533ea22936 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -4937,6 +4937,132 @@ and compatible with the version of msvc selected. + + + + + MSVC_SCRIPTERROR_POLICY + + +Specify the &scons; behavior when Microsoft Visual C/C++ batch file errors are detected. + + + +The &cv-MSVC_SCRIPTERROR_POLICY; specifies the &scons; behavior when msvc batch file errors are +detected. +When &cv-MSVC_SCRIPTERROR_POLICY; is not specified, the default &scons; behavior is to suppress +msvc batch file error messages. + + +The root cause of msvc build failures may be difficult to diagnose. In these situations, setting +the &scons; behavior to issue a warning when msvc batch file errors are detected may +produce additional diagnostic information. + + + +The valid values for &cv-MSVC_SCRIPTERROR_POLICY; and the corresponding &scons; behavior are: + + + + + +'Error' or 'Exception' + + +Raise an exception when msvc batch file errors are detected. + + + + + +'Warning' or 'Warn' + + +Issue a warning when msvc batch file errors are detected. + + + + + +'Ignore' or 'Suppress' + + +Suppress msvc batch file error messages. + + + + + + + +Note: in addition to the camel case values shown above, lower case and upper case values are accepted as well. + + + +Example 1 - A Visual Studio 2022 build with user-defined script arguments: + +env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1']) +env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject']) + + + + +Example 1 - Output fragment: + +... +link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj +LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib' +... + + + + +Example 2 - A Visual Studio 2022 build with user-defined script arguments and the script error policy set +to issue a warning when msvc batch file errors are detected: + +env = environment(MSVC_VERSION='14.3', MSVC_SCRIPT_ARGS=['8.1', 'store', '-vcvars_ver=14.1'], MSVC_SCRIPTERROR_POLICY='warn') +env.Program('hello', ['hello.c'], CCFLAGS='/MD', LIBS=['kernel32', 'user32', 'runtimeobject']) + + + + +Example 2 - Output fragment: + +... +scons: warning: vc script errors detected: +[ERROR:vcvars.bat] The UWP Application Platform requires a Windows 10 SDK. +[ERROR:vcvars.bat] WindowsSdkDir = "C:\Program Files (x86)\Windows Kits\8.1\" +[ERROR:vcvars.bat] host/target architecture is not supported : { x64 , x64 } +... +link /nologo /OUT:_build001\hello.exe kernel32.lib user32.lib runtimeobject.lib _build001\hello.obj +LINK : fatal error LNK1104: cannot open file 'MSVCRT.lib' + + + + +Important usage details: + + + +&cv-MSVC_SCRIPTERROR_POLICY; must be passed as an argument to the &f-link-Environment; +constructor when an msvc tool (e.g., &t-link-msvc;, &t-link-msvs;, etc.) is +loaded via the default tools list or via a tools list passed to the +&f-link-Environment; constructor. +Otherwise, &cv-MSVC_SCRIPTERROR_POLICY; must be set before the first msvc tool is +loaded into the environment. + + + +Due to &scons; implementation details, not all Windows system environment variables are propagated +to the environment in which the msvc batch file is executed. Depending on Visual Studio version +and installation options, non-fatal msvc batch file error messages may be generated for ancillary +tools which may not affect builds with the msvc compiler. For this reason, caution is recommended +when setting the script error policy to raise an exception (e.g., 'Error'). + + + + + diff --git a/doc/generated/variables.mod b/doc/generated/variables.mod index cc51043c12..5d898878c6 100644 --- a/doc/generated/variables.mod +++ b/doc/generated/variables.mod @@ -323,6 +323,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $MSVC_BATCH"> $MSVC_NOTFOUND_POLICY"> $MSVC_SCRIPT_ARGS"> +$MSVC_SCRIPTERROR_POLICY"> $MSVC_SDK_VERSION"> $MSVC_SPECTRE_LIBS"> $MSVC_TOOLSET_VERSION"> @@ -992,6 +993,7 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. $MSVC_BATCH"> $MSVC_NOTFOUND_POLICY"> $MSVC_SCRIPT_ARGS"> +$MSVC_SCRIPTERROR_POLICY"> $MSVC_SDK_VERSION"> $MSVC_SPECTRE_LIBS"> $MSVC_TOOLSET_VERSION"> From 392af455bd2a07b195296cd7f159ed052e4319b5 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 5 Jul 2022 00:38:14 -0400 Subject: [PATCH 051/108] Revert from MSVCNotFoundWarning back to VisualCMissingWarning (test failures). --- CHANGES.txt | 2 +- RELEASE.txt | 2 +- SCons/Tool/MSCommon/MSVC/Policy.py | 3 +-- SCons/Tool/MSCommon/MSVC/Warnings.py | 4 ---- 4 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index a4cf13aeff..3fe1266615 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -84,7 +84,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy - is "warning" or "warn", an MSVCVersionNotFoundWarning warning is issued and the constructed + is "warning" or "warn", a VisualCMissingWarning warning is issued and the constructed environment is likely incomplete. When the active policy is "ignore" or "suppress", no action is taken and the constructed environment is likely incomplete. As implemented, the default global policy is "warning". The ability to set the global policy via an SCons command-line diff --git a/RELEASE.txt b/RELEASE.txt index d7a2690adc..4a3d06ca9b 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -53,7 +53,7 @@ NEW FUNCTIONALITY construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy - is "warning" or "warn", an MSVCVersionNotFoundWarning warning is issued and the constructed + is "warning" or "warn", a VisualCMissingWarning warning is issued and the constructed environment is likely incomplete. When the active policy is "ignore" or "suppress", no action is taken and the constructed environment is likely incomplete. As implemented, the default global policy is "warning". The ability to set the global policy via an SCons command-line diff --git a/SCons/Tool/MSCommon/MSVC/Policy.py b/SCons/Tool/MSCommon/MSVC/Policy.py index 870c73c77f..95643b25e9 100644 --- a/SCons/Tool/MSCommon/MSVC/Policy.py +++ b/SCons/Tool/MSCommon/MSVC/Policy.py @@ -50,7 +50,6 @@ ) from .Warnings import ( - MSVCVersionNotFoundWarning, MSVCScriptExecutionWarning, ) @@ -213,7 +212,7 @@ def msvc_notfound_handler(env, msg): elif notfound_policy_def.value: raise MSVCVersionNotFound(msg) else: - SCons.Warnings.warn(MSVCVersionNotFoundWarning, msg) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) def _msvc_scripterror_policy_lookup(symbol): diff --git a/SCons/Tool/MSCommon/MSVC/Warnings.py b/SCons/Tool/MSCommon/MSVC/Warnings.py index 9f209725d3..c4a0f15787 100644 --- a/SCons/Tool/MSCommon/MSVC/Warnings.py +++ b/SCons/Tool/MSCommon/MSVC/Warnings.py @@ -28,7 +28,6 @@ __all__ = [ 'VisualCWarning', 'MSVCScriptExecutionWarning', - 'MSVCVersionNotFoundWarning', ] import SCons.Warnings @@ -39,6 +38,3 @@ class VisualCWarning(SCons.Warnings.WarningOnByDefault): class MSVCScriptExecutionWarning(VisualCWarning): pass -class MSVCVersionNotFoundWarning(VisualCWarning): - pass - From 36b8d2de33f5ae88d0936be8ac3dee3dfaa41cd5 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 5 Jul 2022 02:08:00 -0400 Subject: [PATCH 052/108] Remove API module and import exceptions and global policy functions --- SCons/Tool/MSCommon/MSVC/API.py | 38 -------------------------- SCons/Tool/MSCommon/MSVC/Exceptions.py | 11 -------- SCons/Tool/MSCommon/MSVC/Warnings.py | 5 ---- SCons/Tool/MSCommon/MSVC/__init__.py | 2 -- SCons/Tool/MSCommon/vc.py | 14 +++++++++- 5 files changed, 13 insertions(+), 57 deletions(-) delete mode 100644 SCons/Tool/MSCommon/MSVC/API.py diff --git a/SCons/Tool/MSCommon/MSVC/API.py b/SCons/Tool/MSCommon/MSVC/API.py deleted file mode 100644 index cfa4195e4b..0000000000 --- a/SCons/Tool/MSCommon/MSVC/API.py +++ /dev/null @@ -1,38 +0,0 @@ -# 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. - -""" -Public API for Microsoft Visual C/C++. -""" - -from .Exceptions import * # noqa: F401 -from .Warnings import * # noqa: F401 - -from .Policy import set_msvc_notfound_policy # noqa: F401 -from .Policy import get_msvc_notfound_policy # noqa: F401 - -from .Policy import set_msvc_scripterror_policy # noqa: F401 -from .Policy import get_msvc_scripterror_policy # noqa: F401 - -from .Util import get_version_elements # noqa: F401 - diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py index 6a311d49aa..a12b3c6b88 100644 --- a/SCons/Tool/MSCommon/MSVC/Exceptions.py +++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py @@ -25,17 +25,6 @@ Exceptions for Microsoft Visual C/C++. """ -__all__ = [ - 'VisualCException', - 'MSVCInternalError', - 'MSVCScriptExecutionError', - 'MSVCVersionNotFound', - 'MSVCSDKVersionNotFound', - 'MSVCToolsetVersionNotFound', - 'MSVCSpectreLibsNotFound', - 'MSVCArgumentError', -] - class VisualCException(Exception): pass diff --git a/SCons/Tool/MSCommon/MSVC/Warnings.py b/SCons/Tool/MSCommon/MSVC/Warnings.py index c4a0f15787..cab5145a99 100644 --- a/SCons/Tool/MSCommon/MSVC/Warnings.py +++ b/SCons/Tool/MSCommon/MSVC/Warnings.py @@ -25,11 +25,6 @@ Warnings for Microsoft Visual C/C++. """ -__all__ = [ - 'VisualCWarning', - 'MSVCScriptExecutionWarning', -] - import SCons.Warnings class VisualCWarning(SCons.Warnings.WarningOnByDefault): diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index d73a7e182e..04a6948d67 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -45,8 +45,6 @@ from . import WinSDK # noqa: F401 from . import ScriptArguments # noqa: F401 -from . import API # noqa: F401 - from . import Dispatcher as _Dispatcher def _reset(): diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 4834b12ee6..a21e09c469 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -62,7 +62,19 @@ from . import MSVC -from .MSVC.API import * +from .MSVC.Policy import set_msvc_notfound_policy # noqa: F401 +from .MSVC.Policy import get_msvc_notfound_policy # noqa: F401 +from .MSVC.Policy import set_msvc_scripterror_policy # noqa: F401 +from .MSVC.Policy import get_msvc_scripterror_policy # noqa: F401 + +from .MSVC.Exceptions import VisualCException +from .MSVC.Exceptions import MSVCInternalError # noqa: F401 +from .MSVC.Exceptions import MSVCScriptExecutionError # noqa: F401 +from .MSVC.Exceptions import MSVCVersionNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCSDKVersionNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCToolsetVersionNotFound +from .MSVC.Exceptions import MSVCSpectreLibsNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCArgumentError class UnsupportedVersion(VisualCException): pass From 42803c3340b53d5a5ad9fb0345bf3a84f792544a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 5 Jul 2022 09:45:19 -0400 Subject: [PATCH 053/108] Rename msvc functions for consistency. Make additional msvc functions and exceptions available via MSCommon module. --- CHANGES.txt | 22 ++++++++-------- RELEASE.txt | 22 ++++++++-------- SCons/Tool/MSCommon/MSVC/Policy.py | 8 +++--- SCons/Tool/MSCommon/__init__.py | 25 +++++++++++++++++++ SCons/Tool/MSCommon/vc.py | 20 +++++---------- .../no_msvc/no_msvcs_sconstruct_version.py | 2 +- 6 files changed, 58 insertions(+), 41 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 3fe1266615..c9aad4c8f5 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -79,8 +79,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Added a global policy setting and an environment construction variable for specifying the action to be taken when an msvc request cannot be satisfied. The available options are "error", "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be - set and retrieved via the functions set_msvc_notfound_policy and get_msvc_notfound_policy, - respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + set and retrieved via the functions msvc_set_notfound_policy and msvc_get_notfound_policy, + respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy @@ -92,8 +92,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Added a global policy setting and an environment construction variable for specifying the action to be taken when msvc script errors are detected. The available options are "error", "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be - set and retrieved via the functions set_msvc_scripterror_policy and get_msvc_scripterror_policy, - respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + set and retrieved via the functions msvc_set_scripterror_policy and msvc_get_scripterror_policy, + respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment construction variable is MSVC_SCRIPTERROR_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCScriptExecutionError exception is raised when msvc batch file @@ -102,13 +102,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER "suppress", msvc batch error messages are suppressed. As implemented, the default global policy is "ignore". The ability to set the global policy via an SCons command-line option may be added in a future enhancement. - - Experimental: added function find_msvc_version_toolset to SCons.Tool.MSCommon.vc. Given a version - specification, this function will return a msvc version and a toolset version. The toolset version - may be None. The msvc version and toolset version can be used in the environment construction - variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The version specification may be an - msvc version or an msvc toolset version. This is an experimental proxy for using a toolset version - to perform instance selection. This function may be removed when toolset version is taken into - account during msvc instance selection. + - Added experimental function msvc_query_version_toolset to SCons.Tool.MSCommon. Given a version + specification, this function will return an msvc version and an msvc toolset version. The msvc + toolset version may be None. The msvc version and msvc toolset version can be used in the + environment construction variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The + version specification may be an msvc version or an msvc toolset version. This is a proxy for + using an msvc toolset version to select an msvc instance. This function may be removed when an + msvc toolset version is used during msvc instance selection. From William Deegan: - Fix check for unsupported Python version. It was broken. Also now the error message diff --git a/RELEASE.txt b/RELEASE.txt index 4a3d06ca9b..ab258dff1b 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -48,8 +48,8 @@ NEW FUNCTIONALITY - Added a global policy setting and an environment construction variable for specifying the action to be taken when an msvc request cannot be satisfied. The available options are "error", "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be - set and retrieved via the functions set_msvc_notfound_policy and get_msvc_notfound_policy, - respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + set and retrieved via the functions msvc_set_notfound_policy and msvc_get_notfound_policy, + respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment construction variable is MSVC_NOTFOUND_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCVersionNotFound exception is raised. When the active policy @@ -61,8 +61,8 @@ NEW FUNCTIONALITY - Added a global policy setting and an environment construction variable for specifying the action to be taken when msvc script errors are detected. The available options are "error", "exception", "warning", "warn", "ignore", and "suppress". The global policy variable may be - set and retrieved via the functions set_msvc_scripterror_policy and get_msvc_scripterror_policy, - respectively. These two methods may be imported from SCons.Tool.MSCommon.vc. The environment + set and retrieved via the functions msvc_set_scripterror_policy and msvc_get_scripterror_policy, + respectively. These two methods may be imported from SCons.Tool.MSCommon. The environment construction variable is MSVC_SCRIPTERROR_POLICY. When defined, the environment construction variable overrides the global policy setting for a given environment. When the active policy is "error" or "exception", an MSVCScriptExecutionError exception is raised when msvc batch file @@ -71,13 +71,13 @@ NEW FUNCTIONALITY "suppress", msvc batch error messages are suppressed. As implemented, the default global policy is "ignore". The ability to set the global policy via an SCons command-line option may be added in a future enhancement. -- Experimental: added function find_msvc_version_toolset to SCons.Tool.MSCommon.vc. Given a version - specification, this function will return a msvc version and a toolset version. The toolset version - may be None. The msvc version and toolset version can be used in the environment construction - variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The version specification may be an - msvc version or an msvc toolset version. This is an experimental proxy for using a toolset version - to perform instance selection. This function may be removed when toolset version is taken into - account during msvc instance selection. +- Added experimental function msvc_query_version_toolset to SCons.Tool.MSCommon. Given a version + specification, this function will return an msvc version and an msvc toolset version. The msvc + toolset version may be None. The msvc version and msvc toolset version can be used in the + environment construction variables MSVC_VERSION and MSVC_TOOLSET_VERSION, respectively. The + version specification may be an msvc version or an msvc toolset version. This is a proxy for + using an msvc toolset version to select an msvc instance. This function may be removed when an + msvc toolset version is used during msvc instance selection. - Fortran: a new construction variable FORTRANCOMMONFLAGS is added which is applied to all Fortran dialects, to enable global (all-dialect) settings. diff --git a/SCons/Tool/MSCommon/MSVC/Policy.py b/SCons/Tool/MSCommon/MSVC/Policy.py index 95643b25e9..9b7025e9ca 100644 --- a/SCons/Tool/MSCommon/MSVC/Policy.py +++ b/SCons/Tool/MSCommon/MSVC/Policy.py @@ -145,7 +145,7 @@ def _msvc_notfound_policy_lookup(symbol): return notfound_policy_def -def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None): +def msvc_set_notfound_policy(MSVC_NOTFOUND_POLICY=None): """ Set the default policy when MSVC is not found. Args: @@ -175,7 +175,7 @@ def set_msvc_notfound_policy(MSVC_NOTFOUND_POLICY=None): return prev_policy -def get_msvc_notfound_policy(): +def msvc_get_notfound_policy(): """Return the active policy when MSVC is not found.""" debug( 'policy.symbol=%s, policy.value=%s', @@ -229,7 +229,7 @@ def _msvc_scripterror_policy_lookup(symbol): return scripterror_policy_def -def set_msvc_scripterror_policy(MSVC_SCRIPTERROR_POLICY=None): +def msvc_set_scripterror_policy(MSVC_SCRIPTERROR_POLICY=None): """ Set the default policy when msvc batch file execution errors are detected. Args: @@ -259,7 +259,7 @@ def set_msvc_scripterror_policy(MSVC_SCRIPTERROR_POLICY=None): return prev_policy -def get_msvc_scripterror_policy(): +def msvc_get_scripterror_policy(): """Return the active policy when msvc batch file execution errors are detected.""" debug( 'policy.symbol=%s, policy.value=%s', diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 7900a8770c..168b1d4d44 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -32,12 +32,37 @@ from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env +from SCons.Tool.MSCommon.MSVC.Exceptions import ( + VisualCException, + MSVCInternalError, + MSVCScriptExecutionError, + MSVCVersionNotFound, + MSVCSDKVersionNotFound, + MSVCToolsetVersionNotFound, + MSVCSpectreLibsNotFound, + MSVCArgumentError, +) + +from SCons.Tool.MSCommon.MSVC.Policy import ( + msvc_set_notfound_policy, + msvc_get_notfound_policy, + msvc_set_scripterror_policy, + msvc_get_scripterror_policy, +) + from SCons.Tool.MSCommon.vc import ( msvc_exists, msvc_setup_env_tool, msvc_setup_env_once, msvc_version_to_maj_min, msvc_find_vswhere, + msvc_sdk_versions, + msvc_toolset_versions, + msvc_query_version_toolset, + MSVCUnsupportedHostArch, + MSVCUnsupportedTargetArch, + MSVCScriptNotFound, + MSVCUseSettingsError, ) from SCons.Tool.MSCommon.vs import ( diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index a21e09c469..e9a62edc78 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -62,19 +62,11 @@ from . import MSVC -from .MSVC.Policy import set_msvc_notfound_policy # noqa: F401 -from .MSVC.Policy import get_msvc_notfound_policy # noqa: F401 -from .MSVC.Policy import set_msvc_scripterror_policy # noqa: F401 -from .MSVC.Policy import get_msvc_scripterror_policy # noqa: F401 - -from .MSVC.Exceptions import VisualCException -from .MSVC.Exceptions import MSVCInternalError # noqa: F401 -from .MSVC.Exceptions import MSVCScriptExecutionError # noqa: F401 -from .MSVC.Exceptions import MSVCVersionNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCSDKVersionNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCToolsetVersionNotFound -from .MSVC.Exceptions import MSVCSpectreLibsNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCArgumentError +from .MSVC.Exceptions import ( + VisualCException, + MSVCArgumentError, + MSVCToolsetVersionNotFound, +) class UnsupportedVersion(VisualCException): pass @@ -1381,7 +1373,7 @@ def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs) return rval -def find_msvc_version_toolset(version, prefer_newest=True): +def msvc_query_version_toolset(version, prefer_newest=True): """ Returns an msvc version and a toolset version given a version specification. diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py index 4c76d44d9f..1e7c55f8ff 100644 --- a/test/fixture/no_msvc/no_msvcs_sconstruct_version.py +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_version.py @@ -10,7 +10,7 @@ def DummyVsWhere(msvc_version, env): SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere -SCons.Tool.MSCommon.vc.set_msvc_notfound_policy('error') +SCons.Tool.MSCommon.msvc_set_notfound_policy('error') env = SCons.Environment.Environment(MSVC_VERSION='14.3') From 7a930d9820f6b4d78d4366957bb065b0794f5499 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 5 Jul 2022 10:18:56 -0400 Subject: [PATCH 054/108] Change to individual imports with noqa F401 to appease sider/flake8. --- SCons/Tool/MSCommon/__init__.py | 41 +++++++++++++++------------------ 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 168b1d4d44..8396e1727b 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -32,24 +32,6 @@ from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env -from SCons.Tool.MSCommon.MSVC.Exceptions import ( - VisualCException, - MSVCInternalError, - MSVCScriptExecutionError, - MSVCVersionNotFound, - MSVCSDKVersionNotFound, - MSVCToolsetVersionNotFound, - MSVCSpectreLibsNotFound, - MSVCArgumentError, -) - -from SCons.Tool.MSCommon.MSVC.Policy import ( - msvc_set_notfound_policy, - msvc_get_notfound_policy, - msvc_set_scripterror_policy, - msvc_get_scripterror_policy, -) - from SCons.Tool.MSCommon.vc import ( msvc_exists, msvc_setup_env_tool, @@ -59,10 +41,6 @@ msvc_sdk_versions, msvc_toolset_versions, msvc_query_version_toolset, - MSVCUnsupportedHostArch, - MSVCUnsupportedTargetArch, - MSVCScriptNotFound, - MSVCUseSettingsError, ) from SCons.Tool.MSCommon.vs import ( @@ -73,6 +51,25 @@ query_versions, ) +from .MSVC.Policy import msvc_set_notfound_policy # noqa: F401 +from .MSVC.Policy import msvc_get_notfound_policy # noqa: F401 +from .MSVC.Policy import msvc_set_scripterror_policy # noqa: F401 +from .MSVC.Policy import msvc_get_scripterror_policy # noqa: F401 + +from .MSVC.Exceptions import VisualCException # noqa: F401 +from .MSVC.Exceptions import MSVCInternalError # noqa: F401 +from .MSVC.Exceptions import MSVCScriptExecutionError # noqa: F401 +from .MSVC.Exceptions import MSVCVersionNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCSDKVersionNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCToolsetVersionNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCSpectreLibsNotFound # noqa: F401 +from .MSVC.Exceptions import MSVCArgumentError # noqa: F401 + +from .vc import MSVCUnsupportedHostArch # noqa: F401 +from .vc import MSVCUnsupportedTargetArch # noqa: F401 +from .vc import MSVCScriptNotFound # noqa: F401 +from .vc import MSVCUseSettingsError # noqa: F401 + # Local Variables: # tab-width:4 # indent-tabs-mode:nil From 7c6def09e1741a0cd52f22d018fcf33965df1427 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 6 Jul 2022 08:37:47 -0400 Subject: [PATCH 055/108] Return full toolset version number for version toolset query function. --- SCons/Tool/MSCommon/vc.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index e9a62edc78..4c4571ff5d 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1471,9 +1471,9 @@ def msvc_query_version_toolset(version, prefer_newest=True): return msvc_version, msvc_toolset_version if force_toolset_msvc_version: - msvc_toolset_version = version_elements_def.vc_version_numstr + query_msvc_toolset_version = version_elements_def.vc_version_numstr else: - msvc_toolset_version = version_elements_def.vc_toolset_numstr + query_msvc_toolset_version = version_elements_def.vc_toolset_numstr if prefer_newest: query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] @@ -1502,8 +1502,9 @@ def msvc_query_version_toolset(version, prefer_newest=True): return query_msvc_version, msvc_toolset_version try: - toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, msvc_toolset_version, vc_dir) + toolset_vcvars = MSVC.ScriptArguments._msvc_toolset_internal(query_msvc_version, query_msvc_toolset_version, vc_dir) if toolset_vcvars: + msvc_toolset_version = toolset_vcvars debug( 'found: msvc_version=%s, msvc_toolset_version=%s', repr(query_msvc_version), repr(msvc_toolset_version) @@ -1513,6 +1514,8 @@ def msvc_query_version_toolset(version, prefer_newest=True): except MSVCToolsetVersionNotFound: pass + msvc_toolset_version = query_msvc_toolset_version + debug( 'not found: msvc_version=%s, msvc_toolset_version=%s', repr(msvc_version), repr(msvc_toolset_version) From f7e37ed5eecbeaeede8ad25ce303494d43611d3e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 6 Jul 2022 20:39:35 -0400 Subject: [PATCH 056/108] Fix typo in comment and add msvc not found, msvc script error, and msvc toolset versions tests. --- SCons/Tool/MSCommon/vc.py | 2 +- test/MSVC/MSVC_NOTFOUND_POLICY.py | 100 +++++++++++++ test/MSVC/MSVC_SCRIPTERROR_POLICY.py | 146 +++++++++++++++++++ test/MSVC/MSVC_TOOLSET_VERSION.py | 204 +++++++++++++++++++++++++++ 4 files changed, 451 insertions(+), 1 deletion(-) create mode 100644 test/MSVC/MSVC_NOTFOUND_POLICY.py create mode 100644 test/MSVC/MSVC_SCRIPTERROR_POLICY.py create mode 100644 test/MSVC/MSVC_TOOLSET_VERSION.py diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 4c4571ff5d..e05f397793 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1378,7 +1378,7 @@ def msvc_query_version_toolset(version, prefer_newest=True): Returns an msvc version and a toolset version given a version specification. - This an EXPERIMENTAL proxy for using a toolset version to perform + This is an EXPERIMENTAL proxy for using a toolset version to perform msvc instance selection. This function will be removed when toolset version is taken into account during msvc instance selection. diff --git a/test/MSVC/MSVC_NOTFOUND_POLICY.py b/test/MSVC/MSVC_NOTFOUND_POLICY.py new file mode 100644 index 0000000000..b6084db34f --- /dev/null +++ b/test/MSVC/MSVC_NOTFOUND_POLICY.py @@ -0,0 +1,100 @@ +# 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. + +""" +Test the msvc not found policy construction variable and functions. +""" + +import sys +import TestSCons + +test = TestSCons.TestSCons() + +if sys.platform != 'win32': + test.skip_test("Not win32 platform. Skipping test\n") + +test.skip_if_not_msvc() + +# Test global functions with valid symbols +test.write('SConstruct', """\ +from SCons.Tool.MSCommon import msvc_set_notfound_policy +from SCons.Tool.MSCommon import msvc_get_notfound_policy +DefaultEnvironment(tools=[]) +for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + old_policy = msvc_set_notfound_policy(policy) + cur_policy = msvc_get_notfound_policy() +if msvc_set_notfound_policy(None) != msvc_get_notfound_policy(): + raise RuntimeError() +""") +test.run(arguments='-Q -s', stdout='') + +# Test global function with invalid symbol +test.write('SConstruct', """\ +from SCons.Tool.MSCommon import msvc_set_notfound_policy +DefaultEnvironment(tools=[]) +msvc_set_notfound_policy('Undefined') +""") +test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_NOTFOUND_POLICY.+", match=TestSCons.match_re_dotall) + +# Test construction variable with valid symbols +test.write('SConstruct', """\ +env_list = [] +DefaultEnvironment(tools=[]) +for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + env = Environment(MSVC_NOTFOUND_POLICY=policy, tools=['msvc']) + env_list.append(env) +""") +test.run(arguments='-Q -s', stdout='') + +# Test construction variable with invalid symbol +test.write('SConstruct', """\ +env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Undefined', tools=['msvc']) +""") +test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_NOTFOUND_POLICY.+", match=TestSCons.match_re_dotall) + +# Test environment construction with global policy +test.write('SConstruct', """\ +from SCons.Tool.MSCommon import msvc_set_notfound_policy +msvc_set_notfound_policy('Exception') +env = Environment(MSVC_VERSION='12.9', tools=['msvc']) +""") +test.run(arguments='-Q -s', status=2, stderr=r"^.* MSVC version '12.9' was not found.+", match=TestSCons.match_re_dotall) + +# Test environment construction with construction variable +test.write('SConstruct', """\ +env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Error', tools=['msvc']) +""") +test.run(arguments='-Q -s', status=2, stderr=r"^.* MSVC version '12.9' was not found.+", match=TestSCons.match_re_dotall) + +# Test environment construction with construction variable (override global) +test.write('SConstruct', """\ +from SCons.Tool.MSCommon import msvc_set_notfound_policy +msvc_set_notfound_policy('Exception') +env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Ignore', tools=['msvc']) +""") +test.run(arguments='-Q -s', stdout='') + +test.pass_test() + diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py new file mode 100644 index 0000000000..a02a7fc160 --- /dev/null +++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py @@ -0,0 +1,146 @@ +# 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. + +""" +Test the msvc script error policy construction variable and functions. +""" + +import sys +import TestSCons + +test = TestSCons.TestSCons() + +if sys.platform != 'win32': + test.skip_test("Not win32 platform. Skipping test\n") + +test.skip_if_not_msvc() + +import textwrap + +from SCons.Tool.MSCommon.vc import ( + get_installed_vcs, + get_msvc_version_numeric, +) + +default_msvc_vernum = float(get_msvc_version_numeric(get_installed_vcs()[0])) + +# Test global functions with valid symbols +test.write('SConstruct', """\ +from SCons.Tool.MSCommon import msvc_set_scripterror_policy +from SCons.Tool.MSCommon import msvc_get_scripterror_policy +DefaultEnvironment(tools=[]) +for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + old_policy = msvc_set_scripterror_policy(policy) + cur_policy = msvc_get_scripterror_policy() +if msvc_set_scripterror_policy(None) != msvc_get_scripterror_policy(): + raise RuntimeError() +""") +test.run(arguments='-Q -s', stdout='') + +# Test global function with invalid symbol +test.write('SConstruct', """\ +from SCons.Tool.MSCommon import msvc_set_scripterror_policy +DefaultEnvironment(tools=[]) +msvc_set_scripterror_policy('Undefined') +""") +test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_SCRIPTERROR_POLICY.+", match=TestSCons.match_re_dotall) + +# Test construction variable with valid symbols +test.write('SConstruct', """\ +env_list = [] +DefaultEnvironment(tools=[]) +for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + env = Environment(MSVC_SCRIPTERROR_POLICY=policy, tools=['msvc']) + env_list.append(env) +""") +test.run(arguments='-Q -s', stdout='') + +if default_msvc_vernum >= 14.1: + # Need VS2017 or later for MSVC_SCRIPT_ARGS + + # Test environment construction with construction variable (invalid) + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Undefined', tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_SCRIPTERROR_POLICY.+", match=TestSCons.match_re_dotall) + + # Test environment construction with construction variable (override global) + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + DefaultEnvironment(tools=[]) + msvc_set_scripterror_policy('Exception') + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Ignore', tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', stdout='') + + # Test environment construction with global policy + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + DefaultEnvironment(tools=[]) + msvc_set_scripterror_policy('Exception') + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', status=2, stderr=r"^.* vc script errors detected.+", match=TestSCons.match_re_dotall) + + # Test environment construction with construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Error', tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', status=2, stderr=r"^.* vc script errors detected.+", match=TestSCons.match_re_dotall) + + # Test environment construction with global policy + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + DefaultEnvironment(tools=[]) + msvc_set_scripterror_policy('Warning') + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', status=0, stderr=None) + test.fail_test(test.stderr().lstrip().split('\n')[0].strip() != "scons: warning: vc script errors detected:") + + # Test environment construction with construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Warning', tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', status=0, stderr=None) + test.fail_test(test.stderr().lstrip().split('\n')[0].strip() != "scons: warning: vc script errors detected:") + +test.pass_test() + diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py new file mode 100644 index 0000000000..43192af634 --- /dev/null +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -0,0 +1,204 @@ +# 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. + +""" +Test the MSVC_TOOLSET_VERSION construction variable. +""" + +import sys +import TestSCons + +test = TestSCons.TestSCons() + +if sys.platform != 'win32': + test.skip_test("Not win32 platform. Skipping test\n") + +test.skip_if_not_msvc() + +import textwrap +from collections import namedtuple + +from SCons.Tool.MSCommon.vc import ( + get_installed_vcs, + get_msvc_version_numeric, +) + +MSVC_VERSION = namedtuple('MSVCVersion', [ + 'msvc_version', + 'msvc_verstr', + 'msvc_vernum', +]) + +def process_version(msvc_version): + msvc_verstr = get_msvc_version_numeric(msvc_version) + msvc_vernum = float(msvc_verstr) + return MSVC_VERSION(msvc_version, msvc_verstr, msvc_vernum) + +installed_versions = [process_version(msvc_version) for msvc_version in get_installed_vcs()] + +default = installed_versions[0] + +if default.msvc_vernum >= 14.1: + # VS2017 and later for toolset argument + + # msvc_version as toolset version + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(default.msvc_verstr)) + )) + test.run(arguments='-Q -s', stdout='') + + # msvc_version as toolset version using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) + """.format(default.msvc_verstr) + )) + test.run(arguments='-Q -s', stdout='') + + # error toolset version and script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) + """.format(repr(default.msvc_verstr), default.msvc_verstr) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS='-vcvars_ver={}':".format( + repr(default.msvc_verstr), default.msvc_verstr + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # msvc_toolset_version does not exist (hopefully) + missing_toolset_version = default.msvc_verstr + '9.99999' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(missing_toolset_version)) + )) + expect = r"^.*MSVCToolsetVersionNotFound: MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}.+".format( + repr(missing_toolset_version), repr(default.msvc_version) + ) + test.run(arguments='-Q -s', status=2, stderr=expect, match=TestSCons.match_re_dotall) + + # msvc_toolset_version is invalid (format) + invalid_toolset_version = default.msvc_verstr + '9.99999.99999' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) format is not supported:".format( + repr(invalid_toolset_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # msvc_toolset_version is invalid (version greater than msvc version) + invalid_toolset_vernum = default.msvc_vernum + 0.1 + invalid_toolset_version = str(invalid_toolset_vernum) + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION:".format( + repr(invalid_toolset_version), repr(invalid_toolset_version), repr(default.msvc_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # msvc_toolset_version is invalid (version less than 14.0) + invalid_toolset_version = '12.0' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < '14.0' VS2015:".format( + repr(invalid_toolset_version), repr(invalid_toolset_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # 14.0 toolset is invalid (toolset version != 14.0) + invalid_toolset_version = '14.00.00001' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0':".format( + repr(invalid_toolset_version), repr(invalid_toolset_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + +unsupported_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] +if unsupported_versions: + # VS2015 and earlier for toolset argument error + + unsupported = unsupported_versions[0] + + # must be VS2017 or later + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(unsupported.msvc_verstr)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.1' VS2017:".format( + repr(unsupported.msvc_version), repr(unsupported.msvc_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + +unsupported_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] +if unsupported_versions: + # VS2013 and earlier for script argument error + + unsupported = unsupported_versions[0] + + # must be VS2015 or later for MSVC_SCRIPT_ARGS + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) + """.format(repr(unsupported.msvc_version), unsupported.msvc_verstr) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ('-vcvars_ver={}') constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format( + unsupported.msvc_verstr, repr(unsupported.msvc_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + +test.pass_test() + From a75614c7726481e98ab1dc2d52f02b17499bb79a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 7 Jul 2022 08:12:34 -0400 Subject: [PATCH 057/108] Return empty list instead of None in msvc_toolset_versions. Expand msvc toolset version tests. --- SCons/Tool/MSCommon/vc.py | 4 +- test/MSVC/MSVC_TOOLSET_VERSION.py | 298 ++++++++++++++++++------------ 2 files changed, 182 insertions(+), 120 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index e05f397793..68b8f0891d 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1368,12 +1368,12 @@ def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): vc_dir = find_vc_pdir(env, msvc_version) if not vc_dir: debug('VC folder not found for version %s', repr(msvc_version)) - return + return rval rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs) return rval -def msvc_query_version_toolset(version, prefer_newest=True): +def msvc_query_version_toolset(version=None, prefer_newest=True): """ Returns an msvc version and a toolset version given a version specification. diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index 43192af634..30dd3447f1 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -56,149 +56,211 @@ def process_version(msvc_version): installed_versions = [process_version(msvc_version) for msvc_version in get_installed_vcs()] -default = installed_versions[0] +default_version = installed_versions[0] -if default.msvc_vernum >= 14.1: +GE_VS2017_versions = [v for v in installed_versions if v.msvc_vernum >= 14.1] +LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] +LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] + +if GE_VS2017_versions: # VS2017 and later for toolset argument - # msvc_version as toolset version - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(default.msvc_verstr)) - )) - test.run(arguments='-Q -s', stdout='') + for supported in GE_VS2017_versions: - # msvc_version as toolset version using script argument - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) - """.format(default.msvc_verstr) - )) - test.run(arguments='-Q -s', stdout='') + # msvc_version as toolset version + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(supported.msvc_verstr)) + )) + test.run(arguments='-Q -s', stdout='') - # error toolset version and script argument - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) - """.format(repr(default.msvc_verstr), default.msvc_verstr) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS='-vcvars_ver={}':".format( - repr(default.msvc_verstr), default.msvc_verstr - ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + # msvc_version as toolset version using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) + """.format(repr(supported.msvc_version), supported.msvc_verstr) + )) + test.run(arguments='-Q -s', stdout='') - # msvc_toolset_version does not exist (hopefully) - missing_toolset_version = default.msvc_verstr + '9.99999' - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(missing_toolset_version)) - )) - expect = r"^.*MSVCToolsetVersionNotFound: MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}.+".format( - repr(missing_toolset_version), repr(default.msvc_version) - ) - test.run(arguments='-Q -s', status=2, stderr=expect, match=TestSCons.match_re_dotall) + # error toolset version and script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) + """.format(repr(supported.msvc_version), repr(supported.msvc_verstr), supported.msvc_verstr) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS='-vcvars_ver={}':".format( + repr(supported.msvc_verstr), supported.msvc_verstr + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) - # msvc_toolset_version is invalid (format) - invalid_toolset_version = default.msvc_verstr + '9.99999.99999' - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(invalid_toolset_version)) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) format is not supported:".format( - repr(invalid_toolset_version) - ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + # msvc_toolset_version does not exist (hopefully) + missing_toolset_version = supported.msvc_verstr + '9.99999' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(missing_toolset_version)) + )) + expect = r"^.*MSVCToolsetVersionNotFound: MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}.+".format( + repr(missing_toolset_version), repr(supported.msvc_version) + ) + test.run(arguments='-Q -s', status=2, stderr=expect, match=TestSCons.match_re_dotall) - # msvc_toolset_version is invalid (version greater than msvc version) - invalid_toolset_vernum = default.msvc_vernum + 0.1 - invalid_toolset_version = str(invalid_toolset_vernum) - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(invalid_toolset_version)) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION:".format( - repr(invalid_toolset_version), repr(invalid_toolset_version), repr(default.msvc_version) - ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + # msvc_toolset_version is invalid (format) + invalid_toolset_version = supported.msvc_verstr + '9.99999.99999' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) format is not supported:".format( + repr(invalid_toolset_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) - # msvc_toolset_version is invalid (version less than 14.0) - invalid_toolset_version = '12.0' - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(invalid_toolset_version)) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < '14.0' VS2015:".format( - repr(invalid_toolset_version), repr(invalid_toolset_version) - ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + # msvc_toolset_version is invalid (version greater than msvc version) + invalid_toolset_vernum = round(supported.msvc_vernum + 0.1, 1) + invalid_toolset_version = str(invalid_toolset_vernum) + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION:".format( + repr(invalid_toolset_version), repr(invalid_toolset_version), repr(supported.msvc_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # msvc_toolset_version is invalid (version less than 14.0) + invalid_toolset_version = '12.0' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < '14.0' VS2015:".format( + repr(invalid_toolset_version), repr(invalid_toolset_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # 14.0 toolset is invalid (toolset version != 14.0) + invalid_toolset_version = '14.00.00001' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(invalid_toolset_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0':".format( + repr(invalid_toolset_version), repr(invalid_toolset_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + if supported == default_version: + msvc_version_list = ['None', repr(supported.msvc_version)] + else: + msvc_version_list = [repr(supported.msvc_version)] + + for test_version in msvc_version_list: + + # msvc toolsets detected + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_toolset_versions + DefaultEnvironment(tools=[]) + for full in (True, False): + for sxs in (True, False): + toolsets = msvc_toolset_versions(msvc_version={}, full=full, sxs=sxs) + """.format(test_version) + )) + test.run(arguments='-Q -s', stdout='') + + # msvc query version toolset + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_query_version_toolset + DefaultEnvironment(tools=[]) + for prefer_newest in (True, False): + msvc_version, msvc_toolset_version = msvc_query_version_toolset(version={}, prefer_newest=prefer_newest) + """.format(test_version) + )) + test.run(arguments='-Q -s', stdout='') - # 14.0 toolset is invalid (toolset version != 14.0) - invalid_toolset_version = '14.00.00001' + # msvc_version is invalid (version greater than default msvc_version) + invalid_msvc_vernum = round(default_version.msvc_vernum + 0.1, 1) + invalid_msvc_version = str(invalid_msvc_vernum) test.write('SConstruct', textwrap.dedent( """ + from SCons.Tool.MSCommon import msvc_toolset_versions DefaultEnvironment(tools=[]) - env = Environment(MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(invalid_toolset_version)) + toolsets = msvc_toolset_versions(msvc_version={}, full=True, sxs=True) + """.format(repr(invalid_msvc_version)) )) test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0':".format( - repr(invalid_toolset_version), repr(invalid_toolset_version) + expect = "MSVCArgumentError: Unsupported msvc version {}:".format( + repr(invalid_msvc_version) ) test.fail_test(test.stderr().split('\n')[0].strip() != expect) -unsupported_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] -if unsupported_versions: +if LT_VS2017_versions: # VS2015 and earlier for toolset argument error - unsupported = unsupported_versions[0] + for unsupported in LT_VS2017_versions: - # must be VS2017 or later - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(unsupported.msvc_version), repr(unsupported.msvc_verstr)) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.1' VS2017:".format( - repr(unsupported.msvc_version), repr(unsupported.msvc_version) - ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + # must be VS2017 or later + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(unsupported.msvc_verstr)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.1' VS2017:".format( + repr(unsupported.msvc_verstr), repr(unsupported.msvc_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) + + # msvc_toolset_versions returns None for versions that don't support toolsets + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_toolset_versions + DefaultEnvironment(tools=[]) + toolsets = msvc_toolset_versions(msvc_version={}, full=True, sxs=True) + if toolsets is not None: + raise RuntimeError("Expected toolsets==None") + """.format(repr(unsupported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout='') -unsupported_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] -if unsupported_versions: +if LT_VS2015_versions: # VS2013 and earlier for script argument error - unsupported = unsupported_versions[0] + for unsupported in LT_VS2015_versions: - # must be VS2015 or later for MSVC_SCRIPT_ARGS - test.write('SConstruct', textwrap.dedent( - """ - DefaultEnvironment(tools=[]) - env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) - """.format(repr(unsupported.msvc_version), unsupported.msvc_verstr) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ('-vcvars_ver={}') constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format( - unsupported.msvc_verstr, repr(unsupported.msvc_version) - ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + # must be VS2015 or later for MSVC_SCRIPT_ARGS + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_ver={}', tools=['msvc']) + """.format(repr(unsupported.msvc_version), unsupported.msvc_verstr) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ('-vcvars_ver={}') constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format( + unsupported.msvc_verstr, repr(unsupported.msvc_version) + ) + test.fail_test(test.stderr().split('\n')[0].strip() != expect) test.pass_test() From e8faf157450ec575d743108624645bb8207c4a00 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 7 Jul 2022 08:40:13 -0400 Subject: [PATCH 058/108] Fix invalid msvc version test. --- test/MSVC/MSVC_TOOLSET_VERSION.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index 30dd3447f1..2e59d469e0 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -198,9 +198,8 @@ def process_version(msvc_version): )) test.run(arguments='-Q -s', stdout='') - # msvc_version is invalid (version greater than default msvc_version) - invalid_msvc_vernum = round(default_version.msvc_vernum + 0.1, 1) - invalid_msvc_version = str(invalid_msvc_vernum) + # msvc_version is invalid + invalid_msvc_version = '12.9' test.write('SConstruct', textwrap.dedent( """ from SCons.Tool.MSCommon import msvc_toolset_versions From 88c534e242abcb17c721ceffd976397cf8d4c097 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 7 Jul 2022 13:41:07 -0400 Subject: [PATCH 059/108] Minor update to msvc exceptions and rework new unit tests. --- SCons/Tool/MSCommon/MSVC/Exceptions.py | 15 ++- SCons/Tool/MSCommon/MSVC/Policy.py | 5 +- SCons/Tool/MSCommon/__init__.py | 1 + SCons/Tool/MSCommon/vc.py | 5 +- test/MSVC/MSVC_NOTFOUND_POLICY.py | 138 ++++++++++++++++--------- test/MSVC/MSVC_SCRIPTERROR_POLICY.py | 80 ++++++++------ test/MSVC/MSVC_TOOLSET_VERSION.py | 27 +++-- 7 files changed, 165 insertions(+), 106 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Exceptions.py b/SCons/Tool/MSCommon/MSVC/Exceptions.py index a12b3c6b88..7b24a2b4b4 100644 --- a/SCons/Tool/MSCommon/MSVC/Exceptions.py +++ b/SCons/Tool/MSCommon/MSVC/Exceptions.py @@ -25,27 +25,32 @@ Exceptions for Microsoft Visual C/C++. """ +# reminder: add exceptions to MSCommon if necessary + class VisualCException(Exception): pass class MSVCInternalError(VisualCException): pass +class MSVCUserError(VisualCException): + pass + class MSVCScriptExecutionError(VisualCException): pass -class MSVCVersionNotFound(VisualCException): +class MSVCVersionNotFound(MSVCUserError): pass -class MSVCSDKVersionNotFound(VisualCException): +class MSVCSDKVersionNotFound(MSVCUserError): pass -class MSVCToolsetVersionNotFound(VisualCException): +class MSVCToolsetVersionNotFound(MSVCUserError): pass -class MSVCSpectreLibsNotFound(VisualCException): +class MSVCSpectreLibsNotFound(MSVCUserError): pass -class MSVCArgumentError(VisualCException): +class MSVCArgumentError(MSVCUserError): pass diff --git a/SCons/Tool/MSCommon/MSVC/Policy.py b/SCons/Tool/MSCommon/MSVC/Policy.py index 9b7025e9ca..fe8da3156b 100644 --- a/SCons/Tool/MSCommon/MSVC/Policy.py +++ b/SCons/Tool/MSCommon/MSVC/Policy.py @@ -45,6 +45,7 @@ ) from .Exceptions import ( + MSVCArgumentError, MSVCVersionNotFound, MSVCScriptExecutionError, ) @@ -141,7 +142,7 @@ def _msvc_notfound_policy_lookup(symbol): repr(symbol), ', '.join([repr(s) for s in MSVC_NOTFOUND_POLICY_EXTERNAL.keys()]) ) - raise ValueError(err_msg) + raise MSVCArgumentError(err_msg) return notfound_policy_def @@ -225,7 +226,7 @@ def _msvc_scripterror_policy_lookup(symbol): repr(symbol), ', '.join([repr(s) for s in MSVC_SCRIPTERROR_POLICY_EXTERNAL.keys()]) ) - raise ValueError(err_msg) + raise MSVCArgumentError(err_msg) return scripterror_policy_def diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 8396e1727b..0640bcefd2 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -58,6 +58,7 @@ from .MSVC.Exceptions import VisualCException # noqa: F401 from .MSVC.Exceptions import MSVCInternalError # noqa: F401 +from .MSVC.Exceptions import MSVCUserError # noqa: F401 from .MSVC.Exceptions import MSVCScriptExecutionError # noqa: F401 from .MSVC.Exceptions import MSVCVersionNotFound # noqa: F401 from .MSVC.Exceptions import MSVCSDKVersionNotFound # noqa: F401 diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 68b8f0891d..27a254b681 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -64,6 +64,7 @@ from .MSVC.Exceptions import ( VisualCException, + MSVCUserError, MSVCArgumentError, MSVCToolsetVersionNotFound, ) @@ -86,10 +87,10 @@ class NoVersionFound(VisualCException): class BatchFileExecutionError(VisualCException): pass -class MSVCScriptNotFound(VisualCException): +class MSVCScriptNotFound(MSVCUserError): pass -class MSVCUseSettingsError(VisualCException): +class MSVCUseSettingsError(MSVCUserError): pass diff --git a/test/MSVC/MSVC_NOTFOUND_POLICY.py b/test/MSVC/MSVC_NOTFOUND_POLICY.py index b6084db34f..e9ed4a6370 100644 --- a/test/MSVC/MSVC_NOTFOUND_POLICY.py +++ b/test/MSVC/MSVC_NOTFOUND_POLICY.py @@ -25,75 +25,117 @@ Test the msvc not found policy construction variable and functions. """ -import sys import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - test.skip_test("Not win32 platform. Skipping test\n") - test.skip_if_not_msvc() +import textwrap + # Test global functions with valid symbols -test.write('SConstruct', """\ -from SCons.Tool.MSCommon import msvc_set_notfound_policy -from SCons.Tool.MSCommon import msvc_get_notfound_policy -DefaultEnvironment(tools=[]) -for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: - for policy in [symbol, symbol.upper(), symbol.lower()]: - old_policy = msvc_set_notfound_policy(policy) - cur_policy = msvc_get_notfound_policy() -if msvc_set_notfound_policy(None) != msvc_get_notfound_policy(): - raise RuntimeError() -""") +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_notfound_policy + from SCons.Tool.MSCommon import msvc_get_notfound_policy + DefaultEnvironment(tools=[]) + for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + old_policy = msvc_set_notfound_policy(policy) + cur_policy = msvc_get_notfound_policy() + if msvc_set_notfound_policy(None) != msvc_get_notfound_policy(): + raise RuntimeError() + """ +)) test.run(arguments='-Q -s', stdout='') # Test global function with invalid symbol -test.write('SConstruct', """\ -from SCons.Tool.MSCommon import msvc_set_notfound_policy -DefaultEnvironment(tools=[]) -msvc_set_notfound_policy('Undefined') -""") -test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_NOTFOUND_POLICY.+", match=TestSCons.match_re_dotall) +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_notfound_policy + DefaultEnvironment(tools=[]) + msvc_set_notfound_policy('Undefined') + """ +)) +test.run(arguments='-Q -s', status=2, stderr=None) +expect = "MSVCArgumentError: Value specified for MSVC_NOTFOUND_POLICY is not supported: 'Undefined'." +test.must_contain_all(test.stderr(), expect) # Test construction variable with valid symbols -test.write('SConstruct', """\ -env_list = [] -DefaultEnvironment(tools=[]) -for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: - for policy in [symbol, symbol.upper(), symbol.lower()]: - env = Environment(MSVC_NOTFOUND_POLICY=policy, tools=['msvc']) - env_list.append(env) -""") +test.write('SConstruct', textwrap.dedent( + """ + env_list = [] + DefaultEnvironment(tools=[]) + for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + env = Environment(MSVC_NOTFOUND_POLICY=policy, tools=['msvc']) + env_list.append(env) + """ +)) test.run(arguments='-Q -s', stdout='') # Test construction variable with invalid symbol -test.write('SConstruct', """\ -env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Undefined', tools=['msvc']) -""") -test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_NOTFOUND_POLICY.+", match=TestSCons.match_re_dotall) +test.write('SConstruct', textwrap.dedent( + """ + env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Undefined', tools=['msvc']) + """ +)) +test.run(arguments='-Q -s', status=2, stderr=None) +expect = "MSVCArgumentError: Value specified for MSVC_NOTFOUND_POLICY is not supported: 'Undefined'." +test.must_contain_all(test.stderr(), expect) + +# Test environment construction with global policy +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_notfound_policy + msvc_set_notfound_policy('Exception') + env = Environment(MSVC_VERSION='12.9', tools=['msvc']) + """ +)) +test.run(arguments='-Q -s', status=2, stderr=None) +expect = "MSVCVersionNotFound: MSVC version '12.9' was not found." +test.must_contain_all(test.stderr(), expect) + +# Test environment construction with construction variable +test.write('SConstruct', textwrap.dedent( + """ + env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Error', tools=['msvc']) + """ +)) +test.run(arguments='-Q -s', status=2, stderr=None) +expect = "MSVCVersionNotFound: MSVC version '12.9' was not found." +test.must_contain_all(test.stderr(), expect) # Test environment construction with global policy -test.write('SConstruct', """\ -from SCons.Tool.MSCommon import msvc_set_notfound_policy -msvc_set_notfound_policy('Exception') -env = Environment(MSVC_VERSION='12.9', tools=['msvc']) -""") -test.run(arguments='-Q -s', status=2, stderr=r"^.* MSVC version '12.9' was not found.+", match=TestSCons.match_re_dotall) +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_notfound_policy + msvc_set_notfound_policy('Warning') + env = Environment(MSVC_VERSION='12.9', tools=['msvc']) + """ +)) +test.run(arguments="-Q -s --warn=visual-c-missing .", status=0, stderr=None) +expect = "scons: warning: MSVC version '12.9' was not found." +test.must_contain_all(test.stderr(), expect) # Test environment construction with construction variable -test.write('SConstruct', """\ -env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Error', tools=['msvc']) -""") -test.run(arguments='-Q -s', status=2, stderr=r"^.* MSVC version '12.9' was not found.+", match=TestSCons.match_re_dotall) +test.write('SConstruct', textwrap.dedent( + """ + env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Warning', tools=['msvc']) + """ +)) +test.run(arguments="-Q -s --warn=visual-c-missing .", status=0, stderr=None) +expect = "scons: warning: MSVC version '12.9' was not found." +test.must_contain_all(test.stderr(), expect) # Test environment construction with construction variable (override global) -test.write('SConstruct', """\ -from SCons.Tool.MSCommon import msvc_set_notfound_policy -msvc_set_notfound_policy('Exception') -env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Ignore', tools=['msvc']) -""") +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_notfound_policy + msvc_set_notfound_policy('Exception') + env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY='Ignore', tools=['msvc']) + """ +)) test.run(arguments='-Q -s', stdout='') test.pass_test() diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py index a02a7fc160..b616db805f 100644 --- a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py +++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py @@ -25,14 +25,10 @@ Test the msvc script error policy construction variable and functions. """ -import sys import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - test.skip_test("Not win32 platform. Skipping test\n") - test.skip_if_not_msvc() import textwrap @@ -45,36 +41,44 @@ default_msvc_vernum = float(get_msvc_version_numeric(get_installed_vcs()[0])) # Test global functions with valid symbols -test.write('SConstruct', """\ -from SCons.Tool.MSCommon import msvc_set_scripterror_policy -from SCons.Tool.MSCommon import msvc_get_scripterror_policy -DefaultEnvironment(tools=[]) -for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: - for policy in [symbol, symbol.upper(), symbol.lower()]: - old_policy = msvc_set_scripterror_policy(policy) - cur_policy = msvc_get_scripterror_policy() -if msvc_set_scripterror_policy(None) != msvc_get_scripterror_policy(): - raise RuntimeError() -""") +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + from SCons.Tool.MSCommon import msvc_get_scripterror_policy + DefaultEnvironment(tools=[]) + for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + old_policy = msvc_set_scripterror_policy(policy) + cur_policy = msvc_get_scripterror_policy() + if msvc_set_scripterror_policy(None) != msvc_get_scripterror_policy(): + raise RuntimeError() + """ +)) test.run(arguments='-Q -s', stdout='') # Test global function with invalid symbol -test.write('SConstruct', """\ -from SCons.Tool.MSCommon import msvc_set_scripterror_policy -DefaultEnvironment(tools=[]) -msvc_set_scripterror_policy('Undefined') -""") -test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_SCRIPTERROR_POLICY.+", match=TestSCons.match_re_dotall) +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + DefaultEnvironment(tools=[]) + msvc_set_scripterror_policy('Undefined') + """ +)) +test.run(arguments='-Q -s', status=2, stderr=None) +expect = "MSVCArgumentError: Value specified for MSVC_SCRIPTERROR_POLICY is not supported: 'Undefined'." +test.must_contain_all(test.stderr(), expect) # Test construction variable with valid symbols -test.write('SConstruct', """\ -env_list = [] -DefaultEnvironment(tools=[]) -for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: - for policy in [symbol, symbol.upper(), symbol.lower()]: - env = Environment(MSVC_SCRIPTERROR_POLICY=policy, tools=['msvc']) - env_list.append(env) -""") +test.write('SConstruct', textwrap.dedent( + """ + env_list = [] + DefaultEnvironment(tools=[]) + for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: + for policy in [symbol, symbol.upper(), symbol.lower()]: + env = Environment(MSVC_SCRIPTERROR_POLICY=policy, tools=['msvc']) + env_list.append(env) + """ +)) test.run(arguments='-Q -s', stdout='') if default_msvc_vernum >= 14.1: @@ -87,7 +91,9 @@ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Undefined', tools=['msvc']) """ )) - test.run(arguments='-Q -s', status=2, stderr=r"^.* Value specified for MSVC_SCRIPTERROR_POLICY.+", match=TestSCons.match_re_dotall) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: Value specified for MSVC_SCRIPTERROR_POLICY is not supported: 'Undefined'." + test.must_contain_all(test.stderr(), expect) # Test environment construction with construction variable (override global) test.write('SConstruct', textwrap.dedent( @@ -109,7 +115,9 @@ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], tools=['msvc']) """ )) - test.run(arguments='-Q -s', status=2, stderr=r"^.* vc script errors detected.+", match=TestSCons.match_re_dotall) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCScriptExecutionError: vc script errors detected:" + test.must_contain_all(test.stderr(), expect) # Test environment construction with construction variable test.write('SConstruct', textwrap.dedent( @@ -118,7 +126,9 @@ env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY='Error', tools=['msvc']) """ )) - test.run(arguments='-Q -s', status=2, stderr=r"^.* vc script errors detected.+", match=TestSCons.match_re_dotall) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCScriptExecutionError: vc script errors detected:" + test.must_contain_all(test.stderr(), expect) # Test environment construction with global policy test.write('SConstruct', textwrap.dedent( @@ -130,7 +140,8 @@ """ )) test.run(arguments='-Q -s', status=0, stderr=None) - test.fail_test(test.stderr().lstrip().split('\n')[0].strip() != "scons: warning: vc script errors detected:") + expect = "scons: warning: vc script errors detected:" + test.must_contain_all(test.stderr(), expect) # Test environment construction with construction variable test.write('SConstruct', textwrap.dedent( @@ -140,7 +151,8 @@ """ )) test.run(arguments='-Q -s', status=0, stderr=None) - test.fail_test(test.stderr().lstrip().split('\n')[0].strip() != "scons: warning: vc script errors detected:") + expect = "scons: warning: vc script errors detected:" + test.must_contain_all(test.stderr(), expect) test.pass_test() diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index 2e59d469e0..ac6cd62151 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -25,14 +25,10 @@ Test the MSVC_TOOLSET_VERSION construction variable. """ -import sys import TestSCons test = TestSCons.TestSCons() -if sys.platform != 'win32': - test.skip_test("Not win32 platform. Skipping test\n") - test.skip_if_not_msvc() import textwrap @@ -60,7 +56,7 @@ def process_version(msvc_version): GE_VS2017_versions = [v for v in installed_versions if v.msvc_vernum >= 14.1] LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] -LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] +LT_VS2015_versions = [v for v in LT_VS2017_versions if v.msvc_vernum < 14.0] if GE_VS2017_versions: # VS2017 and later for toolset argument @@ -96,7 +92,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: multiple toolset version declarations: MSVC_TOOLSET_VERSION={} and MSVC_SCRIPT_ARGS='-vcvars_ver={}':".format( repr(supported.msvc_verstr), supported.msvc_verstr ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) # msvc_toolset_version does not exist (hopefully) missing_toolset_version = supported.msvc_verstr + '9.99999' @@ -106,10 +102,11 @@ def process_version(msvc_version): env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) """.format(repr(supported.msvc_version), repr(missing_toolset_version)) )) - expect = r"^.*MSVCToolsetVersionNotFound: MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}.+".format( + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCToolsetVersionNotFound: MSVC_TOOLSET_VERSION {} not found for MSVC_VERSION {}:".format( repr(missing_toolset_version), repr(supported.msvc_version) ) - test.run(arguments='-Q -s', status=2, stderr=expect, match=TestSCons.match_re_dotall) + test.must_contain_all(test.stderr(), expect) # msvc_toolset_version is invalid (format) invalid_toolset_version = supported.msvc_verstr + '9.99999.99999' @@ -123,7 +120,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) format is not supported:".format( repr(invalid_toolset_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) # msvc_toolset_version is invalid (version greater than msvc version) invalid_toolset_vernum = round(supported.msvc_vernum + 0.1, 1) @@ -138,7 +135,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION:".format( repr(invalid_toolset_version), repr(invalid_toolset_version), repr(supported.msvc_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) # msvc_toolset_version is invalid (version less than 14.0) invalid_toolset_version = '12.0' @@ -152,7 +149,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < '14.0' VS2015:".format( repr(invalid_toolset_version), repr(invalid_toolset_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) # 14.0 toolset is invalid (toolset version != 14.0) invalid_toolset_version = '14.00.00001' @@ -166,7 +163,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0':".format( repr(invalid_toolset_version), repr(invalid_toolset_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) if supported == default_version: msvc_version_list = ['None', repr(supported.msvc_version)] @@ -211,7 +208,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: Unsupported msvc version {}:".format( repr(invalid_msvc_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) if LT_VS2017_versions: # VS2015 and earlier for toolset argument error @@ -229,7 +226,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.1' VS2017:".format( repr(unsupported.msvc_verstr), repr(unsupported.msvc_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) # msvc_toolset_versions returns None for versions that don't support toolsets test.write('SConstruct', textwrap.dedent( @@ -259,7 +256,7 @@ def process_version(msvc_version): expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ('-vcvars_ver={}') constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format( unsupported.msvc_verstr, repr(unsupported.msvc_version) ) - test.fail_test(test.stderr().split('\n')[0].strip() != expect) + test.must_contain_all(test.stderr(), expect) test.pass_test() From 737abe7d169c265f53d5cf8f0629b5d66eca92d4 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 7 Jul 2022 15:47:31 -0400 Subject: [PATCH 060/108] Add utility function for msvc_version components. Add convenience function to return list of installed msvc version component tuples for the test suite. --- SCons/Tool/MSCommon/MSVC/Util.py | 63 ++++++++++++++++++++++++++-- SCons/Tool/MSCommon/vc.py | 6 +++ test/MSVC/MSVC_SCRIPTERROR_POLICY.py | 11 +++-- test/MSVC/MSVC_TOOLSET_VERSION.py | 19 +-------- 4 files changed, 72 insertions(+), 27 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 11b80ec4ea..f7e9091d78 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -113,14 +113,14 @@ def get_msvc_version_prefix(version): rval = '' return rval -VERSION_ELEMENTS_DEFINITION = namedtuple('VersionElements', [ +_VERSION_ELEMENTS_DEFINITION = namedtuple('VersionElements', [ 'vc_version_numstr', # msvc version numeric string ('14.1') 'vc_toolset_numstr', # toolset version numeric string ('14.16.27023') 'vc_version_suffix', # component type ('Exp') 'msvc_version', # msvc version ('14.1Exp') ]) -re_version_elements = re.compile(r'^(?P(?P[1-9][0-9]?[.][0-9])[0-9.]*)(?P.*)$') +re_version_elements = re.compile(r'^(?P(?P[1-9][0-9]?[.][0-9])[0-9.]*)(?P[A-Z]+)*$', re.IGNORECASE) def get_version_elements(version): """ @@ -140,9 +140,9 @@ def get_version_elements(version): vc_version_numstr = m.group('msvc_version') vc_toolset_numstr = m.group('version') - vc_version_suffix = m.group('suffix') + vc_version_suffix = m.group('suffix') if m.group('suffix') else '' - version_elements_def = VERSION_ELEMENTS_DEFINITION( + version_elements_def = _VERSION_ELEMENTS_DEFINITION( vc_version_numstr = vc_version_numstr, vc_toolset_numstr = vc_toolset_numstr, vc_version_suffix = vc_version_suffix, @@ -151,3 +151,58 @@ def get_version_elements(version): return version_elements_def +# test suite convenience function + +_MSVC_VERSION_COMPONENTS = namedtuple('MSVCVersionComponents', [ + 'msvc_version', # msvc version (e.g., '14.1Exp') + 'msvc_verstr', # msvc version numeric string (e.g., '14.1') + 'msvc_suffix', # msvc version component type (e.g., 'Exp') + 'msvc_vernum', # msvc version floating point number (e.g, 14.1) + 'msvc_major', # msvc major version integer number (e.g., 14) + 'msvc_minor', # msvc minor version integer number (e.g., 1) +]) + +re_msvc_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])(?P[A-Z]+)*$', re.IGNORECASE) + +def get_msvc_version_components(vcver): + """ + Get a tuple of msvc version components. + + Tuple fields: + msvc_version: msvc version (e.g., '14.1Exp') + msvc_verstr: msvc version numeric string (e.g., '14.1') + msvc_suffix: msvc version component type (e.g., 'Exp') + msvc_vernum: msvc version floating point number (e.g., 14.1) + msvc_major: msvc major version integer number (e.g., 14) + msvc_minor: msvc minor version integer number (e.g., 1) + + Args: + vcver: str + msvc version specification + + Returns: + None or MSVCVersionComponents namedtuple: + """ + + m = re_msvc_version.match(vcver) + if not m: + return None + + msvc_version = vcver + msvc_verstr = m.group('msvc_version') + msvc_suffix = m.group('suffix') if m.group('suffix') else '' + msvc_vernum = float(msvc_verstr) + + msvc_major, msvc_minor = [int(x) for x in msvc_verstr.split('.')] + + msvc_version_components_def = _MSVC_VERSION_COMPONENTS( + msvc_vernum = msvc_vernum, + msvc_verstr = msvc_verstr, + msvc_suffix = msvc_suffix, + msvc_version = msvc_version, + msvc_major = msvc_major, + msvc_minor = msvc_minor, + ) + + return msvc_version_components_def + diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 27a254b681..7fed093892 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -976,6 +976,12 @@ def get_default_installed_msvc(env=None): debug('msvc_version=%s', repr(msvc_version)) return msvc_version +def get_installed_vcs_components(env=None): + """Test suite convenience function: return list of installed msvc version component tuples""" + vcs = get_installed_vcs(env) + msvc_version_component_defs = [MSVC.Util.get_msvc_version_components(vcver) for vcver in vcs] + return msvc_version_component_defs + # Running these batch files isn't cheap: most of the time spent in # msvs.generate() is due to vcvars*.bat. In a build that uses "tools='msvs'" # in multiple environments, for example: diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py index b616db805f..977dab8f0b 100644 --- a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py +++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py @@ -33,12 +33,11 @@ import textwrap -from SCons.Tool.MSCommon.vc import ( - get_installed_vcs, - get_msvc_version_numeric, -) +from SCons.Tool.MSCommon.vc import get_installed_vcs_components -default_msvc_vernum = float(get_msvc_version_numeric(get_installed_vcs()[0])) +installed_versions = get_installed_vcs_components() + +default_version = installed_versions[0] # Test global functions with valid symbols test.write('SConstruct', textwrap.dedent( @@ -81,7 +80,7 @@ )) test.run(arguments='-Q -s', stdout='') -if default_msvc_vernum >= 14.1: +if default_version.msvc_vernum >= 14.1: # Need VS2017 or later for MSVC_SCRIPT_ARGS # Test environment construction with construction variable (invalid) diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index ac6cd62151..ce6e251e98 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -32,25 +32,10 @@ test.skip_if_not_msvc() import textwrap -from collections import namedtuple -from SCons.Tool.MSCommon.vc import ( - get_installed_vcs, - get_msvc_version_numeric, -) +from SCons.Tool.MSCommon.vc import get_installed_vcs_components -MSVC_VERSION = namedtuple('MSVCVersion', [ - 'msvc_version', - 'msvc_verstr', - 'msvc_vernum', -]) - -def process_version(msvc_version): - msvc_verstr = get_msvc_version_numeric(msvc_version) - msvc_vernum = float(msvc_verstr) - return MSVC_VERSION(msvc_version, msvc_verstr, msvc_vernum) - -installed_versions = [process_version(msvc_version) for msvc_version in get_installed_vcs()] +installed_versions = get_installed_vcs_components() default_version = installed_versions[0] From 3cc119751accbc87228e2725404dc5361db57209 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Jul 2022 07:09:33 -0400 Subject: [PATCH 061/108] Additional policy tests for coverage. --- test/MSVC/MSVC_NOTFOUND_POLICY.py | 12 ++++++++++++ test/MSVC/MSVC_SCRIPTERROR_POLICY.py | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/test/MSVC/MSVC_NOTFOUND_POLICY.py b/test/MSVC/MSVC_NOTFOUND_POLICY.py index e9ed4a6370..d45999a76a 100644 --- a/test/MSVC/MSVC_NOTFOUND_POLICY.py +++ b/test/MSVC/MSVC_NOTFOUND_POLICY.py @@ -96,6 +96,18 @@ expect = "MSVCVersionNotFound: MSVC version '12.9' was not found." test.must_contain_all(test.stderr(), expect) +# Test environment construction with global policy and construction variable ignored +test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_notfound_policy + msvc_set_notfound_policy('Exception') + env = Environment(MSVC_VERSION='12.9', MSVC_NOTFOUND_POLICY=None, tools=['msvc']) + """ +)) +test.run(arguments='-Q -s', status=2, stderr=None) +expect = "MSVCVersionNotFound: MSVC version '12.9' was not found." +test.must_contain_all(test.stderr(), expect) + # Test environment construction with construction variable test.write('SConstruct', textwrap.dedent( """ diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py index 977dab8f0b..acc1a41800 100644 --- a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py +++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py @@ -118,6 +118,19 @@ expect = "MSVCScriptExecutionError: vc script errors detected:" test.must_contain_all(test.stderr(), expect) + # Test environment construction with global policy and construction variable ignored + test.write('SConstruct', textwrap.dedent( + """ + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + DefaultEnvironment(tools=[]) + msvc_set_scripterror_policy('Exception') + env = Environment(MSVC_SCRIPT_ARGS=['-thisdoesnotexist=somevalue'], MSVC_SCRIPTERROR_POLICY=None, tools=['msvc']) + """ + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCScriptExecutionError: vc script errors detected:" + test.must_contain_all(test.stderr(), expect) + # Test environment construction with construction variable test.write('SConstruct', textwrap.dedent( """ From 3bd4f4a33b65aab7811d2a875db9fd0a45feb6bf Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Jul 2022 09:26:47 -0400 Subject: [PATCH 062/108] Add MSVC_SDK_VERSION tests. --- test/MSVC/MSVC_NOTFOUND_POLICY.py | 2 +- test/MSVC/MSVC_SCRIPTERROR_POLICY.py | 2 +- test/MSVC/MSVC_SDK_VERSION.py | 238 +++++++++++++++++++++++++++ 3 files changed, 240 insertions(+), 2 deletions(-) create mode 100644 test/MSVC/MSVC_SDK_VERSION.py diff --git a/test/MSVC/MSVC_NOTFOUND_POLICY.py b/test/MSVC/MSVC_NOTFOUND_POLICY.py index d45999a76a..d123c02922 100644 --- a/test/MSVC/MSVC_NOTFOUND_POLICY.py +++ b/test/MSVC/MSVC_NOTFOUND_POLICY.py @@ -22,7 +22,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -Test the msvc not found policy construction variable and functions. +Test the MSVC_NOTFOUND_POLICY construction variable and functions. """ import TestSCons diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py index acc1a41800..4d23595872 100644 --- a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py +++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py @@ -22,7 +22,7 @@ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -Test the msvc script error policy construction variable and functions. +Test the MSVC_SCRIPTERROR_POLICY construction variable and functions. """ import TestSCons diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py new file mode 100644 index 0000000000..c0b0bc6aa8 --- /dev/null +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -0,0 +1,238 @@ +# 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. + +""" +Test the MSVC_SDK_VERSION construction variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + +import textwrap + +from SCons.Tool.MSCommon.vc import get_installed_vcs_components +from SCons.Tool.MSCommon import msvc_sdk_versions +from SCons.Tool.MSCommon import msvc_toolset_versions + +installed_versions = get_installed_vcs_components() + +default_version = installed_versions[0] + +GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0] +LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] + +default_sdk_versions_uwp = msvc_sdk_versions(msvc_version=None, msvc_uwp_app=True) +default_sdk_versions_def = msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False) + +have_140 = any([v.msvc_verstr == '14.0' for v in GE_VS2015_versions]) + +def version_major(version): + components = version.split('.') + if len(components) >= 2: + return components[0] + '.' + components[1][0] + if len(components) == 1: + return components[0] + '.0' + return version + +def version_major_list(version_list): + versions = [] + seen_major = set() + for version in version_list: + major = version_major(version) + if major in seen_major: + continue + versions.append(version) + seen_major.add(major) + return versions + +if GE_VS2015_versions: + + for supported in GE_VS2015_versions: + + sdk_versions_uwp = msvc_sdk_versions(msvc_version=supported.msvc_version, msvc_uwp_app=True) + sdk_versions_def = msvc_sdk_versions(msvc_version=supported.msvc_version, msvc_uwp_app=False) + + # find sdk version for each major SDK + sdk_versions = version_major_list(sdk_versions_def) + + for sdk_version in sdk_versions: + + # sdk version construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', stdout='') + + # sdk version script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', stdout='') + + # sdk version construction variable and script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(sdk_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: multiple sdk version declarations: MSVC_SDK_VERSION={} and MSVC_SCRIPT_ARGS={}:".format( + repr(sdk_version), repr(sdk_version) + ) + test.must_contain_all(test.stderr(), expect) + + # TODO: non-existent sdk version + + # sdk version is not supported + invalid_sdk_version = '9.1' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(invalid_sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SDK_VERSION ({}) is not supported:".format( + repr(invalid_sdk_version) + ) + test.must_contain_all(test.stderr(), expect) + + # sdk version not found + missing_sdk_version = '10.0.12345.6' + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(missing_sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCSDKVersionNotFound: MSVC_SDK_VERSION {} not found for platform type 'Desktop':".format( + repr(missing_sdk_version) + ) + test.must_contain_all(test.stderr(), expect) + + # platform contraints: 8.1 and UWP + if '8.1' in sdk_versions: + + if supported.msvc_vernum > 14.0: + + toolset_full_versions = msvc_toolset_versions(supported.msvc_version, full=True, sxs=False) + toolset_versions = version_major_list(toolset_full_versions) + + # toolset msvc_version != current msvc_version and toolset msvc_version != 14.0 + toolset_candidates = [v for v in toolset_versions if version_major(v) not in (supported.msvc_verstr, '14.0')] + toolset_version = toolset_candidates[0] if toolset_candidates else None + + # sdk version 8.1, UWP, and msvc_verson > VS2015 + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: MSVC_VERSION {} > '14.0' VS2015:".format( + repr(supported.msvc_version) + ) + test.must_contain_all(test.stderr(), expect) + + if toolset_version: + + # sdk version 8.1, UWP, and msvc_toolset_verson > VS2015 + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(toolset_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: toolset version {} > '14.0' VS2015:".format( + repr(toolset_version) + ) + test.must_contain_all(test.stderr(), expect) + + if have_140: + + # sdk version 8.1, UWP, and msvc_toolset_version > VS2015 + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_TOOLSET_VERSION='14.0', MSVC_UWP_APP=True, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', stdout='') + + elif supported.msvc_vernum == 14.0: + + # sdk version 8.1, UWP, and msvc_verson == VS2015 + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', stdout='') + +if LT_VS2015_versions: + + for unsupported in LT_VS2015_versions: + # must be VS2015 or later + + sdk_version = default_sdk_versions_def[0] if default_sdk_versions_def else '8.1' + + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format( + repr(sdk_version), repr(unsupported.msvc_version) + ) + test.must_contain_all(test.stderr(), expect) + + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(sdk_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < '14.0' VS2015:".format( + repr(sdk_version), repr(unsupported.msvc_version) + ) + test.must_contain_all(test.stderr(), expect) + +test.pass_test() + From cd8271771ae573fe601cdbcdffad301caafbf4ec Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Jul 2022 09:42:03 -0400 Subject: [PATCH 063/108] Fix sider issues. --- test/MSVC/MSVC_SDK_VERSION.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index c0b0bc6aa8..2ebd046a77 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -158,7 +158,7 @@ def version_major_list(version_list): """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(sdk_version)) + """.format(repr(supported.msvc_version)) )) test.run(arguments='-Q -s', status=2, stderr=None) expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: MSVC_VERSION {} > '14.0' VS2015:".format( @@ -173,8 +173,8 @@ def version_major_list(version_list): """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(toolset_version), repr(sdk_version)) - )) + """.format(repr(supported.msvc_version), repr(toolset_version)) + )) test.run(arguments='-Q -s', status=2, stderr=None) expect = "MSVCArgumentError: MSVC_SDK_VERSION ('8.1') and platform type ('UWP') constraint violation: toolset version {} > '14.0' VS2015:".format( repr(toolset_version) @@ -188,7 +188,7 @@ def version_major_list(version_list): """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_TOOLSET_VERSION='14.0', MSVC_UWP_APP=True, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(sdk_version)) + """.format(repr(supported.msvc_version)) )) test.run(arguments='-Q -s', stdout='') @@ -199,7 +199,7 @@ def version_major_list(version_list): """ DefaultEnvironment(tools=[]) env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION='8.1', MSVC_UWP_APP=True, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(sdk_version)) + """.format(repr(supported.msvc_version)) )) test.run(arguments='-Q -s', stdout='') From 6f92901cbcf2a6bcc2927199d196865d752db44d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Jul 2022 11:51:45 -0400 Subject: [PATCH 064/108] Disable cache for MSVC/MSVC_USE_SCRIPT_ARGS test. --- test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct | 3 +++ 1 file changed, 3 insertions(+) diff --git a/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct b/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct index 68ec4b068b..54d140e5b1 100644 --- a/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct +++ b/test/MSVC/MSVC_USE_SCRIPT_ARGS-fixture/SConstruct @@ -1,5 +1,8 @@ import os +if 'SCONS_CACHE_MSVC_CONFIG' in os.environ: + del os.environ['SCONS_CACHE_MSVC_CONFIG'] + os.environ['SCONS_MSCOMMON_DEBUG']='MSDEBUG_OUTPUT.log' DefaultEnvironment(tools=[]) From 23f63fc766f9e157bfe8642b8559983dde684dd3 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 8 Jul 2022 12:13:53 -0400 Subject: [PATCH 065/108] Update readme and changes for mscommon logger fix and msvc use script args fixture update. --- CHANGES.txt | 7 +++++++ RELEASE.txt | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/CHANGES.txt b/CHANGES.txt index c9aad4c8f5..baf9e68776 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -109,6 +109,13 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER version specification may be an msvc version or an msvc toolset version. This is a proxy for using an msvc toolset version to select an msvc instance. This function may be removed when an msvc toolset version is used during msvc instance selection. + - Modify the MSCommon logger configuration to be independent of the root logger. This fixes an issue + when multiple loggers are created and the MSCommon logger added computed fields to the root logger + that are not present in other logging instances. + - Modify the MSVC_USE_SCRIPT_ARGS test fixture to disable the msvc cache. This fixes an issue where + the MSVC_USE_SCRIPT_ARGS test for success relied on a debug log message that was not produced when + the msvc cache file exists and the test keys are already in the cache as the msvc script invocation + was bypassed. From William Deegan: - Fix check for unsupported Python version. It was broken. Also now the error message diff --git a/RELEASE.txt b/RELEASE.txt index ab258dff1b..e4f27d18dd 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -199,6 +199,13 @@ FIXES error caused when bypassing MSVC detection by specifying the MSVC 7.0 batch file directly. - lex: Fixed an issue with the lex tool where file arguments specified to either "--header-file=" or "--tables-file=" which included a space in the path to the file would be processed incorrectly +- Modify the MSCommon logger configuration to be independent of the root logger. This fixes an issue + when multiple loggers are created and the MSCommon logger added computed fields to the root logger + that are not present in other logging instances. +- Modify the MSVC_USE_SCRIPT_ARGS test fixture to disable the msvc cache. This fixes an issue where + the MSVC_USE_SCRIPT_ARGS test for success relied on a debug log message that was not produced when + the msvc cache file exists and the test keys are already in the cache as the msvc script invocation + was bypassed. - Suppress issuing a warning when there are no installed Visual Studio instances for the default tools configuration (issue #2813). When msvc is the default compiler because there are no compilers installed, a build may fail due to the cl.exe command not being recognized. At From 04a5dea1a00abd7ffdc4c274f000273ce6ff37db Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 9 Jul 2022 14:49:53 -0400 Subject: [PATCH 066/108] Rework version convenience functions. Add additional tests. --- SCons/Tool/MSCommon/MSVC/Config.py | 35 +++-- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 6 +- SCons/Tool/MSCommon/MSVC/Util.py | 126 +++++++++++------- SCons/Tool/MSCommon/MSVC/WinSDK.py | 5 +- SCons/Tool/MSCommon/MSVC/__init__.py | 2 +- SCons/Tool/MSCommon/__init__.py | 3 + SCons/Tool/MSCommon/vc.py | 51 +++---- test/MSVC/MSVC_SDK_VERSION.py | 8 +- test/MSVC/MSVC_TOOLSET_VERSION.py | 25 ++++ test/MSVC/msvc_cache_force_defaults.py | 81 +++++++++++ test/MSVC/msvc_sdk_versions.py | 90 +++++++++++++ test/MSVC/msvc_version_components.py | 61 +++++++++ test/MSVC/no_msvc.py | 10 ++ .../no_msvcs_sconstruct_msvc_sdk_versions.py | 15 +++ ..._msvcs_sconstruct_msvc_toolset_versions.py | 15 +++ 15 files changed, 437 insertions(+), 96 deletions(-) create mode 100644 test/MSVC/msvc_cache_force_defaults.py create mode 100644 test/MSVC/msvc_sdk_versions.py create mode 100644 test/MSVC/msvc_version_components.py create mode 100644 test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py create mode 100644 test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 60e2910312..3bb7d074b2 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -179,6 +179,7 @@ MSVC_VERSION_INTERNAL = {} MSVC_VERSION_EXTERNAL = {} +MSVC_VERSION_SUFFIX = {} MSVS_VERSION_MAJOR_MAP = {} @@ -248,13 +249,15 @@ vc_buildtools_def.vc_runtime_def.vc_runtime_vsdef_list.append(vs_def) + vc_version = vc_buildtools_def.vc_version + MSVS_VERSION_INTERNAL[vs_product] = vs_def MSVS_VERSION_EXTERNAL[vs_product] = vs_def MSVS_VERSION_EXTERNAL[vs_version] = vs_def - MSVC_VERSION_INTERNAL[vc_buildtools_def.vc_version] = vs_def + MSVC_VERSION_INTERNAL[vc_version] = vs_def MSVC_VERSION_EXTERNAL[vs_product] = vs_def - MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_version] = vs_def + MSVC_VERSION_EXTERNAL[vc_version] = vs_def MSVC_VERSION_EXTERNAL[vc_buildtools_def.vc_buildtools] = vs_def if vs_product in VS_PRODUCT_ALIAS: @@ -263,14 +266,16 @@ MSVS_VERSION_EXTERNAL[vs_product_alias] = vs_def MSVC_VERSION_EXTERNAL[vs_product_alias] = vs_def + MSVC_VERSION_SUFFIX[vc_version] = vs_def + if vs_express: + MSVC_VERSION_SUFFIX[vc_version + 'Exp'] = vs_def + MSVS_VERSION_MAJOR_MAP[vs_version_major] = vs_def CL_VERSION_MAP[vc_buildtools_def.cl_version] = vs_def - if not vc_sdk: - continue - - MSVC_SDK_VERSIONS.update(vc_sdk) + if vc_sdk: + MSVC_SDK_VERSIONS.update(vc_sdk) # EXPERIMENTAL: msvc version/toolset search lists # @@ -312,21 +317,15 @@ # convert string version set to string version list ranked in descending order MSVC_SDK_VERSIONS = [str(f) for f in sorted([float(s) for s in MSVC_SDK_VERSIONS], reverse=True)] -MSVS_VERSION_LEGACY = {} -MSVC_VERSION_LEGACY = {} - -for vdict in (MSVS_VERSION_EXTERNAL, MSVC_VERSION_INTERNAL): - for key, vs_def in vdict.items(): - if key not in MSVS_VERSION_LEGACY: - MSVS_VERSION_LEGACY[key] = vs_def - MSVC_VERSION_LEGACY[key] = vs_def def verify(): from .. import vc for msvc_version in vc._VCVER: + if not msvc_version in MSVC_VERSION_SUFFIX: + err_msg = 'msvc_version {} not in MSVC_VERSION_SUFFIX'.format(repr(msvc_version)) + raise MSVCInternalError(err_msg) vc_version = Util.get_msvc_version_prefix(msvc_version) - if vc_version in MSVC_VERSION_INTERNAL: - continue - err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version)) - raise MSVCInternalError(err_msg) + if vc_version not in MSVC_VERSION_INTERNAL: + err_msg = 'vc_version {} not in MSVC_VERSION_INTERNAL'.format(repr(vc_version)) + raise MSVCInternalError(err_msg) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index d07b78fc80..fab948e9a6 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -77,6 +77,8 @@ def _verify_re_sdk_dispatch_map(): raise MSVCInternalError(err_msg) return None +# toolset regexes need to accept same formats as extended version regexes in ./Util.py + # capture msvc version re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) @@ -323,7 +325,7 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist): if err_msg: raise MSVCArgumentError(err_msg) - sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_def) + 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( @@ -345,7 +347,7 @@ def _msvc_script_default_sdk(env, msvc, platform_def, arglist): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: return None - sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def.vc_sdk_versions, platform_def) + sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def) if not len(sdk_list): return None diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index f7e9091d78..e5e719c180 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -32,6 +32,8 @@ namedtuple, ) +from . import Config + def listdir_dirs(p): """ Return a list of tuples for each subdirectory of the given directory path. @@ -113,47 +115,9 @@ def get_msvc_version_prefix(version): rval = '' return rval -_VERSION_ELEMENTS_DEFINITION = namedtuple('VersionElements', [ - 'vc_version_numstr', # msvc version numeric string ('14.1') - 'vc_toolset_numstr', # toolset version numeric string ('14.16.27023') - 'vc_version_suffix', # component type ('Exp') - 'msvc_version', # msvc version ('14.1Exp') -]) - -re_version_elements = re.compile(r'^(?P(?P[1-9][0-9]?[.][0-9])[0-9.]*)(?P[A-Z]+)*$', re.IGNORECASE) - -def get_version_elements(version): - """ - Get the version elements from an msvc version or toolset version. - - Args: - version: str - version specification - - Returns: - None or VersionElements namedtuple: - """ - - m = re_version_elements.match(version) - if not m: - return None - - vc_version_numstr = m.group('msvc_version') - vc_toolset_numstr = m.group('version') - vc_version_suffix = m.group('suffix') if m.group('suffix') else '' - - version_elements_def = _VERSION_ELEMENTS_DEFINITION( - vc_version_numstr = vc_version_numstr, - vc_toolset_numstr = vc_toolset_numstr, - vc_version_suffix = vc_version_suffix, - msvc_version = vc_version_numstr + vc_version_suffix, - ) - - return version_elements_def - -# test suite convenience function +# convenience functions -_MSVC_VERSION_COMPONENTS = namedtuple('MSVCVersionComponents', [ +_MSVC_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCVersionComponentsDefinition', [ 'msvc_version', # msvc version (e.g., '14.1Exp') 'msvc_verstr', # msvc version numeric string (e.g., '14.1') 'msvc_suffix', # msvc version component type (e.g., 'Exp') @@ -164,9 +128,9 @@ def get_version_elements(version): re_msvc_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])(?P[A-Z]+)*$', re.IGNORECASE) -def get_msvc_version_components(vcver): +def msvc_version_components(vcver): """ - Get a tuple of msvc version components. + Decompose an msvc version into components. Tuple fields: msvc_version: msvc version (e.g., '14.1Exp') @@ -188,6 +152,10 @@ def get_msvc_version_components(vcver): if not m: return None + vs_def = Config.MSVC_VERSION_SUFFIX.get(vcver) + if not vs_def: + return None + msvc_version = vcver msvc_verstr = m.group('msvc_version') msvc_suffix = m.group('suffix') if m.group('suffix') else '' @@ -195,14 +163,82 @@ def get_msvc_version_components(vcver): msvc_major, msvc_minor = [int(x) for x in msvc_verstr.split('.')] - msvc_version_components_def = _MSVC_VERSION_COMPONENTS( - msvc_vernum = msvc_vernum, + msvc_version_components_def = _MSVC_VERSION_COMPONENTS_DEFINITION( + msvc_version = msvc_version, msvc_verstr = msvc_verstr, msvc_suffix = msvc_suffix, - msvc_version = msvc_version, + msvc_vernum = msvc_vernum, msvc_major = msvc_major, msvc_minor = msvc_minor, ) return msvc_version_components_def +_MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCExtendedVersionComponentsDefinition', [ + 'msvc_version', # msvc version (e.g., '14.1Exp') + 'msvc_verstr', # msvc version numeric string (e.g., '14.1') + 'msvc_suffix', # msvc version component type (e.g., 'Exp') + 'msvc_vernum', # msvc version floating point number (e.g, 14.1) + 'msvc_major', # msvc major version integer number (e.g., 14) + 'msvc_minor', # msvc minor version integer number (e.g., 1) + 'msvc_toolset_version', # msvc toolset version + 'version', # msvc version or msvc toolset version +]) + +# regex needs to accept same formats as toolset regexes in ./ScriptArguments.py + +re_extended_version = re.compile(r'''^ + (?P(?: + ([1-9][0-9]?[.][0-9]{1,2})| # XX.Y - XX.YY + ([1-9][0-9][.][0-9]{2}[.][0-9]{1,5})| # XX.YY.Z - XX.YY.ZZZZZ + ([1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}) # XX.YY.AA.B - XX.YY.AA.BB + )) + (?P[A-Z]+)* +$''', re.IGNORECASE | re.VERBOSE) + +def msvc_extended_version_components(version): + """ + Decompose an msvc version or msvc toolset version into components. + + Args: + version: str + version specification + + Returns: + None or MSVCExtendedVersionComponents namedtuple: + """ + + m = re_extended_version.match(version) + if not m: + return None + + msvc_toolset_version = m.group('version') + + msvc_verstr = get_msvc_version_prefix(msvc_toolset_version) + if not msvc_verstr: + return None + + msvc_suffix = m.group('suffix') if m.group('suffix') else '' + msvc_version = msvc_verstr + msvc_suffix + + vs_def = Config.MSVC_VERSION_SUFFIX.get(msvc_version) + if not vs_def: + return None + + msvc_vernum = float(msvc_verstr) + + msvc_major, msvc_minor = [int(x) for x in msvc_verstr.split('.')] + + msvc_extended_version_components_def = _MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION( + msvc_version = msvc_version, + msvc_verstr = msvc_verstr, + msvc_suffix = msvc_suffix, + msvc_vernum = msvc_vernum, + msvc_major = msvc_major, + msvc_minor = msvc_minor, + msvc_toolset_version = msvc_toolset_version, + version = version, + ) + + return msvc_extended_version_components_def + diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index b17f8508c0..f1fb961112 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -225,7 +225,8 @@ def get_msvc_platform(is_uwp=False): platform_def = _UWP if is_uwp else _DESKTOP return platform_def -def get_sdk_version_list(version_list, platform_def): +def get_sdk_version_list(vs_def, platform_def): + version_list = vs_def.vc_sdk_versions if vs_def.vc_sdk_versions is not None else [] sdk_map = _sdk_map(version_list) sdk_list = sdk_map.get(platform_def.vc_platform, []) return sdk_list @@ -243,7 +244,7 @@ def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False): is_uwp = True if msvc_uwp_app in Config.BOOLEAN_SYMBOLS[True] else False platform_def = get_msvc_platform(is_uwp) - sdk_list = get_sdk_version_list(vs_def.vc_sdk_versions, platform_def) + sdk_list = get_sdk_version_list(vs_def, platform_def) sdk_versions.extend(sdk_list) debug('sdk_versions=%s', repr(sdk_versions)) diff --git a/SCons/Tool/MSCommon/MSVC/__init__.py b/SCons/Tool/MSCommon/MSVC/__init__.py index 04a6948d67..849c82d146 100644 --- a/SCons/Tool/MSCommon/MSVC/__init__.py +++ b/SCons/Tool/MSCommon/MSVC/__init__.py @@ -36,9 +36,9 @@ """ from . import Exceptions # noqa: F401 -from . import Util # noqa: F401 from . import Config # noqa: F401 +from . import Util # noqa: F401 from . import Registry # noqa: F401 from . import SetupEnvDefault # noqa: F401 from . import Policy # noqa: F401 diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 0640bcefd2..ff2e42a009 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -71,6 +71,9 @@ from .vc import MSVCScriptNotFound # noqa: F401 from .vc import MSVCUseSettingsError # noqa: F401 +from .MSVC.Util import msvc_version_components # noqa: F401 +from .MSVC.Util import msvc_extended_version_components # noqa: F401 + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7fed093892..a254dc479b 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -970,7 +970,8 @@ def reset_installed_vcs(): __INSTALLED_VCS_RUN = None MSVC._reset() -def get_default_installed_msvc(env=None): +def msvc_get_default_version(env=None): + """Get default msvc version.""" vcs = get_installed_vcs(env) msvc_version = vcs[0] if vcs else None debug('msvc_version=%s', repr(msvc_version)) @@ -979,7 +980,7 @@ def get_default_installed_msvc(env=None): def get_installed_vcs_components(env=None): """Test suite convenience function: return list of installed msvc version component tuples""" vcs = get_installed_vcs(env) - msvc_version_component_defs = [MSVC.Util.get_msvc_version_components(vcver) for vcver in vcs] + msvc_version_component_defs = [MSVC.Util.msvc_version_components(vcver) for vcver in vcs] return msvc_version_component_defs # Running these batch files isn't cheap: most of the time spent in @@ -1088,7 +1089,7 @@ def get_default_version(env): return msvs_version if not msvc_version: - msvc_version = get_default_installed_msvc(env) + msvc_version = msvc_get_default_version(env) if not msvc_version: #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) debug('No installed VCs') @@ -1340,19 +1341,24 @@ def msvc_setup_env_tool(env=None, version=None, tool=None): rval = True return rval -def msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False): - debug('msvc_version=%s, msvc_uwp_app=%s', repr(msvc_version), repr(msvc_uwp_app)) +def msvc_sdk_versions(version=None, msvc_uwp_app=False): + debug('version=%s, msvc_uwp_app=%s', repr(version), repr(msvc_uwp_app)) rval = [] - if not msvc_version: - msvc_version = get_default_installed_msvc() + if not version: + version = msvc_get_default_version() - if not msvc_version: + if not version: debug('no msvc versions detected') return rval - rval = MSVC.WinSDK.get_msvc_sdk_version_list(msvc_version, msvc_uwp_app) + version_def = MSVC.Util.msvc_extended_version_components(version) + if not version_def: + msg = 'Unsupported version {}'.format(repr(version)) + raise MSVCArgumentError(msg) + + rval = MSVC.WinSDK.get_msvc_sdk_version_list(version, msvc_uwp_app) return rval def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): @@ -1362,7 +1368,7 @@ def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): rval = [] if not msvc_version: - msvc_version = get_default_installed_msvc() + msvc_version = msvc_get_default_version() if not msvc_version: debug('no msvc versions detected') @@ -1438,36 +1444,33 @@ def msvc_query_version_toolset(version=None, prefer_newest=True): ) return msvc_version, msvc_toolset_version - version_elements_def = MSVC.Util.get_version_elements(version) - if not version_elements_def: - msg = 'Unsupported version format {}'.format(repr(version)) - raise MSVCArgumentError(msg) + version_def = MSVC.Util.msvc_extended_version_components(version) - if version_elements_def.msvc_version not in _VCVER: + if not version_def: msg = 'Unsupported msvc version {}'.format(repr(version)) raise MSVCArgumentError(msg) - if version_elements_def.vc_version_suffix: - if version_elements_def.vc_version_numstr != version_elements_def.vc_toolset_numstr: + if version_def.msvc_suffix: + if version_def.msvc_verstr != version_def.msvc_toolset_version: # toolset version with component suffix msg = 'Unsupported toolset version {}'.format(repr(version)) raise MSVCArgumentError(msg) - if float(version_elements_def.vc_version_numstr) > 14.0: + if version_def.msvc_vernum > 14.0: # VS2017 and later force_toolset_msvc_version = False else: # VS2015 and earlier force_toolset_msvc_version = True - extended_version = version_elements_def.vc_version_numstr + '0.00000' - if not extended_version.startswith(version_elements_def.vc_toolset_numstr): + extended_version = version_def.msvc_verstr + '0.00000' + if not extended_version.startswith(version_def.msvc_toolset_version): # toolset not equivalent to msvc version msg = 'Unsupported toolset version {} (expected {})'.format( repr(version), repr(extended_version) ) raise MSVCArgumentError(msg) - msvc_version = version_elements_def.msvc_version + msvc_version = version_def.msvc_version if msvc_version not in MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP: # VS2013 and earlier @@ -1478,9 +1481,9 @@ def msvc_query_version_toolset(version=None, prefer_newest=True): return msvc_version, msvc_toolset_version if force_toolset_msvc_version: - query_msvc_toolset_version = version_elements_def.vc_version_numstr + query_msvc_toolset_version = version_def.msvc_verstr else: - query_msvc_toolset_version = version_elements_def.vc_toolset_numstr + query_msvc_toolset_version = version_def.msvc_toolset_version if prefer_newest: query_version_list = MSVC.Config.MSVC_VERSION_TOOLSET_SEARCH_MAP[msvc_version] @@ -1528,7 +1531,7 @@ def msvc_query_version_toolset(version=None, prefer_newest=True): repr(msvc_version), repr(msvc_toolset_version) ) - if version_elements_def.vc_version_numstr == msvc_toolset_version: + if version_def.msvc_verstr == msvc_toolset_version: msg = 'MSVC version {} was not found'.format(repr(version)) MSVC.Policy.msvc_notfound_handler(None, msg) return msvc_version, msvc_toolset_version diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index 2ebd046a77..627c867783 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -44,8 +44,8 @@ GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0] LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] -default_sdk_versions_uwp = msvc_sdk_versions(msvc_version=None, msvc_uwp_app=True) -default_sdk_versions_def = msvc_sdk_versions(msvc_version=None, msvc_uwp_app=False) +default_sdk_versions_uwp = msvc_sdk_versions(version=None, msvc_uwp_app=True) +default_sdk_versions_def = msvc_sdk_versions(version=None, msvc_uwp_app=False) have_140 = any([v.msvc_verstr == '14.0' for v in GE_VS2015_versions]) @@ -72,8 +72,8 @@ def version_major_list(version_list): for supported in GE_VS2015_versions: - sdk_versions_uwp = msvc_sdk_versions(msvc_version=supported.msvc_version, msvc_uwp_app=True) - sdk_versions_def = msvc_sdk_versions(msvc_version=supported.msvc_version, msvc_uwp_app=False) + sdk_versions_uwp = msvc_sdk_versions(version=supported.msvc_version, msvc_uwp_app=True) + sdk_versions_def = msvc_sdk_versions(version=supported.msvc_version, msvc_uwp_app=False) # find sdk version for each major SDK sdk_versions = version_major_list(sdk_versions_def) diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index ce6e251e98..aa4213fb2e 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -34,6 +34,7 @@ import textwrap from SCons.Tool.MSCommon.vc import get_installed_vcs_components +from SCons.Tool.MSCommon import msvc_toolset_versions installed_versions = get_installed_vcs_components() @@ -48,6 +49,30 @@ for supported in GE_VS2017_versions: + toolset_full_versions = msvc_toolset_versions(supported.msvc_version, full=True, sxs=False) + toolset_full_version = toolset_full_versions[0] if toolset_full_versions else None + + toolset_sxs_versions = msvc_toolset_versions(supported.msvc_version, full=False, sxs=True) + toolset_sxs_version = toolset_sxs_versions[0] if toolset_sxs_versions else None + + if toolset_full_version: + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(toolset_full_version)) + )) + test.run(arguments='-Q -s', stdout='') + + if toolset_sxs_version: + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) + """.format(repr(supported.msvc_version), repr(toolset_sxs_version)) + )) + test.run(arguments='-Q -s', stdout='') + # msvc_version as toolset version test.write('SConstruct', textwrap.dedent( """ diff --git a/test/MSVC/msvc_cache_force_defaults.py b/test/MSVC/msvc_cache_force_defaults.py new file mode 100644 index 0000000000..a964405dd6 --- /dev/null +++ b/test/MSVC/msvc_cache_force_defaults.py @@ -0,0 +1,81 @@ +# 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. + +""" +Test SCONS_CACHE_MSVC_FORCE_DEFAULTS system environment variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + +import textwrap + +from SCons.Tool.MSCommon.vc import get_installed_vcs_components + +installed_versions = get_installed_vcs_components() + +default_version = installed_versions[0] + +if default_version.msvc_vernum >= 14.0: + # VS2015 and later + + # force SDK version and toolset version as msvc batch file arguments + test.write('SConstruct', textwrap.dedent( + """ + import os + import json + + cache_file = 'MSCACHE.json' + + os.environ['SCONS_CACHE_MSVC_CONFIG']=cache_file + os.environ['SCONS_CACHE_MSVC_FORCE_DEFAULTS']='1' + + DefaultEnvironment(tools=[]) + env = Environment(tools=['msvc']) + + envcache_keys = [] + with open(cache_file, 'r') as file: + envcache_list = json.load(file) + envcache_keys = [tuple(d['key']) for d in envcache_list] + + if envcache_keys: + # key = (script, arguments) + print("SCRIPT_ARGS: {}".format(envcache_keys[0][-1])) + """ + )) + test.run(arguments = "-Q -s", status=0, stdout=None) + + cache_arg = test.stdout().strip() + + if default_version.msvc_verstr == '14.0': + # VS2015: target_arch msvc_sdk_version + expect = r'^SCRIPT_ARGS: .* [0-9.]+$' + else: + # VS2017+ msvc_sdk_version msvc_toolset_version + expect = r'^SCRIPT_ARGS: [0-9.]+ -vcvars_ver=[0-9.]+$' + + test.must_contain_all(cache_arg, expect, find=TestSCons.match_re) + diff --git a/test/MSVC/msvc_sdk_versions.py b/test/MSVC/msvc_sdk_versions.py new file mode 100644 index 0000000000..a99c1fe639 --- /dev/null +++ b/test/MSVC/msvc_sdk_versions.py @@ -0,0 +1,90 @@ +# 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. + +""" +Test the msvc_sdk_versions method. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + +import unittest + +from SCons.Tool.MSCommon.vc import _VCVER +from SCons.Tool.MSCommon.vc import msvc_get_default_version +from SCons.Tool.MSCommon import msvc_version_components +from SCons.Tool.MSCommon import msvc_extended_version_components +from SCons.Tool.MSCommon import msvc_sdk_versions +from SCons.Tool.MSCommon import msvc_toolset_versions +from SCons.Tool.MSCommon import MSVCArgumentError + +class MsvcSdkVersionsTests(unittest.TestCase): + + def test_valid_default_msvc(self): + symbol = msvc_get_default_version() + version_def = msvc_version_components(symbol) + for msvc_uwp_app in (True, False): + sdk_list = msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) + if version_def.msvc_vernum >= 14.0: + self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) + else: + self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) + + def test_valid_vcver(self): + for symbol in _VCVER: + version_def = msvc_version_components(symbol) + for msvc_uwp_app in (True, False): + sdk_list = msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + if version_def.msvc_vernum >= 14.0: + self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) + else: + self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) + + def test_valid_vcver_toolsets(self): + for symbol in _VCVER: + toolset_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) + if toolset_list is None: + continue + for toolset in toolset_list: + extended_def = msvc_extended_version_components(toolset) + for msvc_uwp_app in (True, False): + sdk_list = msvc_sdk_versions(version=extended_def.msvc_toolset_version, msvc_uwp_app=msvc_uwp_app) + self.assertTrue(sdk_list, "SDK list is empty for msvc toolset version {}".format(repr(toolset))) + + def test_invalid_vcver(self): + for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + for msvc_uwp_app in (True, False): + with self.assertRaises(MSVCArgumentError): + sdk_list = msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + + def test_invalid_vcver_toolsets(self): + for symbol in ['14.31.123456', '14.31.1.1']: + for msvc_uwp_app in (True, False): + with self.assertRaises(MSVCArgumentError): + sdk_list = msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + +unittest.main() + diff --git a/test/MSVC/msvc_version_components.py b/test/MSVC/msvc_version_components.py new file mode 100644 index 0000000000..d55de88972 --- /dev/null +++ b/test/MSVC/msvc_version_components.py @@ -0,0 +1,61 @@ +# 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. + +""" +Test msvc_version_components and msvc_extended_version_components functions. +""" + +import unittest + +from SCons.Tool.MSCommon.vc import _VCVER +from SCons.Tool.MSCommon import msvc_version_components +from SCons.Tool.MSCommon import msvc_extended_version_components + +class MsvcVersionComponentsTests(unittest.TestCase): + + def test_valid_msvc_versions(self): + for symbol in _VCVER: + version_def = msvc_version_components(symbol) + self.assertNotEqual(version_def, None, "Components tuple is None for {}".format(symbol)) + + def test_invalid_msvc_versions(self): + for symbol in ['14', '14Bug', '14.31', '14.31Bug']: + version_def = msvc_version_components(symbol) + self.assertEqual(version_def, None, "Components tuple is not None for {}".format(symbol)) + + def test_valid_msvc_extended_versions(self): + for symbol in _VCVER: + extended_def = msvc_extended_version_components(symbol) + self.assertNotEqual(extended_def, None, "Components tuple is None for {}".format(symbol)) + for symbol in ['14.31', '14.31.1', '14.31.12', '14.31.123', '14.31.1234', '14.31.12345', '14.31.17.2']: + extended_def = msvc_extended_version_components(symbol) + self.assertNotEqual(extended_def, None, "Components tuple is None for {}".format(symbol)) + + def test_invalid_extended_msvc_versions(self): + for symbol in ['14', '14.3Bug', '14.31Bug', '14.31.123456', '14.3.17', '14.3.1.1']: + extended_def = msvc_extended_version_components(symbol) + self.assertEqual(extended_def, None, "Components tuple is not None for {}".format(symbol)) + +if __name__ == "__main__": + unittest.main() + diff --git a/test/MSVC/no_msvc.py b/test/MSVC/no_msvc.py index 4ab7dd85d3..1a522c6d41 100644 --- a/test/MSVC/no_msvc.py +++ b/test/MSVC/no_msvc.py @@ -67,5 +67,15 @@ def exists(env): test.file_fixture('no_msvc/no_msvcs_sconstruct_tools.py', 'SConstruct') test.run(arguments='-Q -s') +# test no msvc's and msvc_sdk_version() call +test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py', 'SConstruct') +test.run(arguments='-Q -s') +test.must_contain_all(test.stdout(), 'sdk_version_list=[]') + +# test no msvc's and msvc_sdk_version() call +test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py', 'SConstruct') +test.run(arguments='-Q -s') +test.must_contain_all(test.stdout(), 'toolset_version_list=[]') + test.pass_test() diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py new file mode 100644 index 0000000000..62e77ab190 --- /dev/null +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_sdk_versions.py @@ -0,0 +1,15 @@ +import SCons +import SCons.Tool.MSCommon + +def DummyVsWhere(msvc_version, env): + # not testing versions with vswhere, so return none + return None + +for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR: + SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')] + +SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere + +sdk_version_list = SCons.Tool.MSCommon.msvc_sdk_versions() + +print('sdk_version_list='+repr(sdk_version_list)) \ No newline at end of file diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py new file mode 100644 index 0000000000..3aec559b47 --- /dev/null +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_toolset_versions.py @@ -0,0 +1,15 @@ +import SCons +import SCons.Tool.MSCommon + +def DummyVsWhere(msvc_version, env): + # not testing versions with vswhere, so return none + return None + +for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR: + SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')] + +SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere + +toolset_version_list = SCons.Tool.MSCommon.msvc_toolset_versions() + +print('toolset_version_list='+repr(toolset_version_list)) \ No newline at end of file From df5630754d5268765c45a99e8e860bcea697526c Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 9 Jul 2022 14:57:56 -0400 Subject: [PATCH 067/108] Fix sider issues. --- SCons/Tool/MSCommon/MSVC/Config.py | 2 +- test/MSVC/msvc_sdk_versions.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 3bb7d074b2..8b7f5eac10 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -321,7 +321,7 @@ def verify(): from .. import vc for msvc_version in vc._VCVER: - if not msvc_version in MSVC_VERSION_SUFFIX: + if msvc_version not in MSVC_VERSION_SUFFIX: err_msg = 'msvc_version {} not in MSVC_VERSION_SUFFIX'.format(repr(msvc_version)) raise MSVCInternalError(err_msg) vc_version = Util.get_msvc_version_prefix(msvc_version) diff --git a/test/MSVC/msvc_sdk_versions.py b/test/MSVC/msvc_sdk_versions.py index a99c1fe639..c8a34e429b 100644 --- a/test/MSVC/msvc_sdk_versions.py +++ b/test/MSVC/msvc_sdk_versions.py @@ -78,13 +78,13 @@ def test_invalid_vcver(self): for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: for msvc_uwp_app in (True, False): with self.assertRaises(MSVCArgumentError): - sdk_list = msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) def test_invalid_vcver_toolsets(self): for symbol in ['14.31.123456', '14.31.1.1']: for msvc_uwp_app in (True, False): with self.assertRaises(MSVCArgumentError): - sdk_list = msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) unittest.main() From 0d85fd2b2a92f27dbb45e2c974de73c5943f573e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 9 Jul 2022 17:18:23 -0400 Subject: [PATCH 068/108] Move toolset version regexes to utility module. Rename default version method. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 48 +++++---------- SCons/Tool/MSCommon/MSVC/Util.py | 67 ++++++++++++++++----- SCons/Tool/MSCommon/vc.py | 8 +-- test/MSVC/msvc_sdk_versions.py | 4 +- 4 files changed, 72 insertions(+), 55 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index fab948e9a6..85b692c0b6 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -77,24 +77,6 @@ def _verify_re_sdk_dispatch_map(): raise MSVCInternalError(err_msg) return None -# toolset regexes need to accept same formats as extended version regexes in ./Util.py - -# capture msvc version -re_toolset_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])[0-9.]*$', re.IGNORECASE) - -re_toolset_full = re.compile(r'''^(?: - (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY - (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ -)$''', re.VERBOSE) - -re_toolset_140 = re.compile(r'''^(?: - (?:14[.]0{1,2})| # 14.0 - 14.00 - (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000 -)$''', re.VERBOSE) - -# SxS toolset version: MM.mm.VV.vv format -re_toolset_sxs = re.compile(r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$') - # SxS version bugfix _msvc_sxs_bugfix_map = {} _msvc_sxs_bugfix_folder = {} @@ -422,7 +404,7 @@ def _msvc_read_toolset_file(msvc, filename): def _msvc_sxs_toolset_folder(msvc, sxs_folder): - if re_toolset_sxs.match(sxs_folder): + if Util.is_toolset_sxs(sxs_folder): return sxs_folder, sxs_folder key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder) @@ -566,7 +548,7 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version): toolset_vcvars = toolset_version return toolset_vcvars - if re_toolset_sxs.match(toolset_version): + 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: @@ -595,16 +577,16 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version): ) return err_msg - m = re_toolset_version.match(toolset_version) - if not m: - debug('invalid: re_toolset_version: toolset_version=%s', repr(toolset_version)) + toolset_verstr = Util.get_msvc_version_prefix(toolset_version) + + if not toolset_verstr: + 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_ver = m.group('version') - toolset_vernum = float(toolset_ver) + toolset_vernum = float(toolset_verstr) if toolset_vernum < VS2015.vc_buildtools_def.vc_version_numeric: debug( @@ -612,7 +594,7 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version): repr(toolset_vernum), repr(VS2015.vc_buildtools_def.vc_version_numeric) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} < {} VS2015".format( - repr(toolset_version), repr(toolset_ver), repr(VS2015.vc_buildtools_def.vc_version) + repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.vc_version) ) return err_msg @@ -622,14 +604,14 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version): repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric) ) err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format( - repr(toolset_version), repr(toolset_ver), repr(msvc.version) + repr(toolset_version), repr(toolset_verstr), repr(msvc.version) ) return err_msg if toolset_vernum == VS2015.vc_buildtools_def.vc_version_numeric: # tooset = 14.0 - if re_toolset_full.match(toolset_version): - if not re_toolset_140.match(toolset_version): + 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) @@ -640,12 +622,12 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version): return err_msg return None - if re_toolset_full.match(toolset_version): - debug('valid: re_toolset_full: toolset_version=%s', repr(toolset_version)) + if Util.is_toolset_full(toolset_version): + debug('valid: toolset full: toolset_version=%s', repr(toolset_version)) return None - if re_toolset_sxs.match(toolset_version): - debug('valid: re_toolset_sxs: toolset_version=%s', repr(toolset_version)) + 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)) diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index e5e719c180..8d36b16554 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -34,6 +34,8 @@ from . import Config +# path utilities + def listdir_dirs(p): """ Return a list of tuples for each subdirectory of the given directory path. @@ -73,8 +75,39 @@ def process_path(p): p = os.path.normcase(p) return p +# msvc version and msvc toolset version regexes + re_version_prefix = re.compile(r'^(?P[0-9.]+).*$') +re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*$') + +re_msvc_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])(?P[A-Z]+)*$', re.IGNORECASE) + +re_extended_version = re.compile(r'''^ + (?P(?: + ([1-9][0-9]?[.][0-9]{1,2})| # XX.Y - XX.YY + ([1-9][0-9][.][0-9]{2}[.][0-9]{1,5})| # XX.YY.Z - XX.YY.ZZZZZ + ([1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}) # XX.YY.AA.B - XX.YY.AA.BB + )) + (?P[A-Z]+)* +$''', re.IGNORECASE | re.VERBOSE) + +re_toolset_full = re.compile(r'''^(?: + (?:[1-9][0-9][.][0-9]{1,2})| # XX.Y - XX.YY + (?:[1-9][0-9][.][0-9]{2}[.][0-9]{1,5}) # XX.YY.Z - XX.YY.ZZZZZ +)$''', re.VERBOSE) + +re_toolset_140 = re.compile(r'''^(?: + (?:14[.]0{1,2})| # 14.0 - 14.00 + (?:14[.]0{2}[.]0{1,5}) # 14.00.0 - 14.00.00000 +)$''', re.VERBOSE) + +re_toolset_sxs = re.compile( + r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$' # MM.mm.VV.vv format +) + +# version prefix utilities + def get_version_prefix(version): """ Get the version number prefix from a string. @@ -94,8 +127,6 @@ def get_version_prefix(version): rval = '' return rval -re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*$') - def get_msvc_version_prefix(version): """ Get the msvc version number prefix from a string. @@ -115,7 +146,24 @@ def get_msvc_version_prefix(version): rval = '' return rval -# convenience functions +# toolset version query utilities + +def is_toolset_full(toolset_version): + if re_toolset_full.match(toolset_version): + return True + return False + +def is_toolset_140(toolset_version): + if re_toolset_140.match(toolset_version): + return True + return False + +def is_toolset_sxs(toolset_version): + if re_toolset_sxs.match(toolset_version): + return True + return False + +# msvc version and msvc toolset version decomposition utilties _MSVC_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCVersionComponentsDefinition', [ 'msvc_version', # msvc version (e.g., '14.1Exp') @@ -126,8 +174,6 @@ def get_msvc_version_prefix(version): 'msvc_minor', # msvc minor version integer number (e.g., 1) ]) -re_msvc_version = re.compile(r'^(?P[1-9][0-9]?[.][0-9])(?P[A-Z]+)*$', re.IGNORECASE) - def msvc_version_components(vcver): """ Decompose an msvc version into components. @@ -185,17 +231,6 @@ def msvc_version_components(vcver): 'version', # msvc version or msvc toolset version ]) -# regex needs to accept same formats as toolset regexes in ./ScriptArguments.py - -re_extended_version = re.compile(r'''^ - (?P(?: - ([1-9][0-9]?[.][0-9]{1,2})| # XX.Y - XX.YY - ([1-9][0-9][.][0-9]{2}[.][0-9]{1,5})| # XX.YY.Z - XX.YY.ZZZZZ - ([1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}) # XX.YY.AA.B - XX.YY.AA.BB - )) - (?P[A-Z]+)* -$''', re.IGNORECASE | re.VERBOSE) - def msvc_extended_version_components(version): """ Decompose an msvc version or msvc toolset version into components. diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index a254dc479b..83f2d99761 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -970,7 +970,7 @@ def reset_installed_vcs(): __INSTALLED_VCS_RUN = None MSVC._reset() -def msvc_get_default_version(env=None): +def msvc_default_version(env=None): """Get default msvc version.""" vcs = get_installed_vcs(env) msvc_version = vcs[0] if vcs else None @@ -1089,7 +1089,7 @@ def get_default_version(env): return msvs_version if not msvc_version: - msvc_version = msvc_get_default_version(env) + msvc_version = msvc_default_version(env) if not msvc_version: #SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) debug('No installed VCs') @@ -1347,7 +1347,7 @@ def msvc_sdk_versions(version=None, msvc_uwp_app=False): rval = [] if not version: - version = msvc_get_default_version() + version = msvc_default_version() if not version: debug('no msvc versions detected') @@ -1368,7 +1368,7 @@ def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): rval = [] if not msvc_version: - msvc_version = msvc_get_default_version() + msvc_version = msvc_default_version() if not msvc_version: debug('no msvc versions detected') diff --git a/test/MSVC/msvc_sdk_versions.py b/test/MSVC/msvc_sdk_versions.py index c8a34e429b..9b9ead0a66 100644 --- a/test/MSVC/msvc_sdk_versions.py +++ b/test/MSVC/msvc_sdk_versions.py @@ -34,7 +34,7 @@ import unittest from SCons.Tool.MSCommon.vc import _VCVER -from SCons.Tool.MSCommon.vc import msvc_get_default_version +from SCons.Tool.MSCommon.vc import msvc_default_version from SCons.Tool.MSCommon import msvc_version_components from SCons.Tool.MSCommon import msvc_extended_version_components from SCons.Tool.MSCommon import msvc_sdk_versions @@ -44,7 +44,7 @@ class MsvcSdkVersionsTests(unittest.TestCase): def test_valid_default_msvc(self): - symbol = msvc_get_default_version() + symbol = msvc_default_version() version_def = msvc_version_components(symbol) for msvc_uwp_app in (True, False): sdk_list = msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) From ea10a376827411956f9035c91933e1016256e37f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Jul 2022 08:33:29 -0400 Subject: [PATCH 069/108] Modify msvc_query_version_toolset when version is None to use default version. Add additional tests. --- SCons/Tool/MSCommon/vc.py | 10 +- test/MSVC/MSVC_SDK_VERSION.py | 2 - test/MSVC/msvc_query_version_toolset.py | 94 +++++++++++++++++++ test/MSVC/msvc_sdk_versions.py | 4 +- test/MSVC/msvc_toolset_versions.py | 85 +++++++++++++++++ test/MSVC/no_msvc.py | 5 + ...s_sconstruct_msvc_query_toolset_version.py | 15 +++ 7 files changed, 206 insertions(+), 9 deletions(-) create mode 100644 test/MSVC/msvc_query_version_toolset.py create mode 100644 test/MSVC/msvc_toolset_versions.py create mode 100644 test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 83f2d99761..46d6164c3d 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1434,14 +1434,14 @@ def msvc_query_version_toolset(version=None, prefer_newest=True): debug('version=%s, prefer_newest=%s', repr(version), repr(prefer_newest)) env = None - msvc_version = version + msvc_version = None msvc_toolset_version = None if not version: - debug( - 'ignore: msvc_version=%s, msvc_toolset_version=%s', - repr(msvc_version), repr(msvc_toolset_version) - ) + version = msvc_default_version() + + if not version: + debug('no msvc versions detected') return msvc_version, msvc_toolset_version version_def = MSVC.Util.msvc_extended_version_components(version) diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index 627c867783..4a3537b643 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -111,8 +111,6 @@ def version_major_list(version_list): ) test.must_contain_all(test.stderr(), expect) - # TODO: non-existent sdk version - # sdk version is not supported invalid_sdk_version = '9.1' test.write('SConstruct', textwrap.dedent( diff --git a/test/MSVC/msvc_query_version_toolset.py b/test/MSVC/msvc_query_version_toolset.py new file mode 100644 index 0000000000..23b8e57771 --- /dev/null +++ b/test/MSVC/msvc_query_version_toolset.py @@ -0,0 +1,94 @@ +# 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. + +""" +Test the msvc_query_version_toolset method. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + +import unittest + +from SCons.Tool.MSCommon.vc import _VCVER +from SCons.Tool.MSCommon.vc import msvc_default_version +from SCons.Tool.MSCommon import msvc_version_components +from SCons.Tool.MSCommon import msvc_extended_version_components +from SCons.Tool.MSCommon import msvc_toolset_versions +from SCons.Tool.MSCommon import msvc_query_version_toolset +from SCons.Tool.MSCommon import MSVCArgumentError + +class MsvcQueryVersionToolsetTests(unittest.TestCase): + + def test_valid_default_msvc(self): + symbol = msvc_default_version() + for prefer_newest in (True, False): + msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=None, prefer_newest=prefer_newest) + self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(None))) + version_def = msvc_version_components(msvc_version) + if version_def.msvc_vernum > 14.0: + # VS2017 and later for toolset version + self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(repr(None))) + + def test_valid_vcver(self): + for symbol in _VCVER: + version_def = msvc_version_components(symbol) + for prefer_newest in (True, False): + msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) + self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(symbol))) + if version_def.msvc_vernum > 14.0: + # VS2017 and later for toolset version + self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(repr(symbol))) + + def test_valid_vcver_toolsets(self): + for symbol in _VCVER: + toolset_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) + if toolset_list is None: + continue + for toolset in toolset_list: + extended_def = msvc_extended_version_components(toolset) + for prefer_newest in (True, False): + version = extended_def.msvc_toolset_version + msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=version, prefer_newest=prefer_newest) + self.assertTrue(msvc_version, "msvc_version is undefined for msvc toolset version {}".format(repr(toolset))) + if extended_def.msvc_vernum > 14.0: + # VS2017 and later for toolset version + self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc toolset version {}".format(repr(toolset))) + + def test_invalid_vcver(self): + for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + for prefer_newest in (True, False): + with self.assertRaises(MSVCArgumentError): + msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) + + def test_invalid_vcver_toolsets(self): + for symbol in ['14.31.123456', '14.31.1.1']: + for prefer_newest in (True, False): + with self.assertRaises(MSVCArgumentError): + msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) + +unittest.main() + diff --git a/test/MSVC/msvc_sdk_versions.py b/test/MSVC/msvc_sdk_versions.py index 9b9ead0a66..99395788e1 100644 --- a/test/MSVC/msvc_sdk_versions.py +++ b/test/MSVC/msvc_sdk_versions.py @@ -49,9 +49,9 @@ def test_valid_default_msvc(self): for msvc_uwp_app in (True, False): sdk_list = msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) if version_def.msvc_vernum >= 14.0: - self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) + self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(None))) else: - self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None))) def test_valid_vcver(self): for symbol in _VCVER: diff --git a/test/MSVC/msvc_toolset_versions.py b/test/MSVC/msvc_toolset_versions.py new file mode 100644 index 0000000000..1f8d44c720 --- /dev/null +++ b/test/MSVC/msvc_toolset_versions.py @@ -0,0 +1,85 @@ +# 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. + +""" +Test the msvc_toolset_versions method. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + +import unittest + +from SCons.Tool.MSCommon.vc import _VCVER +from SCons.Tool.MSCommon.vc import msvc_default_version +from SCons.Tool.MSCommon import msvc_version_components +from SCons.Tool.MSCommon import msvc_extended_version_components +from SCons.Tool.MSCommon import msvc_toolset_versions +from SCons.Tool.MSCommon import MSVCArgumentError + +class MsvcToolsetVersionsTests(unittest.TestCase): + + def test_valid_default_msvc(self): + symbol = msvc_default_version() + version_def = msvc_version_components(symbol) + toolset_none_list = msvc_toolset_versions(msvc_version=None, full=False, sxs=False) + toolset_full_list = msvc_toolset_versions(msvc_version=None, full=True, sxs=False) + toolset_sxs_list = msvc_toolset_versions(msvc_version=None, full=False, sxs=True) + toolset_all_list = msvc_toolset_versions(msvc_version=None, full=True, sxs=True) + if version_def.msvc_vernum >= 14.1: + # sxs list could be empty + self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) + self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) + else: + self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(None))) + self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(None))) + self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(None))) + self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(None))) + + def test_valid_vcver(self): + for symbol in _VCVER: + version_def = msvc_version_components(symbol) + toolset_none_list = msvc_toolset_versions(msvc_version=symbol, full=False, sxs=False) + toolset_full_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) + toolset_sxs_list = msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True) + toolset_all_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) + if version_def.msvc_vernum >= 14.1: + # sxs list could be empty + self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol))) + self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol))) + else: + self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(symbol))) + + def test_invalid_vcver(self): + for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + with self.assertRaises(MSVCArgumentError): + msvc_toolset_versions(msvc_version=symbol) + +unittest.main() + diff --git a/test/MSVC/no_msvc.py b/test/MSVC/no_msvc.py index 1a522c6d41..e255c65ae9 100644 --- a/test/MSVC/no_msvc.py +++ b/test/MSVC/no_msvc.py @@ -77,5 +77,10 @@ def exists(env): test.run(arguments='-Q -s') test.must_contain_all(test.stdout(), 'toolset_version_list=[]') +# test no msvc's and msvc_query_version_toolset() call +test.file_fixture('no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py', 'SConstruct') +test.run(arguments='-Q -s') +test.must_contain_all(test.stdout(), 'msvc_version=None, msvc_toolset_version=None') + test.pass_test() diff --git a/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py new file mode 100644 index 0000000000..8e3c65fbcb --- /dev/null +++ b/test/fixture/no_msvc/no_msvcs_sconstruct_msvc_query_toolset_version.py @@ -0,0 +1,15 @@ +import SCons +import SCons.Tool.MSCommon + +def DummyVsWhere(msvc_version, env): + # not testing versions with vswhere, so return none + return None + +for key in SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR: + SCons.Tool.MSCommon.vc._VCVER_TO_PRODUCT_DIR[key]=[(SCons.Util.HKEY_LOCAL_MACHINE, r'')] + +SCons.Tool.MSCommon.vc.find_vc_pdir_vswhere = DummyVsWhere + +msvc_version, msvc_toolset_version = SCons.Tool.MSCommon.msvc_query_version_toolset() + +print('msvc_version={}, msvc_toolset_version={}'.format(repr(msvc_version), repr(msvc_toolset_version))) \ No newline at end of file From 43d02a2a7d5eb5949d1b253a87a215061ad67d2a Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 10 Jul 2022 09:24:07 -0400 Subject: [PATCH 070/108] Fix msvc toolset versions test when not msvc version not installed. --- test/MSVC/msvc_query_version_toolset.py | 1 - test/MSVC/msvc_toolset_versions.py | 8 +++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/test/MSVC/msvc_query_version_toolset.py b/test/MSVC/msvc_query_version_toolset.py index 23b8e57771..5880f8c7c7 100644 --- a/test/MSVC/msvc_query_version_toolset.py +++ b/test/MSVC/msvc_query_version_toolset.py @@ -44,7 +44,6 @@ class MsvcQueryVersionToolsetTests(unittest.TestCase): def test_valid_default_msvc(self): - symbol = msvc_default_version() for prefer_newest in (True, False): msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=None, prefer_newest=prefer_newest) self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(None))) diff --git a/test/MSVC/msvc_toolset_versions.py b/test/MSVC/msvc_toolset_versions.py index 1f8d44c720..aed0124b24 100644 --- a/test/MSVC/msvc_toolset_versions.py +++ b/test/MSVC/msvc_toolset_versions.py @@ -34,12 +34,14 @@ import unittest from SCons.Tool.MSCommon.vc import _VCVER +from SCons.Tool.MSCommon.vc import get_installed_vcs_components from SCons.Tool.MSCommon.vc import msvc_default_version from SCons.Tool.MSCommon import msvc_version_components -from SCons.Tool.MSCommon import msvc_extended_version_components from SCons.Tool.MSCommon import msvc_toolset_versions from SCons.Tool.MSCommon import MSVCArgumentError +installed_versions = get_installed_vcs_components() + class MsvcToolsetVersionsTests(unittest.TestCase): def test_valid_default_msvc(self): @@ -49,7 +51,7 @@ def test_valid_default_msvc(self): toolset_full_list = msvc_toolset_versions(msvc_version=None, full=True, sxs=False) toolset_sxs_list = msvc_toolset_versions(msvc_version=None, full=False, sxs=True) toolset_all_list = msvc_toolset_versions(msvc_version=None, full=True, sxs=True) - if version_def.msvc_vernum >= 14.1: + if version_def in installed_versions and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) @@ -66,7 +68,7 @@ def test_valid_vcver(self): toolset_full_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) toolset_sxs_list = msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True) toolset_all_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) - if version_def.msvc_vernum >= 14.1: + if version_def in installed_versions and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol))) From 1291355762a8f0caf67890cf8547cfc64f40d505 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 11 Jul 2022 07:42:41 -0400 Subject: [PATCH 071/108] Local flake8 recommendations. --- SCons/Tool/MSCommon/__init__.py | 57 +++++++++++++++---------- SCons/Tool/MSCommon/common.py | 6 ++- SCons/Tool/MSCommon/vc.py | 14 +++--- SCons/Tool/MSCommon/vcTests.py | 14 +++--- test/MSVC/msvc_query_version_toolset.py | 1 - 5 files changed, 53 insertions(+), 39 deletions(-) diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index ff2e42a009..9d2e14aad5 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -28,11 +28,14 @@ import SCons.Errors import SCons.Platform.win32 -import SCons.Util +import SCons.Util # noqa: F401 -from SCons.Tool.MSCommon.sdk import mssdk_exists, mssdk_setup_env +from SCons.Tool.MSCommon.sdk import ( # noqa: F401 + mssdk_exists, + mssdk_setup_env, +) -from SCons.Tool.MSCommon.vc import ( +from SCons.Tool.MSCommon.vc import ( # noqa: F401 msvc_exists, msvc_setup_env_tool, msvc_setup_env_once, @@ -43,7 +46,7 @@ msvc_query_version_toolset, ) -from SCons.Tool.MSCommon.vs import ( +from SCons.Tool.MSCommon.vs import ( # noqa: F401 get_default_version, get_vs_by_version, merge_default_version, @@ -51,28 +54,36 @@ query_versions, ) -from .MSVC.Policy import msvc_set_notfound_policy # noqa: F401 -from .MSVC.Policy import msvc_get_notfound_policy # noqa: F401 -from .MSVC.Policy import msvc_set_scripterror_policy # noqa: F401 -from .MSVC.Policy import msvc_get_scripterror_policy # noqa: F401 +from .MSVC.Policy import ( # noqa: F401 + msvc_set_notfound_policy, + msvc_get_notfound_policy, + msvc_set_scripterror_policy, + msvc_get_scripterror_policy, +) -from .MSVC.Exceptions import VisualCException # noqa: F401 -from .MSVC.Exceptions import MSVCInternalError # noqa: F401 -from .MSVC.Exceptions import MSVCUserError # noqa: F401 -from .MSVC.Exceptions import MSVCScriptExecutionError # noqa: F401 -from .MSVC.Exceptions import MSVCVersionNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCSDKVersionNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCToolsetVersionNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCSpectreLibsNotFound # noqa: F401 -from .MSVC.Exceptions import MSVCArgumentError # noqa: F401 +from .MSVC.Exceptions import ( # noqa: F401 + VisualCException, + MSVCInternalError, + MSVCUserError, + MSVCScriptExecutionError, + MSVCVersionNotFound, + MSVCSDKVersionNotFound, + MSVCToolsetVersionNotFound, + MSVCSpectreLibsNotFound, + MSVCArgumentError, +) -from .vc import MSVCUnsupportedHostArch # noqa: F401 -from .vc import MSVCUnsupportedTargetArch # noqa: F401 -from .vc import MSVCScriptNotFound # noqa: F401 -from .vc import MSVCUseSettingsError # noqa: F401 +from .vc import ( # noqa: F401 + MSVCUnsupportedHostArch, + MSVCUnsupportedTargetArch, + MSVCScriptNotFound, + MSVCUseSettingsError, +) -from .MSVC.Util import msvc_version_components # noqa: F401 -from .MSVC.Util import msvc_extended_version_components # noqa: F401 +from .MSVC.Util import ( # noqa: F401 + msvc_version_components, + msvc_extended_version_components, +) # Local Variables: # tab-width:4 diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index aa01abcf0c..ad4c827d34 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -45,6 +45,7 @@ class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault): LOGFILE = os.environ.get('SCONS_MSCOMMON_DEBUG') if LOGFILE: import logging + modulelist = ( # root module and parent/root module 'MSCommon', 'Tool', @@ -53,6 +54,7 @@ class MSVCCacheInvalidWarning(SCons.Warnings.WarningOnByDefault): # scons modules 'SCons', 'test', 'scons' ) + def get_relative_filename(filename, module_list): if not filename: return filename @@ -63,6 +65,7 @@ def get_relative_filename(filename, module_list): except ValueError: pass return filename + class _Debug_Filter(logging.Filter): # custom filter for module relative filename def filter(self, record): @@ -70,6 +73,7 @@ def filter(self, record): relfilename = relfilename.replace('\\', '/') record.relfilename = relfilename return True + # Log format looks like: # 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [file] # debug: 00109ms:MSCommon/vc.py:find_vc_pdir#447: VC found '14.3' [stdout] @@ -231,7 +235,7 @@ def normalize_env(env, keys, force=False): # should include it, but keep this here to be safe (needed for reg.exe) sys32_dir = os.path.join( os.environ.get("SystemRoot", os.environ.get("windir", r"C:\Windows")), "System32" -) + ) if sys32_dir not in normenv["PATH"]: normenv["PATH"] = normenv["PATH"] + os.pathsep + sys32_dir diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 46d6164c3d..9d4bc9a163 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -423,7 +423,7 @@ def get_msvc_version_numeric(msvc_version): str: the value converted to a numeric only string """ - return ''.join([x for x in msvc_version if x in string_digits + '.']) + return ''.join([x for x in msvc_version if x in string_digits + '.']) def get_host_platform(host_platform): @@ -621,11 +621,11 @@ def msvc_version_to_maj_min(msvc_version): maj = int(t[0]) min = int(t[1]) return maj, min - except ValueError as e: + except ValueError: raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric)) from None -VSWHERE_PATHS = [os.path.join(p,'vswhere.exe') for p in [ +VSWHERE_PATHS = [os.path.join(p,'vswhere.exe') for p in [ os.path.expandvars(r"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer"), os.path.expandvars(r"%ProgramFiles%\Microsoft Visual Studio\Installer"), os.path.expandvars(r"%ChocolateyInstall%\bin"), @@ -1082,9 +1082,9 @@ def get_default_version(env): if not msvc_version == msvs_version: SCons.Warnings.warn( SCons.Warnings.VisualVersionMismatch, - "Requested msvc version (%s) and msvs version (%s) do " \ - "not match: please use MSVC_VERSION only to request a " \ - "visual studio version, MSVS_VERSION is deprecated" \ + "Requested msvc version (%s) and msvs version (%s) do " + "not match: please use MSVC_VERSION only to request a " + "visual studio version, MSVS_VERSION is deprecated" % (msvc_version, msvs_version)) return msvs_version @@ -1102,7 +1102,7 @@ def get_default_version(env): def msvc_setup_env_once(env, tool=None): try: - has_run = env["MSVC_SETUP_RUN"] + has_run = env["MSVC_SETUP_RUN"] except KeyError: has_run = False diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 2b6fbe5a0f..9a4d1a5ae9 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -50,7 +50,7 @@ class VswhereTestCase(unittest.TestCase): def _createVSWhere(path): os.makedirs(os.path.dirname(path), exist_ok=True) with open(path, 'w') as f: - f.write("Created:%s"%f) + f.write("Created:%s" % f) def testDefaults(self): """ @@ -59,13 +59,13 @@ def testDefaults(self): # import pdb; pdb.set_trace() vswhere_dirs = [os.path.splitdrive(p)[1] for p in SCons.Tool.MSCommon.vc.VSWHERE_PATHS] base_dir = test.workpath('fake_vswhere') - test_vswhere_dirs = [os.path.join(base_dir,d[1:]) for d in vswhere_dirs] + test_vswhere_dirs = [os.path.join(base_dir,d[1:]) for d in vswhere_dirs] SCons.Tool.MSCommon.vc.VSWHERE_PATHS = test_vswhere_dirs for vsw in test_vswhere_dirs: VswhereTestCase._createVSWhere(vsw) find_path = SCons.Tool.MSCommon.vc.msvc_find_vswhere() - self.assertTrue(vsw == find_path, "Didn't find vswhere in %s found in %s"%(vsw, find_path)) + self.assertTrue(vsw == find_path, "Didn't find vswhere in %s found in %s" % (vsw, find_path)) os.remove(vsw) # def specifiedVswherePathTest(self): @@ -132,7 +132,7 @@ def runTest(self): with open(tools_version_file, 'w') as tf: tf.write(MS_TOOLS_VERSION) except IOError as e: - print("Failed trying to write :%s :%s"%(tools_version_file, e)) + print("Failed trying to write :%s :%s" % (tools_version_file, e)) # Now walk all the valid combinations of host/target for 14.1 (VS2017) and later @@ -158,7 +158,7 @@ def runTest(self): except MSVCUnsupportedHostArch: pass else: - self.fail('Did not fail when HOST_ARCH specified as: %s'%env['HOST_ARCH']) + self.fail('Did not fail when HOST_ARCH specified as: %s' % env['HOST_ARCH']) # Now test bogus value for TARGET_ARCH env={'TARGET_ARCH':'GARBAGE', 'HOST_ARCH':'x86'} @@ -189,7 +189,7 @@ def runTest(self): try: result=check(env, '.', '9.0') # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s"%(env['HOST_ARCH'], env['TARGET_ARCH'])) + self.assertFalse(result, "Did not fail with bogus HOST_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) except MSVCUnsupportedHostArch: pass else: @@ -200,7 +200,7 @@ def runTest(self): try: result=check(env, '.', '9.0') # print("for:%s got :%s"%(env, result)) - self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s"%(env['HOST_ARCH'], env['TARGET_ARCH'])) + self.assertFalse(result, "Did not fail with bogus TARGET_ARCH host: %s target: %s" % (env['HOST_ARCH'], env['TARGET_ARCH'])) except MSVCUnsupportedTargetArch: pass else: diff --git a/test/MSVC/msvc_query_version_toolset.py b/test/MSVC/msvc_query_version_toolset.py index 5880f8c7c7..6b7b731261 100644 --- a/test/MSVC/msvc_query_version_toolset.py +++ b/test/MSVC/msvc_query_version_toolset.py @@ -34,7 +34,6 @@ import unittest from SCons.Tool.MSCommon.vc import _VCVER -from SCons.Tool.MSCommon.vc import msvc_default_version from SCons.Tool.MSCommon import msvc_version_components from SCons.Tool.MSCommon import msvc_extended_version_components from SCons.Tool.MSCommon import msvc_toolset_versions From 2eb66e95804bc903009f186288d9538236141d55 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 11 Jul 2022 14:34:39 -0400 Subject: [PATCH 072/108] Add additional tests. Remove trailing whitespace. --- SCons/Tool/MSCommon/MSVC/ConfigTests.py | 62 ++++++++ SCons/Tool/MSCommon/MSVC/DispatcherTests.py | 126 +++++++++++++++ SCons/Tool/MSCommon/MSVC/PolicyTests.py | 168 ++++++++++++++++++++ SCons/Tool/MSCommon/__init__.py | 4 +- SCons/Tool/MSCommon/vcTests.py | 2 +- test/MSVC/MSVC_NOTFOUND_POLICY.py | 28 ---- test/MSVC/MSVC_SCRIPTERROR_POLICY.py | 28 ---- test/MSVC/msvc_cache_force_defaults.py | 2 +- 8 files changed, 360 insertions(+), 60 deletions(-) create mode 100644 SCons/Tool/MSCommon/MSVC/ConfigTests.py create mode 100644 SCons/Tool/MSCommon/MSVC/DispatcherTests.py create mode 100644 SCons/Tool/MSCommon/MSVC/PolicyTests.py diff --git a/SCons/Tool/MSCommon/MSVC/ConfigTests.py b/SCons/Tool/MSCommon/MSVC/ConfigTests.py new file mode 100644 index 0000000000..570861c7d4 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/ConfigTests.py @@ -0,0 +1,62 @@ +# 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. + +""" +Test constants and initialized data structures for Microsoft Visual C/C++. +""" + +import unittest + +from SCons.Tool.MSCommon import vc +from SCons.Tool.MSCommon.MSVC import Config +from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError + +_VCVER = vc._VCVER +_MSVC_VERSION_INTERNAL = Config.MSVC_VERSION_INTERNAL + +class ConfigTests(unittest.TestCase): + + def test_vcver(self): + # all vc._VCVER in Config.MSVC_VERSION_SUFFIX + _ADD_VCVER = list(_VCVER) + _ADD_VCVER.append('99.9') + vc._VCVER = _ADD_VCVER + with self.assertRaises(MSVCInternalError): + Config.verify() + vc._VCVER = _VCVER + + def test_msvc_version_internal(self): + # all vc._VCVER numstr in Config.MSVC_VERSION_INTERNAL + _DEL_MSVC_VERSION_INTERNAL = dict(_MSVC_VERSION_INTERNAL) + del _DEL_MSVC_VERSION_INTERNAL['14.3'] + Config.MSVC_VERSION_INTERNAL = _DEL_MSVC_VERSION_INTERNAL + with self.assertRaises(MSVCInternalError): + Config.verify() + Config.MSVC_VERSION_INTERNAL = _MSVC_VERSION_INTERNAL + + def test_verify(self): + Config.verify() + +if __name__ == "__main__": + unittest.main() + diff --git a/SCons/Tool/MSCommon/MSVC/DispatcherTests.py b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py new file mode 100644 index 0000000000..01379fc4c3 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py @@ -0,0 +1,126 @@ +# 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. + +""" +Test internal method dispatcher for Microsoft Visual C/C++. +""" + +import unittest + +from SCons.Tool.MSCommon import MSVC +MSVC.Dispatcher.register_modulename(__name__) + +# current module - not callable +_reset = None +reset = None +_verify = None +verify = None + +reset_count = 0 +verify_count = 0 + +class StaticMethods: + + @staticmethod + def _reset(): + global reset_count + reset_count += 1 + + @staticmethod + def reset(): + global reset_count + reset_count += 1 + + @staticmethod + def _verify(): + global verify_count + verify_count += 1 + + @staticmethod + def verify(): + global verify_count + verify_count += 1 + +class ClassMethods: + + @classmethod + def _reset(cls): + global reset_count + reset_count += 1 + + @classmethod + def reset(cls): + global reset_count + reset_count += 1 + + @classmethod + def _verify(cls): + global verify_count + verify_count += 1 + + @classmethod + def verify(cls): + global verify_count + verify_count += 1 + +class NotCallable: + + _reset = None + reset = None + + _verify = None + _verify = None + +MSVC.Dispatcher.register_class(StaticMethods) +MSVC.Dispatcher.register_class(ClassMethods) +MSVC.Dispatcher.register_class(NotCallable) + +class DispatcherTests(unittest.TestCase): + + def test_dispatcher_reset(self): + global reset_count + MSVC.Dispatcher.reset() + self.assertTrue(reset_count == 4, "MSVC.Dispatcher.reset() count failed") + reset_count = 0 + + def test_dispatcher_verify(self): + global verify_count + MSVC.Dispatcher.verify() + self.assertTrue(verify_count == 4, "MSVC.Dispatcher.verify() count failed") + verify_count = 0 + + def test_msvc_reset(self): + global reset_count + MSVC._reset() + self.assertTrue(reset_count == 4, "MSVC._reset() count failed") + reset_count = 0 + + def test_msvc_verify(self): + global verify_count + MSVC._verify() + self.assertTrue(verify_count == 4, "MSVC._verify() count failed") + verify_count = 0 + +if __name__ == "__main__": + unittest.main() + diff --git a/SCons/Tool/MSCommon/MSVC/PolicyTests.py b/SCons/Tool/MSCommon/MSVC/PolicyTests.py new file mode 100644 index 0000000000..5126a6b13a --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/PolicyTests.py @@ -0,0 +1,168 @@ +# 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. + +""" +Test Microsoft Visual C/C++ policy handlers. +""" + +import unittest + +import SCons.Warnings + +from SCons.Tool.MSCommon.MSVC import Policy + +from SCons.Tool.MSCommon.MSVC.Exceptions import ( + MSVCArgumentError, + MSVCVersionNotFound, + MSVCScriptExecutionError, +) + +from SCons.Tool.MSCommon.MSVC.Warnings import ( + MSVCScriptExecutionWarning, +) + +class PolicyTests(unittest.TestCase): + + _prev_warnstate = None + _enabled = None + _warningAsException = None + + def push_warning_as_exception(self, Warning): + self._enabled = SCons.Warnings._enabled + self._warningAsException = SCons.Warnings._warningAsException + SCons.Warnings._enabled = [] + SCons.Warnings._warningAsException = 0 + SCons.Warnings.enableWarningClass(Warning) + self._prev_warnstate = SCons.Warnings.warningAsException() + + def pop_warning_as_exception(self): + _ = SCons.Warnings.warningAsException(self._prev_warnstate) + SCons.Warnings._enabled = self._enabled + SCons.Warnings._warningAsException = self._warningAsException + self._prev_warnstate = None + self._ebabled = None + self._warningAsException = None + + # msvc_set_notfound_policy, msvc_get_notfound_policy, and MSVC_NOTFOUND_POLICY + + def test_notfound_func_valid_symbols(self): + def_policy = Policy.msvc_get_notfound_policy() + last_policy = def_policy + for notfound_def in Policy.MSVC_NOTFOUND_DEFINITION_LIST: + for symbol in [notfound_def.symbol, notfound_def.symbol.lower(), notfound_def.symbol.upper()]: + prev_policy = Policy.msvc_set_notfound_policy(symbol) + self.assertTrue(prev_policy == last_policy, "prev_policy != last_policy") + cur_set_policy = Policy.msvc_set_notfound_policy() + cur_get_policy = Policy.msvc_get_notfound_policy() + self.assertTrue(cur_set_policy == cur_get_policy, "cur_set_policy != cur_get_policy") + last_policy = cur_get_policy + Policy.msvc_set_notfound_policy(def_policy) + + def test_notfound_func_invalid_symbol(self): + with self.assertRaises(MSVCArgumentError): + Policy.msvc_set_notfound_policy('Undefined') + + def test_notfound_handler_invalid_symbol(self): + with self.assertRaises(MSVCArgumentError): + Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': 'Undefined'}, '') + + def test_notfound_handler_ignore(self): + def_policy = Policy.msvc_set_notfound_policy('Ignore') + Policy.msvc_notfound_handler(None, '') + Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': None}, '') + Policy.msvc_set_notfound_policy(def_policy) + + def test_notfound_handler_warning(self): + # treat warning as exception for testing + self.push_warning_as_exception(SCons.Warnings.VisualCMissingWarning) + def_policy = Policy.msvc_set_notfound_policy('Warning') + with self.assertRaises(SCons.Warnings.VisualCMissingWarning): + Policy.msvc_notfound_handler(None, '') + Policy.msvc_set_notfound_policy('Ignore') + with self.assertRaises(SCons.Warnings.VisualCMissingWarning): + Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': 'Warning'}, '') + Policy.msvc_set_notfound_policy(def_policy) + self.pop_warning_as_exception() + + def test_notfound_handler_error(self): + def_policy = Policy.msvc_set_notfound_policy('Error') + with self.assertRaises(MSVCVersionNotFound): + Policy.msvc_notfound_handler(None, '') + Policy.msvc_set_notfound_policy('Ignore') + with self.assertRaises(MSVCVersionNotFound): + Policy.msvc_notfound_handler({'MSVC_NOTFOUND_POLICY': 'Error'}, '') + Policy.msvc_set_notfound_policy(def_policy) + + # msvc_set_scripterror_policy, msvc_get_scripterror_policy, and MSVC_SCRIPTERROR_POLICY + + def test_scripterror_func_valid_symbols(self): + def_policy = Policy.msvc_get_scripterror_policy() + last_policy = def_policy + for scripterror_def in Policy.MSVC_SCRIPTERROR_DEFINITION_LIST: + for symbol in [scripterror_def.symbol, scripterror_def.symbol.lower(), scripterror_def.symbol.upper()]: + prev_policy = Policy.msvc_set_scripterror_policy(symbol) + self.assertTrue(prev_policy == last_policy, "prev_policy != last_policy") + cur_set_policy = Policy.msvc_set_scripterror_policy() + cur_get_policy = Policy.msvc_get_scripterror_policy() + self.assertTrue(cur_set_policy == cur_get_policy, "cur_set_policy != cur_get_policy") + last_policy = cur_get_policy + Policy.msvc_set_scripterror_policy(def_policy) + + def test_scripterror_func_invalid_symbol(self): + with self.assertRaises(MSVCArgumentError): + Policy.msvc_set_scripterror_policy('Undefined') + + def test_scripterror_handler_invalid_symbol(self): + with self.assertRaises(MSVCArgumentError): + Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': 'Undefined'}, '') + + def test_scripterror_handler_ignore(self): + def_policy = Policy.msvc_set_scripterror_policy('Ignore') + Policy.msvc_scripterror_handler(None, '') + Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': None}, '') + Policy.msvc_set_scripterror_policy(def_policy) + + def test_scripterror_handler_warning(self): + # treat warning as exception for testing + self.push_warning_as_exception(MSVCScriptExecutionWarning) + def_policy = Policy.msvc_set_scripterror_policy('Warning') + with self.assertRaises(MSVCScriptExecutionWarning): + Policy.msvc_scripterror_handler(None, '') + Policy.msvc_set_scripterror_policy('Ignore') + with self.assertRaises(MSVCScriptExecutionWarning): + Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': 'Warning'}, '') + Policy.msvc_set_scripterror_policy(def_policy) + self.pop_warning_as_exception() + + def test_scripterror_handler_error(self): + def_policy = Policy.msvc_set_scripterror_policy('Error') + with self.assertRaises(MSVCScriptExecutionError): + Policy.msvc_scripterror_handler(None, '') + Policy.msvc_set_scripterror_policy('Ignore') + with self.assertRaises(MSVCScriptExecutionError): + Policy.msvc_scripterror_handler({'MSVC_SCRIPTERROR_POLICY': 'Error'}, '') + Policy.msvc_set_scripterror_policy(def_policy) + +if __name__ == "__main__": + unittest.main() + diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 9d2e14aad5..ee443d9725 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -31,7 +31,7 @@ import SCons.Util # noqa: F401 from SCons.Tool.MSCommon.sdk import ( # noqa: F401 - mssdk_exists, + mssdk_exists, mssdk_setup_env, ) @@ -83,7 +83,7 @@ from .MSVC.Util import ( # noqa: F401 msvc_version_components, msvc_extended_version_components, -) +) # Local Variables: # tab-width:4 diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 9a4d1a5ae9..dd2ba03019 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -80,7 +80,7 @@ class MSVcTestCase(unittest.TestCase): @staticmethod def _createDummyCl(path, add_bin=True): """ - Creates a dummy cl.exe in the correct directory. + Creates a dummy cl.exe in the correct directory. It will create all missing parent directories as well Args: diff --git a/test/MSVC/MSVC_NOTFOUND_POLICY.py b/test/MSVC/MSVC_NOTFOUND_POLICY.py index d123c02922..f5a8de29a4 100644 --- a/test/MSVC/MSVC_NOTFOUND_POLICY.py +++ b/test/MSVC/MSVC_NOTFOUND_POLICY.py @@ -33,34 +33,6 @@ import textwrap -# Test global functions with valid symbols -test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_set_notfound_policy - from SCons.Tool.MSCommon import msvc_get_notfound_policy - DefaultEnvironment(tools=[]) - for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: - for policy in [symbol, symbol.upper(), symbol.lower()]: - old_policy = msvc_set_notfound_policy(policy) - cur_policy = msvc_get_notfound_policy() - if msvc_set_notfound_policy(None) != msvc_get_notfound_policy(): - raise RuntimeError() - """ -)) -test.run(arguments='-Q -s', stdout='') - -# Test global function with invalid symbol -test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_set_notfound_policy - DefaultEnvironment(tools=[]) - msvc_set_notfound_policy('Undefined') - """ -)) -test.run(arguments='-Q -s', status=2, stderr=None) -expect = "MSVCArgumentError: Value specified for MSVC_NOTFOUND_POLICY is not supported: 'Undefined'." -test.must_contain_all(test.stderr(), expect) - # Test construction variable with valid symbols test.write('SConstruct', textwrap.dedent( """ diff --git a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py index 4d23595872..9dcfb53e5f 100644 --- a/test/MSVC/MSVC_SCRIPTERROR_POLICY.py +++ b/test/MSVC/MSVC_SCRIPTERROR_POLICY.py @@ -39,34 +39,6 @@ default_version = installed_versions[0] -# Test global functions with valid symbols -test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_set_scripterror_policy - from SCons.Tool.MSCommon import msvc_get_scripterror_policy - DefaultEnvironment(tools=[]) - for symbol in ['Error', 'Exception', 'Warn', 'Warning', 'Ignore', 'Suppress']: - for policy in [symbol, symbol.upper(), symbol.lower()]: - old_policy = msvc_set_scripterror_policy(policy) - cur_policy = msvc_get_scripterror_policy() - if msvc_set_scripterror_policy(None) != msvc_get_scripterror_policy(): - raise RuntimeError() - """ -)) -test.run(arguments='-Q -s', stdout='') - -# Test global function with invalid symbol -test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_set_scripterror_policy - DefaultEnvironment(tools=[]) - msvc_set_scripterror_policy('Undefined') - """ -)) -test.run(arguments='-Q -s', status=2, stderr=None) -expect = "MSVCArgumentError: Value specified for MSVC_SCRIPTERROR_POLICY is not supported: 'Undefined'." -test.must_contain_all(test.stderr(), expect) - # Test construction variable with valid symbols test.write('SConstruct', textwrap.dedent( """ diff --git a/test/MSVC/msvc_cache_force_defaults.py b/test/MSVC/msvc_cache_force_defaults.py index a964405dd6..7aee8503bb 100644 --- a/test/MSVC/msvc_cache_force_defaults.py +++ b/test/MSVC/msvc_cache_force_defaults.py @@ -56,7 +56,7 @@ DefaultEnvironment(tools=[]) env = Environment(tools=['msvc']) - envcache_keys = [] + envcache_keys = [] with open(cache_file, 'r') as file: envcache_list = json.load(file) envcache_keys = [tuple(d['key']) for d in envcache_list] From 3f9d6685f513d30d514e9dc78cc8a4ad4be470b5 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 12 Jul 2022 15:08:56 -0400 Subject: [PATCH 073/108] Update utility functions and add tests. --- SCons/Tool/MSCommon/MSVC/PolicyTests.py | 37 ++--- SCons/Tool/MSCommon/MSVC/RegistryTests.py | 41 +++++ SCons/Tool/MSCommon/MSVC/Util.py | 62 ++++---- SCons/Tool/MSCommon/MSVC/UtilTests.py | 175 ++++++++++++++++++++++ 4 files changed, 272 insertions(+), 43 deletions(-) create mode 100644 SCons/Tool/MSCommon/MSVC/RegistryTests.py create mode 100644 SCons/Tool/MSCommon/MSVC/UtilTests.py diff --git a/SCons/Tool/MSCommon/MSVC/PolicyTests.py b/SCons/Tool/MSCommon/MSVC/PolicyTests.py index 5126a6b13a..faa0f786e4 100644 --- a/SCons/Tool/MSCommon/MSVC/PolicyTests.py +++ b/SCons/Tool/MSCommon/MSVC/PolicyTests.py @@ -43,25 +43,18 @@ class PolicyTests(unittest.TestCase): - _prev_warnstate = None - _enabled = None - _warningAsException = None + def setUp(self): + self.warnstack = [] def push_warning_as_exception(self, Warning): - self._enabled = SCons.Warnings._enabled - self._warningAsException = SCons.Warnings._warningAsException - SCons.Warnings._enabled = [] - SCons.Warnings._warningAsException = 0 SCons.Warnings.enableWarningClass(Warning) - self._prev_warnstate = SCons.Warnings.warningAsException() + prev_state = SCons.Warnings.warningAsException() + self.warnstack.append((Warning, prev_state)) def pop_warning_as_exception(self): - _ = SCons.Warnings.warningAsException(self._prev_warnstate) - SCons.Warnings._enabled = self._enabled - SCons.Warnings._warningAsException = self._warningAsException - self._prev_warnstate = None - self._ebabled = None - self._warningAsException = None + Warning, prev_state = self.warnstack.pop() + SCons.Warnings.warningAsException(prev_state) + SCons.Warnings.suppressWarningClass(Warning) # msvc_set_notfound_policy, msvc_get_notfound_policy, and MSVC_NOTFOUND_POLICY @@ -71,10 +64,14 @@ def test_notfound_func_valid_symbols(self): for notfound_def in Policy.MSVC_NOTFOUND_DEFINITION_LIST: for symbol in [notfound_def.symbol, notfound_def.symbol.lower(), notfound_def.symbol.upper()]: prev_policy = Policy.msvc_set_notfound_policy(symbol) - self.assertTrue(prev_policy == last_policy, "prev_policy != last_policy") + self.assertTrue(prev_policy == last_policy, "notfound policy: {} != {}".format( + repr(prev_policy), repr(last_policy) + )) cur_set_policy = Policy.msvc_set_notfound_policy() cur_get_policy = Policy.msvc_get_notfound_policy() - self.assertTrue(cur_set_policy == cur_get_policy, "cur_set_policy != cur_get_policy") + self.assertTrue(cur_set_policy == cur_get_policy, "notfound policy: {} != {}".format( + repr(cur_set_policy), repr(cur_get_policy) + )) last_policy = cur_get_policy Policy.msvc_set_notfound_policy(def_policy) @@ -121,10 +118,14 @@ def test_scripterror_func_valid_symbols(self): for scripterror_def in Policy.MSVC_SCRIPTERROR_DEFINITION_LIST: for symbol in [scripterror_def.symbol, scripterror_def.symbol.lower(), scripterror_def.symbol.upper()]: prev_policy = Policy.msvc_set_scripterror_policy(symbol) - self.assertTrue(prev_policy == last_policy, "prev_policy != last_policy") + self.assertTrue(prev_policy == last_policy, "scripterror policy: {} != {}".format( + repr(prev_policy), repr(last_policy) + )) cur_set_policy = Policy.msvc_set_scripterror_policy() cur_get_policy = Policy.msvc_get_scripterror_policy() - self.assertTrue(cur_set_policy == cur_get_policy, "cur_set_policy != cur_get_policy") + self.assertTrue(cur_set_policy == cur_get_policy, "scripterror policy: {} != {}".format( + repr(cur_set_policy), repr(cur_get_policy) + )) last_policy = cur_get_policy Policy.msvc_set_scripterror_policy(def_policy) diff --git a/SCons/Tool/MSCommon/MSVC/RegistryTests.py b/SCons/Tool/MSCommon/MSVC/RegistryTests.py new file mode 100644 index 0000000000..9fe2f0e3b6 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/RegistryTests.py @@ -0,0 +1,41 @@ +# 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. + +""" +Test windows registry functions for Microsoft Visual C/C++. +""" + +import unittest +import sys + +from SCons.Tool.MSCommon.MSVC import Registry # noqa: F401 + +@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") +class RegistryTests(unittest.TestCase): + + def test(self): + pass + +if __name__ == "__main__": + unittest.main() + diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 8d36b16554..55e4464d3e 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -40,7 +40,6 @@ def listdir_dirs(p): """ Return a list of tuples for each subdirectory of the given directory path. Each tuple is comprised of the subdirectory name and the qualified subdirectory path. - Assumes the given directory path exists and is a directory. Args: p: str @@ -51,10 +50,11 @@ def listdir_dirs(p): """ dirs = [] - for dir_name in os.listdir(p): - dir_path = os.path.join(p, dir_name) - if os.path.isdir(dir_path): - dirs.append((dir_name, dir_path)) + if p and os.path.exists(p) and os.path.isdir(p): + for dir_name in os.listdir(p): + dir_path = os.path.join(p, dir_name) + if os.path.isdir(dir_path): + dirs.append((dir_name, dir_path)) return dirs def process_path(p): @@ -77,7 +77,7 @@ def process_path(p): # msvc version and msvc toolset version regexes -re_version_prefix = re.compile(r'^(?P[0-9.]+).*$') +re_version_prefix = re.compile('^(?P[0-9]+(?:[.][0-9]+)*)(?![.]).*$') re_msvc_version_prefix = re.compile(r'^(?P[1-9][0-9]?[.][0-9]).*$') @@ -120,11 +120,11 @@ def get_version_prefix(version): str: the version number prefix """ - m = re_version_prefix.match(version) - if m: - rval = m.group('version') - else: - rval = '' + rval = '' + if version: + m = re_version_prefix.match(version) + if m: + rval = m.group('version') return rval def get_msvc_version_prefix(version): @@ -139,29 +139,35 @@ def get_msvc_version_prefix(version): str: the msvc version number prefix """ - m = re_msvc_version_prefix.match(version) - if m: - rval = m.group('version') - else: - rval = '' + rval = '' + if version: + m = re_msvc_version_prefix.match(version) + if m: + rval = m.group('version') return rval # toolset version query utilities def is_toolset_full(toolset_version): - if re_toolset_full.match(toolset_version): - return True - return False + rval = False + if toolset_version: + if re_toolset_full.match(toolset_version): + rval = True + return rval def is_toolset_140(toolset_version): - if re_toolset_140.match(toolset_version): - return True - return False + rval = False + if toolset_version: + if re_toolset_140.match(toolset_version): + rval = True + return rval def is_toolset_sxs(toolset_version): - if re_toolset_sxs.match(toolset_version): - return True - return False + rval = False + if toolset_version: + if re_toolset_sxs.match(toolset_version): + rval = True + return rval # msvc version and msvc toolset version decomposition utilties @@ -194,6 +200,9 @@ def msvc_version_components(vcver): None or MSVCVersionComponents namedtuple: """ + if not vcver: + return None + m = re_msvc_version.match(vcver) if not m: return None @@ -243,6 +252,9 @@ def msvc_extended_version_components(version): None or MSVCExtendedVersionComponents namedtuple: """ + if not version: + return None + m = re_extended_version.match(version) if not m: return None diff --git a/SCons/Tool/MSCommon/MSVC/UtilTests.py b/SCons/Tool/MSCommon/MSVC/UtilTests.py new file mode 100644 index 0000000000..d3ce56bcc9 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/UtilTests.py @@ -0,0 +1,175 @@ +# 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. + +""" +Test helper functions for Microsoft Visual C/C++. +""" + +import unittest +import os +import re + +from SCons.Tool.MSCommon.MSVC import Util + +util_parent_dir = os.path.join(os.path.dirname(Util.__file__), os.pardir) + +class UtilTests(unittest.TestCase): + + def test_listdir_dirs(self): + func = Util.listdir_dirs + for dirname, expect in [ + (None, False), ('', False), ('doesnotexist.xyz.abc', False), + (util_parent_dir, True), + ]: + dirs = func(dirname) + self.assertTrue((len(dirs) > 0) == expect, "{}({}): {}".format( + func.__name__, repr(dirname), 'list is empty' if expect else 'list is not empty' + )) + + def test_process_path(self): + func = Util.process_path + for p, expect in [ + (None, True), ('', True), + ('doesnotexist.xyz.abc', False), (util_parent_dir, False), + ]: + rval = func(p) + self.assertTrue((p == rval) == expect, "{}({}): {}".format( + func.__name__, repr(p), repr(rval) + )) + + def test_get_version_prefix(self): + func = Util.get_version_prefix + for version, expect in [ + (None, ''), ('', ''), + ('.', ''), ('0..0', ''), ('.0', ''), ('0.', ''), ('0.0.', ''), + ('0', '0'), ('0Abc', '0'), ('0 0', '0'), ('0,0', '0'), + ('0.0', '0.0'), ('0.0.0', '0.0.0'), ('0.0.0.0', '0.0.0.0'), ('0.0.0.0.0', '0.0.0.0.0'), + ('00.00.00000', '00.00.00000'), ('00.00.00.0', '00.00.00.0'), ('00.00.00.00', '00.00.00.00'), ('00.0.00000.0', '00.0.00000.0'), + ('0.0A', '0.0'), ('0.0.0B', '0.0.0'), ('0.0.0.0 C', '0.0.0.0'), ('0.0.0.0.0 D', '0.0.0.0.0'), + ]: + prefix = func(version) + self.assertTrue(prefix == expect, "{}({}): {} != {}".format( + func.__name__, repr(version), repr(prefix), repr(expect) + )) + + def test_get_msvc_version_prefix(self): + func = Util.get_msvc_version_prefix + for version, expect in [ + (None, ''), ('', ''), + ('.', ''), ('0..0', ''), ('.0', ''), ('0.', ''), ('0.0.', ''), + ('0', ''), ('0Abc', ''), ('0 0', ''), ('0,0', ''), + ('0.0', ''), ('0.0.0', ''), ('0.0.0.0', ''), ('0.0.0.0.0', ''), + ('1.0A', '1.0'), ('1.0.0B', '1.0'), ('1.0.0.0 C', '1.0'), ('1.0.0.0.0 D', '1.0'), + ('1.00A', '1.0'), ('1.00.0B', '1.0'), ('1.00.0.0 C', '1.0'), ('1.00.0.0.0 D', '1.0'), + ]: + prefix = func(version) + self.assertTrue(prefix == expect, "{}({}): {} != {}".format( + func.__name__, repr(version), repr(prefix), repr(expect) + )) + + def test_is_toolset_full(self): + func = Util.is_toolset_full + for toolset, expect in [ + (None, False), ('', False), + ('14.1.', False), ('14.10.', False), ('14.10.00000.', False), ('14.10.000000', False), ('14.1Exp', False), + ('14.1', True), ('14.10', True), ('14.10.0', True), ('14.10.00', True), ('14.10.000', True), ('14.10.0000', True), ('14.10.0000', True), + ]: + rval = func(toolset) + self.assertTrue(rval == expect, "{}({}) != {}".format( + func.__name__, repr(toolset), repr(rval) + )) + + def test_is_toolset_140(self): + func = Util.is_toolset_140 + for toolset, expect in [ + (None, False), ('', False), + ('14.0.', False), ('14.00.', False), ('14.00.00000.', False), ('14.00.000000', False), ('14.0Exp', False), + ('14.0', True), ('14.00', True), ('14.00.0', True), ('14.00.00', True), ('14.00.000', True), ('14.00.0000', True), ('14.00.0000', True), + ]: + rval = func(toolset) + self.assertTrue(rval == expect, "{}({}) != {}".format( + func.__name__, repr(toolset), repr(rval) + )) + + def test_is_toolset_sxs(self): + func = Util.is_toolset_sxs + for toolset, expect in [ + (None, False), ('', False), + ('14.2.', False), ('14.29.', False), ('14.29.1.', False), ('14.29.16.', False), ('14.29.16.1.', False), + ('14.29.16.1', True), ('14.29.16.10', True), + ]: + rval = func(toolset) + self.assertTrue(rval == expect, "{}({}) != {}".format( + func.__name__, repr(toolset), repr(rval) + )) + + def test_msvc_version_components(self): + func = Util.msvc_version_components + for vcver, expect in [ + (None, False), ('', False), ('ABC', False), ('14', False), ('14.1.', False), ('14.16', False), + ('14.1', True), ('14.1Exp', True), + ('14.1Bug', False), + ]: + comps_def = func(vcver) + msg = 'msvc version components definition is None' if expect else 'msvc version components definition is not None' + self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format( + func.__name__, repr(vcver), repr(msg) + )) + + def test_msvc_extended_version_components(self): + func = Util.msvc_extended_version_components + # normal code paths + for vcver, expect in [ + (None, False), ('', False), ('ABC', False), ('14', False), ('14.1.', False), + ('14.1', True), ('14.16', True), + ('14.1Exp', True), ('14.16Exp', True), + ('14.16.2', True), ('14.16.27', True), ('14.16.270', True), + ('14.16.2702', True), ('14.16.2702', True), ('14.16.27023', True), + ('14.16.270239', False), + ('14.16.2Exp', True), ('14.16.27Exp', True), ('14.16.270Exp', True), + ('14.16.2702Exp', True), ('14.16.2702Exp', True), ('14.16.27023Exp', True), + ('14.16.270239Exp', False), + ('14.28.16.9', True), ('14.28.16.10', True), + ('14.28.16.9Exp', False), ('14.28.16.10Exp', False), + ]: + comps_def = func(vcver) + msg = 'msvc extended version components definition is None' if expect else 'msvc extended version components definition is not None' + self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format( + func.__name__, repr(vcver), repr(msg) + )) + # force 'just in case' guard code path + save_re = Util.re_extended_version + Util.re_extended_version = re.compile(r'^(?P[0-9]+)$') + for vcver, expect in [ + ('14', False), + ]: + comps_def = func(vcver) + msg = 'msvc extended version components definition is None' if expect else 'msvc extended version components definition is not None' + self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format( + func.__name__, repr(vcver), repr(msg) + )) + Util.re_extended_version = save_re + +if __name__ == "__main__": + unittest.main() + From afd7d109c441a4d548d0706dfd6cd9521851965d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:11:17 -0400 Subject: [PATCH 074/108] Rework registry read_value to expand variables if necessary. Add registry tests. --- SCons/Tool/MSCommon/MSVC/Registry.py | 32 +++++++++------ SCons/Tool/MSCommon/MSVC/RegistryTests.py | 48 +++++++++++++++++++++-- 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Registry.py b/SCons/Tool/MSCommon/MSVC/Registry.py index 9ffa01e681..9519e15538 100644 --- a/SCons/Tool/MSCommon/MSVC/Registry.py +++ b/SCons/Tool/MSCommon/MSVC/Registry.py @@ -30,11 +30,11 @@ from SCons.Util import ( HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER, + RegGetValue, ) from .. common import ( debug, - read_reg, ) from . import Util @@ -43,21 +43,24 @@ Dispatcher.register_modulename(__name__) -def read_value(hkey, subkey_valname): +# A null-terminated string that contains unexpanded references to environment variables. +REG_EXPAND_SZ = 2 + +def read_value(hkey, subkey_valname, expand=True): try: - rval = read_reg(subkey_valname, hkroot=hkey) + rval_t = RegGetValue(hkey, subkey_valname) except OSError: debug('OSError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) return None - except IndexError: - debug('IndexError: hkey=%s, subkey=%s', repr(hkey), repr(subkey_valname)) - return None + rval, regtype = rval_t + if regtype == REG_EXPAND_SZ and expand: + rval = os.path.expandvars(rval) debug('hkey=%s, subkey=%s, rval=%s', repr(hkey), repr(subkey_valname), repr(rval)) return rval -def registry_query_path(key, val, suffix): +def registry_query_path(key, val, suffix, expand=True): extval = val + '\\' + suffix if suffix else val - qpath = read_value(key, extval) + qpath = read_value(key, extval, expand=expand) if qpath and os.path.exists(qpath): qpath = Util.process_path(qpath) else: @@ -71,12 +74,12 @@ def registry_query_path(key, val, suffix): (HKEY_CURRENT_USER, r'Software\Microsoft'), ] -def microsoft_query_paths(suffix, usrval=None): +def microsoft_query_paths(suffix, usrval=None, expand=True): paths = [] records = [] for key, val in REG_SOFTWARE_MICROSOFT: extval = val + '\\' + suffix if suffix else val - qpath = read_value(key, extval) + qpath = read_value(key, extval, expand=expand) if qpath and os.path.exists(qpath): qpath = Util.process_path(qpath) if qpath not in paths: @@ -84,13 +87,13 @@ def microsoft_query_paths(suffix, usrval=None): records.append((qpath, key, val, extval, usrval)) return records -def microsoft_query_keys(suffix, usrval=None): +def microsoft_query_keys(suffix, usrval=None, expand=True): records = [] for key, val in REG_SOFTWARE_MICROSOFT: extval = val + '\\' + suffix if suffix else val - rval = read_value(key, extval) + rval = read_value(key, extval, expand=expand) if rval: - records.append((key, val, extval, usrval)) + records.append((rval, key, val, extval, usrval)) return records def microsoft_sdks(version): @@ -110,3 +113,6 @@ def windows_kit_query_paths(version): def vstudio_sxs_vc7(version): return '\\'.join([r'VisualStudio\SxS\VC7', version]) +def devdiv_vs_servicing_component(version, component): + return '\\'.join([r'DevDiv\VS\Servicing', version, component, 'Install']) + diff --git a/SCons/Tool/MSCommon/MSVC/RegistryTests.py b/SCons/Tool/MSCommon/MSVC/RegistryTests.py index 9fe2f0e3b6..aff3b3f997 100644 --- a/SCons/Tool/MSCommon/MSVC/RegistryTests.py +++ b/SCons/Tool/MSCommon/MSVC/RegistryTests.py @@ -28,13 +28,55 @@ import unittest import sys -from SCons.Tool.MSCommon.MSVC import Registry # noqa: F401 +from SCons.Tool.MSCommon.MSVC import Config +from SCons.Tool.MSCommon.MSVC import Registry @unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") class RegistryTests(unittest.TestCase): - def test(self): - pass + _sdk_versions = None + + @classmethod + def setUpClass(cls): + cls._sdk_versions = [] + sdk_seen = set() + for vs_def in Config.VISUALSTUDIO_DEFINITION_LIST: + if not vs_def.vc_sdk_versions: + continue + for sdk_version in vs_def.vc_sdk_versions: + if sdk_version in sdk_seen: + continue + sdk_seen.add(sdk_version) + cls._sdk_versions.append(sdk_version) + + def setUp(self): + self.sdk_versions = self.__class__._sdk_versions + + def test_sdk_query_paths(self): + for sdk_version in self.sdk_versions: + _ = Registry.sdk_query_paths(sdk_version) + + def test_vstudio_sxs_vc7(self): + suffix = Registry.vstudio_sxs_vc7('14.0') + _ = Registry.microsoft_query_paths(suffix) + + def test_microsoft_query_keys(self): + val = r'SYSTEM\CurrentControlSet\Control\Session Manager\Environment' + for suffix in ['Temp', 'Tmp']: + _ = Registry.registry_query_path(Registry.HKEY_LOCAL_MACHINE, val, suffix, expand=True) + _ = Registry.registry_query_path(Registry.HKEY_LOCAL_MACHINE, val, suffix, expand=False) + + def test_registry_query_path(self): + # need a better test for when VS2015 is no longer installed + for component_reg in ('enterprise', 'professional', 'community'): + suffix = Registry.devdiv_vs_servicing_component('14.0', component_reg) + rval = Registry.microsoft_query_keys(suffix, component_reg) + if not rval: + continue + + def test_windows_kit_query_paths(self): + for sdk_version in self.sdk_versions: + _ = Registry.windows_kit_query_paths(sdk_version) if __name__ == "__main__": unittest.main() From 85957733efc4fc8a1d48d8ff68b37246e1bab463 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 13 Jul 2022 14:38:39 -0400 Subject: [PATCH 075/108] Update UtilTests with version components tests and remove separate msvc version components test. --- SCons/Tool/MSCommon/MSVC/UtilTests.py | 11 +++++ test/MSVC/msvc_version_components.py | 61 --------------------------- 2 files changed, 11 insertions(+), 61 deletions(-) delete mode 100644 test/MSVC/msvc_version_components.py diff --git a/SCons/Tool/MSCommon/MSVC/UtilTests.py b/SCons/Tool/MSCommon/MSVC/UtilTests.py index d3ce56bcc9..404bf47c94 100644 --- a/SCons/Tool/MSCommon/MSVC/UtilTests.py +++ b/SCons/Tool/MSCommon/MSVC/UtilTests.py @@ -29,6 +29,7 @@ import os import re +from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC import Util util_parent_dir = os.path.join(os.path.dirname(Util.__file__), os.pardir) @@ -135,6 +136,11 @@ def test_msvc_version_components(self): self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format( func.__name__, repr(vcver), repr(msg) )) + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + comps_def = func(vcver) + self.assertNotEqual(comps_def, None, "{}({}) is None".format( + func.__name__, repr(vcver) + )) def test_msvc_extended_version_components(self): func = Util.msvc_extended_version_components @@ -157,6 +163,11 @@ def test_msvc_extended_version_components(self): self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format( func.__name__, repr(vcver), repr(msg) )) + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + comps_def = func(vcver) + self.assertNotEqual(comps_def, None, "{}({}) is None".format( + func.__name__, repr(vcver) + )) # force 'just in case' guard code path save_re = Util.re_extended_version Util.re_extended_version = re.compile(r'^(?P[0-9]+)$') diff --git a/test/MSVC/msvc_version_components.py b/test/MSVC/msvc_version_components.py deleted file mode 100644 index d55de88972..0000000000 --- a/test/MSVC/msvc_version_components.py +++ /dev/null @@ -1,61 +0,0 @@ -# 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. - -""" -Test msvc_version_components and msvc_extended_version_components functions. -""" - -import unittest - -from SCons.Tool.MSCommon.vc import _VCVER -from SCons.Tool.MSCommon import msvc_version_components -from SCons.Tool.MSCommon import msvc_extended_version_components - -class MsvcVersionComponentsTests(unittest.TestCase): - - def test_valid_msvc_versions(self): - for symbol in _VCVER: - version_def = msvc_version_components(symbol) - self.assertNotEqual(version_def, None, "Components tuple is None for {}".format(symbol)) - - def test_invalid_msvc_versions(self): - for symbol in ['14', '14Bug', '14.31', '14.31Bug']: - version_def = msvc_version_components(symbol) - self.assertEqual(version_def, None, "Components tuple is not None for {}".format(symbol)) - - def test_valid_msvc_extended_versions(self): - for symbol in _VCVER: - extended_def = msvc_extended_version_components(symbol) - self.assertNotEqual(extended_def, None, "Components tuple is None for {}".format(symbol)) - for symbol in ['14.31', '14.31.1', '14.31.12', '14.31.123', '14.31.1234', '14.31.12345', '14.31.17.2']: - extended_def = msvc_extended_version_components(symbol) - self.assertNotEqual(extended_def, None, "Components tuple is None for {}".format(symbol)) - - def test_invalid_extended_msvc_versions(self): - for symbol in ['14', '14.3Bug', '14.31Bug', '14.31.123456', '14.3.17', '14.3.1.1']: - extended_def = msvc_extended_version_components(symbol) - self.assertEqual(extended_def, None, "Components tuple is not None for {}".format(symbol)) - -if __name__ == "__main__": - unittest.main() - From 1365adbda99eeddfee28acbeec0db891cf849ffd Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 13 Jul 2022 15:17:41 -0400 Subject: [PATCH 076/108] Move individual tests file content into vcTests.py --- SCons/Tool/MSCommon/vcTests.py | 157 +++++++++++++++++++++++- test/MSVC/msvc_query_version_toolset.py | 92 -------------- test/MSVC/msvc_sdk_versions.py | 90 -------------- test/MSVC/msvc_toolset_versions.py | 87 ------------- 4 files changed, 154 insertions(+), 272 deletions(-) delete mode 100644 test/MSVC/msvc_query_version_toolset.py delete mode 100644 test/MSVC/msvc_sdk_versions.py delete mode 100644 test/MSVC/msvc_toolset_versions.py diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index dd2ba03019..655d8e10e2 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -31,6 +31,7 @@ import SCons.Node.FS import SCons.Warnings import SCons.Tool.MSCommon.vc +from SCons.Tool import MSCommon import TestCmd @@ -72,9 +73,6 @@ def testDefaults(self): # "Verify that msvc.generate() respects VSWHERE Specified" - - - class MSVcTestCase(unittest.TestCase): @staticmethod @@ -241,6 +239,159 @@ def runTest(self): self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) +class MsvcSdkVersionsTests(unittest.TestCase): + + def test_valid_default_msvc(self): + symbol = MSCommon.vc.msvc_default_version() + version_def = MSCommon.msvc_version_components(symbol) + for msvc_uwp_app in (True, False): + sdk_list = MSCommon.vc.msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) + if version_def.msvc_vernum >= 14.0: + self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(None))) + else: + self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None))) + + def test_valid_vcver(self): + for symbol in MSCommon.vc._VCVER: + version_def = MSCommon.msvc_version_components(symbol) + for msvc_uwp_app in (True, False): + sdk_list = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + if version_def.msvc_vernum >= 14.0: + self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) + else: + self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) + + def test_valid_vcver_toolsets(self): + for symbol in MSCommon.vc._VCVER: + toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) + if toolset_list is None: + continue + for toolset in toolset_list: + extended_def = MSCommon.msvc_extended_version_components(toolset) + for msvc_uwp_app in (True, False): + sdk_list = MSCommon.vc.msvc_sdk_versions( + version=extended_def.msvc_toolset_version, + msvc_uwp_app=msvc_uwp_app + ) + self.assertTrue(sdk_list, "SDK list is empty for msvc toolset version {}".format(repr(toolset))) + + def test_invalid_vcver(self): + for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + for msvc_uwp_app in (True, False): + with self.assertRaises(MSCommon.vc.MSVCArgumentError): + _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + + def test_invalid_vcver_toolsets(self): + for symbol in ['14.31.123456', '14.31.1.1']: + for msvc_uwp_app in (True, False): + with self.assertRaises(MSCommon.vc.MSVCArgumentError): + _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) + + +installed_vcs_components = MSCommon.vc.get_installed_vcs_components() + +class MsvcToolsetVersionsTests(unittest.TestCase): + + def test_valid_default_msvc(self): + symbol = MSCommon.vc.msvc_default_version() + version_def = MSCommon.msvc_version_components(symbol) + toolset_none_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=False) + toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=False) + toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=True) + toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=True) + if version_def in installed_vcs_components and version_def.msvc_vernum >= 14.1: + # sxs list could be empty + self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) + self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) + else: + self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(None))) + self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(None))) + self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(None))) + self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(None))) + + def test_valid_vcver(self): + for symbol in MSCommon.vc._VCVER: + version_def = MSCommon.msvc_version_components(symbol) + toolset_none_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=False, sxs=False) + toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) + toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True) + toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) + if version_def in installed_vcs_components and version_def.msvc_vernum >= 14.1: + # sxs list could be empty + self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol))) + self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol))) + else: + self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(symbol))) + self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(symbol))) + + def test_invalid_vcver(self): + for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + with self.assertRaises(MSCommon.vc.MSVCArgumentError): + _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol) + + +class MsvcQueryVersionToolsetTests(unittest.TestCase): + + def test_valid_default_msvc(self): + for prefer_newest in (True, False): + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=None, prefer_newest=prefer_newest + ) + self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(None))) + version_def = MSCommon.msvc_version_components(msvc_version) + if version_def.msvc_vernum > 14.0: + # VS2017 and later for toolset version + self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format( + repr(None) + )) + + def test_valid_vcver(self): + for symbol in MSCommon.vc._VCVER: + version_def = MSCommon.msvc_version_components(symbol) + for prefer_newest in (True, False): + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=symbol, prefer_newest=prefer_newest + ) + self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(symbol))) + if version_def.msvc_vernum > 14.0: + # VS2017 and later for toolset version + self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format( + repr(symbol) + )) + + def test_valid_vcver_toolsets(self): + for symbol in MSCommon.vc._VCVER: + toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) + if toolset_list is None: + continue + for toolset in toolset_list: + extended_def = MSCommon.msvc_extended_version_components(toolset) + for prefer_newest in (True, False): + version = extended_def.msvc_toolset_version + msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( + version=version, prefer_newest=prefer_newest + ) + self.assertTrue(msvc_version, "msvc_version is undefined for msvc toolset version {}".format(repr(toolset))) + if extended_def.msvc_vernum > 14.0: + # VS2017 and later for toolset version + self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc toolset version {}".format( + repr(toolset) + )) + + def test_invalid_vcver(self): + for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + for prefer_newest in (True, False): + with self.assertRaises(MSCommon.vc.MSVCArgumentError): + _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) + + def test_invalid_vcver_toolsets(self): + for symbol in ['14.31.123456', '14.31.1.1']: + for prefer_newest in (True, False): + with self.assertRaises(MSCommon.vc.MSVCArgumentError): + _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) + if __name__ == "__main__": unittest.main() diff --git a/test/MSVC/msvc_query_version_toolset.py b/test/MSVC/msvc_query_version_toolset.py deleted file mode 100644 index 6b7b731261..0000000000 --- a/test/MSVC/msvc_query_version_toolset.py +++ /dev/null @@ -1,92 +0,0 @@ -# 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. - -""" -Test the msvc_query_version_toolset method. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.skip_if_not_msvc() - -import unittest - -from SCons.Tool.MSCommon.vc import _VCVER -from SCons.Tool.MSCommon import msvc_version_components -from SCons.Tool.MSCommon import msvc_extended_version_components -from SCons.Tool.MSCommon import msvc_toolset_versions -from SCons.Tool.MSCommon import msvc_query_version_toolset -from SCons.Tool.MSCommon import MSVCArgumentError - -class MsvcQueryVersionToolsetTests(unittest.TestCase): - - def test_valid_default_msvc(self): - for prefer_newest in (True, False): - msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=None, prefer_newest=prefer_newest) - self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(None))) - version_def = msvc_version_components(msvc_version) - if version_def.msvc_vernum > 14.0: - # VS2017 and later for toolset version - self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(repr(None))) - - def test_valid_vcver(self): - for symbol in _VCVER: - version_def = msvc_version_components(symbol) - for prefer_newest in (True, False): - msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) - self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(symbol))) - if version_def.msvc_vernum > 14.0: - # VS2017 and later for toolset version - self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format(repr(symbol))) - - def test_valid_vcver_toolsets(self): - for symbol in _VCVER: - toolset_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) - if toolset_list is None: - continue - for toolset in toolset_list: - extended_def = msvc_extended_version_components(toolset) - for prefer_newest in (True, False): - version = extended_def.msvc_toolset_version - msvc_version, msvc_toolset_version = msvc_query_version_toolset(version=version, prefer_newest=prefer_newest) - self.assertTrue(msvc_version, "msvc_version is undefined for msvc toolset version {}".format(repr(toolset))) - if extended_def.msvc_vernum > 14.0: - # VS2017 and later for toolset version - self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc toolset version {}".format(repr(toolset))) - - def test_invalid_vcver(self): - for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: - for prefer_newest in (True, False): - with self.assertRaises(MSVCArgumentError): - msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) - - def test_invalid_vcver_toolsets(self): - for symbol in ['14.31.123456', '14.31.1.1']: - for prefer_newest in (True, False): - with self.assertRaises(MSVCArgumentError): - msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) - -unittest.main() - diff --git a/test/MSVC/msvc_sdk_versions.py b/test/MSVC/msvc_sdk_versions.py deleted file mode 100644 index 99395788e1..0000000000 --- a/test/MSVC/msvc_sdk_versions.py +++ /dev/null @@ -1,90 +0,0 @@ -# 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. - -""" -Test the msvc_sdk_versions method. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.skip_if_not_msvc() - -import unittest - -from SCons.Tool.MSCommon.vc import _VCVER -from SCons.Tool.MSCommon.vc import msvc_default_version -from SCons.Tool.MSCommon import msvc_version_components -from SCons.Tool.MSCommon import msvc_extended_version_components -from SCons.Tool.MSCommon import msvc_sdk_versions -from SCons.Tool.MSCommon import msvc_toolset_versions -from SCons.Tool.MSCommon import MSVCArgumentError - -class MsvcSdkVersionsTests(unittest.TestCase): - - def test_valid_default_msvc(self): - symbol = msvc_default_version() - version_def = msvc_version_components(symbol) - for msvc_uwp_app in (True, False): - sdk_list = msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) - if version_def.msvc_vernum >= 14.0: - self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(None))) - else: - self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None))) - - def test_valid_vcver(self): - for symbol in _VCVER: - version_def = msvc_version_components(symbol) - for msvc_uwp_app in (True, False): - sdk_list = msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - if version_def.msvc_vernum >= 14.0: - self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) - else: - self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) - - def test_valid_vcver_toolsets(self): - for symbol in _VCVER: - toolset_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) - if toolset_list is None: - continue - for toolset in toolset_list: - extended_def = msvc_extended_version_components(toolset) - for msvc_uwp_app in (True, False): - sdk_list = msvc_sdk_versions(version=extended_def.msvc_toolset_version, msvc_uwp_app=msvc_uwp_app) - self.assertTrue(sdk_list, "SDK list is empty for msvc toolset version {}".format(repr(toolset))) - - def test_invalid_vcver(self): - for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: - for msvc_uwp_app in (True, False): - with self.assertRaises(MSVCArgumentError): - msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - - def test_invalid_vcver_toolsets(self): - for symbol in ['14.31.123456', '14.31.1.1']: - for msvc_uwp_app in (True, False): - with self.assertRaises(MSVCArgumentError): - msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - -unittest.main() - diff --git a/test/MSVC/msvc_toolset_versions.py b/test/MSVC/msvc_toolset_versions.py deleted file mode 100644 index aed0124b24..0000000000 --- a/test/MSVC/msvc_toolset_versions.py +++ /dev/null @@ -1,87 +0,0 @@ -# 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. - -""" -Test the msvc_toolset_versions method. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -test.skip_if_not_msvc() - -import unittest - -from SCons.Tool.MSCommon.vc import _VCVER -from SCons.Tool.MSCommon.vc import get_installed_vcs_components -from SCons.Tool.MSCommon.vc import msvc_default_version -from SCons.Tool.MSCommon import msvc_version_components -from SCons.Tool.MSCommon import msvc_toolset_versions -from SCons.Tool.MSCommon import MSVCArgumentError - -installed_versions = get_installed_vcs_components() - -class MsvcToolsetVersionsTests(unittest.TestCase): - - def test_valid_default_msvc(self): - symbol = msvc_default_version() - version_def = msvc_version_components(symbol) - toolset_none_list = msvc_toolset_versions(msvc_version=None, full=False, sxs=False) - toolset_full_list = msvc_toolset_versions(msvc_version=None, full=True, sxs=False) - toolset_sxs_list = msvc_toolset_versions(msvc_version=None, full=False, sxs=True) - toolset_all_list = msvc_toolset_versions(msvc_version=None, full=True, sxs=True) - if version_def in installed_versions and version_def.msvc_vernum >= 14.1: - # sxs list could be empty - self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) - self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) - else: - self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(None))) - self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(None))) - self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(None))) - self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(None))) - - def test_valid_vcver(self): - for symbol in _VCVER: - version_def = msvc_version_components(symbol) - toolset_none_list = msvc_toolset_versions(msvc_version=symbol, full=False, sxs=False) - toolset_full_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) - toolset_sxs_list = msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True) - toolset_all_list = msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) - if version_def in installed_versions and version_def.msvc_vernum >= 14.1: - # sxs list could be empty - self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol))) - self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol))) - else: - self.assertFalse(toolset_full_list, "Toolset full list is not empty for msvc version {}".format(repr(symbol))) - self.assertFalse(toolset_sxs_list, "Toolset sxs list is not empty for msvc version {}".format(repr(symbol))) - self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(symbol))) - self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(symbol))) - - def test_invalid_vcver(self): - for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: - with self.assertRaises(MSVCArgumentError): - msvc_toolset_versions(msvc_version=symbol) - -unittest.main() - From 744dac6103fed53c29d6957854c995ce99ea5181 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Wed, 13 Jul 2022 16:23:19 -0400 Subject: [PATCH 077/108] Disable new vcTests for non-windows platforms. --- SCons/Tool/MSCommon/vcTests.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 655d8e10e2..376a0d5df2 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -24,6 +24,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys import os import os.path import unittest @@ -239,6 +240,7 @@ def runTest(self): self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) +@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") class MsvcSdkVersionsTests(unittest.TestCase): def test_valid_default_msvc(self): @@ -288,10 +290,18 @@ def test_invalid_vcver_toolsets(self): _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) -installed_vcs_components = MSCommon.vc.get_installed_vcs_components() - +@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") class MsvcToolsetVersionsTests(unittest.TestCase): + _installed_vcs_components = None + + @classmethod + def setUpClass(cls): + cls._installed_vcs_components = MSCommon.vc.get_installed_vcs_components() + + def setUp(self): + self.installed_vcs_components = self.__class__._installed_vcs_components + def test_valid_default_msvc(self): symbol = MSCommon.vc.msvc_default_version() version_def = MSCommon.msvc_version_components(symbol) @@ -299,7 +309,7 @@ def test_valid_default_msvc(self): toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=False) toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=True) toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=True) - if version_def in installed_vcs_components and version_def.msvc_vernum >= 14.1: + if version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) @@ -316,7 +326,7 @@ def test_valid_vcver(self): toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True) toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) - if version_def in installed_vcs_components and version_def.msvc_vernum >= 14.1: + if version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol))) @@ -332,6 +342,7 @@ def test_invalid_vcver(self): _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol) +@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") class MsvcQueryVersionToolsetTests(unittest.TestCase): def test_valid_default_msvc(self): From 61c7fb50ea1fb207549490a747e9c3ec9475915f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 14 Jul 2022 08:03:56 -0400 Subject: [PATCH 078/108] Fix new tests in vcTests.py to run on non-windows platforms. --- SCons/Tool/MSCommon/vcTests.py | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 376a0d5df2..d26441617a 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -24,7 +24,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import sys import os import os.path import unittest @@ -240,7 +239,9 @@ def runTest(self): self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) -@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") +_HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False + + class MsvcSdkVersionsTests(unittest.TestCase): def test_valid_default_msvc(self): @@ -248,7 +249,7 @@ def test_valid_default_msvc(self): version_def = MSCommon.msvc_version_components(symbol) for msvc_uwp_app in (True, False): sdk_list = MSCommon.vc.msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) - if version_def.msvc_vernum >= 14.0: + if _HAVE_MSVC and version_def.msvc_vernum >= 14.0: self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(None))) else: self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None))) @@ -258,7 +259,7 @@ def test_valid_vcver(self): version_def = MSCommon.msvc_version_components(symbol) for msvc_uwp_app in (True, False): sdk_list = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - if version_def.msvc_vernum >= 14.0: + if _HAVE_MSVC and version_def.msvc_vernum >= 14.0: self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) else: self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) @@ -290,7 +291,6 @@ def test_invalid_vcver_toolsets(self): _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) -@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") class MsvcToolsetVersionsTests(unittest.TestCase): _installed_vcs_components = None @@ -309,7 +309,7 @@ def test_valid_default_msvc(self): toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=False) toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=True) toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=True) - if version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: + if _HAVE_MSVC and version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) @@ -342,7 +342,6 @@ def test_invalid_vcver(self): _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol) -@unittest.skipUnless(sys.platform.startswith("win"), "requires Windows") class MsvcQueryVersionToolsetTests(unittest.TestCase): def test_valid_default_msvc(self): @@ -350,9 +349,12 @@ def test_valid_default_msvc(self): msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( version=None, prefer_newest=prefer_newest ) - self.assertTrue(msvc_version, "msvc_version is undefined for msvc version {}".format(repr(None))) + expect = (_HAVE_MSVC and msvc_version) or (not _HAVE_MSVC and not msvc_version) + self.assertTrue(expect, "unexpected msvc_version {} for for msvc version {}".format( + repr(msvc_version), repr(None) + )) version_def = MSCommon.msvc_version_components(msvc_version) - if version_def.msvc_vernum > 14.0: + if _HAVE_MSVC and version_def.msvc_vernum > 14.0: # VS2017 and later for toolset version self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format( repr(None) From a05b891171c549cd3082a10b90123348b0a41c56 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 14 Jul 2022 09:26:50 -0400 Subject: [PATCH 079/108] Add WinSDK tests. --- SCons/Tool/MSCommon/MSVC/WinSDK.py | 5 +- SCons/Tool/MSCommon/MSVC/WinSDKTests.py | 96 +++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 SCons/Tool/MSCommon/MSVC/WinSDKTests.py diff --git a/SCons/Tool/MSCommon/MSVC/WinSDK.py b/SCons/Tool/MSCommon/MSVC/WinSDK.py index f1fb961112..6d18d07307 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDK.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDK.py @@ -199,7 +199,6 @@ def _verify_sdk_dispatch_map(): return None def _version_list_sdk_map(version_list): - sdk_map_list = [] for version in version_list: func, reg_version = _sdk_dispatch_map[version] @@ -237,6 +236,10 @@ def get_msvc_sdk_version_list(msvc_version, msvc_uwp_app=False): sdk_versions = [] verstr = Util.get_msvc_version_prefix(msvc_version) + if not verstr: + debug('msvc_version is not defined') + return sdk_versions + vs_def = Config.MSVC_VERSION_EXTERNAL.get(verstr, None) if not vs_def: debug('vs_def is not defined') diff --git a/SCons/Tool/MSCommon/MSVC/WinSDKTests.py b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py new file mode 100644 index 0000000000..d8ede1eea2 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py @@ -0,0 +1,96 @@ +# 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. + +""" +Test Windows SDK functions for Microsoft Visual C/C++. +""" + +import unittest + +from SCons.Tool.MSCommon.MSVC import Config +from SCons.Tool.MSCommon.MSVC import WinSDK +from SCons.Tool.MSCommon.MSVC import Registry +from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError + +_REGISTRY_SDK_QUERY_PATHS = Registry.sdk_query_paths + +def registry_sdk_query_paths(version): + # return duplicate sdk version roots + sdk_roots = _REGISTRY_SDK_QUERY_PATHS(version) + if sdk_roots: + sdk_roots = sdk_roots + sdk_roots + return sdk_roots + +Registry.sdk_query_paths = registry_sdk_query_paths + +class WinSDKTests(unittest.TestCase): + + def test_verify(self): + _MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS + msvc_sdk_versions = set(Config.MSVC_SDK_VERSIONS) + msvc_sdk_versions.add('99.0') + Config.MSVC_SDK_VERSIONS = msvc_sdk_versions + with self.assertRaises(MSVCInternalError): + WinSDK.verify() + Config.MSVC_SDK_VERSIONS = _MSVC_SDK_VERSIONS + + def _run_reset(self): + WinSDK.reset() + self.assertFalse(WinSDK._sdk_map_cache, "WinSDK._sdk_map_cache was not reset") + self.assertFalse(WinSDK._sdk_cache, "WinSDK._sdk_cache was not reset") + + def _run_get_msvc_sdk_version_list(self): + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + for msvc_uwp_app in (True, False): + _ = WinSDK.get_msvc_sdk_version_list(vcver, msvc_uwp_app=msvc_uwp_app) + + def _run_version_list_sdk_map(self): + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + vs_def = Config.MSVC_VERSION_SUFFIX.get(vcver) + if not vs_def.vc_sdk_versions: + continue + _ = WinSDK._version_list_sdk_map(vs_def.vc_sdk_versions) + + def test_version_list_sdk_map(self): + self._run_version_list_sdk_map() + self._run_version_list_sdk_map() + self.assertTrue(WinSDK._sdk_map_cache, "WinSDK._sdk_map_cache is empty") + + def test_get_msvc_sdk_version_list(self): + self._run_get_msvc_sdk_version_list() + self._run_get_msvc_sdk_version_list() + self.assertTrue(WinSDK._sdk_cache, "WinSDK._sdk_cache is empty") + + def test_get_msvc_sdk_version_list_empty(self): + for vcver in [None, '', '99', '99.9']: + sdk_versions = WinSDK.get_msvc_sdk_version_list(vcver) + self.assertFalse(sdk_versions, "sdk_versions list was not empty for msvc version {}".format( + repr(vcver) + )) + + def test_reset(self): + self._run_reset() + +if __name__ == "__main__": + unittest.main() + From 7db4e09f80ebe250cdb930ed1211c09f39e9fdb7 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Thu, 14 Jul 2022 15:58:12 -0400 Subject: [PATCH 080/108] Rework special case for 9.0 VCForPython handling (vc dir and batchfile location). Add additional tests. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 7 + .../MSCommon/MSVC/ScriptArgumentsTests.py | 151 ++++++++++++++++++ SCons/Tool/MSCommon/MSVC/WinSDKTests.py | 7 +- SCons/Tool/MSCommon/vc.py | 24 ++- SCons/Tool/MSCommon/vcTests.py | 4 +- test/MSVC/MSVC_TOOLSET_VERSION.py | 57 ------- 6 files changed, 175 insertions(+), 75 deletions(-) create mode 100644 SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 85b692c0b6..c66f03ae4d 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -104,6 +104,9 @@ def _verify_re_sdk_dispatch_map(): # Force default toolset argument _MSVC_FORCE_DEFAULT_TOOLSET = False +# Force default arguments +_MSVC_FORCE_DEFAULT_ARGUMENTS = False + def _msvc_force_default_sdk(force=True): global _MSVC_FORCE_DEFAULT_SDK _MSVC_FORCE_DEFAULT_SDK = force @@ -115,8 +118,12 @@ def _msvc_force_default_toolset(force=True): debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force)) def msvc_force_default_arguments(force=True): + global _MSVC_FORCE_DEFAULT_ARGUMENTS + prev_policy = _MSVC_FORCE_DEFAULT_ARGUMENTS + _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) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py new file mode 100644 index 0000000000..90a76c2fe5 --- /dev/null +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -0,0 +1,151 @@ +# 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. + +""" +Test batch file argument functions for Microsoft Visual C/C++. +""" + +import unittest + +import SCons.Environment + +from SCons.Tool.MSCommon import vc + +from SCons.Tool.MSCommon.MSVC import Config +from SCons.Tool.MSCommon.MSVC import Util +from SCons.Tool.MSCommon.MSVC import ScriptArguments +from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError + +def Environment(**kwargs): + tools_key = 'tools' + if tools_key not in kwargs: + tools = [] + else: + tools = kwargs[tools_key] + del kwargs[tools_key] + return SCons.Environment.Base(tools=tools, **kwargs) + +class ScriptArgumentsTests(unittest.TestCase): + + # all versions + _all_versions_list = [] + + # installed versions + _vcdir_list = [] + + @classmethod + def setUpClass(cls): + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + version_def = Util.msvc_version_components(vcver) + vc_dir = vc.find_vc_pdir(None, vcver) + t = (version_def, vc_dir) + cls._all_versions_list.append(t) + if vc_dir: + cls._vcdir_list.append(t) + + def setUp(self): + self.all_versions_list = self.__class__._all_versions_list + self.vcdir_list = self.__class__._vcdir_list + + def test_verify(self): + _MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS + msvc_sdk_versions = set(Config.MSVC_SDK_VERSIONS) + msvc_sdk_versions.add('99.0') + Config.MSVC_SDK_VERSIONS = msvc_sdk_versions + with self.assertRaises(MSVCInternalError): + ScriptArguments.verify() + Config.MSVC_SDK_VERSIONS = _MSVC_SDK_VERSIONS + + def test_msvc_script_arguments_defaults(self): + env = Environment() + func = ScriptArguments.msvc_script_arguments + # disable forcing sdk and toolset versions as arguments + force = ScriptArguments.msvc_force_default_arguments(force=False) + for version_def, vc_dir in self.vcdir_list: + for arg in ('', 'arch'): + scriptargs = func(env, version_def.msvc_version, vc_dir, arg) + self.assertTrue(scriptargs == arg, "{}({},{}) != {} [force=False]".format( + func.__name__, repr(version_def.msvc_version), repr(arg), repr(scriptargs) + )) + # enable forcing sdk and toolset versions as arguments + force = ScriptArguments.msvc_force_default_arguments(force=True) + for version_def, vc_dir in self.vcdir_list: + scriptargs = func(env, version_def.msvc_version, vc_dir, '') + for arg in ('', 'arch'): + scriptargs = func(env, version_def.msvc_version, vc_dir, arg) + if version_def.msvc_vernum >= 14.0: + if arg and scriptargs.startswith(arg): + testargs = scriptargs[len(arg):].lstrip() + else: + testargs = scriptargs + self.assertTrue(testargs, "{}({},{}) is empty [force=True]".format( + func.__name__, repr(version_def.msvc_version), repr(arg) + )) + else: + self.assertTrue(scriptargs == arg, "{}({},{}) != {} [force=True]".format( + func.__name__, repr(version_def.msvc_version), repr(arg), repr(scriptargs) + )) + # restore forcing sdk and toolset versions as arguments + ScriptArguments.msvc_force_default_arguments(force=force) + + def test_msvc_toolset_versions_internal(self): + func = ScriptArguments._msvc_toolset_versions_internal + for version_def, vc_dir in self.vcdir_list: + for full in (True, False): + for sxs in (True, False): + toolset_versions = func(version_def.msvc_version, vc_dir, full=full, sxs=sxs) + if version_def.msvc_vernum < 14.1: + self.assertTrue(toolset_versions is None, "{}({},{},full={},sxs={}) is not None ({})".format( + func.__name__, repr(version_def.msvc_version), repr(vc_dir), repr(full), repr(sxs), + repr(toolset_versions) + )) + elif full: + self.assertTrue(len(toolset_versions), "{}({},{},full={},sxs={}) is empty".format( + func.__name__, repr(version_def.msvc_version), repr(vc_dir), repr(full), repr(sxs) + )) + elif sxs: + # sxs list can be empty + pass + else: + self.assertFalse(len(toolset_versions), "{}({},{},full={},sxs={}) is not empty".format( + func.__name__, repr(version_def.msvc_version), repr(vc_dir), repr(full), repr(sxs) + )) + + def test_msvc_toolset_internal(self): + func = ScriptArguments._msvc_toolset_internal + for version_def, vc_dir in self.vcdir_list: + toolset_versions = ScriptArguments._msvc_toolset_versions_internal(version_def.msvc_version, vc_dir, full=True, sxs=True) + if not toolset_versions: + continue + for toolset_version in toolset_versions: + _ = func(version_def.msvc_version, toolset_version, vc_dir) + + def test_reset(self): + ScriptArguments.reset() + self.assertTrue(ScriptArguments._toolset_have140_cache is None, "ScriptArguments._toolset_have140_cache was not reset") + self.assertFalse(ScriptArguments._toolset_version_cache, "ScriptArguments._toolset_version_cache was not reset") + self.assertFalse(ScriptArguments._toolset_default_cache, "ScriptArguments._toolset_default_cache was not reset") + +if __name__ == "__main__": + unittest.main() + diff --git a/SCons/Tool/MSCommon/MSVC/WinSDKTests.py b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py index d8ede1eea2..6b074e0ffe 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDKTests.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py @@ -82,10 +82,11 @@ def test_get_msvc_sdk_version_list(self): self.assertTrue(WinSDK._sdk_cache, "WinSDK._sdk_cache is empty") def test_get_msvc_sdk_version_list_empty(self): + func = WinSDK.get_msvc_sdk_version_list for vcver in [None, '', '99', '99.9']: - sdk_versions = WinSDK.get_msvc_sdk_version_list(vcver) - self.assertFalse(sdk_versions, "sdk_versions list was not empty for msvc version {}".format( - repr(vcver) + sdk_versions = func(vcver) + self.assertFalse(sdk_versions, "{}: sdk versions list was not empty for msvc version {}".format( + func.__name__, repr(vcver) )) def test_reset(self): diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 9d4bc9a163..d19e30a62d 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -751,6 +751,9 @@ def find_vc_pdir(env, msvc_version): except OSError: debug('no VC registry key %s', repr(key)) else: + if msvc_version == '9.0' and key.lower().endswith('\\vcforpython\\9.0\\installdir'): + # Visual C++ for Python registry key is installdir (root) not productdir (vc) + comps = os.path.join(comps, 'VC') debug('found VC in registry: %s', comps) if os.path.exists(comps): return comps @@ -790,6 +793,9 @@ def find_batch_file(env, msvc_version, host_arch, target_arch): # 14.0 (VS2015) to 8.0 (VS2005) arg, _ = _LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[(host_arch, target_arch)] batfilename = os.path.join(pdir, "vcvarsall.bat") + if msvc_version == '9.0' and not os.path.exists(batfilename): + # Visual C++ for Python batch file is in installdir (root) not productdir (vc) + batfilename = os.path.normpath(os.path.join(pdir, os.pardir, "vcvarsall.bat")) else: # 7.1 (VS2003) and earlier pdir = os.path.join(pdir, "Bin") @@ -884,11 +890,6 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): elif 14 >= vernum >= 8: # 14.0 (VS2015) to 8.0 (VS2005) - cl_path_prefixes = [None] - if msvc_version == '9.0': - # Visual C++ for Python registry key is installdir (root) not productdir (vc) - cl_path_prefixes.append(('VC',)) - for host_platform, target_platform in host_target_list: debug('host platform %s, target platform %s for version %s', host_platform, target_platform, msvc_version) @@ -899,15 +900,12 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): continue _, cl_path_comps = batcharg_clpathcomps - for cl_path_prefix in cl_path_prefixes: - - cl_path_comps_adj = cl_path_prefix + cl_path_comps if cl_path_prefix else cl_path_comps - cl_path = os.path.join(vc_dir, *cl_path_comps_adj, _CL_EXE_NAME) - debug('checking for %s at %s', _CL_EXE_NAME, cl_path) + cl_path = os.path.join(vc_dir, *cl_path_comps, _CL_EXE_NAME) + debug('checking for %s at %s', _CL_EXE_NAME, cl_path) - if os.path.exists(cl_path): - debug('found %s', _CL_EXE_NAME) - return True + if os.path.exists(cl_path): + debug('found %s', _CL_EXE_NAME) + return True elif 8 > vernum >= 6: # 7.1 (VS2003) to 6.0 (VS6) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index d26441617a..bf5af53507 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -337,7 +337,7 @@ def test_valid_vcver(self): self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(symbol))) def test_invalid_vcver(self): - for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']: with self.assertRaises(MSCommon.vc.MSVCArgumentError): _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol) @@ -394,7 +394,7 @@ def test_valid_vcver_toolsets(self): )) def test_invalid_vcver(self): - for symbol in ['6.0Exp', '14.3Exp', '99', '14.1Bug']: + for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']: for prefer_newest in (True, False): with self.assertRaises(MSCommon.vc.MSVCArgumentError): _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index aa4213fb2e..aa3c44ca54 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -175,51 +175,6 @@ ) test.must_contain_all(test.stderr(), expect) - if supported == default_version: - msvc_version_list = ['None', repr(supported.msvc_version)] - else: - msvc_version_list = [repr(supported.msvc_version)] - - for test_version in msvc_version_list: - - # msvc toolsets detected - test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_toolset_versions - DefaultEnvironment(tools=[]) - for full in (True, False): - for sxs in (True, False): - toolsets = msvc_toolset_versions(msvc_version={}, full=full, sxs=sxs) - """.format(test_version) - )) - test.run(arguments='-Q -s', stdout='') - - # msvc query version toolset - test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_query_version_toolset - DefaultEnvironment(tools=[]) - for prefer_newest in (True, False): - msvc_version, msvc_toolset_version = msvc_query_version_toolset(version={}, prefer_newest=prefer_newest) - """.format(test_version) - )) - test.run(arguments='-Q -s', stdout='') - - # msvc_version is invalid - invalid_msvc_version = '12.9' - test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_toolset_versions - DefaultEnvironment(tools=[]) - toolsets = msvc_toolset_versions(msvc_version={}, full=True, sxs=True) - """.format(repr(invalid_msvc_version)) - )) - test.run(arguments='-Q -s', status=2, stderr=None) - expect = "MSVCArgumentError: Unsupported msvc version {}:".format( - repr(invalid_msvc_version) - ) - test.must_contain_all(test.stderr(), expect) - if LT_VS2017_versions: # VS2015 and earlier for toolset argument error @@ -238,18 +193,6 @@ ) test.must_contain_all(test.stderr(), expect) - # msvc_toolset_versions returns None for versions that don't support toolsets - test.write('SConstruct', textwrap.dedent( - """ - from SCons.Tool.MSCommon import msvc_toolset_versions - DefaultEnvironment(tools=[]) - toolsets = msvc_toolset_versions(msvc_version={}, full=True, sxs=True) - if toolsets is not None: - raise RuntimeError("Expected toolsets==None") - """.format(repr(unsupported.msvc_version)) - )) - test.run(arguments='-Q -s', stdout='') - if LT_VS2015_versions: # VS2013 and earlier for script argument error From 8ad53e893644bd84c0044ee4ee987140ac6eccee Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Fri, 15 Jul 2022 08:37:41 -0400 Subject: [PATCH 081/108] Add additional tests for msvc_sdk_versions, msvc_toolset_versions, and msvc_query_toolset_version. --- SCons/Tool/MSCommon/vcTests.py | 90 +++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 8 deletions(-) diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index bf5af53507..b66e319655 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -241,19 +241,37 @@ def runTest(self): _HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False +_orig_msvc_default_version = MSCommon.vc.msvc_default_version + +def _msvc_default_version_none(): + return None + +def _enable_msvc_default_version_none(): + MSCommon.vc.msvc_default_version = _msvc_default_version_none + +def _restore_msvc_default_version(): + MSCommon.vc.msvc_default_version = _orig_msvc_default_version class MsvcSdkVersionsTests(unittest.TestCase): + """Test msvc_sdk_versions""" - def test_valid_default_msvc(self): + def run_valid_default_msvc(self): symbol = MSCommon.vc.msvc_default_version() version_def = MSCommon.msvc_version_components(symbol) for msvc_uwp_app in (True, False): sdk_list = MSCommon.vc.msvc_sdk_versions(version=None, msvc_uwp_app=msvc_uwp_app) - if _HAVE_MSVC and version_def.msvc_vernum >= 14.0: + if symbol and version_def.msvc_vernum >= 14.0: self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(None))) else: self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None))) + def test_valid_default_msvc(self): + if _HAVE_MSVC: + _enable_msvc_default_version_none() + self.run_valid_default_msvc() + _restore_msvc_default_version() + self.run_valid_default_msvc() + def test_valid_vcver(self): for symbol in MSCommon.vc._VCVER: version_def = MSCommon.msvc_version_components(symbol) @@ -292,6 +310,7 @@ def test_invalid_vcver_toolsets(self): class MsvcToolsetVersionsTests(unittest.TestCase): + """Test msvc_toolset_versions""" _installed_vcs_components = None @@ -302,14 +321,14 @@ def setUpClass(cls): def setUp(self): self.installed_vcs_components = self.__class__._installed_vcs_components - def test_valid_default_msvc(self): + def run_valid_default_msvc(self): symbol = MSCommon.vc.msvc_default_version() version_def = MSCommon.msvc_version_components(symbol) toolset_none_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=False) toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=False) toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=True) toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=True) - if _HAVE_MSVC and version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: + if symbol and version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) @@ -319,6 +338,13 @@ def test_valid_default_msvc(self): self.assertFalse(toolset_all_list, "Toolset all list is not empty for msvc version {}".format(repr(None))) self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(None))) + def test_valid_default_msvc(self): + if _HAVE_MSVC: + _enable_msvc_default_version_none() + self.run_valid_default_msvc() + _restore_msvc_default_version() + self.run_valid_default_msvc() + def test_valid_vcver(self): for symbol in MSCommon.vc._VCVER: version_def = MSCommon.msvc_version_components(symbol) @@ -343,23 +369,31 @@ def test_invalid_vcver(self): class MsvcQueryVersionToolsetTests(unittest.TestCase): + """Test msvc_query_toolset_version""" - def test_valid_default_msvc(self): + def run_valid_default_msvc(self, have_msvc): for prefer_newest in (True, False): msvc_version, msvc_toolset_version = MSCommon.vc.msvc_query_version_toolset( version=None, prefer_newest=prefer_newest ) - expect = (_HAVE_MSVC and msvc_version) or (not _HAVE_MSVC and not msvc_version) + expect = (have_msvc and msvc_version) or (not have_msvc and not msvc_version) self.assertTrue(expect, "unexpected msvc_version {} for for msvc version {}".format( repr(msvc_version), repr(None) )) version_def = MSCommon.msvc_version_components(msvc_version) - if _HAVE_MSVC and version_def.msvc_vernum > 14.0: + if have_msvc and version_def.msvc_vernum > 14.0: # VS2017 and later for toolset version self.assertTrue(msvc_toolset_version, "msvc_toolset_version is undefined for msvc version {}".format( repr(None) )) + def test_valid_default_msvc(self): + if _HAVE_MSVC: + _enable_msvc_default_version_none() + self.run_valid_default_msvc(have_msvc=False) + _restore_msvc_default_version() + self.run_valid_default_msvc(have_msvc=_HAVE_MSVC) + def test_valid_vcver(self): for symbol in MSCommon.vc._VCVER: version_def = MSCommon.msvc_version_components(symbol) @@ -393,6 +427,46 @@ def test_valid_vcver_toolsets(self): repr(toolset) )) + def notfound_toolset_list(self, toolset_seen, toolset_list): + new_toolset_list = [] + if not toolset_list: + return new_toolset_list + for toolset_version in toolset_list: + version = toolset_version + comps = version.split('.') + if len(comps) != 3: + continue + # full versions only + nloop = 0 + while nloop < 10: + ival = int(comps[-1]) + if ival == 0: + ival = 1000000 + ival -= 1 + version = '{}.{}.{:05d}'.format(comps[0], comps[1], ival) + if version not in toolset_seen: + toolset_seen.add(version) + new_toolset_list.append(version) + break + nloop += 1 + return new_toolset_list + + def test_toolset_not_found(self): + toolset_seen = set() + toolset_lists = [] + for symbol in MSCommon.vc._VCVER: + toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) + if toolset_list is None: + continue + toolset_seen.update(toolset_list) + toolset_lists.append(toolset_list) + for toolset_list in toolset_lists: + notfound_toolset_list = self.notfound_toolset_list(toolset_seen, toolset_list) + for toolset in notfound_toolset_list: + for prefer_newest in (True, False): + with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound): + _ = MSCommon.vc.msvc_query_version_toolset(version=toolset, prefer_newest=prefer_newest) + def test_invalid_vcver(self): for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']: for prefer_newest in (True, False): @@ -400,7 +474,7 @@ def test_invalid_vcver(self): _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) def test_invalid_vcver_toolsets(self): - for symbol in ['14.31.123456', '14.31.1.1']: + for symbol in ['14.16.00000Exp', '14.00.00001', '14.31.123456', '14.31.1.1']: for prefer_newest in (True, False): with self.assertRaises(MSCommon.vc.MSVCArgumentError): _ = MSCommon.vc.msvc_query_version_toolset(version=symbol, prefer_newest=prefer_newest) From 6d35399005cf32b92dac1c1b54436b01565021a0 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 16 Jul 2022 08:16:38 -0400 Subject: [PATCH 082/108] Rework unit tests: move global data to Data class and move monkey patching to Patch class. --- SCons/Tool/MSCommon/MSVC/ConfigTests.py | 49 +++++++++++--- SCons/Tool/MSCommon/MSVC/DispatcherTests.py | 52 ++++++--------- .../MSCommon/MSVC/ScriptArgumentsTests.py | 66 +++++++++++-------- SCons/Tool/MSCommon/MSVC/UtilTests.py | 8 ++- SCons/Tool/MSCommon/MSVC/WinSDKTests.py | 61 +++++++++++++---- SCons/Tool/MSCommon/vcTests.py | 61 ++++++++++------- 6 files changed, 189 insertions(+), 108 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ConfigTests.py b/SCons/Tool/MSCommon/MSVC/ConfigTests.py index 570861c7d4..0528930934 100644 --- a/SCons/Tool/MSCommon/MSVC/ConfigTests.py +++ b/SCons/Tool/MSCommon/MSVC/ConfigTests.py @@ -31,28 +31,57 @@ from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError -_VCVER = vc._VCVER -_MSVC_VERSION_INTERNAL = Config.MSVC_VERSION_INTERNAL +class Patch: + + class vc: + + class _VCVER: + + _VCVER = vc._VCVER + + @classmethod + def enable_copy(cls): + hook = list(cls._VCVER) + vc._VCVER = hook + return hook + + @classmethod + def restore(cls): + vc._VCVER = cls._VCVER + + class Config: + + class MSVC_VERSION_INTERNAL: + + MSVC_VERSION_INTERNAL = Config.MSVC_VERSION_INTERNAL + + @classmethod + def enable_copy(cls): + hook = dict(cls.MSVC_VERSION_INTERNAL) + Config.MSVC_VERSION_INTERNAL = hook + return hook + + @classmethod + def restore(cls): + Config.MSVC_VERSION_INTERNAL = cls.MSVC_VERSION_INTERNAL class ConfigTests(unittest.TestCase): def test_vcver(self): # all vc._VCVER in Config.MSVC_VERSION_SUFFIX - _ADD_VCVER = list(_VCVER) - _ADD_VCVER.append('99.9') - vc._VCVER = _ADD_VCVER + _VCVER = Patch.vc._VCVER.enable_copy() + _VCVER.append('99.9') with self.assertRaises(MSVCInternalError): Config.verify() - vc._VCVER = _VCVER + Patch.vc._VCVER.restore() def test_msvc_version_internal(self): # all vc._VCVER numstr in Config.MSVC_VERSION_INTERNAL - _DEL_MSVC_VERSION_INTERNAL = dict(_MSVC_VERSION_INTERNAL) - del _DEL_MSVC_VERSION_INTERNAL['14.3'] - Config.MSVC_VERSION_INTERNAL = _DEL_MSVC_VERSION_INTERNAL + MSVC_VERSION_INTERNAL = Patch.Config.MSVC_VERSION_INTERNAL.enable_copy() + del MSVC_VERSION_INTERNAL['14.3'] with self.assertRaises(MSVCInternalError): Config.verify() - Config.MSVC_VERSION_INTERNAL = _MSVC_VERSION_INTERNAL + Patch.Config.MSVC_VERSION_INTERNAL.restore() def test_verify(self): Config.verify() diff --git a/SCons/Tool/MSCommon/MSVC/DispatcherTests.py b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py index 01379fc4c3..ce7880c07f 100644 --- a/SCons/Tool/MSCommon/MSVC/DispatcherTests.py +++ b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py @@ -30,58 +30,52 @@ from SCons.Tool.MSCommon import MSVC MSVC.Dispatcher.register_modulename(__name__) +class Data: + + reset_count = 0 + verify_count = 0 + # current module - not callable _reset = None reset = None _verify = None verify = None -reset_count = 0 -verify_count = 0 - class StaticMethods: @staticmethod def _reset(): - global reset_count - reset_count += 1 + Data.reset_count += 1 @staticmethod def reset(): - global reset_count - reset_count += 1 + Data.reset_count += 1 @staticmethod def _verify(): - global verify_count - verify_count += 1 + Data.verify_count += 1 @staticmethod def verify(): - global verify_count - verify_count += 1 + Data.verify_count += 1 class ClassMethods: @classmethod def _reset(cls): - global reset_count - reset_count += 1 + Data.reset_count += 1 @classmethod def reset(cls): - global reset_count - reset_count += 1 + Data.reset_count += 1 @classmethod def _verify(cls): - global verify_count - verify_count += 1 + Data.verify_count += 1 @classmethod def verify(cls): - global verify_count - verify_count += 1 + Data.verify_count += 1 class NotCallable: @@ -98,28 +92,24 @@ class NotCallable: class DispatcherTests(unittest.TestCase): def test_dispatcher_reset(self): - global reset_count MSVC.Dispatcher.reset() - self.assertTrue(reset_count == 4, "MSVC.Dispatcher.reset() count failed") - reset_count = 0 + self.assertTrue(Data.reset_count == 4, "MSVC.Dispatcher.reset() count failed") + Data.reset_count = 0 def test_dispatcher_verify(self): - global verify_count MSVC.Dispatcher.verify() - self.assertTrue(verify_count == 4, "MSVC.Dispatcher.verify() count failed") - verify_count = 0 + self.assertTrue(Data.verify_count == 4, "MSVC.Dispatcher.verify() count failed") + Data.verify_count = 0 def test_msvc_reset(self): - global reset_count MSVC._reset() - self.assertTrue(reset_count == 4, "MSVC._reset() count failed") - reset_count = 0 + self.assertTrue(Data.reset_count == 4, "MSVC._reset() count failed") + Data.reset_count = 0 def test_msvc_verify(self): - global verify_count MSVC._verify() - self.assertTrue(verify_count == 4, "MSVC._verify() count failed") - verify_count = 0 + self.assertTrue(Data.verify_count == 4, "MSVC._verify() count failed") + Data.verify_count = 0 if __name__ == "__main__": unittest.main() diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 90a76c2fe5..f3884d2557 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -45,43 +45,55 @@ def Environment(**kwargs): del kwargs[tools_key] return SCons.Environment.Base(tools=tools, **kwargs) -class ScriptArgumentsTests(unittest.TestCase): +class Data: # all versions - _all_versions_list = [] + ALL_VERSIONS_PAIRS = [] # installed versions - _vcdir_list = [] - - @classmethod - def setUpClass(cls): - for vcver in Config.MSVC_VERSION_SUFFIX.keys(): - version_def = Util.msvc_version_components(vcver) - vc_dir = vc.find_vc_pdir(None, vcver) - t = (version_def, vc_dir) - cls._all_versions_list.append(t) - if vc_dir: - cls._vcdir_list.append(t) - - def setUp(self): - self.all_versions_list = self.__class__._all_versions_list - self.vcdir_list = self.__class__._vcdir_list + INSTALLED_VERSIONS_PAIRS = [] + + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + version_def = Util.msvc_version_components(vcver) + vc_dir = vc.find_vc_pdir(None, vcver) + t = (version_def, vc_dir) + ALL_VERSIONS_PAIRS.append(t) + if vc_dir: + INSTALLED_VERSIONS_PAIRS.append(t) + +class Patch: + + class Config: + + class MSVC_SDK_VERSIONS: + + MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS + + @classmethod + def enable_copy(cls): + hook = set(cls.MSVC_SDK_VERSIONS) + Config.MSVC_SDK_VERSIONS = hook + return hook + + @classmethod + def restore(cls): + Config.MSVC_SDK_VERSIONS = cls.MSVC_SDK_VERSIONS + +class ScriptArgumentsTests(unittest.TestCase): def test_verify(self): - _MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS - msvc_sdk_versions = set(Config.MSVC_SDK_VERSIONS) - msvc_sdk_versions.add('99.0') - Config.MSVC_SDK_VERSIONS = msvc_sdk_versions + MSVC_SDK_VERSIONS = Patch.Config.MSVC_SDK_VERSIONS.enable_copy() + MSVC_SDK_VERSIONS.add('99.0') with self.assertRaises(MSVCInternalError): ScriptArguments.verify() - Config.MSVC_SDK_VERSIONS = _MSVC_SDK_VERSIONS + Patch.Config.MSVC_SDK_VERSIONS.restore() def test_msvc_script_arguments_defaults(self): - env = Environment() func = ScriptArguments.msvc_script_arguments + env = Environment() # disable forcing sdk and toolset versions as arguments force = ScriptArguments.msvc_force_default_arguments(force=False) - for version_def, vc_dir in self.vcdir_list: + for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: for arg in ('', 'arch'): scriptargs = func(env, version_def.msvc_version, vc_dir, arg) self.assertTrue(scriptargs == arg, "{}({},{}) != {} [force=False]".format( @@ -89,7 +101,7 @@ def test_msvc_script_arguments_defaults(self): )) # enable forcing sdk and toolset versions as arguments force = ScriptArguments.msvc_force_default_arguments(force=True) - for version_def, vc_dir in self.vcdir_list: + for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: scriptargs = func(env, version_def.msvc_version, vc_dir, '') for arg in ('', 'arch'): scriptargs = func(env, version_def.msvc_version, vc_dir, arg) @@ -110,7 +122,7 @@ def test_msvc_script_arguments_defaults(self): def test_msvc_toolset_versions_internal(self): func = ScriptArguments._msvc_toolset_versions_internal - for version_def, vc_dir in self.vcdir_list: + for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: for full in (True, False): for sxs in (True, False): toolset_versions = func(version_def.msvc_version, vc_dir, full=full, sxs=sxs) @@ -133,7 +145,7 @@ def test_msvc_toolset_versions_internal(self): def test_msvc_toolset_internal(self): func = ScriptArguments._msvc_toolset_internal - for version_def, vc_dir in self.vcdir_list: + for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: toolset_versions = ScriptArguments._msvc_toolset_versions_internal(version_def.msvc_version, vc_dir, full=True, sxs=True) if not toolset_versions: continue diff --git a/SCons/Tool/MSCommon/MSVC/UtilTests.py b/SCons/Tool/MSCommon/MSVC/UtilTests.py index 404bf47c94..0497878015 100644 --- a/SCons/Tool/MSCommon/MSVC/UtilTests.py +++ b/SCons/Tool/MSCommon/MSVC/UtilTests.py @@ -32,7 +32,9 @@ from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC import Util -util_parent_dir = os.path.join(os.path.dirname(Util.__file__), os.pardir) +class Data: + + UTIL_PARENT_DIR = os.path.join(os.path.dirname(Util.__file__), os.pardir) class UtilTests(unittest.TestCase): @@ -40,7 +42,7 @@ def test_listdir_dirs(self): func = Util.listdir_dirs for dirname, expect in [ (None, False), ('', False), ('doesnotexist.xyz.abc', False), - (util_parent_dir, True), + (Data.UTIL_PARENT_DIR, True), ]: dirs = func(dirname) self.assertTrue((len(dirs) > 0) == expect, "{}({}): {}".format( @@ -51,7 +53,7 @@ def test_process_path(self): func = Util.process_path for p, expect in [ (None, True), ('', True), - ('doesnotexist.xyz.abc', False), (util_parent_dir, False), + ('doesnotexist.xyz.abc', False), (Data.UTIL_PARENT_DIR, False), ]: rval = func(p) self.assertTrue((p == rval) == expect, "{}({}): {}".format( diff --git a/SCons/Tool/MSCommon/MSVC/WinSDKTests.py b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py index 6b074e0ffe..2a40e9a682 100644 --- a/SCons/Tool/MSCommon/MSVC/WinSDKTests.py +++ b/SCons/Tool/MSCommon/MSVC/WinSDKTests.py @@ -32,27 +32,62 @@ from SCons.Tool.MSCommon.MSVC import Registry from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError -_REGISTRY_SDK_QUERY_PATHS = Registry.sdk_query_paths +class Patch: -def registry_sdk_query_paths(version): - # return duplicate sdk version roots - sdk_roots = _REGISTRY_SDK_QUERY_PATHS(version) - if sdk_roots: - sdk_roots = sdk_roots + sdk_roots - return sdk_roots + class Config: -Registry.sdk_query_paths = registry_sdk_query_paths + class MSVC_SDK_VERSIONS: + + MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS + + @classmethod + def enable_copy(cls): + hook = set(cls.MSVC_SDK_VERSIONS) + Config.MSVC_SDK_VERSIONS = hook + return hook + + @classmethod + def restore(cls): + Config.MSVC_SDK_VERSIONS = cls.MSVC_SDK_VERSIONS + + class Registry: + + class sdk_query_paths: + + sdk_query_paths = Registry.sdk_query_paths + + @classmethod + def sdk_query_paths_duplicate(cls, version): + sdk_roots = cls.sdk_query_paths(version) + sdk_roots = sdk_roots + sdk_roots if sdk_roots else sdk_roots + return sdk_roots + + @classmethod + def enable_duplicate(cls): + hook = cls.sdk_query_paths_duplicate + Registry.sdk_query_paths = hook + return hook + + @classmethod + def restore(cls): + Registry.sdk_query_paths = cls.sdk_query_paths class WinSDKTests(unittest.TestCase): + @classmethod + def setUpClass(cls): + Patch.Registry.sdk_query_paths.enable_duplicate() + + @classmethod + def tearDownClass(cls): + Patch.Registry.sdk_query_paths.restore() + def test_verify(self): - _MSVC_SDK_VERSIONS = Config.MSVC_SDK_VERSIONS - msvc_sdk_versions = set(Config.MSVC_SDK_VERSIONS) - msvc_sdk_versions.add('99.0') - Config.MSVC_SDK_VERSIONS = msvc_sdk_versions + MSVC_SDK_VERSIONS = Patch.Config.MSVC_SDK_VERSIONS.enable_copy() + MSVC_SDK_VERSIONS.add('99.0') with self.assertRaises(MSVCInternalError): WinSDK.verify() - Config.MSVC_SDK_VERSIONS = _MSVC_SDK_VERSIONS + Patch.Config.MSVC_SDK_VERSIONS.restore() def _run_reset(self): WinSDK.reset() diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index b66e319655..e844944694 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -94,14 +94,14 @@ def _createDummyCl(path, add_bin=True): create_path = path if create_path and not os.path.isdir(create_path): os.makedirs(create_path) - + create_this = os.path.join(create_path,'cl.exe') # print("Creating: %s"%create_this) with open(create_this,'w') as ct: ct.write('created') - + def runTest(self): """ Check that all proper HOST_PLATFORM and TARGET_PLATFORM are handled. @@ -115,7 +115,7 @@ def runTest(self): _, clpathcomps = SCons.Tool.MSCommon.vc._LE2015_HOST_TARGET_BATCHARG_CLPATHCOMPS[('x86','x86')] path = os.path.join('.', *clpathcomps) MSVcTestCase._createDummyCl(path, add_bin=False) - + # print("retval:%s"%check(env, '.', '8.0')) @@ -239,18 +239,33 @@ def runTest(self): self.fail('Did not fail when TARGET_ARCH specified as: %s' % env['TARGET_ARCH']) -_HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False +class Data: + + HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False + +class Patch: -_orig_msvc_default_version = MSCommon.vc.msvc_default_version + class MSCommon: -def _msvc_default_version_none(): - return None + class vc: -def _enable_msvc_default_version_none(): - MSCommon.vc.msvc_default_version = _msvc_default_version_none + class msvc_default_version: -def _restore_msvc_default_version(): - MSCommon.vc.msvc_default_version = _orig_msvc_default_version + msvc_default_version = MSCommon.vc.msvc_default_version + + @classmethod + def msvc_default_version_none(cls): + return None + + @classmethod + def enable_none(cls): + hook = cls.msvc_default_version_none + MSCommon.vc.msvc_default_version = hook + return hook + + @classmethod + def restore(cls): + MSCommon.vc.msvc_default_version = cls.msvc_default_version class MsvcSdkVersionsTests(unittest.TestCase): """Test msvc_sdk_versions""" @@ -266,10 +281,10 @@ def run_valid_default_msvc(self): self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(None))) def test_valid_default_msvc(self): - if _HAVE_MSVC: - _enable_msvc_default_version_none() + if Data.HAVE_MSVC: + Patch.MSCommon.vc.msvc_default_version.enable_none() self.run_valid_default_msvc() - _restore_msvc_default_version() + Patch.MSCommon.vc.msvc_default_version.restore() self.run_valid_default_msvc() def test_valid_vcver(self): @@ -277,7 +292,7 @@ def test_valid_vcver(self): version_def = MSCommon.msvc_version_components(symbol) for msvc_uwp_app in (True, False): sdk_list = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - if _HAVE_MSVC and version_def.msvc_vernum >= 14.0: + if Data.HAVE_MSVC and version_def.msvc_vernum >= 14.0: self.assertTrue(sdk_list, "SDK list is empty for msvc version {}".format(repr(symbol))) else: self.assertFalse(sdk_list, "SDK list is not empty for msvc version {}".format(repr(symbol))) @@ -308,7 +323,6 @@ def test_invalid_vcver_toolsets(self): with self.assertRaises(MSCommon.vc.MSVCArgumentError): _ = MSCommon.vc.msvc_sdk_versions(version=symbol, msvc_uwp_app=msvc_uwp_app) - class MsvcToolsetVersionsTests(unittest.TestCase): """Test msvc_toolset_versions""" @@ -339,10 +353,10 @@ def run_valid_default_msvc(self): self.assertFalse(toolset_none_list, "Toolset none list is not empty for msvc version {}".format(repr(None))) def test_valid_default_msvc(self): - if _HAVE_MSVC: - _enable_msvc_default_version_none() + if Data.HAVE_MSVC: + Patch.MSCommon.vc.msvc_default_version.enable_none() self.run_valid_default_msvc() - _restore_msvc_default_version() + Patch.MSCommon.vc.msvc_default_version.restore() self.run_valid_default_msvc() def test_valid_vcver(self): @@ -367,7 +381,6 @@ def test_invalid_vcver(self): with self.assertRaises(MSCommon.vc.MSVCArgumentError): _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol) - class MsvcQueryVersionToolsetTests(unittest.TestCase): """Test msvc_query_toolset_version""" @@ -388,11 +401,11 @@ def run_valid_default_msvc(self, have_msvc): )) def test_valid_default_msvc(self): - if _HAVE_MSVC: - _enable_msvc_default_version_none() + if Data.HAVE_MSVC: + Patch.MSCommon.vc.msvc_default_version.enable_none() self.run_valid_default_msvc(have_msvc=False) - _restore_msvc_default_version() - self.run_valid_default_msvc(have_msvc=_HAVE_MSVC) + Patch.MSCommon.vc.msvc_default_version.restore() + self.run_valid_default_msvc(have_msvc=Data.HAVE_MSVC) def test_valid_vcver(self): for symbol in MSCommon.vc._VCVER: From 279b5c5bae1a476e6fe8d3a1c85124f660e22a53 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 16 Jul 2022 14:43:36 -0400 Subject: [PATCH 083/108] Return previous policy when setting default argument policy. Add more tests. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 9 +- .../MSCommon/MSVC/ScriptArgumentsTests.py | 281 +++++++++++++++++- SCons/Tool/MSCommon/vcTests.py | 82 ++--- 3 files changed, 330 insertions(+), 42 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index c66f03ae4d..1818b93428 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -117,12 +117,13 @@ def _msvc_force_default_toolset(force=True): _MSVC_FORCE_DEFAULT_TOOLSET = force debug('_MSVC_FORCE_DEFAULT_TOOLSET=%s', repr(force)) -def msvc_force_default_arguments(force=True): +def msvc_force_default_arguments(force=None): global _MSVC_FORCE_DEFAULT_ARGUMENTS prev_policy = _MSVC_FORCE_DEFAULT_ARGUMENTS - _MSVC_FORCE_DEFAULT_ARGUMENTS = force - _msvc_force_default_sdk(force) - _msvc_force_default_toolset(force) + 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: diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index f3884d2557..408da291b0 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -30,11 +30,20 @@ import SCons.Environment from SCons.Tool.MSCommon import vc +from SCons.Tool.MSCommon import vcTests from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC import Util +from SCons.Tool.MSCommon.MSVC import WinSDK from SCons.Tool.MSCommon.MSVC import ScriptArguments -from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError + +from SCons.Tool.MSCommon.MSVC.Exceptions import ( + MSVCInternalError, + MSVCArgumentError, + MSVCToolsetVersionNotFound, + MSVCSDKVersionNotFound, + MSVCSpectreLibsNotFound, +) def Environment(**kwargs): tools_key = 'tools' @@ -61,6 +70,73 @@ class Data: if vc_dir: INSTALLED_VERSIONS_PAIRS.append(t) + HAVE_MSVC = True if len(INSTALLED_VERSIONS_PAIRS) else False + + @classmethod + def msvc_toolset_notfound_dict(cls): + return vcTests.Data.msvc_toolset_notfound_dict() + + @classmethod + def msvc_toolset_notfound_version(cls, msvc_version): + d = cls.msvc_toolset_notfound_dict() + notfound_versions = d.get(msvc_version,[]) + if not notfound_versions: + notfound_version = msvc_version + '0.00001' + else: + notfound_version = notfound_versions[0] + return notfound_version + + @classmethod + def _msvc_sdk_notfound_list(cls, sdk_seen, sdk_list): + new_sdk_list = [] + if not sdk_list: + return new_sdk_list + for sdk_version in sdk_list: + version = sdk_version + comps = version.split('.') + if len(comps) != 4: + continue + nloop = 0 + while nloop < 10: + ival = int(comps[-2]) + if ival == 0: + ival = 1000000 + ival -= 1 + version = '{}.{}.{:05d}.{}'.format(comps[0], comps[1], ival, comps[-1]) + if version not in sdk_seen: + new_sdk_list.append(version) + break + nloop += 1 + return new_sdk_list + + _msvc_sdk_notfound_dict = {True: None, False: None} + + @classmethod + def msvc_sdk_notfound_dict(cls, msvc_uwp_app=False): + if cls._msvc_sdk_notfound_dict[msvc_uwp_app] is None: + sdk_seen = set() + sdk_dict = {} + for symbol in Config.MSVC_VERSION_SUFFIX.keys(): + sdk_list = WinSDK.get_msvc_sdk_version_list(symbol, msvc_uwp_app=msvc_uwp_app) + if not sdk_list: + continue + sdk_seen.update(sdk_list) + sdk_dict[symbol] = sdk_list + for key, val in sdk_dict.items(): + sdk_dict[key] = cls._msvc_sdk_notfound_list(sdk_seen, val) + cls._msvc_sdk_notfound_dict[msvc_uwp_app] = sdk_dict + return cls._msvc_sdk_notfound_dict[msvc_uwp_app] + + @classmethod + def msvc_sdk_notfound_version(cls, msvc_version): + d = cls.msvc_sdk_notfound_dict() + notfound_versions = d.get(msvc_version,[]) + if not notfound_versions: + notfound_version = '10.0.00000.1' + else: + notfound_version = notfound_versions[0] + return notfound_version + class Patch: class Config: @@ -100,7 +176,7 @@ def test_msvc_script_arguments_defaults(self): func.__name__, repr(version_def.msvc_version), repr(arg), repr(scriptargs) )) # enable forcing sdk and toolset versions as arguments - force = ScriptArguments.msvc_force_default_arguments(force=True) + ScriptArguments.msvc_force_default_arguments(force=True) for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: scriptargs = func(env, version_def.msvc_version, vc_dir, '') for arg in ('', 'arch'): @@ -144,6 +220,8 @@ def test_msvc_toolset_versions_internal(self): )) def test_msvc_toolset_internal(self): + if not Data.HAVE_MSVC: + return func = ScriptArguments._msvc_toolset_internal for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: toolset_versions = ScriptArguments._msvc_toolset_versions_internal(version_def.msvc_version, vc_dir, full=True, sxs=True) @@ -152,6 +230,205 @@ def test_msvc_toolset_internal(self): for toolset_version in toolset_versions: _ = func(version_def.msvc_version, toolset_version, vc_dir) + def run_msvc_script_args_none(self): + func = ScriptArguments.msvc_script_arguments + for version_def, vc_dir in Data.ALL_VERSIONS_PAIRS: + for kwargs in [ + {'MSVC_SCRIPT_ARGS': None}, + {'MSVC_SCRIPT_ARGS': None, 'MSVC_UWP_APP': None}, + {'MSVC_SCRIPT_ARGS': None, 'MSVC_TOOLSET_VERSION': None}, + {'MSVC_SCRIPT_ARGS': None, 'MSVC_SDK_VERSION': None}, + {'MSVC_SCRIPT_ARGS': None, 'MSVC_SPECTRE_LIBS': None}, + ]: + env = Environment(**kwargs) + _ = func(env, version_def.msvc_version, vc_dir, '') + + def run_msvc_script_args(self): + func = ScriptArguments.msvc_script_arguments + for version_def, vc_dir in Data.ALL_VERSIONS_PAIRS: + if version_def.msvc_vernum >= 14.1: + # VS2017 and later + + # should not raise exception (argument not validated) + env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') + _ = func(env, version_def.msvc_version, vc_dir, '') + + for kwargs in [ + {'MSVC_UWP_APP': False, 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_UWP_APP': '0', 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_UWP_APP': False, 'MSVC_SCRIPT_ARGS': 'store'}, + {'MSVC_UWP_APP': '0', 'MSVC_SCRIPT_ARGS': 'store'}, + {'MSVC_SPECTRE_LIBS': False, 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre'}, + {'MSVC_SPECTRE_LIBS': 'True', 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre'}, # not boolean ignored + ]: + env = Environment(**kwargs) + _ = func(env, version_def.msvc_version, vc_dir, '') + + for msvc_uwp_app in (True, False): + + sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) + for sdk_version in sdk_list: + + if sdk_version == '8.1' and msvc_uwp_app: + + for exc, kwargs in [ + (False, {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}), + (True, {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}), + ]: + env = Environment(**kwargs) + if exc: + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir, '') + else: + _ = func(env, version_def.msvc_version, vc_dir, '') + + else: + + for kwargs in [ + {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + ]: + env = Environment(**kwargs) + _ = func(env, version_def.msvc_version, vc_dir, '') + + for kwargs in [ + {'MSVC_SCRIPT_ARGS': '-vcvars_ver={}'.format(version_def.msvc_verstr)}, + {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr}, + ]: + env = Environment(**kwargs) + _ = func(env, version_def.msvc_version, vc_dir, '') + + msvc_toolset_notfound_version = Data.msvc_toolset_notfound_version(version_def.msvc_version) + + for kwargs in [ + {'MSVC_TOOLSET_VERSION': msvc_toolset_notfound_version}, + ]: + env = Environment(**kwargs) + with self.assertRaises(MSVCToolsetVersionNotFound): + _ = func(env, version_def.msvc_version, vc_dir, '') + + msvc_sdk_notfound_version = Data.msvc_sdk_notfound_version(version_def.msvc_version) + + for kwargs in [ + {'MSVC_SDK_VERSION': msvc_sdk_notfound_version}, + ]: + env = Environment(**kwargs) + with self.assertRaises(MSVCSDKVersionNotFound): + _ = func(env, version_def.msvc_version, vc_dir, '') + + for kwargs, exc_t in [ + ({'MSVC_UWP_APP': True, + 'MSVC_SCRIPT_ARGS': 'uwp' + }, (MSVCArgumentError, ), + ), + ({'MSVC_UWP_APP': True, + 'MSVC_SCRIPT_ARGS': 'uwp undefined store' + }, (MSVCArgumentError, ), + ), + ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, + 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(version_def.msvc_verstr) + }, + (MSVCArgumentError, ), + ), + ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, + 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(version_def.msvc_verstr) + }, + (MSVCArgumentError, ), + ), + ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, + 'MSVC_SCRIPT_ARGS': "-vcvars_ver={0} undefined -vcvars_ver={0}".format(version_def.msvc_verstr) + }, + (MSVCArgumentError, ), + ), + ({'MSVC_SDK_VERSION': '9.1', + }, + (MSVCArgumentError, ), + ), + ({'MSVC_SPECTRE_LIBS': True, + 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre undefined -vcvars_spectre_libs=spectre' + }, + (MSVCArgumentError, MSVCSpectreLibsNotFound), + ), + ({'MSVC_SPECTRE_LIBS': True, + 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre' + }, + (MSVCArgumentError, MSVCSpectreLibsNotFound), + ), + ({'MSVC_SPECTRE_LIBS': True, + 'MSVC_UWP_APP': True, + }, + (MSVCArgumentError, MSVCSpectreLibsNotFound), + ), + ({'MSVC_SPECTRE_LIBS': True, + 'MSVC_TOOLSET_VERSION': '14.00.00000', + }, + (MSVCArgumentError, MSVCSpectreLibsNotFound, MSVCToolsetVersionNotFound), + ), + ]: + env = Environment(**kwargs) + with self.assertRaises(exc_t): + _ = func(env, version_def.msvc_version, vc_dir, '') + + elif version_def.msvc_verstr == '14.0': + # VS2015: MSVC_SDK_VERSION and MSVC_UWP_APP + + env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') + _ = func(env, version_def.msvc_version, vc_dir, '') + + for msvc_uwp_app in (True, False): + + sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) + for sdk_version in sdk_list: + + for kwargs in [ + {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + ]: + env = Environment(**kwargs) + _ = func(env, version_def.msvc_version, vc_dir, '') + + for kwargs in [ + {'MSVC_SPECTRE_LIBS': True, 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, 'MSVC_SCRIPT_ARGS': None}, + ]: + env = Environment(**kwargs) + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir, '') + + else: + # VS2013 and earlier: no arguments + + env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir, '') + + for kwargs in [ + {'MSVC_UWP_APP': True, 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_UWP_APP': '1', 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_SPECTRE_LIBS': True, 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, 'MSVC_SCRIPT_ARGS': None}, + {'MSVC_SDK_VERSION': '10.0.00000.0', 'MSVC_SCRIPT_ARGS': None}, + ]: + env = Environment(**kwargs) + with self.assertRaises(MSVCArgumentError): + _ = func(env, version_def.msvc_version, vc_dir, '') + + def test_msvc_script_args_none(self): + force = ScriptArguments.msvc_force_default_arguments(force=False) + self.run_msvc_script_args_none() + if Data.HAVE_MSVC: + ScriptArguments.msvc_force_default_arguments(force=True) + self.run_msvc_script_args_none() + ScriptArguments.msvc_force_default_arguments(force=force) + + @unittest.skipUnless(Data.HAVE_MSVC, "requires msvc") + def test_msvc_script_args(self): + force = ScriptArguments.msvc_force_default_arguments(force=False) + self.run_msvc_script_args() + ScriptArguments.msvc_force_default_arguments(force=True) + self.run_msvc_script_args() + ScriptArguments.msvc_force_default_arguments(force=force) + def test_reset(self): ScriptArguments.reset() self.assertTrue(ScriptArguments._toolset_have140_cache is None, "ScriptArguments._toolset_have140_cache was not reset") diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index e844944694..53b56bc68a 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -243,6 +243,48 @@ class Data: HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False + @classmethod + def _msvc_toolset_notfound_list(cls, toolset_seen, toolset_list): + new_toolset_list = [] + if not toolset_list: + return new_toolset_list + for toolset_version in toolset_list: + version = toolset_version + comps = version.split('.') + if len(comps) != 3: + continue + # full versions only + nloop = 0 + while nloop < 10: + ival = int(comps[-1]) + if ival == 0: + ival = 1000000 + ival -= 1 + version = '{}.{}.{:05d}'.format(comps[0], comps[1], ival) + if version not in toolset_seen: + new_toolset_list.append(version) + break + nloop += 1 + return new_toolset_list + + _msvc_toolset_notfound_dict = None + + @classmethod + def msvc_toolset_notfound_dict(cls): + if cls._msvc_toolset_notfound_dict is None: + toolset_seen = set() + toolset_dict = {} + for symbol in MSCommon.vc._VCVER: + toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) + if not toolset_list: + continue + toolset_seen.update(toolset_list) + toolset_dict[symbol] = toolset_list + for key, val in toolset_dict.items(): + toolset_dict[key] = cls._msvc_toolset_notfound_list(toolset_seen, val) + cls._msvc_toolset_notfound_dict = toolset_dict + return cls._msvc_toolset_notfound_dict + class Patch: class MSCommon: @@ -440,42 +482,10 @@ def test_valid_vcver_toolsets(self): repr(toolset) )) - def notfound_toolset_list(self, toolset_seen, toolset_list): - new_toolset_list = [] - if not toolset_list: - return new_toolset_list - for toolset_version in toolset_list: - version = toolset_version - comps = version.split('.') - if len(comps) != 3: - continue - # full versions only - nloop = 0 - while nloop < 10: - ival = int(comps[-1]) - if ival == 0: - ival = 1000000 - ival -= 1 - version = '{}.{}.{:05d}'.format(comps[0], comps[1], ival) - if version not in toolset_seen: - toolset_seen.add(version) - new_toolset_list.append(version) - break - nloop += 1 - return new_toolset_list - - def test_toolset_not_found(self): - toolset_seen = set() - toolset_lists = [] - for symbol in MSCommon.vc._VCVER: - toolset_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) - if toolset_list is None: - continue - toolset_seen.update(toolset_list) - toolset_lists.append(toolset_list) - for toolset_list in toolset_lists: - notfound_toolset_list = self.notfound_toolset_list(toolset_seen, toolset_list) - for toolset in notfound_toolset_list: + def test_msvc_query_version_toolset_notfound(self): + toolset_notfound_dict = Data.msvc_toolset_notfound_dict() + for toolset_notfound_list in toolset_notfound_dict.values(): + for toolset in toolset_notfound_list[:1]: for prefer_newest in (True, False): with self.assertRaises(MSCommon.vc.MSVCToolsetVersionNotFound): _ = MSCommon.vc.msvc_query_version_toolset(version=toolset, prefer_newest=prefer_newest) From f879f81dbb77502f0098f84ad8f11398a8179a0e Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sat, 16 Jul 2022 15:45:04 -0400 Subject: [PATCH 084/108] Use installed versions instead of all versions. --- SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 408da291b0..5a3d291c61 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -232,7 +232,7 @@ def test_msvc_toolset_internal(self): def run_msvc_script_args_none(self): func = ScriptArguments.msvc_script_arguments - for version_def, vc_dir in Data.ALL_VERSIONS_PAIRS: + for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: for kwargs in [ {'MSVC_SCRIPT_ARGS': None}, {'MSVC_SCRIPT_ARGS': None, 'MSVC_UWP_APP': None}, @@ -245,7 +245,7 @@ def run_msvc_script_args_none(self): def run_msvc_script_args(self): func = ScriptArguments.msvc_script_arguments - for version_def, vc_dir in Data.ALL_VERSIONS_PAIRS: + for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: if version_def.msvc_vernum >= 14.1: # VS2017 and later From 57d61702dc4f6ec324a3e9e4e8b1e151d63ce538 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 17 Jul 2022 06:02:48 -0400 Subject: [PATCH 085/108] Add version components tuple to version breakdown definitions. --- .../Tool/MSCommon/MSVC/ScriptArgumentsTests.py | 1 - SCons/Tool/MSCommon/MSVC/Util.py | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 5a3d291c61..570d48c80b 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -421,7 +421,6 @@ def test_msvc_script_args_none(self): self.run_msvc_script_args_none() ScriptArguments.msvc_force_default_arguments(force=force) - @unittest.skipUnless(Data.HAVE_MSVC, "requires msvc") def test_msvc_script_args(self): force = ScriptArguments.msvc_force_default_arguments(force=False) self.run_msvc_script_args() diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 55e4464d3e..2c197c6674 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -178,6 +178,7 @@ def is_toolset_sxs(toolset_version): 'msvc_vernum', # msvc version floating point number (e.g, 14.1) 'msvc_major', # msvc major version integer number (e.g., 14) 'msvc_minor', # msvc minor version integer number (e.g., 1) + 'msvc_comps', # msvc version components tuple (e.g., ('14', '1')) ]) def msvc_version_components(vcver): @@ -191,6 +192,7 @@ def msvc_version_components(vcver): msvc_vernum: msvc version floating point number (e.g., 14.1) msvc_major: msvc major version integer number (e.g., 14) msvc_minor: msvc minor version integer number (e.g., 1) + msvc_comps: msvc version components tuple (e.g., ('14', '1')) Args: vcver: str @@ -216,7 +218,8 @@ def msvc_version_components(vcver): msvc_suffix = m.group('suffix') if m.group('suffix') else '' msvc_vernum = float(msvc_verstr) - msvc_major, msvc_minor = [int(x) for x in msvc_verstr.split('.')] + msvc_comps = tuple(msvc_verstr.split('.')) + msvc_major, msvc_minor = [int(x) for x in msvc_comps] msvc_version_components_def = _MSVC_VERSION_COMPONENTS_DEFINITION( msvc_version = msvc_version, @@ -225,6 +228,7 @@ def msvc_version_components(vcver): msvc_vernum = msvc_vernum, msvc_major = msvc_major, msvc_minor = msvc_minor, + msvc_comps = msvc_comps, ) return msvc_version_components_def @@ -236,8 +240,10 @@ def msvc_version_components(vcver): 'msvc_vernum', # msvc version floating point number (e.g, 14.1) 'msvc_major', # msvc major version integer number (e.g., 14) 'msvc_minor', # msvc minor version integer number (e.g., 1) - 'msvc_toolset_version', # msvc toolset version - 'version', # msvc version or msvc toolset version + 'msvc_comps', # msvc version components tuple (e.g., ('14', '1')) + 'msvc_toolset_version', # msvc toolset version + 'msvc_toolset_comps', # msvc toolset version components + 'version', # msvc version or msvc toolset version ]) def msvc_extended_version_components(version): @@ -260,6 +266,7 @@ def msvc_extended_version_components(version): return None msvc_toolset_version = m.group('version') + msvc_toolset_comps = tuple(msvc_toolset_version.split('.')) msvc_verstr = get_msvc_version_prefix(msvc_toolset_version) if not msvc_verstr: @@ -274,7 +281,8 @@ def msvc_extended_version_components(version): msvc_vernum = float(msvc_verstr) - msvc_major, msvc_minor = [int(x) for x in msvc_verstr.split('.')] + msvc_comps = tuple(msvc_verstr.split('.')) + msvc_major, msvc_minor = [int(x) for x in msvc_comps] msvc_extended_version_components_def = _MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION( msvc_version = msvc_version, @@ -283,7 +291,9 @@ def msvc_extended_version_components(version): msvc_vernum = msvc_vernum, msvc_major = msvc_major, msvc_minor = msvc_minor, + msvc_comps = msvc_comps, msvc_toolset_version = msvc_toolset_version, + msvc_toolset_comps = msvc_toolset_comps, version = version, ) From a3c7e6571780c8e4e310df4e76ccad9249f7fd2b Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 17 Jul 2022 09:25:51 -0400 Subject: [PATCH 086/108] Add msvc sdk version components utility function and tuple. Finish script arguments tests. --- .../MSCommon/MSVC/ScriptArgumentsTests.py | 269 +++++++++++++----- SCons/Tool/MSCommon/MSVC/Util.py | 65 +++++ SCons/Tool/MSCommon/MSVC/UtilTests.py | 21 ++ SCons/Tool/MSCommon/__init__.py | 1 + 4 files changed, 292 insertions(+), 64 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 570d48c80b..5a5abad405 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -54,6 +54,67 @@ def Environment(**kwargs): del kwargs[tools_key] return SCons.Environment.Base(tools=tools, **kwargs) + +def _sdk_versions_comps_dict_seen(installed_version_pairs): + + sdk_versions_comps_dict = {} + sdk_versions_seen = set() + + _sdk_version_list_seen = {} + for version_def, _ in installed_version_pairs: + sdk_versions_comps_dict[version_def.msvc_version] = {} + for msvc_uwp_app in (True, False): + sdk_version_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) + key = tuple(sdk_version_list) + if key in _sdk_version_list_seen: + sdk_comps_list = _sdk_version_list_seen[key] + else: + sdk_versions_seen.update(sdk_version_list) + sdk_comps_list = [Util.msvc_sdk_version_components(sdk_version) for sdk_version in sdk_version_list] + _sdk_version_list_seen[key] = sdk_comps_list + sdk_versions_comps_dict[version_def.msvc_version][msvc_uwp_app] = sdk_comps_list + + return sdk_versions_comps_dict, sdk_versions_seen + +def _sdk_versions_notfound(installed_version_pairs, sdk_versions_comps_dict, sdk_versions_seen): + + sdk_versions_notfound_dict = {} + sdk_notfound_seen = {} + + def _make_notfound_version(sdk_seen, sdk_def): + if len(sdk_def.sdk_comps) == 4: + nloop = 0 + while nloop < 10: + ival = int(sdk_def.sdk_comps[-2]) + if ival == 0: + ival = 1000000 + ival -= 1 + version = '{}.{}.{:05d}.{}'.format( + sdk_def.sdk_comps[0], sdk_def.sdk_comps[1], ival, sdk_def.sdk_comps[-1] + ) + if version not in sdk_seen: + return version + nloop += 1 + return None + + for version_def, _ in installed_version_pairs: + sdk_versions_notfound_dict[version_def.msvc_version] = {} + for msvc_uwp_app in (True, False): + sdk_notfound_list = [] + sdk_versions_notfound_dict[version_def.msvc_version][msvc_uwp_app] = sdk_notfound_list + sdk_comps_list = sdk_versions_comps_dict[version_def.msvc_version][msvc_uwp_app] + for sdk_def in sdk_comps_list: + if sdk_def.sdk_version in sdk_notfound_seen: + sdk_notfound_version = sdk_notfound_seen[sdk_def.sdk_version] + else: + sdk_notfound_version = _make_notfound_version(sdk_versions_seen, sdk_def) + sdk_notfound_seen[sdk_def.sdk_version] = sdk_notfound_version + if not sdk_notfound_version: + continue + sdk_notfound_list.append(sdk_notfound_version) + + return sdk_versions_notfound_dict + class Data: # all versions @@ -62,6 +123,9 @@ class Data: # installed versions INSTALLED_VERSIONS_PAIRS = [] + # VS2015 installed + HAVE140_TOOLSET = ScriptArguments._msvc_have140_toolset() + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): version_def = Util.msvc_version_components(vcver) vc_dir = vc.find_vc_pdir(None, vcver) @@ -72,67 +136,48 @@ class Data: HAVE_MSVC = True if len(INSTALLED_VERSIONS_PAIRS) else False + SDK_VERSIONS_COMPS_DICT, SDK_VERSIONS_SEEN = _sdk_versions_comps_dict_seen(INSTALLED_VERSIONS_PAIRS) + + SDK_VERSIONS_NOTFOUND_DICT = _sdk_versions_notfound( + INSTALLED_VERSIONS_PAIRS, SDK_VERSIONS_COMPS_DICT, SDK_VERSIONS_SEEN + ) + @classmethod - def msvc_toolset_notfound_dict(cls): - return vcTests.Data.msvc_toolset_notfound_dict() + def msvc_sdk_version_list_components(cls, msvc_version, msvc_uwp_app=False): + comps_dict = cls.SDK_VERSIONS_COMPS_DICT.get(msvc_version, {}) + comps_list = comps_dict.get(msvc_uwp_app, []) + return comps_list @classmethod - def msvc_toolset_notfound_version(cls, msvc_version): - d = cls.msvc_toolset_notfound_dict() - notfound_versions = d.get(msvc_version,[]) - if not notfound_versions: - notfound_version = msvc_version + '0.00001' + def msvc_sdk_version(cls, msvc_version, msvc_uwp_app=False): + comps_dict = cls.SDK_VERSIONS_COMPS_DICT.get(msvc_version, {}) + comps_list = comps_dict.get(msvc_uwp_app, []) + if not comps_list: + sdk_version = '10.0.20348.0' else: - notfound_version = notfound_versions[0] - return notfound_version + sdk_version = comps_list[0].sdk_version + return sdk_version @classmethod - def _msvc_sdk_notfound_list(cls, sdk_seen, sdk_list): - new_sdk_list = [] - if not sdk_list: - return new_sdk_list - for sdk_version in sdk_list: - version = sdk_version - comps = version.split('.') - if len(comps) != 4: - continue - nloop = 0 - while nloop < 10: - ival = int(comps[-2]) - if ival == 0: - ival = 1000000 - ival -= 1 - version = '{}.{}.{:05d}.{}'.format(comps[0], comps[1], ival, comps[-1]) - if version not in sdk_seen: - new_sdk_list.append(version) - break - nloop += 1 - return new_sdk_list - - _msvc_sdk_notfound_dict = {True: None, False: None} + def msvc_sdk_notfound_version(cls, msvc_version, msvc_uwp_app=False): + notfound_dict = cls.SDK_VERSIONS_NOTFOUND_DICT.get(msvc_version, {}) + notfound_list = notfound_dict.get(msvc_uwp_app, []) + if not notfound_list: + notfound_version = '10.0.00000.1' + else: + notfound_version = notfound_list[0] + return notfound_version @classmethod - def msvc_sdk_notfound_dict(cls, msvc_uwp_app=False): - if cls._msvc_sdk_notfound_dict[msvc_uwp_app] is None: - sdk_seen = set() - sdk_dict = {} - for symbol in Config.MSVC_VERSION_SUFFIX.keys(): - sdk_list = WinSDK.get_msvc_sdk_version_list(symbol, msvc_uwp_app=msvc_uwp_app) - if not sdk_list: - continue - sdk_seen.update(sdk_list) - sdk_dict[symbol] = sdk_list - for key, val in sdk_dict.items(): - sdk_dict[key] = cls._msvc_sdk_notfound_list(sdk_seen, val) - cls._msvc_sdk_notfound_dict[msvc_uwp_app] = sdk_dict - return cls._msvc_sdk_notfound_dict[msvc_uwp_app] + def msvc_toolset_notfound_dict(cls): + return vcTests.Data.msvc_toolset_notfound_dict() @classmethod - def msvc_sdk_notfound_version(cls, msvc_version): - d = cls.msvc_sdk_notfound_dict() + def msvc_toolset_notfound_version(cls, msvc_version): + d = cls.msvc_toolset_notfound_dict() notfound_versions = d.get(msvc_version,[]) if not notfound_versions: - notfound_version = '10.0.00000.1' + notfound_version = msvc_version + '0.00001' else: notfound_version = notfound_versions[0] return notfound_version @@ -249,6 +294,19 @@ def run_msvc_script_args(self): if version_def.msvc_vernum >= 14.1: # VS2017 and later + toolset_versions = [ + Util.msvc_extended_version_components(toolset_version) + for toolset_version in + ScriptArguments._msvc_toolset_versions_internal( + version_def.msvc_version, vc_dir, full=True, sxs=False + ) + ] + + toolset_def = toolset_versions[0] if toolset_versions else Util.msvc_extended_version_components(version_def.msvc_verstr) + + earlier_toolset_versions = [toolset_def for toolset_def in toolset_versions if toolset_def.msvc_vernum != version_def.msvc_vernum] + earlier_toolset_def = earlier_toolset_versions[0] if earlier_toolset_versions else None + # should not raise exception (argument not validated) env = Environment(MSVC_SCRIPT_ARGS='undefinedsymbol') _ = func(env, version_def.msvc_version, vc_dir, '') @@ -266,15 +324,44 @@ def run_msvc_script_args(self): for msvc_uwp_app in (True, False): - sdk_list = WinSDK.get_msvc_sdk_version_list(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) - for sdk_version in sdk_list: + sdk_list = Data.msvc_sdk_version_list_components(version_def.msvc_version, msvc_uwp_app=msvc_uwp_app) + for sdk_def in sdk_list: - if sdk_version == '8.1' and msvc_uwp_app: + if sdk_def.sdk_verstr == '8.1' and msvc_uwp_app: + + more_tests = [] + + if earlier_toolset_def: + # SDK 8.1 and UWP: toolset must be 14.0 + expect = True if earlier_toolset_def.msvc_vernum > 14.0 else False + more_tests.append( + (expect, { + 'MSVC_SDK_VERSION': sdk_def.sdk_version, + 'MSVC_UWP_APP': msvc_uwp_app, + 'MSVC_TOOLSET_VERSION': earlier_toolset_def.msvc_toolset_version + }) + ) + + expect = True if version_def.msvc_vernum > 14.0 else False for exc, kwargs in [ - (False, {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}), - (True, {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}), - ]: + # script args not validated + (False, { + 'MSVC_SCRIPT_ARGS': sdk_def.sdk_version, + 'MSVC_UWP_APP': msvc_uwp_app + }), + # SDK 8.1 and UWP: msvc_version > 14.0 + (True, { + 'MSVC_SDK_VERSION': sdk_def.sdk_version, + 'MSVC_UWP_APP': msvc_uwp_app + }), + # SDK 8.1 and UWP: toolset must be 14.0 + (expect, { + 'MSVC_SDK_VERSION': sdk_def.sdk_version, + 'MSVC_UWP_APP': msvc_uwp_app, + 'MSVC_TOOLSET_VERSION': version_def.msvc_verstr + }), + ] + more_tests: env = Environment(**kwargs) if exc: with self.assertRaises(MSVCArgumentError): @@ -285,8 +372,8 @@ def run_msvc_script_args(self): else: for kwargs in [ - {'MSVC_SCRIPT_ARGS': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, - {'MSVC_SDK_VERSION': sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + {'MSVC_SCRIPT_ARGS': sdk_def.sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, + {'MSVC_SDK_VERSION': sdk_def.sdk_version, 'MSVC_UWP_APP': msvc_uwp_app}, ]: env = Environment(**kwargs) _ = func(env, version_def.msvc_version, vc_dir, '') @@ -302,6 +389,9 @@ def run_msvc_script_args(self): for kwargs in [ {'MSVC_TOOLSET_VERSION': msvc_toolset_notfound_version}, + {'MSVC_TOOLSET_VERSION': "{}.{}.00.0".format( + toolset_def.msvc_toolset_comps[0], toolset_def.msvc_toolset_comps[1] + )}, ]: env = Environment(**kwargs) with self.assertRaises(MSVCToolsetVersionNotFound): @@ -316,55 +406,106 @@ def run_msvc_script_args(self): with self.assertRaises(MSVCSDKVersionNotFound): _ = func(env, version_def.msvc_version, vc_dir, '') + msvc_sdk_version = Data.msvc_sdk_version(version_def.msvc_version) + + more_tests = [] + + if Data.HAVE140_TOOLSET: + + more_tests.append( + # toolset != 14.0 + ({'MSVC_TOOLSET_VERSION': '14.00.00001', + }, + (MSVCArgumentError, ), + ), + ) + for kwargs, exc_t in [ + # multiple definitions ({'MSVC_UWP_APP': True, 'MSVC_SCRIPT_ARGS': 'uwp' }, (MSVCArgumentError, ), ), + # multiple definitions (args) ({'MSVC_UWP_APP': True, 'MSVC_SCRIPT_ARGS': 'uwp undefined store' }, (MSVCArgumentError, ), ), + # multiple definitions ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(version_def.msvc_verstr) }, (MSVCArgumentError, ), ), + # multiple definitions (args) ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, - 'MSVC_SCRIPT_ARGS': "-vcvars_ver={}".format(version_def.msvc_verstr) + 'MSVC_SCRIPT_ARGS': "-vcvars_ver={0} undefined -vcvars_ver={0}".format(version_def.msvc_verstr) }, (MSVCArgumentError, ), ), - ({'MSVC_TOOLSET_VERSION': version_def.msvc_verstr, - 'MSVC_SCRIPT_ARGS': "-vcvars_ver={0} undefined -vcvars_ver={0}".format(version_def.msvc_verstr) + # multiple definitions + ({'MSVC_SDK_VERSION': msvc_sdk_version, + 'MSVC_SCRIPT_ARGS': msvc_sdk_version }, (MSVCArgumentError, ), ), - ({'MSVC_SDK_VERSION': '9.1', + # multiple definitions (args) + ({'MSVC_SDK_VERSION': msvc_sdk_version, + 'MSVC_SCRIPT_ARGS': '{0} undefined {0}'.format(msvc_sdk_version) }, (MSVCArgumentError, ), ), + # multiple definitions ({'MSVC_SPECTRE_LIBS': True, - 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre undefined -vcvars_spectre_libs=spectre' + 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre' }, (MSVCArgumentError, MSVCSpectreLibsNotFound), ), + # multiple definitions (args) ({'MSVC_SPECTRE_LIBS': True, - 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre' + 'MSVC_SCRIPT_ARGS': '-vcvars_spectre_libs=spectre undefined -vcvars_spectre_libs=spectre' }, (MSVCArgumentError, MSVCSpectreLibsNotFound), ), + # toolset < 14.0 + ({'MSVC_TOOLSET_VERSION': '12.0', + }, + (MSVCArgumentError, ), + ), + # toolset > msvc_version + ({'MSVC_TOOLSET_VERSION': '{}.{}'.format(version_def.msvc_major, version_def.msvc_minor+1), + }, + (MSVCArgumentError, ), + ), + # version not supported + ({'MSVC_TOOLSET_VERSION': "{}".format(version_def.msvc_major), + }, + (MSVCArgumentError, ), + ), + # version not supported + ({'MSVC_TOOLSET_VERSION': "{}.{}.00000.0".format( + toolset_def.msvc_toolset_comps[0], toolset_def.msvc_toolset_comps[1] + )}, + (MSVCArgumentError, ), + ), + # version not supported + ({'MSVC_SDK_VERSION': '9.1', + }, + (MSVCArgumentError, ), + ), + # spectre not available for UWP ({'MSVC_SPECTRE_LIBS': True, 'MSVC_UWP_APP': True, }, (MSVCArgumentError, MSVCSpectreLibsNotFound), ), + # spectre not available in VS2015 ({'MSVC_SPECTRE_LIBS': True, 'MSVC_TOOLSET_VERSION': '14.00.00000', }, (MSVCArgumentError, MSVCSpectreLibsNotFound, MSVCToolsetVersionNotFound), ), - ]: + ] + more_tests: env = Environment(**kwargs) with self.assertRaises(exc_t): _ = func(env, version_def.msvc_version, vc_dir, '') diff --git a/SCons/Tool/MSCommon/MSVC/Util.py b/SCons/Tool/MSCommon/MSVC/Util.py index 2c197c6674..4b487da0e0 100644 --- a/SCons/Tool/MSCommon/MSVC/Util.py +++ b/SCons/Tool/MSCommon/MSVC/Util.py @@ -106,6 +106,15 @@ def process_path(p): r'^[1-9][0-9][.][0-9]{2}[.][0-9]{2}[.][0-9]{1,2}$' # MM.mm.VV.vv format ) +# msvc sdk version regexes + +re_msvc_sdk_version = re.compile(r'''^ + (?P(?: + ([1-9][0-9]?[.][0-9])| # XX.Y + ([1-9][0-9][.][0-9]{1}[.][0-9]{5}[.][0-9]{1,2}) # XX.Y.ZZZZZ.A - XX.Y.ZZZZZ.AA + )) +$''', re.IGNORECASE | re.VERBOSE) + # version prefix utilities def get_version_prefix(version): @@ -299,3 +308,59 @@ def msvc_extended_version_components(version): return msvc_extended_version_components_def +# msvc sdk version decomposition utilties + +_MSVC_SDK_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCSDKVersionComponentsDefinition', [ + 'sdk_version', # sdk version (e.g., '10.0.20348.0') + 'sdk_verstr', # sdk version numeric string (e.g., '10.0') + 'sdk_vernum', # sdk version floating point number (e.g, 10.0) + 'sdk_major', # sdk major version integer number (e.g., 10) + 'sdk_minor', # sdk minor version integer number (e.g., 0) + 'sdk_comps', # sdk version components tuple (e.g., ('10', '0', '20348', '0')) +]) + +def msvc_sdk_version_components(version): + """ + Decompose an msvc sdk version into components. + + Tuple fields: + sdk_version: sdk version (e.g., '10.0.20348.0') + sdk_verstr: sdk version numeric string (e.g., '10.0') + sdk_vernum: sdk version floating point number (e.g., 10.0) + sdk_major: sdk major version integer number (e.g., 10) + sdk_minor: sdk minor version integer number (e.g., 0) + sdk_comps: sdk version components tuple (e.g., ('10', '0', '20348', '0')) + + Args: + version: str + sdk version specification + + Returns: + None or MSVCSDKVersionComponents namedtuple: + """ + + if not version: + return None + + m = re_msvc_sdk_version.match(version) + if not m: + return None + + sdk_version = version + sdk_comps = tuple(sdk_version.split('.')) + sdk_verstr = '.'.join(sdk_comps[:2]) + sdk_vernum = float(sdk_verstr) + + sdk_major, sdk_minor = [int(x) for x in sdk_comps[:2]] + + msvc_sdk_version_components_def = _MSVC_SDK_VERSION_COMPONENTS_DEFINITION( + sdk_version = sdk_version, + sdk_verstr = sdk_verstr, + sdk_vernum = sdk_vernum, + sdk_major = sdk_major, + sdk_minor = sdk_minor, + sdk_comps = sdk_comps, + ) + + return msvc_sdk_version_components_def + diff --git a/SCons/Tool/MSCommon/MSVC/UtilTests.py b/SCons/Tool/MSCommon/MSVC/UtilTests.py index 0497878015..5e14d506d5 100644 --- a/SCons/Tool/MSCommon/MSVC/UtilTests.py +++ b/SCons/Tool/MSCommon/MSVC/UtilTests.py @@ -31,6 +31,7 @@ from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC import Util +from SCons.Tool.MSCommon.MSVC import WinSDK class Data: @@ -183,6 +184,26 @@ def test_msvc_extended_version_components(self): )) Util.re_extended_version = save_re + def test_msvc_sdk_version_components(self): + func = Util.msvc_sdk_version_components + for vcver, expect in [ + (None, False), ('', False), ('ABC', False), ('14', False), ('14.1.', False), ('14.16', False), + ('8.1', True), ('10.0', True), ('10.0.20348.0', True), + ]: + comps_def = func(vcver) + msg = 'msvc sdk version components definition is None' if expect else 'msvc sdk version components definition is not None' + self.assertTrue((comps_def is not None) == expect, "{}({}): {}".format( + func.__name__, repr(vcver), repr(msg) + )) + for vcver in Config.MSVC_VERSION_SUFFIX.keys(): + comps_def = func(vcver) + sdk_list = WinSDK.get_msvc_sdk_version_list(vcver, msvc_uwp_app=False) + for sdk_version in sdk_list: + comps_def = func(sdk_version) + self.assertNotEqual(comps_def, None, "{}({}) is None".format( + func.__name__, repr(vcver) + )) + if __name__ == "__main__": unittest.main() diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index ee443d9725..7a65371bd4 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -83,6 +83,7 @@ from .MSVC.Util import ( # noqa: F401 msvc_version_components, msvc_extended_version_components, + msvc_sdk_version_components, ) # Local Variables: From bf6ea8aa10d560c959288aab34e5948552e835e0 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 17 Jul 2022 12:48:15 -0400 Subject: [PATCH 087/108] Add function to return toolsets with spectre libs folder (target may not be installed). Add additional tests. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 23 ++++++++- .../MSCommon/MSVC/ScriptArgumentsTests.py | 14 ++++++ SCons/Tool/MSCommon/__init__.py | 1 + SCons/Tool/MSCommon/vc.py | 25 ++++++++++ SCons/Tool/MSCommon/vcTests.py | 49 ++++++++++++++----- 5 files changed, 100 insertions(+), 12 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 1818b93428..390e93ade6 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -764,6 +764,10 @@ def _msvc_script_argument_spectre_constraints(msvc, toolset, spectre_libs, platf 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'] @@ -780,7 +784,7 @@ def _msvc_script_argument_spectre(env, msvc, vc_dir, toolset, platform_def, argl raise MSVCArgumentError(err_msg) if toolset: - spectre_dir = os.path.join(vc_dir, "Tools", "MSVC", toolset.version, "lib", "spectre") + 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', @@ -992,6 +996,23 @@ def _msvc_toolset_versions_internal(msvc_version, vc_dir, full=True, sxs=False): return toolset_versions +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(): debug('') _reset_have140_cache() diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 5a5abad405..551513e1d5 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -136,6 +136,11 @@ class Data: HAVE_MSVC = True if len(INSTALLED_VERSIONS_PAIRS) else False + SPECTRE_TOOLSET_VERSIONS = { + version_def.msvc_version: vc.msvc_toolset_versions_spectre(version_def.msvc_version) + for version_def, _ in INSTALLED_VERSIONS_PAIRS + } + SDK_VERSIONS_COMPS_DICT, SDK_VERSIONS_SEEN = _sdk_versions_comps_dict_seen(INSTALLED_VERSIONS_PAIRS) SDK_VERSIONS_NOTFOUND_DICT = _sdk_versions_notfound( @@ -406,6 +411,15 @@ def run_msvc_script_args(self): with self.assertRaises(MSVCSDKVersionNotFound): _ = func(env, version_def.msvc_version, vc_dir, '') + have_spectre = toolset_def.msvc_toolset_version in Data.SPECTRE_TOOLSET_VERSIONS.get(version_def.msvc_version,[]) + print("HAVE_SPECTRE", have_spectre, version_def.msvc_version, toolset_def.msvc_toolset_version) + env = Environment(MSVC_SPECTRE_LIBS=True, MSVC_TOOLSET_VERSION=toolset_def.msvc_toolset_version) + if not have_spectre: + with self.assertRaises(MSVCSpectreLibsNotFound): + _ = func(env, version_def.msvc_version, vc_dir, '') + else: + _ = func(env, version_def.msvc_version, vc_dir, '') + msvc_sdk_version = Data.msvc_sdk_version(version_def.msvc_version) more_tests = [] diff --git a/SCons/Tool/MSCommon/__init__.py b/SCons/Tool/MSCommon/__init__.py index 7a65371bd4..c3078ac630 100644 --- a/SCons/Tool/MSCommon/__init__.py +++ b/SCons/Tool/MSCommon/__init__.py @@ -43,6 +43,7 @@ msvc_find_vswhere, msvc_sdk_versions, msvc_toolset_versions, + msvc_toolset_versions_spectre, msvc_query_version_toolset, ) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index d19e30a62d..823ba4b266 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -1384,6 +1384,31 @@ def msvc_toolset_versions(msvc_version=None, full=True, sxs=False): rval = MSVC.ScriptArguments._msvc_toolset_versions_internal(msvc_version, vc_dir, full=full, sxs=sxs) return rval +def msvc_toolset_versions_spectre(msvc_version=None): + debug('msvc_version=%s', repr(msvc_version)) + + env = None + rval = [] + + if not msvc_version: + msvc_version = msvc_default_version() + + if not msvc_version: + debug('no msvc versions detected') + return rval + + if msvc_version not in _VCVER: + msg = 'Unsupported msvc version {}'.format(repr(msvc_version)) + raise MSVCArgumentError(msg) + + vc_dir = find_vc_pdir(env, msvc_version) + if not vc_dir: + debug('VC folder not found for version %s', repr(msvc_version)) + return rval + + rval = MSVC.ScriptArguments._msvc_toolset_versions_spectre_internal(msvc_version, vc_dir) + return rval + def msvc_query_version_toolset(version=None, prefer_newest=True): """ Returns an msvc version and a toolset version given a version diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index 53b56bc68a..cd310ddf17 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -243,6 +243,8 @@ class Data: HAVE_MSVC = True if MSCommon.vc.msvc_default_version() else False + INSTALLED_VCS_COMPONENTS = MSCommon.vc.get_installed_vcs_components() + @classmethod def _msvc_toolset_notfound_list(cls, toolset_seen, toolset_list): new_toolset_list = [] @@ -368,15 +370,6 @@ def test_invalid_vcver_toolsets(self): class MsvcToolsetVersionsTests(unittest.TestCase): """Test msvc_toolset_versions""" - _installed_vcs_components = None - - @classmethod - def setUpClass(cls): - cls._installed_vcs_components = MSCommon.vc.get_installed_vcs_components() - - def setUp(self): - self.installed_vcs_components = self.__class__._installed_vcs_components - def run_valid_default_msvc(self): symbol = MSCommon.vc.msvc_default_version() version_def = MSCommon.msvc_version_components(symbol) @@ -384,7 +377,7 @@ def run_valid_default_msvc(self): toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=False) toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=False, sxs=True) toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=None, full=True, sxs=True) - if symbol and version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: + if symbol and version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(None))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(None))) @@ -408,7 +401,7 @@ def test_valid_vcver(self): toolset_full_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=False) toolset_sxs_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=False, sxs=True) toolset_all_list = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol, full=True, sxs=True) - if version_def in self.installed_vcs_components and version_def.msvc_vernum >= 14.1: + if version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1: # sxs list could be empty self.assertTrue(toolset_full_list, "Toolset full list is empty for msvc version {}".format(repr(symbol))) self.assertTrue(toolset_all_list, "Toolset all list is empty for msvc version {}".format(repr(symbol))) @@ -423,6 +416,40 @@ def test_invalid_vcver(self): with self.assertRaises(MSCommon.vc.MSVCArgumentError): _ = MSCommon.vc.msvc_toolset_versions(msvc_version=symbol) +class MsvcToolsetVersionsSpectreTests(unittest.TestCase): + + def run_valid_default_msvc(self): + symbol = MSCommon.vc.msvc_default_version() + version_def = MSCommon.msvc_version_components(symbol) + spectre_toolset_list = MSCommon.vc.msvc_toolset_versions_spectre(msvc_version=None) + if symbol and version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1: + # spectre toolset list can empty (may not be installed) + pass + else: + self.assertFalse(spectre_toolset_list, "Toolset spectre list is not empty for msvc version {}".format(repr(None))) + + def test_valid_default_msvc(self): + if Data.HAVE_MSVC: + Patch.MSCommon.vc.msvc_default_version.enable_none() + self.run_valid_default_msvc() + Patch.MSCommon.vc.msvc_default_version.restore() + self.run_valid_default_msvc() + + def test_valid_vcver(self): + for symbol in MSCommon.vc._VCVER: + version_def = MSCommon.msvc_version_components(symbol) + spectre_toolset_list = MSCommon.vc.msvc_toolset_versions_spectre(msvc_version=symbol) + if version_def in Data.INSTALLED_VCS_COMPONENTS and version_def.msvc_vernum >= 14.1: + # spectre toolset list can empty (may not be installed) + pass + else: + self.assertFalse(spectre_toolset_list, "Toolset spectre list is not empty for msvc version {}".format(repr(symbol))) + + def test_invalid_vcver(self): + for symbol in ['12.9', '6.0Exp', '14.3Exp', '99', '14.1Bug']: + with self.assertRaises(MSCommon.vc.MSVCArgumentError): + _ = MSCommon.vc.msvc_toolset_versions_spectre(msvc_version=symbol) + class MsvcQueryVersionToolsetTests(unittest.TestCase): """Test msvc_query_toolset_version""" From 79c8e917d4c0b79b62811c1ac6330d85775bf8ae Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 17 Jul 2022 14:58:16 -0400 Subject: [PATCH 088/108] Remove blank line and redundant test invocation. --- SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 551513e1d5..1f3fcf608f 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -54,7 +54,6 @@ def Environment(**kwargs): del kwargs[tools_key] return SCons.Environment.Base(tools=tools, **kwargs) - def _sdk_versions_comps_dict_seen(installed_version_pairs): sdk_versions_comps_dict = {} @@ -228,7 +227,6 @@ def test_msvc_script_arguments_defaults(self): # enable forcing sdk and toolset versions as arguments ScriptArguments.msvc_force_default_arguments(force=True) for version_def, vc_dir in Data.INSTALLED_VERSIONS_PAIRS: - scriptargs = func(env, version_def.msvc_version, vc_dir, '') for arg in ('', 'arch'): scriptargs = func(env, version_def.msvc_version, vc_dir, arg) if version_def.msvc_vernum >= 14.0: From f4efe6bcd4694c34c391fe92422b059e3f737e1d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 17 Jul 2022 15:00:49 -0400 Subject: [PATCH 089/108] Remove print statement from ScriptArgumentsTests.py. --- SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py | 1 - 1 file changed, 1 deletion(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py index 1f3fcf608f..4413256536 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArgumentsTests.py @@ -410,7 +410,6 @@ def run_msvc_script_args(self): _ = func(env, version_def.msvc_version, vc_dir, '') have_spectre = toolset_def.msvc_toolset_version in Data.SPECTRE_TOOLSET_VERSIONS.get(version_def.msvc_version,[]) - print("HAVE_SPECTRE", have_spectre, version_def.msvc_version, toolset_def.msvc_toolset_version) env = Environment(MSVC_SPECTRE_LIBS=True, MSVC_TOOLSET_VERSION=toolset_def.msvc_toolset_version) if not have_spectre: with self.assertRaises(MSVCSpectreLibsNotFound): From d7664eb17d902d3540cfcdda226bd55e9c86cdf0 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Sun, 17 Jul 2022 19:43:09 -0400 Subject: [PATCH 090/108] Add msvc spectre libs test. Add library path checks to msvc sdk version and msvc toolset version tests. --- test/MSVC/MSVC_SDK_VERSION.py | 14 ++- test/MSVC/MSVC_SPECTRE_LIBS.py | 153 ++++++++++++++++++++++++++++++ test/MSVC/MSVC_TOOLSET_VERSION.py | 23 ++++- 3 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 test/MSVC/MSVC_SPECTRE_LIBS.py diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index 4a3537b643..1d9f0f45bb 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -84,8 +84,11 @@ def version_major_list(version_list): test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) - env = Environment(MSVC_VERSION={}, MSVC_SDK_VERSION={}, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(sdk_version)) + env = Environment(MSVC_VERSION={0}, MSVC_SDK_VERSION={1}, tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\{2}\\\\' not in lib_path: + raise RuntimeError("{1} not found in lib_path " + lib_path) + """.format(repr(supported.msvc_version), repr(sdk_version), sdk_version) )) test.run(arguments='-Q -s', stdout='') @@ -93,8 +96,11 @@ def version_major_list(version_list): test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) - env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS={}, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(sdk_version)) + env = Environment(MSVC_VERSION={0}, MSVC_SCRIPT_ARGS={1}, tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\{2}\\\\' not in lib_path: + raise RuntimeError("{1} not found in lib_path " + lib_path) + """.format(repr(supported.msvc_version), repr(sdk_version), sdk_version) )) test.run(arguments='-Q -s', stdout='') diff --git a/test/MSVC/MSVC_SPECTRE_LIBS.py b/test/MSVC/MSVC_SPECTRE_LIBS.py new file mode 100644 index 0000000000..d5e57c920a --- /dev/null +++ b/test/MSVC/MSVC_SPECTRE_LIBS.py @@ -0,0 +1,153 @@ +# 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. + +""" +Test the MSVC_SPECTRE_LIBS construction variable. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.skip_if_not_msvc() + +import textwrap + +from SCons.Tool.MSCommon.vc import get_installed_vcs_components +from SCons.Tool.MSCommon import msvc_toolset_versions_spectre + +installed_versions = get_installed_vcs_components() + +GE_VS2017_versions = [v for v in installed_versions if v.msvc_vernum >= 14.1] +LT_VS2017_versions = [v for v in installed_versions if v.msvc_vernum < 14.1] + +if GE_VS2017_versions: + # VS2017 and later for toolset argument + + for supported in GE_VS2017_versions: + + spectre_toolset_versions = msvc_toolset_versions_spectre(supported.msvc_version) + spectre_toolset_version = spectre_toolset_versions[0] if spectre_toolset_versions else None + + if spectre_toolset_version: + + # spectre libs using construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\lib\\\\spectre\\\\' not in lib_path.lower(): + raise RuntimeError("'spectre' not found in lib_path " + lib_path) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout='') + + # spectre libs using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\lib\\\\spectre\\\\' not in lib_path.lower(): + raise RuntimeError("'spectre' not found in lib_path " + lib_path) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout='') + + # error construction variable and script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', tools=['msvc']) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + expect = "MSVCArgumentError: multiple spectre declarations: MSVC_SPECTRE_LIBS=True and MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre':" + test.must_contain_all(test.stderr(), expect) + + else: + + # spectre libs using construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc']) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + if not test.stderr().strip().startswith('MSVCSpectreLibsNotFound: Spectre libraries not found'): + test.fail_test() + + # spectre libs using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', MSVC_SCRIPTERROR_POLICY='error', tools=['msvc']) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + if not test.stderr().strip().startswith('MSVCScriptExecutionError: vc script errors detected:'): + test.fail_test() + + # spectre libs using construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=False, tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\lib\\\\spectre\\\\' in lib_path.lower(): + raise RuntimeError("'spectre' found in lib_path " + lib_path) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout='') + +if LT_VS2017_versions: + # VS2015 and earlier for toolset argument error + + for unsupported in LT_VS2017_versions: + + # must be VS2017 or later + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc']) + """.format(repr(unsupported.msvc_version)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + if not test.stderr().strip().startswith('MSVCArgumentError: MSVC_SPECTRE_LIBS (True) constraint violation:'): + test.fail_test() + + for disabled in (False, None): + + # ignore + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS={}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), disabled) + )) + test.run(arguments='-Q -s', stdout='') + +test.pass_test() + diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index aa3c44ca54..b016147474 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -56,15 +56,34 @@ toolset_sxs_version = toolset_sxs_versions[0] if toolset_sxs_versions else None if toolset_full_version: + + # toolset version using construction variable test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) - env = Environment(MSVC_VERSION={}, MSVC_TOOLSET_VERSION={}, tools=['msvc']) - """.format(repr(supported.msvc_version), repr(toolset_full_version)) + env = Environment(MSVC_VERSION={0}, MSVC_TOOLSET_VERSION={1}, tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\{2}\\\\' not in lib_path: + raise RuntimeError("{1} not found in lib_path " + lib_path) + """.format(repr(supported.msvc_version), repr(toolset_full_version), toolset_full_version) + )) + test.run(arguments='-Q -s', stdout='') + + # toolset version using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={0}, MSVC_SCRIPT_ARGS='-vcvars_ver={1}', tools=['msvc']) + lib_path = env['ENV']['LIB'] + if '\\\\{1}\\\\' not in lib_path: + raise RuntimeError("'{1}' not found in lib_path " + lib_path) + """.format(repr(supported.msvc_version), toolset_full_version) )) test.run(arguments='-Q -s', stdout='') if toolset_sxs_version: + + # sxs toolset version using construction variable test.write('SConstruct', textwrap.dedent( """ DefaultEnvironment(tools=[]) From 32bfd79a27f2c28f8b8feea396e1636c55fa0f28 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 03:17:42 -0400 Subject: [PATCH 091/108] Add special case for msvc 14.0 and store/uwp specified via script args and sdk specified via construction variable or forced. Revise forced default toolset handling (may have passed default toolset as an argument inadvertently). Add more tests. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 44 +-- test/MSVC/MSVC_SDK_VERSION.py | 4 +- test/MSVC/MSVC_SPECTRE_LIBS.py | 6 +- test/MSVC/MSVC_TOOLSET_VERSION.py | 4 +- test/MSVC/MSVC_UWP_APP.py | 310 +++++++++----------- 5 files changed, 165 insertions(+), 203 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index 390e93ade6..b4191de0ad 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -157,7 +157,6 @@ def msvc_force_default_arguments(force=None): @enum.unique class SortOrder(enum.IntEnum): - ARCH = 0 # arch UWP = 1 # MSVC_UWP_APP SDK = 2 # MSVC_SDK_VERSION TOOLSET = 3 # MSVC_TOOLSET_VERSION @@ -232,7 +231,7 @@ def _user_script_argument_uwp(env, uwp, user_argstr): matches = [m for m in re_vcvars_uwp.finditer(user_argstr)] if not matches: - return None + return False if len(matches) > 1: debug('multiple uwp declarations: MSVC_SCRIPT_ARGS=%s', repr(user_argstr)) @@ -240,7 +239,7 @@ def _user_script_argument_uwp(env, uwp, user_argstr): raise MSVCArgumentError(err_msg) if not uwp: - return None + 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)) @@ -686,7 +685,7 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist): return toolset_vcvars -def _msvc_script_default_toolset(env, msvc, vc_dir, arglist): +def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: return None @@ -697,8 +696,9 @@ def _msvc_script_default_toolset(env, msvc, vc_dir, arglist): debug('MSVC_VERSION=%s, toolset_default=%s', repr(msvc.version), repr(toolset_default)) - argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default)) - arglist.append(argpair) + if force_toolset: + argpair = (SortOrder.TOOLSET, '-vcvars_ver={}'.format(toolset_default)) + arglist.append(argpair) return toolset_default @@ -874,11 +874,10 @@ def _msvc_process_construction_variables(env): def msvc_script_arguments(env, version, vc_dir, arg): - arglist = [] + arguments = [arg] if arg else [] - if arg: - argpair = (SortOrder.ARCH, arg) - arglist.append(argpair) + arglist = [] + arglist_reverse = False msvc = _msvc_version(version) @@ -897,7 +896,9 @@ def msvc_script_arguments(env, version, vc_dir, arg): uwp = None if user_argstr: - _user_script_argument_uwp(env, uwp, 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) @@ -915,14 +916,10 @@ def msvc_script_arguments(env, version, vc_dir, arg): user_toolset = None if not toolset_version and not user_toolset: - default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist) + default_toolset = _msvc_script_default_toolset(env, msvc, vc_dir, arglist, _MSVC_FORCE_DEFAULT_TOOLSET) else: default_toolset = None - if _MSVC_FORCE_DEFAULT_TOOLSET: - if default_toolset: - toolset_version = default_toolset - if user_toolset: toolset = None elif toolset_version: @@ -958,11 +955,18 @@ def msvc_script_arguments(env, version, vc_dir, arg): if user_argstr: _user_script_argument_spectre(env, spectre, user_argstr) - if arglist: + if msvc.vs_def.vc_buildtools_def.vc_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() - argstr = ' '.join([argpair[-1] for argpair in arglist]).strip() - else: - argstr = '' + if arglist_reverse: + arglist.reverse() + + arguments.extend([argpair[-1] for argpair in arglist]) + argstr = ' '.join(arguments).strip() debug('arguments: %s', repr(argstr)) return argstr diff --git a/test/MSVC/MSVC_SDK_VERSION.py b/test/MSVC/MSVC_SDK_VERSION.py index 1d9f0f45bb..62eaf6e0bb 100644 --- a/test/MSVC/MSVC_SDK_VERSION.py +++ b/test/MSVC/MSVC_SDK_VERSION.py @@ -87,7 +87,7 @@ def version_major_list(version_list): env = Environment(MSVC_VERSION={0}, MSVC_SDK_VERSION={1}, tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\{2}\\\\' not in lib_path: - raise RuntimeError("{1} not found in lib_path " + lib_path) + raise RuntimeError("{1} not found in lib path " + lib_path) """.format(repr(supported.msvc_version), repr(sdk_version), sdk_version) )) test.run(arguments='-Q -s', stdout='') @@ -99,7 +99,7 @@ def version_major_list(version_list): env = Environment(MSVC_VERSION={0}, MSVC_SCRIPT_ARGS={1}, tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\{2}\\\\' not in lib_path: - raise RuntimeError("{1} not found in lib_path " + lib_path) + raise RuntimeError("{1} not found in lib path " + lib_path) """.format(repr(supported.msvc_version), repr(sdk_version), sdk_version) )) test.run(arguments='-Q -s', stdout='') diff --git a/test/MSVC/MSVC_SPECTRE_LIBS.py b/test/MSVC/MSVC_SPECTRE_LIBS.py index d5e57c920a..d5b0c4de27 100644 --- a/test/MSVC/MSVC_SPECTRE_LIBS.py +++ b/test/MSVC/MSVC_SPECTRE_LIBS.py @@ -58,7 +58,7 @@ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=True, tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\lib\\\\spectre\\\\' not in lib_path.lower(): - raise RuntimeError("'spectre' not found in lib_path " + lib_path) + raise RuntimeError("'spectre' not found in lib path " + lib_path) """.format(repr(supported.msvc_version)) )) test.run(arguments='-Q -s', stdout='') @@ -70,7 +70,7 @@ env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre', tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\lib\\\\spectre\\\\' not in lib_path.lower(): - raise RuntimeError("'spectre' not found in lib_path " + lib_path) + raise RuntimeError("'spectre' not found in lib path " + lib_path) """.format(repr(supported.msvc_version)) )) test.run(arguments='-Q -s', stdout='') @@ -117,7 +117,7 @@ env = Environment(MSVC_VERSION={}, MSVC_SPECTRE_LIBS=False, tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\lib\\\\spectre\\\\' in lib_path.lower(): - raise RuntimeError("'spectre' found in lib_path " + lib_path) + raise RuntimeError("'spectre' found in lib path " + lib_path) """.format(repr(supported.msvc_version)) )) test.run(arguments='-Q -s', stdout='') diff --git a/test/MSVC/MSVC_TOOLSET_VERSION.py b/test/MSVC/MSVC_TOOLSET_VERSION.py index b016147474..01d43b1f16 100644 --- a/test/MSVC/MSVC_TOOLSET_VERSION.py +++ b/test/MSVC/MSVC_TOOLSET_VERSION.py @@ -64,7 +64,7 @@ env = Environment(MSVC_VERSION={0}, MSVC_TOOLSET_VERSION={1}, tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\{2}\\\\' not in lib_path: - raise RuntimeError("{1} not found in lib_path " + lib_path) + raise RuntimeError("{1} not found in lib path " + lib_path) """.format(repr(supported.msvc_version), repr(toolset_full_version), toolset_full_version) )) test.run(arguments='-Q -s', stdout='') @@ -76,7 +76,7 @@ env = Environment(MSVC_VERSION={0}, MSVC_SCRIPT_ARGS='-vcvars_ver={1}', tools=['msvc']) lib_path = env['ENV']['LIB'] if '\\\\{1}\\\\' not in lib_path: - raise RuntimeError("'{1}' not found in lib_path " + lib_path) + raise RuntimeError("'{1}' not found in lib path " + lib_path) """.format(repr(supported.msvc_version), toolset_full_version) )) test.run(arguments='-Q -s', stdout='') diff --git a/test/MSVC/MSVC_UWP_APP.py b/test/MSVC/MSVC_UWP_APP.py index 6ccd1177f1..1f1c230a0e 100644 --- a/test/MSVC/MSVC_UWP_APP.py +++ b/test/MSVC/MSVC_UWP_APP.py @@ -1,6 +1,6 @@ -#!/usr/bin/env python +# MIT License # -# __COPYRIGHT__ +# 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 @@ -20,190 +20,148 @@ # 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. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ -Test the ability to configure the $MSVC_UWP_APP construction variable with -the desired effect. +Test the MSVC_UWP_APP construction variable. """ import TestSCons -import SCons.Tool.MSCommon.vc as msvc -from SCons.Tool.MSCommon.vc import get_msvc_version_numeric - -def AreVCStoreLibPathsInLIBPATH(output): - libpath = None - msvc_version = None - UWP_APP = None - lines = output.splitlines() - for line in lines: - if 'env[ENV][LIBPATH]=' in line: - libpath = line.split('=')[1] - elif 'env[MSVC_VERSION]=' in line: - msvc_version = line.split('=')[1] - elif 'env[ENV][VSCMD_ARG_app_plat]=' in line: - UWP_APP = line.split('=')[1] - - if not libpath or not msvc_version: - # Couldn't find the libpath or msvc version in the output - return (False, False, None) - - libpaths = libpath.lower().split(';') - msvc_num = float(get_msvc_version_numeric(msvc_version)) - - (vclibstore_path_present, vclibstorerefs_path_present) = (False, False) - for path in libpaths: - # Look for the Store VC Lib paths in the LIBPATH: - # [VS install path]\VC\LIB\store[\arch] and - # [VS install path]\VC\LIB\store\references - # For example, - # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\amd64 - # C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\references - - if msvc_num <= 14: - if r'vc\lib\store\references' in path: - vclibstorerefs_path_present = True - elif r'vc\lib\store' in path: - vclibstore_path_present = True - elif msvc_num > 14: - if UWP_APP == "UWP": - if(r'\lib\x86\store\references' in path - or r'\lib\x64\store' in path): - vclibstorerefs_path_present = True - vclibstore_path_present = True - - return (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) - -_python_ = TestSCons._python_ test = TestSCons.TestSCons() test.skip_if_not_msvc() -installed_msvc_versions = msvc.get_installed_vcs() -# MSVC guaranteed to be at least one version on the system or else -# skip_if_not_msvc() function would have skipped the test - -msvc_140 = '14.0' in installed_msvc_versions -msvc_141 = '14.1' in installed_msvc_versions -msvc_142 = '14.2' in installed_msvc_versions -msvc_143 = '14.3' in installed_msvc_versions - -if not any((msvc_140, msvc_141, msvc_142, msvc_143)): - test.skip_test("Available MSVC doesn't support App store\n") - -if msvc_140: - test.write('SConstruct', """\ -if ARGUMENTS.get('MSVC_UWP_APP'): - help_vars = Variables() - help_vars.Add(EnumVariable( - 'MSVC_UWP_APP', - 'Build a Universal Windows Platform (UWP) Application', - '0', - allowed_values=('0', '1'))) -else: - help_vars = None -env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.0') -# Print the ENV LIBPATH to stdout -print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH')) -print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION')) -""") - - # Test setting MSVC_UWP_APP is '1' (True) - test.run(arguments = "MSVC_UWP_APP=1") - (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout()) - test.fail_test((vclibstore_path_present is False) or (vclibstorerefs_path_present is False), - message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version) - - # Test setting MSVC_UWP_APP is '0' (False) - test.run(arguments = "MSVC_UWP_APP=0") - (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout()) - test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True), - message='VC Store LIBPATHs present when MSVC_UWP_APP=0 (msvc_version=%s)' % msvc_version) - - # Test not setting MSVC_UWP_APP - test.run(arguments = "") - (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout()) - test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True), - message='VC Store LIBPATHs present when MSVC_UWP_APP not set (msvc_version=%s)' % msvc_version) - -if msvc_141 or msvc_142 or msvc_143: - if msvc_143: - test.write('SConstruct', """\ -if ARGUMENTS.get('MSVC_UWP_APP'): - help_vars = Variables() - help_vars.Add(EnumVariable( - 'MSVC_UWP_APP', - 'Build a Universal Windows Platform (UWP) Application', - '0', - allowed_values=('0', '1'))) -else: - help_vars = None -env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.3') -# Print the ENV LIBPATH to stdout -print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH')) -print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION')) -print('env[ENV][VSCMD_ARG_app_plat]=%s' % env.get('ENV').get('VSCMD_ARG_app_plat')) -""") - elif msvc_142: - test.write('SConstruct', """\ -if ARGUMENTS.get('MSVC_UWP_APP'): - help_vars = Variables() - help_vars.Add(EnumVariable( - 'MSVC_UWP_APP', - 'Build a Universal Windows Platform (UWP) Application', - '0', - allowed_values=('0', '1'))) -else: - help_vars = None -env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.2') -# Print the ENV LIBPATH to stdout -print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH')) -print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION')) -print('env[ENV][VSCMD_ARG_app_plat]=%s' % env.get('ENV').get('VSCMD_ARG_app_plat')) -""") - elif msvc_141: - test.write('SConstruct', """\ -if ARGUMENTS.get('MSVC_UWP_APP'): - help_vars = Variables() - help_vars.Add(EnumVariable( - 'MSVC_UWP_APP', - 'Build a Universal Windows Platform (UWP) Application', - '0', - allowed_values=('0', '1'))) -else: - help_vars = None -env = Environment(tools=['default', 'msvc'], variables=help_vars, MSVC_VERSION='14.1') -# Print the ENV LIBPATH to stdout -print('env[ENV][LIBPATH]=%s' % env.get('ENV').get('LIBPATH')) -print('env[MSVC_VERSION]=%s' % env.get('MSVC_VERSION')) -print('env[ENV][VSCMD_ARG_app_plat]=%s' % env.get('ENV').get('VSCMD_ARG_app_plat')) -""") - - # Test setting MSVC_UWP_APP is '1' (True) - test.run(arguments = "MSVC_UWP_APP=1") - (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout()) - test.fail_test((vclibstore_path_present is False) or (vclibstorerefs_path_present is False), - message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version) - - # Test setting MSVC_UWP_APP is '0' (False) - test.run(arguments = "MSVC_UWP_APP=0") - (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout()) - test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True), - message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version) - - # Test not setting MSVC_UWP_APP - test.run(arguments = "") - (vclibstore_path_present, vclibstorerefs_path_present, msvc_version) = AreVCStoreLibPathsInLIBPATH(test.stdout()) - test.fail_test((vclibstore_path_present is True) or (vclibstorerefs_path_present is True), - message='VC Store LIBPATHs NOT present when MSVC_UWP_APP=1 (msvc_version=%s)' % msvc_version) +import textwrap +import re + +from SCons.Tool.MSCommon.vc import get_installed_vcs_components + +installed_versions = get_installed_vcs_components() + +GE_VS2015_versions = [v for v in installed_versions if v.msvc_vernum >= 14.0] +LT_VS2015_versions = [v for v in installed_versions if v.msvc_vernum < 14.0] + +# Look for the Store VC Lib paths in the LIBPATH: +# [VS install path]\VC\LIB\store[\arch] and +# [VS install path]\VC\LIB\store\references +# For example, +# C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\amd64 +# C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\LIB\store\references + +re_lib_eq2015_1 = re.compile(r'\\vc\\lib\\store\\references', re.IGNORECASE) +re_lib_eq2015_2 = re.compile(r'\\vc\\lib\\store', re.IGNORECASE) + +re_lib_ge2017_1 = re.compile(r'\\lib\\x86\\store\\references', re.IGNORECASE) +re_lib_ge2017_2 = re.compile(r'\\lib\\x64\\store', re.IGNORECASE) + +def check_libpath(msvc, active, output): + + def _check_libpath(msvc, output): + outdict = {key.strip(): val.strip() for key, val in [line.split('|') for line in output.splitlines()]} + platform = outdict.get('PLATFORM', '') + libpath = outdict.get('LIBPATH', '') + n_matches = 0 + if msvc.msvc_verstr == '14.0': + for regex in (re_lib_eq2015_1, re_lib_eq2015_2): + if regex.search(libpath): + n_matches += 1 + return (n_matches >= 2, 'store', libpath) + elif platform == 'UWP': + for regex in (re_lib_ge2017_1, re_lib_ge2017_2): + if regex.search(libpath): + n_matches += 1 + return (n_matches > 0, 'uwp', libpath) + return (False, 'uwp', libpath) + + found, kind, libpath = _check_libpath(msvc, output) + + failmsg = None + + if active and not found: + failmsg = 'msvc version {} {} paths not found in lib path {}'.format( + repr(msvc.msvc_version), repr(kind), repr(libpath) + ) + elif not active and found: + failmsg = 'msvc version {} {} paths found in lib path {}'.format( + repr(msvc.msvc_version), repr(kind), repr(libpath) + ) + + return failmsg + +if GE_VS2015_versions: + # VS2015 and later for uwp/store argument + + for supported in GE_VS2015_versions: + + for msvc_uwp_app in (True, '1', False, '0', None): + + active = msvc_uwp_app in (True, '1') + + # uwp using construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_UWP_APP={}, tools=['msvc']) + print('LIBPATH|' + env['ENV'].get('LIBPATH', '')) + print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat','')) + """.format(repr(supported.msvc_version), repr(msvc_uwp_app)) + )) + test.run(arguments='-Q -s', stdout=None) + failmsg = check_libpath(supported, active, test.stdout()) + if failmsg: + test.fail_test(message=failmsg) + + if not active: + continue + + # error construction variable and script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_UWP_APP={}, MSVC_SCRIPT_ARGS='store', tools=['msvc']) + """.format(repr(supported.msvc_version), repr(msvc_uwp_app)) + )) + test.run(arguments='-Q -s', status=2, stderr=None) + if not test.stderr().strip().startswith("MSVCArgumentError: multiple uwp declarations:"): + test.fail_test(message='Expected MSVCArgumentError') + + # uwp using script argument + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={}, MSVC_SCRIPT_ARGS='store', tools=['msvc']) + print('LIBPATH|' + env['ENV'].get('LIBPATH', '')) + print('PLATFORM|' + env['ENV'].get('VSCMD_ARG_app_plat','')) + """.format(repr(supported.msvc_version)) + )) + test.run(arguments='-Q -s', stdout=None) + failmsg = check_libpath(supported, True, test.stdout()) + if failmsg: + test.fail_test(message=failmsg) + +if LT_VS2015_versions: + # VS2013 and earlier for uwp/store error + + for unsupported in LT_VS2015_versions: + + for msvc_uwp_app in (True, '1', False, '0', None): + + active = msvc_uwp_app in (True, '1') + + # uwp using construction variable + test.write('SConstruct', textwrap.dedent( + """ + DefaultEnvironment(tools=[]) + env = Environment(MSVC_VERSION={0}, MSVC_UWP_APP={1}, tools=['msvc']) + """.format(repr(unsupported.msvc_version), repr(msvc_uwp_app)) + )) + if not active: + test.run(arguments='-Q -s', stdout=None) + else: + test.run(arguments='-Q -s', status=2, stderr=None) + expect = 'MSVCArgumentError: MSVC_UWP_APP ({}) constraint violation:'.format(repr(msvc_uwp_app)) + if not test.stderr().strip().startswith(expect): + test.fail_test(message='Expected MSVCArgumentError') test.pass_test() -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: From d6c4499f50573499f9c8c87742610e8d26be0f94 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 09:12:31 -0400 Subject: [PATCH 092/108] Minor tweaks for readability and consistency. --- SCons/Tool/MSCommon/MSVC/ScriptArguments.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py index b4191de0ad..57dbf9d3fe 100644 --- a/SCons/Tool/MSCommon/MSVC/ScriptArguments.py +++ b/SCons/Tool/MSCommon/MSVC/ScriptArguments.py @@ -331,7 +331,7 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist): return sdk_version -def _msvc_script_default_sdk(env, msvc, platform_def, arglist): +def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk=False): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric: return None @@ -347,8 +347,9 @@ def _msvc_script_default_sdk(env, msvc, platform_def, arglist): repr(msvc.version), repr(sdk_default), repr(platform_def.vc_platform) ) - argpair = (SortOrder.SDK, sdk_default) - arglist.append(argpair) + if force_sdk: + argpair = (SortOrder.SDK, sdk_default) + arglist.append(argpair) return sdk_default @@ -685,7 +686,7 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist): return toolset_vcvars -def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset): +def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset=False): if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric: return None @@ -917,6 +918,8 @@ def msvc_script_arguments(env, version, vc_dir, arg): 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 @@ -943,7 +946,7 @@ def msvc_script_arguments(env, version, vc_dir, arg): if _MSVC_FORCE_DEFAULT_SDK: if not sdk_version and not user_sdk: - sdk_version = _msvc_script_default_sdk(env, msvc, platform_def, arglist) + sdk_version = _msvc_script_default_sdk(env, msvc, platform_def, arglist, _MSVC_FORCE_DEFAULT_SDK) # MSVC_SPECTRE_LIBS @@ -964,7 +967,7 @@ def msvc_script_arguments(env, version, vc_dir, arg): arglist.sort() if arglist_reverse: arglist.reverse() - + arguments.extend([argpair[-1] for argpair in arglist]) argstr = ' '.join(arguments).strip() From e99127b30bbbba727deb804feacb38b65c71874f Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 12:01:55 -0400 Subject: [PATCH 093/108] Add MSCommon/README.rst with verbatim content from MSCommon/README. Add out-of-date warning to MSCommon/README. [ci skip] --- SCons/Tool/MSCommon/README | 5 ++ SCons/Tool/MSCommon/README.rst | 125 +++++++++++++++++++++++++++++++++ 2 files changed, 130 insertions(+) create mode 100644 SCons/Tool/MSCommon/README.rst diff --git a/SCons/Tool/MSCommon/README b/SCons/Tool/MSCommon/README index 226865197a..5b09b58f60 100644 --- a/SCons/Tool/MSCommon/README +++ b/SCons/Tool/MSCommon/README @@ -1,3 +1,8 @@ +#################################################################################### +# WARNING: The compiler detection logic documentation below is likely out-of-date. # +# The content of this file is transitioning to README.rst. # +#################################################################################### + This is the flow of the compiler detection logic: External to MSCommon: diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst new file mode 100644 index 0000000000..8054552caa --- /dev/null +++ b/SCons/Tool/MSCommon/README.rst @@ -0,0 +1,125 @@ +README - SCons.Tool.MSCommon +############################ + +Compiler Detection Logic +======================== + +**WARNING: the compiler detection logic documentation below is likely out-of-date.** + +The verbatim content below is taken in its entirety from the existing ``MSCommom/README`` +file. In the future, the the compiler detection logic documentation will will be updated +and converted to the current document format. + +:: + + This is the flow of the compiler detection logic: + + External to MSCommon: + + The Tool init modules, in their exists() routines, call -> msvc_exists(env) + + At the moment, those modules are: + SCons/Tool/midl.py + SCons/Tool/mslib.py + SCons/Tool/mslink.py + SCons/Tool/msvc.py + SCons/Tool/msvs.py + + env may contain a version request in MSVC_VERSION, but this is not used + in the detection that follows from msvc_exists(), only in the later + batch that starts with a call to msvc_setup_env(). + + Internal to MSCommon/vc.py: + + + MSCommon/vc.py:msvc_exists: + | vcs = cached_get_installed_vcs(env) + | returns True if vcs > 0 + | + +-> MSCommon/vc.py:cached_get_installed_vcs: + | checks global if we've run previously, if so return it + | populate the global from -> get_installed_vcs(env) + | + +-> MSCommon/vc.py:get_installed_vcs: + | loop through "known" versions of msvc, granularity is maj.min + | check for product dir -> find_vc_pdir(env, ver) + | + +-> MSCommon/vc.py:find_vc_pdir: + | From the msvc-version to pdir mapping dict, get reg key base and value + | If value is none -> find_vc_pdir_vswhere(ver, env) + | + +-> MSCommon/vc.py:find_vc_pdir_vswhere: + | From the vc-version to VS-version mapping table get string + | Figure out where vswhere is -> msvc_find_vswhere() + | Use subprocess to call vswhere, return first line of match + / + | else get product directory from registry (<= 14.0) + / + | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) + | + +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: + | Figure out host/target pair + | if version > 14.0 get specific version by looking in + | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt + | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe + | if 14.0 or less, "do older stuff" + + All of this just got us a yes-no answer on whether /some/ msvc version + exists, but does populate __INSTALLED_VCS_RUN with all of the top-level + versions as noted for get_installed_vcs + + Externally: + + Once a module's exists() has been called (or, in the case of + clang/clangxx, after the compiler has been detected by other means - + those still expect the rest of the msvc chain but not cl.exe) + the module's generate() function calls -> msvc_setup_env_once(env) + + Internally: + + + MSCommon/vc.py:msvc_setup_env_once: + | checks for environment flag MSVC_SETUP_RUN + | if not, -> msvc_setup_env(env) and set flag + | + +-+ MSCommon/vc.py:msvc_setup_env: + | set ver from -> get_default_version(env) + | + +-+ MSCommon/vc.py:get_default_version: + | if no version specified in env.MSVC_VERSION: + | return first entry from -> cached_get_installed_vcs(env) + | else return requested version + / + | get script from MSVC_USE_SCRIPT if set to a filename + | -> script_env(script) + | + +-+ MSCommon/vc.py:script_env: + | return (possibly cached) script variables matching script arg + / + | else -> msvc_find_valid_batch_script(env, version) + | + +-+ MSCommon/vc.py:msvc_find_valid_batch_script: + | Build a list of plausible target values, and loop through + | look for host + target -> find_batch_file(env, ver, host, target) + | + +-+ MSCommon/vc.py:find_batch_file: + | call -> find_vc_pdir (see above) + | use the return to construct a version-biased batfile path, check + / + | if not found, try sdk scripts (unknown if this is still useful) + + + Problems: + - For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS + - For vswhere-ready versions, detection does not proceed beyond the + product level ("2019") into individual "features" (individual msvc) + - As documented for MSVC_VERSION, compilers can only be requested if versions + are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 + - Information found in the first pass (msvs_exists) isn't really + available anywhere except the cached version list, since we just + return true/false. + - Since msvc_exists chain of calls does not look at version, we + can proceed to compiler setup if *any* msvc was found, even if the + one requested wasn't found. + + + + From 4822e9b0663b5ba1b80bf7f6877e442820a55caf Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:24:41 -0400 Subject: [PATCH 094/108] Rename README to README.rst and update contents for RST. [ci skip] --- SCons/Tool/MSCommon/README | 112 --------------------------------- SCons/Tool/MSCommon/README.rst | 9 ++- 2 files changed, 4 insertions(+), 117 deletions(-) delete mode 100644 SCons/Tool/MSCommon/README diff --git a/SCons/Tool/MSCommon/README b/SCons/Tool/MSCommon/README deleted file mode 100644 index 5b09b58f60..0000000000 --- a/SCons/Tool/MSCommon/README +++ /dev/null @@ -1,112 +0,0 @@ -#################################################################################### -# WARNING: The compiler detection logic documentation below is likely out-of-date. # -# The content of this file is transitioning to README.rst. # -#################################################################################### - -This is the flow of the compiler detection logic: - -External to MSCommon: - - The Tool init modules, in their exists() routines, call -> msvc_exists(env) - -At the moment, those modules are: - SCons/Tool/midl.py - SCons/Tool/mslib.py - SCons/Tool/mslink.py - SCons/Tool/msvc.py - SCons/Tool/msvs.py - -env may contain a version request in MSVC_VERSION, but this is not used -in the detection that follows from msvc_exists(), only in the later -batch that starts with a call to msvc_setup_env(). - -Internal to MSCommon/vc.py: - -+ MSCommon/vc.py:msvc_exists: -| vcs = cached_get_installed_vcs(env) -| returns True if vcs > 0 -| -+-> MSCommon/vc.py:cached_get_installed_vcs: - | checks global if we've run previously, if so return it - | populate the global from -> get_installed_vcs(env) - | - +-> MSCommon/vc.py:get_installed_vcs: - | loop through "known" versions of msvc, granularity is maj.min - | check for product dir -> find_vc_pdir(env, ver) - | - +-> MSCommon/vc.py:find_vc_pdir: - | From the msvc-version to pdir mapping dict, get reg key base and value - | If value is none -> find_vc_pdir_vswhere(ver, env) - | - +-> MSCommon/vc.py:find_vc_pdir_vswhere: - | From the vc-version to VS-version mapping table get string - | Figure out where vswhere is -> msvc_find_vswhere() - | Use subprocess to call vswhere, return first line of match - / - | else get product directory from registry (<= 14.0) - / - | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) - | - +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: - | Figure out host/target pair - | if version > 14.0 get specific version by looking in - | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt - | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe - | if 14.0 or less, "do older stuff" - -All of this just got us a yes-no answer on whether /some/ msvc version -exists, but does populate __INSTALLED_VCS_RUN with all of the top-level -versions as noted for get_installed_vcs - -Externally: - - Once a module's exists() has been called (or, in the case of - clang/clangxx, after the compiler has been detected by other means - - those still expect the rest of the msvc chain but not cl.exe) - the module's generate() function calls -> msvc_setup_env_once(env) - -Internally: - -+ MSCommon/vc.py:msvc_setup_env_once: -| checks for environment flag MSVC_SETUP_RUN -| if not, -> msvc_setup_env(env) and set flag -| -+-+ MSCommon/vc.py:msvc_setup_env: - | set ver from -> get_default_version(env) - | - +-+ MSCommon/vc.py:get_default_version: - | if no version specified in env.MSVC_VERSION: - | return first entry from -> cached_get_installed_vcs(env) - | else return requested version - / - | get script from MSVC_USE_SCRIPT if set to a filename - | -> script_env(script) - | - +-+ MSCommon/vc.py:script_env: - | return (possibly cached) script variables matching script arg - / - | else -> msvc_find_valid_batch_script(env, version) - | - +-+ MSCommon/vc.py:msvc_find_valid_batch_script: - | Build a list of plausible target values, and loop through - | look for host + target -> find_batch_file(env, ver, host, target) - | - +-+ MSCommon/vc.py:find_batch_file: - | call -> find_vc_pdir (see above) - | use the return to construct a version-biased batfile path, check - / - | if not found, try sdk scripts (unknown if this is still useful) - - -Problems: -- For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS -- For vswhere-ready versions, detection does not proceed beyond the - product level ("2019") into individual "features" (individual msvc) -- As documented for MSVC_VERSION, compilers can only be requested if versions - are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 -- Information found in the first pass (msvs_exists) isn't really - available anywhere except the cached version list, since we just - return true/false. -- Since msvc_exists chain of calls does not look at version, we - can proceed to compiler setup if *any* msvc was found, even if the - one requested wasn't found. diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 8054552caa..7050c815c9 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -7,8 +7,10 @@ Compiler Detection Logic **WARNING: the compiler detection logic documentation below is likely out-of-date.** The verbatim content below is taken in its entirety from the existing ``MSCommom/README`` -file. In the future, the the compiler detection logic documentation will will be updated -and converted to the current document format. +file. + +In the future, the compiler detection logic documentation will be updated and integrated +into the current document format as appropriate. :: @@ -120,6 +122,3 @@ and converted to the current document format. can proceed to compiler setup if *any* msvc was found, even if the one requested wasn't found. - - - From 587d55d96e579409dc07cb377669db141386e327 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:33:33 -0400 Subject: [PATCH 095/108] Revert "Rename README to README.rst and update contents for RST. [ci skip]" This reverts commit 4822e9b0663b5ba1b80bf7f6877e442820a55caf. --- SCons/Tool/MSCommon/README | 112 +++++++++++++++++++++++++++++++++ SCons/Tool/MSCommon/README.rst | 9 +-- 2 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 SCons/Tool/MSCommon/README diff --git a/SCons/Tool/MSCommon/README b/SCons/Tool/MSCommon/README new file mode 100644 index 0000000000..5b09b58f60 --- /dev/null +++ b/SCons/Tool/MSCommon/README @@ -0,0 +1,112 @@ +#################################################################################### +# WARNING: The compiler detection logic documentation below is likely out-of-date. # +# The content of this file is transitioning to README.rst. # +#################################################################################### + +This is the flow of the compiler detection logic: + +External to MSCommon: + + The Tool init modules, in their exists() routines, call -> msvc_exists(env) + +At the moment, those modules are: + SCons/Tool/midl.py + SCons/Tool/mslib.py + SCons/Tool/mslink.py + SCons/Tool/msvc.py + SCons/Tool/msvs.py + +env may contain a version request in MSVC_VERSION, but this is not used +in the detection that follows from msvc_exists(), only in the later +batch that starts with a call to msvc_setup_env(). + +Internal to MSCommon/vc.py: + ++ MSCommon/vc.py:msvc_exists: +| vcs = cached_get_installed_vcs(env) +| returns True if vcs > 0 +| ++-> MSCommon/vc.py:cached_get_installed_vcs: + | checks global if we've run previously, if so return it + | populate the global from -> get_installed_vcs(env) + | + +-> MSCommon/vc.py:get_installed_vcs: + | loop through "known" versions of msvc, granularity is maj.min + | check for product dir -> find_vc_pdir(env, ver) + | + +-> MSCommon/vc.py:find_vc_pdir: + | From the msvc-version to pdir mapping dict, get reg key base and value + | If value is none -> find_vc_pdir_vswhere(ver, env) + | + +-> MSCommon/vc.py:find_vc_pdir_vswhere: + | From the vc-version to VS-version mapping table get string + | Figure out where vswhere is -> msvc_find_vswhere() + | Use subprocess to call vswhere, return first line of match + / + | else get product directory from registry (<= 14.0) + / + | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) + | + +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: + | Figure out host/target pair + | if version > 14.0 get specific version by looking in + | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt + | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe + | if 14.0 or less, "do older stuff" + +All of this just got us a yes-no answer on whether /some/ msvc version +exists, but does populate __INSTALLED_VCS_RUN with all of the top-level +versions as noted for get_installed_vcs + +Externally: + + Once a module's exists() has been called (or, in the case of + clang/clangxx, after the compiler has been detected by other means - + those still expect the rest of the msvc chain but not cl.exe) + the module's generate() function calls -> msvc_setup_env_once(env) + +Internally: + ++ MSCommon/vc.py:msvc_setup_env_once: +| checks for environment flag MSVC_SETUP_RUN +| if not, -> msvc_setup_env(env) and set flag +| ++-+ MSCommon/vc.py:msvc_setup_env: + | set ver from -> get_default_version(env) + | + +-+ MSCommon/vc.py:get_default_version: + | if no version specified in env.MSVC_VERSION: + | return first entry from -> cached_get_installed_vcs(env) + | else return requested version + / + | get script from MSVC_USE_SCRIPT if set to a filename + | -> script_env(script) + | + +-+ MSCommon/vc.py:script_env: + | return (possibly cached) script variables matching script arg + / + | else -> msvc_find_valid_batch_script(env, version) + | + +-+ MSCommon/vc.py:msvc_find_valid_batch_script: + | Build a list of plausible target values, and loop through + | look for host + target -> find_batch_file(env, ver, host, target) + | + +-+ MSCommon/vc.py:find_batch_file: + | call -> find_vc_pdir (see above) + | use the return to construct a version-biased batfile path, check + / + | if not found, try sdk scripts (unknown if this is still useful) + + +Problems: +- For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS +- For vswhere-ready versions, detection does not proceed beyond the + product level ("2019") into individual "features" (individual msvc) +- As documented for MSVC_VERSION, compilers can only be requested if versions + are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 +- Information found in the first pass (msvs_exists) isn't really + available anywhere except the cached version list, since we just + return true/false. +- Since msvc_exists chain of calls does not look at version, we + can proceed to compiler setup if *any* msvc was found, even if the + one requested wasn't found. diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 7050c815c9..8054552caa 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -7,10 +7,8 @@ Compiler Detection Logic **WARNING: the compiler detection logic documentation below is likely out-of-date.** The verbatim content below is taken in its entirety from the existing ``MSCommom/README`` -file. - -In the future, the compiler detection logic documentation will be updated and integrated -into the current document format as appropriate. +file. In the future, the the compiler detection logic documentation will will be updated +and converted to the current document format. :: @@ -122,3 +120,6 @@ into the current document format as appropriate. can proceed to compiler setup if *any* msvc was found, even if the one requested wasn't found. + + + From d9f89fe72ef228d7bf746a65a550c251f6448c39 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:34:53 -0400 Subject: [PATCH 096/108] Remove README.rst. --- SCons/Tool/MSCommon/README.rst | 125 --------------------------------- 1 file changed, 125 deletions(-) delete mode 100644 SCons/Tool/MSCommon/README.rst diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst deleted file mode 100644 index 8054552caa..0000000000 --- a/SCons/Tool/MSCommon/README.rst +++ /dev/null @@ -1,125 +0,0 @@ -README - SCons.Tool.MSCommon -############################ - -Compiler Detection Logic -======================== - -**WARNING: the compiler detection logic documentation below is likely out-of-date.** - -The verbatim content below is taken in its entirety from the existing ``MSCommom/README`` -file. In the future, the the compiler detection logic documentation will will be updated -and converted to the current document format. - -:: - - This is the flow of the compiler detection logic: - - External to MSCommon: - - The Tool init modules, in their exists() routines, call -> msvc_exists(env) - - At the moment, those modules are: - SCons/Tool/midl.py - SCons/Tool/mslib.py - SCons/Tool/mslink.py - SCons/Tool/msvc.py - SCons/Tool/msvs.py - - env may contain a version request in MSVC_VERSION, but this is not used - in the detection that follows from msvc_exists(), only in the later - batch that starts with a call to msvc_setup_env(). - - Internal to MSCommon/vc.py: - - + MSCommon/vc.py:msvc_exists: - | vcs = cached_get_installed_vcs(env) - | returns True if vcs > 0 - | - +-> MSCommon/vc.py:cached_get_installed_vcs: - | checks global if we've run previously, if so return it - | populate the global from -> get_installed_vcs(env) - | - +-> MSCommon/vc.py:get_installed_vcs: - | loop through "known" versions of msvc, granularity is maj.min - | check for product dir -> find_vc_pdir(env, ver) - | - +-> MSCommon/vc.py:find_vc_pdir: - | From the msvc-version to pdir mapping dict, get reg key base and value - | If value is none -> find_vc_pdir_vswhere(ver, env) - | - +-> MSCommon/vc.py:find_vc_pdir_vswhere: - | From the vc-version to VS-version mapping table get string - | Figure out where vswhere is -> msvc_find_vswhere() - | Use subprocess to call vswhere, return first line of match - / - | else get product directory from registry (<= 14.0) - / - | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) - | - +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: - | Figure out host/target pair - | if version > 14.0 get specific version by looking in - | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt - | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe - | if 14.0 or less, "do older stuff" - - All of this just got us a yes-no answer on whether /some/ msvc version - exists, but does populate __INSTALLED_VCS_RUN with all of the top-level - versions as noted for get_installed_vcs - - Externally: - - Once a module's exists() has been called (or, in the case of - clang/clangxx, after the compiler has been detected by other means - - those still expect the rest of the msvc chain but not cl.exe) - the module's generate() function calls -> msvc_setup_env_once(env) - - Internally: - - + MSCommon/vc.py:msvc_setup_env_once: - | checks for environment flag MSVC_SETUP_RUN - | if not, -> msvc_setup_env(env) and set flag - | - +-+ MSCommon/vc.py:msvc_setup_env: - | set ver from -> get_default_version(env) - | - +-+ MSCommon/vc.py:get_default_version: - | if no version specified in env.MSVC_VERSION: - | return first entry from -> cached_get_installed_vcs(env) - | else return requested version - / - | get script from MSVC_USE_SCRIPT if set to a filename - | -> script_env(script) - | - +-+ MSCommon/vc.py:script_env: - | return (possibly cached) script variables matching script arg - / - | else -> msvc_find_valid_batch_script(env, version) - | - +-+ MSCommon/vc.py:msvc_find_valid_batch_script: - | Build a list of plausible target values, and loop through - | look for host + target -> find_batch_file(env, ver, host, target) - | - +-+ MSCommon/vc.py:find_batch_file: - | call -> find_vc_pdir (see above) - | use the return to construct a version-biased batfile path, check - / - | if not found, try sdk scripts (unknown if this is still useful) - - - Problems: - - For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS - - For vswhere-ready versions, detection does not proceed beyond the - product level ("2019") into individual "features" (individual msvc) - - As documented for MSVC_VERSION, compilers can only be requested if versions - are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 - - Information found in the first pass (msvs_exists) isn't really - available anywhere except the cached version list, since we just - return true/false. - - Since msvc_exists chain of calls does not look at version, we - can proceed to compiler setup if *any* msvc was found, even if the - one requested wasn't found. - - - - From 1eefde235c05761687424189bf6eee2ed9b321d1 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:36:12 -0400 Subject: [PATCH 097/108] Rename README to README.rst. --- SCons/Tool/MSCommon/{README => README.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename SCons/Tool/MSCommon/{README => README.rst} (100%) diff --git a/SCons/Tool/MSCommon/README b/SCons/Tool/MSCommon/README.rst similarity index 100% rename from SCons/Tool/MSCommon/README rename to SCons/Tool/MSCommon/README.rst From 89fc869b2a1f7592474b548061e16fb5522f6d21 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 14:37:43 -0400 Subject: [PATCH 098/108] Convert format from plain text to RST. [ci skip] --- SCons/Tool/MSCommon/README.rst | 230 +++++++++++++++++---------------- 1 file changed, 121 insertions(+), 109 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 5b09b58f60..7050c815c9 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -1,112 +1,124 @@ -#################################################################################### -# WARNING: The compiler detection logic documentation below is likely out-of-date. # -# The content of this file is transitioning to README.rst. # -#################################################################################### - -This is the flow of the compiler detection logic: - -External to MSCommon: - - The Tool init modules, in their exists() routines, call -> msvc_exists(env) - -At the moment, those modules are: - SCons/Tool/midl.py - SCons/Tool/mslib.py - SCons/Tool/mslink.py - SCons/Tool/msvc.py - SCons/Tool/msvs.py - -env may contain a version request in MSVC_VERSION, but this is not used -in the detection that follows from msvc_exists(), only in the later -batch that starts with a call to msvc_setup_env(). - -Internal to MSCommon/vc.py: - -+ MSCommon/vc.py:msvc_exists: -| vcs = cached_get_installed_vcs(env) -| returns True if vcs > 0 -| -+-> MSCommon/vc.py:cached_get_installed_vcs: - | checks global if we've run previously, if so return it - | populate the global from -> get_installed_vcs(env) - | - +-> MSCommon/vc.py:get_installed_vcs: - | loop through "known" versions of msvc, granularity is maj.min - | check for product dir -> find_vc_pdir(env, ver) +README - SCons.Tool.MSCommon +############################ + +Compiler Detection Logic +======================== + +**WARNING: the compiler detection logic documentation below is likely out-of-date.** + +The verbatim content below is taken in its entirety from the existing ``MSCommom/README`` +file. + +In the future, the compiler detection logic documentation will be updated and integrated +into the current document format as appropriate. + +:: + + This is the flow of the compiler detection logic: + + External to MSCommon: + + The Tool init modules, in their exists() routines, call -> msvc_exists(env) + + At the moment, those modules are: + SCons/Tool/midl.py + SCons/Tool/mslib.py + SCons/Tool/mslink.py + SCons/Tool/msvc.py + SCons/Tool/msvs.py + + env may contain a version request in MSVC_VERSION, but this is not used + in the detection that follows from msvc_exists(), only in the later + batch that starts with a call to msvc_setup_env(). + + Internal to MSCommon/vc.py: + + + MSCommon/vc.py:msvc_exists: + | vcs = cached_get_installed_vcs(env) + | returns True if vcs > 0 | - +-> MSCommon/vc.py:find_vc_pdir: - | From the msvc-version to pdir mapping dict, get reg key base and value - | If value is none -> find_vc_pdir_vswhere(ver, env) + +-> MSCommon/vc.py:cached_get_installed_vcs: + | checks global if we've run previously, if so return it + | populate the global from -> get_installed_vcs(env) | - +-> MSCommon/vc.py:find_vc_pdir_vswhere: - | From the vc-version to VS-version mapping table get string - | Figure out where vswhere is -> msvc_find_vswhere() - | Use subprocess to call vswhere, return first line of match - / - | else get product directory from registry (<= 14.0) - / - | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) - | - +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: - | Figure out host/target pair - | if version > 14.0 get specific version by looking in - | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt - | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe - | if 14.0 or less, "do older stuff" - -All of this just got us a yes-no answer on whether /some/ msvc version -exists, but does populate __INSTALLED_VCS_RUN with all of the top-level -versions as noted for get_installed_vcs - -Externally: - - Once a module's exists() has been called (or, in the case of - clang/clangxx, after the compiler has been detected by other means - - those still expect the rest of the msvc chain but not cl.exe) - the module's generate() function calls -> msvc_setup_env_once(env) - -Internally: - -+ MSCommon/vc.py:msvc_setup_env_once: -| checks for environment flag MSVC_SETUP_RUN -| if not, -> msvc_setup_env(env) and set flag -| -+-+ MSCommon/vc.py:msvc_setup_env: - | set ver from -> get_default_version(env) - | - +-+ MSCommon/vc.py:get_default_version: - | if no version specified in env.MSVC_VERSION: - | return first entry from -> cached_get_installed_vcs(env) - | else return requested version - / - | get script from MSVC_USE_SCRIPT if set to a filename - | -> script_env(script) - | - +-+ MSCommon/vc.py:script_env: - | return (possibly cached) script variables matching script arg - / - | else -> msvc_find_valid_batch_script(env, version) - | - +-+ MSCommon/vc.py:msvc_find_valid_batch_script: - | Build a list of plausible target values, and loop through - | look for host + target -> find_batch_file(env, ver, host, target) + +-> MSCommon/vc.py:get_installed_vcs: + | loop through "known" versions of msvc, granularity is maj.min + | check for product dir -> find_vc_pdir(env, ver) + | + +-> MSCommon/vc.py:find_vc_pdir: + | From the msvc-version to pdir mapping dict, get reg key base and value + | If value is none -> find_vc_pdir_vswhere(ver, env) + | + +-> MSCommon/vc.py:find_vc_pdir_vswhere: + | From the vc-version to VS-version mapping table get string + | Figure out where vswhere is -> msvc_find_vswhere() + | Use subprocess to call vswhere, return first line of match + / + | else get product directory from registry (<= 14.0) + / + | if we found one -> _check_cl_exists_in_vc_dir(env, pdir, ver) + | + +-> MSCommon/vc.py:_check_cl_exists_in_vc_dir: + | Figure out host/target pair + | if version > 14.0 get specific version by looking in + | pdir + Auxiliary/Build/Microsoft/VCToolsVersion/default.txt + | look for pdir + Tools/MSVC/{specver}/bin/host/target/cl.exe + | if 14.0 or less, "do older stuff" + + All of this just got us a yes-no answer on whether /some/ msvc version + exists, but does populate __INSTALLED_VCS_RUN with all of the top-level + versions as noted for get_installed_vcs + + Externally: + + Once a module's exists() has been called (or, in the case of + clang/clangxx, after the compiler has been detected by other means - + those still expect the rest of the msvc chain but not cl.exe) + the module's generate() function calls -> msvc_setup_env_once(env) + + Internally: + + + MSCommon/vc.py:msvc_setup_env_once: + | checks for environment flag MSVC_SETUP_RUN + | if not, -> msvc_setup_env(env) and set flag | - +-+ MSCommon/vc.py:find_batch_file: - | call -> find_vc_pdir (see above) - | use the return to construct a version-biased batfile path, check - / - | if not found, try sdk scripts (unknown if this is still useful) - - -Problems: -- For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS -- For vswhere-ready versions, detection does not proceed beyond the - product level ("2019") into individual "features" (individual msvc) -- As documented for MSVC_VERSION, compilers can only be requested if versions - are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 -- Information found in the first pass (msvs_exists) isn't really - available anywhere except the cached version list, since we just - return true/false. -- Since msvc_exists chain of calls does not look at version, we - can proceed to compiler setup if *any* msvc was found, even if the - one requested wasn't found. + +-+ MSCommon/vc.py:msvc_setup_env: + | set ver from -> get_default_version(env) + | + +-+ MSCommon/vc.py:get_default_version: + | if no version specified in env.MSVC_VERSION: + | return first entry from -> cached_get_installed_vcs(env) + | else return requested version + / + | get script from MSVC_USE_SCRIPT if set to a filename + | -> script_env(script) + | + +-+ MSCommon/vc.py:script_env: + | return (possibly cached) script variables matching script arg + / + | else -> msvc_find_valid_batch_script(env, version) + | + +-+ MSCommon/vc.py:msvc_find_valid_batch_script: + | Build a list of plausible target values, and loop through + | look for host + target -> find_batch_file(env, ver, host, target) + | + +-+ MSCommon/vc.py:find_batch_file: + | call -> find_vc_pdir (see above) + | use the return to construct a version-biased batfile path, check + / + | if not found, try sdk scripts (unknown if this is still useful) + + + Problems: + - For VS >= 2017, VS and VS are not 1:1, there can be many VC for one VS + - For vswhere-ready versions, detection does not proceed beyond the + product level ("2019") into individual "features" (individual msvc) + - As documented for MSVC_VERSION, compilers can only be requested if versions + are from the set in _VCVER, so 14.1 but not 14.16 or 14.16.27023 + - Information found in the first pass (msvs_exists) isn't really + available anywhere except the cached version list, since we just + return true/false. + - Since msvc_exists chain of calls does not look at version, we + can proceed to compiler setup if *any* msvc was found, even if the + one requested wasn't found. + From e274cf1e8a902f02fe6e49b699dc5bf613f57e4d Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Mon, 18 Jul 2022 15:37:57 -0400 Subject: [PATCH 099/108] Remove sentence that predates file rename. [ci skip] --- SCons/Tool/MSCommon/README.rst | 3 --- 1 file changed, 3 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index 7050c815c9..a38242c7a6 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -6,9 +6,6 @@ Compiler Detection Logic **WARNING: the compiler detection logic documentation below is likely out-of-date.** -The verbatim content below is taken in its entirety from the existing ``MSCommom/README`` -file. - In the future, the compiler detection logic documentation will be updated and integrated into the current document format as appropriate. From aca539b05c509bbc04692e9cab4f23a256c676b8 Mon Sep 17 00:00:00 2001 From: Joseph Brill <48932340+jcbrill@users.noreply.github.com> Date: Tue, 19 Jul 2022 14:15:05 -0400 Subject: [PATCH 100/108] Update MSCommon/README.rst. --- SCons/Tool/MSCommon/README.rst | 384 ++++++++++++++++++++++++++++++++- 1 file changed, 382 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/README.rst b/SCons/Tool/MSCommon/README.rst index a38242c7a6..36e58aad46 100644 --- a/SCons/Tool/MSCommon/README.rst +++ b/SCons/Tool/MSCommon/README.rst @@ -1,8 +1,388 @@ -README - SCons.Tool.MSCommon +.. sectnum:: + +README - SCons.Tool.MSCommon ############################ +.. contents:: **Table of Contents** + :depth: 2 + :local: + + +Design Notes +============ + +* Public, user-callable functions and exception types are available via + the ``SCons.Tool.MSCommon`` namespace. + +* Some existing code has been moved from ``MSCommon/vc.py`` to the appropriate + ``MSCommon/MSVC/``. + +* No functions from the MSVC module or its child modules are intended to be invoked directly. + All functions of interest are made available via the ``SCons.Tool.MSCommon`` namespace. + It is anticipated that more code may be moved in the future as new features are added. + By exposing the public API through ``SCons.Tool.MSCommon`` there should not be a problem + with code movement. + +* Additional helper functions primarily used for the test suite were added to + ``MSCommon/vc.py`` and are available via the ``SCons.Tool.MSCommon`` namespace. + + +Known Issues +============ + +The following issues are known to exist: + +* Using ``MSVC_USE_SCRIPT`` and ``MSVC_USE_SCRIPT_ARGS`` to call older Microsoft SDK + ``SetEnv.cmd`` batch files may result in build failures. Some of these batch files + require delayed expansion to be enabled which is not usually the Windows default. + One solution would be to launch the MSVC batch file command in a new command interpreter + instance with delayed expansion enabled via command-line options. + +* The code to suppress the "No versions of the MSVC compiler were found" warning for + the default environment was moved from ``MSCommon/vc.py`` to ``MSCommon/MSVC/SetupEnvDefault.py``. + There very few, if any, existing unit tests. Now that the code is isolated in its own + module with a limited API, unit tests may be easier to implement. + + +Experimental Features +===================== + +msvc_query_version_toolset(version=None, prefer_newest=True) +------------------------------------------------------------ + +The experimental function ``msvc_query_version_toolset`` was added to ``MSCommon/vc.py`` +and is available via the ``SCons.Tool.MSCommon`` namespace. This function takes a version +specification or a toolset version specification and a product preference as arguments and +returns the msvc version and the msvc toolset version for the corresponding version specification. + +This is a proxy for using the toolset version for selection until that functionality can be added. + +Example usage: +:: + for version in [ + '14.3', + '14.2', + '14.1', + '14.0', + '14.32', + '14.31', + '14.29', + '14.16', + '14.00', + '14.28.29333', # only 14.2 + '14.20.29333', # fictitious for testing + ]: + + for prefer_newest in (True, False): + try: + msvc_version, msvc_toolset_version = msvc_query_version_toolset(version, prefer_newest=prefer_newest) + failed = False + except MSVCToolsetVersionNotFound: + failed = True + if failed: + msg = 'FAILED' + newline = '\n' + else: + env = Environment(MSVC_VERSION=msvc_version, MSVC_TOOLSET_VERSION=msvc_toolset_version) + msg = 'passed' + newline = '' + print('{}Query: {} version={}, prefer_newest={}'.format(newline, msg, version, prefer_newest)) + +Example output fragment +:: + Build: _build003 {'MSVC_VERSION': '14.3', 'MSVC_TOOLSET_VERSION': '14.29.30133'} + Where: C:\Software\MSVS-2022-143-Com\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64\cl.exe + Where: C:\Software\MSVS-2022-143-Com\Common7\Tools\guidgen.exe + Query: passed version=14.2, prefer_newest=True + + Build: _build004 {'MSVC_VERSION': '14.2', 'MSVC_TOOLSET_VERSION': '14.29.30133'} + Where: C:\Software\MSVS-2019-142-Com\VC\Tools\MSVC\14.29.30133\bin\HostX64\x64\cl.exe + Where: C:\Software\MSVS-2019-142-Com\Common7\Tools\guidgen.exe + Query: passed version=14.2, prefer_newest=False + + +Undocumented Features +===================== + +set SCONS_CACHE_MSVC_FORCE_DEFAULTS=1 +------------------------------------- + +The Windows system environment variable ``SCONS_CACHE_MSVC_FORCE_DEFAULTS`` was added. This variable is only +evaluated when the msvc cache is enabled and accepts the values ``1``, ``true``, and ``True``. + +When enabled, the default msvc toolset version and the default sdk version, if not otherwise specified, are +added to the batch file argument list. This is intended to make the cache more resilient to Visual Studio +updates that may change the default toolset version and/or the default SDK version. + +Example usage: +:: + + @echo Enabling scons cache ... + @set "SCONS_CACHE_MSVC_CONFIG=mycachefile.json" + @set "SCONS_CACHE_MSVC_FORCE_DEFAULTS=True" + + +End-User Diagnostic Tools +========================= + +Due to the proliferation of user-defined msvc batch file arguments, the likelihood of end-user build +failures has increased. + +Some of the options that may be employed in diagnosing end-user msvc build failures are listed below. + +msvc_set_scripterror_policy('Warning') and MSVC_SCRIPTERROR_POLICY='Warning' +---------------------------------------------------------------------------- + +Enabling warnings to be produced for detected msvc batch file errors may provide additional context +for build failures. Refer to the documentation for details. + +Change the default policy: +:: + from SCons.Tool.MSCommon import msvc_set_scripterror_policy + + msvc_set_scripterror_policy('Warning') + +Specify the policy per-environment: +:: + + env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True, MSVC_SCRIPTERROR_POLICY='Warning') + + +set SCONS_MSCOMMON_DEBUG=mydebugfile.txt +---------------------------------------- + +The traditional method of diagnosing end-user issues is to enable the internal msvc debug logging. + + +set SCONS_CACHE_MSVC_CONFIG=mycachefile.json +-------------------------------------------- + +On occasion, enabling the cache file can prove to be a useful diagnostic tool. If nothing else, +issues with the msvc environment may be readily apparent. + + +vswhere.exe +----------- + +On occasion, the raw vswhere output may prove useful especially if there are suspected issues with +detection of installed msvc instances. + +Windows command-line sample invocations: +:: + @rem 64-Bit Windows + "%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere.exe" -all -sort -prerelease -products * -legacy -format json >MYVSWHEREOUTPUT.json + + @rem 32-Bit Windows: + "%ProgramFiles%\Microsoft Visual Studio\Installer\vswhere.exe" -all -sort -prerelease -products * -legacy -format json >MYVSWHEREOUTPUT.json + + +Visual Studio Implementation Notes +================================== + +Batch File Arguments +-------------------- + +Supported MSVC batch file arguments by product: + +======= === === ======= ======= +Product UWP SDK Toolset Spectre +======= === === ======= ======= +VS2022 X X X X +------- --- --- ------- ------- +VS2019 X X X X +------- --- --- ------- ------- +VS2017 X X X X +------- --- --- ------- ------- +VS2015 X X +======= === === ======= ======= + +Supported MSVC batch file arguments in SCons: + +======== ====================================== =================================================== +Argument Construction Variable Script Argument Equivalent +======== ====================================== =================================================== +UWP ``MSVC_UWP_APP=True`` ``MSVC_SCRIPT_ARGS='store'`` +-------- -------------------------------------- --------------------------------------------------- +SDK ``MSVC_SDK_VERSION='10.0.20348.0'`` ``MSVC_SCRIPT_ARGS='10.0.20348.0'`` +-------- -------------------------------------- --------------------------------------------------- +Toolset ``MSVC_TOOLSET_VERSION='14.31.31103'`` ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.31.31103'`` +-------- -------------------------------------- --------------------------------------------------- +Spectre ``MSVC_SPECTRE_LIBS=True`` ``MSVC_SCRIPT_ARGS='-vcvars_spectre_libs=spectre'`` +======== ====================================== =================================================== + +**MSVC_SCRIPT_ARGS contents are not validated. Utilizing script arguments that have construction +variable equivalents is discouraged and may lead to difficult to diagnose build errors.** + +Additional constraints: + +* ``MSVC_SDK_VERSION='8.1'`` and ``MSVC_UWP_APP=True`` is supported only for the v140 + build tools (i.e., ``MSVC_VERSION='14.0'`` or ``MSVC_TOOLSET_VERSION='14.0'``). + +* ``MSVC_SPECTRE_LIBS=True`` and ``MSVC_UWP_APP=True`` is not supported (i.e., there + are no spectre mitigations libraries for UWP builds). + +Default Toolset Version +----------------------- + +Side-by-side toolset versions were introduced in Visual Studio 2017. +The examples shown below are for Visual Studio 2022. + +The msvc default toolset version is dependent on the installation options +selected. This means that the default toolset version may be different for +each machine given the same Visual Studio product. + +The msvc default toolset is not necessarily the latest toolset installed. +This has implications when a toolset version is specified using only one minor +digit (e.g., ``MSVC_TOOLSET_VERSION='14.3'`` or ``MSVC_SCRIPT_ARGS='-vcvars_ver=14.3'``). + +Explicitly defining ``MSVC_TOOLSET_VERSION=None`` will return the same toolset +that the msvc batch files would return. When using ``MSVC_SCRIPT_ARGS``, the +toolset specification should be omitted entirely. + +Local installation and summary test results: +:: + VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt + 14.31.31103 + + VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.default.txt + 14.32.31326 + +Toolset version summary: +:: + 14.31.31103 Environment() + 14.31.31103 Environment(MSVC_TOOLSET_VERSION=None) + + 14.32.31326* Environment(MSVC_TOOLSET_VERSION='14.3') + 14.32.31326* Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.3']) + + 14.31.31103 Environment(MSVC_TOOLSET_VERSION='14.31') + 14.31.31103 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.31']) + + 14.32.31326 Environment(MSVC_TOOLSET_VERSION='14.32') + 14.32.31326 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.32']) + +VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment: +:: + @echo -vcvars_ver=version : Version of VC++ Toolset to select + @echo ** [Default] : If -vcvars_ver=version is NOT specified, the toolset specified by + @echo [VSInstallDir]\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt will be used. + @echo ** 14.0 : VS 2015 (v140) VC++ Toolset (installation of the v140 toolset is a prerequisite) + @echo ** 14.xx : VS 2017 or VS 2019 VC++ Toolset, if that version is installed on the system under + @echo [VSInstallDir]\VC\MSVC\Tools\[version]. Where '14.xx' specifies a partial + @echo [version]. The latest [version] directory that matches the specified value will + @echo be used. + @echo ** 14.xx.yyyyy : VS 2017 or VS 2019 VC++ Toolset, if that version is installed on the system under + @echo [VSInstallDir]\VC\MSVC\Tools\[version]. Where '14.xx.yyyyy' specifies an + @echo exact [version] directory to be used. + @echo ** 14.xx.VV.vv : VS 2019 C++ side-by-side toolset package identity alias, if the SxS toolset has been installed on the system. + @echo Where '14.xx.VV.vv' corresponds to a SxS toolset + @echo VV = VS Update Major Version (e.g. "16" for VS 2019 v16.9) + @echo vv = VS Update Minor version (e.g. "9" for VS 2019 v16.9) + @echo Please see [VSInstallDir]\VC\Auxiliary\Build\[version]\Microsoft.VCToolsVersion.[version].txt for mapping of + @echo SxS toolset to [VSInstallDir]\VC\MSVC\Tools\ directory. + +VS2022 batch file fragment to determine the default toolset version: +:: + @REM Add MSVC + set "__VCVARS_DEFAULT_CONFIG_FILE=%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.default.txt" + + @REM We will "fallback" to Microsoft.VCToolsVersion.default.txt (latest) if Microsoft.VCToolsVersion.v143.default.txt does not exist. + if EXIST "%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt" ( + if "%VSCMD_DEBUG%" GEQ "2" @echo [DEBUG:ext\%~nx0] Microsoft.VCToolsVersion.v143.default.txt was found. + set "__VCVARS_DEFAULT_CONFIG_FILE=%VCINSTALLDIR%Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt" + + ) else ( + if "%VSCMD_DEBUG%" GEQ "1" @echo [DEBUG:ext\%~nx0] Microsoft.VCToolsVersion.v143.default.txt was not found. Defaulting to 'Microsoft.VCToolsVersion.default.txt'. + ) + +Empirical evidence suggests that the default toolset version is different from the latest +toolset version when the toolset version immediately preceding the latest version is +installed. For example, the ``14.31`` toolset version is installed when the ``14.32`` +toolset version is the latest. + + +Visual Studio Version Notes +============================ + +SDK Versions +------------ + +==== ============ +SDK Format +==== ============ +10.0 10.0.XXXXX.Y +---- ------------ +8.1 8.1 +==== ============ + +BuildTools Versions +------------------- + +========== ===== ===== ======== +BuildTools VCVER CLVER MSVCRT +========== ===== ===== ======== +v143 14.3 19.3 140/ucrt +---------- ----- ----- -------- +v142 14.2 19.2 140/ucrt +---------- ----- ----- -------- +v141 14.1 19.1 140/ucrt +---------- ----- ----- -------- +v140 14.0 19.0 140/ucrt +---------- ----- ----- -------- +v120 12.0 18.0 120 +---------- ----- ----- -------- +v110 11.0 17.0 110 +---------- ----- ----- -------- +v100 10.0 16.0 100 +---------- ----- ----- -------- +v90 9.0 15.0 90 +---------- ----- ----- -------- +v80 8.0 14.0 80 +---------- ----- ----- -------- +v71 7.1 13.1 71 +---------- ----- ----- -------- +v70 7.0 13.0 70 +---------- ----- ----- -------- +v60 6.0 12.0 60 +========== ===== ===== ======== + +Product Versions +---------------- + +======== ===== ========= ============ +Product VSVER SDK BuildTools +======== ===== ========= ============ +2022 17.0 10.0, 8.1 v143 .. v140 +-------- ----- --------- ------------ +2019 16.0 10.0, 8.1 v142 .. v140 +-------- ----- --------- ------------ +2017 15.0 10.0, 8.1 v141 .. v140 +-------- ----- --------- ------------ +2015 14.0 10.0, 8.1 v140 +-------- ----- --------- ------------ +2013 12.0 v120 +-------- ----- --------- ------------ +2012 11.0 v110 +-------- ----- --------- ------------ +2010 10.0 v100 +-------- ----- --------- ------------ +2008 9.0 v90 +-------- ----- --------- ------------ +2005 8.0 v80 +-------- ----- --------- ------------ +2003.NET 7.1 v71 +-------- ----- --------- ------------ +2002.NET 7.0 v70 +-------- ----- --------- ------------ +6.0 6.0 v60 +======== ===== ========= ============ + + +SCons Implementation Notes +========================== + Compiler Detection Logic -======================== +------------------------ **WARNING: the compiler detection logic documentation below is likely out-of-date.** From 65204c92d3a39c3ccf101649a0639a91d7497ebe Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 23 Jul 2022 17:06:38 -0700 Subject: [PATCH 101/108] refactore to avoid shadowing 'bool' changed variable name to bool_val --- SCons/Tool/MSCommon/MSVC/Config.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Config.py b/SCons/Tool/MSCommon/MSVC/Config.py index 8b7f5eac10..015cf7245f 100644 --- a/SCons/Tool/MSCommon/MSVC/Config.py +++ b/SCons/Tool/MSCommon/MSVC/Config.py @@ -44,16 +44,16 @@ BOOLEAN_SYMBOLS = {} BOOLEAN_EXTERNAL = {} -for bool, symbol_list, symbol_case_list in [ +for bool_val, symbol_list, symbol_case_list in [ (False, (False, 0, '0', None, ''), ('False', 'No', 'F', 'N')), (True, (True, 1, '1'), ('True', 'Yes', 'T', 'Y')), ]: - BOOLEAN_SYMBOLS[bool] = list(symbol_list) + BOOLEAN_SYMBOLS[bool_val] = list(symbol_list) for symbol in symbol_case_list: - BOOLEAN_SYMBOLS[bool].extend([symbol, symbol.lower(), symbol.upper()]) + BOOLEAN_SYMBOLS[bool_val].extend([symbol, symbol.lower(), symbol.upper()]) - for symbol in BOOLEAN_SYMBOLS[bool]: - BOOLEAN_EXTERNAL[symbol] = bool + for symbol in BOOLEAN_SYMBOLS[bool_val]: + BOOLEAN_EXTERNAL[symbol] = bool_val MSVC_PLATFORM_DEFINITION = namedtuple('MSVCPlatform', [ 'vc_platform', From 69bb077b5069735a200c6c94b3e71d7e8deedfc2 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 23 Jul 2022 17:24:46 -0700 Subject: [PATCH 102/108] Format code via black --- SCons/Tool/MSCommon/MSVC/ConfigTests.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/ConfigTests.py b/SCons/Tool/MSCommon/MSVC/ConfigTests.py index 0528930934..89db6cda5b 100644 --- a/SCons/Tool/MSCommon/MSVC/ConfigTests.py +++ b/SCons/Tool/MSCommon/MSVC/ConfigTests.py @@ -31,12 +31,10 @@ from SCons.Tool.MSCommon.MSVC import Config from SCons.Tool.MSCommon.MSVC.Exceptions import MSVCInternalError -class Patch: +class Patch: class vc: - class _VCVER: - _VCVER = vc._VCVER @classmethod @@ -50,9 +48,7 @@ def restore(cls): vc._VCVER = cls._VCVER class Config: - class MSVC_VERSION_INTERNAL: - MSVC_VERSION_INTERNAL = Config.MSVC_VERSION_INTERNAL @classmethod @@ -65,6 +61,7 @@ def enable_copy(cls): def restore(cls): Config.MSVC_VERSION_INTERNAL = cls.MSVC_VERSION_INTERNAL + class ConfigTests(unittest.TestCase): def test_vcver(self): @@ -86,6 +83,6 @@ def test_msvc_version_internal(self): def test_verify(self): Config.verify() + if __name__ == "__main__": unittest.main() - From 6625bc4df0c557d3faf8414f8b4aa6c232a2d03b Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 23 Jul 2022 17:42:19 -0700 Subject: [PATCH 103/108] Format code via black --- SCons/Tool/MSCommon/MSVC/Dispatcher.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/Dispatcher.py b/SCons/Tool/MSCommon/MSVC/Dispatcher.py index dab1e15719..42b5287b0f 100644 --- a/SCons/Tool/MSCommon/MSVC/Dispatcher.py +++ b/SCons/Tool/MSCommon/MSVC/Dispatcher.py @@ -50,16 +50,18 @@ debug, ) - _refs = [] + def register_modulename(modname): module = sys.modules[modname] _refs.append(module) + def register_class(ref): _refs.append(ref) + def reset(): debug('') for ref in _refs: @@ -70,6 +72,7 @@ def reset(): func = getattr(ref, method) func() + def verify(): debug('') for ref in _refs: @@ -79,4 +82,3 @@ def verify(): debug('call %s.%s()', ref.__name__, method) func = getattr(ref, method) func() - From 3b79d4342918f4d27cf86d239ac3f82d12ca0bc5 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 23 Jul 2022 17:47:21 -0700 Subject: [PATCH 104/108] Format code via black --- SCons/Tool/MSCommon/MSVC/DispatcherTests.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/DispatcherTests.py b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py index ce7880c07f..d6af8d4648 100644 --- a/SCons/Tool/MSCommon/MSVC/DispatcherTests.py +++ b/SCons/Tool/MSCommon/MSVC/DispatcherTests.py @@ -28,21 +28,23 @@ import unittest from SCons.Tool.MSCommon import MSVC + MSVC.Dispatcher.register_modulename(__name__) -class Data: +class Data: reset_count = 0 verify_count = 0 + # current module - not callable _reset = None reset = None _verify = None verify = None -class StaticMethods: +class StaticMethods: @staticmethod def _reset(): Data.reset_count += 1 @@ -59,8 +61,8 @@ def _verify(): def verify(): Data.verify_count += 1 -class ClassMethods: +class ClassMethods: @classmethod def _reset(cls): Data.reset_count += 1 @@ -77,20 +79,21 @@ def _verify(cls): def verify(cls): Data.verify_count += 1 -class NotCallable: +class NotCallable: _reset = None reset = None _verify = None _verify = None + MSVC.Dispatcher.register_class(StaticMethods) MSVC.Dispatcher.register_class(ClassMethods) MSVC.Dispatcher.register_class(NotCallable) -class DispatcherTests(unittest.TestCase): +class DispatcherTests(unittest.TestCase): def test_dispatcher_reset(self): MSVC.Dispatcher.reset() self.assertTrue(Data.reset_count == 4, "MSVC.Dispatcher.reset() count failed") @@ -111,6 +114,6 @@ def test_msvc_verify(self): self.assertTrue(Data.verify_count == 4, "MSVC._verify() count failed") Data.verify_count = 0 + if __name__ == "__main__": unittest.main() - From eebf3b6e436f9d39246fc106f225d421415105a1 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sat, 23 Jul 2022 17:59:01 -0700 Subject: [PATCH 105/108] fix variable names which were shadowing global/module symbols --- SCons/Tool/MSCommon/MSVC/PolicyTests.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/SCons/Tool/MSCommon/MSVC/PolicyTests.py b/SCons/Tool/MSCommon/MSVC/PolicyTests.py index faa0f786e4..013fd477d0 100644 --- a/SCons/Tool/MSCommon/MSVC/PolicyTests.py +++ b/SCons/Tool/MSCommon/MSVC/PolicyTests.py @@ -46,15 +46,15 @@ class PolicyTests(unittest.TestCase): def setUp(self): self.warnstack = [] - def push_warning_as_exception(self, Warning): - SCons.Warnings.enableWarningClass(Warning) + def push_warning_as_exception(self, warning_class): + SCons.Warnings.enableWarningClass(warning_class) prev_state = SCons.Warnings.warningAsException() - self.warnstack.append((Warning, prev_state)) + self.warnstack.append((warning_class, prev_state)) def pop_warning_as_exception(self): - Warning, prev_state = self.warnstack.pop() + warning_class, prev_state = self.warnstack.pop() SCons.Warnings.warningAsException(prev_state) - SCons.Warnings.suppressWarningClass(Warning) + SCons.Warnings.suppressWarningClass(warning_class) # msvc_set_notfound_policy, msvc_get_notfound_policy, and MSVC_NOTFOUND_POLICY From bf659c3c39f4f0532dcc7efa5086575deb28fd0c Mon Sep 17 00:00:00 2001 From: William Deegan Date: Sun, 24 Jul 2022 17:21:10 -0700 Subject: [PATCH 106/108] Updated copyright block, minor code fixes --- SCons/Tool/MSCommon/vc.py | 17 +++++++++-------- SCons/Tool/MSCommon/vcTests.py | 9 ++++----- SCons/Tool/msvc.xml | 24 +++++++++++++++++++++++- 3 files changed, 36 insertions(+), 14 deletions(-) diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 823ba4b266..7871940627 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -518,7 +518,7 @@ def get_host_target(env, msvc_version, all_host_targets=False): msg = "Unrecognized host architecture %s for version %s" raise MSVCUnsupportedHostArch(msg % (repr(host_platform), msvc_version)) from None - return (host_platform, target_platform, host_target_list) + return host_platform, target_platform, host_target_list # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the # MSVC_VERSION documentation in Tool/msvc.xml. @@ -681,7 +681,8 @@ def find_vc_pdir_vswhere(msvc_version, env=None): debug("running: %s", vswhere_cmd) - #cp = subprocess.run(vswhere_cmd, capture_output=True, check=True) # 3.7+ only + # TODO: Python 3.7 + # cp = subprocess.run(vswhere_cmd, capture_output=True, check=True) # 3.7+ only cp = subprocess.run(vswhere_cmd, stdout=PIPE, stderr=PIPE, check=True) if cp.stdout: @@ -814,9 +815,9 @@ def find_batch_file(env, msvc_version, host_arch, target_arch): sdk_bat_file_path = os.path.join(pdir, sdk_bat_file) if os.path.exists(sdk_bat_file_path): debug('sdk_bat_file_path:%s', sdk_bat_file_path) - return (batfilename, arg, vcdir, sdk_bat_file_path) + return batfilename, arg, vcdir, sdk_bat_file_path - return (batfilename, arg, vcdir, None) + return batfilename, arg, vcdir, None __INSTALLED_VCS_RUN = None _VC_TOOLS_VERSION_FILE_PATH = ['Auxiliary', 'Build', 'Microsoft.VCToolsVersion.default.txt'] @@ -858,7 +859,7 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version): # 2017 and newer allowed multiple versions of the VC toolset to be # installed at the same time. This changes the layout. # Just get the default tool version for now - #TODO: support setting a specific minor VC version + # TODO: support setting a specific minor VC version default_toolset_file = os.path.join(vc_dir, _VC_TOOLS_VERSION_FILE) try: with open(default_toolset_file) as f: @@ -1220,17 +1221,17 @@ def get_use_script_use_settings(env): if use_script != _UNDEFINED: # use_script defined, use_settings ignored (not type checked) - return (use_script, None) + return use_script, None # undefined or None: use_settings ignored use_settings = env.get('MSVC_USE_SETTINGS', None) if use_settings is not None: # use script undefined, use_settings defined and not None (type checked) - return (False, use_settings) + return False, use_settings # use script undefined, use_settings undefined or None - return (True, None) + return True, None def msvc_setup_env(env): debug('called') diff --git a/SCons/Tool/MSCommon/vcTests.py b/SCons/Tool/MSCommon/vcTests.py index cd310ddf17..3e37def2e9 100644 --- a/SCons/Tool/MSCommon/vcTests.py +++ b/SCons/Tool/MSCommon/vcTests.py @@ -1,5 +1,6 @@ +# MIT License # -# __COPYRIGHT__ +# 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 @@ -19,10 +20,7 @@ # 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. -# -# from typing import Dict, Any -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path @@ -44,7 +42,8 @@ MSVCUnsupportedHostArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedHostArch MSVCUnsupportedTargetArch = SCons.Tool.MSCommon.vc.MSVCUnsupportedTargetArch -MS_TOOLS_VERSION='1.1.1' +MS_TOOLS_VERSION = '1.1.1' + class VswhereTestCase(unittest.TestCase): @staticmethod diff --git a/SCons/Tool/msvc.xml b/SCons/Tool/msvc.xml index c582bb4d65..c26c20da89 100644 --- a/SCons/Tool/msvc.xml +++ b/SCons/Tool/msvc.xml @@ -1,6 +1,28 @@