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
|
digits of the signature. The prefix length used for directory
|
||||||
names can be changed by this script.
|
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"
|
__buildsys__ = "M1Dog2021"
|
||||||
|
|
||||||
__date__ = "Sun, 17 Mar 2024 17:33:54 -0700"
|
__date__ = "Sun, 07 Jul 2024 16:52:07 -0700"
|
||||||
|
|
||||||
__developer__ = "bdbaddog"
|
__developer__ = "bdbaddog"
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ import sys
|
||||||
# python compatibility check
|
# python compatibility check
|
||||||
if sys.version_info < (3, 6, 0):
|
if sys.version_info < (3, 6, 0):
|
||||||
msg = "scons: *** SCons version %s does not run under Python version %s.\n\
|
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.stderr.write(msg % (__version__, sys.version.split()[0]))
|
||||||
sys.exit(1)
|
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
|
kwargs['check'] = check
|
||||||
|
|
||||||
# TODO: Python version-compat stuff: remap/remove too-new args if needed
|
# 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')
|
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')
|
capture_output = kwargs.pop('capture_output')
|
||||||
if capture_output:
|
if capture_output:
|
||||||
kwargs['stdout'] = kwargs['stderr'] = PIPE
|
kwargs['stdout'] = kwargs['stderr'] = PIPE
|
||||||
|
@ -1454,16 +1454,6 @@ class FunctionAction(_ActionAction):
|
||||||
except TypeError:
|
except TypeError:
|
||||||
result.command=self.strfunction(target, source, env)
|
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
|
return result
|
||||||
finally:
|
finally:
|
||||||
# Break the cycle between the traceback object and this
|
# Break the cycle between the traceback object and this
|
|
@ -387,7 +387,7 @@ class BuilderBase:
|
||||||
target_scanner = None,
|
target_scanner = None,
|
||||||
source_scanner = None,
|
source_scanner = None,
|
||||||
emitter = None,
|
emitter = None,
|
||||||
multi: int = 0,
|
multi: bool = False,
|
||||||
env = None,
|
env = None,
|
||||||
single_source: bool = False,
|
single_source: bool = False,
|
||||||
name = None,
|
name = None,
|
|
@ -23,10 +23,10 @@
|
||||||
|
|
||||||
"""Code for debugging SCons internal things.
|
"""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
|
from SCons.Debug import caller_trace
|
||||||
caller_trace()
|
caller_trace()
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import atexit
|
import atexit
|
|
@ -291,7 +291,15 @@ def copy_func(dest, src, symlinks: bool=True) -> int:
|
||||||
|
|
||||||
elif os.path.islink(src):
|
elif os.path.islink(src):
|
||||||
if symlinks:
|
if symlinks:
|
||||||
os.symlink(os.readlink(src), dest)
|
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 0
|
||||||
|
|
||||||
return copy_func(dest, os.path.realpath(src))
|
return copy_func(dest, os.path.realpath(src))
|
|
@ -35,9 +35,9 @@ import os
|
||||||
import sys
|
import sys
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
from collections import UserDict, deque
|
from collections import UserDict, UserList, deque
|
||||||
from subprocess import PIPE, DEVNULL
|
from subprocess import PIPE, DEVNULL
|
||||||
from typing import Optional
|
from typing import Callable, Collection, Optional, Sequence, Union
|
||||||
|
|
||||||
import SCons.Action
|
import SCons.Action
|
||||||
import SCons.Builder
|
import SCons.Builder
|
||||||
|
@ -510,13 +510,6 @@ class BuilderDict(UserDict):
|
||||||
self.__setitem__(i, v)
|
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:
|
class SubstitutionEnvironment:
|
||||||
"""Base class for different flavors of construction environments.
|
"""Base class for different flavors of construction environments.
|
||||||
|
|
||||||
|
@ -587,26 +580,20 @@ class SubstitutionEnvironment:
|
||||||
return self._dict[key]
|
return self._dict[key]
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
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:
|
if key in self._special_set_keys:
|
||||||
self._special_set[key](self, key, value)
|
self._special_set[key](self, key, value)
|
||||||
else:
|
else:
|
||||||
# If we already have the entry, then it's obviously a valid
|
# Performance: since this is heavily used, try to avoid checking
|
||||||
# key and we don't need to check. If we do check, using a
|
# if the variable is valid unless necessary. bench/__setitem__.py
|
||||||
# global, pre-compiled regular expression directly is more
|
# times a bunch of different approaches. Based the most recent
|
||||||
# efficient than calling another function or a method.
|
# run, against Python 3.6-3.13(beta), the best we can do across
|
||||||
if key not in self._dict and not _is_valid_var.match(key):
|
# different combinations of actions is to use a membership test
|
||||||
raise UserError("Illegal construction variable `%s'" % key)
|
# 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
|
self._dict[key] = value
|
||||||
|
|
||||||
def get(self, key, default=None):
|
def get(self, key, default=None):
|
||||||
|
@ -881,11 +868,11 @@ class SubstitutionEnvironment:
|
||||||
'RPATH' : [],
|
'RPATH' : [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def do_parse(arg) -> None:
|
def do_parse(arg: Union[str, Sequence]) -> None:
|
||||||
# if arg is a sequence, recurse with each element
|
|
||||||
if not arg:
|
if not arg:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# if arg is a sequence, recurse with each element
|
||||||
if not is_String(arg):
|
if not is_String(arg):
|
||||||
for t in arg: do_parse(t)
|
for t in arg: do_parse(t)
|
||||||
return
|
return
|
||||||
|
@ -902,7 +889,7 @@ class SubstitutionEnvironment:
|
||||||
else:
|
else:
|
||||||
mapping['CPPDEFINES'].append([t[0], '='.join(t[1:])])
|
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
|
# This tries to strike a balance between checking for all possible
|
||||||
# flags and keeping the logic to a finite size, so it doesn't
|
# 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
|
# 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
|
append_next_arg_to = None # for multi-word args
|
||||||
for arg in params:
|
for arg in params:
|
||||||
if append_next_arg_to:
|
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':
|
if append_next_arg_to == 'CPPDEFINES':
|
||||||
append_define(arg)
|
append_define(arg)
|
||||||
elif append_next_arg_to == '-include':
|
elif append_next_arg_to == '-include':
|
||||||
|
@ -1022,6 +1011,8 @@ class SubstitutionEnvironment:
|
||||||
else:
|
else:
|
||||||
key = 'CFLAGS'
|
key = 'CFLAGS'
|
||||||
mapping[key].append(arg)
|
mapping[key].append(arg)
|
||||||
|
elif arg.startswith('-stdlib='):
|
||||||
|
mapping['CXXFLAGS'].append(arg)
|
||||||
elif arg[0] == '+':
|
elif arg[0] == '+':
|
||||||
mapping['CCFLAGS'].append(arg)
|
mapping['CCFLAGS'].append(arg)
|
||||||
mapping['LINKFLAGS'].append(arg)
|
mapping['LINKFLAGS'].append(arg)
|
||||||
|
@ -1250,9 +1241,11 @@ class Base(SubstitutionEnvironment):
|
||||||
SCons.Tool.Initializers(self)
|
SCons.Tool.Initializers(self)
|
||||||
|
|
||||||
if tools is None:
|
if tools is None:
|
||||||
tools = self._dict.get('TOOLS', None)
|
tools = self._dict.get('TOOLS', ['default'])
|
||||||
if tools is None:
|
else:
|
||||||
tools = ['default']
|
# 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)
|
apply_tools(self, tools, toolpath)
|
||||||
|
|
||||||
# Now restore the passed-in and customized variables
|
# Now restore the passed-in and customized variables
|
||||||
|
@ -1562,16 +1555,28 @@ class Base(SubstitutionEnvironment):
|
||||||
self._dict[key] = dk + val
|
self._dict[key] = dk + val
|
||||||
self.scanner_map_delete(kw)
|
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.
|
"""Return a copy of a construction Environment.
|
||||||
|
|
||||||
The copy is like a Python "deep copy"--that is, independent
|
The copy is like a Python "deep copy": independent copies are made
|
||||||
copies are made recursively of each objects--except that
|
recursively of each object, except that a reference is copied when
|
||||||
a reference is copied when an object is not deep-copyable
|
an object is not deep-copyable (like a function). There are no
|
||||||
(like a function). There are no references to any mutable
|
references to any mutable objects in the original environment.
|
||||||
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', {})
|
builders = self._dict.get('BUILDERS', {})
|
||||||
|
|
||||||
clone = copy.copy(self)
|
clone = copy.copy(self)
|
||||||
|
@ -1597,6 +1602,8 @@ class Base(SubstitutionEnvironment):
|
||||||
for key, value in kw.items():
|
for key, value in kw.items():
|
||||||
new[key] = SCons.Subst.scons_subst_once(value, self, key)
|
new[key] = SCons.Subst.scons_subst_once(value, self, key)
|
||||||
clone.Replace(**new)
|
clone.Replace(**new)
|
||||||
|
if variables:
|
||||||
|
variables.Update(clone)
|
||||||
|
|
||||||
apply_tools(clone, tools, toolpath)
|
apply_tools(clone, tools, toolpath)
|
||||||
|
|
||||||
|
@ -1687,17 +1694,23 @@ class Base(SubstitutionEnvironment):
|
||||||
return dlist
|
return dlist
|
||||||
|
|
||||||
|
|
||||||
def Dump(self, key=None, format: str='pretty'):
|
def Dump(self, key: Optional[str] = None, format: str = 'pretty') -> str:
|
||||||
""" Return construction variables serialized to a string.
|
""" 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:
|
Args:
|
||||||
key (optional): if None, format the whole dict of variables.
|
key: if ``None``, format the whole dict of variables,
|
||||||
Else format the value of `key` (Default value = None)
|
else format just the value of *key*.
|
||||||
format (str, optional): specify the format to serialize to.
|
format: specify the format to serialize to. ``"pretty"`` generates
|
||||||
`"pretty"` generates a pretty-printed string,
|
a pretty-printed string, ``"json"`` a JSON-formatted string.
|
||||||
`"json"` a JSON-formatted string.
|
|
||||||
(Default value = `"pretty"`)
|
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: *format* is not a recognized serialization format.
|
||||||
"""
|
"""
|
||||||
if key:
|
if key:
|
||||||
cvars = self.Dictionary(key)
|
cvars = self.Dictionary(key)
|
||||||
|
@ -1707,9 +1720,9 @@ class Base(SubstitutionEnvironment):
|
||||||
fmt = format.lower()
|
fmt = format.lower()
|
||||||
|
|
||||||
if fmt == 'pretty':
|
if fmt == 'pretty':
|
||||||
import pprint
|
import pprint # pylint: disable=import-outside-toplevel
|
||||||
pp = pprint.PrettyPrinter(indent=2)
|
|
||||||
|
|
||||||
|
pp = pprint.PrettyPrinter(indent=2)
|
||||||
# TODO: pprint doesn't do a nice job on path-style values
|
# TODO: pprint doesn't do a nice job on path-style values
|
||||||
# if the paths contain spaces (i.e. Windows), because the
|
# if the paths contain spaces (i.e. Windows), because the
|
||||||
# algorithm tries to break lines on spaces, while breaking
|
# algorithm tries to break lines on spaces, while breaking
|
||||||
|
@ -1718,26 +1731,33 @@ class Base(SubstitutionEnvironment):
|
||||||
return pp.pformat(cvars)
|
return pp.pformat(cvars)
|
||||||
|
|
||||||
elif fmt == 'json':
|
elif fmt == 'json':
|
||||||
import json
|
import json # pylint: disable=import-outside-toplevel
|
||||||
def non_serializable(obj):
|
|
||||||
return '<<non-serializable: %s>>' % type(obj).__qualname__
|
class DumpEncoder(json.JSONEncoder):
|
||||||
return json.dumps(cvars, indent=4, default=non_serializable)
|
"""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:
|
else:
|
||||||
raise ValueError("Unsupported serialization format: %s." % fmt)
|
raise ValueError("Unsupported serialization format: %s." % fmt)
|
||||||
|
|
||||||
|
|
||||||
def FindIxes(self, paths, prefix, suffix):
|
def FindIxes(self, paths: Sequence[str], prefix: str, suffix: str) -> Optional[str]:
|
||||||
"""Search a list of paths for something that matches the prefix and suffix.
|
"""Search *paths* for a path that has *prefix* and *suffix*.
|
||||||
|
|
||||||
Args:
|
Returns on first match.
|
||||||
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
|
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``
|
||||||
"""
|
"""
|
||||||
|
|
||||||
suffix = self.subst('$'+suffix)
|
suffix = self.subst('$'+suffix)
|
||||||
prefix = self.subst('$'+prefix)
|
prefix = self.subst('$'+prefix)
|
||||||
|
|
||||||
|
@ -2001,11 +2021,20 @@ class Base(SubstitutionEnvironment):
|
||||||
def _find_toolpath_dir(self, tp):
|
def _find_toolpath_dir(self, tp):
|
||||||
return self.fs.Dir(self.subst(tp)).srcnode().get_abspath()
|
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*.
|
"""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
|
.. versionchanged:: 4.2
|
||||||
returns the tool module rather than ``None``.
|
returns the tool object rather than ``None``.
|
||||||
"""
|
"""
|
||||||
if is_String(tool):
|
if is_String(tool):
|
||||||
tool = self.subst(tool)
|
tool = self.subst(tool)
|
||||||
|
@ -2180,52 +2209,44 @@ class Base(SubstitutionEnvironment):
|
||||||
return SCons.SConf.SConf(*nargs, **nkw)
|
return SCons.SConf.SConf(*nargs, **nkw)
|
||||||
|
|
||||||
def Command(self, target, source, action, **kw):
|
def Command(self, target, source, action, **kw):
|
||||||
"""Builds the supplied target files from the supplied
|
"""Set up a one-off build command.
|
||||||
source files using the supplied action. Action may
|
|
||||||
be any type that the Builder constructor will accept
|
Builds *target* from *source* using *action*, which may be
|
||||||
for an action."""
|
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 = {
|
bkw = {
|
||||||
'action': action,
|
'action': action,
|
||||||
'target_factory': self.fs.Entry,
|
'target_factory': self.fs.Entry,
|
||||||
'source_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
|
||||||
try:
|
# them out of the args for the subsequent builder call.
|
||||||
bkw['target_scanner'] = kw['target_scanner']
|
for arg in [
|
||||||
except KeyError:
|
'source_scanner',
|
||||||
pass
|
'target_scanner',
|
||||||
else:
|
'source_factory',
|
||||||
del kw['target_scanner']
|
'target_factory',
|
||||||
|
]:
|
||||||
# source factory
|
try:
|
||||||
try:
|
bkw[arg] = kw.pop(arg)
|
||||||
bkw['source_factory'] = kw['source_factory']
|
except KeyError:
|
||||||
except KeyError:
|
pass
|
||||||
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)
|
bld = SCons.Builder.Builder(**bkw)
|
||||||
return bld(self, target, source, **kw)
|
return bld(self, target, source, **kw)
|
||||||
|
|
||||||
def Depends(self, target, dependency):
|
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)
|
tlist = self.arg2nodes(target, self.fs.Entry)
|
||||||
dlist = self.arg2nodes(dependency, self.fs.Entry)
|
dlist = self.arg2nodes(dependency, self.fs.Entry)
|
||||||
for t in tlist:
|
for t in tlist:
|
||||||
|
@ -2253,7 +2274,7 @@ class Base(SubstitutionEnvironment):
|
||||||
return self.fs.PyPackageDir(s)
|
return self.fs.PyPackageDir(s)
|
||||||
|
|
||||||
def NoClean(self, *targets):
|
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 = []
|
tlist = []
|
||||||
for t in targets:
|
for t in targets:
|
||||||
tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
||||||
|
@ -2262,7 +2283,7 @@ class Base(SubstitutionEnvironment):
|
||||||
return tlist
|
return tlist
|
||||||
|
|
||||||
def NoCache(self, *targets):
|
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 = []
|
tlist = []
|
||||||
for t in targets:
|
for t in targets:
|
||||||
tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
tlist.extend(self.arg2nodes(t, self.fs.Entry))
|
||||||
|
@ -2551,8 +2572,12 @@ class OverrideEnvironment(Base):
|
||||||
return self.__dict__['__subject'].__getitem__(key)
|
return self.__dict__['__subject'].__getitem__(key)
|
||||||
|
|
||||||
def __setitem__(self, key, value):
|
def __setitem__(self, key, value):
|
||||||
if not is_valid_construction_var(key):
|
# This doesn't have the same performance equation as a "real"
|
||||||
raise UserError("Illegal construction variable `%s'" % key)
|
# 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
|
self.__dict__['overrides'][key] = value
|
||||||
|
|
||||||
def __delitem__(self, key):
|
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
|
# (for example, failure to create the directory in which the
|
||||||
# target file will appear).
|
# target file will appear).
|
||||||
filename = getattr(status, 'filename', None)
|
filename = getattr(status, 'filename', None)
|
||||||
strerror = getattr(status, 'strerror', str(status))
|
strerror = getattr(status, 'strerror', None)
|
||||||
errno = getattr(status, 'errno', 2)
|
if strerror is None:
|
||||||
|
strerror = str(status)
|
||||||
|
errno = getattr(status, 'errno', None)
|
||||||
|
if errno is None:
|
||||||
|
errno = 2
|
||||||
|
|
||||||
buildError = BuildError(
|
buildError = BuildError(
|
||||||
errstr=strerror,
|
errstr=strerror,
|
|
@ -57,38 +57,6 @@ class AliasNodeInfo(SCons.Node.NodeInfoBase):
|
||||||
def str_to_node(self, s):
|
def str_to_node(self, s):
|
||||||
return default_ans.Alias(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):
|
class AliasBuildInfo(SCons.Node.BuildInfoBase):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
||||||
current_version_id = 2
|
current_version_id = 2
|
|
@ -120,30 +120,31 @@ def save_strings(val) -> None:
|
||||||
global Save_Strings
|
global Save_Strings
|
||||||
Save_Strings = val
|
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
|
do_splitdrive = None
|
||||||
_my_splitdrive =None
|
_my_splitdrive = None
|
||||||
|
|
||||||
def initialize_do_splitdrive() -> None:
|
def initialize_do_splitdrive() -> None:
|
||||||
global do_splitdrive
|
"""Set up splitdrive usage.
|
||||||
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')
|
|
||||||
|
|
||||||
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 :data:`do_splitdrive` is True, :func:`_my_splitdrive` will be a real
|
||||||
if has_unc:
|
function which we can call. As all supported Python versions' ntpath module
|
||||||
def splitdrive(p):
|
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] == ':':
|
if p[1:2] == ':':
|
||||||
return p[:2], p[2:]
|
return p[:2], p[2:]
|
||||||
if p[0:2] == '//':
|
if p[0:2] == '//':
|
||||||
|
@ -151,12 +152,9 @@ def initialize_do_splitdrive() -> None:
|
||||||
# because UNC paths are always absolute.
|
# because UNC paths are always absolute.
|
||||||
return '//', p[1:]
|
return '//', p[1:]
|
||||||
return '', p
|
return '', p
|
||||||
else:
|
# TODO: the os routine should work and be better debugged than ours,
|
||||||
def splitdrive(p):
|
# but unit test test_unc_path fails on POSIX platforms. Resolve someday.
|
||||||
if p[1:2] == ':':
|
# _my_splitdrive = os.path.splitdrive
|
||||||
return p[:2], p[2:]
|
|
||||||
return '', p
|
|
||||||
_my_splitdrive = splitdrive
|
|
||||||
|
|
||||||
# Keep some commonly used values in global variables to skip to
|
# Keep some commonly used values in global variables to skip to
|
||||||
# module look-up costs.
|
# module look-up costs.
|
||||||
|
@ -328,7 +326,11 @@ LocalCopy = SCons.Action.Action(LinkFunc, LocalString)
|
||||||
|
|
||||||
def UnlinkFunc(target, source, env) -> int:
|
def UnlinkFunc(target, source, env) -> int:
|
||||||
t = target[0]
|
t = target[0]
|
||||||
t.fs.unlink(t.get_abspath())
|
file = t.get_abspath()
|
||||||
|
try:
|
||||||
|
t.fs.unlink(file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
Unlink = SCons.Action.Action(UnlinkFunc, None)
|
Unlink = SCons.Action.Action(UnlinkFunc, None)
|
||||||
|
@ -1238,7 +1240,10 @@ class FS(LocalFS):
|
||||||
self.pathTop = os.getcwd()
|
self.pathTop = os.getcwd()
|
||||||
else:
|
else:
|
||||||
self.pathTop = path
|
self.pathTop = path
|
||||||
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
|
if do_splitdrive:
|
||||||
|
self.defaultDrive = _my_normcase(_my_splitdrive(self.pathTop)[0])
|
||||||
|
else:
|
||||||
|
self.defaultDrive = ""
|
||||||
|
|
||||||
self.Top = self.Dir(self.pathTop)
|
self.Top = self.Dir(self.pathTop)
|
||||||
self.Top._path = '.'
|
self.Top._path = '.'
|
||||||
|
@ -1554,11 +1559,15 @@ class DirNodeInfo(SCons.Node.NodeInfoBase):
|
||||||
def str_to_node(self, s):
|
def str_to_node(self, s):
|
||||||
top = self.fs.Top
|
top = self.fs.Top
|
||||||
root = top.root
|
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:
|
if do_splitdrive:
|
||||||
drive, s = _my_splitdrive(s)
|
drive, s = _my_splitdrive(s)
|
||||||
if drive:
|
if drive:
|
||||||
root = self.fs.get_root(drive)
|
root = self.fs.get_root(drive)
|
||||||
if not os.path.isabs(s):
|
if not os.path.isabs(path):
|
||||||
s = top.get_labspath() + '/' + s
|
s = top.get_labspath() + '/' + s
|
||||||
return root._lookup_abs(s, Entry)
|
return root._lookup_abs(s, Entry)
|
||||||
|
|
||||||
|
@ -2380,7 +2389,7 @@ class RootDir(Dir):
|
||||||
# The // entry is necessary because os.path.normpath()
|
# The // entry is necessary because os.path.normpath()
|
||||||
# preserves double slashes at the beginning of a path on Posix
|
# preserves double slashes at the beginning of a path on Posix
|
||||||
# platforms.
|
# platforms.
|
||||||
if not has_unc:
|
if not do_splitdrive:
|
||||||
self._lookupDict['//'] = self
|
self._lookupDict['//'] = self
|
||||||
|
|
||||||
def _morph(self) -> None:
|
def _morph(self) -> None:
|
||||||
|
@ -2511,45 +2520,18 @@ class FileNodeInfo(SCons.Node.NodeInfoBase):
|
||||||
def str_to_node(self, s):
|
def str_to_node(self, s):
|
||||||
top = self.fs.Top
|
top = self.fs.Top
|
||||||
root = top.root
|
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:
|
if do_splitdrive:
|
||||||
drive, s = _my_splitdrive(s)
|
drive, s = _my_splitdrive(s)
|
||||||
if drive:
|
if drive:
|
||||||
root = self.fs.get_root(drive)
|
root = self.fs.get_root(drive)
|
||||||
if not os.path.isabs(s):
|
if not os.path.isabs(path):
|
||||||
s = top.get_labspath() + '/' + s
|
s = top.get_labspath() + '/' + s
|
||||||
return root._lookup_abs(s, Entry)
|
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):
|
def __eq__(self, other):
|
||||||
return self.csig == other.csig and self.timestamp == other.timestamp and self.size == other.size
|
return self.csig == other.csig and self.timestamp == other.timestamp and self.size == other.size
|
||||||
|
|
||||||
|
@ -2986,7 +2968,7 @@ class File(Base):
|
||||||
# created.
|
# created.
|
||||||
self.dir._create()
|
self.dir._create()
|
||||||
|
|
||||||
def push_to_cache(self) -> None:
|
def push_to_cache(self) -> bool:
|
||||||
"""Try to push the node into a cache
|
"""Try to push the node into a cache
|
||||||
"""
|
"""
|
||||||
# This should get called before the Nodes' .built() method is
|
# 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()
|
# the node to cache so that the memoization of the self.exists()
|
||||||
# return value doesn't interfere.
|
# return value doesn't interfere.
|
||||||
if self.nocache:
|
if self.nocache:
|
||||||
return
|
return None
|
||||||
self.clear_memoized_values()
|
self.clear_memoized_values()
|
||||||
if self.exists():
|
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:
|
def retrieve_from_cache(self) -> bool:
|
||||||
"""Try to retrieve the node's content from a cache
|
"""Try to retrieve the node's content from a cache
|
||||||
|
@ -3176,12 +3158,16 @@ class File(Base):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def do_duplicate(self, src):
|
def do_duplicate(self, src):
|
||||||
|
"""Create a duplicate of this file from the specified source."""
|
||||||
self._createDir()
|
self._createDir()
|
||||||
if SCons.Node.print_duplicate:
|
if SCons.Node.print_duplicate:
|
||||||
print(f"dup: relinking variant '{self}' from '{src}'")
|
print(f"dup: relinking variant '{self}' from '{src}'")
|
||||||
Unlink(self, None, None)
|
Unlink(self, None, None)
|
||||||
e = Link(self, src, None)
|
try:
|
||||||
if isinstance(e, SCons.Errors.BuildError):
|
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}.")
|
raise SCons.Errors.StopError(f"Cannot duplicate `{src.get_internal_path()}' in `{self.dir._path}': {e.errstr}.")
|
||||||
self.linked = 1
|
self.linked = 1
|
||||||
# The Link() action may or may not have actually
|
# 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
|
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 T: Trace('is_up_to_date(%s):' % self)
|
||||||
if not self.exists():
|
if not self.exists():
|
||||||
if T: Trace(' not self.exists():')
|
if T: Trace(' not self.exists():')
|
||||||
|
@ -3730,7 +3716,10 @@ class FileFinder:
|
||||||
if fd is None:
|
if fd is None:
|
||||||
fd = self.default_filedir
|
fd = self.default_filedir
|
||||||
dir, name = os.path.split(fd)
|
dir, name = os.path.split(fd)
|
||||||
drive, d = _my_splitdrive(dir)
|
if do_splitdrive:
|
||||||
|
drive, d = _my_splitdrive(dir)
|
||||||
|
else:
|
||||||
|
drive, d = "", dir
|
||||||
if not name and d[:1] in ('/', OS_SEP):
|
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).dir_on_disk(name)
|
||||||
return p.fs.get_root(drive)
|
return p.fs.get_root(drive)
|
|
@ -37,37 +37,6 @@ class ValueNodeInfo(SCons.Node.NodeInfoBase):
|
||||||
def str_to_node(self, s):
|
def str_to_node(self, s):
|
||||||
return ValueWithMemo(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):
|
class ValueBuildInfo(SCons.Node.BuildInfoBase):
|
||||||
__slots__ = ()
|
__slots__ = ()
|
|
@ -678,7 +678,7 @@ class Node(metaclass=NoSlotsPyPy):
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def push_to_cache(self) -> None:
|
def push_to_cache(self) -> bool:
|
||||||
"""Try to push a node into a cache
|
"""Try to push a node into a cache
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
@ -1063,6 +1063,7 @@ class Node(metaclass=NoSlotsPyPy):
|
||||||
# Don't bother scanning non-derived files, because we don't
|
# Don't bother scanning non-derived files, because we don't
|
||||||
# care what their dependencies are.
|
# care what their dependencies are.
|
||||||
# Don't scan again, if we already have scanned.
|
# Don't scan again, if we already have scanned.
|
||||||
|
T = False
|
||||||
if self.implicit is not None:
|
if self.implicit is not None:
|
||||||
return
|
return
|
||||||
self.implicit = []
|
self.implicit = []
|
||||||
|
@ -1087,7 +1088,12 @@ class Node(metaclass=NoSlotsPyPy):
|
||||||
# essentially short-circuits an N*M scan of the
|
# essentially short-circuits an N*M scan of the
|
||||||
# sources for each individual target, which is a hell
|
# sources for each individual target, which is a hell
|
||||||
# of a lot more efficient.
|
# 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():
|
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)
|
tgt.add_to_implicit(implicit)
|
||||||
|
|
||||||
if implicit_deps_unchanged or self.is_up_to_date():
|
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()
|
@see: FS.File.changed(), FS.File.release_target_info()
|
||||||
"""
|
"""
|
||||||
t = 0
|
T = False
|
||||||
if t: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
|
if T: Trace('changed(%s [%s], %s)' % (self, classname(self), node))
|
||||||
if node is None:
|
if node is None:
|
||||||
node = self
|
node = self
|
||||||
|
|
||||||
|
@ -1491,25 +1497,24 @@ class Node(metaclass=NoSlotsPyPy):
|
||||||
# entries to equal the new dependency list, for the benefit
|
# entries to equal the new dependency list, for the benefit
|
||||||
# of the loop below that updates node information.
|
# of the loop below that updates node information.
|
||||||
then.extend([None] * diff)
|
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
|
result = True
|
||||||
|
|
||||||
for child, prev_ni in zip(children, then):
|
for child, prev_ni in zip(children, then):
|
||||||
if _decider_map[child.changed_since_last_build](child, self, prev_ni, node):
|
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
|
result = True
|
||||||
|
|
||||||
if self.has_builder():
|
if self.has_builder():
|
||||||
contents = self.get_executor().get_contents()
|
contents = self.get_executor().get_contents()
|
||||||
newsig = hash_signature(contents)
|
newsig = hash_signature(contents)
|
||||||
if bi.bactsig != newsig:
|
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
|
result = True
|
||||||
|
|
||||||
if not result:
|
if not result:
|
||||||
if t: Trace(': up to date')
|
if T: Trace(': up to date')
|
||||||
|
if T: Trace('\n')
|
||||||
if t: Trace('\n')
|
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
|
|
||||||
"""Handle lists of directory paths.
|
"""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
|
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
|
still keeping the evaluation delayed so that we Do the Right Thing
|
||||||
(almost) regardless of how the variable is specified.
|
(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
|
This is the "string conversion" routine that we have our substitutions
|
||||||
use to return Nodes, not strings. This relies on the fact that an
|
use to return Nodes, not strings. This relies on the fact that an
|
||||||
EntryProxy object has a get() method that returns the underlying
|
:class:`~SCons.Node.FS.EntryProxy` object has a ``get()`` method that
|
||||||
Node that it wraps, which is a bit of architectural dependence
|
returns the underlying Node that it wraps, which is a bit of
|
||||||
that we might need to break or modify in the future in response to
|
architectural dependence that we might need to break or modify in the
|
||||||
additional requirements.
|
future in response to additional requirements.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
get = obj.get
|
get = obj.get
|
||||||
|
@ -64,34 +64,35 @@ def node_conv(obj):
|
||||||
return result
|
return result
|
||||||
|
|
||||||
class _PathList:
|
class _PathList:
|
||||||
"""An actual PathList object."""
|
"""An actual PathList object.
|
||||||
|
|
||||||
|
Initializes a :class:`PathList` object, canonicalizing the input and
|
||||||
|
pre-processing it for quicker substitution later.
|
||||||
|
|
||||||
|
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
|
||||||
|
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``,
|
||||||
|
which require re-evaluation for every target + source)
|
||||||
|
|
||||||
|
* 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 :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:
|
def __init__(self, pathlist, split=True) -> None:
|
||||||
"""
|
|
||||||
Initializes a 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_*
|
|
||||||
variables defined above. We distinguish between:
|
|
||||||
|
|
||||||
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',
|
|
||||||
which require re-evaluation for every target + source)
|
|
||||||
|
|
||||||
other objects (which may be something like an 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
|
|
||||||
over and over for each target.
|
|
||||||
"""
|
|
||||||
if SCons.Util.is_String(pathlist):
|
if SCons.Util.is_String(pathlist):
|
||||||
if split:
|
if split:
|
||||||
pathlist = pathlist.split(os.pathsep)
|
pathlist = pathlist.split(os.pathsep)
|
||||||
|
@ -152,34 +153,33 @@ class PathListCache:
|
||||||
use the same Memoizer pattern that we use elsewhere to count cache
|
use the same Memoizer pattern that we use elsewhere to count cache
|
||||||
hits and misses, which is very valuable.
|
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
|
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',
|
all forms of the same lookup key. For example, ``x:y`` and ``['x', 'y']``
|
||||||
'y'] logically represent the same list, but we don't bother to
|
logically represent the same list, but we don't bother to
|
||||||
split string representations and treat those two equivalently.
|
split string representations and treat those two equivalently.
|
||||||
(Note, however, that we do, treat lists and tuples the same.)
|
(Note, however, that we do, treat lists and tuples the same.)
|
||||||
|
|
||||||
The main type of duplication we're trying to catch will come from
|
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
|
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()
|
env2 = env1.Clone()
|
||||||
|
|
||||||
both env1 and env2 will have the same CPPPATH value, and we can
|
both ``env1`` and ``env2`` will have the same ``CPPPATH`` value, and we can
|
||||||
cheaply avoid re-parsing both values of CPPPATH by using the
|
cheaply avoid re-parsing both values of ``CPPPATH`` by using the
|
||||||
common value from this cache.
|
common value from this cache.
|
||||||
"""
|
"""
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._memo = {}
|
self._memo = {}
|
||||||
|
|
||||||
def _PathList_key(self, pathlist):
|
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
|
Note that we want this to be pretty quick, so we don't completely
|
||||||
canonicalize all forms of the same list. For example,
|
canonicalize all forms of the same list. For example,
|
||||||
'dir1:$ROOT/dir2' and ['$ROOT/dir1', 'dir'] may logically
|
``dir1:$ROOT/dir2`` and ``['$ROOT/dir1', 'dir']`` may logically
|
||||||
represent the same list if you're executing from $ROOT, but
|
represent the same list if you're executing from ``$ROOT``, but
|
||||||
we're not going to bother splitting strings into path elements,
|
we're not going to bother splitting strings into path elements,
|
||||||
or massaging strings into Nodes, to identify that equivalence.
|
or massaging strings into Nodes, to identify that equivalence.
|
||||||
We just want to eliminate obvious redundancy from the normal
|
We just want to eliminate obvious redundancy from the normal
|
||||||
|
@ -191,9 +191,10 @@ class PathListCache:
|
||||||
|
|
||||||
@SCons.Memoize.CountDictCall(_PathList_key)
|
@SCons.Memoize.CountDictCall(_PathList_key)
|
||||||
def PathList(self, pathlist, split=True):
|
def PathList(self, pathlist, split=True):
|
||||||
"""
|
"""Entry point for getting PathLists.
|
||||||
Returns the cached _PathList object for the specified pathlist,
|
|
||||||
creating and caching a new object as necessary.
|
Returns the cached :class:`_PathList` object for the specified
|
||||||
|
pathlist, creating and caching a new object as necessary.
|
||||||
"""
|
"""
|
||||||
pathlist = self._PathList_key(pathlist)
|
pathlist = self._PathList_key(pathlist)
|
||||||
try:
|
try:
|
||||||
|
@ -215,7 +216,8 @@ class PathListCache:
|
||||||
|
|
||||||
PathList = PathListCache().PathList
|
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
|
del PathListCache
|
||||||
|
|
||||||
# Local Variables:
|
# Local Variables:
|
|
@ -41,7 +41,7 @@ import time
|
||||||
import traceback
|
import traceback
|
||||||
import platform
|
import platform
|
||||||
import threading
|
import threading
|
||||||
from typing import Optional, List
|
from typing import Optional, List, TYPE_CHECKING
|
||||||
|
|
||||||
import SCons.CacheDir
|
import SCons.CacheDir
|
||||||
import SCons.Debug
|
import SCons.Debug
|
||||||
|
@ -59,15 +59,17 @@ import SCons.Taskmaster
|
||||||
import SCons.Util
|
import SCons.Util
|
||||||
import SCons.Warnings
|
import SCons.Warnings
|
||||||
import SCons.Script.Interactive
|
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.Util.stats import count_stats, memory_stats, time_stats, ENABLE_JSON, write_scons_stats_file, JSON_OUTPUT_FILE
|
||||||
|
|
||||||
from SCons import __version__ as SConsVersion
|
from SCons import __version__ as SConsVersion
|
||||||
|
|
||||||
# these define the range of versions SCons supports
|
# these define the range of versions SCons supports
|
||||||
minimum_python_version = (3, 6, 0)
|
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 = [
|
KNOWN_SCONSTRUCT_NAMES = [
|
||||||
'SConstruct',
|
'SConstruct',
|
||||||
'Sconstruct',
|
'Sconstruct',
|
||||||
|
@ -84,7 +86,11 @@ KNOWN_SCONSCRIPTS = [
|
||||||
"Sconstruct",
|
"Sconstruct",
|
||||||
"sconstruct",
|
"sconstruct",
|
||||||
"SConscript",
|
"SConscript",
|
||||||
|
"Sconscript",
|
||||||
"sconscript",
|
"sconscript",
|
||||||
|
"SCsub", # Uncommon alternative to SConscript
|
||||||
|
"Scsub",
|
||||||
|
"scsub",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Global variables
|
# Global variables
|
||||||
|
@ -174,6 +180,7 @@ class Progressor:
|
||||||
ProgressObject = SCons.Util.Null()
|
ProgressObject = SCons.Util.Null()
|
||||||
|
|
||||||
def Progress(*args, **kw) -> None:
|
def Progress(*args, **kw) -> None:
|
||||||
|
"""Show progress during building - Public API."""
|
||||||
global ProgressObject
|
global ProgressObject
|
||||||
ProgressObject = Progressor(*args, **kw)
|
ProgressObject = Progressor(*args, **kw)
|
||||||
|
|
||||||
|
@ -501,29 +508,47 @@ class FakeOptionParser:
|
||||||
# TODO: to quiet checkers, FakeOptionParser should also define
|
# TODO: to quiet checkers, FakeOptionParser should also define
|
||||||
# raise_exception_on_error, preserve_unknown_options, largs and parse_args
|
# 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
|
pass
|
||||||
|
|
||||||
|
|
||||||
OptionsParser = FakeOptionParser()
|
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:
|
if 'default' not in kw:
|
||||||
kw['default'] = None
|
kw['default'] = None
|
||||||
|
kw['settable'] = settable
|
||||||
result = OptionsParser.add_local_option(*args, **kw)
|
result = OptionsParser.add_local_option(*args, **kw)
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def GetOption(name):
|
def GetOption(name: str):
|
||||||
|
"""Get the value from an option - Public API."""
|
||||||
return getattr(OptionsParser.values, name)
|
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)
|
return OptionsParser.values.set_option(name, value)
|
||||||
|
|
||||||
def DebugOptions(json=None):
|
def DebugOptions(json: Optional[str] = None) -> None:
|
||||||
"""
|
"""Specify options to SCons debug logic - Public API.
|
||||||
API to allow specifying options to SCons debug logic
|
|
||||||
Currently only json is supported which changes the
|
Currently only *json* is supported, which changes the JSON file
|
||||||
json file written by --debug=json from the default
|
written to if the ``--debug=json`` command-line option is specified
|
||||||
|
to the value supplied.
|
||||||
|
|
||||||
|
.. versionadded:: 4.6.0
|
||||||
|
|
||||||
"""
|
"""
|
||||||
if json is not None:
|
if json is not None:
|
||||||
json_node = SCons.Defaults.DefaultEnvironment().arg2nodes(json)
|
json_node = SCons.Defaults.DefaultEnvironment().arg2nodes(json)
|
||||||
|
@ -540,7 +565,7 @@ def DebugOptions(json=None):
|
||||||
raise SCons.Errors.UserError(f"Unable to create directory for JSON debug output file: {SCons.Util.stats.JSON_OUTPUT_FILE}")
|
raise SCons.Errors.UserError(f"Unable to create directory for JSON debug output file: {SCons.Util.stats.JSON_OUTPUT_FILE}")
|
||||||
|
|
||||||
|
|
||||||
def ValidateOptions(throw_exception: bool=False) -> None:
|
def ValidateOptions(throw_exception: bool = False) -> None:
|
||||||
"""Validate options passed to SCons on the command line.
|
"""Validate options passed to SCons on the command line.
|
||||||
|
|
||||||
Checks that all options given on the command line are known to this
|
Checks that all options given on the command line are known to this
|
||||||
|
@ -1104,11 +1129,13 @@ def _main(parser):
|
||||||
# warning about deprecated Python versions--delayed until here
|
# warning about deprecated Python versions--delayed until here
|
||||||
# in case they disabled the warning in the SConscript files.
|
# in case they disabled the warning in the SConscript files.
|
||||||
if python_version_deprecated():
|
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))
|
deprecated_version_string = ".".join(map(str, deprecated_python_version))
|
||||||
SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
|
msg = (
|
||||||
msg % (deprecated_version_string, python_version_string()))
|
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:
|
if not options.help:
|
||||||
# [ ] Clarify why we need to create Builder here at all, and
|
# [ ] 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.
|
sconscript files that don't have the suffix.
|
||||||
|
|
||||||
.. versionadded:: 4.6.0
|
.. 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):
|
if os.path.isabs(filename) and os.path.exists(filename):
|
||||||
return filename
|
return filename
|
|
@ -66,17 +66,14 @@ def diskcheck_convert(value):
|
||||||
|
|
||||||
|
|
||||||
class SConsValues(optparse.Values):
|
class SConsValues(optparse.Values):
|
||||||
"""
|
"""Holder class for uniform access to SCons options.
|
||||||
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).
|
|
||||||
|
|
||||||
A SCons option value can originate three different ways:
|
A SCons option value can originate three different ways:
|
||||||
|
|
||||||
1) set on the command line;
|
1. set on the command line.
|
||||||
2) set in an SConscript file;
|
2. set in an SConscript file via :func:`~SCons.Script.Main.SetOption`.
|
||||||
3) the default setting (from the the op.add_option()
|
3. the default setting (from the the ``op.add_option()``
|
||||||
calls in the Parser() function, below).
|
calls in the :func:`Parser` function, below).
|
||||||
|
|
||||||
The command line always overrides a value set in a SConscript file,
|
The command line always overrides a value set in a SConscript file,
|
||||||
which in turn always overrides default settings. Because we want
|
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
|
The solution implemented in this class is to keep these different sets
|
||||||
of settings separate (command line, SConscript file, and default)
|
of settings separate (command line, SConscript file, and default)
|
||||||
and to override the __getattr__() method to check them in turn.
|
and to override the :meth:`__getattr__` method to check them in turn.
|
||||||
This should allow the rest of the code to just fetch values as
|
This allows the rest of the code to just fetch values as attributes of
|
||||||
attributes of an instance of this class, without having to worry
|
an instance of this class, without having to worry about where they
|
||||||
about where they came from.
|
came from (the scheme is similar to a ``ChainMap``).
|
||||||
|
|
||||||
Note that not all command line options are settable from SConscript
|
Note that not all command line options are settable from SConscript
|
||||||
files, and the ones that are must be explicitly added to the
|
files, and the ones that are must be explicitly added to the
|
||||||
"settable" list in this class, and optionally validated and coerced
|
:attr:`settable` list in this class, and optionally validated and coerced
|
||||||
in the set_option() method.
|
in the :meth:`set_option` method.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, defaults) -> None:
|
def __init__(self, defaults) -> None:
|
||||||
|
@ -103,28 +100,24 @@ class SConsValues(optparse.Values):
|
||||||
self.__SConscript_settings__ = {}
|
self.__SConscript_settings__ = {}
|
||||||
|
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
"""
|
"""Fetch an options value, respecting priority rules.
|
||||||
Fetches an options value, checking first for explicit settings
|
|
||||||
from the command line (which are direct attributes), then the
|
This is a little tricky: since we're answering questions
|
||||||
SConscript file settings, then the default values.
|
about outselves, we have avoid lookups that would send us into
|
||||||
|
into infinite recursion, thus the ``__dict__`` stuff.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return self.__dict__[attr]
|
return self.__dict__[attr]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
try:
|
||||||
return self.__dict__['__SConscript_settings__'][attr]
|
return self.__dict__['__SConscript_settings__'][attr]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
try:
|
try:
|
||||||
return getattr(self.__dict__['__defaults__'], attr)
|
return getattr(self.__dict__['__defaults__'], attr)
|
||||||
except KeyError:
|
except KeyError as exc:
|
||||||
# Added because with py3 this is a new class,
|
# Need to respond with AttributeError because
|
||||||
# not a classic class, and due to the way
|
# deepcopy expects that if __setstate__ is not available.
|
||||||
# In that case it will create an object without
|
raise AttributeError(attr) from exc
|
||||||
# __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)
|
|
||||||
|
|
||||||
# keep this list in sync with the SetOption doc in SCons/Script/Main.xml
|
# keep this list in sync with the SetOption doc in SCons/Script/Main.xml
|
||||||
# search for UPDATE_SETOPTION_DOCS there.
|
# search for UPDATE_SETOPTION_DOCS there.
|
||||||
|
@ -148,26 +141,26 @@ class SConsValues(optparse.Values):
|
||||||
'silent',
|
'silent',
|
||||||
'stack_size',
|
'stack_size',
|
||||||
'warn',
|
'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):
|
def set_option(self, name: str, value) -> None:
|
||||||
"""Sets an option from an SConscript file.
|
"""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:
|
Raises:
|
||||||
UserError: invalid or malformed option ("error in your script")
|
UserError: the option is not settable.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
if name not in self.settable:
|
if name not in self.settable:
|
||||||
raise SCons.Errors.UserError(
|
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
|
# 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])
|
return tuple([self.check_value(opt, v) for v in value])
|
||||||
|
|
||||||
def process(self, opt, value, values, parser):
|
def process(self, opt, value, values, parser):
|
||||||
|
|
||||||
# First, convert the value(s) to the right type. Howl if any
|
# First, convert the value(s) to the right type. Howl if any
|
||||||
# value(s) are bogus.
|
# value(s) are bogus.
|
||||||
value = self.convert_value(opt, value)
|
value = self.convert_value(opt, value)
|
||||||
|
@ -313,9 +305,7 @@ class SConsOptionParser(optparse.OptionParser):
|
||||||
raise_exception_on_error = False
|
raise_exception_on_error = False
|
||||||
|
|
||||||
def error(self, msg):
|
def error(self, msg):
|
||||||
"""
|
"""Overridden OptionValueError exception handler."""
|
||||||
overridden OptionValueError exception handler
|
|
||||||
"""
|
|
||||||
if self.raise_exception_on_error:
|
if self.raise_exception_on_error:
|
||||||
raise SConsBadOptionError(msg, self)
|
raise SConsBadOptionError(msg, self)
|
||||||
else:
|
else:
|
||||||
|
@ -397,46 +387,46 @@ class SConsOptionParser(optparse.OptionParser):
|
||||||
option.process(opt, value, values, self)
|
option.process(opt, value, values, self)
|
||||||
|
|
||||||
def reparse_local_options(self) -> None:
|
def reparse_local_options(self) -> None:
|
||||||
""" Re-parse the leftover command-line options.
|
"""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
|
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
|
We mimic the processing of the single args
|
||||||
in the original OptionParser :func:`_process_args`, but here we
|
in the original OptionParser :func:`_process_args`, but here we
|
||||||
allow exact matches for long-opts only (no partial argument names!).
|
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
|
below. When called from there, we try to reparse the
|
||||||
command-line arguments that
|
command-line arguments that
|
||||||
|
|
||||||
1. haven't been processed so far (`self.largs`), but
|
1. haven't been processed so far (`self.largs`), but
|
||||||
2. are possibly not added to the list of options yet.
|
2. are possibly not added to the list of options yet.
|
||||||
|
|
||||||
So, when we only have a value for "--myargument" so far,
|
So, when we only have a value for ``--myargument`` so far,
|
||||||
a command-line argument of "--myarg=test" would set it,
|
a command-line argument of ``--myarg=test`` would set it,
|
||||||
per the behaviour of :func:`_match_long_opt`,
|
per the behaviour of :func:`_match_long_opt`,
|
||||||
which allows for partial matches of the option name,
|
which allows for partial matches of the option name,
|
||||||
as long as the common prefix appears to be unique.
|
as long as the common prefix appears to be unique.
|
||||||
This would lead to further confusion, because we might want
|
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 = []
|
rargs = []
|
||||||
largs_restore = []
|
largs_restore = []
|
||||||
# Loop over all remaining arguments
|
# Loop over all remaining arguments
|
||||||
skip = False
|
skip = False
|
||||||
for l in self.largs:
|
for larg in self.largs:
|
||||||
if skip:
|
if skip:
|
||||||
# Accept all remaining arguments as they are
|
# Accept all remaining arguments as they are
|
||||||
largs_restore.append(l)
|
largs_restore.append(larg)
|
||||||
else:
|
else:
|
||||||
if len(l) > 2 and l[0:2] == "--":
|
if len(larg) > 2 and larg[0:2] == "--":
|
||||||
# Check long option
|
# Check long option
|
||||||
lopt = (l,)
|
lopt = [larg]
|
||||||
if "=" in l:
|
if "=" in larg:
|
||||||
# Split into option and value
|
# Split into option and value
|
||||||
lopt = l.split("=", 1)
|
lopt = larg.split("=", 1)
|
||||||
|
|
||||||
if lopt[0] in self._long_opt:
|
if lopt[0] in self._long_opt:
|
||||||
# Argument is already known
|
# Argument is already known
|
||||||
|
@ -445,26 +435,35 @@ class SConsOptionParser(optparse.OptionParser):
|
||||||
# Not known yet, so reject for now
|
# Not known yet, so reject for now
|
||||||
largs_restore.append('='.join(lopt))
|
largs_restore.append('='.join(lopt))
|
||||||
else:
|
else:
|
||||||
if l == "--" or l == "-":
|
if larg in("--", "-"):
|
||||||
# Stop normal processing and don't
|
# Stop normal processing and don't
|
||||||
# process the rest of the command-line opts
|
# process the rest of the command-line opts
|
||||||
largs_restore.append(l)
|
largs_restore.append(larg)
|
||||||
skip = True
|
skip = True
|
||||||
else:
|
else:
|
||||||
rargs.append(l)
|
rargs.append(larg)
|
||||||
|
|
||||||
# Parse the filtered list
|
# Parse the filtered list
|
||||||
self.parse_args(rargs, self.values)
|
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...
|
# next call of AddOption/add_local_option...
|
||||||
self.largs = self.largs + largs_restore
|
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.
|
""" Adds a local option to the parser.
|
||||||
|
|
||||||
This is initiated by an :func:`AddOption` call to add a user-defined
|
This is initiated by an :func:`~SCons.Script.Main.AddOption` call to
|
||||||
command-line option. We add the option to a separate option
|
add a user-defined command-line option. Add the option to a separate
|
||||||
group for the local options, creating the group if necessary.
|
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:
|
try:
|
||||||
group = self.local_option_group
|
group = self.local_option_group
|
||||||
|
@ -473,6 +472,7 @@ class SConsOptionParser(optparse.OptionParser):
|
||||||
group = self.add_option_group(group)
|
group = self.add_option_group(group)
|
||||||
self.local_option_group = group
|
self.local_option_group = group
|
||||||
|
|
||||||
|
settable = kw.pop('settable')
|
||||||
result = group.add_option(*args, **kw)
|
result = group.add_option(*args, **kw)
|
||||||
if result:
|
if result:
|
||||||
# The option was added successfully. We now have to add the
|
# The option was added successfully. We now have to add the
|
||||||
|
@ -485,6 +485,8 @@ class SConsOptionParser(optparse.OptionParser):
|
||||||
# right away.
|
# right away.
|
||||||
setattr(self.values.__defaults__, result.dest, result.default)
|
setattr(self.values.__defaults__, result.dest, result.default)
|
||||||
self.reparse_local_options()
|
self.reparse_local_options()
|
||||||
|
if settable:
|
||||||
|
SConsValues.settable.append(result.dest)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
@ -614,7 +616,8 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter):
|
||||||
"""Local-only version of store_option_strings.
|
"""Local-only version of store_option_strings.
|
||||||
|
|
||||||
We need to replicate this so the formatter will be set up
|
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
|
.. versionadded:: 4.6.0
|
||||||
"""
|
"""
|
||||||
|
@ -633,15 +636,18 @@ def Parser(version):
|
||||||
"""Returns a parser object initialized with the standard SCons options.
|
"""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
|
Add options in the order we want them to show up in the ``-H`` help
|
||||||
text, basically alphabetical. Each ``op.add_option()`` call
|
text, basically alphabetical. For readability, Each
|
||||||
should have a consistent format::
|
:meth:`~optparse.OptionContainer.add_option` call should have a
|
||||||
|
consistent format::
|
||||||
|
|
||||||
op.add_option("-L", "--long-option-name",
|
op.add_option(
|
||||||
nargs=1, type="string",
|
"-L", "--long-option-name",
|
||||||
dest="long_option_name", default='foo',
|
nargs=1, type="string",
|
||||||
action="callback", callback=opt_long_option,
|
dest="long_option_name", default='foo',
|
||||||
help="help text goes here",
|
action="callback", callback=opt_long_option,
|
||||||
metavar="VAR")
|
help="help text goes here",
|
||||||
|
metavar="VAR"
|
||||||
|
)
|
||||||
|
|
||||||
Even though the :mod:`optparse` module constructs reasonable default
|
Even though the :mod:`optparse` module constructs reasonable default
|
||||||
destination names from the long option names, we're going to be
|
destination names from the long option names, we're going to be
|
|
@ -45,6 +45,7 @@ import re
|
||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
import time
|
import time
|
||||||
|
from typing import Tuple
|
||||||
|
|
||||||
class SConscriptReturn(Exception):
|
class SConscriptReturn(Exception):
|
||||||
pass
|
pass
|
||||||
|
@ -385,7 +386,7 @@ class SConsEnvironment(SCons.Environment.Base):
|
||||||
# Private methods of an SConsEnvironment.
|
# Private methods of an SConsEnvironment.
|
||||||
#
|
#
|
||||||
@staticmethod
|
@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)
|
"""Split a version string into major, minor and (optionally)
|
||||||
revision parts.
|
revision parts.
|
||||||
|
|
||||||
|
@ -484,15 +485,22 @@ class SConsEnvironment(SCons.Environment.Base):
|
||||||
SCons.Script._Set_Default_Targets(self, targets)
|
SCons.Script._Set_Default_Targets(self, targets)
|
||||||
|
|
||||||
@staticmethod
|
@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."""
|
"""Exit abnormally if the SCons version is not late enough."""
|
||||||
# split string to avoid replacement during build process
|
# split string to avoid replacement during build process
|
||||||
if SCons.__version__ == '__' + 'VERSION__':
|
if SCons.__version__ == '__' + 'VERSION__':
|
||||||
SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning,
|
SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning,
|
||||||
"EnsureSConsVersion is ignored for development version")
|
"EnsureSConsVersion is ignored for development version")
|
||||||
return
|
return
|
||||||
scons_ver = SConsEnvironment._get_major_minor_revision(SCons.__version__)
|
if SConsEnvironment.GetSConsVersion() < (major, minor, revision):
|
||||||
if scons_ver < (major, minor, revision):
|
|
||||||
if revision:
|
if revision:
|
||||||
scons_ver_string = '%d.%d.%d' % (major, minor, revision)
|
scons_ver_string = '%d.%d.%d' % (major, minor, revision)
|
||||||
else:
|
else:
|
|
@ -297,6 +297,7 @@ def Variables(files=None, args=ARGUMENTS):
|
||||||
#
|
#
|
||||||
# Static functions that do not trigger initialization of
|
# Static functions that do not trigger initialization of
|
||||||
# DefaultEnvironment() and don't use its state.
|
# DefaultEnvironment() and don't use its state.
|
||||||
|
GetSConsVersion = _SConscript.SConsEnvironment.GetSConsVersion
|
||||||
EnsureSConsVersion = _SConscript.SConsEnvironment.EnsureSConsVersion
|
EnsureSConsVersion = _SConscript.SConsEnvironment.EnsureSConsVersion
|
||||||
EnsurePythonVersion = _SConscript.SConsEnvironment.EnsurePythonVersion
|
EnsurePythonVersion = _SConscript.SConsEnvironment.EnsurePythonVersion
|
||||||
Exit = _SConscript.SConsEnvironment.Exit
|
Exit = _SConscript.SConsEnvironment.Exit
|
|
@ -278,9 +278,8 @@ class ThreadPool:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
prev_size = threading.stack_size(stack_size * 1024)
|
prev_size = threading.stack_size(stack_size * 1024)
|
||||||
except AttributeError as e:
|
except RuntimeError as e:
|
||||||
# Only print a warning if the stack size has been
|
# Only print a warning if the stack size has been explicitly set.
|
||||||
# explicitly set.
|
|
||||||
if explicit_stack_size is not None:
|
if explicit_stack_size is not None:
|
||||||
msg = "Setting stack size is unsupported by this version of Python:\n " + \
|
msg = "Setting stack size is unsupported by this version of Python:\n " + \
|
||||||
e.args[0]
|
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,
|
namedtuple,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
from contextlib import (
|
||||||
|
contextmanager,
|
||||||
|
)
|
||||||
|
|
||||||
import SCons.Warnings
|
import SCons.Warnings
|
||||||
|
|
||||||
from ..common import (
|
from ..common import (
|
||||||
|
@ -215,6 +219,18 @@ def msvc_notfound_handler(env, msg):
|
||||||
else:
|
else:
|
||||||
SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg)
|
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):
|
def _msvc_scripterror_policy_lookup(symbol):
|
||||||
|
|
||||||
|
@ -299,3 +315,16 @@ def msvc_scripterror_handler(env, msg):
|
||||||
else:
|
else:
|
||||||
SCons.Warnings.warn(MSVCScriptExecutionWarning, msg)
|
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)
|
q = windows_kits(version)
|
||||||
return microsoft_query_paths(q)
|
return microsoft_query_paths(q)
|
||||||
|
|
||||||
|
def vstudio_sxs_vs7(version):
|
||||||
|
return '\\'.join([r'VisualStudio\SxS\VS7', version])
|
||||||
|
|
||||||
def vstudio_sxs_vc7(version):
|
def vstudio_sxs_vc7(version):
|
||||||
return '\\'.join([r'VisualStudio\SxS\VC7', version])
|
return '\\'.join([r'VisualStudio\SxS\VC7', version])
|
||||||
|
|
|
@ -42,6 +42,7 @@ from . import Util
|
||||||
from . import Config
|
from . import Config
|
||||||
from . import Registry
|
from . import Registry
|
||||||
from . import WinSDK
|
from . import WinSDK
|
||||||
|
from . import Kind
|
||||||
|
|
||||||
from .Exceptions import (
|
from .Exceptions import (
|
||||||
MSVCInternalError,
|
MSVCInternalError,
|
||||||
|
@ -172,6 +173,12 @@ MSVC_VERSION_ARGS_DEFINITION = namedtuple('MSVCVersionArgsDefinition', [
|
||||||
'vs_def',
|
'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):
|
def _msvc_version(version):
|
||||||
|
|
||||||
verstr = Util.get_msvc_version_prefix(version)
|
verstr = Util.get_msvc_version_prefix(version)
|
||||||
|
@ -184,19 +191,22 @@ def _msvc_version(version):
|
||||||
|
|
||||||
return version_args
|
return version_args
|
||||||
|
|
||||||
def _toolset_version(version):
|
def _toolset_version(version, is_user=False):
|
||||||
|
|
||||||
verstr = Util.get_msvc_version_prefix(version)
|
vc_series = Util.get_msvc_version_prefix(version)
|
||||||
vs_def = Config.MSVC_VERSION_INTERNAL[verstr]
|
|
||||||
|
|
||||||
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,
|
version = version,
|
||||||
vs_def = vs_def,
|
vc_buildtools_def = vc_buildtools_def,
|
||||||
|
is_user = is_user,
|
||||||
)
|
)
|
||||||
|
|
||||||
return version_args
|
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']
|
uwp_app = env['MSVC_UWP_APP']
|
||||||
debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
|
debug('MSVC_VERSION=%s, MSVC_UWP_APP=%s', repr(msvc.version), repr(uwp_app))
|
||||||
|
@ -207,17 +217,34 @@ def _msvc_script_argument_uwp(env, msvc, arglist):
|
||||||
if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY:
|
if uwp_app not in _ARGUMENT_BOOLEAN_TRUE_LEGACY:
|
||||||
return None
|
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(
|
debug(
|
||||||
'invalid: msvc version constraint: %s < %s VS2015',
|
'invalid: msvc version constraint: %s < %s VS2015',
|
||||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
err_msg = "MSVC_UWP_APP ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
|
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)
|
raise MSVCArgumentError(err_msg)
|
||||||
|
|
||||||
|
is_supported, is_target = Kind.msvc_version_uwp_is_supported(msvc.version, target_arch, env)
|
||||||
|
if not is_supported:
|
||||||
|
_, kind_str = Kind.get_msvc_version_kind(msvc.version)
|
||||||
|
debug(
|
||||||
|
'invalid: msvc_version constraint: %s %s %s',
|
||||||
|
repr(msvc.version), repr(kind_str), repr(target_arch)
|
||||||
|
)
|
||||||
|
if is_target and target_arch:
|
||||||
|
err_msg = "MSVC_UWP_APP ({}) TARGET_ARCH ({}) is not supported for MSVC_VERSION {} ({})".format(
|
||||||
|
repr(uwp_app), repr(target_arch), repr(msvc.version), repr(kind_str)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
err_msg = "MSVC_UWP_APP ({}) is not supported for MSVC_VERSION {} ({})".format(
|
||||||
|
repr(uwp_app), repr(msvc.version), repr(kind_str)
|
||||||
|
)
|
||||||
|
raise MSVCArgumentError(err_msg)
|
||||||
|
|
||||||
# VS2017+ rewrites uwp => store for 14.0 toolset
|
# VS2017+ rewrites uwp => store for 14.0 toolset
|
||||||
uwp_arg = msvc.vs_def.vc_uwp
|
uwp_arg = msvc.vs_def.vc_uwp
|
||||||
|
|
||||||
|
@ -250,16 +277,24 @@ def _user_script_argument_uwp(env, uwp, user_argstr) -> bool:
|
||||||
|
|
||||||
raise MSVCArgumentError(err_msg)
|
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(
|
debug(
|
||||||
'invalid: msvc_version constraint: %s < %s VS2015',
|
'invalid: msvc_version constraint: %s < %s VS2015',
|
||||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
err_msg = "MSVC_SDK_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
|
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
|
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:
|
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(
|
debug(
|
||||||
'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015',
|
'invalid: uwp/store SDK 8.1 msvc_version constraint: %s > %s VS2015',
|
||||||
repr(vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
if toolset and toolset.vs_def != msvc.vs_def:
|
if toolset and toolset.is_user:
|
||||||
err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset version {} > {} VS2015".format(
|
err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: toolset {} MSVC_VERSION {} > {} VS2015".format(
|
||||||
repr(sdk_version), repr(platform_def.vc_platform),
|
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:
|
else:
|
||||||
err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format(
|
err_msg = "MSVC_SDK_VERSION ({}) and platform type ({}) constraint violation: MSVC_VERSION {} > {} VS2015".format(
|
||||||
repr(sdk_version), repr(platform_def.vc_platform),
|
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
|
return err_msg
|
||||||
|
|
||||||
|
@ -310,7 +345,7 @@ def _msvc_script_argument_sdk(env, msvc, toolset, platform_def, arglist):
|
||||||
if not sdk_version:
|
if not sdk_version:
|
||||||
return None
|
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:
|
if err_msg:
|
||||||
raise MSVCArgumentError(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):
|
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
|
return None
|
||||||
|
|
||||||
sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
|
sdk_list = WinSDK.get_sdk_version_list(msvc.vs_def, platform_def)
|
||||||
|
@ -415,10 +453,11 @@ def _msvc_sxs_toolset_folder(msvc, sxs_folder):
|
||||||
if Util.is_toolset_sxs(sxs_folder):
|
if Util.is_toolset_sxs(sxs_folder):
|
||||||
return sxs_folder, sxs_folder
|
return sxs_folder, sxs_folder
|
||||||
|
|
||||||
key = (msvc.vs_def.vc_buildtools_def.vc_version, sxs_folder)
|
for vc_buildseries_def in msvc.vs_def.vc_buildtools_def.vc_buildseries_list:
|
||||||
if key in _msvc_sxs_bugfix_folder:
|
key = (vc_buildseries_def.vc_version, sxs_folder)
|
||||||
sxs_version = _msvc_sxs_bugfix_folder[key]
|
sxs_version = _msvc_sxs_bugfix_folder.get(key)
|
||||||
return sxs_folder, sxs_version
|
if sxs_version:
|
||||||
|
return sxs_folder, sxs_version
|
||||||
|
|
||||||
debug('sxs folder: ignore version=%s', repr(sxs_folder))
|
debug('sxs folder: ignore version=%s', repr(sxs_folder))
|
||||||
return None, None
|
return None, None
|
||||||
|
@ -574,49 +613,60 @@ def _msvc_version_toolset_vcvars(msvc, vc_dir, toolset_version):
|
||||||
|
|
||||||
def _msvc_script_argument_toolset_constraints(msvc, 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(
|
debug(
|
||||||
'invalid: msvc version constraint: %s < %s VS2017',
|
'invalid: msvc version constraint: %s < %s VS2017',
|
||||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2017.vc_buildtools_def.vc_version_numeric)
|
repr(VS2017.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
err_msg = "MSVC_TOOLSET_VERSION ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
|
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
|
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))
|
debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
|
||||||
err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
|
err_msg = 'MSVC_TOOLSET_VERSION {} format is not supported'.format(
|
||||||
repr(toolset_version)
|
repr(toolset_version)
|
||||||
)
|
)
|
||||||
return err_msg
|
return err_msg
|
||||||
|
|
||||||
toolset_vernum = float(toolset_verstr)
|
toolset_buildseries_def = Config.MSVC_BUILDSERIES_EXTERNAL.get(toolset_series)
|
||||||
|
if not toolset_buildseries_def:
|
||||||
if toolset_vernum < VS2015.vc_buildtools_def.vc_version_numeric:
|
debug('invalid: msvc version: toolset_version=%s', repr(toolset_version))
|
||||||
debug(
|
err_msg = 'MSVC_TOOLSET_VERSION {} build series {} is not supported'.format(
|
||||||
'invalid: toolset version constraint: %s < %s VS2015',
|
repr(toolset_version), repr(toolset_series)
|
||||||
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)
|
|
||||||
)
|
)
|
||||||
return err_msg
|
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(
|
debug(
|
||||||
'invalid: toolset version constraint: toolset %s > %s msvc',
|
'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)
|
repr(toolset_version), repr(toolset_verstr), repr(msvc.version)
|
||||||
)
|
)
|
||||||
return err_msg
|
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
|
# tooset = 14.0
|
||||||
if Util.is_toolset_full(toolset_version):
|
if Util.is_toolset_full(toolset_version):
|
||||||
if not Util.is_toolset_140(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',
|
'invalid: toolset version 14.0 constraint: %s != 14.0',
|
||||||
repr(toolset_version)
|
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)
|
repr(toolset_version), repr(toolset_version)
|
||||||
)
|
)
|
||||||
return err_msg
|
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):
|
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
|
return None
|
||||||
|
|
||||||
toolset_default = _msvc_default_toolset(msvc, vc_dir)
|
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):
|
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(
|
debug(
|
||||||
'invalid: msvc version constraint: %s < %s VS2017',
|
'invalid: msvc version constraint: %s < %s VS2017',
|
||||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2017.vc_buildtools_def.vc_version_numeric)
|
repr(VS2017.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: MSVC_VERSION {} < {} VS2017".format(
|
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
|
return err_msg
|
||||||
|
|
||||||
if toolset:
|
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(
|
debug(
|
||||||
'invalid: toolset version constraint: %s < %s VS2017',
|
'invalid: toolset version constraint: %s < %s VS2017',
|
||||||
repr(toolset.vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(toolset.vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2017.vc_buildtools_def.vc_version_numeric)
|
repr(VS2017.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
err_msg = "MSVC_SPECTRE_LIBS ({}) constraint violation: toolset version {} < {} VS2017".format(
|
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
|
return err_msg
|
||||||
|
|
||||||
|
@ -836,14 +886,14 @@ def _msvc_script_argument_user(env, msvc, arglist):
|
||||||
if not script_args:
|
if not script_args:
|
||||||
return None
|
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(
|
debug(
|
||||||
'invalid: msvc version constraint: %s < %s VS2015',
|
'invalid: msvc version constraint: %s < %s VS2015',
|
||||||
repr(msvc.vs_def.vc_buildtools_def.vc_version_numeric),
|
repr(msvc.vs_def.vc_buildtools_def.msvc_version_numeric),
|
||||||
repr(VS2015.vc_buildtools_def.vc_version_numeric)
|
repr(VS2015.vc_buildtools_def.msvc_version_numeric)
|
||||||
)
|
)
|
||||||
err_msg = "MSVC_SCRIPT_ARGS ({}) constraint violation: MSVC_VERSION {} < {} VS2015".format(
|
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)
|
raise MSVCArgumentError(err_msg)
|
||||||
|
|
||||||
|
@ -873,7 +923,18 @@ def _msvc_process_construction_variables(env) -> bool:
|
||||||
|
|
||||||
return False
|
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 []
|
arguments = [arg] if arg else []
|
||||||
|
|
||||||
|
@ -889,10 +950,12 @@ def msvc_script_arguments(env, version, vc_dir, arg):
|
||||||
|
|
||||||
if _msvc_process_construction_variables(env):
|
if _msvc_process_construction_variables(env):
|
||||||
|
|
||||||
|
target_arch = env.get('TARGET_ARCH')
|
||||||
|
|
||||||
# MSVC_UWP_APP
|
# MSVC_UWP_APP
|
||||||
|
|
||||||
if 'MSVC_UWP_APP' in env:
|
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:
|
else:
|
||||||
uwp = None
|
uwp = None
|
||||||
|
|
||||||
|
@ -926,7 +989,7 @@ def msvc_script_arguments(env, version, vc_dir, arg):
|
||||||
if user_toolset:
|
if user_toolset:
|
||||||
toolset = None
|
toolset = None
|
||||||
elif toolset_version:
|
elif toolset_version:
|
||||||
toolset = _toolset_version(toolset_version)
|
toolset = _toolset_version(toolset_version, is_user=True)
|
||||||
elif default_toolset:
|
elif default_toolset:
|
||||||
toolset = _toolset_version(default_toolset)
|
toolset = _toolset_version(default_toolset)
|
||||||
else:
|
else:
|
||||||
|
@ -958,7 +1021,7 @@ def msvc_script_arguments(env, version, vc_dir, arg):
|
||||||
if user_argstr:
|
if user_argstr:
|
||||||
_user_script_argument_spectre(env, spectre, 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:
|
if user_uwp and sdk_version and len(arglist) == 2:
|
||||||
# VS2015 toolset argument order issue: SDK store => store SDK
|
# VS2015 toolset argument order issue: SDK store => store SDK
|
||||||
arglist_reverse = True
|
arglist_reverse = True
|
||||||
|
@ -1003,6 +1066,14 @@ def _msvc_toolset_versions_internal(msvc_version, vc_dir, full: bool=True, sxs:
|
||||||
|
|
||||||
return toolset_versions
|
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):
|
def _msvc_toolset_versions_spectre_internal(msvc_version, vc_dir):
|
||||||
|
|
||||||
msvc = _msvc_version(msvc_version)
|
msvc = _msvc_version(msvc_version)
|
|
@ -37,6 +37,15 @@ from ..common import debug
|
||||||
|
|
||||||
from . import Config
|
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
|
# path utilities
|
||||||
|
|
||||||
# windows drive specification (e.g., 'C:')
|
# windows drive specification (e.g., 'C:')
|
||||||
|
@ -234,6 +243,26 @@ def get_msvc_version_prefix(version):
|
||||||
rval = m.group('version')
|
rval = m.group('version')
|
||||||
return rval
|
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
|
# toolset version query utilities
|
||||||
|
|
||||||
def is_toolset_full(toolset_version) -> bool:
|
def is_toolset_full(toolset_version) -> bool:
|
||||||
|
@ -322,15 +351,21 @@ def msvc_version_components(vcver):
|
||||||
return msvc_version_components_def
|
return msvc_version_components_def
|
||||||
|
|
||||||
_MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCExtendedVersionComponentsDefinition', [
|
_MSVC_EXTENDED_VERSION_COMPONENTS_DEFINITION = namedtuple('MSVCExtendedVersionComponentsDefinition', [
|
||||||
'msvc_version', # msvc version (e.g., '14.1Exp')
|
'msvc_version', # msvc version (e.g., '14.1Exp')
|
||||||
'msvc_verstr', # msvc version numeric string (e.g., '14.1')
|
'msvc_verstr', # msvc version numeric string (e.g., '14.1')
|
||||||
'msvc_suffix', # msvc version component type (e.g., 'Exp')
|
'msvc_suffix', # msvc version component type (e.g., 'Exp')
|
||||||
'msvc_vernum', # msvc version floating point number (e.g, 14.1)
|
'msvc_suffix_rank', # msvc version component rank (0, 1)
|
||||||
'msvc_major', # msvc major version integer number (e.g., 14)
|
'msvc_vernum', # msvc version floating point number (e.g, 14.1)
|
||||||
'msvc_minor', # msvc minor version integer number (e.g., 1)
|
'msvc_major', # msvc major version integer number (e.g., 14)
|
||||||
'msvc_comps', # msvc version components tuple (e.g., ('14', '1'))
|
'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_version', # msvc toolset version
|
||||||
'msvc_toolset_comps', # msvc toolset version components
|
'msvc_toolset_comps', # msvc toolset version components
|
||||||
|
'msvc_toolset_is_sxs', # msvc toolset version is sxs
|
||||||
'version', # msvc version or msvc toolset version
|
'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_version = m.group('version')
|
||||||
msvc_toolset_comps = tuple(msvc_toolset_version.split('.'))
|
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)
|
vc_verstr = get_msvc_version_prefix(msvc_toolset_version)
|
||||||
if not msvc_verstr:
|
if not vc_verstr:
|
||||||
return None
|
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_suffix = m.group('suffix') if m.group('suffix') else ''
|
||||||
msvc_version = msvc_verstr + msvc_suffix
|
msvc_version = msvc_verstr + msvc_suffix
|
||||||
|
|
||||||
|
@ -376,12 +419,18 @@ def msvc_extended_version_components(version):
|
||||||
msvc_version = msvc_version,
|
msvc_version = msvc_version,
|
||||||
msvc_verstr = msvc_verstr,
|
msvc_verstr = msvc_verstr,
|
||||||
msvc_suffix = msvc_suffix,
|
msvc_suffix = msvc_suffix,
|
||||||
|
msvc_suffix_rank = 0 if not msvc_suffix else 1,
|
||||||
msvc_vernum = msvc_vernum,
|
msvc_vernum = msvc_vernum,
|
||||||
msvc_major = msvc_major,
|
msvc_major = msvc_major,
|
||||||
msvc_minor = msvc_minor,
|
msvc_minor = msvc_minor,
|
||||||
msvc_comps = msvc_comps,
|
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_version = msvc_toolset_version,
|
||||||
msvc_toolset_comps = msvc_toolset_comps,
|
msvc_toolset_comps = msvc_toolset_comps,
|
||||||
|
msvc_toolset_is_sxs = msvc_toolset_is_sxs,
|
||||||
version = version,
|
version = version,
|
||||||
)
|
)
|
||||||
|
|
|
@ -40,6 +40,7 @@ from . import Exceptions # noqa: F401
|
||||||
from . import Config # noqa: F401
|
from . import Config # noqa: F401
|
||||||
from . import Util # noqa: F401
|
from . import Util # noqa: F401
|
||||||
from . import Registry # noqa: F401
|
from . import Registry # noqa: F401
|
||||||
|
from . import Kind # noqa: F401
|
||||||
from . import SetupEnvDefault # noqa: F401
|
from . import SetupEnvDefault # noqa: F401
|
||||||
from . import Policy # noqa: F401
|
from . import Policy # noqa: F401
|
||||||
from . import WinSDK # 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.
|
``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
|
Known Issues
|
||||||
============
|
============
|
||||||
|
|
||||||
The following issues are known to exist:
|
The following issues are known to exist:
|
||||||
|
|
||||||
* Using ``MSVC_USE_SCRIPT`` and ``MSVC_USE_SCRIPT_ARGS`` to call older Microsoft SDK
|
* 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
|
``SetEnv.cmd`` batch files may result in build failures.
|
||||||
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
|
Typically, the reasons for build failures with SDK batch files are one, or both, of:
|
||||||
instance with delayed expansion enabled via command-line options.
|
|
||||||
|
* 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 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``.
|
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
|
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``
|
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
|
and is available via the ``SCons.Tool.MSCommon`` namespace.
|
||||||
specification or a toolset version specification and a product preference as arguments and
|
|
||||||
returns the msvc version and the msvc toolset version for the corresponding version specification.
|
This 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.
|
This is a proxy for using the toolset version for selection until that functionality can be added.
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
for version in [
|
for version in [
|
||||||
|
'14.4',
|
||||||
'14.3',
|
'14.3',
|
||||||
'14.2',
|
'14.2',
|
||||||
'14.1',
|
'14.1',
|
||||||
|
@ -90,6 +254,7 @@ Example usage:
|
||||||
print('{}Query: {} version={}, prefer_newest={}'.format(newline, msg, version, prefer_newest))
|
print('{}Query: {} version={}, prefer_newest={}'.format(newline, msg, version, prefer_newest))
|
||||||
|
|
||||||
Example output fragment
|
Example output fragment
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
Build: _build003 {'MSVC_VERSION': '14.3', 'MSVC_TOOLSET_VERSION': '14.29.30133'}
|
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.
|
updates that may change the default toolset version and/or the default SDK version.
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@echo Enabling scons cache ...
|
@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.
|
for build failures. Refer to the documentation for details.
|
||||||
|
|
||||||
Change the default policy:
|
Change the default policy:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
from SCons.Tool.MSCommon import msvc_set_scripterror_policy
|
from SCons.Tool.MSCommon import msvc_set_scripterror_policy
|
||||||
|
@ -146,6 +313,7 @@ Change the default policy:
|
||||||
msvc_set_scripterror_policy('Warning')
|
msvc_set_scripterror_policy('Warning')
|
||||||
|
|
||||||
Specify the policy per-environment:
|
Specify the policy per-environment:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
env = Environment(MSVC_VERSION='14.3', MSVC_SPECTRE_LIBS=True, MSVC_SCRIPTERROR_POLICY='Warning')
|
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.
|
detection of installed msvc instances.
|
||||||
|
|
||||||
Windows command-line sample invocations:
|
Windows command-line sample invocations:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@rem 64-Bit Windows
|
@rem 64-Bit Windows
|
||||||
|
@ -188,31 +357,37 @@ Batch File Arguments
|
||||||
|
|
||||||
Supported MSVC batch file arguments by product:
|
Supported MSVC batch file arguments by product:
|
||||||
|
|
||||||
======= === === ======= =======
|
+---------+---------+--------+---------+---------+
|
||||||
Product UWP SDK Toolset Spectre
|
| Product | UWP | SDK | Toolset | Spectre |
|
||||||
======= === === ======= =======
|
+=========+=========+========+=========+=========+
|
||||||
VS2022 X X X X
|
| VS2022 | X | X | X | X |
|
||||||
------- --- --- ------- -------
|
+---------+---------+--------+---------+---------+
|
||||||
VS2019 X X X X
|
| VS2019 | X | X | X | X |
|
||||||
------- --- --- ------- -------
|
+---------+---------+--------+---------+---------+
|
||||||
VS2017 X X X X
|
| VS2017 | X | X | X | X |
|
||||||
------- --- --- ------- -------
|
+---------+---------+--------+---------+---------+
|
||||||
VS2015 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:
|
Supported MSVC batch file arguments in SCons:
|
||||||
|
|
||||||
======== ====================================== ===================================================
|
+----------+----------------------------------------+-----------------------------------------------------+
|
||||||
Argument Construction Variable Script Argument Equivalent
|
| Argument | Construction Variable | Script Argument Equivalent |
|
||||||
======== ====================================== ===================================================
|
+==========+========================================+=====================================================+
|
||||||
UWP ``MSVC_UWP_APP=True`` ``MSVC_SCRIPT_ARGS='store'``
|
| UWP | ``MSVC_UWP_APP=True`` | ``MSVC_SCRIPT_ARGS='store'`` |
|
||||||
-------- -------------------------------------- ---------------------------------------------------
|
+----------+----------------------------------------+-----------------------------------------------------+
|
||||||
SDK ``MSVC_SDK_VERSION='10.0.20348.0'`` ``MSVC_SCRIPT_ARGS='10.0.20348.0'``
|
| 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'``
|
| 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'``
|
| 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
|
**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.**
|
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.
|
toolset specification should be omitted entirely.
|
||||||
|
|
||||||
Local installation and summary test results:
|
Local installation and summary test results:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt
|
VS2022\VC\Auxiliary\Build\Microsoft.VCToolsVersion.v143.default.txt
|
||||||
|
@ -253,6 +429,7 @@ Local installation and summary test results:
|
||||||
14.32.31326
|
14.32.31326
|
||||||
|
|
||||||
Toolset version summary:
|
Toolset version summary:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
14.31.31103 Environment()
|
14.31.31103 Environment()
|
||||||
|
@ -268,6 +445,7 @@ Toolset version summary:
|
||||||
14.32.31326 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.32'])
|
14.32.31326 Environment(MSVC_SCRIPT_ARGS=['-vcvars_ver=14.32'])
|
||||||
|
|
||||||
VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment:
|
VS2022\\Common7\\Tools\\vsdevcmd\\ext\\vcvars.bat usage fragment:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@echo -vcvars_ver=version : Version of VC++ Toolset to select
|
@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.
|
@echo SxS toolset to [VSInstallDir]\VC\MSVC\Tools\ directory.
|
||||||
|
|
||||||
VS2022 batch file fragment to determine the default toolset version:
|
VS2022 batch file fragment to determine the default toolset version:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
@REM Add MSVC
|
@REM Add MSVC
|
||||||
|
@ -315,75 +494,114 @@ Visual Studio Version Notes
|
||||||
SDK Versions
|
SDK Versions
|
||||||
------------
|
------------
|
||||||
|
|
||||||
==== ============
|
+------+-------------------+
|
||||||
SDK Format
|
| SDK | Format |
|
||||||
==== ============
|
+======+===================+
|
||||||
10.0 10.0.XXXXX.Y
|
| 10.0 | 10.0.XXXXX.Y [1] |
|
||||||
---- ------------
|
+------+-------------------+
|
||||||
8.1 8.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 Versions
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
========== ===== ===== ========
|
+------------+-------------+----------+
|
||||||
BuildTools VCVER CLVER MSVCRT
|
| BuildTools | BuildSeries | MSVCRT |
|
||||||
========== ===== ===== ========
|
+============+=============+==========+
|
||||||
v143 14.3 19.3 140/ucrt
|
| v143 | 14.4, 14.3 | 140/ucrt |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v142 14.2 19.2 140/ucrt
|
| v142 | 14.2 | 140/ucrt |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v141 14.1 19.1 140/ucrt
|
| v141 | 14.1 | 140/ucrt |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v140 14.0 19.0 140/ucrt
|
| v140 | 14.0 | 140/ucrt |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v120 12.0 18.0 120
|
| v120 | 12.0 | 120 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v110 11.0 17.0 110
|
| v110 | 11.0 | 110 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v100 10.0 16.0 100
|
| v100 | 10.0 | 100 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v90 9.0 15.0 90
|
| v90 | 9.0 | 90 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v80 8.0 14.0 80
|
| v80 | 8.0 | 80 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v71 7.1 13.1 71
|
| v71 | 7.1 | 71 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v70 7.0 13.0 70
|
| v70 | 7.0 | 70 |
|
||||||
---------- ----- ----- --------
|
+------------+-------------+----------+
|
||||||
v60 6.0 12.0 60
|
| v60 | 6.0 | 60 |
|
||||||
========== ===== ===== ========
|
+------------+-------------+----------+
|
||||||
|
|
||||||
Product Versions
|
Product Versions
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
======== ===== ========= ======================
|
+----------+-------+-------+-----------+------------------------+
|
||||||
Product VSVER SDK BuildTools
|
| Product | VSVER | SCons | SDK | BuildTools |
|
||||||
======== ===== ========= ======================
|
+==========+=======+=======+===========+========================+
|
||||||
2022 17.0 10.0, 8.1 v143, v142, v141, v140
|
| 2022 | 17.0 | 14.3 | 10.0, 8.1 | v143, v142, v141, v140 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2019 16.0 10.0, 8.1 v142, v141, v140
|
| 2019 | 16.0 | 14.2 | 10.0, 8.1 | v142, v141, v140 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2017 15.0 10.0, 8.1 v141, v140
|
| 2017 | 15.0 | 14.1 | 10.0, 8.1 | v141, v140 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2015 14.0 10.0, 8.1 v140
|
| 2015 | 14.0 | 14.0 | 10.0, 8.1 | v140 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2013 12.0 v120
|
| 2013 | 12.0 | 12.0 | | v120 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2012 11.0 v110
|
| 2012 | 11.0 | 11.0 | | v110 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2010 10.0 v100
|
| 2010 | 10.0 | 10.0 | | v100 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2008 9.0 v90
|
| 2008 | 9.0 | 9.0 | | v90 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2005 8.0 v80
|
| 2005 | 8.0 | 8.0 | | v80 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2003.NET 7.1 v71
|
| 2003.NET | 7.1 | 7.1 | | v71 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
2002.NET 7.0 v70
|
| 2002.NET | 7.0 | 7.0 | | v70 |
|
||||||
-------- ----- --------- ----------------------
|
+----------+-------+-------+-----------+------------------------+
|
||||||
6.0 6.0 v60
|
| 6.0 | 6.0 | 6.0 | | v60 |
|
||||||
======== ===== ========= ======================
|
+----------+-------+-------+-----------+------------------------+
|
||||||
|
|
||||||
|
|
||||||
SCons Implementation Notes
|
SCons Implementation Notes
|
|
@ -45,6 +45,9 @@ from SCons.Tool.MSCommon.vc import ( # noqa: F401
|
||||||
msvc_toolset_versions,
|
msvc_toolset_versions,
|
||||||
msvc_toolset_versions_spectre,
|
msvc_toolset_versions_spectre,
|
||||||
msvc_query_version_toolset,
|
msvc_query_version_toolset,
|
||||||
|
vswhere_register_executable,
|
||||||
|
vswhere_get_executable,
|
||||||
|
vswhere_freeze_executable,
|
||||||
)
|
)
|
||||||
|
|
||||||
from SCons.Tool.MSCommon.vs import ( # noqa: F401
|
from SCons.Tool.MSCommon.vs import ( # noqa: F401
|
||||||
|
@ -60,6 +63,8 @@ from .MSVC.Policy import ( # noqa: F401
|
||||||
msvc_get_notfound_policy,
|
msvc_get_notfound_policy,
|
||||||
msvc_set_scripterror_policy,
|
msvc_set_scripterror_policy,
|
||||||
msvc_get_scripterror_policy,
|
msvc_get_scripterror_policy,
|
||||||
|
msvc_notfound_policy_contextmanager,
|
||||||
|
msvc_scripterror_policy_contextmanager,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .MSVC.Exceptions import ( # noqa: F401
|
from .MSVC.Exceptions import ( # noqa: F401
|
||||||
|
@ -78,7 +83,9 @@ from .vc import ( # noqa: F401
|
||||||
MSVCUnsupportedHostArch,
|
MSVCUnsupportedHostArch,
|
||||||
MSVCUnsupportedTargetArch,
|
MSVCUnsupportedTargetArch,
|
||||||
MSVCScriptNotFound,
|
MSVCScriptNotFound,
|
||||||
|
MSVCUseScriptError,
|
||||||
MSVCUseSettingsError,
|
MSVCUseSettingsError,
|
||||||
|
VSWhereUserError,
|
||||||
)
|
)
|
||||||
|
|
||||||
from .MSVC.Util import ( # noqa: F401
|
from .MSVC.Util import ( # noqa: F401
|
|
@ -372,7 +372,7 @@ def mssdk_setup_env(env):
|
||||||
return
|
return
|
||||||
msvs_version = env.subst(msvs_version)
|
msvs_version = env.subst(msvs_version)
|
||||||
from . import vs
|
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)
|
debug('mssdk_setup_env:msvs is :%s', msvs)
|
||||||
if not msvs:
|
if not msvs:
|
||||||
debug('mssdk_setup_env: no VS version detected, bailingout:%s', 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 os
|
||||||
|
|
||||||
import SCons.Errors
|
import SCons.Errors
|
||||||
import SCons.Tool.MSCommon.vc
|
|
||||||
import SCons.Util
|
import SCons.Util
|
||||||
|
|
||||||
from .common import (
|
from .common import (
|
||||||
|
@ -40,6 +39,19 @@ from .common import (
|
||||||
read_reg,
|
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:
|
class VisualStudio:
|
||||||
"""
|
"""
|
||||||
|
@ -48,6 +60,9 @@ class VisualStudio:
|
||||||
"""
|
"""
|
||||||
def __init__(self, version, **kw) -> None:
|
def __init__(self, version, **kw) -> None:
|
||||||
self.version = version
|
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['vc_version'] = kw.get('vc_version', version)
|
||||||
kw['sdk_version'] = kw.get('sdk_version', version)
|
kw['sdk_version'] = kw.get('sdk_version', version)
|
||||||
self.__dict__.update(kw)
|
self.__dict__.update(kw)
|
||||||
|
@ -66,7 +81,7 @@ class VisualStudio:
|
||||||
return batch_file
|
return batch_file
|
||||||
|
|
||||||
def find_vs_dir_by_vc(self, env):
|
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:
|
if not dir:
|
||||||
debug('no installed VC %s', self.vc_version)
|
debug('no installed VC %s', self.vc_version)
|
||||||
return None
|
return None
|
||||||
|
@ -428,6 +443,7 @@ InstalledVSMap = None
|
||||||
def get_installed_visual_studios(env=None):
|
def get_installed_visual_studios(env=None):
|
||||||
global InstalledVSList
|
global InstalledVSList
|
||||||
global InstalledVSMap
|
global InstalledVSMap
|
||||||
|
vswhere_freeze_env(env)
|
||||||
if InstalledVSList is None:
|
if InstalledVSList is None:
|
||||||
InstalledVSList = []
|
InstalledVSList = []
|
||||||
InstalledVSMap = {}
|
InstalledVSMap = {}
|
||||||
|
@ -436,12 +452,21 @@ def get_installed_visual_studios(env=None):
|
||||||
if vs.get_executable(env):
|
if vs.get_executable(env):
|
||||||
debug('found VS %s', vs.version)
|
debug('found VS %s', vs.version)
|
||||||
InstalledVSList.append(vs)
|
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
|
InstalledVSMap[vs.version] = vs
|
||||||
return InstalledVSList
|
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:
|
def reset_installed_visual_studios() -> None:
|
||||||
global InstalledVSList
|
global InstalledVSList
|
||||||
global InstalledVSMap
|
global InstalledVSMap
|
||||||
|
debug('')
|
||||||
InstalledVSList = None
|
InstalledVSList = None
|
||||||
InstalledVSMap = None
|
InstalledVSMap = None
|
||||||
for vs in SupportedVSList:
|
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
|
# Need to clear installed VC's as well as they are used in finding
|
||||||
# installed VS's
|
# installed VS's
|
||||||
SCons.Tool.MSCommon.vc.reset_installed_vcs()
|
reset_installed_vcs()
|
||||||
|
|
||||||
|
|
||||||
# We may be asked to update multiple construction environments with
|
# 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:
|
def msvs_exists(env=None) -> bool:
|
||||||
return len(get_installed_visual_studios(env)) > 0
|
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 InstalledVSMap
|
||||||
global SupportedVSMap
|
global SupportedVSMap
|
||||||
|
|
||||||
|
@ -495,7 +520,7 @@ def get_vs_by_version(msvs):
|
||||||
if msvs not in SupportedVSMap:
|
if msvs not in SupportedVSMap:
|
||||||
msg = "Visual Studio version %s is not supported" % repr(msvs)
|
msg = "Visual Studio version %s is not supported" % repr(msvs)
|
||||||
raise SCons.Errors.UserError(msg)
|
raise SCons.Errors.UserError(msg)
|
||||||
get_installed_visual_studios()
|
get_installed_visual_studios(env)
|
||||||
vs = InstalledVSMap.get(msvs)
|
vs = InstalledVSMap.get(msvs)
|
||||||
debug('InstalledVSMap:%s', InstalledVSMap)
|
debug('InstalledVSMap:%s', InstalledVSMap)
|
||||||
debug('found vs:%s', vs)
|
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']):
|
if 'MSVS' not in env or not SCons.Util.is_Dict(env['MSVS']):
|
||||||
# get all versions, and remember them for speed later
|
# 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}
|
env['MSVS'] = {'VERSIONS' : versions}
|
||||||
else:
|
else:
|
||||||
versions = env['MSVS'].get('VERSIONS', [])
|
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?
|
# TODO: refers to versions and arch which aren't defined; called nowhere. Drop?
|
||||||
def msvs_setup_env(env) -> None:
|
def msvs_setup_env(env) -> None:
|
||||||
msvs = get_vs_by_version(version)
|
msvs = get_vs_by_version(version, env)
|
||||||
if msvs is None:
|
if msvs is None:
|
||||||
return
|
return
|
||||||
batfilename = msvs.get_batch_file()
|
batfilename = msvs.get_batch_file()
|
||||||
|
@ -582,7 +607,7 @@ def msvs_setup_env(env) -> None:
|
||||||
|
|
||||||
vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE')
|
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]
|
vscommonvarnames = [vs.common_tools_var for vs in msvs_list]
|
||||||
save_ENV = env['ENV']
|
save_ENV = env['ENV']
|
||||||
nenv = normalize_env(env['ENV'],
|
nenv = normalize_env(env['ENV'],
|
||||||
|
@ -597,11 +622,10 @@ def msvs_setup_env(env) -> None:
|
||||||
for k, v in vars.items():
|
for k, v in vars.items():
|
||||||
env.PrependENVPath(k, v, delete_existing=1)
|
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
|
"""Query the system to get available versions of VS. A version is
|
||||||
considered when a batfile is found."""
|
considered when a batfile is found."""
|
||||||
msvs_list = get_installed_visual_studios()
|
versions = _get_installed_vss(env)
|
||||||
versions = [msvs.version for msvs in msvs_list]
|
|
||||||
return versions
|
return versions
|
||||||
|
|
||||||
# Local Variables:
|
# Local Variables:
|
|
@ -251,7 +251,7 @@ class Tool:
|
||||||
kw.update(call_kw)
|
kw.update(call_kw)
|
||||||
else:
|
else:
|
||||||
kw = self.init_kw
|
kw = self.init_kw
|
||||||
env.Append(TOOLS=[self.name])
|
env.AppendUnique(TOOLS=[self.name])
|
||||||
if hasattr(self, 'options'):
|
if hasattr(self, 'options'):
|
||||||
import SCons.Variables
|
import SCons.Variables
|
||||||
if 'options' not in env:
|
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