diff options
Diffstat (limited to 'engine/SCons/Node/__init__.py')
-rw-r--r-- | engine/SCons/Node/__init__.py | 122 |
1 files changed, 84 insertions, 38 deletions
diff --git a/engine/SCons/Node/__init__.py b/engine/SCons/Node/__init__.py index 299673f..c1f8d89 100644 --- a/engine/SCons/Node/__init__.py +++ b/engine/SCons/Node/__init__.py @@ -22,7 +22,7 @@ be able to depend on any other type of "thing." from __future__ import print_function # -# Copyright (c) 2001 - 2017 The SCons Foundation +# Copyright (c) 2001 - 2019 The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -43,12 +43,18 @@ from __future__ import print_function # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -__revision__ = "src/engine/SCons/Node/__init__.py 74b2c53bc42290e911b334a6b44f187da698a668 2017/11/14 13:16:53 bdbaddog" +__revision__ = "src/engine/SCons/Node/__init__.py 72ae09dc35ac2626f8ff711d8c4b30b6138e08e3 2019-08-08 14:50:06 bdeegan" +import os import collections import copy from itertools import chain +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + import SCons.Debug from SCons.Debug import logInstanceCreation import SCons.Executor @@ -102,9 +108,9 @@ implicit_deps_changed = 0 # A variable that can be set to an interface-specific function be called # to annotate a Node with information about its creation. -def do_nothing(node): pass +def do_nothing_node(node): pass -Annotate = do_nothing +Annotate = do_nothing_node # Gets set to 'True' if we're running in interactive mode. Is # currently used to release parts of a target's info during @@ -139,6 +145,7 @@ def exists_entry(node): node.disambiguate() return _exists_map[node._func_exists](node) + def exists_file(node): # Duplicate from source path if we are set up to do this. if node.duplicate and not node.is_derived() and not node.linked: @@ -155,7 +162,7 @@ def exists_file(node): # The source file does not exist. Make sure no old # copy remains in the variant directory. if print_duplicate: - print("dup: no src for %s, unlinking old variant copy"%self) + print("dup: no src for %s, unlinking old variant copy" % node) if exists_base(node) or node.islink(): node.fs.unlink(node.get_internal_path()) # Return None explicitly because the Base.exists() call @@ -249,7 +256,7 @@ _target_from_source_map = {0 : target_from_source_none, # # 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 @@ -269,27 +276,33 @@ 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 except AttributeError: 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): - return (node.state != SCons.Node.up_to_date) +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_target(node, target, prev_ni): - return target.get_build_env().decide_target(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 changed_since_last_build_python(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, repo_node=None): cur_csig = node.get_csig() try: return cur_csig != prev_ni.csig @@ -380,6 +393,7 @@ class NodeInfoBase(object): """ state = other.__getstate__() self.__setstate__(state) + def format(self, field_list=None, names=0): if field_list is None: try: @@ -505,6 +519,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): __slots__ = ['sources', 'sources_set', + 'target_peers', '_specific_sources', 'depends', 'depends_set', @@ -665,7 +680,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): executor.cleanup() def reset_executor(self): - "Remove cached executor; forces recompute when needed." + """Remove cached executor; forces recompute when needed.""" try: delattr(self, 'executor') except AttributeError: @@ -760,6 +775,25 @@ class Node(object, with_metaclass(NoSlotsPyPy)): for parent in self.waiting_parents: parent.implicit = None + # Handle issue where builder emits more than one target and + # the source file for the builder is generated. + # in that case only the first target was getting it's .implicit + # cleared when the source file is built (second scan). + # leaving only partial implicits from scan before source file is generated + # typically the compiler only. Then scanned files are appended + # This is persisted to sconsign and rebuild causes false rebuilds + # because the ordering of the implicit list then changes to what it + # should have been. + # This is at least the following bugs + # https://github.com/SCons/scons/issues/2811 + # https://jira.mongodb.org/browse/SERVER-33111 + try: + for peer in parent.target_peers: + peer.implicit = None + except AttributeError: + pass + + self.clear() if self.pseudo: @@ -1136,7 +1170,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): binfo.bactsig = SCons.Util.MD5signature(executor.get_contents()) if self._specific_sources: - sources = [ s for s in self.sources if not s in ignore_set] + sources = [s for s in self.sources if s not in ignore_set] else: sources = executor.get_unignored_sources(self, self.ignore) @@ -1145,13 +1179,17 @@ class Node(object, with_metaclass(NoSlotsPyPy)): binfo.bsources = [s for s in sources if s not in seen and not seen.add(s)] binfo.bsourcesigs = [s.get_ninfo() for s in binfo.bsources] + binfo.bdepends = [d for d in self.depends if d not in ignore_set] + binfo.bdependsigs = [d.get_ninfo() for d in self.depends] - binfo.bdepends = self.depends - binfo.bdependsigs = [d.get_ninfo() for d in self.depends if d not in ignore_set] - - binfo.bimplicit = self.implicit or [] - binfo.bimplicitsigs = [i.get_ninfo() for i in binfo.bimplicit if i not in ignore_set] - + # Because self.implicit is initialized to None (and not empty list []) + # we have to handle this case + if not self.implicit: + binfo.bimplicit = [] + binfo.bimplicitsigs = [] + else: + binfo.bimplicit = [i for i in self.implicit if i not in ignore_set] + binfo.bimplicitsigs = [i.get_ninfo() for i in binfo.bimplicit] return binfo @@ -1213,7 +1251,7 @@ class Node(object, with_metaclass(NoSlotsPyPy)): return _exists_map[self._func_exists](self) def rexists(self): - """Does this node exist locally or in a repositiory?""" + """Does this node exist locally or in a repository?""" # There are no repositories by default: return _rexists_map[self._func_rexists](self) @@ -1452,13 +1490,12 @@ class Node(object, with_metaclass(NoSlotsPyPy)): result = True for child, prev_ni in zip(children, then): - if _decider_map[child.changed_since_last_build](child, self, prev_ni): + if _decider_map[child.changed_since_last_build](child, self, prev_ni, node): if t: Trace(': %s changed' % child) result = True - contents = self.get_executor().get_contents() if self.has_builder(): - import SCons.Util + contents = self.get_executor().get_contents() newsig = SCons.Util.MD5signature(contents) if bi.bactsig != newsig: if t: Trace(': bactsig %s != newsig %s' % (bi.bactsig, newsig)) @@ -1607,29 +1644,39 @@ class Node(object, with_metaclass(NoSlotsPyPy)): # so we only print them after running them through this lambda # to turn them into the right relative Node and then return # its string. - def stringify( s, E=self.dir.Entry ) : + def stringify( s, E=self.dir.Entry): if hasattr( s, 'dir' ) : return str(E(s)) return str(s) lines = [] - removed = [x for x in old_bkids if not x in new_bkids] + removed = [x for x in old_bkids if x not in new_bkids] if removed: - removed = list(map(stringify, removed)) + removed = [stringify(r) for r in removed] fmt = "`%s' is no longer a dependency\n" lines.extend([fmt % s for s in removed]) for k in new_bkids: - if not k in old_bkids: + if k not in old_bkids: lines.append("`%s' is a new dependency\n" % stringify(k)) - elif _decider_map[k.changed_since_last_build](k, self, osig[k]): - lines.append("`%s' changed\n" % stringify(k)) + else: + changed = _decider_map[k.changed_since_last_build](k, self, osig[k]) + + if changed: + lines.append("`%s' changed\n" % stringify(k)) if len(lines) == 0 and old_bkids != new_bkids: - lines.append("the dependency order changed:\n" + - "%sold: %s\n" % (' '*15, list(map(stringify, old_bkids))) + - "%snew: %s\n" % (' '*15, list(map(stringify, new_bkids)))) + lines.append("the dependency order changed:\n") + lines.append("->Sources\n") + for (o,n) in zip_longest(old.bsources, new.bsources, fillvalue=None): + lines.append("Old:%s\tNew:%s\n"%(o,n)) + lines.append("->Depends\n") + for (o,n) in zip_longest(old.bdepends, new.bdepends, fillvalue=None): + lines.append("Old:%s\tNew:%s\n"%(o,n)) + lines.append("->Implicit\n") + for (o,n) in zip_longest(old.bimplicit, new.bimplicit, fillvalue=None): + lines.append("Old:%s\tNew:%s\n"%(o,n)) if len(lines) == 0: def fmt_with_title(title, strlines): @@ -1672,7 +1719,6 @@ class Walker(object): This is depth-first, children are visited before the parent. The Walker object can be initialized with any node, and returns the next node on the descent with each get_next() call. - 'kids_func' is an optional function that will be called to get the children of a node instead of calling 'children'. 'cycle_func' is an optional function that will be called when a cycle is detected. |