Upgrade SCons to v4.8.0 [skip ci]
This commit is contained in:
parent
7a1663f0a2
commit
dc97085b31
1539 changed files with 3884 additions and 1577 deletions
10
scons/scons-configure-cache.py
vendored
10
scons/scons-configure-cache.py
vendored
|
@ -32,15 +32,15 @@ The files are split into directories named by the first few
|
|||
digits of the signature. The prefix length used for directory
|
||||
names can be changed by this script.
|
||||
"""
|
||||
__revision__ = "scripts/scons-configure-cache.py 265be6883fadbb5a545612265acc919595158366 Sun, 17 Mar 2024 17:33:54 -0700 bdbaddog"
|
||||
__revision__ = "scripts/scons-configure-cache.py 7c688f694c644b61342670ce92977bf4a396c0d4 Sun, 07 Jul 2024 16:52:07 -0700 bdbaddog"
|
||||
|
||||
__version__ = "4.7.0"
|
||||
__version__ = "4.8.0"
|
||||
|
||||
__build__ = "265be6883fadbb5a545612265acc919595158366"
|
||||
__build__ = "7c688f694c644b61342670ce92977bf4a396c0d4"
|
||||
|
||||
__buildsys__ = "M1Dog2021"
|
||||
|
||||
__date__ = "Sun, 17 Mar 2024 17:33:54 -0700"
|
||||
__date__ = "Sun, 07 Jul 2024 16:52:07 -0700"
|
||||
|
||||
__developer__ = "bdbaddog"
|
||||
|
||||
|
@ -51,7 +51,7 @@ import sys
|
|||
# python compatibility check
|
||||
if sys.version_info < (3, 6, 0):
|
||||
msg = "scons: *** SCons version %s does not run under Python version %s.\n\
|
||||
Python >= 3.5 is required.\n"
|
||||
Python >= 3.6.0 is required.\n"
|
||||
sys.stderr.write(msg % (__version__, sys.version.split()[0]))
|
||||
sys.exit(1)
|
||||
|
||||
|
|
|
@ -1,110 +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.
|
||||
|
||||
"""Variable type for enumeration Variables.
|
||||
|
||||
Enumeration variables allow selection of one from a specified set of values.
|
||||
|
||||
Usage example::
|
||||
|
||||
opts = Variables()
|
||||
opts.Add(
|
||||
EnumVariable(
|
||||
'debug',
|
||||
help='debug output and symbols',
|
||||
default='no',
|
||||
allowed_values=('yes', 'no', 'full'),
|
||||
map={},
|
||||
ignorecase=2,
|
||||
)
|
||||
)
|
||||
...
|
||||
if env['debug'] == 'full':
|
||||
...
|
||||
"""
|
||||
|
||||
from typing import Tuple, Callable
|
||||
|
||||
import SCons.Errors
|
||||
|
||||
__all__ = ['EnumVariable',]
|
||||
|
||||
|
||||
def _validator(key, val, env, vals) -> None:
|
||||
if val not in vals:
|
||||
raise SCons.Errors.UserError(
|
||||
'Invalid value for option %s: %s. Valid values are: %s' % (key, val, vals))
|
||||
|
||||
|
||||
def EnumVariable(key, help, default, allowed_values, map={}, ignorecase: int=0) -> Tuple[str, str, str, Callable, Callable]:
|
||||
"""Return a tuple describing an enumaration SCons Variable.
|
||||
|
||||
The input parameters describe an option with only certain values
|
||||
allowed. Returns A tuple including an appropriate converter and
|
||||
validator. The result is usable as input to :meth:`Add`.
|
||||
|
||||
*key* and *default* are passed directly on to :meth:`Add`.
|
||||
|
||||
*help* is the descriptive part of the help text,
|
||||
and will have the allowed values automatically appended.
|
||||
|
||||
*allowed_values* is a list of strings, which are the allowed values
|
||||
for this option.
|
||||
|
||||
The *map*-dictionary may be used for converting the input value
|
||||
into canonical values (e.g. for aliases).
|
||||
|
||||
The value of *ignorecase* defines the behaviour of the validator:
|
||||
|
||||
* 0: the validator/converter are case-sensitive.
|
||||
* 1: the validator/converter are case-insensitive.
|
||||
* 2: the validator/converter is case-insensitive and the
|
||||
converted value will always be lower-case.
|
||||
|
||||
The *validator* tests whether the value is in the list of allowed values.
|
||||
The *converter* converts input values according to the given
|
||||
*map*-dictionary (unmapped input values are returned unchanged).
|
||||
"""
|
||||
|
||||
help = '%s (%s)' % (help, '|'.join(allowed_values))
|
||||
# define validator
|
||||
if ignorecase:
|
||||
validator = lambda key, val, env: \
|
||||
_validator(key, val.lower(), env, allowed_values)
|
||||
else:
|
||||
validator = lambda key, val, env: \
|
||||
_validator(key, val, env, allowed_values)
|
||||
# define converter
|
||||
if ignorecase == 2:
|
||||
converter = lambda val: map.get(val.lower(), val).lower()
|
||||
elif ignorecase == 1:
|
||||
converter = lambda val: map.get(val.lower(), val)
|
||||
else:
|
||||
converter = lambda val: map.get(val, val)
|
||||
return (key, help, default, validator, converter)
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:4
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
|
@ -1,152 +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.
|
||||
|
||||
"""Variable type for list Variables.
|
||||
|
||||
A 'list' option may either be 'all', 'none' or a list of names
|
||||
separated by comma. After the option has been processed, the option
|
||||
value holds either the named list elements, all list elements or no
|
||||
list elements at all.
|
||||
|
||||
Usage example::
|
||||
|
||||
list_of_libs = Split('x11 gl qt ical')
|
||||
|
||||
opts = Variables()
|
||||
opts.Add(
|
||||
ListVariable(
|
||||
'shared',
|
||||
help='libraries to build as shared libraries',
|
||||
default='all',
|
||||
elems=list_of_libs,
|
||||
)
|
||||
)
|
||||
...
|
||||
for lib in list_of_libs:
|
||||
if lib in env['shared']:
|
||||
env.SharedObject(...)
|
||||
else:
|
||||
env.Object(...)
|
||||
"""
|
||||
|
||||
# Known Bug: This should behave like a Set-Type, but does not really,
|
||||
# since elements can occur twice.
|
||||
|
||||
import collections
|
||||
from typing import Tuple, Callable
|
||||
|
||||
import SCons.Util
|
||||
|
||||
__all__ = ['ListVariable',]
|
||||
|
||||
|
||||
class _ListVariable(collections.UserList):
|
||||
def __init__(self, initlist=None, allowedElems=None) -> None:
|
||||
if initlist is None:
|
||||
initlist = []
|
||||
if allowedElems is None:
|
||||
allowedElems = []
|
||||
super().__init__([_f for _f in initlist if _f])
|
||||
self.allowedElems = sorted(allowedElems)
|
||||
|
||||
def __cmp__(self, other):
|
||||
return NotImplemented
|
||||
|
||||
def __eq__(self, other):
|
||||
return NotImplemented
|
||||
|
||||
def __ge__(self, other):
|
||||
return NotImplemented
|
||||
|
||||
def __gt__(self, other):
|
||||
return NotImplemented
|
||||
|
||||
def __le__(self, other):
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other):
|
||||
return NotImplemented
|
||||
|
||||
def __str__(self) -> str:
|
||||
if not len(self):
|
||||
return 'none'
|
||||
self.data.sort()
|
||||
if self.data == self.allowedElems:
|
||||
return 'all'
|
||||
else:
|
||||
return ','.join(self)
|
||||
|
||||
def prepare_to_store(self):
|
||||
return self.__str__()
|
||||
|
||||
def _converter(val, allowedElems, mapdict) -> _ListVariable:
|
||||
""" """
|
||||
if val == 'none':
|
||||
val = []
|
||||
elif val == 'all':
|
||||
val = allowedElems
|
||||
else:
|
||||
val = [_f for _f in val.split(',') if _f]
|
||||
val = [mapdict.get(v, v) for v in val]
|
||||
notAllowed = [v for v in val if v not in allowedElems]
|
||||
if notAllowed:
|
||||
raise ValueError(
|
||||
"Invalid value(s) for option: %s" % ','.join(notAllowed)
|
||||
)
|
||||
return _ListVariable(val, allowedElems)
|
||||
|
||||
|
||||
# def _validator(key, val, env) -> None:
|
||||
# """ """
|
||||
# # TODO: write validator for pgk list
|
||||
# pass
|
||||
|
||||
|
||||
def ListVariable(key, help, default, names, map={}) -> Tuple[str, str, str, None, Callable]:
|
||||
"""Return a tuple describing a list SCons Variable.
|
||||
|
||||
The input parameters describe a 'list' option. Returns
|
||||
a tuple including the correct converter and validator.
|
||||
The result is usable for input to :meth:`Add`.
|
||||
|
||||
*help* will have text appended indicating the legal values
|
||||
(not including any extra names from *map*).
|
||||
|
||||
*map* can be used to map alternative names to the ones in *names* -
|
||||
that is, a form of alias.
|
||||
|
||||
A 'list' option may either be 'all', 'none' or a list of
|
||||
names (separated by commas).
|
||||
"""
|
||||
names_str = 'allowed names: %s' % ' '.join(names)
|
||||
if SCons.Util.is_List(default):
|
||||
default = ','.join(default)
|
||||
help = '\n '.join(
|
||||
(help, '(all|none|comma-separated list of names)', names_str))
|
||||
return (key, help, default, None, lambda val: _converter(val, names, map))
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:4
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
336
scons/scons-local-4.7.0/SCons/Variables/__init__.py
vendored
336
scons/scons-local-4.7.0/SCons/Variables/__init__.py
vendored
|
@ -1,336 +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.
|
||||
|
||||
"""Adds user-friendly customizable variables to an SCons build. """
|
||||
|
||||
import os.path
|
||||
import sys
|
||||
from functools import cmp_to_key
|
||||
|
||||
import SCons.Environment
|
||||
import SCons.Errors
|
||||
import SCons.Util
|
||||
import SCons.Warnings
|
||||
|
||||
from .BoolVariable import BoolVariable # okay
|
||||
from .EnumVariable import EnumVariable # okay
|
||||
from .ListVariable import ListVariable # naja
|
||||
from .PackageVariable import PackageVariable # naja
|
||||
from .PathVariable import PathVariable # okay
|
||||
|
||||
|
||||
class Variables:
|
||||
"""
|
||||
Holds all the options, updates the environment with the variables,
|
||||
and renders the help text.
|
||||
|
||||
If *is_global* is true, this is a singleton, create only once.
|
||||
|
||||
Args:
|
||||
files (optional): List of option configuration files to load
|
||||
(backward compatibility). If a single string is passed it is
|
||||
automatically placed in a file list (Default value = None)
|
||||
args (optional): dictionary to override values set from *files*.
|
||||
(Default value = None)
|
||||
is_global (optional): global instance? (Default value = True)
|
||||
|
||||
"""
|
||||
instance = None
|
||||
|
||||
def __init__(self, files=None, args=None, is_global: bool=True) -> None:
|
||||
if args is None:
|
||||
args = {}
|
||||
self.options = []
|
||||
self.args = args
|
||||
if not SCons.Util.is_List(files):
|
||||
if files:
|
||||
files = [files,]
|
||||
else:
|
||||
files = []
|
||||
self.files = files
|
||||
self.unknown = {}
|
||||
|
||||
# create the singleton instance
|
||||
if is_global:
|
||||
self = Variables.instance
|
||||
if not Variables.instance:
|
||||
Variables.instance=self
|
||||
|
||||
def _do_add(self, key, help: str="", default=None, validator=None, converter=None, **kwargs) -> None:
|
||||
|
||||
class Variable:
|
||||
pass
|
||||
|
||||
option = Variable()
|
||||
|
||||
# If we get a list or a tuple, we take the first element as the
|
||||
# option key and store the remaining in aliases.
|
||||
if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key):
|
||||
option.key = key[0]
|
||||
option.aliases = list(key[1:])
|
||||
else:
|
||||
option.key = key
|
||||
# TODO: normalize to not include key in aliases. Currently breaks tests.
|
||||
option.aliases = [key,]
|
||||
if not SCons.Environment.is_valid_construction_var(option.key):
|
||||
raise SCons.Errors.UserError("Illegal Variables key `%s'" % str(option.key))
|
||||
option.help = help
|
||||
option.default = default
|
||||
option.validator = validator
|
||||
option.converter = converter
|
||||
|
||||
self.options.append(option)
|
||||
|
||||
# options might be added after the 'unknown' dict has been set up,
|
||||
# so we remove the key and all its aliases from that dict
|
||||
for alias in option.aliases + [option.key,]:
|
||||
if alias in self.unknown:
|
||||
del self.unknown[alias]
|
||||
|
||||
def keys(self) -> list:
|
||||
"""Returns the keywords for the options."""
|
||||
return [o.key for o in self.options]
|
||||
|
||||
def Add(self, key, *args, **kwargs) -> None:
|
||||
r""" Adds an option.
|
||||
|
||||
Arguments:
|
||||
key: the name of the variable, or a 5-tuple (or list).
|
||||
If a tuple, and there are no additional arguments,
|
||||
the tuple is unpacked into the four named kwargs from below.
|
||||
If a tuple and there are additional arguments, the first word
|
||||
of the tuple is taken as the key, and the remainder as aliases.
|
||||
*args: optional positional arguments, corresponding to the four
|
||||
named kwargs below.
|
||||
|
||||
Keyword Args:
|
||||
help: help text for the options (Default value = "")
|
||||
default: default value for option (Default value = None)
|
||||
validator: function called to validate the option's value
|
||||
(Default value = None)
|
||||
converter: function to be called to convert the option's
|
||||
value before putting it in the environment. (Default value = None)
|
||||
**kwargs: arbitrary keyword arguments used by the variable itself.
|
||||
|
||||
"""
|
||||
if SCons.Util.is_List(key) or SCons.Util.is_Tuple(key):
|
||||
if not (len(args) or len(kwargs)):
|
||||
return self._do_add(*key)
|
||||
|
||||
return self._do_add(key, *args, **kwargs)
|
||||
|
||||
def AddVariables(self, *optlist) -> None:
|
||||
""" Adds a list of options.
|
||||
|
||||
Each list element is a tuple/list of arguments to be passed on
|
||||
to the underlying method for adding options.
|
||||
|
||||
Example::
|
||||
|
||||
opt.AddVariables(
|
||||
('debug', '', 0),
|
||||
('CC', 'The C compiler'),
|
||||
('VALIDATE', 'An option for testing validation', 'notset', validator, None),
|
||||
)
|
||||
|
||||
"""
|
||||
|
||||
for o in optlist:
|
||||
self._do_add(*o)
|
||||
|
||||
def Update(self, env, args=None) -> None:
|
||||
""" Updates an environment with the option variables.
|
||||
|
||||
Args:
|
||||
env: the environment to update.
|
||||
args (optional): a dictionary of keys and values to update
|
||||
in *env*. If omitted, uses the variables from the commandline.
|
||||
"""
|
||||
|
||||
values = {}
|
||||
|
||||
# first set the defaults:
|
||||
for option in self.options:
|
||||
if option.default is not None:
|
||||
values[option.key] = option.default
|
||||
|
||||
# next set the value specified in the options file
|
||||
for filename in self.files:
|
||||
if os.path.exists(filename):
|
||||
dir = os.path.split(os.path.abspath(filename))[0]
|
||||
if dir:
|
||||
sys.path.insert(0, dir)
|
||||
try:
|
||||
values['__name__'] = filename
|
||||
with open(filename) as f:
|
||||
contents = f.read()
|
||||
exec(contents, {}, values)
|
||||
finally:
|
||||
if dir:
|
||||
del sys.path[0]
|
||||
del values['__name__']
|
||||
|
||||
# set the values specified on the command line
|
||||
if args is None:
|
||||
args = self.args
|
||||
|
||||
for arg, value in args.items():
|
||||
added = False
|
||||
for option in self.options:
|
||||
if arg in option.aliases + [option.key,]:
|
||||
values[option.key] = value
|
||||
added = True
|
||||
if not added:
|
||||
self.unknown[arg] = value
|
||||
|
||||
# put the variables in the environment:
|
||||
# (don't copy over variables that are not declared as options)
|
||||
for option in self.options:
|
||||
try:
|
||||
env[option.key] = values[option.key]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# apply converters
|
||||
for option in self.options:
|
||||
if option.converter and option.key in values:
|
||||
value = env.subst('${%s}'%option.key)
|
||||
try:
|
||||
try:
|
||||
env[option.key] = option.converter(value)
|
||||
except TypeError:
|
||||
env[option.key] = option.converter(value, env)
|
||||
except ValueError as x:
|
||||
raise SCons.Errors.UserError('Error converting option: %s\n%s'%(option.key, x))
|
||||
|
||||
|
||||
# apply validators
|
||||
for option in self.options:
|
||||
if option.validator and option.key in values:
|
||||
option.validator(option.key, env.subst('${%s}'%option.key), env)
|
||||
|
||||
def UnknownVariables(self) -> dict:
|
||||
""" Returns unknown variables.
|
||||
|
||||
Identifies options that were not known, declared options in this object.
|
||||
"""
|
||||
return self.unknown
|
||||
|
||||
def Save(self, filename, env) -> None:
|
||||
""" Save the options to a file.
|
||||
|
||||
Saves all the options which have non-default settings
|
||||
to the given file as Python expressions. This file can
|
||||
then be used to load the options for a subsequent run.
|
||||
This can be used to create an option cache file.
|
||||
|
||||
Args:
|
||||
filename: Name of the file to save into
|
||||
env: the environment get the option values from
|
||||
"""
|
||||
|
||||
# Create the file and write out the header
|
||||
try:
|
||||
with open(filename, 'w') as fh:
|
||||
# Make an assignment in the file for each option
|
||||
# within the environment that was assigned a value
|
||||
# other than the default. We don't want to save the
|
||||
# ones set to default: in case the SConscript settings
|
||||
# change you would then pick up old defaults.
|
||||
for option in self.options:
|
||||
try:
|
||||
value = env[option.key]
|
||||
try:
|
||||
prepare = value.prepare_to_store
|
||||
except AttributeError:
|
||||
try:
|
||||
eval(repr(value))
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except:
|
||||
# Convert stuff that has a repr() that
|
||||
# cannot be evaluated into a string
|
||||
value = SCons.Util.to_String(value)
|
||||
else:
|
||||
value = prepare()
|
||||
|
||||
defaultVal = env.subst(SCons.Util.to_String(option.default))
|
||||
if option.converter:
|
||||
try:
|
||||
defaultVal = option.converter(defaultVal)
|
||||
except TypeError:
|
||||
defaultVal = option.converter(defaultVal, env)
|
||||
|
||||
if str(env.subst('${%s}' % option.key)) != str(defaultVal):
|
||||
fh.write('%s = %s\n' % (option.key, repr(value)))
|
||||
except KeyError:
|
||||
pass
|
||||
except OSError as x:
|
||||
raise SCons.Errors.UserError('Error writing options to file: %s\n%s' % (filename, x))
|
||||
|
||||
def GenerateHelpText(self, env, sort=None) -> str:
|
||||
""" Generates the help text for the options.
|
||||
|
||||
Args:
|
||||
env: an environment that is used to get the current values
|
||||
of the options.
|
||||
sort: Either a comparison function used for sorting
|
||||
(must take two arguments and return -1, 0 or 1)
|
||||
or a boolean to indicate if it should be sorted.
|
||||
"""
|
||||
|
||||
if callable(sort):
|
||||
options = sorted(self.options, key=cmp_to_key(lambda x, y: sort(x.key, y.key)))
|
||||
elif sort is True:
|
||||
options = sorted(self.options, key=lambda x: x.key)
|
||||
else:
|
||||
options = self.options
|
||||
|
||||
def format_opt(opt, self=self, env=env) -> str:
|
||||
if opt.key in env:
|
||||
actual = env.subst('${%s}' % opt.key)
|
||||
else:
|
||||
actual = None
|
||||
return self.FormatVariableHelpText(env, opt.key, opt.help, opt.default, actual, opt.aliases)
|
||||
|
||||
lines = [_f for _f in map(format_opt, options) if _f]
|
||||
return ''.join(lines)
|
||||
|
||||
fmt = '\n%s: %s\n default: %s\n actual: %s\n'
|
||||
aliasfmt = '\n%s: %s\n default: %s\n actual: %s\n aliases: %s\n'
|
||||
|
||||
def FormatVariableHelpText(self, env, key, help, default, actual, aliases=None) -> str:
|
||||
if aliases is None:
|
||||
aliases = []
|
||||
# Don't display the key name itself as an alias.
|
||||
aliases = [a for a in aliases if a != key]
|
||||
if aliases:
|
||||
return self.aliasfmt % (key, help, default, actual, aliases)
|
||||
else:
|
||||
return self.fmt % (key, help, default, actual)
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:4
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=4 shiftwidth=4:
|
|
@ -892,10 +892,10 @@ def scons_subproc_run(scons_env, *args, **kwargs) -> subprocess.CompletedProcess
|
|||
kwargs['check'] = check
|
||||
|
||||
# TODO: Python version-compat stuff: remap/remove too-new args if needed
|
||||
if 'text' in kwargs and sys.version_info[:3] < (3, 7):
|
||||
if 'text' in kwargs and sys.version_info < (3, 7):
|
||||
kwargs['universal_newlines'] = kwargs.pop('text')
|
||||
|
||||
if 'capture_output' in kwargs and sys.version_info[:3] < (3, 7):
|
||||
if 'capture_output' in kwargs and sys.version_info < (3, 7):
|
||||
capture_output = kwargs.pop('capture_output')
|
||||
if capture_output:
|
||||
kwargs['stdout'] = kwargs['stderr'] = PIPE
|
||||
|
@ -1454,16 +1454,6 @@ class FunctionAction(_ActionAction):
|
|||
except TypeError:
|
||||
result.command=self.strfunction(target, source, env)
|
||||
|
||||
# FIXME: This maintains backward compatibility with respect to
|
||||
# which type of exceptions were returned by raising an
|
||||
# exception and which ones were returned by value. It would
|
||||
# probably be best to always return them by value here, but
|
||||
# some codes do not check the return value of Actions and I do
|
||||
# not have the time to modify them at this point.
|
||||
if (exc_info[1] and
|
||||
not isinstance(exc_info[1], EnvironmentError)):
|
||||
raise result
|
||||
|
||||
return result
|
||||
finally:
|
||||
# Break the cycle between the traceback object and this
|
|
@ -387,7 +387,7 @@ class BuilderBase:
|
|||
target_scanner = None,
|
||||
source_scanner = None,
|
||||
emitter = None,
|
||||
multi: int = 0,
|
||||
multi: bool = False,
|
||||
env = None,
|
||||
single_source: bool = False,
|
||||
name = None,
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
"""Code for debugging SCons internal things.
|
||||
|
||||
Shouldn't be needed by most users. Quick shortcuts:
|
||||
Shouldn't be needed by most users. Quick shortcuts::
|
||||
|
||||
from SCons.Debug import caller_trace
|
||||
caller_trace()
|
|
@ -291,7 +291,15 @@ def copy_func(dest, src, symlinks: bool=True) -> int:
|
|||
|
||||
elif os.path.islink(src):
|
||||
if symlinks:
|
||||
try:
|
||||
os.symlink(os.readlink(src), dest)
|
||||
except FileExistsError:
|
||||
raise SCons.Errors.BuildError(
|
||||
errstr=(
|
||||
f'Error: Copy() called to create symlink at "{dest}",'
|
||||
' but a file already exists at that location.'
|
||||
)
|
||||
)
|
||||
return 0
|
||||
|
||||
return copy_func(dest, os.path.realpath(src))
|
|
@ -35,9 +35,9 @@ import os
|
|||
import sys
|
||||
import re
|
||||
import shlex
|
||||
from collections import UserDict, deque
|
||||
from collections import UserDict, UserList, deque
|
||||
from subprocess import PIPE, DEVNULL
|
||||
from typing import Optional
|
||||
from typing import Callable, Collection, Optional, Sequence, Union
|
||||
|
||||
import SCons.Action
|
||||
import SCons.Builder
|
||||
|
@ -510,13 +510,6 @@ class BuilderDict(UserDict):
|
|||
self.__setitem__(i, v)
|
||||
|
||||
|
||||
_is_valid_var = re.compile(r'[_a-zA-Z]\w*$')
|
||||
|
||||
def is_valid_construction_var(varstr) -> bool:
|
||||
"""Return True if *varstr* is a legitimate construction variable."""
|
||||
return _is_valid_var.match(varstr)
|
||||
|
||||
|
||||
class SubstitutionEnvironment:
|
||||
"""Base class for different flavors of construction environments.
|
||||
|
||||
|
@ -587,26 +580,20 @@ class SubstitutionEnvironment:
|
|||
return self._dict[key]
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
# This is heavily used. This implementation is the best we have
|
||||
# according to the timings in bench/env.__setitem__.py.
|
||||
#
|
||||
# The "key in self._special_set_keys" test here seems to perform
|
||||
# pretty well for the number of keys we have. A hard-coded
|
||||
# list worked a little better in Python 2.5, but that has the
|
||||
# disadvantage of maybe getting out of sync if we ever add more
|
||||
# variable names.
|
||||
# So right now it seems like a good trade-off, but feel free to
|
||||
# revisit this with bench/env.__setitem__.py as needed (and
|
||||
# as newer versions of Python come out).
|
||||
if key in self._special_set_keys:
|
||||
self._special_set[key](self, key, value)
|
||||
else:
|
||||
# If we already have the entry, then it's obviously a valid
|
||||
# key and we don't need to check. If we do check, using a
|
||||
# global, pre-compiled regular expression directly is more
|
||||
# efficient than calling another function or a method.
|
||||
if key not in self._dict and not _is_valid_var.match(key):
|
||||
raise UserError("Illegal construction variable `%s'" % key)
|
||||
# Performance: since this is heavily used, try to avoid checking
|
||||
# if the variable is valid unless necessary. bench/__setitem__.py
|
||||
# times a bunch of different approaches. Based the most recent
|
||||
# run, against Python 3.6-3.13(beta), the best we can do across
|
||||
# different combinations of actions is to use a membership test
|
||||
# to see if we already have the variable, if so it must already
|
||||
# have been checked, so skip; if we do check, "isidentifier()"
|
||||
# (new in Python 3 so wasn't in benchmark until recently)
|
||||
# on the key is the best.
|
||||
if key not in self._dict and not key.isidentifier():
|
||||
raise UserError(f"Illegal construction variable {key!r}")
|
||||
self._dict[key] = value
|
||||
|
||||
def get(self, key, default=None):
|
||||
|
@ -881,11 +868,11 @@ class SubstitutionEnvironment:
|
|||
'RPATH' : [],
|
||||
}
|
||||
|
||||
def do_parse(arg) -> None:
|
||||
# if arg is a sequence, recurse with each element
|
||||
def do_parse(arg: Union[str, Sequence]) -> None:
|
||||
if not arg:
|
||||
return
|
||||
|
||||
# if arg is a sequence, recurse with each element
|
||||
if not is_String(arg):
|
||||
for t in arg: do_parse(t)
|
||||
return
|
||||
|
@ -902,7 +889,7 @@ class SubstitutionEnvironment:
|
|||
else:
|
||||
mapping['CPPDEFINES'].append([t[0], '='.join(t[1:])])
|
||||
|
||||
# Loop through the flags and add them to the appropriate option.
|
||||
# Loop through the flags and add them to the appropriate variable.
|
||||
# This tries to strike a balance between checking for all possible
|
||||
# flags and keeping the logic to a finite size, so it doesn't
|
||||
# check for some that don't occur often. It particular, if the
|
||||
|
@ -926,6 +913,8 @@ class SubstitutionEnvironment:
|
|||
append_next_arg_to = None # for multi-word args
|
||||
for arg in params:
|
||||
if append_next_arg_to:
|
||||
# these are the second pass for options where the
|
||||
# option-argument follows as a second word.
|
||||
if append_next_arg_to == 'CPPDEFINES':
|
||||
append_define(arg)
|
||||
elif append_next_arg_to == '-include':
|
||||
|
@ -1022,6 +1011,8 @@ class SubstitutionEnvironment:
|
|||
else:
|
||||
key = 'CFLAGS'
|
||||
mapping[key].append(arg)
|
||||
elif arg.startswith('-stdlib='):
|
||||
mapping['CXXFLAGS'].append(arg)
|
||||
elif arg[0] == '+':
|
||||
mapping['CCFLAGS'].append(arg)
|
||||
mapping['LINKFLAGS'].append(arg)
|
||||
|
@ -1250,9 +1241,11 @@ class Base(SubstitutionEnvironment):
|
|||
SCons.Tool.Initializers(self)
|
||||
|
||||
if tools is None:
|
||||
tools = self._dict.get('TOOLS', None)
|
||||
if tools is None:
|
||||
tools = ['default']
|
||||
tools = self._dict.get('TOOLS', ['default'])
|
||||
else:
|
||||
# for a new env, if we didn't use TOOLS, make sure it starts empty
|
||||
# so it only shows tools actually initialized.
|
||||
self._dict['TOOLS'] = []
|
||||
apply_tools(self, tools, toolpath)
|
||||
|
||||
# Now restore the passed-in and customized variables
|
||||
|
@ -1562,16 +1555,28 @@ class Base(SubstitutionEnvironment):
|
|||
self._dict[key] = dk + val
|
||||
self.scanner_map_delete(kw)
|
||||
|
||||
def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
|
||||
def Clone(self, tools=[], toolpath=None, variables=None, parse_flags=None, **kw):
|
||||
"""Return a copy of a construction Environment.
|
||||
|
||||
The copy is like a Python "deep copy"--that is, independent
|
||||
copies are made recursively of each objects--except that
|
||||
a reference is copied when an object is not deep-copyable
|
||||
(like a function). There are no references to any mutable
|
||||
objects in the original Environment.
|
||||
"""
|
||||
The copy is like a Python "deep copy": independent copies are made
|
||||
recursively of each object, except that a reference is copied when
|
||||
an object is not deep-copyable (like a function). There are no
|
||||
references to any mutable objects in the original environment.
|
||||
|
||||
Unrecognized keyword arguments are taken as construction variable
|
||||
assignments.
|
||||
|
||||
Arguments:
|
||||
tools: list of tools to initialize.
|
||||
toolpath: list of paths to search for tools.
|
||||
variables: a :class:`~SCons.Variables.Variables` object to
|
||||
use to populate construction variables from command-line
|
||||
variables.
|
||||
parse_flags: option strings to parse into construction variables.
|
||||
|
||||
.. versionadded:: 4.8.0
|
||||
The optional *variables* parameter was added.
|
||||
"""
|
||||
builders = self._dict.get('BUILDERS', {})
|
||||
|
||||
clone = copy.copy(self)
|
||||
|
@ -1597,6 +1602,8 @@ class Base(SubstitutionEnvironment):
|
|||
for key, value in kw.items():
|
||||
new[key] = SCons.Subst.scons_subst_once(value, self, key)
|
||||
clone.Replace(**new)
|
||||
if variables:
|
||||
variables.Update(clone)
|
||||
|
||||
apply_tools(clone, tools, toolpath)
|
||||
|
||||
|
@ -1687,17 +1694,23 @@ class Base(SubstitutionEnvironment):
|
|||
return dlist
|
||||
|
||||
|
||||
def Dump(self, key=None, format: str='pretty'):
|
||||
""" Return construction variables serialized to a string.
|
||||
def Dump(self, key: Optional[str] = None, format: str = 'pretty') -> str:
|
||||
""" Returns a dump of serialized construction variables.
|
||||
|
||||
The display formats are intended for humaan readers when
|
||||
debugging - none of the supported formats produce a result that
|
||||
SCons itself can directly make use of. Objects that cannot
|
||||
directly be represented get a placeholder like
|
||||
``<function foo at 0x123456>`` or ``<<non-serializable: function>>``.
|
||||
|
||||
Args:
|
||||
key (optional): if None, format the whole dict of variables.
|
||||
Else format the value of `key` (Default value = None)
|
||||
format (str, optional): specify the format to serialize to.
|
||||
`"pretty"` generates a pretty-printed string,
|
||||
`"json"` a JSON-formatted string.
|
||||
(Default value = `"pretty"`)
|
||||
key: if ``None``, format the whole dict of variables,
|
||||
else format just the value of *key*.
|
||||
format: specify the format to serialize to. ``"pretty"`` generates
|
||||
a pretty-printed string, ``"json"`` a JSON-formatted string.
|
||||
|
||||
Raises:
|
||||
ValueError: *format* is not a recognized serialization format.
|
||||
"""
|
||||
if key:
|
||||
cvars = self.Dictionary(key)
|
||||
|
@ -1707,9 +1720,9 @@ class Base(SubstitutionEnvironment):
|
|||
fmt = format.lower()
|
||||
|
||||
if fmt == 'pretty':
|
||||
import pprint
|
||||
pp = pprint.PrettyPrinter(indent=2)
|
||||
import pprint # pylint: disable=import-outside-toplevel
|
||||
|
||||
pp = pprint.PrettyPrinter(indent=2)
|
||||
# TODO: pprint doesn't do a nice job on path-style values
|
||||
# if the paths contain spaces (i.e. Windows), because the
|
||||
# algorithm tries to break lines on spaces, while breaking
|
||||
|
@ -1718,26 +1731,33 @@ class Base(SubstitutionEnvironment):
|
|||
return pp.pformat(cvars)
|
||||
|
||||
elif fmt == 'json':
|
||||
import json
|
||||
def non_serializable(obj):
|
||||
return '<<non-serializable: %s>>' % type(obj).__qualname__
|
||||
return json.dumps(cvars, indent=4, default=non_serializable)
|
||||
import json # pylint: disable=import-outside-toplevel
|
||||
|
||||
class DumpEncoder(json.JSONEncoder):
|
||||
"""SCons special json Dump formatter."""
|
||||
def default(self, obj):
|
||||
if isinstance(obj, (UserList, UserDict)):
|
||||
return obj.data
|
||||
return f'<<non-serializable: {type(obj).__qualname__}>>'
|
||||
|
||||
return json.dumps(cvars, indent=4, cls=DumpEncoder, sort_keys=True)
|
||||
else:
|
||||
raise ValueError("Unsupported serialization format: %s." % fmt)
|
||||
|
||||
|
||||
def FindIxes(self, paths, prefix, suffix):
|
||||
"""Search a list of paths for something that matches the prefix and suffix.
|
||||
def FindIxes(self, paths: Sequence[str], prefix: str, suffix: str) -> Optional[str]:
|
||||
"""Search *paths* for a path that has *prefix* and *suffix*.
|
||||
|
||||
Args:
|
||||
Returns on first match.
|
||||
|
||||
Arguments:
|
||||
paths: the list of paths or nodes.
|
||||
prefix: construction variable for the prefix.
|
||||
suffix: construction variable for the suffix.
|
||||
|
||||
Returns: the matched path or None
|
||||
|
||||
Returns:
|
||||
The matched path or ``None``
|
||||
"""
|
||||
|
||||
suffix = self.subst('$'+suffix)
|
||||
prefix = self.subst('$'+prefix)
|
||||
|
||||
|
@ -2001,11 +2021,20 @@ class Base(SubstitutionEnvironment):
|
|||
def _find_toolpath_dir(self, tp):
|
||||
return self.fs.Dir(self.subst(tp)).srcnode().get_abspath()
|
||||
|
||||
def Tool(self, tool, toolpath=None, **kwargs) -> SCons.Tool.Tool:
|
||||
def Tool(
|
||||
self, tool: Union[str, Callable], toolpath: Optional[Collection[str]] = None, **kwargs
|
||||
) -> Callable:
|
||||
"""Find and run tool module *tool*.
|
||||
|
||||
*tool* is generally a string, but can also be a callable object,
|
||||
in which case it is just called, without any of the setup.
|
||||
The skipped setup includes storing *kwargs* into the created
|
||||
:class:`~SCons.Tool.Tool` instance, which is extracted and used
|
||||
when the instance is called, so in the skip case, the called
|
||||
object will not get the *kwargs*.
|
||||
|
||||
.. versionchanged:: 4.2
|
||||
returns the tool module rather than ``None``.
|
||||
returns the tool object rather than ``None``.
|
||||
"""
|
||||
if is_String(tool):
|
||||
tool = self.subst(tool)
|
||||
|
@ -2180,52 +2209,44 @@ class Base(SubstitutionEnvironment):
|
|||
return SCons.SConf.SConf(*nargs, **nkw)
|
||||
|
||||
def Command(self, target, source, action, **kw):
|
||||
"""Builds the supplied target files from the supplied
|
||||
source files using the supplied action. Action may
|
||||
be any type that the Builder constructor will accept
|
||||
for an action."""
|
||||
"""Set up a one-off build command.
|
||||
|
||||
Builds *target* from *source* using *action*, which may be
|
||||
be any type that the Builder factory will accept for an action.
|
||||
Generates an anonymous builder and calls it, to add the details
|
||||
to the build graph. The builder is not named, added to ``BUILDERS``,
|
||||
or otherwise saved.
|
||||
|
||||
Recognizes the :func:`~SCons.Builder.Builder` keywords
|
||||
``source_scanner``, ``target_scanner``, ``source_factory`` and
|
||||
``target_factory``. All other arguments from *kw* are passed on
|
||||
to the builder when it is called.
|
||||
"""
|
||||
# Build a kwarg dict for the builder construction
|
||||
bkw = {
|
||||
'action': action,
|
||||
'target_factory': self.fs.Entry,
|
||||
'source_factory': self.fs.Entry,
|
||||
}
|
||||
# source scanner
|
||||
try:
|
||||
bkw['source_scanner'] = kw['source_scanner']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
del kw['source_scanner']
|
||||
|
||||
# target scanner
|
||||
# Recognize these kwargs for the builder construction and take
|
||||
# them out of the args for the subsequent builder call.
|
||||
for arg in [
|
||||
'source_scanner',
|
||||
'target_scanner',
|
||||
'source_factory',
|
||||
'target_factory',
|
||||
]:
|
||||
try:
|
||||
bkw['target_scanner'] = kw['target_scanner']
|
||||
bkw[arg] = kw.pop(arg)
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
del kw['target_scanner']
|
||||
|
||||
# source factory
|
||||
try:
|
||||
bkw['source_factory'] = kw['source_factory']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
del kw['source_factory']
|
||||
|
||||
# target factory
|
||||
try:
|
||||
bkw['target_factory'] = kw['target_factory']
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
del kw['target_factory']
|
||||
|
||||
bld = SCons.Builder.Builder(**bkw)
|
||||
return bld(self, target, source, **kw)
|
||||
|
||||
def Depends(self, target, dependency):
|
||||
"""Explicity specify that 'target's depend on 'dependency'."""
|
||||
"""Explicity specify that *target* depends on *dependency*."""
|
||||
tlist = self.arg2nodes(target, self.fs.Entry)
|
||||
dlist = self.arg2nodes(dependency, self.fs.Entry)
|
||||
for t in tlist:
|
||||
|
@ -2253,7 +2274,7 @@ class Base(SubstitutionEnvironment):
|
|||
return self.fs.PyPackageDir(s)
|
||||
|
||||
def NoClean(self, *targets):
|
||||
"""Tags a target so that it will not be cleaned by -c"""
|
||||
"""Tag target(s) so that it will not be cleaned by -c."""
|
||||
tlist = []
|
||||
for t in targets:
|
||||
tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
||||
|
@ -2262,7 +2283,7 @@ class Base(SubstitutionEnvironment):
|
|||
return tlist
|
||||
|
||||
def NoCache(self, *targets):
|
||||
"""Tags a target so that it will not be cached"""
|
||||
"""Tag target(s) so that it will not be cached."""
|
||||
tlist = []
|
||||
for t in targets:
|
||||
tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
||||
|
@ -2551,8 +2572,12 @@ class OverrideEnvironment(Base):
|
|||
return self.__dict__['__subject'].__getitem__(key)
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if not is_valid_construction_var(key):
|
||||
raise UserError("Illegal construction variable `%s'" % key)
|
||||
# This doesn't have the same performance equation as a "real"
|
||||
# environment: in an override you're basically just writing
|
||||
# new stuff; it's not a common case to be changing values already
|
||||
# set in the override dict, so don't spend time checking for existance.
|
||||
if not key.isidentifier():
|
||||
raise UserError(f"Illegal construction variable {key!r}")
|
||||
self.__dict__['overrides'][key] = value
|
||||
|
||||
def __delitem__(self, key):
|
|
@ -177,8 +177,12 @@ def convert_to_BuildError(status, exc_info=None):
|
|||
# (for example, failure to create the directory in which the
|
||||
# target file will appear).
|
||||
filename = getattr(status, 'filename', None)
|
||||
strerror = getattr(status, 'strerror', str(status))
|
||||
errno = getattr(status, 'errno', 2)
|
||||
strerror = getattr(status, 'strerror', None)
|
||||
if strerror is None:
|
||||
strerror = str(status)
|
||||
errno = getattr(status, 'errno', None)
|
||||
if errno is None:
|
||||
errno = 2
|
||||
|
||||
buildError = BuildError(
|
||||
errstr=strerror,
|
|
@ -57,38 +57,6 @@ class AliasNodeInfo(SCons.Node.NodeInfoBase):
|
|||
def str_to_node(self, s):
|
||||
return default_ans.Alias(s)
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
Return all fields that shall be pickled. Walk the slots in the class
|
||||
hierarchy and add those to the state dictionary. If a '__dict__' slot is
|
||||
available, copy all entries to the dictionary. Also include the version
|
||||
id, which is fixed for all instances of a class.
|
||||
"""
|
||||
state = getattr(self, '__dict__', {}).copy()
|
||||
for obj in type(self).mro():
|
||||
for name in getattr(obj,'__slots__',()):
|
||||
if hasattr(self, name):
|
||||
state[name] = getattr(self, name)
|
||||
|
||||
state['_version_id'] = self.current_version_id
|
||||
try:
|
||||
del state['__weakref__']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return state
|
||||
|
||||
def __setstate__(self, state) -> None:
|
||||
"""
|
||||
Restore the attributes from a pickled state.
|
||||
"""
|
||||
# TODO check or discard version
|
||||
del state['_version_id']
|
||||
for key, value in state.items():
|
||||
if key not in ('__weakref__',):
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
class AliasBuildInfo(SCons.Node.BuildInfoBase):
|
||||
__slots__ = ()
|
||||
current_version_id = 2
|
|
@ -120,30 +120,31 @@ def save_strings(val) -> None:
|
|||
global Save_Strings
|
||||
Save_Strings = val
|
||||
|
||||
#
|
||||
# Avoid unnecessary function calls by recording a Boolean value that
|
||||
# tells us whether or not os.path.splitdrive() actually does anything
|
||||
# on this system, and therefore whether we need to bother calling it
|
||||
# when looking up path names in various methods below.
|
||||
#
|
||||
|
||||
do_splitdrive = None
|
||||
_my_splitdrive = None
|
||||
|
||||
def initialize_do_splitdrive() -> None:
|
||||
global do_splitdrive
|
||||
global has_unc
|
||||
drive, path = os.path.splitdrive('X:/foo')
|
||||
# splitunc is removed from python 3.7 and newer
|
||||
# so we can also just test if splitdrive works with UNC
|
||||
has_unc = (hasattr(os.path, 'splitunc')
|
||||
or os.path.splitdrive(r'\\split\drive\test')[0] == r'\\split\drive')
|
||||
"""Set up splitdrive usage.
|
||||
|
||||
do_splitdrive = not not drive or has_unc
|
||||
Avoid unnecessary function calls by recording a flag that tells us whether
|
||||
or not :func:`os.path.splitdrive` actually does anything on this system,
|
||||
and therefore whether we need to bother calling it when looking up path
|
||||
names in various methods below.
|
||||
|
||||
global _my_splitdrive
|
||||
if has_unc:
|
||||
def splitdrive(p):
|
||||
If :data:`do_splitdrive` is True, :func:`_my_splitdrive` will be a real
|
||||
function which we can call. As all supported Python versions' ntpath module
|
||||
now handle UNC paths correctly, we no longer special-case that.
|
||||
|
||||
Deferring the setup of ``_my_splitdrive`` also lets unit tests do
|
||||
their thing and test UNC path handling on a POSIX host.
|
||||
"""
|
||||
global do_splitdrive, _my_splitdrive
|
||||
|
||||
do_splitdrive = bool(os.path.splitdrive('X:/foo')[0])
|
||||
|
||||
if do_splitdrive:
|
||||
def _my_splitdrive(p):
|
||||
if p[1:2] == ':':
|
||||
return p[:2], p[2:]
|
||||
if p[0:2] == '//':
|
||||
|
@ -151,12 +152,9 @@ def initialize_do_splitdrive() -> None:
|
|||
# because UNC paths are always absolute.
|
||||
return '//', p[1:]
|
||||
return '', p
|
||||
else:
|
||||
def splitdrive(p):
|
||||
if p[1:2] == ':':
|
||||
return p[:2], p[2:]
|
||||
return '', p
|
||||
_my_splitdrive = splitdrive
|
||||
# TODO: the os routine should work and be better debugged than ours,
|
||||
# but unit test test_unc_path fails on POSIX platforms. Resolve someday.
|
||||
# _my_splitdrive = os.path.splitdrive
|
||||
|
||||
# Keep some commonly used values in global variables to skip to
|
||||
# module look-up costs.
|
||||
|
@ -328,7 +326,11 @@ LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
|
|||
|
||||
def UnlinkFunc(target, source, env) -> int:
|
||||
t = target[0]
|
||||
t.fs.unlink(t.get_abspath())
|
||||
file = t.get_abspath()
|
||||
try:
|
||||
t.fs.unlink(file)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
return 0
|
||||
|
||||
Unlink = SCons.Action.Action(UnlinkFunc, None)
|
||||
|
@ -1238,7 +1240,10 @@ class FS(LocalFS):
|
|||
self.pathTop = os.getcwd()
|
||||
else:
|
||||
self.pathTop = path
|
||||
if do_splitdrive:
|
||||
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
|
||||
else:
|
||||
self.defaultDrive = ""
|
||||
|
||||
self.Top = self.Dir(self.pathTop)
|
||||
self.Top._path = '.'
|
||||
|
@ -1554,11 +1559,15 @@ class DirNodeInfo(SCons.Node.NodeInfoBase):
|
|||
def str_to_node(self, s):
|
||||
top = self.fs.Top
|
||||
root = top.root
|
||||
# Python 3.13/Win changed isabs() - after you split C:/foo/bar,
|
||||
# the path part is no longer considerd absolute. Save the passed
|
||||
# path for the isabs check so we can get the right answer.
|
||||
path = s
|
||||
if do_splitdrive:
|
||||
drive, s = _my_splitdrive(s)
|
||||
if drive:
|
||||
root = self.fs.get_root(drive)
|
||||
if not os.path.isabs(s):
|
||||
if not os.path.isabs(path):
|
||||
s = top.get_labspath() + '/' + s
|
||||
return root._lookup_abs(s, Entry)
|
||||
|
||||
|
@ -2380,7 +2389,7 @@ class RootDir(Dir):
|
|||
# The // entry is necessary because os.path.normpath()
|
||||
# preserves double slashes at the beginning of a path on Posix
|
||||
# platforms.
|
||||
if not has_unc:
|
||||
if not do_splitdrive:
|
||||
self._lookupDict['//'] = self
|
||||
|
||||
def _morph(self) -> None:
|
||||
|
@ -2511,45 +2520,18 @@ class FileNodeInfo(SCons.Node.NodeInfoBase):
|
|||
def str_to_node(self, s):
|
||||
top = self.fs.Top
|
||||
root = top.root
|
||||
# Python 3.13/Win changed isabs() - after you split C:/foo/bar,
|
||||
# the path part is no longer considerd absolute. Save the passed
|
||||
# path for the isabs check so we can get the right answer.
|
||||
path = s
|
||||
if do_splitdrive:
|
||||
drive, s = _my_splitdrive(s)
|
||||
if drive:
|
||||
root = self.fs.get_root(drive)
|
||||
if not os.path.isabs(s):
|
||||
if not os.path.isabs(path):
|
||||
s = top.get_labspath() + '/' + s
|
||||
return root._lookup_abs(s, Entry)
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
Return all fields that shall be pickled. Walk the slots in the class
|
||||
hierarchy and add those to the state dictionary. If a '__dict__' slot is
|
||||
available, copy all entries to the dictionary. Also include the version
|
||||
id, which is fixed for all instances of a class.
|
||||
"""
|
||||
state = getattr(self, '__dict__', {}).copy()
|
||||
for obj in type(self).mro():
|
||||
for name in getattr(obj, '__slots__', ()):
|
||||
if hasattr(self, name):
|
||||
state[name] = getattr(self, name)
|
||||
|
||||
state['_version_id'] = self.current_version_id
|
||||
try:
|
||||
del state['__weakref__']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return state
|
||||
|
||||
def __setstate__(self, state) -> None:
|
||||
"""
|
||||
Restore the attributes from a pickled state.
|
||||
"""
|
||||
# TODO check or discard version
|
||||
del state['_version_id']
|
||||
for key, value in state.items():
|
||||
if key not in ('__weakref__',):
|
||||
setattr(self, key, value)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.csig == other.csig and self.timestamp == other.timestamp and self.size == other.size
|
||||
|
||||
|
@ -2986,7 +2968,7 @@ class File(Base):
|
|||
# created.
|
||||
self.dir._create()
|
||||
|
||||
def push_to_cache(self) -> None:
|
||||
def push_to_cache(self) -> bool:
|
||||
"""Try to push the node into a cache
|
||||
"""
|
||||
# This should get called before the Nodes' .built() method is
|
||||
|
@ -2997,10 +2979,10 @@ class File(Base):
|
|||
# the node to cache so that the memoization of the self.exists()
|
||||
# return value doesn't interfere.
|
||||
if self.nocache:
|
||||
return
|
||||
return None
|
||||
self.clear_memoized_values()
|
||||
if self.exists():
|
||||
self.get_build_env().get_CacheDir().push(self)
|
||||
return self.get_build_env().get_CacheDir().push(self)
|
||||
|
||||
def retrieve_from_cache(self) -> bool:
|
||||
"""Try to retrieve the node's content from a cache
|
||||
|
@ -3176,12 +3158,16 @@ class File(Base):
|
|||
return None
|
||||
|
||||
def do_duplicate(self, src):
|
||||
"""Create a duplicate of this file from the specified source."""
|
||||
self._createDir()
|
||||
if SCons.Node.print_duplicate:
|
||||
print(f"dup: relinking variant '{self}' from '{src}'")
|
||||
Unlink(self, None, None)
|
||||
try:
|
||||
e = Link(self, src, None)
|
||||
if isinstance(e, SCons.Errors.BuildError):
|
||||
raise e
|
||||
except SCons.Errors.BuildError as e:
|
||||
raise SCons.Errors.StopError(f"Cannot duplicate `{src.get_internal_path()}' in `{self.dir._path}': {e.errstr}.")
|
||||
self.linked = 1
|
||||
# The Link() action may or may not have actually
|
||||
|
@ -3534,7 +3520,7 @@ class File(Base):
|
|||
|
||||
In all cases self is the target we're checking to see if it's up to date
|
||||
"""
|
||||
T = 0
|
||||
T = False
|
||||
if T: Trace('is_up_to_date(%s):' % self)
|
||||
if not self.exists():
|
||||
if T: Trace(' not self.exists():')
|
||||
|
@ -3730,7 +3716,10 @@ class FileFinder:
|
|||
if fd is None:
|
||||
fd = self.default_filedir
|
||||
dir, name = os.path.split(fd)
|
||||
if do_splitdrive:
|
||||
drive, d = _my_splitdrive(dir)
|
||||
else:
|
||||
drive, d = "", dir
|
||||
if not name and d[:1] in ('/', OS_SEP):
|
||||
#return p.fs.get_root(drive).dir_on_disk(name)
|
||||
return p.fs.get_root(drive)
|
|
@ -37,37 +37,6 @@ class ValueNodeInfo(SCons.Node.NodeInfoBase):
|
|||
def str_to_node(self, s):
|
||||
return ValueWithMemo(s)
|
||||
|
||||
def __getstate__(self):
|
||||
"""
|
||||
Return all fields that shall be pickled. Walk the slots in the class
|
||||
hierarchy and add those to the state dictionary. If a '__dict__' slot
|
||||
is available, copy all entries to the dictionary. Also include the
|
||||
version id, which is fixed for all instances of a class.
|
||||
"""
|
||||
state = getattr(self, '__dict__', {}).copy()
|
||||
for obj in type(self).mro():
|
||||
for name in getattr(obj, '__slots__', ()):
|
||||
if hasattr(self, name):
|
||||
state[name] = getattr(self, name)
|
||||
|
||||
state['_version_id'] = self.current_version_id
|
||||
try:
|
||||
del state['__weakref__']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
return state
|
||||
|
||||
def __setstate__(self, state) -> None:
|
||||
"""
|
||||
Restore the attributes from a pickled state.
|
||||
"""
|
||||
# TODO check or discard version
|
||||
del state['_version_id']
|
||||
for key, value in state.items():
|
||||
if key not in ('__weakref__',):
|
||||
setattr(self, key, value)
|
||||
|
||||
|
||||
class ValueBuildInfo(SCons.Node.BuildInfoBase):
|
||||
__slots__ = ()
|
|
@ -678,7 +678,7 @@ class Node(metaclass=NoSlotsPyPy):
|
|||
except AttributeError:
|
||||
pass
|
||||
|
||||
def push_to_cache(self) -> None:
|
||||
def push_to_cache(self) -> bool:
|
||||
"""Try to push a node into a cache
|
||||
"""
|
||||
pass
|
||||
|
@ -1063,6 +1063,7 @@ class Node(metaclass=NoSlotsPyPy):
|
|||
# Don't bother scanning non-derived files, because we don't
|
||||
# care what their dependencies are.
|
||||
# Don't scan again, if we already have scanned.
|
||||
T = False
|
||||
if self.implicit is not None:
|
||||
return
|
||||
self.implicit = []
|
||||
|
@ -1087,7 +1088,12 @@ class Node(metaclass=NoSlotsPyPy):
|
|||
# essentially short-circuits an N*M scan of the
|
||||
# sources for each individual target, which is a hell
|
||||
# of a lot more efficient.
|
||||
def print_nodelist(n):
|
||||
tgts = [f"{t.path!r}" for t in n]
|
||||
return f"[{', '.join(tgts)}]"
|
||||
|
||||
for tgt in executor.get_all_targets():
|
||||
if T: Trace(f"adding implicit {print_nodelist(implicit)} to {tgt!s}\n")
|
||||
tgt.add_to_implicit(implicit)
|
||||
|
||||
if implicit_deps_unchanged or self.is_up_to_date():
|
||||
|
@ -1472,8 +1478,8 @@ class Node(metaclass=NoSlotsPyPy):
|
|||
|
||||
@see: FS.File.changed(), FS.File.release_target_info()
|
||||
"""
|
||||
t = 0
|
||||
if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
|
||||
T = False
|
||||
if T: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
|
||||
if node is None:
|
||||
node = self
|
||||
|
||||
|
@ -1491,25 +1497,24 @@ class Node(metaclass=NoSlotsPyPy):
|
|||
# entries to equal the new dependency list, for the benefit
|
||||
# of the loop below that updates node information.
|
||||
then.extend([None] * diff)
|
||||
if t: Trace(': old %s new %s' % (len(then), len(children)))
|
||||
if T: Trace(': old %s new %s' % (len(then), len(children)))
|
||||
result = True
|
||||
|
||||
for child, prev_ni in zip(children, then):
|
||||
if _decider_map[child.changed_since_last_build](child, self, prev_ni, node):
|
||||
if t: Trace(': %s changed' % child)
|
||||
if T: Trace(f": '{child!s}' changed")
|
||||
result = True
|
||||
|
||||
if self.has_builder():
|
||||
contents = self.get_executor().get_contents()
|
||||
newsig = hash_signature(contents)
|
||||
if bi.bactsig != newsig:
|
||||
if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig))
|
||||
if T: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig))
|
||||
result = True
|
||||
|
||||
if not result:
|
||||
if t: Trace(': up to date')
|
||||
|
||||
if t: Trace('\n')
|
||||
if T: Trace(': up to date')
|
||||
if T: Trace('\n')
|
||||
|
||||
return result
|
||||
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
"""Handle lists of directory paths.
|
||||
|
||||
These are the path lists that get set as CPPPATH, LIBPATH,
|
||||
These are the path lists that get set as ``CPPPATH``, ``LIBPATH``,
|
||||
etc.) with as much caching of data and efficiency as we can, while
|
||||
still keeping the evaluation delayed so that we Do the Right Thing
|
||||
(almost) regardless of how the variable is specified.
|
||||
|
@ -47,10 +47,10 @@ def node_conv(obj):
|
|||
"""
|
||||
This is the "string conversion" routine that we have our substitutions
|
||||
use to return Nodes, not strings. This relies on the fact that an
|
||||
EntryProxy object has a get() method that returns the underlying
|
||||
Node that it wraps, which is a bit of architectural dependence
|
||||
that we might need to break or modify in the future in response to
|
||||
additional requirements.
|
||||
:class:`~SCons.Node.FS.EntryProxy` object has a ``get()`` method that
|
||||
returns the underlying Node that it wraps, which is a bit of
|
||||
architectural dependence that we might need to break or modify in the
|
||||
future in response to additional requirements.
|
||||
"""
|
||||
try:
|
||||
get = obj.get
|
||||
|
@ -64,34 +64,35 @@ def node_conv(obj):
|
|||
return result
|
||||
|
||||
class _PathList:
|
||||
"""An actual PathList object."""
|
||||
"""An actual PathList object.
|
||||
|
||||
def __init__(self, pathlist, split=True) -> None:
|
||||
"""
|
||||
Initializes a PathList object, canonicalizing the input and
|
||||
Initializes a :class:`PathList` object, canonicalizing the input and
|
||||
pre-processing it for quicker substitution later.
|
||||
|
||||
The stored representation of the PathList is a list of tuples
|
||||
containing (type, value), where the "type" is one of the TYPE_*
|
||||
The stored representation of the :class:`PathList` is a list of tuples
|
||||
containing (type, value), where the "type" is one of the ``TYPE_*``
|
||||
variables defined above. We distinguish between:
|
||||
|
||||
strings that contain no '$' and therefore need no
|
||||
* Strings that contain no ``$`` and therefore need no
|
||||
delayed-evaluation string substitution (we expect that there
|
||||
will be many of these and that we therefore get a pretty
|
||||
big win from avoiding string substitution)
|
||||
|
||||
strings that contain '$' and therefore need substitution
|
||||
(the hard case is things like '${TARGET.dir}/include',
|
||||
* Strings that contain ``$`` and therefore need substitution
|
||||
(the hard case is things like ``${TARGET.dir}/include``,
|
||||
which require re-evaluation for every target + source)
|
||||
|
||||
other objects (which may be something like an EntryProxy
|
||||
* Other objects (which may be something like an
|
||||
:class:`~SCons.Node.FS.EntryProxy`
|
||||
that needs a method called to return a Node)
|
||||
|
||||
Pre-identifying the type of each element in the PathList up-front
|
||||
and storing the type in the list of tuples is intended to reduce
|
||||
the amount of calculation when we actually do the substitution
|
||||
Pre-identifying the type of each element in the :class:`PathList`
|
||||
up-front and storing the type in the list of tuples is intended to
|
||||
reduce the amount of calculation when we actually do the substitution
|
||||
over and over for each target.
|
||||
"""
|
||||
|
||||
def __init__(self, pathlist, split=True) -> None:
|
||||
if SCons.Util.is_String(pathlist):
|
||||
if split:
|
||||
pathlist = pathlist.split(os.pathsep)
|
||||
|
@ -152,34 +153,33 @@ class PathListCache:
|
|||
use the same Memoizer pattern that we use elsewhere to count cache
|
||||
hits and misses, which is very valuable.
|
||||
|
||||
Lookup keys in the cache are computed by the _PathList_key() method.
|
||||
Lookup keys in the cache are computed by the :meth:`_PathList_key` method.
|
||||
Cache lookup should be quick, so we don't spend cycles canonicalizing
|
||||
all forms of the same lookup key. For example, 'x:y' and ['x',
|
||||
'y'] logically represent the same list, but we don't bother to
|
||||
all forms of the same lookup key. For example, ``x:y`` and ``['x', 'y']``
|
||||
logically represent the same list, but we don't bother to
|
||||
split string representations and treat those two equivalently.
|
||||
(Note, however, that we do, treat lists and tuples the same.)
|
||||
|
||||
The main type of duplication we're trying to catch will come from
|
||||
looking up the same path list from two different clones of the
|
||||
same construction environment. That is, given
|
||||
same construction environment. That is, given::
|
||||
|
||||
env2 = env1.Clone()
|
||||
|
||||
both env1 and env2 will have the same CPPPATH value, and we can
|
||||
cheaply avoid re-parsing both values of CPPPATH by using the
|
||||
both ``env1`` and ``env2`` will have the same ``CPPPATH`` value, and we can
|
||||
cheaply avoid re-parsing both values of ``CPPPATH`` by using the
|
||||
common value from this cache.
|
||||
"""
|
||||
def __init__(self) -> None:
|
||||
self._memo = {}
|
||||
|
||||
def _PathList_key(self, pathlist):
|
||||
"""
|
||||
Returns the key for memoization of PathLists.
|
||||
"""Returns the key for memoization of PathLists.
|
||||
|
||||
Note that we want this to be pretty quick, so we don't completely
|
||||
canonicalize all forms of the same list. For example,
|
||||
'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically
|
||||
represent the same list if you're executing from $ROOT, but
|
||||
``dir1:$ROOT/dir2`` and ``['$ROOT/dir1', 'dir']`` may logically
|
||||
represent the same list if you're executing from ``$ROOT``, but
|
||||
we're not going to bother splitting strings into path elements,
|
||||
or massaging strings into Nodes, to identify that equivalence.
|
||||
We just want to eliminate obvious redundancy from the normal
|
||||
|
@ -191,9 +191,10 @@ class PathListCache:
|
|||
|
||||
@SCons.Memoize.CountDictCall(_PathList_key)
|
||||
def PathList(self, pathlist, split=True):
|
||||
"""
|
||||
Returns the cached _PathList object for the specified pathlist,
|
||||
creating and caching a new object as necessary.
|
||||
"""Entry point for getting PathLists.
|
||||
|
||||
Returns the cached :class:`_PathList` object for the specified
|
||||
pathlist, creating and caching a new object as necessary.
|
||||
"""
|
||||
pathlist = self._PathList_key(pathlist)
|
||||
try:
|
||||
|
@ -215,7 +216,8 @@ class PathListCache:
|
|||
|
||||
PathList = PathListCache().PathList
|
||||
|
||||
|
||||
# TODO: removing the class object here means Sphinx doesn't pick up its
|
||||
# docstrings: they're fine for reading here, but are not in API Docs.
|
||||
del PathListCache
|
||||
|
||||
# Local Variables:
|
|
@ -41,7 +41,7 @@ import time
|
|||
import traceback
|
||||
import platform
|
||||
import threading
|
||||
from typing import Optional, List
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
|
||||
import SCons.CacheDir
|
||||
import SCons.Debug
|
||||
|
@ -59,15 +59,17 @@ import SCons.Taskmaster
|
|||
import SCons.Util
|
||||
import SCons.Warnings
|
||||
import SCons.Script.Interactive
|
||||
if TYPE_CHECKING:
|
||||
from SCons.Script import SConsOption
|
||||
from SCons.Util.stats import count_stats, memory_stats, time_stats, ENABLE_JSON, write_scons_stats_file, JSON_OUTPUT_FILE
|
||||
|
||||
from SCons import __version__ as SConsVersion
|
||||
|
||||
# these define the range of versions SCons supports
|
||||
minimum_python_version = (3, 6, 0)
|
||||
deprecated_python_version = (3, 6, 0)
|
||||
deprecated_python_version = (3, 7, 0) # the first non-deprecated version
|
||||
|
||||
# ordered list of SConsctruct names to look for if there is no -f flag
|
||||
# ordered list of SConstruct names to look for if there is no -f flag
|
||||
KNOWN_SCONSTRUCT_NAMES = [
|
||||
'SConstruct',
|
||||
'Sconstruct',
|
||||
|
@ -84,7 +86,11 @@ KNOWN_SCONSCRIPTS = [
|
|||
"Sconstruct",
|
||||
"sconstruct",
|
||||
"SConscript",
|
||||
"Sconscript",
|
||||
"sconscript",
|
||||
"SCsub", # Uncommon alternative to SConscript
|
||||
"Scsub",
|
||||
"scsub",
|
||||
]
|
||||
|
||||
# Global variables
|
||||
|
@ -174,6 +180,7 @@ class Progressor:
|
|||
ProgressObject = SCons.Util.Null()
|
||||
|
||||
def Progress(*args, **kw) -> None:
|
||||
"""Show progress during building - Public API."""
|
||||
global ProgressObject
|
||||
ProgressObject = Progressor(*args, **kw)
|
||||
|
||||
|
@ -501,29 +508,47 @@ class FakeOptionParser:
|
|||
# TODO: to quiet checkers, FakeOptionParser should also define
|
||||
# raise_exception_on_error, preserve_unknown_options, largs and parse_args
|
||||
|
||||
def add_local_option(self, *args, **kw) -> None:
|
||||
def add_local_option(self, *args, **kw) -> "SConsOption":
|
||||
pass
|
||||
|
||||
|
||||
OptionsParser = FakeOptionParser()
|
||||
|
||||
def AddOption(*args, **kw):
|
||||
def AddOption(*args, settable: bool = False, **kw) -> "SConsOption":
|
||||
"""Add a local option to the option parser - Public API.
|
||||
|
||||
If the *settable* parameter is true, the option will be included in the
|
||||
list of settable options; all other keyword arguments are passed on to
|
||||
:meth:`~SCons.Script.SConsOptions.SConsOptionParser.add_local_option`.
|
||||
|
||||
.. versionchanged:: 4.8.0
|
||||
The *settable* parameter added to allow including the new option
|
||||
to the table of options eligible to use :func:`SetOption`.
|
||||
|
||||
"""
|
||||
if 'default' not in kw:
|
||||
kw['default'] = None
|
||||
kw['settable'] = settable
|
||||
result = OptionsParser.add_local_option(*args, **kw)
|
||||
return result
|
||||
|
||||
def GetOption(name):
|
||||
def GetOption(name: str):
|
||||
"""Get the value from an option - Public API."""
|
||||
return getattr(OptionsParser.values, name)
|
||||
|
||||
def SetOption(name, value):
|
||||
def SetOption(name: str, value):
|
||||
"""Set the value of an option - Public API."""
|
||||
return OptionsParser.values.set_option(name, value)
|
||||
|
||||
def DebugOptions(json=None):
|
||||
"""
|
||||
API to allow specifying options to SCons debug logic
|
||||
Currently only json is supported which changes the
|
||||
json file written by --debug=json from the default
|
||||
def DebugOptions(json: Optional[str] = None) -> None:
|
||||
"""Specify options to SCons debug logic - Public API.
|
||||
|
||||
Currently only *json* is supported, which changes the JSON file
|
||||
written to if the ``--debug=json`` command-line option is specified
|
||||
to the value supplied.
|
||||
|
||||
.. versionadded:: 4.6.0
|
||||
|
||||
"""
|
||||
if json is not None:
|
||||
json_node = SCons.Defaults.DefaultEnvironment().arg2nodes(json)
|
||||
|
@ -1104,11 +1129,13 @@ def _main(parser):
|
|||
# warning about deprecated Python versions--delayed until here
|
||||
# in case they disabled the warning in the SConscript files.
|
||||
if python_version_deprecated():
|
||||
msg = "Support for pre-%s Python version (%s) is deprecated.\n" + \
|
||||
" If this will cause hardship, contact scons-dev@scons.org"
|
||||
deprecated_version_string = ".".join(map(str, deprecated_python_version))
|
||||
SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
|
||||
msg % (deprecated_version_string, python_version_string()))
|
||||
msg = (
|
||||
f"Support for Python older than {deprecated_version_string}"
|
||||
f" is deprecated ({python_version_string()} detected).\n"
|
||||
" If this will cause hardship, contact scons-dev@scons.org"
|
||||
)
|
||||
SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning, msg)
|
||||
|
||||
if not options.help:
|
||||
# [ ] Clarify why we need to create Builder here at all, and
|
||||
|
@ -1396,6 +1423,10 @@ def _exec_main(parser, values) -> None:
|
|||
sconscript files that don't have the suffix.
|
||||
|
||||
.. versionadded:: 4.6.0
|
||||
|
||||
.. versionchanged:: 4.8.0
|
||||
The additional name ``SCsub`` (with spelling variants)
|
||||
is also recognized - Godot uses this name.
|
||||
"""
|
||||
if os.path.isabs(filename) and os.path.exists(filename):
|
||||
return filename
|
|
@ -66,17 +66,14 @@ def diskcheck_convert(value):
|
|||
|
||||
|
||||
class SConsValues(optparse.Values):
|
||||
"""
|
||||
Holder class for uniform access to SCons options, regardless
|
||||
of whether they can be set on the command line or in the
|
||||
SConscript files (using the SetOption() function).
|
||||
"""Holder class for uniform access to SCons options.
|
||||
|
||||
A SCons option value can originate three different ways:
|
||||
|
||||
1) set on the command line;
|
||||
2) set in an SConscript file;
|
||||
3) the default setting (from the the op.add_option()
|
||||
calls in the Parser() function, below).
|
||||
1. set on the command line.
|
||||
2. set in an SConscript file via :func:`~SCons.Script.Main.SetOption`.
|
||||
3. the default setting (from the the ``op.add_option()``
|
||||
calls in the :func:`Parser` function, below).
|
||||
|
||||
The command line always overrides a value set in a SConscript file,
|
||||
which in turn always overrides default settings. Because we want
|
||||
|
@ -87,15 +84,15 @@ class SConsValues(optparse.Values):
|
|||
|
||||
The solution implemented in this class is to keep these different sets
|
||||
of settings separate (command line, SConscript file, and default)
|
||||
and to override the __getattr__() method to check them in turn.
|
||||
This should allow the rest of the code to just fetch values as
|
||||
attributes of an instance of this class, without having to worry
|
||||
about where they came from.
|
||||
and to override the :meth:`__getattr__` method to check them in turn.
|
||||
This allows the rest of the code to just fetch values as attributes of
|
||||
an instance of this class, without having to worry about where they
|
||||
came from (the scheme is similar to a ``ChainMap``).
|
||||
|
||||
Note that not all command line options are settable from SConscript
|
||||
files, and the ones that are must be explicitly added to the
|
||||
"settable" list in this class, and optionally validated and coerced
|
||||
in the set_option() method.
|
||||
:attr:`settable` list in this class, and optionally validated and coerced
|
||||
in the :meth:`set_option` method.
|
||||
"""
|
||||
|
||||
def __init__(self, defaults) -> None:
|
||||
|
@ -103,10 +100,11 @@ class SConsValues(optparse.Values):
|
|||
self.__SConscript_settings__ = {}
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""
|
||||
Fetches an options value, checking first for explicit settings
|
||||
from the command line (which are direct attributes), then the
|
||||
SConscript file settings, then the default values.
|
||||
"""Fetch an options value, respecting priority rules.
|
||||
|
||||
This is a little tricky: since we're answering questions
|
||||
about outselves, we have avoid lookups that would send us into
|
||||
into infinite recursion, thus the ``__dict__`` stuff.
|
||||
"""
|
||||
try:
|
||||
return self.__dict__[attr]
|
||||
|
@ -116,15 +114,10 @@ class SConsValues(optparse.Values):
|
|||
except KeyError:
|
||||
try:
|
||||
return getattr(self.__dict__['__defaults__'], attr)
|
||||
except KeyError:
|
||||
# Added because with py3 this is a new class,
|
||||
# not a classic class, and due to the way
|
||||
# In that case it will create an object without
|
||||
# __defaults__, and then query for __setstate__
|
||||
# which will throw an exception of KeyError
|
||||
# deepcopy() is expecting AttributeError if __setstate__
|
||||
# is not available.
|
||||
raise AttributeError(attr)
|
||||
except KeyError as exc:
|
||||
# Need to respond with AttributeError because
|
||||
# deepcopy expects that if __setstate__ is not available.
|
||||
raise AttributeError(attr) from exc
|
||||
|
||||
# keep this list in sync with the SetOption doc in SCons/Script/Main.xml
|
||||
# search for UPDATE_SETOPTION_DOCS there.
|
||||
|
@ -148,26 +141,26 @@ class SConsValues(optparse.Values):
|
|||
'silent',
|
||||
'stack_size',
|
||||
'warn',
|
||||
|
||||
# TODO: Remove these once we update the AddOption() API to allow setting
|
||||
# added flag as settable.
|
||||
# Requested settable flag in : https://github.com/SCons/scons/issues/3983
|
||||
# From experimental ninja
|
||||
'disable_execute_ninja',
|
||||
'disable_ninja',
|
||||
'skip_ninja_regen'
|
||||
]
|
||||
|
||||
def set_option(self, name, value):
|
||||
"""Sets an option from an SConscript file.
|
||||
def set_option(self, name: str, value) -> None:
|
||||
"""Sets an option *name* from an SConscript file.
|
||||
|
||||
Vvalidation steps for known (that is, defined in SCons itself)
|
||||
options are in-line here. Validation should be along the same
|
||||
lines as for options processed from the command line -
|
||||
it's kind of a pain to have to duplicate. Project-defined options
|
||||
can specify callbacks for the command-line version, but will have
|
||||
no inbuilt validation here. It's up to the build system maintainer
|
||||
to make sure :func:`~SCons.Script.Main.SetOption` is being used
|
||||
correctly, we can't really do any better here.
|
||||
|
||||
Raises:
|
||||
UserError: invalid or malformed option ("error in your script")
|
||||
UserError: the option is not settable.
|
||||
"""
|
||||
|
||||
if name not in self.settable:
|
||||
raise SCons.Errors.UserError(
|
||||
"This option is not settable from a SConscript file: %s" % name
|
||||
f"This option is not settable from an SConscript file: {name!r}"
|
||||
)
|
||||
|
||||
# the following are for options that need some extra processing
|
||||
|
@ -247,7 +240,6 @@ class SConsOption(optparse.Option):
|
|||
return tuple([self.check_value(opt, v) for v in value])
|
||||
|
||||
def process(self, opt, value, values, parser):
|
||||
|
||||
# First, convert the value(s) to the right type. Howl if any
|
||||
# value(s) are bogus.
|
||||
value = self.convert_value(opt, value)
|
||||
|
@ -313,9 +305,7 @@ class SConsOptionParser(optparse.OptionParser):
|
|||
raise_exception_on_error = False
|
||||
|
||||
def error(self, msg):
|
||||
"""
|
||||
overridden OptionValueError exception handler
|
||||
"""
|
||||
"""Overridden OptionValueError exception handler."""
|
||||
if self.raise_exception_on_error:
|
||||
raise SConsBadOptionError(msg, self)
|
||||
else:
|
||||
|
@ -399,44 +389,44 @@ class SConsOptionParser(optparse.OptionParser):
|
|||
def reparse_local_options(self) -> None:
|
||||
"""Re-parse the leftover command-line options.
|
||||
|
||||
Parse options stored in `self.largs`, so that any value
|
||||
Leftover options are stored in ``self.largs``, so that any value
|
||||
overridden on the command line is immediately available
|
||||
if the user turns around and does a :func:`GetOption` right away.
|
||||
if the user turns around and does a :func:`~SCons.Script.Main.GetOption`
|
||||
right away.
|
||||
|
||||
We mimic the processing of the single args
|
||||
in the original OptionParser :func:`_process_args`, but here we
|
||||
allow exact matches for long-opts only (no partial argument names!).
|
||||
Otherwise there could be problems in :func:`add_local_option`
|
||||
Otherwise there could be problems in :meth:`add_local_option`
|
||||
below. When called from there, we try to reparse the
|
||||
command-line arguments that
|
||||
|
||||
1. haven't been processed so far (`self.largs`), but
|
||||
2. are possibly not added to the list of options yet.
|
||||
|
||||
So, when we only have a value for "--myargument" so far,
|
||||
a command-line argument of "--myarg=test" would set it,
|
||||
So, when we only have a value for ``--myargument`` so far,
|
||||
a command-line argument of ``--myarg=test`` would set it,
|
||||
per the behaviour of :func:`_match_long_opt`,
|
||||
which allows for partial matches of the option name,
|
||||
as long as the common prefix appears to be unique.
|
||||
This would lead to further confusion, because we might want
|
||||
to add another option "--myarg" later on (see issue #2929).
|
||||
|
||||
to add another option ``--myarg`` later on (see issue #2929).
|
||||
"""
|
||||
rargs = []
|
||||
largs_restore = []
|
||||
# Loop over all remaining arguments
|
||||
skip = False
|
||||
for l in self.largs:
|
||||
for larg in self.largs:
|
||||
if skip:
|
||||
# Accept all remaining arguments as they are
|
||||
largs_restore.append(l)
|
||||
largs_restore.append(larg)
|
||||
else:
|
||||
if len(l) > 2 and l[0:2] == "--":
|
||||
if len(larg) > 2 and larg[0:2] == "--":
|
||||
# Check long option
|
||||
lopt = (l,)
|
||||
if "=" in l:
|
||||
lopt = [larg]
|
||||
if "=" in larg:
|
||||
# Split into option and value
|
||||
lopt = l.split("=", 1)
|
||||
lopt = larg.split("=", 1)
|
||||
|
||||
if lopt[0] in self._long_opt:
|
||||
# Argument is already known
|
||||
|
@ -445,26 +435,35 @@ class SConsOptionParser(optparse.OptionParser):
|
|||
# Not known yet, so reject for now
|
||||
largs_restore.append('='.join(lopt))
|
||||
else:
|
||||
if l == "--" or l == "-":
|
||||
if larg in("--", "-"):
|
||||
# Stop normal processing and don't
|
||||
# process the rest of the command-line opts
|
||||
largs_restore.append(l)
|
||||
largs_restore.append(larg)
|
||||
skip = True
|
||||
else:
|
||||
rargs.append(l)
|
||||
rargs.append(larg)
|
||||
|
||||
# Parse the filtered list
|
||||
self.parse_args(rargs, self.values)
|
||||
# Restore the list of remaining arguments for the
|
||||
# Restore the list of leftover arguments for the
|
||||
# next call of AddOption/add_local_option...
|
||||
self.largs = self.largs + largs_restore
|
||||
|
||||
def add_local_option(self, *args, **kw):
|
||||
def add_local_option(self, *args, **kw) -> SConsOption:
|
||||
""" Adds a local option to the parser.
|
||||
|
||||
This is initiated by an :func:`AddOption` call to add a user-defined
|
||||
command-line option. We add the option to a separate option
|
||||
group for the local options, creating the group if necessary.
|
||||
This is initiated by an :func:`~SCons.Script.Main.AddOption` call to
|
||||
add a user-defined command-line option. Add the option to a separate
|
||||
option group for the local options, creating the group if necessary.
|
||||
|
||||
The keyword argument *settable* is recognized specially (and
|
||||
removed from *kw*). If true, the option is marked as modifiable;
|
||||
by default "local" (project-added) options are not eligible for
|
||||
for :func:`~SCons.Script.Main.SetOption` calls.
|
||||
|
||||
.. versionchanged:: 4.8.0
|
||||
Added special handling of *settable*.
|
||||
|
||||
"""
|
||||
try:
|
||||
group = self.local_option_group
|
||||
|
@ -473,6 +472,7 @@ class SConsOptionParser(optparse.OptionParser):
|
|||
group = self.add_option_group(group)
|
||||
self.local_option_group = group
|
||||
|
||||
settable = kw.pop('settable')
|
||||
result = group.add_option(*args, **kw)
|
||||
if result:
|
||||
# The option was added successfully. We now have to add the
|
||||
|
@ -485,6 +485,8 @@ class SConsOptionParser(optparse.OptionParser):
|
|||
# right away.
|
||||
setattr(self.values.__defaults__, result.dest, result.default)
|
||||
self.reparse_local_options()
|
||||
if settable:
|
||||
SConsValues.settable.append(result.dest)
|
||||
|
||||
return result
|
||||
|
||||
|
@ -614,7 +616,8 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
|
|||
"""Local-only version of store_option_strings.
|
||||
|
||||
We need to replicate this so the formatter will be set up
|
||||
properly if we didn't go through the "normal" store_option_strings
|
||||
properly if we didn't go through the "normal"
|
||||
:math:`~optparse.HelpFormatter.store_option_strings`.
|
||||
|
||||
.. versionadded:: 4.6.0
|
||||
"""
|
||||
|
@ -633,15 +636,18 @@ def Parser(version):
|
|||
"""Returns a parser object initialized with the standard SCons options.
|
||||
|
||||
Add options in the order we want them to show up in the ``-H`` help
|
||||
text, basically alphabetical. Each ``op.add_option()`` call
|
||||
should have a consistent format::
|
||||
text, basically alphabetical. For readability, Each
|
||||
:meth:`~optparse.OptionContainer.add_option` call should have a
|
||||
consistent format::
|
||||
|
||||
op.add_option("-L", "--long-option-name",
|
||||
op.add_option(
|
||||
"-L", "--long-option-name",
|
||||
nargs=1, type="string",
|
||||
dest="long_option_name", default='foo',
|
||||
action="callback", callback=opt_long_option,
|
||||
help="help text goes here",
|
||||
metavar="VAR")
|
||||
metavar="VAR"
|
||||
)
|
||||
|
||||
Even though the :mod:`optparse` module constructs reasonable default
|
||||
destination names from the long option names, we're going to be
|
|
@ -45,6 +45,7 @@ import re
|
|||
import sys
|
||||
import traceback
|
||||
import time
|
||||
from typing import Tuple
|
||||
|
||||
class SConscriptReturn(Exception):
|
||||
pass
|
||||
|
@ -385,7 +386,7 @@ class SConsEnvironment(SCons.Environment.Base):
|
|||
# Private methods of an SConsEnvironment.
|
||||
#
|
||||
@staticmethod
|
||||
def _get_major_minor_revision(version_string):
|
||||
def _get_major_minor_revision(version_string: str) -> Tuple[int, int, int]:
|
||||
"""Split a version string into major, minor and (optionally)
|
||||
revision parts.
|
||||
|
||||
|
@ -484,15 +485,22 @@ class SConsEnvironment(SCons.Environment.Base):
|
|||
SCons.Script._Set_Default_Targets(self, targets)
|
||||
|
||||
@staticmethod
|
||||
def EnsureSConsVersion(major, minor, revision: int=0) -> None:
|
||||
def GetSConsVersion() -> Tuple[int, int, int]:
|
||||
"""Return the current SCons version.
|
||||
|
||||
.. versionadded:: 4.8.0
|
||||
"""
|
||||
return SConsEnvironment._get_major_minor_revision(SCons.__version__)
|
||||
|
||||
@staticmethod
|
||||
def EnsureSConsVersion(major: int, minor: int, revision: int = 0) -> None:
|
||||
"""Exit abnormally if the SCons version is not late enough."""
|
||||
# split string to avoid replacement during build process
|
||||
if SCons.__version__ == '__' + 'VERSION__':
|
||||
SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning,
|
||||
"EnsureSConsVersion is ignored for development version")
|
||||
return
|
||||
scons_ver = SConsEnvironment._get_major_minor_revision(SCons.__version__)
|
||||
if scons_ver < (major, minor, revision):
|
||||
if SConsEnvironment.GetSConsVersion() < (major, minor, revision):
|
||||
if revision:
|
||||
scons_ver_string = '%d.%d.%d' % (major, minor, revision)
|
||||
else:
|
|
@ -297,6 +297,7 @@ def Variables(files=None, args=ARGUMENTS):
|
|||
#
|
||||
# Static functions that do not trigger initialization of
|
||||
# DefaultEnvironment() and don't use its state.
|
||||
GetSConsVersion = _SConscript.SConsEnvironment.GetSConsVersion
|
||||
EnsureSConsVersion = _SConscript.SConsEnvironment.EnsureSConsVersion
|
||||
EnsurePythonVersion = _SConscript.SConsEnvironment.EnsurePythonVersion
|
||||
Exit = _SConscript.SConsEnvironment.Exit
|
|
@ -278,9 +278,8 @@ class ThreadPool:
|
|||
|
||||
try:
|
||||
prev_size = threading.stack_size(stack_size * 1024)
|
||||
except AttributeError as e:
|
||||
# Only print a warning if the stack size has been
|
||||
# explicitly set.
|
||||
except RuntimeError as e:
|
||||
# Only print a warning if the stack size has been explicitly set.
|
||||
if explicit_stack_size is not None:
|
||||
msg = "Setting stack size is unsupported by this version of Python:\n " + \
|
||||
e.args[0]
|
668
scons/scons-local-4.8.0/SCons/Tool/MSCommon/MSVC/Kind.py
vendored
Normal file
668
scons/scons-local-4.8.0/SCons/Tool/MSCommon/MSVC/Kind.py
vendored
Normal file
|
@ -0,0 +1,668 @@
|
|||
# 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.
|
||||
|
||||
"""
|
||||
Version kind categorization for Microsoft Visual C/C++.
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
from collections import (
|
||||
namedtuple,
|
||||
)
|
||||
|
||||
from ..common import (
|
||||
debug,
|
||||
)
|
||||
|
||||
from . import Registry
|
||||
from . import Util
|
||||
|
||||
from . import Dispatcher
|
||||
Dispatcher.register_modulename(__name__)
|
||||
|
||||
|
||||
# use express install for non-express msvc_version if no other installations found
|
||||
USE_EXPRESS_FOR_NONEXPRESS = True
|
||||
|
||||
# productdir kind
|
||||
|
||||
VCVER_KIND_UNKNOWN = 0 # undefined
|
||||
VCVER_KIND_DEVELOP = 1 # devenv binary
|
||||
VCVER_KIND_EXPRESS = 2 # express binary
|
||||
VCVER_KIND_BTDISPATCH = 3 # no ide binaries (buildtools dispatch folder)
|
||||
VCVER_KIND_VCFORPYTHON = 4 # no ide binaries (2008/9.0)
|
||||
VCVER_KIND_EXPRESS_WIN = 5 # express for windows binary (VSWinExpress)
|
||||
VCVER_KIND_EXPRESS_WEB = 6 # express for web binary (VWDExpress)
|
||||
VCVER_KIND_SDK = 7 # no ide binaries
|
||||
VCVER_KIND_CMDLINE = 8 # no ide binaries
|
||||
|
||||
VCVER_KIND_STR = {
|
||||
VCVER_KIND_UNKNOWN: '<Unknown>',
|
||||
VCVER_KIND_DEVELOP: 'Develop',
|
||||
VCVER_KIND_EXPRESS: 'Express',
|
||||
VCVER_KIND_BTDISPATCH: 'BTDispatch',
|
||||
VCVER_KIND_VCFORPYTHON: 'VCForPython',
|
||||
VCVER_KIND_EXPRESS_WIN: 'Express-Win',
|
||||
VCVER_KIND_EXPRESS_WEB: 'Express-Web',
|
||||
VCVER_KIND_SDK: 'SDK',
|
||||
VCVER_KIND_CMDLINE: 'CmdLine',
|
||||
}
|
||||
|
||||
BITFIELD_KIND_DEVELOP = 0b_1000
|
||||
BITFIELD_KIND_EXPRESS = 0b_0100
|
||||
BITFIELD_KIND_EXPRESS_WIN = 0b_0010
|
||||
BITFIELD_KIND_EXPRESS_WEB = 0b_0001
|
||||
|
||||
VCVER_KIND_PROGRAM = namedtuple("VCVerKindProgram", [
|
||||
'kind', # relpath from pdir to vsroot
|
||||
'program', # ide binaries
|
||||
'bitfield',
|
||||
])
|
||||
|
||||
#
|
||||
|
||||
IDE_PROGRAM_DEVENV_COM = VCVER_KIND_PROGRAM(
|
||||
kind=VCVER_KIND_DEVELOP,
|
||||
program='devenv.com',
|
||||
bitfield=BITFIELD_KIND_DEVELOP,
|
||||
)
|
||||
|
||||
IDE_PROGRAM_MSDEV_COM = VCVER_KIND_PROGRAM(
|
||||
kind=VCVER_KIND_DEVELOP,
|
||||
program='msdev.com',
|
||||
bitfield=BITFIELD_KIND_DEVELOP,
|
||||
)
|
||||
|
||||
IDE_PROGRAM_WDEXPRESS_EXE = VCVER_KIND_PROGRAM(
|
||||
kind=VCVER_KIND_EXPRESS,
|
||||
program='WDExpress.exe',
|
||||
bitfield=BITFIELD_KIND_EXPRESS,
|
||||
)
|
||||
|
||||
IDE_PROGRAM_VCEXPRESS_EXE = VCVER_KIND_PROGRAM(
|
||||
kind=VCVER_KIND_EXPRESS,
|
||||
program='VCExpress.exe',
|
||||
bitfield=BITFIELD_KIND_EXPRESS,
|
||||
)
|
||||
|
||||
IDE_PROGRAM_VSWINEXPRESS_EXE = VCVER_KIND_PROGRAM(
|
||||
kind=VCVER_KIND_EXPRESS_WIN,
|
||||
program='VSWinExpress.exe',
|
||||
bitfield=BITFIELD_KIND_EXPRESS_WIN,
|
||||
)
|
||||
|
||||
IDE_PROGRAM_VWDEXPRESS_EXE = VCVER_KIND_PROGRAM(
|
||||
kind=VCVER_KIND_EXPRESS_WEB,
|
||||
program='VWDExpress.exe',
|
||||
bitfield=BITFIELD_KIND_EXPRESS_WEB,
|
||||
)
|
||||
|
||||
# detection configuration
|
||||
|
||||
VCVER_KIND_DETECT = namedtuple("VCVerKindDetect", [
|
||||
'root', # relpath from pdir to vsroot
|
||||
'path', # vsroot to ide dir
|
||||
'programs', # ide binaries
|
||||
])
|
||||
|
||||
# detected binaries
|
||||
|
||||
VCVER_DETECT_BINARIES = namedtuple("VCVerDetectBinaries", [
|
||||
'bitfields', # detect values
|
||||
'have_dev', # develop ide binary
|
||||
'have_exp', # express ide binary
|
||||
'have_exp_win', # express windows ide binary
|
||||
'have_exp_web', # express web ide binary
|
||||
])
|
||||
|
||||
|
||||
VCVER_DETECT_KIND = namedtuple("VCVerDetectKind", [
|
||||
'skip', # skip vs root
|
||||
'save', # save in case no other kind found
|
||||
'kind', # internal kind
|
||||
'binaries_t',
|
||||
'extended',
|
||||
])
|
||||
|
||||
# unknown value
|
||||
|
||||
_VCVER_DETECT_KIND_UNKNOWN = VCVER_DETECT_KIND(
|
||||
skip=True,
|
||||
save=False,
|
||||
kind=VCVER_KIND_UNKNOWN,
|
||||
binaries_t=VCVER_DETECT_BINARIES(
|
||||
bitfields=0b0,
|
||||
have_dev=False,
|
||||
have_exp=False,
|
||||
have_exp_win=False,
|
||||
have_exp_web=False,
|
||||
),
|
||||
extended={},
|
||||
)
|
||||
|
||||
#
|
||||
|
||||
_msvc_pdir_func = None
|
||||
|
||||
def register_msvc_version_pdir_func(func):
|
||||
global _msvc_pdir_func
|
||||
if func:
|
||||
_msvc_pdir_func = func
|
||||
|
||||
_cache_vcver_kind_map = {}
|
||||
|
||||
def msvc_version_register_kind(msvc_version, kind_t) -> None:
|
||||
global _cache_vcver_kind_map
|
||||
if kind_t is None:
|
||||
kind_t = _VCVER_DETECT_KIND_UNKNOWN
|
||||
debug('msvc_version=%s, kind=%s', repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind]))
|
||||
_cache_vcver_kind_map[msvc_version] = kind_t
|
||||
|
||||
def _msvc_version_kind_lookup(msvc_version, env=None):
|
||||
global _cache_vcver_kind_map
|
||||
global _msvc_pdir_func
|
||||
if msvc_version not in _cache_vcver_kind_map:
|
||||
_msvc_pdir_func(msvc_version, env)
|
||||
kind_t = _cache_vcver_kind_map.get(msvc_version, _VCVER_DETECT_KIND_UNKNOWN)
|
||||
debug(
|
||||
'kind=%s, dev=%s, exp=%s, msvc_version=%s',
|
||||
repr(VCVER_KIND_STR[kind_t.kind]),
|
||||
kind_t.binaries_t.have_dev, kind_t.binaries_t.have_exp,
|
||||
repr(msvc_version)
|
||||
)
|
||||
return kind_t
|
||||
|
||||
def msvc_version_is_btdispatch(msvc_version, env=None):
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
is_btdispatch = bool(kind_t.kind == VCVER_KIND_BTDISPATCH)
|
||||
debug(
|
||||
'is_btdispatch=%s, kind:%s, msvc_version=%s',
|
||||
repr(is_btdispatch), repr(VCVER_KIND_STR[kind_t.kind]), repr(msvc_version)
|
||||
)
|
||||
return is_btdispatch
|
||||
|
||||
def msvc_version_is_express(msvc_version, env=None):
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
is_express = bool(kind_t.kind == VCVER_KIND_EXPRESS)
|
||||
debug(
|
||||
'is_express=%s, kind:%s, msvc_version=%s',
|
||||
repr(is_express), repr(VCVER_KIND_STR[kind_t.kind]), repr(msvc_version)
|
||||
)
|
||||
return is_express
|
||||
|
||||
def msvc_version_is_vcforpython(msvc_version, env=None):
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
is_vcforpython = bool(kind_t.kind == VCVER_KIND_VCFORPYTHON)
|
||||
debug(
|
||||
'is_vcforpython=%s, kind:%s, msvc_version=%s',
|
||||
repr(is_vcforpython), repr(VCVER_KIND_STR[kind_t.kind]), repr(msvc_version)
|
||||
)
|
||||
return is_vcforpython
|
||||
|
||||
def msvc_version_skip_uwp_target(env, msvc_version):
|
||||
|
||||
vernum = float(Util.get_msvc_version_prefix(msvc_version))
|
||||
vernum_int = int(vernum * 10)
|
||||
|
||||
if vernum_int != 140:
|
||||
return False
|
||||
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
if kind_t.kind != VCVER_KIND_EXPRESS:
|
||||
return False
|
||||
|
||||
target_arch = env.get('TARGET_ARCH')
|
||||
|
||||
uwp_is_supported = kind_t.extended.get('uwp_is_supported', {})
|
||||
is_supported = uwp_is_supported.get(target_arch, True)
|
||||
|
||||
if is_supported:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def _pdir_detect_binaries(pdir, detect):
|
||||
|
||||
vs_root = os.path.join(pdir, detect.root)
|
||||
ide_path = os.path.join(vs_root, detect.path)
|
||||
|
||||
bitfields = 0b_0000
|
||||
for ide_program in detect.programs:
|
||||
prog = os.path.join(ide_path, ide_program.program)
|
||||
if not os.path.exists(prog):
|
||||
continue
|
||||
bitfields |= ide_program.bitfield
|
||||
|
||||
have_dev = bool(bitfields & BITFIELD_KIND_DEVELOP)
|
||||
have_exp = bool(bitfields & BITFIELD_KIND_EXPRESS)
|
||||
have_exp_win = bool(bitfields & BITFIELD_KIND_EXPRESS_WIN)
|
||||
have_exp_web = bool(bitfields & BITFIELD_KIND_EXPRESS_WEB)
|
||||
|
||||
binaries_t = VCVER_DETECT_BINARIES(
|
||||
bitfields=bitfields,
|
||||
have_dev=have_dev,
|
||||
have_exp=have_exp,
|
||||
have_exp_win=have_exp_win,
|
||||
have_exp_web=have_exp_web,
|
||||
)
|
||||
|
||||
debug(
|
||||
'vs_root=%s, dev=%s, exp=%s, exp_win=%s, exp_web=%s, pdir=%s',
|
||||
repr(vs_root),
|
||||
binaries_t.have_dev, binaries_t.have_exp,
|
||||
binaries_t.have_exp_win, binaries_t.have_exp_web,
|
||||
repr(pdir)
|
||||
)
|
||||
|
||||
return vs_root, binaries_t
|
||||
|
||||
_cache_pdir_vswhere_kind = {}
|
||||
|
||||
def msvc_version_pdir_vswhere_kind(msvc_version, pdir, detect_t):
|
||||
global _cache_pdir_vswhere_kind
|
||||
|
||||
vc_dir = os.path.normcase(os.path.normpath(pdir))
|
||||
cache_key = (msvc_version, vc_dir)
|
||||
|
||||
rval = _cache_pdir_vswhere_kind.get(cache_key)
|
||||
if rval is not None:
|
||||
debug('cache=%s', repr(rval))
|
||||
return rval
|
||||
|
||||
extended = {}
|
||||
|
||||
prefix, suffix = Util.get_msvc_version_prefix_suffix(msvc_version)
|
||||
|
||||
vs_root, binaries_t = _pdir_detect_binaries(pdir, detect_t)
|
||||
|
||||
if binaries_t.have_dev:
|
||||
kind = VCVER_KIND_DEVELOP
|
||||
elif binaries_t.have_exp:
|
||||
kind = VCVER_KIND_EXPRESS
|
||||
else:
|
||||
kind = VCVER_KIND_CMDLINE
|
||||
|
||||
skip = False
|
||||
save = False
|
||||
|
||||
if suffix != 'Exp' and kind == VCVER_KIND_EXPRESS:
|
||||
skip = True
|
||||
save = USE_EXPRESS_FOR_NONEXPRESS
|
||||
elif suffix == 'Exp' and kind != VCVER_KIND_EXPRESS:
|
||||
skip = True
|
||||
|
||||
kind_t = VCVER_DETECT_KIND(
|
||||
skip=skip,
|
||||
save=save,
|
||||
kind=kind,
|
||||
binaries_t=binaries_t,
|
||||
extended=extended,
|
||||
)
|
||||
|
||||
debug(
|
||||
'skip=%s, save=%s, kind=%s, msvc_version=%s, pdir=%s',
|
||||
kind_t.skip, kind_t.save, repr(VCVER_KIND_STR[kind_t.kind]),
|
||||
repr(msvc_version), repr(pdir)
|
||||
)
|
||||
|
||||
_cache_pdir_vswhere_kind[cache_key] = kind_t
|
||||
|
||||
return kind_t
|
||||
|
||||
# VS2015 buildtools batch file call detection
|
||||
# vs2015 buildtools do not support sdk_version or UWP arguments
|
||||
|
||||
_VS2015BT_PATH = r'..\Microsoft Visual C++ Build Tools\vcbuildtools.bat'
|
||||
|
||||
_VS2015BT_REGEX_STR = ''.join([
|
||||
r'^\s*if\s+exist\s+',
|
||||
re.escape(fr'"%~dp0..\{_VS2015BT_PATH}"'),
|
||||
r'\s+goto\s+setup_buildsku\s*$',
|
||||
])
|
||||
|
||||
_VS2015BT_VCVARS_BUILDTOOLS = re.compile(_VS2015BT_REGEX_STR, re.IGNORECASE)
|
||||
_VS2015BT_VCVARS_STOP = re.compile(r'^\s*[:]Setup_VS\s*$', re.IGNORECASE)
|
||||
|
||||
def _vs_buildtools_2015_vcvars(vcvars_file):
|
||||
have_buildtools_vcvars = False
|
||||
with open(vcvars_file) as fh:
|
||||
for line in fh:
|
||||
if _VS2015BT_VCVARS_BUILDTOOLS.match(line):
|
||||
have_buildtools_vcvars = True
|
||||
break
|
||||
if _VS2015BT_VCVARS_STOP.match(line):
|
||||
break
|
||||
return have_buildtools_vcvars
|
||||
|
||||
def _vs_buildtools_2015(vs_root, vc_dir):
|
||||
|
||||
is_btdispatch = False
|
||||
|
||||
do_once = True
|
||||
while do_once:
|
||||
do_once = False
|
||||
|
||||
buildtools_file = os.path.join(vs_root, _VS2015BT_PATH)
|
||||
have_buildtools = os.path.exists(buildtools_file)
|
||||
debug('have_buildtools=%s', have_buildtools)
|
||||
if not have_buildtools:
|
||||
break
|
||||
|
||||
vcvars_file = os.path.join(vc_dir, 'vcvarsall.bat')
|
||||
have_vcvars = os.path.exists(vcvars_file)
|
||||
debug('have_vcvars=%s', have_vcvars)
|
||||
if not have_vcvars:
|
||||
break
|
||||
|
||||
have_buildtools_vcvars = _vs_buildtools_2015_vcvars(vcvars_file)
|
||||
debug('have_buildtools_vcvars=%s', have_buildtools_vcvars)
|
||||
if not have_buildtools_vcvars:
|
||||
break
|
||||
|
||||
is_btdispatch = True
|
||||
|
||||
debug('is_btdispatch=%s', is_btdispatch)
|
||||
return is_btdispatch
|
||||
|
||||
_VS2015EXP_VCVARS_LIBPATH = re.compile(
|
||||
''.join([
|
||||
r'^\s*\@if\s+exist\s+\"\%VCINSTALLDIR\%LIB\\store\\(amd64|arm)"\s+',
|
||||
r'set (LIB|LIBPATH)=\%VCINSTALLDIR\%LIB\\store\\(amd64|arm);.*\%(LIB|LIBPATH)\%\s*$'
|
||||
]),
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
_VS2015EXP_VCVARS_STOP = re.compile(r'^\s*[:]GetVSCommonToolsDir\s*$', re.IGNORECASE)
|
||||
|
||||
def _vs_express_2015_vcvars(vcvars_file):
|
||||
n_libpath = 0
|
||||
with open(vcvars_file) as fh:
|
||||
for line in fh:
|
||||
if _VS2015EXP_VCVARS_LIBPATH.match(line):
|
||||
n_libpath += 1
|
||||
elif _VS2015EXP_VCVARS_STOP.match(line):
|
||||
break
|
||||
have_uwp_fix = n_libpath >= 2
|
||||
return have_uwp_fix
|
||||
|
||||
def _vs_express_2015(pdir):
|
||||
|
||||
have_uwp_amd64 = False
|
||||
have_uwp_arm = False
|
||||
|
||||
vcvars_file = os.path.join(pdir, r'vcvarsall.bat')
|
||||
if os.path.exists(vcvars_file):
|
||||
|
||||
vcvars_file = os.path.join(pdir, r'bin\x86_amd64\vcvarsx86_amd64.bat')
|
||||
if os.path.exists(vcvars_file):
|
||||
have_uwp_fix = _vs_express_2015_vcvars(vcvars_file)
|
||||
if have_uwp_fix:
|
||||
have_uwp_amd64 = True
|
||||
|
||||
vcvars_file = os.path.join(pdir, r'bin\x86_arm\vcvarsx86_arm.bat')
|
||||
if os.path.exists(vcvars_file):
|
||||
have_uwp_fix = _vs_express_2015_vcvars(vcvars_file)
|
||||
if have_uwp_fix:
|
||||
have_uwp_arm = True
|
||||
|
||||
debug('have_uwp_amd64=%s, have_uwp_arm=%s', have_uwp_amd64, have_uwp_arm)
|
||||
return have_uwp_amd64, have_uwp_arm
|
||||
|
||||
# winsdk installed 2010 [7.1], 2008 [7.0, 6.1] folders
|
||||
|
||||
_REGISTRY_WINSDK_VERSIONS = {'10.0', '9.0'}
|
||||
|
||||
_cache_pdir_registry_winsdk = {}
|
||||
|
||||
def _msvc_version_pdir_registry_winsdk(msvc_version, pdir):
|
||||
global _cache_pdir_registry_winsdk
|
||||
|
||||
# detect winsdk-only installations
|
||||
#
|
||||
# registry keys:
|
||||
# [prefix]\VisualStudio\SxS\VS7\10.0 <undefined>
|
||||
# [prefix]\VisualStudio\SxS\VC7\10.0 product directory
|
||||
# [prefix]\VisualStudio\SxS\VS7\9.0 <undefined>
|
||||
# [prefix]\VisualStudio\SxS\VC7\9.0 product directory
|
||||
#
|
||||
# winsdk notes:
|
||||
# - winsdk installs do not define the common tools env var
|
||||
# - the product dir is detected but the vcvars batch files will fail
|
||||
# - regular installations populate the VS7 registry keys
|
||||
#
|
||||
|
||||
vc_dir = os.path.normcase(os.path.normpath(pdir))
|
||||
cache_key = (msvc_version, vc_dir)
|
||||
|
||||
rval = _cache_pdir_registry_winsdk.get(cache_key)
|
||||
if rval is not None:
|
||||
debug('cache=%s', repr(rval))
|
||||
return rval
|
||||
|
||||
if msvc_version not in _REGISTRY_WINSDK_VERSIONS:
|
||||
|
||||
is_sdk = False
|
||||
|
||||
debug('is_sdk=%s, msvc_version=%s', is_sdk, repr(msvc_version))
|
||||
|
||||
else:
|
||||
|
||||
vc_dir = os.path.normcase(os.path.normpath(pdir))
|
||||
|
||||
vc_suffix = Registry.vstudio_sxs_vc7(msvc_version)
|
||||
vc_qresults = [record[0] for record in Registry.microsoft_query_paths(vc_suffix)]
|
||||
vc_root = os.path.normcase(os.path.normpath(vc_qresults[0])) if vc_qresults else None
|
||||
|
||||
if vc_dir != vc_root:
|
||||
# registry vc path is not the current pdir
|
||||
|
||||
is_sdk = False
|
||||
|
||||
debug(
|
||||
'is_sdk=%s, msvc_version=%s, pdir=%s, vc_root=%s',
|
||||
is_sdk, repr(msvc_version), repr(vc_dir), repr(vc_root)
|
||||
)
|
||||
|
||||
else:
|
||||
# registry vc path is the current pdir
|
||||
|
||||
vs_suffix = Registry.vstudio_sxs_vs7(msvc_version)
|
||||
vs_qresults = [record[0] for record in Registry.microsoft_query_paths(vs_suffix)]
|
||||
vs_root = vs_qresults[0] if vs_qresults else None
|
||||
|
||||
is_sdk = bool(not vs_root and vc_root)
|
||||
|
||||
debug(
|
||||
'is_sdk=%s, msvc_version=%s, vs_root=%s, vc_root=%s',
|
||||
is_sdk, repr(msvc_version), repr(vs_root), repr(vc_root)
|
||||
)
|
||||
|
||||
_cache_pdir_registry_winsdk[cache_key] = is_sdk
|
||||
|
||||
return is_sdk
|
||||
|
||||
_cache_pdir_registry_kind = {}
|
||||
|
||||
def msvc_version_pdir_registry_kind(msvc_version, pdir, detect_t, is_vcforpython=False):
|
||||
global _cache_pdir_registry_kind
|
||||
|
||||
vc_dir = os.path.normcase(os.path.normpath(pdir))
|
||||
cache_key = (msvc_version, vc_dir)
|
||||
|
||||
rval = _cache_pdir_registry_kind.get(cache_key)
|
||||
if rval is not None:
|
||||
debug('cache=%s', repr(rval))
|
||||
return rval
|
||||
|
||||
extended = {}
|
||||
|
||||
prefix, suffix = Util.get_msvc_version_prefix_suffix(msvc_version)
|
||||
|
||||
vs_root, binaries_t = _pdir_detect_binaries(pdir, detect_t)
|
||||
|
||||
if binaries_t.have_dev:
|
||||
kind = VCVER_KIND_DEVELOP
|
||||
elif binaries_t.have_exp:
|
||||
kind = VCVER_KIND_EXPRESS
|
||||
elif msvc_version == '14.0' and _vs_buildtools_2015(vs_root, pdir):
|
||||
kind = VCVER_KIND_BTDISPATCH
|
||||
elif msvc_version == '9.0' and is_vcforpython:
|
||||
kind = VCVER_KIND_VCFORPYTHON
|
||||
elif binaries_t.have_exp_win:
|
||||
kind = VCVER_KIND_EXPRESS_WIN
|
||||
elif binaries_t.have_exp_web:
|
||||
kind = VCVER_KIND_EXPRESS_WEB
|
||||
elif _msvc_version_pdir_registry_winsdk(msvc_version, pdir):
|
||||
kind = VCVER_KIND_SDK
|
||||
else:
|
||||
kind = VCVER_KIND_CMDLINE
|
||||
|
||||
skip = False
|
||||
save = False
|
||||
|
||||
if kind in (VCVER_KIND_EXPRESS_WIN, VCVER_KIND_EXPRESS_WEB, VCVER_KIND_SDK):
|
||||
skip = True
|
||||
elif suffix != 'Exp' and kind == VCVER_KIND_EXPRESS:
|
||||
skip = True
|
||||
save = USE_EXPRESS_FOR_NONEXPRESS
|
||||
elif suffix == 'Exp' and kind != VCVER_KIND_EXPRESS:
|
||||
skip = True
|
||||
|
||||
if prefix == '14.0' and kind == VCVER_KIND_EXPRESS:
|
||||
have_uwp_amd64, have_uwp_arm = _vs_express_2015(pdir)
|
||||
uwp_is_supported = {
|
||||
'x86': True,
|
||||
'amd64': have_uwp_amd64,
|
||||
'arm': have_uwp_arm,
|
||||
}
|
||||
extended['uwp_is_supported'] = uwp_is_supported
|
||||
|
||||
kind_t = VCVER_DETECT_KIND(
|
||||
skip=skip,
|
||||
save=save,
|
||||
kind=kind,
|
||||
binaries_t=binaries_t,
|
||||
extended=extended,
|
||||
)
|
||||
|
||||
debug(
|
||||
'skip=%s, save=%s, kind=%s, msvc_version=%s, pdir=%s',
|
||||
kind_t.skip, kind_t.save, repr(VCVER_KIND_STR[kind_t.kind]),
|
||||
repr(msvc_version), repr(pdir)
|
||||
)
|
||||
|
||||
_cache_pdir_registry_kind[cache_key] = kind_t
|
||||
|
||||
return kind_t
|
||||
|
||||
# queries
|
||||
|
||||
def get_msvc_version_kind(msvc_version, env=None):
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
kind_str = VCVER_KIND_STR[kind_t.kind]
|
||||
debug(
|
||||
'kind=%s, kind_str=%s, msvc_version=%s',
|
||||
repr(kind_t.kind), repr(kind_str), repr(msvc_version)
|
||||
)
|
||||
return (kind_t.kind, kind_str)
|
||||
|
||||
def msvc_version_sdk_version_is_supported(msvc_version, env=None):
|
||||
|
||||
vernum = float(Util.get_msvc_version_prefix(msvc_version))
|
||||
vernum_int = int(vernum * 10)
|
||||
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
|
||||
if vernum_int >= 141:
|
||||
# VS2017 and later
|
||||
is_supported = True
|
||||
elif vernum_int == 140:
|
||||
# VS2015:
|
||||
# True: Develop, CmdLine
|
||||
# False: Express, BTDispatch
|
||||
is_supported = True
|
||||
if kind_t.kind == VCVER_KIND_EXPRESS:
|
||||
is_supported = False
|
||||
elif kind_t.kind == VCVER_KIND_BTDISPATCH:
|
||||
is_supported = False
|
||||
else:
|
||||
# VS2013 and earlier
|
||||
is_supported = False
|
||||
|
||||
debug(
|
||||
'is_supported=%s, msvc_version=%s, kind=%s',
|
||||
is_supported, repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind])
|
||||
)
|
||||
return is_supported
|
||||
|
||||
def msvc_version_uwp_is_supported(msvc_version, target_arch=None, env=None):
|
||||
|
||||
vernum = float(Util.get_msvc_version_prefix(msvc_version))
|
||||
vernum_int = int(vernum * 10)
|
||||
|
||||
kind_t = _msvc_version_kind_lookup(msvc_version, env)
|
||||
|
||||
is_target = False
|
||||
|
||||
if vernum_int >= 141:
|
||||
# VS2017 and later
|
||||
is_supported = True
|
||||
elif vernum_int == 140:
|
||||
# VS2015:
|
||||
# True: Develop, CmdLine
|
||||
# Maybe: Express
|
||||
# False: BTDispatch
|
||||
is_supported = True
|
||||
if kind_t.kind == VCVER_KIND_EXPRESS:
|
||||
uwp_is_supported = kind_t.extended.get('uwp_is_supported', {})
|
||||
is_supported = uwp_is_supported.get(target_arch, True)
|
||||
is_target = True
|
||||
elif kind_t.kind == VCVER_KIND_BTDISPATCH:
|
||||
is_supported = False
|
||||
else:
|
||||
# VS2013 and earlier
|
||||
is_supported = False
|
||||
|
||||
debug(
|
||||
'is_supported=%s, is_target=%s, msvc_version=%s, kind=%s, target_arch=%s',
|
||||
is_supported, is_target, repr(msvc_version), repr(VCVER_KIND_STR[kind_t.kind]), repr(target_arch)
|
||||
)
|
||||
|
||||
return is_supported, is_target
|
||||
|
||||
# reset cache
|
||||
|
||||
def reset() -> None:
|
||||
global _cache_vcver_kind_map
|
||||
global _cache_pdir_vswhere_kind
|
||||
global _cache_pdir_registry_kind
|
||||
global _cache_pdir_registry_winsdk
|
||||
|
||||
debug('')
|
||||
|
||||
_cache_vcver_kind_map = {}
|
||||
_cache_pdir_vswhere_kind = {}
|
||||
_cache_pdir_registry_kind = {}
|
||||
_cache_pdir_registry_winsdk = {}
|
|
@ -38,6 +38,10 @@ from collections import (
|
|||
namedtuple,
|
||||
)
|
||||
|
||||
from contextlib import (
|
||||
contextmanager,
|
||||
)
|
||||
|
||||
import SCons.Warnings
|
||||
|
||||
from ..common import (
|
||||
|
@ -215,6 +219,18 @@ def msvc_notfound_handler(env, msg):
|
|||
else:
|
||||
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
|
||||
|
||||
@contextmanager
|
||||
def msvc_notfound_policy_contextmanager(MSVC_NOTFOUND_POLICY=None):
|
||||
""" Temporarily change the MSVC not found policy within a context.
|
||||
|
||||
Args:
|
||||
MSVC_NOTFOUND_POLICY:
|
||||
string representing the policy behavior
|
||||
when MSVC is not found or None
|
||||
"""
|
||||
prev_policy = msvc_set_notfound_policy(MSVC_NOTFOUND_POLICY)
|
||||
yield
|
||||
msvc_set_notfound_policy(prev_policy)
|
||||
|
||||
def _msvc_scripterror_policy_lookup(symbol):
|
||||
|
||||
|
@ -299,3 +315,16 @@ def msvc_scripterror_handler(env, msg):
|
|||
else:
|
||||
SCons.Warnings.warn(MSVCScriptExecutionWarning, msg)
|
||||
|
||||
@contextmanager
|
||||
def msvc_scripterror_policy_contextmanager(MSVC_SCRIPTERROR_POLICY=None):
|
||||
""" Temporarily change the msvc batch execution errors policy within a context.
|
||||
|
||||
Args:
|
||||
MSVC_SCRIPTERROR_POLICY:
|
||||
string representing the policy behavior
|
||||
when msvc batch file execution errors are detected or None
|
||||
"""
|
||||
prev_policy = msvc_set_scripterror_policy(MSVC_SCRIPTERROR_POLICY)
|
||||
yield
|
||||
msvc_set_scripterror_policy(prev_policy)
|
||||
|
|
@ -110,6 +110,9 @@ def windows_kit_query_paths(version):
|
|||
q = windows_kits(version)
|
||||
return microsoft_query_paths(q)
|
||||
|
||||
def vstudio_sxs_vs7(version):
|
||||
return '\\'.join([r'VisualStudio\SxS\VS7', version])
|
||||
|
||||
def vstudio_sxs_vc7(version):
|
||||
return '\\'.join([r'VisualStudio\SxS\VC7', version])
|
||||
|
|
@ -42,6 +42,7 @@ from . import Util
|
|||
from . import Config
|
||||
from . import Registry
|
||||
from . import WinSDK
|
||||
from . import Kind
|
||||
|
||||
from .Exceptions import (
|
||||
MSVCInternalError,
|
||||
|
@ -172,6 +173,12 @@ MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
|
|||
'vs_def',
|
||||
])
|
||||
|
||||
TOOLSET_VERSION_ARGS_DEFINITION = namedtuple('ToolsetVersionArgsDefinition', [
|
||||
'version', # full version (e.g., '14.1Exp', '14.32.31326')
|
||||
'vc_buildtools_def',
|
||||
'is_user',
|
||||
])
|
||||
|
||||
def _msvc_version(version):
|
||||
|
||||
verstr = Util.get_msvc_version_prefix(version)
|
||||
|
@ -184,19 +191,22 @@ def _msvc_version(version):
|
|||
|
||||
return version_args
|
||||
|
||||
def _toolset_version(version):
|
||||
def _toolset_version(version, is_user=False):
|
||||
|
||||
verstr = Util.get_msvc_version_prefix(version)
|
||||
vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
|
||||
vc_series = Util.get_msvc_version_prefix(version)
|
||||
|
||||
version_args = MSVC_VERSION_ARGS_DEFINITION(
|
||||
vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL[vc_series]
|
||||
vc_buildtools_def = Config.VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries]
|
||||
|
||||
version_args = TOOLSET_VERSION_ARGS_DEFINITION(
|
||||
version = version,
|
||||
vs_def = vs_def,
|
||||
vc_buildtools_def = vc_buildtools_def,
|
||||
is_user = is_user,
|
||||
)
|
||||
|
||||
return version_args
|
||||
|
||||
def _msvc_script_argument_uwp(env, msvc, arglist):
|
||||
def _msvc_script_argument_uwp(env, msvc, arglist, target_arch):
|
||||
|
||||
uwp_app = env['MSVC_UWP_APP']
|
||||
debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
|
||||
|
@ -207,14 +217,31 @@ def _msvc_script_argument_uwp(env, msvc, arglist):
|
|||
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:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: msvc version constraint: %s < %s VS2015',
|
||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
||||
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
|
||||
repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
|
||||
repr(uwp_app), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
raise MSVCArgumentError(err_msg)
|
||||
|
||||
is_supported, is_target = Kind.msvc_version_uwp_is_supported(msvc.version, target_arch, env)
|
||||
if not is_supported:
|
||||
_, kind_str = Kind.get_msvc_version_kind(msvc.version)
|
||||
debug(
|
||||
'invalid: msvc_version constraint: %s %s %s',
|
||||
repr(msvc.version), repr(kind_str), repr(target_arch)
|
||||
)
|
||||
if is_target and target_arch:
|
||||
err_msg = "MSVC_UWP_APP ({}) TARGET_ARCH ({}) is not supported for MSVC_VERSION {} ({})".format(
|
||||
repr(uwp_app), repr(target_arch), repr(msvc.version), repr(kind_str)
|
||||
)
|
||||
else:
|
||||
err_msg = "MSVC_UWP_APP ({}) is not supported for MSVC_VERSION {} ({})".format(
|
||||
repr(uwp_app), repr(msvc.version), repr(kind_str)
|
||||
)
|
||||
raise MSVCArgumentError(err_msg)
|
||||
|
||||
|
@ -250,16 +277,24 @@ def _user_script_argument_uwp(env, uwp, user_argstr) -> bool:
|
|||
|
||||
raise MSVCArgumentError(err_msg)
|
||||
|
||||
def _msvc_script_argument_sdk_constraints(msvc, sdk_version):
|
||||
def _msvc_script_argument_sdk_constraints(msvc, sdk_version, env):
|
||||
|
||||
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: msvc_version constraint: %s < %s VS2015',
|
||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
||||
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
|
||||
repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
|
||||
repr(sdk_version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env):
|
||||
_, kind_str = Kind.get_msvc_version_kind(msvc.version)
|
||||
debug('invalid: msvc_version constraint: %s %s', repr(msvc.version), repr(kind_str))
|
||||
err_msg = "MSVC_SDK_VERSION ({}) is not supported for MSVC_VERSION {} ({})".format(
|
||||
repr(sdk_version), repr(msvc.version), repr(kind_str)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
|
@ -277,23 +312,23 @@ def _msvc_script_argument_sdk_platform_constraints(msvc, toolset, sdk_version, p
|
|||
|
||||
if sdk_version == '8.1' and platform_def.is_uwp:
|
||||
|
||||
vs_def = toolset.vs_def if toolset else msvc.vs_def
|
||||
vc_buildtools_def = toolset.vc_buildtools_def if toolset else msvc.vs_def.vc_buildtools_def
|
||||
|
||||
if vs_def.vc_buildtools_def.vc_version_numeric > VS2015.vc_buildtools_def.vc_version_numeric:
|
||||
if vc_buildtools_def.msvc_version_numeric > VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015',
|
||||
repr(vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
||||
repr(vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.msvc_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(
|
||||
if toolset and toolset.is_user:
|
||||
err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset {} MSVC_VERSION {} > {} VS2015".format(
|
||||
repr(sdk_version), repr(platform_def.vc_platform),
|
||||
repr(toolset.version), repr(VS2015.vc_buildtools_def.vc_version)
|
||||
repr(toolset.version), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
else:
|
||||
err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format(
|
||||
repr(sdk_version), repr(platform_def.vc_platform),
|
||||
repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
|
||||
repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
|
@ -310,7 +345,7 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist):
|
|||
if not sdk_version:
|
||||
return None
|
||||
|
||||
err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version)
|
||||
err_msg = _msvc_script_argument_sdk_constraints(msvc, sdk_version, env)
|
||||
if err_msg:
|
||||
raise MSVCArgumentError(err_msg)
|
||||
|
||||
|
@ -333,7 +368,10 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist):
|
|||
|
||||
def _msvc_script_default_sdk(env, msvc, platform_def, arglist, force_sdk: bool=False):
|
||||
|
||||
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
return None
|
||||
|
||||
if not Kind.msvc_version_sdk_version_is_supported(msvc.version, env):
|
||||
return None
|
||||
|
||||
sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
|
||||
|
@ -415,9 +453,10 @@ def _msvc_sxs_toolset_folder(msvc, 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)
|
||||
if key in _msvc_sxs_bugfix_folder:
|
||||
sxs_version = _msvc_sxs_bugfix_folder[key]
|
||||
for vc_buildseries_def in msvc.vs_def.vc_buildtools_def.vc_buildseries_list:
|
||||
key = (vc_buildseries_def.vc_version, sxs_folder)
|
||||
sxs_version = _msvc_sxs_bugfix_folder.get(key)
|
||||
if sxs_version:
|
||||
return sxs_folder, sxs_version
|
||||
|
||||
debug('sxs folder: ignore version=%s', repr(sxs_folder))
|
||||
|
@ -574,49 +613,60 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
|
|||
|
||||
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:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: msvc version constraint: %s < %s VS2017',
|
||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2017.vc_buildtools_def.vc_version_numeric)
|
||||
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2017.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
|
||||
repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
|
||||
repr(toolset_version), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
toolset_verstr = Util.get_msvc_version_prefix(toolset_version)
|
||||
toolset_series = Util.get_msvc_version_prefix(toolset_version)
|
||||
|
||||
if not toolset_verstr:
|
||||
if not toolset_series:
|
||||
debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
|
||||
err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
|
||||
repr(toolset_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
toolset_vernum = float(toolset_verstr)
|
||||
|
||||
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_verstr), repr(VS2015.vc_buildtools_def.vc_version)
|
||||
toolset_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(toolset_series)
|
||||
if not toolset_buildseries_def:
|
||||
debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
|
||||
err_msg = 'MSVC_TOOLSET_VERSION {} build series {} is not supported'.format(
|
||||
repr(toolset_version), repr(toolset_series)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
if toolset_vernum > msvc.vs_def.vc_buildtools_def.vc_version_numeric:
|
||||
toolset_buildtools_def = Config.VC_BUILDTOOLS_MAP[toolset_buildseries_def.vc_buildseries]
|
||||
|
||||
toolset_verstr = toolset_buildtools_def.msvc_version
|
||||
toolset_vernum = toolset_buildtools_def.msvc_version_numeric
|
||||
|
||||
if toolset_vernum < VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: toolset version constraint: %s < %s VS2015',
|
||||
repr(toolset_vernum), repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} < {} VS2015".format(
|
||||
repr(toolset_version), repr(toolset_verstr), repr(VS2015.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
if toolset_vernum > msvc.vs_def.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: toolset version constraint: toolset %s > %s msvc',
|
||||
repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric)
|
||||
repr(toolset_vernum), repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} > {} MSVC_VERSION".format(
|
||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} > {} MSVC_VERSION".format(
|
||||
repr(toolset_version), repr(toolset_verstr), repr(msvc.version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
if toolset_vernum == VS2015.vc_buildtools_def.vc_version_numeric:
|
||||
if toolset_vernum == VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
# tooset = 14.0
|
||||
if Util.is_toolset_full(toolset_version):
|
||||
if not Util.is_toolset_140(toolset_version):
|
||||
|
@ -624,7 +674,7 @@ def _msvc_script_argument_toolset_constraints(msvc, toolset_version):
|
|||
'invalid: toolset version 14.0 constraint: %s != 14.0',
|
||||
repr(toolset_version)
|
||||
)
|
||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset version {} != '14.0'".format(
|
||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: toolset msvc version {} != '14.0'".format(
|
||||
repr(toolset_version), repr(toolset_version)
|
||||
)
|
||||
return err_msg
|
||||
|
@ -688,7 +738,7 @@ def _msvc_script_argument_toolset(env, msvc, vc_dir, arglist):
|
|||
|
||||
def _msvc_script_default_toolset(env, msvc, vc_dir, arglist, force_toolset: bool=False):
|
||||
|
||||
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
|
||||
return None
|
||||
|
||||
toolset_default = _msvc_default_toolset(msvc, vc_dir)
|
||||
|
@ -729,26 +779,26 @@ def _user_script_argument_toolset(env, toolset_version, user_argstr):
|
|||
|
||||
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:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: msvc version constraint: %s < %s VS2017',
|
||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2017.vc_buildtools_def.vc_version_numeric)
|
||||
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2017.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
|
||||
repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.vc_version)
|
||||
repr(spectre_libs), repr(msvc.version), repr(VS2017.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
if toolset:
|
||||
if toolset.vs_def.vc_buildtools_def.vc_version_numeric < VS2017.vc_buildtools_def.vc_version_numeric:
|
||||
if toolset.vc_buildtools_def.msvc_version_numeric < VS2017.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: toolset version constraint: %s < %s VS2017',
|
||||
repr(toolset.vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2017.vc_buildtools_def.vc_version_numeric)
|
||||
repr(toolset.vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2017.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format(
|
||||
repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.vc_version)
|
||||
repr(spectre_libs), repr(toolset.version), repr(VS2017.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
return err_msg
|
||||
|
||||
|
@ -836,14 +886,14 @@ def _msvc_script_argument_user(env, msvc, arglist):
|
|||
if not script_args:
|
||||
return None
|
||||
|
||||
if msvc.vs_def.vc_buildtools_def.vc_version_numeric < VS2015.vc_buildtools_def.vc_version_numeric:
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version_numeric < VS2015.vc_buildtools_def.msvc_version_numeric:
|
||||
debug(
|
||||
'invalid: msvc version constraint: %s < %s VS2015',
|
||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
||||
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||
)
|
||||
err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
|
||||
repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.vc_version)
|
||||
repr(script_args), repr(msvc.version), repr(VS2015.vc_buildtools_def.msvc_version)
|
||||
)
|
||||
raise MSVCArgumentError(err_msg)
|
||||
|
||||
|
@ -873,7 +923,18 @@ def _msvc_process_construction_variables(env) -> bool:
|
|||
|
||||
return False
|
||||
|
||||
def msvc_script_arguments(env, version, vc_dir, arg):
|
||||
def msvc_script_arguments_has_uwp(env):
|
||||
|
||||
if not _msvc_process_construction_variables(env):
|
||||
return False
|
||||
|
||||
uwp_app = env.get('MSVC_UWP_APP')
|
||||
is_uwp = bool(uwp_app and uwp_app in _ARGUMENT_BOOLEAN_TRUE_LEGACY)
|
||||
|
||||
debug('is_uwp=%s', is_uwp)
|
||||
return is_uwp
|
||||
|
||||
def msvc_script_arguments(env, version, vc_dir, arg=None):
|
||||
|
||||
arguments = [arg] if arg else []
|
||||
|
||||
|
@ -889,10 +950,12 @@ def msvc_script_arguments(env, version, vc_dir, arg):
|
|||
|
||||
if _msvc_process_construction_variables(env):
|
||||
|
||||
target_arch = env.get('TARGET_ARCH')
|
||||
|
||||
# MSVC_UWP_APP
|
||||
|
||||
if 'MSVC_UWP_APP' in env:
|
||||
uwp = _msvc_script_argument_uwp(env, msvc, arglist)
|
||||
uwp = _msvc_script_argument_uwp(env, msvc, arglist, target_arch)
|
||||
else:
|
||||
uwp = None
|
||||
|
||||
|
@ -926,7 +989,7 @@ def msvc_script_arguments(env, version, vc_dir, arg):
|
|||
if user_toolset:
|
||||
toolset = None
|
||||
elif toolset_version:
|
||||
toolset = _toolset_version(toolset_version)
|
||||
toolset = _toolset_version(toolset_version, is_user=True)
|
||||
elif default_toolset:
|
||||
toolset = _toolset_version(default_toolset)
|
||||
else:
|
||||
|
@ -958,7 +1021,7 @@ def msvc_script_arguments(env, version, vc_dir, arg):
|
|||
if user_argstr:
|
||||
_user_script_argument_spectre(env, spectre, user_argstr)
|
||||
|
||||
if msvc.vs_def.vc_buildtools_def.vc_version == '14.0':
|
||||
if msvc.vs_def.vc_buildtools_def.msvc_version == '14.0':
|
||||
if user_uwp and sdk_version and len(arglist) == 2:
|
||||
# VS2015 toolset argument order issue: SDK store => store SDK
|
||||
arglist_reverse = True
|
||||
|
@ -1003,6 +1066,14 @@ def _msvc_toolset_versions_internal(msvc_version, vc_dir, full: bool=True, sxs:
|
|||
|
||||
return toolset_versions
|
||||
|
||||
def _msvc_version_toolsets_internal(msvc_version, vc_dir):
|
||||
|
||||
msvc = _msvc_version(msvc_version)
|
||||
|
||||
toolsets_sxs, toolsets_full = _msvc_version_toolsets(msvc, vc_dir)
|
||||
|
||||
return toolsets_sxs, toolsets_full
|
||||
|
||||
def _msvc_toolset_versions_spectre_internal(msvc_version, vc_dir):
|
||||
|
||||
msvc = _msvc_version(msvc_version)
|
|
@ -37,6 +37,15 @@ from ..common import debug
|
|||
|
||||
from . import Config
|
||||
|
||||
|
||||
# call _initialize method upon class definition completion
|
||||
|
||||
class AutoInitialize:
|
||||
def __init_subclass__(cls, **kwargs):
|
||||
super().__init_subclass__(**kwargs)
|
||||
if hasattr(cls, '_initialize') and callable(getattr(cls, '_initialize', None)):
|
||||
cls._initialize()
|
||||
|
||||
# path utilities
|
||||
|
||||
# windows drive specification (e.g., 'C:')
|
||||
|
@ -234,6 +243,26 @@ def get_msvc_version_prefix(version):
|
|||
rval = m.group('version')
|
||||
return rval
|
||||
|
||||
def get_msvc_version_prefix_suffix(version):
|
||||
"""
|
||||
Get the msvc version number prefix and suffix from a string.
|
||||
|
||||
Args:
|
||||
version: str
|
||||
version specification
|
||||
|
||||
Returns:
|
||||
(str, str): the msvc version prefix and suffix
|
||||
|
||||
"""
|
||||
prefix = suffix = ''
|
||||
if version:
|
||||
m = re_msvc_version.match(version)
|
||||
if m:
|
||||
prefix = m.group('msvc_version')
|
||||
suffix = m.group('suffix') if m.group('suffix') else ''
|
||||
return prefix, suffix
|
||||
|
||||
# toolset version query utilities
|
||||
|
||||
def is_toolset_full(toolset_version) -> bool:
|
||||
|
@ -325,12 +354,18 @@ _MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCExtendedVersionCo
|
|||
'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_suffix_rank', # msvc version component rank (0, 1)
|
||||
'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'))
|
||||
'msvc_buildtools', # msvc build tools
|
||||
'msvc_buildtools_num', # msvc build tools integer number
|
||||
'msvc_buildseries', # msvc build series
|
||||
'msvc_buildseries_num', # msvc build series floating point number
|
||||
'msvc_toolset_version', # msvc toolset version
|
||||
'msvc_toolset_comps', # msvc toolset version components
|
||||
'msvc_toolset_is_sxs', # msvc toolset version is sxs
|
||||
'version', # msvc version or msvc toolset version
|
||||
])
|
||||
|
||||
|
@ -355,11 +390,19 @@ def msvc_extended_version_components(version):
|
|||
|
||||
msvc_toolset_version = m.group('version')
|
||||
msvc_toolset_comps = tuple(msvc_toolset_version.split('.'))
|
||||
msvc_toolset_is_sxs = is_toolset_sxs(msvc_toolset_version)
|
||||
|
||||
msvc_verstr = get_msvc_version_prefix(msvc_toolset_version)
|
||||
if not msvc_verstr:
|
||||
vc_verstr = get_msvc_version_prefix(msvc_toolset_version)
|
||||
if not vc_verstr:
|
||||
return None
|
||||
|
||||
vc_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(vc_verstr)
|
||||
if not vc_buildseries_def:
|
||||
return None
|
||||
|
||||
vc_buildtools_def = Config.VC_BUILDTOOLS_MAP[vc_buildseries_def.vc_buildseries]
|
||||
|
||||
msvc_verstr = vc_buildtools_def.msvc_version
|
||||
msvc_suffix = m.group('suffix') if m.group('suffix') else ''
|
||||
msvc_version = msvc_verstr + msvc_suffix
|
||||
|
||||
|
@ -376,12 +419,18 @@ def msvc_extended_version_components(version):
|
|||
msvc_version = msvc_version,
|
||||
msvc_verstr = msvc_verstr,
|
||||
msvc_suffix = msvc_suffix,
|
||||
msvc_suffix_rank = 0 if not msvc_suffix else 1,
|
||||
msvc_vernum = msvc_vernum,
|
||||
msvc_major = msvc_major,
|
||||
msvc_minor = msvc_minor,
|
||||
msvc_comps = msvc_comps,
|
||||
msvc_buildtools = vc_buildtools_def.msvc_version,
|
||||
msvc_buildtools_num = vc_buildtools_def.msvc_version_numeric,
|
||||
msvc_buildseries = vc_buildseries_def.vc_version,
|
||||
msvc_buildseries_num = vc_buildseries_def.vc_version_numeric,
|
||||
msvc_toolset_version = msvc_toolset_version,
|
||||
msvc_toolset_comps = msvc_toolset_comps,
|
||||
msvc_toolset_is_sxs = msvc_toolset_is_sxs,
|
||||
version = version,
|
||||
)
|
||||
|
|
@ -40,6 +40,7 @@ from . import Exceptions # noqa: F401
|
|||
from . import Config # noqa: F401
|
||||
from . import Util # noqa: F401
|
||||
from . import Registry # noqa: F401
|
||||
from . import Kind # noqa: F401
|
||||
from . import SetupEnvDefault # noqa: F401
|
||||
from . import Policy # noqa: F401
|
||||
from . import WinSDK # noqa: F401
|
|
@ -27,16 +27,176 @@ Design Notes
|
|||
``MSCommon/vc.py`` and are available via the ``SCons.Tool.MSCommon`` namespace.
|
||||
|
||||
|
||||
MSVC Detection Priority
|
||||
=======================
|
||||
|
||||
For msvc version specifications without an 'Exp' suffix, an express
|
||||
installation is used only when no other installation is detected.
|
||||
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| Product | VCVer | Priority |
|
||||
+=========+=========+==========================================================+
|
||||
| VS2022 | 14.3 | Enterprise, Professional, Community, BuildTools |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2019 | 14.2 | Enterprise, Professional, Community, BuildTools |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2017 | 14.1 | Enterprise, Professional, Community, BuildTools, Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2017 | 14.1Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2015 | 14.0 | [Develop, BuildTools, CmdLine], Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2015 | 14.0Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2013 | 12.0 | Develop, Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2013 | 12.0Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2012 | 11.0 | Develop, Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2012 | 11.0Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2010 | 10.0 | Develop, Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2010 | 10.0Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2008 | 9.0 | Develop, VCForPython, Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2008 | 9.0Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2005 | 8.0 | Develop, Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2005 | 8.0Exp | Express |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2003 | 7.1 | Develop |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS2002 | 7.0 | Develop |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
| VS6.0 | 6.0 | Develop |
|
||||
+---------+---------+----------------------------------------------------------+
|
||||
|
||||
Legend:
|
||||
|
||||
Develop
|
||||
devenv.com or msdev.com is detected.
|
||||
|
||||
Express
|
||||
WDExpress.exe or VCExpress.exe is detected.
|
||||
|
||||
BuildTools [VS2015]
|
||||
The vcvarsall batch file dispatches to the buildtools batch file.
|
||||
|
||||
CmdLine [VS2015]
|
||||
Neither Develop, Express, or BuildTools.
|
||||
|
||||
VS2015 Edition Limitations
|
||||
==========================
|
||||
|
||||
VS2015 BuildTools
|
||||
-----------------
|
||||
|
||||
The VS2015 BuildTools stand-alone batch file does not support the ``sdk version`` argument.
|
||||
|
||||
The VS2015 BuildTools stand-alone batch file does not support the ``store`` argument.
|
||||
|
||||
These arguments appear to be silently ignored and likely would result in compiler
|
||||
and/or linker build failures.
|
||||
|
||||
The VS2015 BuildTools ``vcvarsall.bat`` batch file dispatches to the stand-alone buildtools
|
||||
batch file under certain circumstances. A fragment from the vcvarsall batch file is:
|
||||
|
||||
::
|
||||
|
||||
if exist "%~dp0..\common7\IDE\devenv.exe" goto setup_VS
|
||||
if exist "%~dp0..\common7\IDE\wdexpress.exe" goto setup_VS
|
||||
if exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto setup_buildsku
|
||||
|
||||
:setup_VS
|
||||
|
||||
...
|
||||
|
||||
:setup_buildsku
|
||||
if not exist "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" goto usage
|
||||
set CurrentDir=%CD%
|
||||
call "%~dp0..\..\Microsoft Visual C++ Build Tools\vcbuildtools.bat" %1 %2
|
||||
cd /d %CurrentDir%
|
||||
goto :eof
|
||||
|
||||
VS2015 Express
|
||||
--------------
|
||||
|
||||
The VS2015 Express batch file does not support the ``sdk version`` argument.
|
||||
|
||||
The VS2015 Express batch file does not support the ``store`` argument for the ``amd64`` and
|
||||
``arm`` target architectures
|
||||
|
||||
amd64 Target Architecture
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As installed, VS2015 Express does not support the ``store`` argument for the ``amd64`` target
|
||||
architecture. The generated ``store`` library paths include directories that do not exist.
|
||||
|
||||
The store library paths appear in two places in the ``vcvarsx86_amd64`` batch file:
|
||||
|
||||
::
|
||||
|
||||
:setstorelib
|
||||
@if exist "%VCINSTALLDIR%LIB\amd64\store" set LIB=%VCINSTALLDIR%LIB\amd64\store;%LIB%
|
||||
...
|
||||
:setstorelibpath
|
||||
@if exist "%VCINSTALLDIR%LIB\amd64\store" set LIBPATH=%VCINSTALLDIR%LIB\amd64\store;%LIBPATH%
|
||||
|
||||
The correct store library paths would be:
|
||||
|
||||
::
|
||||
|
||||
:setstorelib
|
||||
@if exist "%VCINSTALLDIR%LIB\store\amd64" set LIB=%VCINSTALLDIR%LIB\store\amd64;%LIB%
|
||||
...
|
||||
:setstorelibpath
|
||||
@if exist "%VCINSTALLDIR%LIB\store\amd64" set LIBPATH=%VCINSTALLDIR%LIB\store\amd64;%LIBPATH%
|
||||
|
||||
arm Target Architecture
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
As installed, VS2015 Express does not support the ``store`` argument for the ``arm`` target
|
||||
architecture. The generated ``store`` library paths include directories that do not exist.
|
||||
|
||||
The store library paths appear in two places in the ``vcvarsx86_arm`` batch file:
|
||||
|
||||
::
|
||||
|
||||
:setstorelib
|
||||
@if exist "%VCINSTALLDIR%LIB\ARM\store" set LIB=%VCINSTALLDIR%LIB\ARM\store;%LIB%
|
||||
...
|
||||
:setstorelibpath
|
||||
@if exist "%VCINSTALLDIR%LIB\ARM\store" set LIBPATH=%VCINSTALLDIR%LIB\ARM\store;%LIBPATH%
|
||||
|
||||
The correct store library paths would be file:
|
||||
|
||||
::
|
||||
|
||||
:setstorelib
|
||||
@if exist "%VCINSTALLDIR%LIB\store\ARM" set LIB=%VCINSTALLDIR%LIB\store\ARM;%LIB%
|
||||
...
|
||||
:setstorelibpath
|
||||
@if exist "%VCINSTALLDIR%LIB\store\ARM" set LIBPATH=%VCINSTALLDIR%LIB\store\ARM;%LIBPATH%
|
||||
|
||||
|
||||
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.
|
||||
``SetEnv.cmd`` batch files may result in build failures.
|
||||
|
||||
Typically, the reasons for build failures with SDK batch files are one, or both, of:
|
||||
|
||||
* The batch files require delayed expansion to be enabled which is not usually the Windows default.
|
||||
|
||||
* The batch files inspect environment variables that are not defined in the minimal subprocess
|
||||
environment in which the batch files are invoked.
|
||||
|
||||
* 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``.
|
||||
|
@ -47,20 +207,24 @@ The following issues are known to exist:
|
|||
Experimental Features
|
||||
=====================
|
||||
|
||||
msvc_query_version_toolset(version=None, prefer_newest=True)
|
||||
------------------------------------------------------------
|
||||
msvc_query_version_toolset(version=None, prefer_newest=True, vswhere_exe=None)
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
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.
|
||||
and is available via the ``SCons.Tool.MSCommon`` namespace.
|
||||
|
||||
This function takes a version specification or a toolset version specification, an optional product
|
||||
preference, and an optional vswhere executable location 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.4',
|
||||
'14.3',
|
||||
'14.2',
|
||||
'14.1',
|
||||
|
@ -90,6 +254,7 @@ Example usage:
|
|||
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'}
|
||||
|
@ -117,6 +282,7 @@ added to the batch file argument list. This is intended to make the cache more
|
|||
updates that may change the default toolset version and/or the default SDK version.
|
||||
|
||||
Example usage:
|
||||
|
||||
::
|
||||
|
||||
@echo Enabling scons cache ...
|
||||
|
@ -139,6 +305,7 @@ Enabling warnings to be produced for detected msvc batch file errors may provide
|
|||
for build failures. Refer to the documentation for details.
|
||||
|
||||
Change the default policy:
|
||||
|
||||
::
|
||||
|
||||
from SCons.Tool.MSCommon import msvc_set_scripterror_policy
|
||||
|
@ -146,6 +313,7 @@ Change the default 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')
|
||||
|
@ -171,6 +339,7 @@ On occasion, the raw vswhere output may prove useful especially if there are sus
|
|||
detection of installed msvc instances.
|
||||
|
||||
Windows command-line sample invocations:
|
||||
|
||||
::
|
||||
|
||||
@rem 64-Bit Windows
|
||||
|
@ -188,31 +357,37 @@ 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
|
||||
======= === === ======= =======
|
||||
+---------+---------+--------+---------+---------+
|
||||
| Product | UWP | SDK | Toolset | Spectre |
|
||||
+=========+=========+========+=========+=========+
|
||||
| VS2022 | X | X | X | X |
|
||||
+---------+---------+--------+---------+---------+
|
||||
| VS2019 | X | X | X | X |
|
||||
+---------+---------+--------+---------+---------+
|
||||
| VS2017 | X | X | X | X |
|
||||
+---------+---------+--------+---------+---------+
|
||||
| VS2015 | X [1] | X [2] | | |
|
||||
+---------+---------+--------+---------+---------+
|
||||
|
||||
Notes:
|
||||
|
||||
1) The BuildTools edition does not support the ``store`` argument. The Express edition
|
||||
supports the ``store`` argument for the ``x86`` target only.
|
||||
2) The ``sdk version`` argument is not supported in the BuildTools and Express editions.
|
||||
|
||||
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'``
|
||||
======== ====================================== ===================================================
|
||||
+----------+----------------------------------------+-----------------------------------------------------+
|
||||
| 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.**
|
||||
|
@ -244,6 +419,7 @@ 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
|
||||
|
@ -253,6 +429,7 @@ Local installation and summary test results:
|
|||
14.32.31326
|
||||
|
||||
Toolset version summary:
|
||||
|
||||
::
|
||||
|
||||
14.31.31103 Environment()
|
||||
|
@ -268,6 +445,7 @@ Toolset version summary:
|
|||
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
|
||||
|
@ -289,6 +467,7 @@ VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment:
|
|||
@echo SxS toolset to [VSInstallDir]\VC\MSVC\Tools\ directory.
|
||||
|
||||
VS2022 batch file fragment to determine the default toolset version:
|
||||
|
||||
::
|
||||
|
||||
@REM Add MSVC
|
||||
|
@ -315,75 +494,114 @@ Visual Studio Version Notes
|
|||
SDK Versions
|
||||
------------
|
||||
|
||||
==== ============
|
||||
SDK Format
|
||||
==== ============
|
||||
10.0 10.0.XXXXX.Y
|
||||
---- ------------
|
||||
8.1 8.1
|
||||
==== ============
|
||||
+------+-------------------+
|
||||
| SDK | Format |
|
||||
+======+===================+
|
||||
| 10.0 | 10.0.XXXXX.Y [1] |
|
||||
+------+-------------------+
|
||||
| 8.1 | 8.1 |
|
||||
+------+-------------------+
|
||||
|
||||
Notes:
|
||||
|
||||
1) The Windows 10 SDK version number is 10.0.20348.0 and earlier.
|
||||
|
||||
The Windows 11 SDK version number is 10.0.22000.194 and later.
|
||||
|
||||
BuildSeries Versions
|
||||
--------------------
|
||||
|
||||
+-------------+-------+-------+
|
||||
| BuildSeries | VCVER | CLVER |
|
||||
+=============+=======+=======+
|
||||
| 14.4 | 14.4X | 19.4 |
|
||||
+-------------+-------+-------+
|
||||
| 14.3 | 14.3X | 19.3 |
|
||||
+-------------+-------+-------+
|
||||
| 14.2 | 14.2X | 19.2 |
|
||||
+-------------+-------+-------+
|
||||
| 14.1 | 14.1X | 19.1 |
|
||||
+-------------+-------+-------+
|
||||
| 14.0 | 14.0 | 19.0 |
|
||||
+-------------+-------+-------+
|
||||
| 12.0 | 12.0 | 18.0 |
|
||||
+-------------+-------+-------+
|
||||
| 11.0 | 11.0 | 17.0 |
|
||||
+-------------+-------+-------+
|
||||
| 10.0 | 10.0 | 16.0 |
|
||||
+-------------+-------+-------+
|
||||
| 9.0 | 9.0 | 15.0 |
|
||||
+-------------+-------+-------+
|
||||
| 8.0 | 8.0 | 14.0 |
|
||||
+-------------+-------+-------+
|
||||
| 7.1 | 7.1 | 13.1 |
|
||||
+-------------+-------+-------+
|
||||
| 7.0 | 7.0 | 13.0 |
|
||||
+-------------+-------+-------+
|
||||
| 6.0 | 6.0 | 12.0 |
|
||||
+-------------+-------+-------+
|
||||
|
||||
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
|
||||
========== ===== ===== ========
|
||||
+------------+-------------+----------+
|
||||
| BuildTools | BuildSeries | MSVCRT |
|
||||
+============+=============+==========+
|
||||
| v143 | 14.4, 14.3 | 140/ucrt |
|
||||
+------------+-------------+----------+
|
||||
| v142 | 14.2 | 140/ucrt |
|
||||
+------------+-------------+----------+
|
||||
| v141 | 14.1 | 140/ucrt |
|
||||
+------------+-------------+----------+
|
||||
| v140 | 14.0 | 140/ucrt |
|
||||
+------------+-------------+----------+
|
||||
| v120 | 12.0 | 120 |
|
||||
+------------+-------------+----------+
|
||||
| v110 | 11.0 | 110 |
|
||||
+------------+-------------+----------+
|
||||
| v100 | 10.0 | 100 |
|
||||
+------------+-------------+----------+
|
||||
| v90 | 9.0 | 90 |
|
||||
+------------+-------------+----------+
|
||||
| v80 | 8.0 | 80 |
|
||||
+------------+-------------+----------+
|
||||
| v71 | 7.1 | 71 |
|
||||
+------------+-------------+----------+
|
||||
| v70 | 7.0 | 70 |
|
||||
+------------+-------------+----------+
|
||||
| v60 | 6.0 | 60 |
|
||||
+------------+-------------+----------+
|
||||
|
||||
Product Versions
|
||||
----------------
|
||||
|
||||
======== ===== ========= ======================
|
||||
Product VSVER SDK BuildTools
|
||||
======== ===== ========= ======================
|
||||
2022 17.0 10.0, 8.1 v143, v142, v141, v140
|
||||
-------- ----- --------- ----------------------
|
||||
2019 16.0 10.0, 8.1 v142, v141, 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
|
||||
======== ===== ========= ======================
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| Product | VSVER | SCons | SDK | BuildTools |
|
||||
+==========+=======+=======+===========+========================+
|
||||
| 2022 | 17.0 | 14.3 | 10.0, 8.1 | v143, v142, v141, v140 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2019 | 16.0 | 14.2 | 10.0, 8.1 | v142, v141, v140 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2017 | 15.0 | 14.1 | 10.0, 8.1 | v141, v140 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2015 | 14.0 | 14.0 | 10.0, 8.1 | v140 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2013 | 12.0 | 12.0 | | v120 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2012 | 11.0 | 11.0 | | v110 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2010 | 10.0 | 10.0 | | v100 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2008 | 9.0 | 9.0 | | v90 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2005 | 8.0 | 8.0 | | v80 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2003.NET | 7.1 | 7.1 | | v71 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 2002.NET | 7.0 | 7.0 | | v70 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
| 6.0 | 6.0 | 6.0 | | v60 |
|
||||
+----------+-------+-------+-----------+------------------------+
|
||||
|
||||
|
||||
SCons Implementation Notes
|
|
@ -45,6 +45,9 @@ from SCons.Tool.MSCommon.vc import ( # noqa: F401
|
|||
msvc_toolset_versions,
|
||||
msvc_toolset_versions_spectre,
|
||||
msvc_query_version_toolset,
|
||||
vswhere_register_executable,
|
||||
vswhere_get_executable,
|
||||
vswhere_freeze_executable,
|
||||
)
|
||||
|
||||
from SCons.Tool.MSCommon.vs import ( # noqa: F401
|
||||
|
@ -60,6 +63,8 @@ from .MSVC.Policy import ( # noqa: F401
|
|||
msvc_get_notfound_policy,
|
||||
msvc_set_scripterror_policy,
|
||||
msvc_get_scripterror_policy,
|
||||
msvc_notfound_policy_contextmanager,
|
||||
msvc_scripterror_policy_contextmanager,
|
||||
)
|
||||
|
||||
from .MSVC.Exceptions import ( # noqa: F401
|
||||
|
@ -78,7 +83,9 @@ from .vc import ( # noqa: F401
|
|||
MSVCUnsupportedHostArch,
|
||||
MSVCUnsupportedTargetArch,
|
||||
MSVCScriptNotFound,
|
||||
MSVCUseScriptError,
|
||||
MSVCUseSettingsError,
|
||||
VSWhereUserError,
|
||||
)
|
||||
|
||||
from .MSVC.Util import ( # noqa: F401
|
|
@ -372,7 +372,7 @@ def mssdk_setup_env(env):
|
|||
return
|
||||
msvs_version = env.subst(msvs_version)
|
||||
from . import vs
|
||||
msvs = vs.get_vs_by_version(msvs_version)
|
||||
msvs = vs.get_vs_by_version(msvs_version, env)
|
||||
debug('mssdk_setup_env:msvs is :%s', msvs)
|
||||
if not msvs:
|
||||
debug('mssdk_setup_env: no VS version detected, bailingout:%s', msvs)
|
File diff suppressed because it is too large
Load diff
|
@ -28,7 +28,6 @@ MS Compilers: detect Visual Studio and/or Visual C/C++
|
|||
import os
|
||||
|
||||
import SCons.Errors
|
||||
import SCons.Tool.MSCommon.vc
|
||||
import SCons.Util
|
||||
|
||||
from .common import (
|
||||
|
@ -40,6 +39,19 @@ from .common import (
|
|||
read_reg,
|
||||
)
|
||||
|
||||
from .vc import (
|
||||
find_vc_pdir,
|
||||
get_msvc_version_numeric,
|
||||
reset_installed_vcs,
|
||||
vswhere_freeze_env,
|
||||
)
|
||||
|
||||
|
||||
# Visual Studio express version policy when unqualified version is not installed:
|
||||
# True: use express version for unqualified version (e.g., use 12.0Exp for 12.0)
|
||||
# False: do not use express version for unqualified version
|
||||
_VSEXPRESS_USE_VERSTR = True
|
||||
|
||||
|
||||
class VisualStudio:
|
||||
"""
|
||||
|
@ -48,6 +60,9 @@ class VisualStudio:
|
|||
"""
|
||||
def __init__(self, version, **kw) -> None:
|
||||
self.version = version
|
||||
self.verstr = get_msvc_version_numeric(version)
|
||||
self.vernum = float(self.verstr)
|
||||
self.is_express = True if self.verstr != self.version else False
|
||||
kw['vc_version'] = kw.get('vc_version', version)
|
||||
kw['sdk_version'] = kw.get('sdk_version', version)
|
||||
self.__dict__.update(kw)
|
||||
|
@ -66,7 +81,7 @@ class VisualStudio:
|
|||
return batch_file
|
||||
|
||||
def find_vs_dir_by_vc(self, env):
|
||||
dir = SCons.Tool.MSCommon.vc.find_vc_pdir(env, self.vc_version)
|
||||
dir = find_vc_pdir(self.vc_version, env)
|
||||
if not dir:
|
||||
debug('no installed VC %s', self.vc_version)
|
||||
return None
|
||||
|
@ -428,6 +443,7 @@ InstalledVSMap = None
|
|||
def get_installed_visual_studios(env=None):
|
||||
global InstalledVSList
|
||||
global InstalledVSMap
|
||||
vswhere_freeze_env(env)
|
||||
if InstalledVSList is None:
|
||||
InstalledVSList = []
|
||||
InstalledVSMap = {}
|
||||
|
@ -436,12 +452,21 @@ def get_installed_visual_studios(env=None):
|
|||
if vs.get_executable(env):
|
||||
debug('found VS %s', vs.version)
|
||||
InstalledVSList.append(vs)
|
||||
if vs.is_express and vs.verstr not in InstalledVSMap:
|
||||
if _VSEXPRESS_USE_VERSTR:
|
||||
InstalledVSMap[vs.verstr] = vs
|
||||
InstalledVSMap[vs.version] = vs
|
||||
return InstalledVSList
|
||||
|
||||
def _get_installed_vss(env=None):
|
||||
get_installed_visual_studios(env)
|
||||
versions = list(InstalledVSMap.keys())
|
||||
return versions
|
||||
|
||||
def reset_installed_visual_studios() -> None:
|
||||
global InstalledVSList
|
||||
global InstalledVSMap
|
||||
debug('')
|
||||
InstalledVSList = None
|
||||
InstalledVSMap = None
|
||||
for vs in SupportedVSList:
|
||||
|
@ -449,7 +474,7 @@ def reset_installed_visual_studios() -> None:
|
|||
|
||||
# Need to clear installed VC's as well as they are used in finding
|
||||
# installed VS's
|
||||
SCons.Tool.MSCommon.vc.reset_installed_vcs()
|
||||
reset_installed_vcs()
|
||||
|
||||
|
||||
# We may be asked to update multiple construction environments with
|
||||
|
@ -487,7 +512,7 @@ def reset_installed_visual_studios() -> None:
|
|||
def msvs_exists(env=None) -> bool:
|
||||
return len(get_installed_visual_studios(env)) > 0
|
||||
|
||||
def get_vs_by_version(msvs):
|
||||
def get_vs_by_version(msvs, env=None):
|
||||
global InstalledVSMap
|
||||
global SupportedVSMap
|
||||
|
||||
|
@ -495,7 +520,7 @@ def get_vs_by_version(msvs):
|
|||
if msvs not in SupportedVSMap:
|
||||
msg = "Visual Studio version %s is not supported" % repr(msvs)
|
||||
raise SCons.Errors.UserError(msg)
|
||||
get_installed_visual_studios()
|
||||
get_installed_visual_studios(env)
|
||||
vs = InstalledVSMap.get(msvs)
|
||||
debug('InstalledVSMap:%s', InstalledVSMap)
|
||||
debug('found vs:%s', vs)
|
||||
|
@ -524,7 +549,7 @@ def get_default_version(env):
|
|||
"""
|
||||
if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']):
|
||||
# get all versions, and remember them for speed later
|
||||
versions = [vs.version for vs in get_installed_visual_studios()]
|
||||
versions = _get_installed_vss(env)
|
||||
env['MSVS'] = {'VERSIONS' : versions}
|
||||
else:
|
||||
versions = env['MSVS'].get('VERSIONS', [])
|
||||
|
@ -570,7 +595,7 @@ def merge_default_version(env) -> None:
|
|||
|
||||
# TODO: refers to versions and arch which aren't defined; called nowhere. Drop?
|
||||
def msvs_setup_env(env) -> None:
|
||||
msvs = get_vs_by_version(version)
|
||||
msvs = get_vs_by_version(version, env)
|
||||
if msvs is None:
|
||||
return
|
||||
batfilename = msvs.get_batch_file()
|
||||
|
@ -582,7 +607,7 @@ def msvs_setup_env(env) -> None:
|
|||
|
||||
vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE')
|
||||
|
||||
msvs_list = get_installed_visual_studios()
|
||||
msvs_list = get_installed_visual_studios(env)
|
||||
vscommonvarnames = [vs.common_tools_var for vs in msvs_list]
|
||||
save_ENV = env['ENV']
|
||||
nenv = normalize_env(env['ENV'],
|
||||
|
@ -597,11 +622,10 @@ def msvs_setup_env(env) -> None:
|
|||
for k, v in vars.items():
|
||||
env.PrependENVPath(k, v, delete_existing=1)
|
||||
|
||||
def query_versions():
|
||||
def query_versions(env=None):
|
||||
"""Query the system to get available versions of VS. A version is
|
||||
considered when a batfile is found."""
|
||||
msvs_list = get_installed_visual_studios()
|
||||
versions = [msvs.version for msvs in msvs_list]
|
||||
versions = _get_installed_vss(env)
|
||||
return versions
|
||||
|
||||
# Local Variables:
|
|
@ -251,7 +251,7 @@ class Tool:
|
|||
kw.update(call_kw)
|
||||
else:
|
||||
kw = self.init_kw
|
||||
env.Append(TOOLS=[self.name])
|
||||
env.AppendUnique(TOOLS=[self.name])
|
||||
if hasattr(self, 'options'):
|
||||
import SCons.Variables
|
||||
if 'options' not in env:
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue