summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r--src/engine/SCons/Node/FS.py7
-rw-r--r--src/engine/SCons/Node/NodeTests.py49
-rw-r--r--src/engine/SCons/Node/__init__.py211
3 files changed, 149 insertions, 118 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index f73161f..6ff3833 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -36,6 +36,7 @@ that can be used by scripts or modules looking for the canonical default.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import fnmatch
+from itertools import izip
import os
import os.path
import re
@@ -1895,7 +1896,7 @@ class Dir(Base):
rep_nodes = map(dir.Entry, disk_names)
#rep_nodes = [ n.disambiguate() for n in rep_nodes ]
rep_nodes = map(lambda n: n.disambiguate(), rep_nodes)
- for node, name in zip(rep_nodes, disk_names):
+ for node, name in izip(rep_nodes, disk_names):
n = self.Entry(name)
if n.__class__ != node.__class__:
n.__class__ = node.__class__
@@ -2110,7 +2111,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
pass
else:
nodes = []
- for s, ni in zip(strings, nodeinfos):
+ for s, ni in izip(strings, nodeinfos):
if not isinstance(s, SCons.Node.Node):
s = ni.str_to_node(s)
nodes.append(s)
@@ -2119,7 +2120,7 @@ class FileBuildInfo(SCons.Node.BuildInfoBase):
result = []
bkids = self.bsources + self.bdepends + self.bimplicit
bkidsigs = self.bsourcesigs + self.bdependsigs + self.bimplicitsigs
- for bkid, bkidsig in zip(bkids, bkidsigs):
+ for bkid, bkidsig in izip(bkids, bkidsigs):
result.append(str(bkid) + ': ' +
string.join(bkidsig.format(names=names), ' '))
result.append('%s [%s]' % (self.bactsig, self.bact))
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 8e9a3f8..8bceaf6 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -714,23 +714,23 @@ class NodeTestCase(unittest.TestCase):
n1 = SCons.Node.Node()
n1.builder_set(Builder())
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n1])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n1])
node.prepare() # should not throw an exception
n2 = SCons.Node.Node()
n2.linked = 1
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n2])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n2])
node.prepare() # should not throw an exception
n3 = SCons.Node.Node()
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n3])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n3])
node.prepare() # should not throw an exception
@@ -739,8 +739,8 @@ class NodeTestCase(unittest.TestCase):
return None
n4 = MyNode()
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n4])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n4])
exc_caught = 0
try:
node.prepare()
@@ -810,7 +810,7 @@ class NodeTestCase(unittest.TestCase):
pass
else:
raise "did not catch expected exception"
- assert node.sources == [zero, one, two, three, four]
+ assert node.sources == [zero, one, two, three, four], node.sources
def test_add_ignore(self):
"""Test adding files whose dependencies should be ignored.
@@ -1033,9 +1033,9 @@ class NodeTestCase(unittest.TestCase):
node.add_source([n1, n2, n3])
node.add_dependency([n4, n5, n6])
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
- node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
+ node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
node.add_ignore([n2, n5, n8, n11])
kids = node.children()
@@ -1064,9 +1064,9 @@ class NodeTestCase(unittest.TestCase):
node.add_source([n1, n2, n3])
node.add_dependency([n4, n5, n6])
node.implicit = []
- node.implicit_dict = {}
- node._add_child(node.implicit, node.implicit_dict, [n7, n8, n9])
- node._add_child(node.implicit, node.implicit_dict, [n10, n11, n12])
+ node.implicit_set = set()
+ node._add_child(node.implicit, node.implicit_set, [n7, n8, n9])
+ node._add_child(node.implicit, node.implicit_set, [n10, n11, n12])
node.add_ignore([n2, n5, n8, n11])
kids = node.all_children()
@@ -1217,7 +1217,6 @@ class NodeTestCase(unittest.TestCase):
n.clear()
assert n.includes is None, n.includes
- assert n.found_includes == {}, n.found_includes
assert x.cleaned_up
def test_get_subst_proxy(self):
@@ -1241,32 +1240,22 @@ class NodeTestCase(unittest.TestCase):
def test_postprocess(self):
"""Test calling the base Node postprocess() method"""
n = SCons.Node.Node()
- n.waiting_parents = {'foo':1, 'bar':1}
+ n.waiting_parents = set( ['foo','bar'] )
n.postprocess()
- assert n.waiting_parents == {}, n.waiting_parents
+ assert n.waiting_parents == set(), n.waiting_parents
def test_add_to_waiting_parents(self):
"""Test the add_to_waiting_parents() method"""
n1 = SCons.Node.Node()
n2 = SCons.Node.Node()
- assert n1.waiting_parents == {}, n1.waiting_parents
+ assert n1.waiting_parents == set(), n1.waiting_parents
r = n1.add_to_waiting_parents(n2)
assert r == 1, r
- assert n1.waiting_parents == {n2:1}, n1.waiting_parents
+ assert n1.waiting_parents == set((n2,)), n1.waiting_parents
r = n1.add_to_waiting_parents(n2)
assert r == 0, r
- def test_call_for_all_waiting_parents(self):
- """Test the call_for_all_waiting_parents() method"""
- n1 = SCons.Node.Node()
- n2 = SCons.Node.Node()
- n1.add_to_waiting_parents(n2)
- result = []
- def func(node, result=result):
- result.append(node)
- n1.call_for_all_waiting_parents(func)
- assert result == [n1, n2], result
class NodeListTestCase(unittest.TestCase):
def test___str__(self):
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 2e136f0..c765ee9 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -47,6 +47,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.compat
import copy
+from itertools import chain, izip
import string
import UserList
@@ -75,7 +76,7 @@ executed = 4
failed = 5
StateString = {
- 0 : "0",
+ 0 : "no_state",
1 : "pending",
2 : "executing",
3 : "up_to_date",
@@ -202,15 +203,16 @@ class Node:
# a class. (Of course, we could always still do that in the
# future if we had a good reason to...).
self.sources = [] # source files used to build node
- self.sources_dict = {}
+ self.sources_set = set()
+ self._specific_sources = False
self.depends = [] # explicit dependencies (from Depends)
- self.depends_dict = {}
+ self.depends_set = set()
self.ignore = [] # dependencies to ignore
- self.ignore_dict = {}
+ self.ignore_set = set()
self.prerequisites = SCons.Util.UniqueList()
self.implicit = None # implicit (scanned) dependencies (None means not scanned yet)
- self.waiting_parents = {}
- self.waiting_s_e = {}
+ self.waiting_parents = set()
+ self.waiting_s_e = set()
self.ref_count = 0
self.wkids = None # Kids yet to walk, when it's an array
@@ -220,7 +222,6 @@ class Node:
self.noclean = 0
self.nocache = 0
self.always_build = None
- self.found_includes = {}
self.includes = None
self.attributes = self.Attrs() # Generic place to stick information about the Node.
self.side_effect = 0 # true iff this node is a side effect
@@ -330,24 +331,29 @@ class Node:
is out-of-date and must be rebuilt, but before actually calling
the method to build the Node.
- This default implemenation checks that all children either exist
- or are derived, and initializes the BuildInfo structure that
- will hold the information about how this node is, uh, built.
+ This default implementation checks that explicit or implicit
+ dependencies either exist or are derived, and initializes the
+ BuildInfo structure that will hold the information about how
+ this node is, uh, built.
+
+ (The existence of source files is checked separately by the
+ Executor, which aggregates checks for all of the targets built
+ by a specific action.)
Overriding this method allows for for a Node subclass to remove
the underlying file from the file system. Note that subclass
methods should call this base class method to get the child
check and the BuildInfo structure.
"""
- l = self.depends
+ for d in self.depends:
+ if d.missing():
+ msg = "Explicit dependency `%s' not found, needed by target `%s'."
+ raise SCons.Errors.StopError, msg % (d, self)
if not self.implicit is None:
- l = l + self.implicit
- missing_sources = self.get_executor().get_missing_sources() \
- + filter(lambda c: c.missing(), l)
- if missing_sources:
- desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self)
- raise SCons.Errors.StopError, desc
-
+ for i in self.implicit:
+ if i.missing():
+ msg = "Implicit dependency `%s' not found, needed by target `%s'."
+ raise SCons.Errors.StopError, msg % (i, self)
self.binfo = self.get_binfo()
def build(self, **kw):
@@ -373,7 +379,7 @@ class Node:
# Clear the implicit dependency caches of any Nodes
# waiting for this Node to be built.
- for parent in self.waiting_parents.keys():
+ for parent in self.waiting_parents:
parent.implicit = None
self.clear()
@@ -398,7 +404,7 @@ class Node:
#
def add_to_waiting_s_e(self, node):
- self.waiting_s_e[node] = 1
+ self.waiting_s_e.add(node)
def add_to_waiting_parents(self, node):
"""
@@ -409,23 +415,16 @@ class Node:
True and False instead...)
"""
wp = self.waiting_parents
- if wp.has_key(node):
- result = 0
- else:
- result = 1
- wp[node] = 1
- return result
-
- def call_for_all_waiting_parents(self, func):
- func(self)
- for parent in self.waiting_parents.keys():
- parent.call_for_all_waiting_parents(func)
+ if node in wp:
+ return 0
+ wp.add(node)
+ return 1
def postprocess(self):
"""Clean up anything we don't need to hang onto after we've
been built."""
self.executor_cleanup()
- self.waiting_parents = {}
+ self.waiting_parents = set()
def clear(self):
"""Completely clear a Node of all its cached state (so that it
@@ -444,7 +443,6 @@ class Node:
except AttributeError:
pass
self.includes = None
- self.found_includes = {}
def clear_memoized_values(self):
self._memo = {}
@@ -592,9 +590,9 @@ class Node:
def add_to_implicit(self, deps):
if not hasattr(self, 'implicit') or self.implicit is None:
self.implicit = []
- self.implicit_dict = {}
+ self.implicit_set = set()
self._children_reset()
- self._add_child(self.implicit, self.implicit_dict, deps)
+ self._add_child(self.implicit, self.implicit_set, deps)
def scan(self):
"""Scan this node's dependents for implicit dependencies."""
@@ -604,7 +602,7 @@ class Node:
if not self.implicit is None:
return
self.implicit = []
- self.implicit_dict = {}
+ self.implicit_set = set()
self._children_reset()
if not self.has_builder():
return
@@ -633,7 +631,7 @@ class Node:
# one of this node's sources has changed,
# so we must recalculate the implicit deps:
self.implicit = []
- self.implicit_dict = {}
+ self.implicit_set = set()
# Have the executor scan the sources.
executor.scan_sources(self.builder.source_scanner)
@@ -706,33 +704,44 @@ class Node:
self.binfo = binfo
executor = self.get_executor()
-
- sources = executor.get_unignored_sources(self.ignore)
-
- depends = self.depends
- implicit = self.implicit or []
-
- if self.ignore:
- depends = filter(self.do_not_ignore, depends)
- implicit = filter(self.do_not_ignore, implicit)
-
- def get_ninfo(node):
- return node.get_ninfo()
-
- sourcesigs = map(get_ninfo, sources)
- dependsigs = map(get_ninfo, depends)
- implicitsigs = map(get_ninfo, implicit)
+ ignore_set = self.ignore_set
if self.has_builder():
binfo.bact = str(executor)
binfo.bactsig = SCons.Util.MD5signature(executor.get_contents())
- binfo.bsources = sources
- binfo.bdepends = depends
- binfo.bimplicit = implicit
+ if self._specific_sources:
+ sources = []
+ for s in self.sources:
+ if s not in ignore_set:
+ sources.append(s)
+ else:
+ sources = executor.get_unignored_sources(self.ignore)
+ seen = set()
+ bsources = []
+ bsourcesigs = []
+ for s in sources:
+ if not s in seen:
+ seen.add(s)
+ bsources.append(s)
+ bsourcesigs.append(s.get_ninfo())
+ binfo.bsources = bsources
+ binfo.bsourcesigs = bsourcesigs
- binfo.bsourcesigs = sourcesigs
+ depends = self.depends
+ dependsigs = []
+ for d in depends:
+ if d not in ignore_set:
+ dependsigs.append(d.get_ninfo())
+ binfo.bdepends = depends
binfo.bdependsigs = dependsigs
+
+ implicit = self.implicit or []
+ implicitsigs = []
+ for i in implicit:
+ if i not in ignore_set:
+ implicitsigs.append(i.get_ninfo())
+ binfo.bimplicit = implicit
binfo.bimplicitsigs = implicitsigs
return binfo
@@ -816,7 +825,7 @@ class Node:
def add_dependency(self, depend):
"""Adds dependencies."""
try:
- self._add_child(self.depends, self.depends_dict, depend)
+ self._add_child(self.depends, self.depends_set, depend)
except TypeError, e:
e = e.args[0]
if SCons.Util.is_List(e):
@@ -833,7 +842,7 @@ class Node:
def add_ignore(self, depend):
"""Adds dependencies to ignore."""
try:
- self._add_child(self.ignore, self.ignore_dict, depend)
+ self._add_child(self.ignore, self.ignore_set, depend)
except TypeError, e:
e = e.args[0]
if SCons.Util.is_List(e):
@@ -844,8 +853,10 @@ class Node:
def add_source(self, source):
"""Adds sources."""
+ if self._specific_sources:
+ return
try:
- self._add_child(self.sources, self.sources_dict, source)
+ self._add_child(self.sources, self.sources_set, source)
except TypeError, e:
e = e.args[0]
if SCons.Util.is_List(e):
@@ -854,9 +865,9 @@ class Node:
s = str(e)
raise SCons.Errors.UserError("attempted to add a non-Node as source of %s:\n\t%s is a %s, not a Node" % (str(self), s, type(e)))
- def _add_child(self, collection, dict, child):
- """Adds 'child' to 'collection', first checking 'dict' to see
- if it's already present."""
+ def _add_child(self, collection, set, child):
+ """Adds 'child' to 'collection', first checking 'set' to see if it's
+ already present."""
#if type(child) is not type([]):
# child = [child]
#for c in child:
@@ -864,13 +875,17 @@ class Node:
# raise TypeError, c
added = None
for c in child:
- if not dict.has_key(c):
+ if c not in set:
+ set.add(c)
collection.append(c)
- dict[c] = 1
added = 1
if added:
self._children_reset()
+ def set_specific_source(self, source):
+ self.add_source(source)
+ self._specific_sources = True
+
def add_wkid(self, wkid):
"""Add a node to the list of kids waiting to be evaluated"""
if self.wkids != None:
@@ -882,10 +897,14 @@ class Node:
# build info that it's cached so we can re-calculate it.
self.executor_cleanup()
- def do_not_ignore(self, node):
- return node not in self.ignore
+ memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
+
+ def _children_get(self):
+ try:
+ return self._memo['children_get']
+ except KeyError:
+ pass
- def _all_children_get(self):
# The return list may contain duplicate Nodes, especially in
# source trees where there are a lot of repeated #includes
# of a tangle of .h files. Profiling shows, however, that
@@ -903,21 +922,22 @@ class Node:
# using dictionary keys, lose the order, and the only ordered
# dictionary patterns I found all ended up using "not in"
# internally anyway...)
- if self.implicit is None:
- return self.sources + self.depends
- else:
- return self.sources + self.depends + self.implicit
+ if self.ignore_set:
+ if self.implicit is None:
+ iter = chain(self.sources,self.depends)
+ else:
+ iter = chain(self.sources, self.depends, self.implicit)
- memoizer_counters.append(SCons.Memoize.CountValue('_children_get'))
+ children = []
+ for i in iter:
+ if i not in self.ignore_set:
+ children.append(i)
+ else:
+ if self.implicit is None:
+ children = self.sources + self.depends
+ else:
+ children = self.sources + self.depends + self.implicit
- def _children_get(self):
- try:
- return self._memo['children_get']
- except KeyError:
- pass
- children = self._all_children_get()
- if self.ignore:
- children = filter(self.do_not_ignore, children)
self._memo['children_get'] = children
return children
@@ -925,7 +945,28 @@ class Node:
"""Return a list of all the node's direct children."""
if scan:
self.scan()
- return self._all_children_get()
+
+ # The return list may contain duplicate Nodes, especially in
+ # source trees where there are a lot of repeated #includes
+ # of a tangle of .h files. Profiling shows, however, that
+ # eliminating the duplicates with a brute-force approach that
+ # preserves the order (that is, something like:
+ #
+ # u = []
+ # for n in list:
+ # if n not in u:
+ # u.append(n)"
+ #
+ # takes more cycles than just letting the underlying methods
+ # hand back cached values if a Node's information is requested
+ # multiple times. (Other methods of removing duplicates, like
+ # using dictionary keys, lose the order, and the only ordered
+ # dictionary patterns I found all ended up using "not in"
+ # internally anyway...)
+ if self.implicit is None:
+ return self.sources + self.depends
+ else:
+ return self.sources + self.depends + self.implicit
def children(self, scan=1):
"""Return a list of the node's direct children, minus those
@@ -1009,7 +1050,7 @@ class Node:
if t: Trace(': old %s new %s' % (len(then), len(children)))
result = True
- for child, prev_ni in zip(children, then):
+ for child, prev_ni in izip(children, then):
if child.changed_since_last_build(self, prev_ni):
if t: Trace(': %s changed' % child)
result = True
@@ -1152,8 +1193,8 @@ class Node:
new_bkids = new.bsources + new.bdepends + new.bimplicit
new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs
- osig = dict(zip(old_bkids, old_bkidsigs))
- nsig = dict(zip(new_bkids, new_bkidsigs))
+ osig = dict(izip(old_bkids, old_bkidsigs))
+ nsig = dict(izip(new_bkids, new_bkidsigs))
# The sources and dependencies we'll want to report are all stored
# as relative paths to this target's directory, but we want to