summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-05-19 17:49:55 (GMT)
committerSteven Knight <knight@baldmt.com>2004-05-19 17:49:55 (GMT)
commit04ebe107191659f805bd63148c61c1026efeb686 (patch)
tree9122729d1120c1119fe10ff360f4675789ca8f9b /src/engine/SCons/Node
parent12d2ae1193b9e32e0164218bb73240791914f976 (diff)
downloadSCons-04ebe107191659f805bd63148c61c1026efeb686.zip
SCons-04ebe107191659f805bd63148c61c1026efeb686.tar.gz
SCons-04ebe107191659f805bd63148c61c1026efeb686.tar.bz2
Fix spurious rebuilds/reinstalls of header files and circular dependencies with generated header files by allowing Scanners to be associated explicitly with Builders, not just through Scanner file suffix lists.
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r--src/engine/SCons/Node/FS.py9
-rw-r--r--src/engine/SCons/Node/FSTests.py8
-rw-r--r--src/engine/SCons/Node/NodeTests.py74
-rw-r--r--src/engine/SCons/Node/__init__.py77
4 files changed, 103 insertions, 65 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 15434cb..0d158a1 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -488,12 +488,6 @@ class Base(SCons.Node.Node):
self._rexists = self.rfile().exists()
return self._rexists
- def get_parents(self):
- parents = SCons.Node.Node.get_parents(self)
- if self.dir and not isinstance(self.dir, ParentOfRoot):
- parents.append(self.dir)
- return parents
-
def is_under(self, dir):
if self is dir:
return 1
@@ -1117,8 +1111,7 @@ class Dir(Base):
self.abspath_ = self.abspath + os.sep
self.repositories = []
self.srcdir = None
- self.source_scanner = None
-
+
self.entries = {}
self.entries['.'] = self
self.entries['..'] = self.dir
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index fb2e0fa..f69dd4e 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -60,6 +60,8 @@ class Scanner:
return [self.node]
def __hash__(self):
return self.hash
+ def select(self, node):
+ return self
class Environment:
def __init__(self):
@@ -1034,12 +1036,6 @@ class FSTestCase(unittest.TestCase):
skey = fs.Dir('ddd.x').scanner_key()
assert skey is None, skey
- d1 = fs.Dir('dir')
- f1 = fs.File('dir/file')
- assert f1.dir == d1, f1.dir
- parents = f1.get_parents()
- assert parents == [ d1 ], parents
-
test.write("i_am_not_a_directory", "\n")
try:
exc_caught = 0
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 535c84e..e36d6ce 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -138,6 +138,8 @@ class Scanner:
def __call__(self, node):
self.called = 1
return node.found_includes
+ def select(self, node):
+ return self
class MyNode(SCons.Node.Node):
"""The base Node class contains a number of do-nothing methods that
@@ -543,12 +545,6 @@ class NodeTestCase(unittest.TestCase):
node.add_dependency([three, four, one])
assert node.depends == [zero, one, two, three, four]
- assert zero.get_parents() == [node]
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
-
try:
node.add_depends([[five, six]])
except:
@@ -556,8 +552,6 @@ class NodeTestCase(unittest.TestCase):
else:
raise "did not catch expected exception"
assert node.depends == [zero, one, two, three, four]
- assert five.get_parents() == []
- assert six.get_parents() == []
def test_add_source(self):
@@ -583,12 +577,6 @@ class NodeTestCase(unittest.TestCase):
node.add_source([three, four, one])
assert node.sources == [zero, one, two, three, four]
- assert zero.get_parents() == [node]
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
-
try:
node.add_source([[five, six]])
except:
@@ -596,8 +584,6 @@ class NodeTestCase(unittest.TestCase):
else:
raise "did not catch expected exception"
assert node.sources == [zero, one, two, three, four]
- assert five.get_parents() == []
- assert six.get_parents() == []
def test_add_ignore(self):
"""Test adding files whose dependencies should be ignored.
@@ -622,12 +608,6 @@ class NodeTestCase(unittest.TestCase):
node.add_ignore([three, four, one])
assert node.ignore == [zero, one, two, three, four]
- assert zero.get_parents() == [node]
- assert one.get_parents() == [node]
- assert two.get_parents() == [node]
- assert three.get_parents() == [node]
- assert four.get_parents() == [node]
-
try:
node.add_ignore([[five, six]])
except:
@@ -635,8 +615,6 @@ class NodeTestCase(unittest.TestCase):
else:
raise "did not catch expected exception"
assert node.ignore == [zero, one, two, three, four]
- assert five.get_parents() == []
- assert six.get_parents() == []
def test_get_found_includes(self):
"""Test the default get_found_includes() method
@@ -689,6 +667,33 @@ class NodeTestCase(unittest.TestCase):
deps = node.get_implicit_deps(env, s, target)
assert deps == [d, e, f], map(str, deps)
+ def test_get_source_scanner(self):
+ """Test fetching the source scanner for a Node
+ """
+ class Builder:
+ pass
+ target = SCons.Node.Node()
+ source = SCons.Node.Node()
+ s = target.get_source_scanner(source)
+ assert s is None, s
+
+ ts1 = Scanner()
+ ts2 = Scanner()
+ ts3 = Scanner()
+
+ source.backup_source_scanner = ts1
+ s = target.get_source_scanner(source)
+ assert s is ts1, s
+
+ source.builder = Builder()
+ source.builder.source_scanner = ts2
+ s = target.get_source_scanner(source)
+ assert s is ts2, s
+
+ target.source_scanner = ts3
+ s = target.get_source_scanner(source)
+ assert s is ts3, s
+
def test_scan(self):
"""Test Scanner functionality
"""
@@ -947,6 +952,7 @@ class NodeTestCase(unittest.TestCase):
n.includes = 'testincludes'
n.found_include = {'testkey':'testvalue'}
n.implicit = 'testimplicit'
+ n.waiting_parents = ['foo', 'bar']
n.clear()
@@ -955,6 +961,7 @@ class NodeTestCase(unittest.TestCase):
assert n.includes is None, n.includes
assert n.found_includes == {}, n.found_includes
assert n.implicit is None, n.implicit
+ assert n.waiting_parents == [], n.waiting_parents
def test_get_subst_proxy(self):
"""Test the get_subst_proxy method."""
@@ -985,6 +992,25 @@ class NodeTestCase(unittest.TestCase):
n = SCons.Node.Node()
n.postprocess()
+ 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
+ n1.add_to_waiting_parents(n2)
+ assert n1.waiting_parents == [n2], n1.waiting_parents
+
+ 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
+
if __name__ == "__main__":
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 2841759..75b3a6d 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -116,10 +116,11 @@ class Node:
self.ignore = [] # dependencies to ignore
self.ignore_dict = {}
self.implicit = None # implicit (scanned) dependencies (None means not scanned yet)
- self.parents = {}
+ self.waiting_parents = []
self.wkids = None # Kids yet to walk, when it's an array
self.target_scanner = None # explicit scanner from this node's Builder
- self.source_scanner = None # source scanner
+ self.source_scanner = None
+ self.backup_source_scanner = None
self.env = None
self.state = None
@@ -208,20 +209,29 @@ class Node:
else:
self.store_info(new_binfo)
- # Clear out the implicit dependency caches:
- # XXX this really should somehow be made more general and put
- # under the control of the scanners.
- if self.source_scanner:
- self.found_includes = {}
- self.includes = None
- for parent in self.get_parents():
- parent.implicit = None
- parent.del_binfo()
+ # Clear our scanned included files.
+ self.found_includes = {}
+ self.includes = None
+
+ # Clear the implicit dependency caches of any Nodes
+ # waiting for this Node to be built.
+ for parent in self.waiting_parents:
+ parent.implicit = None
+ parent.del_binfo()
+ self.waiting_parents = []
# The content just changed, delete any cached info
# so it will get recalculated.
self.del_cinfo()
+ def add_to_waiting_parents(self, node):
+ self.waiting_parents.append(node)
+
+ def call_for_all_waiting_parents(self, func):
+ func(self)
+ for parent in self.waiting_parents:
+ parent.call_for_all_waiting_parents(func)
+
def postprocess(self):
"""Clean up anything we don't need to hang onto after we've
been built."""
@@ -248,6 +258,8 @@ class Node:
self.found_includes = {}
self.implicit = None
+ self.waiting_parents = []
+
def visited(self):
"""Called just after this node has been visited
without requiring a build.."""
@@ -329,6 +341,10 @@ class Node:
if not scanner:
return []
+ # Give the scanner a chance to select a more specific scanner
+ # for this Node.
+ scanner = scanner.select(self)
+
try:
recurse = scanner.recursive
except AttributeError:
@@ -367,6 +383,22 @@ class Node:
self.implicit_factory_cache[path] = n
return n
+ def get_source_scanner(self, node):
+ """Fetch the source scanner for the specified node
+
+ NOTE: "self" is the target being built, "node" is
+ the source file for which we want to fetch the scanner.
+ """
+ if self.source_scanner:
+ return self.source_scanner
+ try:
+ scanner = node.builder.source_scanner
+ if scanner:
+ return scanner
+ except AttributeError:
+ pass
+ return node.backup_source_scanner or None
+
def scan(self):
"""Scan this node's dependents for implicit dependencies."""
# Don't bother scanning non-derived files, because we don't
@@ -405,20 +437,14 @@ class Node:
# self.del_binfo()
for child in self.children(scan=0):
- scanner = child.source_scanner
+ scanner = self.get_source_scanner(child)
if scanner:
- self._add_child(self.implicit,
- self.implicit_dict,
- child.get_implicit_deps(build_env,
- scanner,
- self))
+ deps = child.get_implicit_deps(build_env, scanner, self)
+ self._add_child(self.implicit, self.implicit_dict, deps)
# scan this node itself for implicit dependencies
- self._add_child(self.implicit,
- self.implicit_dict,
- self.get_implicit_deps(build_env,
- self.target_scanner,
- self))
+ deps = self.get_implicit_deps(build_env, self.target_scanner, self)
+ self._add_child(self.implicit, self.implicit_dict, deps)
# XXX See note above re: --implicit-cache.
#if implicit_cache:
@@ -632,7 +658,6 @@ class Node:
collection.append(c)
dict[c] = 1
added = 1
- c.parents[self] = 1
if added:
self._children_reset()
@@ -686,9 +711,6 @@ class Node:
else:
return self.sources + self.depends + self.implicit
- def get_parents(self):
- return self.parents.keys()
-
def set_state(self, state):
self.state = state
@@ -738,7 +760,8 @@ class Node:
if self.is_derived() and self.env:
env = self.get_build_env()
for s in self.sources:
- def f(node, env=env, scanner=s.source_scanner, target=self):
+ scanner = s.get_source_scanner(self)
+ def f(node, env=env, scanner=scanner, target=self):
return node.get_found_includes(env, scanner, target)
return SCons.Util.render_tree(s, f, 1)
else: