summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorWilliam Deegan <bill@baddogconsulting.com>2019-07-19 21:27:47 (GMT)
committerGitHub <noreply@github.com>2019-07-19 21:27:47 (GMT)
commit116364e65a86d3ca9b596bc1d589832595a57443 (patch)
tree4cf050f3ebbda29c94f80c354e25fa0b976946ba /src
parent9f49d9c9b11bd3aa69fef5944838f52f49b804ae (diff)
parentfe8e5013e9c0a313efe97c9eb55dc8f050ac5e0a (diff)
downloadSCons-116364e65a86d3ca9b596bc1d589832595a57443.zip
SCons-116364e65a86d3ca9b596bc1d589832595a57443.tar.gz
SCons-116364e65a86d3ca9b596bc1d589832595a57443.tar.bz2
Merge branch 'master' into topic/grossag/msvs
Diffstat (limited to 'src')
-rwxr-xr-xsrc/CHANGES.txt25
-rwxr-xr-xsrc/RELEASE.txt16
-rw-r--r--src/engine/SCons/Action.py2
-rw-r--r--src/engine/SCons/Environment.py54
-rw-r--r--src/engine/SCons/Environment.xml13
-rw-r--r--src/engine/SCons/EnvironmentTests.py11
-rw-r--r--src/engine/SCons/Node/FS.py110
-rw-r--r--src/engine/SCons/Node/__init__.py47
-rw-r--r--src/engine/SCons/SConf.py9
-rw-r--r--src/engine/SCons/Tool/MSCommon/vc.py91
-rw-r--r--src/engine/SCons/Tool/MSCommon/vs.py11
-rw-r--r--src/engine/SCons/Tool/msvc.xml1
12 files changed, 250 insertions, 140 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index ad42104..2b3e627 100755
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -9,8 +9,9 @@
RELEASE VERSION/DATE TO BE FILLED IN LATER
- From Adam Gross:
- - Upgrade and improve Visual Studio solution/project generation code
+ From Joseph Brill:
+ - Code to supply correct version-specifier argument to vswhere for
+ VS version selection.
From Peter Diener:
- Additional fix to issue #3135 - Also handle 'pure' and 'elemental' type bound procedures
@@ -44,14 +45,30 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Fix Issue #3350 - mslink failing when too many objects. This is resolved by adding TEMPFILEARGJOIN variable
which specifies what character to join all the argements output into the tempfile. The default remains a space
when mslink, msvc, or mslib tools are loaded they change the TEMPFILEARGJOIN to be a line separator (\r\n on win32)
+ - Fix performance degradation for MD5-timestamp decider. NOTE: This changes the Decider() function arguments.
+ From:
+ def my_decider(dependency, target, prev_ni):
+ To:
+ def my_decider(dependency, target, prev_ni, repo_node):
+ Where repo_node is the repository (or other) node to use to check if the node is out of date instead of dependency.
+
+ From Adam Gross:
+ - Upgrade and improve Visual Studio solution/project generation code
+
From Michael Hartmann:
- Fix handling of Visual Studio Compilers to properly reject any unknown HOST_PLATFORM or TARGET_PLATFORM
+ From Bert Huijben:
+ - Added support for Visual Studio 2019 toolset.
+
From Mathew Robinson:
- Update cache debug output to include cache hit rate.
- No longer unintentionally hide exceptions in Action.py
+ From Leonard de Ruijter:
+ - Add logic to derive correct version argument to vswhere
+
From Lukas Schrangl:
- Enable LaTeX scanner to find more than one include per line
@@ -82,6 +99,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER
- Fix more re patterns that contain \ but not specified as raw strings
(affects scanners for D, LaTeX, swig)
+ From Mathew Robinson:
+ - Update cache debug output to include cache hit rate.
+ - No longer unintentionally hide exceptions in Action.py
+ - Allow builders and pseudo-builders to inherit from OverrideEnvironments
RELEASE 3.0.5 - Mon, 26 Mar 2019 15:04:42 -0700
diff --git a/src/RELEASE.txt b/src/RELEASE.txt
index efa35eb..9e4e314 100755
--- a/src/RELEASE.txt
+++ b/src/RELEASE.txt
@@ -41,6 +41,14 @@
CHANGED/ENHANCED EXISTING FUNCTIONALITY
+ - Fix performance degradation for MD5-timestamp decider. NOTE: This changes the Decider() function arguments.
+ From:
+ def my_decider(dependency, target, prev_ni):
+ To:
+ def my_decider(dependency, target, prev_ni, repo_node):
+ Where repo_node is the repository (or other) node to use to check if the node is out of date instead of dependency.
+
+
- Enhanced --debug=explain output. Now the separate components of the dependency list are split up
as follows:
@@ -54,6 +62,14 @@
->Implicit
Old:/usr/bin/python New:/usr/bin/python
+
+ - Changed: Pseudo-builders now inherit OverrideEnvironments. For
+ example when calling a pseudo-builder from another
+ pseudo-builder the override variables passed to the first
+ pseudo-builder call had to be explicitly passed on to the
+ internal pseudo-builder call. Now the second pseudo-builder call
+ will automatically inherit these override values.
+
FIXES
- List fixes of outright bugs
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 3ec8a4c..c6fc575 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -534,7 +534,7 @@ class ActionBase(object):
result = self.get_presig(target, source, env)
if not isinstance(result,(bytes, bytearray)):
- result = bytearray("",'utf-8').join([ SCons.Util.to_bytes(r) for r in result ])
+ result = bytearray(result, 'utf-8')
else:
# Make a copy and put in bytearray, without this the contents returned by get_presig
# can be changed by the logic below, appending with each call and causing very
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 7f9a76c..8d06af7 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -864,18 +864,21 @@ class SubstitutionEnvironment(object):
return self
-def default_decide_source(dependency, target, prev_ni):
+def default_decide_source(dependency, target, prev_ni, repo_node=None):
f = SCons.Defaults.DefaultEnvironment().decide_source
- return f(dependency, target, prev_ni)
+ return f(dependency, target, prev_ni, repo_node)
-def default_decide_target(dependency, target, prev_ni):
+
+def default_decide_target(dependency, target, prev_ni, repo_node=None):
f = SCons.Defaults.DefaultEnvironment().decide_target
- return f(dependency, target, prev_ni)
+ return f(dependency, target, prev_ni, repo_node)
+
def default_copy_from_cache(src, dst):
f = SCons.Defaults.DefaultEnvironment().copy_from_cache
return f(src, dst)
+
class Base(SubstitutionEnvironment):
"""Base class for "real" construction Environments. These are the
primary objects used to communicate dependency and construction
@@ -1434,30 +1437,30 @@ class Base(SubstitutionEnvironment):
_warn_copy_deprecated = False
return self.Clone(*args, **kw)
- def _changed_build(self, dependency, target, prev_ni):
- if dependency.changed_state(target, prev_ni):
+ def _changed_build(self, dependency, target, prev_ni, repo_node=None):
+ if dependency.changed_state(target, prev_ni, repo_node):
return 1
- return self.decide_source(dependency, target, prev_ni)
+ return self.decide_source(dependency, target, prev_ni, repo_node)
- def _changed_content(self, dependency, target, prev_ni):
- return dependency.changed_content(target, prev_ni)
+ def _changed_content(self, dependency, target, prev_ni, repo_node=None):
+ return dependency.changed_content(target, prev_ni, repo_node)
- def _changed_source(self, dependency, target, prev_ni):
+ def _changed_source(self, dependency, target, prev_ni, repo_node=None):
target_env = dependency.get_build_env()
type = target_env.get_tgt_sig_type()
if type == 'source':
- return target_env.decide_source(dependency, target, prev_ni)
+ return target_env.decide_source(dependency, target, prev_ni, repo_node)
else:
- return target_env.decide_target(dependency, target, prev_ni)
+ return target_env.decide_target(dependency, target, prev_ni, repo_node)
- def _changed_timestamp_then_content(self, dependency, target, prev_ni):
- return dependency.changed_timestamp_then_content(target, prev_ni)
+ def _changed_timestamp_then_content(self, dependency, target, prev_ni, repo_node=None):
+ return dependency.changed_timestamp_then_content(target, prev_ni, repo_node)
- def _changed_timestamp_newer(self, dependency, target, prev_ni):
- return dependency.changed_timestamp_newer(target, prev_ni)
+ def _changed_timestamp_newer(self, dependency, target, prev_ni, repo_node=None):
+ return dependency.changed_timestamp_newer(target, prev_ni, repo_node)
- def _changed_timestamp_match(self, dependency, target, prev_ni):
- return dependency.changed_timestamp_match(target, prev_ni)
+ def _changed_timestamp_match(self, dependency, target, prev_ni, repo_node=None):
+ return dependency.changed_timestamp_match(target, prev_ni, repo_node)
def _copy_from_cache(self, src, dst):
return self.fs.copy(src, dst)
@@ -2305,7 +2308,20 @@ class OverrideEnvironment(Base):
# Methods that make this class act like a proxy.
def __getattr__(self, name):
- return getattr(self.__dict__['__subject'], name)
+ attr = getattr(self.__dict__['__subject'], name)
+ # Here we check if attr is one of the Wrapper classes. For
+ # example when a pseudo-builder is being called from an
+ # OverrideEnvironment.
+ #
+ # These wrappers when they're constructed capture the
+ # Environment they are being constructed with and so will not
+ # have access to overrided values. So we rebuild them with the
+ # OverrideEnvironment so they have access to overrided values.
+ if isinstance(attr, (MethodWrapper, BuilderWrapper)):
+ return attr.clone(self)
+ else:
+ return attr
+
def __setattr__(self, name, value):
setattr(self.__dict__['__subject'], name, value)
diff --git a/src/engine/SCons/Environment.xml b/src/engine/SCons/Environment.xml
index 0982c31..1b0a04c 100644
--- a/src/engine/SCons/Environment.xml
+++ b/src/engine/SCons/Environment.xml
@@ -1213,6 +1213,17 @@ size, or content signature.
</para>
</listitem>
</varlistentry>
+ <varlistentry>
+<term><parameter>repo_node</parameter></term>
+<listitem>
+<para>
+Use this node instead of the one specified by
+<varname>dependency</varname>
+ to determine if the dependency has changed.
+</para>
+</listitem>
+</varlistentry>
+
</variablelist>
</para>
@@ -1249,7 +1260,7 @@ Example:
</para>
<example_commands>
-def my_decider(dependency, target, prev_ni):
+def my_decider(dependency, target, prev_ni, repo_node=None):
return not os.path.exists(str(target))
env.Decider(my_decider)
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 1a75a90..834cfd1 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -3587,6 +3587,10 @@ class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
def setUp(self):
env = Environment()
env._dict = {'XXX' : 'x', 'YYY' : 'y'}
+ def verify_value(env, key, value, *args, **kwargs):
+ """Verifies that key is value on the env this is called with."""
+ assert env[key] == value
+ env.AddMethod(verify_value)
env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
self.envs = [ env, env2, env3 ]
@@ -3777,6 +3781,13 @@ class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
# """Test the OverrideEnvironment WhereIs() method"""
# pass
+ def test_PseudoBuilderInherits(self):
+ """Test that pseudo-builders inherit the overrided values."""
+ env, env2, env3 = self.envs
+ env.verify_value('XXX', 'x')
+ env2.verify_value('XXX', 'x2')
+ env3.verify_value('XXX', 'x3')
+
def test_Dir(self):
"""Test the OverrideEnvironment Dir() method"""
env, env2, env3 = self.envs
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 91d349d..33105fb 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -57,7 +57,6 @@ import SCons.Util
import SCons.Warnings
from SCons.Debug import Trace
-from . import DeciderNeedsNode
print_duplicate = 0
@@ -2261,7 +2260,7 @@ class RootDir(Dir):
this directory.
"""
- __slots__ = ['_lookupDict']
+ __slots__ = ('_lookupDict', )
def __init__(self, drive, fs):
if SCons.Debug.track_instances: logInstanceCreation(self, 'Node.FS.RootDir')
@@ -2467,7 +2466,7 @@ class FileNodeInfo(SCons.Node.NodeInfoBase):
"""
state = getattr(self, '__dict__', {}).copy()
for obj in type(self).mro():
- for name in getattr(obj,'__slots__',()):
+ for name in getattr(obj, '__slots__', ()):
if hasattr(self, name):
state[name] = getattr(self, name)
@@ -2511,7 +2510,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
or count of any of these could yield writing wrong csig, and then false positive
rebuilds
"""
- __slots__ = ('dependency_map')
+ __slots__ = ['dependency_map', ]
current_version_id = 2
def __setattr__(self, key, value):
@@ -3283,14 +3282,14 @@ class File(Base):
self._memo['changed'] = has_changed
return has_changed
- def changed_content(self, target, prev_ni):
+ def changed_content(self, target, prev_ni, repo_node=None):
cur_csig = self.get_csig()
try:
return cur_csig != prev_ni.csig
except AttributeError:
return 1
- def changed_state(self, target, prev_ni):
+ def changed_state(self, target, prev_ni, repo_node=None):
return self.state != SCons.Node.up_to_date
@@ -3317,13 +3316,26 @@ class File(Base):
len(binfo.bimplicitsigs)) == 0:
return {}
-
- # store this info so we can avoid regenerating it.
- binfo.dependency_map = { str(child):signature for child, signature in zip(chain(binfo.bsources, binfo.bdepends, binfo.bimplicit),
+ binfo.dependency_map = { child:signature for child, signature in zip(chain(binfo.bsources, binfo.bdepends, binfo.bimplicit),
chain(binfo.bsourcesigs, binfo.bdependsigs, binfo.bimplicitsigs))}
return binfo.dependency_map
+ # @profile
+ def _add_strings_to_dependency_map(self, dmap):
+ """
+ In the case comparing node objects isn't sufficient, we'll add the strings for the nodes to the dependency map
+ :return:
+ """
+
+ first_string = str(next(iter(dmap)))
+
+ # print("DMAP:%s"%id(dmap))
+ if first_string not in dmap:
+ string_dict = {str(child): signature for child, signature in dmap.items()}
+ dmap.update(string_dict)
+ return dmap
+
def _get_previous_signatures(self, dmap):
"""
Return a list of corresponding csigs from previous
@@ -3342,37 +3354,62 @@ class File(Base):
if len(dmap) == 0:
if MD5_TIMESTAMP_DEBUG: print("Nothing dmap shortcutting")
return None
+ elif MD5_TIMESTAMP_DEBUG: print("len(dmap):%d"%len(dmap))
+
- if MD5_TIMESTAMP_DEBUG: print("len(dmap):%d"%len(dmap))
- # First try the simple name for node
- c_str = str(self)
- if MD5_TIMESTAMP_DEBUG: print("Checking :%s"%c_str)
- df = dmap.get(c_str, None)
+ # First try retrieving via Node
+ if MD5_TIMESTAMP_DEBUG: print("Checking if self is in map:%s id:%s type:%s"%(str(self), id(self), type(self)))
+ df = dmap.get(self, False)
if df:
return df
+ # Now check if self's repository file is in map.
+ rf = self.rfile()
+ if MD5_TIMESTAMP_DEBUG: print("Checking if self.rfile is in map:%s id:%s type:%s"%(str(rf), id(rf), type(rf)))
+ rfm = dmap.get(rf, False)
+ if rfm:
+ return rfm
+
+ # get default string for node and then also string swapping os.altsep for os.sep (/ for \)
+ c_strs = [str(self)]
+
if os.altsep:
- c_str = c_str.replace(os.sep, os.altsep)
- df = dmap.get(c_str, None)
- if MD5_TIMESTAMP_DEBUG: print("-->%s"%df)
+ c_strs.append(c_strs[0].replace(os.sep, os.altsep))
+
+ # In some cases the dependency_maps' keys are already strings check.
+ # Check if either string is now in dmap.
+ for s in c_strs:
+ if MD5_TIMESTAMP_DEBUG: print("Checking if str(self) is in map :%s" % s)
+ df = dmap.get(s, False)
if df:
return df
+ # Strings don't exist in map, add them and try again
+ # If there are no strings in this dmap, then add them.
+ # This may not be necessary, we could walk the nodes in the dmap and check each string
+ # rather than adding ALL the strings to dmap. In theory that would be n/2 vs 2n str() calls on node
+ # if not dmap.has_strings:
+ dmap = self._add_strings_to_dependency_map(dmap)
+
+ # In some cases the dependency_maps' keys are already strings check.
+ # Check if either string is now in dmap.
+ for s in c_strs:
+ if MD5_TIMESTAMP_DEBUG: print("Checking if str(self) is in map (now with strings) :%s" % s)
+ df = dmap.get(s, False)
+ if df:
+ return df
+
+ # Lastly use nodes get_path() to generate string and see if that's in dmap
if not df:
try:
# this should yield a path which matches what's in the sconsign
c_str = self.get_path()
- df = dmap.get(c_str, None)
- if MD5_TIMESTAMP_DEBUG: print("-->%s"%df)
- if df:
- return df
-
if os.altsep:
c_str = c_str.replace(os.sep, os.altsep)
- df = dmap.get(c_str, None)
- if MD5_TIMESTAMP_DEBUG: print("-->%s"%df)
- if df:
- return df
+
+ if MD5_TIMESTAMP_DEBUG: print("Checking if self.get_path is in map (now with strings) :%s" % s)
+
+ df = dmap.get(c_str, None)
except AttributeError as e:
raise FileBuildInfoFileToCsigMappingError("No mapping from file name to content signature for :%s"%c_str)
@@ -3392,16 +3429,12 @@ class File(Base):
self - dependency
target - target
prev_ni - The NodeInfo object loaded from previous builds .sconsign
- node - Node instance. This is the only changed* function which requires
- node to function. So if we detect that it's not passed.
- we throw DeciderNeedsNode, and caller should handle this and pass node.
+ node - Node instance. Check this node for file existence/timestamp
+ if specified.
Returns:
Boolean - Indicates if node(File) has changed.
"""
- if node is None:
- # We need required node argument to get BuildInfo to function
- raise DeciderNeedsNode(self.changed_timestamp_then_content)
# Now get sconsign name -> csig map and then get proper prev_ni if possible
bi = node.get_stored_info().binfo
@@ -3433,7 +3466,6 @@ class File(Base):
print("Mismatch self.changed_timestamp_match(%s, prev_ni) old:%s new:%s"%(str(target), old, new))
new_prev_ni = self._get_previous_signatures(dependency_map)
-
if not new:
try:
# NOTE: We're modifying the current node's csig in a query.
@@ -3443,13 +3475,13 @@ class File(Base):
return False
return self.changed_content(target, new_prev_ni)
- def changed_timestamp_newer(self, target, prev_ni):
+ def changed_timestamp_newer(self, target, prev_ni, repo_node=None):
try:
return self.get_timestamp() > target.get_timestamp()
except AttributeError:
return 1
- def changed_timestamp_match(self, target, prev_ni):
+ def changed_timestamp_match(self, target, prev_ni, repo_node=None):
"""
Return True if the timestamps don't match or if there is no previous timestamp
:param target:
@@ -3462,14 +3494,18 @@ class File(Base):
return 1
def is_up_to_date(self):
+ """Check for whether the Node is current
+ In all cases self is the target we're checking to see if it's up to date
+ """
+
T = 0
if T: Trace('is_up_to_date(%s):' % self)
if not self.exists():
if T: Trace(' not self.exists():')
- # The file doesn't exist locally...
+ # The file (always a target) doesn't exist locally...
r = self.rfile()
if r != self:
- # ...but there is one in a Repository...
+ # ...but there is one (always a target) in a Repository...
if not self.changed(r):
if T: Trace(' changed(%s):' % r)
# ...and it's even up-to-date...
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index aeb7092..3073d59 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -253,25 +253,10 @@ _target_from_source_map = {0 : target_from_source_none,
# used by it.
#
-
-class DeciderNeedsNode(Exception):
- """
- Indicate that the decider needs the node as well as the target and the dependency.
- Normally the node and the target are the same, but in the case of repository
- They may be different. Also the NodeInfo is retrieved from the node
- """
- def __init__(self, call_this_decider):
- """
- :param call_this_decider: to return the decider to call directly since deciders
- are called through several levels of indirection
- """
- self.decider = call_this_decider
-
-
#
# First, the single decider functions
#
-def changed_since_last_build_node(node, target, prev_ni):
+def changed_since_last_build_node(node, target, prev_ni, repo_node=None):
"""
Must be overridden in a specific subclass to return True if this
@@ -292,7 +277,7 @@ def changed_since_last_build_node(node, target, prev_ni):
raise NotImplementedError
-def changed_since_last_build_alias(node, target, prev_ni):
+def changed_since_last_build_alias(node, target, prev_ni, repo_node=None):
cur_csig = node.get_csig()
try:
return cur_csig != prev_ni.csig
@@ -300,24 +285,24 @@ def changed_since_last_build_alias(node, target, prev_ni):
return 1
-def changed_since_last_build_entry(node, target, prev_ni):
+def changed_since_last_build_entry(node, target, prev_ni, repo_node=None):
node.disambiguate()
- return _decider_map[node.changed_since_last_build](node, target, prev_ni)
+ return _decider_map[node.changed_since_last_build](node, target, prev_ni, repo_node)
-def changed_since_last_build_state_changed(node, target, prev_ni):
+def changed_since_last_build_state_changed(node, target, prev_ni, repo_node=None):
return node.state != SCons.Node.up_to_date
-def decide_source(node, target, prev_ni):
- return target.get_build_env().decide_source(node, target, prev_ni)
+def decide_source(node, target, prev_ni, repo_node=None):
+ return target.get_build_env().decide_source(node, target, prev_ni, repo_node)
-def decide_target(node, target, prev_ni):
- return target.get_build_env().decide_target(node, target, prev_ni)
+def decide_target(node, target, prev_ni, repo_node=None):
+ return target.get_build_env().decide_target(node, target, prev_ni, repo_node)
-def changed_since_last_build_python(node, target, prev_ni):
+def changed_since_last_build_python(node, target, prev_ni, repo_node=None):
cur_csig = node.get_csig()
try:
return cur_csig != prev_ni.csig
@@ -1505,17 +1490,11 @@ class Node(object, with_metaclass(NoSlotsPyPy)):
result = True
for child, prev_ni in zip(children, then):
- try:
- if _decider_map[child.changed_since_last_build](child, self, prev_ni):
- if t: Trace(': %s changed' % child)
- result = True
- except DeciderNeedsNode as e:
- if e.decider(self, prev_ni, node=node):
- if t: Trace(': %s changed' % child)
- result = True
+ if _decider_map[child.changed_since_last_build](child, self, prev_ni, node):
+ if t: Trace(': %s changed' % child)
+ result = True
if self.has_builder():
- import SCons.Util
contents = self.get_executor().get_contents()
newsig = SCons.Util.MD5signature(contents)
if bi.bactsig != newsig:
diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py
index 59afb40..71729c9 100644
--- a/src/engine/SCons/SConf.py
+++ b/src/engine/SCons/SConf.py
@@ -56,7 +56,6 @@ import SCons.Warnings
import SCons.Conftest
from SCons.Debug import Trace
-from SCons.Node import DeciderNeedsNode
# Turn off the Conftest error logging
SCons.Conftest.LogInputFiles = 0
@@ -407,12 +406,10 @@ class SConfBase(object):
# that the correct .sconsign info will get calculated
# and keep the build state consistent.
def force_build(dependency, target, prev_ni,
- env_decider=env.decide_source,
- node=None):
+ repo_node=None,
+ env_decider=env.decide_source):
try:
- env_decider(dependency, target, prev_ni)
- except DeciderNeedsNode as e:
- e.decider(target, prev_ni, node=target)
+ env_decider(dependency, target, prev_ni, repo_node)
except Exception as e:
raise e
return True
diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py
index d2a5573..0e4ef15 100644
--- a/src/engine/SCons/Tool/MSCommon/vc.py
+++ b/src/engine/SCons/Tool/MSCommon/vc.py
@@ -189,11 +189,19 @@ def get_host_target(env):
# If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the
# MSVC_VERSION documentation in Tool/msvc.xml.
-_VCVER = ["14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"]
+_VCVER = ["14.2", "14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"]
+
+# if using vswhere, a further mapping is needed
+_VCVER_TO_VSWHERE_VER = {
+ '14.2' : '[16.0, 17.0)',
+ '14.1' : '[15.0, 16.0)',
+}
_VCVER_TO_PRODUCT_DIR = {
+ '14.2' : [
+ (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # VS 2019 doesn't set this key
'14.1' : [
- (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # Visual Studio 2017 doesn't set this registry key anymore
+ (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # VS 2017 doesn't set this key
'14.0' : [
(SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')],
'14.0Exp' : [
@@ -254,42 +262,40 @@ def msvc_version_to_maj_min(msvc_version):
raise ValueError("Unrecognized version %s (%s)" % (msvc_version,msvc_version_numeric))
def is_host_target_supported(host_target, msvc_version):
- """Check if the given (host, target) tuple is supported for given version.
-
- Args:
- host_target: tuple
- tuple of (canonalized) host-targets, e.g. ("x86", "amd64")
- for cross compilation from 32 bit Windows to 64 bits.
- msvc_version: str
- msvc version (major.minor, e.g. 10.0)
+ """Check if (host, target) pair is supported for a VC version.
- Returns:
- bool:
-
- Note:
- This only checks whether a given version *may* support the given (host,
+ :note: only checks whether a given version *may* support the given (host,
target), not that the toolchain is actually present on the machine.
+ :param tuple host_target: canonalized host-targets pair, e.g.
+ ("x86", "amd64") for cross compilation from 32 bit Windows to 64 bits.
+ :param str msvc_version: Visual C++ version (major.minor), e.g. "10.0"
+ :returns: True or False
"""
# We assume that any Visual Studio version supports x86 as a target
if host_target[1] != "x86":
maj, min = msvc_version_to_maj_min(msvc_version)
if maj < 8:
return False
-
return True
def find_vc_pdir_vswhere(msvc_version):
"""
- Find the MSVC product directory using vswhere.exe.
+ Find the MSVC product directory using the vswhere program.
- Run it asking for specified version and get MSVS install location
- :param msvc_version:
+ :param msvc_version: MSVC version to search for
:return: MSVC install dir or None
+ :raises UnsupportedVersion: if the version is not known by this file
"""
+ try:
+ vswhere_version = _VCVER_TO_VSWHERE_VER[msvc_version]
+ except KeyError:
+ debug("Unknown version of MSVC: %s" % msvc_version)
+ raise UnsupportedVersion("Unknown version %s" % msvc_version)
+
# For bug 3333 - support default location of vswhere for both 64 and 32 bit windows
- # installs.
+ # installs.
for pf in ['Program Files (x86)', 'Program Files']:
vswhere_path = os.path.join(
'C:\\',
@@ -301,31 +307,36 @@ def find_vc_pdir_vswhere(msvc_version):
if os.path.exists(vswhere_path):
# If we found vswhere, then use it.
break
+ else:
+ # No vswhere on system, no install info available
+ return None
- vswhere_cmd = [vswhere_path, '-products', '*', '-version', msvc_version, '-property', 'installationPath']
-
- if os.path.exists(vswhere_path):
- #TODO PY27 cannot use Popen as context manager
- # try putting it back to the old way for now
- sp = subprocess.Popen(vswhere_cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- vsdir, err = sp.communicate()
- if vsdir:
- vsdir = vsdir.decode("mbcs").splitlines()
- # vswhere could easily return multiple lines
- # we could define a way to pick the one we prefer, but since
- # this data is currently only used to make a check for existence,
- # returning the first hit should be good enough for now.
- vc_pdir = os.path.join(vsdir[0], 'VC')
- return vc_pdir
+ vswhere_cmd = [vswhere_path,
+ '-products', '*',
+ '-version', vswhere_version,
+ '-property', 'installationPath']
+
+ #TODO PY27 cannot use Popen as context manager
+ # try putting it back to the old way for now
+ sp = subprocess.Popen(vswhere_cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ vsdir, err = sp.communicate()
+ if vsdir:
+ vsdir = vsdir.decode("mbcs").splitlines()
+ # vswhere could easily return multiple lines
+ # we could define a way to pick the one we prefer, but since
+ # this data is currently only used to make a check for existence,
+ # returning the first hit should be good enough for now.
+ vc_pdir = os.path.join(vsdir[0], 'VC')
+ return vc_pdir
else:
# No vswhere on system, no install info available
return None
def find_vc_pdir(msvc_version):
- """Find the product directory for the given version.
+ """Find the MSVC product directory for the given version.
Tries to look up the path using a registry key from the table
_VCVER_TO_PRODUCT_DIR; if there is no key, calls find_vc_pdir_wshere
@@ -462,7 +473,7 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version):
host_platform = _ARCH_TO_CANONICAL[host_platform]
target_platform = _ARCH_TO_CANONICAL[target_platform]
- debug('_check_cl_exists_in_vc_dir(): host platform %s, target platform %s' % (host_platform, target_platform))
+ debug('_check_cl_exists_in_vc_dir(): host platform %s, target platform %s for version %s' % (host_platform, target_platform, msvc_version))
ver_num = float(get_msvc_version_numeric(msvc_version))
@@ -510,7 +521,7 @@ def _check_cl_exists_in_vc_dir(env, vc_dir, msvc_version):
# older versions of visual studio only had x86 binaries,
# so if the host platform is amd64, we need to check cross
# compile options (x86 binary compiles some other target on a 64 bit os)
-
+
# Set default value to be -1 as "" which is the value for x86/x86 yields true when tested
# if not host_trgt_dir
host_trgt_dir = _HOST_TARGET_TO_CL_DIR.get(('x86', target_platform), None)
diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py
index 598b5e6..dfca48d 100644
--- a/src/engine/SCons/Tool/MSCommon/vs.py
+++ b/src/engine/SCons/Tool/MSCommon/vs.py
@@ -198,6 +198,17 @@ class VisualStudio(object):
# Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml.
SupportedVSList = [
+ # Visual Studio 2019
+ VisualStudio('14.2',
+ vc_version='14.2',
+ sdk_version='10.0A',
+ hkeys=[],
+ common_tools_var='VS160COMNTOOLS',
+ executable_path=r'Common7\IDE\devenv.com',
+ batch_file_path=r'VC\Auxiliary\Build\vsvars32.bat',
+ supported_arch=['x86', 'amd64', "arm"],
+ ),
+
# Visual Studio 2017
VisualStudio('14.1',
vc_version='14.1',
diff --git a/src/engine/SCons/Tool/msvc.xml b/src/engine/SCons/Tool/msvc.xml
index dacdcba..ff65364 100644
--- a/src/engine/SCons/Tool/msvc.xml
+++ b/src/engine/SCons/Tool/msvc.xml
@@ -353,6 +353,7 @@ constructor; setting it later has no effect.
<para>
Valid values for Windows are
+<literal>14.2</literal>,
<literal>14.1</literal>,
<literal>14.0</literal>,
<literal>14.0Exp</literal>,