diff options
| author | Steven Knight <knight@baldmt.com> | 2003-01-12 16:25:20 (GMT) |
|---|---|---|
| committer | Steven Knight <knight@baldmt.com> | 2003-01-12 16:25:20 (GMT) |
| commit | 5d3b3d8891c805fe334be3cd71a2670b5c76d65a (patch) | |
| tree | 02c6faa63bafe79bd10f4b51aaba813f86725aa9 /src/engine/SCons/Node | |
| parent | e1c2427eeba11db45c65fbefb38d7f4b603121f0 (diff) | |
| download | SCons-5d3b3d8891c805fe334be3cd71a2670b5c76d65a.zip SCons-5d3b3d8891c805fe334be3cd71a2670b5c76d65a.tar.gz SCons-5d3b3d8891c805fe334be3cd71a2670b5c76d65a.tar.bz2 | |
Eliminate unnecessary scanning before a Node is rebuilt.
Diffstat (limited to 'src/engine/SCons/Node')
| -rw-r--r-- | src/engine/SCons/Node/FS.py | 5 | ||||
| -rw-r--r-- | src/engine/SCons/Node/FSTests.py | 35 | ||||
| -rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 123 | ||||
| -rw-r--r-- | src/engine/SCons/Node/__init__.py | 42 |
4 files changed, 157 insertions, 48 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index aa7f973..1b627fd 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -857,7 +857,10 @@ class File(Entry): def get_stored_implicit(self): return self.dir.sconsign().get_implicit(self.name) - def get_implicit_deps(self, env, scanner, target): + def get_found_includes(self, env, scanner, target): + """Return the included implicit dependencies in this file. + Cache results so we only scan the file once regardless of + how many times this information is requested.""" if not scanner: return [] diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 6c8893d..63cdf2c 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -65,14 +65,15 @@ class Builder: scanner_count = 0 class Scanner: - def __init__(self): + def __init__(self, node=None): global scanner_count scanner_count = scanner_count + 1 self.hash = scanner_count + self.node = node def path(self, env, target): return () def __call__(self, node, env, path): - return [node] + return [self.node] def __hash__(self): return self.hash @@ -674,45 +675,47 @@ class FSTestCase(unittest.TestCase): # Test scanning f1.builder_set(Builder(fs.File)) f1.env_set(Environment()) - f1.target_scanner = Scanner() + xyz = fs.File("xyz") + f1.target_scanner = Scanner(xyz) + f1.scan() - assert f1.implicit[0].path_ == os.path.join("d1", "f1") + assert f1.implicit[0].path_ == "xyz" f1.implicit = [] f1.scan() assert f1.implicit == [] f1.implicit = None f1.scan() - assert f1.implicit[0].path_ == os.path.join("d1", "f1"), f1.implicit[0].path_ + assert f1.implicit[0].path_ == "xyz" f1.store_implicit() - assert f1.get_stored_implicit()[0] == os.path.join("d1", "f1") + assert f1.get_stored_implicit()[0] == "xyz" - # Test underlying scanning functionality in get_implicit_deps() + # Test underlying scanning functionality in get_found_includes() env = Environment() f12 = fs.File("f12") t1 = fs.File("t1") - deps = f12.get_implicit_deps(env, None, t1) + deps = f12.get_found_includes(env, None, t1) assert deps == [], deps class MyScanner(Scanner): call_count = 0 def __call__(self, node, env, path): self.call_count = self.call_count + 1 - return [node] - s = MyScanner() + return Scanner.__call__(self, node, env, path) + s = MyScanner(xyz) - deps = f12.get_implicit_deps(env, s, t1) - assert deps == [f12], deps + deps = f12.get_found_includes(env, s, t1) + assert deps == [xyz], deps assert s.call_count == 1, s.call_count - deps = f12.get_implicit_deps(env, s, t1) - assert deps == [f12], deps + deps = f12.get_found_includes(env, s, t1) + assert deps == [xyz], deps assert s.call_count == 1, s.call_count f12.built() - deps = f12.get_implicit_deps(env, s, t1) - assert deps == [f12], deps + deps = f12.get_found_includes(env, s, t1) + assert deps == [xyz], deps assert s.call_count == 2, s.call_count # Test building a file whose directory is not there yet... diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index a2b95a5..7f55980 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -89,6 +89,26 @@ class Environment: def Override(selv, overrides): return overrides +class Scanner: + called = None + def __call__(self, node): + self.called = 1 + return node.found_includes + +class MyNode(SCons.Node.Node): + """The base Node class contains a number of do-nothing methods that + we expect to be overridden by real, functional Node subclasses. So + simulate a real, functional Node subclass. + """ + def __init__(self, name): + SCons.Node.Node.__init__(self) + self.name = name + self.found_includes = [] + def __str__(self): + return self.name + def get_found_includes(self, env, scanner, target): + return scanner(self) + class NodeTestCase(unittest.TestCase): @@ -98,27 +118,23 @@ class NodeTestCase(unittest.TestCase): """ global built_it - class MyNode(SCons.Node.Node): - def __str__(self): - return self.path # Make sure it doesn't blow up if no builder is set. - node = MyNode() + node = MyNode("www") node.build() assert built_it == None - node = MyNode() + node = MyNode("xxx") node.builder_set(Builder()) node.env_set(Environment()) node.path = "xxx" node.sources = ["yyy", "zzz"] node.build() assert built_it - assert type(built_target[0]) == type(MyNode()), type(built_target[0]) - assert str(built_target[0]) == "xxx", str(built_target[0]) + assert built_target[0] == node, built_target[0] assert built_source == ["yyy", "zzz"], built_source built_it = None - node = MyNode() + node = MyNode("qqq") node.builder_set(NoneBuilder()) node.env_set(Environment()) node.path = "qqq" @@ -126,14 +142,13 @@ class NodeTestCase(unittest.TestCase): node.overrides = { "foo" : 1, "bar" : 2 } node.build() assert built_it - assert type(built_target[0]) == type(MyNode()), type(built_target[0]) - assert str(built_target[0]) == "qqq", str(built_target[0]) + assert built_target[0] == node, build_target[0] assert built_source == ["rrr", "sss"], built_source assert built_args["foo"] == 1, built_args assert built_args["bar"] == 2, built_args - fff = MyNode() - ggg = MyNode() + fff = MyNode("fff") + ggg = MyNode("ggg") lb = ListBuilder(fff, ggg) e = Environment() fff.builder_set(lb) @@ -146,6 +161,8 @@ class NodeTestCase(unittest.TestCase): ggg.sources = ["hhh", "iii"] def test_depends_on(self): + """Test the depends_on() method + """ parent = SCons.Node.Node() child = SCons.Node.Node() parent.add_dependency([child]) @@ -348,17 +365,75 @@ class NodeTestCase(unittest.TestCase): assert three.get_parents() == [node] assert four.get_parents() == [node] - def test_scan(self): - """Test Scanner functionality""" - class DummyScanner: - pass - ds=DummyScanner() + def test_get_found_includes(self): + """Test the default get_found_includes() method + """ node = SCons.Node.Node() + target = SCons.Node.Node() + e = Environment() + deps = node.get_found_includes(e, None, target) + assert deps == [], deps + + def test_get_implicit_deps(self): + """Test get_implicit_deps() + """ + node = MyNode("nnn") + target = MyNode("ttt") + env = Environment() + + # No scanner at all returns [] + deps = node.get_implicit_deps(env, None, target) + assert deps == [], deps + + s = Scanner() + d = MyNode("ddd") + node.found_includes = [d] + + # Simple return of the found includes + deps = node.get_implicit_deps(env, s, target) + assert deps == [d], deps + + # No "recursive" attribute on scanner doesn't recurse + e = MyNode("eee") + d.found_includes = [e] + deps = node.get_implicit_deps(env, s, target) + assert deps == [d], map(str, deps) + + # Explicit "recursive" attribute on scanner doesn't recurse + s.recursive = None + deps = node.get_implicit_deps(env, s, target) + assert deps == [d], map(str, deps) + + # Explicit "recursive" attribute on scanner which does recurse + s.recursive = 1 + deps = node.get_implicit_deps(env, s, target) + assert deps == [d, e], map(str, deps) + + # Recursive scanning eliminates duplicates + f = MyNode("fff") + d.found_includes = [e, f] + e.found_includes = [f] + deps = node.get_implicit_deps(env, s, target) + assert deps == [d, e, f], map(str, deps) + + def test_scan(self): + """Test Scanner functionality + """ + node = MyNode("nnn") + node.builder = 1 + node.env_set(Environment()) + s = Scanner() + + d = MyNode("ddd") + node.found_includes = [d] + assert node.target_scanner == None, node.target_scanner - node.target_scanner = ds + node.target_scanner = s assert node.implicit is None + node.scan() - assert node.implicit == [] + assert s.called + assert node.implicit == [d], node.implicit def test_scanner_key(self): """Test that a scanner_key() method exists""" @@ -438,11 +513,6 @@ class NodeTestCase(unittest.TestCase): """Test walking a Node tree. """ - class MyNode(SCons.Node.Node): - def __init__(self, name): - SCons.Node.Node.__init__(self) - self.name = name - n1 = MyNode("n1") nw = SCons.Node.Walker(n1) @@ -505,11 +575,6 @@ class NodeTestCase(unittest.TestCase): def test_rstr(self): """Test the rstr() method.""" - class MyNode(SCons.Node.Node): - def __init__(self, name): - self.name = name - def __str__(self): - return self.name n1 = MyNode("n1") assert n1.rstr() == 'n1', n1.rstr() diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 16e28e2..bac547c 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -191,10 +191,48 @@ class Node: return None return Adapter(self) - def get_implicit_deps(self, env, scanner, target): - """Return a list of implicit dependencies for this node""" + def get_found_includes(self, env, scanner, target): + """Return the scanned include lines (implicit dependencies) + found in this node. + + The default is no implicit dependencies. We expect this method + to be overridden by any subclass that can be scanned for + implicit dependencies. + """ return [] + def get_implicit_deps(self, env, scanner, target): + """Return a list of implicit dependencies for this node. + + This method exists to handle recursive invocation of the scanner + on the implicit dependencies returned by the scanner, if the + scanner's recursive flag says that we should. + """ + if not scanner: + return [] + + try: + recurse = scanner.recursive + except AttributeError: + recurse = None + + nodes = [self] + seen = {} + seen[self] = 1 + deps = [] + while nodes: + n = nodes.pop(0) + d = filter(lambda x, seen=seen: not seen.has_key(x), + n.get_found_includes(env, scanner, target)) + if d: + deps.extend(d) + for n in d: + seen[n] = 1 + if recurse: + nodes.extend(d) + + return deps + def scan(self): """Scan this node's dependents for implicit dependencies.""" # Don't bother scanning non-derived files, because we don't |
