summaryrefslogtreecommitdiffstats
path: root/SCons/Tool
diff options
context:
space:
mode:
authorJoseph Brill <48932340+jcbrill@users.noreply.github.com>2022-06-16 15:41:17 (GMT)
committerJoseph Brill <48932340+jcbrill@users.noreply.github.com>2022-06-16 15:41:17 (GMT)
commit5187917d8a99b86e965ddeb676b0f8fe6c670318 (patch)
tree2419e29fcd5e9284cd4708418bcddc87946d179b /SCons/Tool
parent5b8d1c4ba03b422f7415bea638cb84c4b974a494 (diff)
downloadSCons-5187917d8a99b86e965ddeb676b0f8fe6c670318.zip
SCons-5187917d8a99b86e965ddeb676b0f8fe6c670318.tar.gz
SCons-5187917d8a99b86e965ddeb676b0f8fe6c670318.tar.bz2
Add SDK version support and validate all arguments.
Diffstat (limited to 'SCons/Tool')
-rw-r--r--SCons/Tool/MSCommon/vc.py1079
1 files changed, 731 insertions, 348 deletions
diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py
index 0c9fcee..c44c698 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,10 +1634,571 @@ def msvc_setup_env_once(env, tool=None):
" Requested tool(s) are: {}".format(req_tools)
_msvc_notfound_policy_handler(env, msg)
-class _MSVCScriptArguments:
+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 = _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
+
+ 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
+
+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:
+
+ sdk_map_cache = {}
+ sdk_cache = {}
+
+ @classmethod
+ def reset(cls):
+ cls.sdk_map_cache = {}
+ cls.sdk_cache = {}
+
+ @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, 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)
- # Force -vcvars_ver argument for default toolset
- MSVC_TOOLSET_DEFAULT_VCVARSVER = False
+ sdk_map[platform].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()
+
+ 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 _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 _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'),
+ }
+
+ 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):
+ sdk_map = cls._sdk_map(version_list)
+ sdk_list = sdk_map.get(platform, [])
+ return sdk_list
+
+def get_sdk_versions(MSVC_VERSION=None, MSVC_UWP_APP=False):
+
+ sdk_versions = []
+
+ if not MSVC_VERSION:
+ vcs = get_installed_vcs()
+ if not vcs:
+ 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:
+ 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)
+
+ 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<version>[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'(?:(?<!\S)|^)(?P<uwp>(?:uwp|store))(?:(?!\S)|$)',re.IGNORECASE)
+ re_vcvars_sdk = re.compile(r'(?:(?<!\S)|^)(?P<sdk>(?:[1-9][0-9]*[.]\S*))(?:(?!\S)|$)',re.IGNORECASE)
+ re_vcvars_toolset = re.compile(r'(?:(?<!\S)|^)(?P<toolset_arg>(?:[-]{1,2}|[/])vcvars_ver[=](?P<toolset>\S*))(?:(?!\S)|$)', re.IGNORECASE)
+ re_vcvars_spectre = re.compile(r'(?:(?<!\S)|^)(?P<spectre_arg>(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P<spectre>\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:
#
@@ -1681,7 +2254,7 @@ class _MSVCScriptArguments:
if not uwp_app:
return None
- if uwp_app not in (True, '1'):
+ if uwp_app not in _Const.BOOLEAN_KEYS[True]:
return None
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < cls.VS2015.vc_buildtools_def.vc_version_numeric:
@@ -1704,8 +2277,29 @@ class _MSVCScriptArguments:
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}$')
+ @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):
@@ -1721,27 +2315,23 @@ class _MSVCScriptArguments:
)
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
-
- if cls.re_sdk_version_10.match(sdk_version):
- debug('valid: sdk_version=%s', repr(sdk_version))
- return None
+ 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, is_uwp, arglist):
+ def _msvc_script_argument_sdk(cls, env, msvc, platform, arglist):
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)
+ 'MSVC_VERSION=%s, MSVC_SDK_VERSION=%s, platform=%s',
+ repr(msvc.version), repr(sdk_version), repr(platform)
)
if not sdk_version:
@@ -1751,6 +2341,14 @@ class _MSVCScriptArguments:
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)
+
# sdk folder may not exist
argpair = (cls.SortOrder.SDK, sdk_version)
arglist.append(argpair)
@@ -1758,6 +2356,53 @@ class _MSVCScriptArguments:
return sdk_version
@classmethod
+ def _msvc_script_default_sdk(cls, env, msvc, platform, 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)
+ 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)
+ )
+
+ 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:
@@ -1904,22 +2549,6 @@ class _MSVCScriptArguments:
return None
- # capture msvc version
- re_toolset_version = re.compile(r'^(?P<version>[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, msvc, toolset_version):
@@ -2047,6 +2676,31 @@ class _MSVCScriptArguments:
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']
@@ -2104,79 +2758,21 @@ class _MSVCScriptArguments:
return script_args
- re_vcvars_uwp = re.compile(r'(?:\s|^)(?P<uwp>(?:uwp|store))(?:\s|$)',re.IGNORECASE)
- re_vcvars_sdk = re.compile(r'(?:\s|^)(?P<sdk>(?:[1-9][0-9]*[.]\S*))(?:\s|$)',re.IGNORECASE)
- re_vcvars_spectre = re.compile(r'(?:\s|^)(?P<spectre_arg>(?:[-]{1,2}|[/])vcvars_spectre_libs[=](?P<spectre>\S*))(?:\s|$)',re.IGNORECASE)
- re_vcvars_toolset = re.compile(r'(?:\s|^)(?P<toolset_arg>(?:[-]{1,2}|[/])vcvars_ver[=](?P<toolset>\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):
+ def _user_script_argument_spectre(cls, env, spectre, user_argstr):
- m = cls.re_vcvars_toolset.search(user_argstr)
- if not m:
+ matches = [m for m in cls.re_vcvars_spectre.finditer(user_argstr)]
+ if not matches:
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 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
- 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))
@@ -2189,55 +2785,70 @@ class _MSVCScriptArguments:
@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 @@ class _MSVCScriptArguments:
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
-