diff options
| author | Steven Knight <knight@baldmt.com> | 2005-02-14 03:22:34 (GMT) |
|---|---|---|
| committer | Steven Knight <knight@baldmt.com> | 2005-02-14 03:22:34 (GMT) |
| commit | 08d7c4cd103fb39b6010b980209a777ceea1ead2 (patch) | |
| tree | 1fea16d051dcdb1147ced94deefb11fd31b151c5 /src/engine/SCons/Node | |
| parent | 35451af4f3052befef3b41b3a971b3a8025b0577 (diff) | |
| download | SCons-08d7c4cd103fb39b6010b980209a777ceea1ead2.zip SCons-08d7c4cd103fb39b6010b980209a777ceea1ead2.tar.gz SCons-08d7c4cd103fb39b6010b980209a777ceea1ead2.tar.bz2 | |
Don't read up entire directories to decide if an Alias is up-to-date.
Diffstat (limited to 'src/engine/SCons/Node')
| -rw-r--r-- | src/engine/SCons/Node/FS.py | 81 | ||||
| -rw-r--r-- | src/engine/SCons/Node/FSTests.py | 29 | ||||
| -rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 33 | ||||
| -rw-r--r-- | src/engine/SCons/Node/__init__.py | 22 |
4 files changed, 103 insertions, 62 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 314faf8..cc0fe95 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -599,6 +599,16 @@ class Entry(Base): time comes, and then call the same-named method in the transformed class.""" + def disambiguate(self): + if self.fs.isdir(self.abspath): + self.__class__ = Dir + self._morph() + else: + self.__class__ = File + self._morph() + self.clear() + return self + def rfile(self): """We're a generic Entry, but the caller is actually looking for a File at this point, so morph into one.""" @@ -610,8 +620,7 @@ class Entry(Base): def get_found_includes(self, env, scanner, path): """If we're looking for included files, it's because this Entry is really supposed to be a File itself.""" - node = self.rfile() - return node.get_found_includes(env, scanner, path) + return self.disambiguate().get_found_includes(env, scanner, path) def scanner_key(self): return self.get_suffix() @@ -638,29 +647,13 @@ class Entry(Base): """Return if the Entry exists. Check the file system to see what we should turn into first. Assume a file if there's no directory.""" - if self.fs.isdir(self.abspath): - self.__class__ = Dir - self._morph() - return Dir.exists(self) - else: - self.__class__ = File - self._morph() - self.clear() - return File.exists(self) + return self.disambiguate().exists() def calc_signature(self, calc=None): """Return the Entry's calculated signature. Check the file system to see what we should turn into first. Assume a file if there's no directory.""" - if self.fs.isdir(self.abspath): - self.__class__ = Dir - self._morph() - return Dir.calc_signature(self, calc) - else: - self.__class__ = File - self._morph() - self.clear() - return File.calc_signature(self, calc) + return self.disambiguate().calc_signature(calc) def must_be_a_Dir(self): """Called to make sure a Node is a Dir. Since we're an @@ -1180,6 +1173,9 @@ class Dir(Base): self._sconsign = None self.build_dirs = [] + def disambiguate(self): + return self + def __clearRepositoryCache(self, duplicate=None): """Called when we change the repository(ies) for a directory. This clears any cached information that is invalidated by changing @@ -1256,19 +1252,33 @@ class Dir(Base): self.implicit = [] self.implicit_dict = {} self._children_reset() - try: - for filename in self.fs.listdir(self.abspath): - if filename != '.sconsign': - self.Entry(filename) - except OSError: - # Directory does not exist. No big deal - pass - keys = filter(lambda k: k != '.' and k != '..', self.entries.keys()) - kids = map(lambda x, s=self: s.entries[x], keys) - def c(one, two): - return cmp(one.abspath, two.abspath) - kids.sort(c) - self._add_child(self.implicit, self.implicit_dict, kids) + + dont_scan = lambda k: k not in ['.', '..', '.sconsign'] + deps = filter(dont_scan, self.entries.keys()) + # keys() is going to give back the entries in an internal, + # unsorted order. Sort 'em so the order is deterministic. + deps.sort() + entries = map(lambda n, e=self.entries: e[n], deps) + + self._add_child(self.implicit, self.implicit_dict, entries) + + def get_found_includes(self, env, scanner, path): + """Return the included implicit dependencies in this file. + Cache results so we only scan the file once per path + regardless of how many times this information is requested. + __cacheable__""" + if not scanner: + return [] + # Clear cached info for this Node. If we already visited this + # directory on our walk down the tree (because we didn't know at + # that point it was being used as the source for another Node) + # then we may have calculated build signature before realizing + # we had to scan the disk. Now that we have to, though, we need + # to invalidate the old calculated signature so that any node + # dependent on our directory structure gets one that includes + # info about everything on disk. + self.clear() + return scanner(self, env, path) def build(self, **kw): """A null "builder" for directories.""" @@ -1295,7 +1305,7 @@ class Dir(Base): for kid in self.children(): contents.write(kid.get_contents()) return contents.getvalue() - + def prepare(self): pass @@ -1464,6 +1474,9 @@ class File(Base): if not hasattr(self, '_local'): self._local = 0 + def disambiguate(self): + return self + def root(self): return self.dir.root() diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 2846f64..99a95b6 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -62,6 +62,8 @@ class Scanner: return self.hash def select(self, node): return self + def recurse_nodes(self, nodes): + return nodes class Environment: def __init__(self): @@ -1876,6 +1878,32 @@ class clearTestCase(unittest.TestCase): assert not f.rexists() assert str(f) == test.workpath('f'), str(f) +class disambiguateTestCase(unittest.TestCase): + def runTest(self): + """Test calling the disambiguate() method.""" + test = TestCmd(workdir='') + + fs = SCons.Node.FS.FS() + + ddd = fs.Dir('ddd') + d = ddd.disambiguate() + assert d is ddd, d + + fff = fs.File('fff') + f = fff.disambiguate() + assert f is fff, f + + test.subdir('edir') + test.write('efile', "efile\n") + + edir = fs.Entry(test.workpath('edir')) + d = edir.disambiguate() + assert d.__class__ is ddd.__class__, d.__class__ + + efile = fs.Entry(test.workpath('efile')) + f = efile.disambiguate() + assert f.__class__ is fff.__class__, f.__class__ + class postprocessTestCase(unittest.TestCase): def runTest(self): """Test calling the postprocess() method.""" @@ -2108,6 +2136,7 @@ if __name__ == "__main__": suite.addTest(SConstruct_dirTestCase()) suite.addTest(CacheDirTestCase()) suite.addTest(clearTestCase()) + suite.addTest(disambiguateTestCase()) suite.addTest(postprocessTestCase()) suite.addTest(SpecialAttrTestCase()) suite.addTest(SaveStringsTestCase()) diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 281b5f2..90bb332 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -181,6 +181,8 @@ class Scanner: return () def select(self, node): return self + def recurse_nodes(self, nodes): + return nodes class MyNode(SCons.Node.Node): """The base Node class contains a number of do-nothing methods that @@ -807,28 +809,31 @@ class NodeTestCase(unittest.TestCase): deps = node.get_implicit_deps(env, s, target) assert deps == [d], deps - # No "recursive" attribute on scanner doesn't recurse + # By default, our fake scanner recurses e = MyNode("eee") - d.found_includes = [e] + f = MyNode("fff") + g = MyNode("ggg") + d.found_includes = [e, f] + f.found_includes = [g] deps = node.get_implicit_deps(env, s, target) - assert deps == [d], map(str, deps) + assert deps == [d, e, f, g], map(str, deps) - # Explicit "recursive" attribute on scanner doesn't recurse - s.recursive = None + # Recursive scanning eliminates duplicates + e.found_includes = [f] deps = node.get_implicit_deps(env, s, target) - assert deps == [d], map(str, deps) + assert deps == [d, e, f, g], map(str, deps) - # Explicit "recursive" attribute on scanner which does recurse - s.recursive = 1 + # Scanner method can select specific nodes to recurse + def no_fff(nodes): + return filter(lambda n: str(n)[0] != 'f', nodes) + s.recurse_nodes = no_fff deps = node.get_implicit_deps(env, s, target) - assert deps == [d, e], map(str, deps) + assert deps == [d, e, f], map(str, deps) - # Recursive scanning eliminates duplicates - f = MyNode("fff") - d.found_includes = [e, f] - e.found_includes = [f] + # Scanner method can short-circuit recursing entirely + s.recurse_nodes = lambda nodes: [] deps = node.get_implicit_deps(env, s, target) - assert deps == [d, e, f], map(str, deps) + assert deps == [d], map(str, deps) def test_get_scanner(self): """Test fetching the environment scanner for a Node diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 96a78ca..3c0ce99 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -394,25 +394,19 @@ class Node: # for this Node. scanner = scanner.select(self) - 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, path)) - if d: - deps.extend(d) - for n in d: - seen[n] = 1 - if recurse: - nodes.extend(d) + n = nodes.pop(0) + d = filter(lambda x, seen=seen: not seen.has_key(x), + n.get_found_includes(env, scanner, path)) + if d: + deps.extend(d) + for n in d: + seen[n] = 1 + nodes.extend(scanner.recurse_nodes(d)) return deps |
