diff options
author | Steven Knight <knight@baldmt.com> | 2004-12-29 21:04:56 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2004-12-29 21:04:56 (GMT) |
commit | a2b119edf2fdd972c426f08f9898fb2efbe36646 (patch) | |
tree | 12b6722f049211b37574477e82ab5c49a0521052 /src/engine/SCons/Node | |
parent | 9113805b081ef58fdf56bd5b5a9be6afad0b7a41 (diff) | |
download | SCons-a2b119edf2fdd972c426f08f9898fb2efbe36646.zip SCons-a2b119edf2fdd972c426f08f9898fb2efbe36646.tar.gz SCons-a2b119edf2fdd972c426f08f9898fb2efbe36646.tar.bz2 |
Add a Memoizer metaclass to collect the logic for caching values in one location. Convert by-hand caching to use of Memoizer. (Kevin Quick)
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r-- | src/engine/SCons/Node/FS.py | 245 | ||||
-rw-r--r-- | src/engine/SCons/Node/FSTests.py | 69 | ||||
-rw-r--r-- | src/engine/SCons/Node/NodeTests.py | 34 | ||||
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 117 |
4 files changed, 197 insertions, 268 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 60ebb79..a67fa76 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -419,6 +419,12 @@ class EntryProxy(SCons.Util.Proxy): except AttributeError: entry = self.get() classname = string.split(str(entry.__class__), '.')[-1] + if classname[-2:] == "'>": + # new-style classes report their name as: + # "<class 'something'>" + # instead of the classic classes: + # "something" + classname = classname[:-2] raise AttributeError, "%s instance '%s' has no attribute '%s'" % (classname, entry.name, name) return attr @@ -447,7 +453,6 @@ class Base(SCons.Node.Node): self.name = name self.fs = fs - self.relpath = {self : '.'} assert directory, "A directory must be provided" @@ -465,31 +470,16 @@ class Base(SCons.Node.Node): """Completely clear a Node.FS.Base object of all its cached state (so that it can be re-evaluated by interfaces that do continuous integration builds). + __cache_reset__ """ SCons.Node.Node.clear(self) - try: - delattr(self, '_exists') - except AttributeError: - pass - try: - delattr(self, '_rexists') - except AttributeError: - pass - try: - delattr(self, '_str_val') - except AttributeError: - pass - self.relpath = {self : '.'} def get_dir(self): return self.dir def get_suffix(self): - try: - return self.ext - except AttributeError: - self.ext = SCons.Util.splitext(self.name)[1] - return self.ext + "__cacheable__" + return SCons.Util.splitext(self.name)[1] def rfile(self): return self @@ -497,33 +487,29 @@ class Base(SCons.Node.Node): def __str__(self): """A Node.FS.Base object's string representation is its path name.""" - try: - return self._str_val - except AttributeError: - global Save_Strings - if self.duplicate or self.is_derived(): - str_val = self.get_path() - else: - str_val = self.srcnode().get_path() - if Save_Strings: - self._str_val = str_val - return str_val + global Save_Strings + if Save_Strings: + return self._save_str() + return self._get_str() + + def _save_str(self): + "__cacheable__" + return self._get_str() + + def _get_str(self): + if self.duplicate or self.is_derived(): + return self.get_path() + return self.srcnode().get_path() rstr = __str__ def exists(self): - try: - return self._exists - except AttributeError: - self._exists = self.fs.exists(self.abspath) - return self._exists + "__cacheable__" + return self.fs.exists(self.abspath) def rexists(self): - try: - return self._rexists - except AttributeError: - self._rexists = self.rfile().exists() - return self._rexists + "__cacheable__" + return self.rfile().exists() def is_under(self, dir): if self is dir: @@ -537,44 +523,40 @@ class Base(SCons.Node.Node): def srcnode(self): """If this node is in a build path, return the node corresponding to its source file. Otherwise, return - ourself.""" - try: - return self._srcnode - except AttributeError: - dir=self.dir - name=self.name - while dir: - if dir.srcdir: - self._srcnode = self.fs.Entry(name, dir.srcdir, - klass=self.__class__) - if self._srcnode.is_under(dir): - # Shouldn't source from something in the build - # path: probably means build_dir is under - # src_dir and we are reflecting. - break - return self._srcnode - name = dir.name + os.sep + name - dir=dir.get_dir() - self._srcnode = self - return self._srcnode + ourself. + __cacheable__""" + dir=self.dir + name=self.name + while dir: + if dir.srcdir: + srcnode = self.fs.Entry(name, dir.srcdir, + klass=self.__class__) + if srcnode.is_under(dir): + # Shouldn't source from something in the build + # path: probably means build_dir is under + # src_dir and we are reflecting. + break + return srcnode + name = dir.name + os.sep + name + dir=dir.get_dir() + return self def get_path(self, dir=None): """Return path relative to the current working directory of the Node.FS.Base object that owns us.""" if not dir: dir = self.fs.getcwd() - try: - return self.relpath[dir] - except KeyError: - path_elems = [] - d = self + path_elems = [] + d = self + if d == dir: + path_elems.append('.') + else: while d != dir and not isinstance(d, ParentOfRoot): path_elems.append(d.name) d = d.dir path_elems.reverse() - ret = string.join(path_elems, os.sep) - self.relpath[dir] = ret - return ret + ret = string.join(path_elems, os.sep) + return ret def set_src_builder(self, builder): """Set the source code builder for this node.""" @@ -1142,7 +1124,8 @@ class Dir(Base): Set up this directory's entries and hook it into the file system tree. Specify that directories (this Node) don't use - signatures for calculating whether they're current.""" + signatures for calculating whether they're current. + __cache_reset__""" self.repositories = [] self.srcdir = None @@ -1166,30 +1149,11 @@ class Dir(Base): if node != self and isinstance(node, Dir): node.__clearRepositoryCache(duplicate) else: + node.clear() try: del node._srcreps except AttributeError: pass - try: - del node._rfile - except AttributeError: - pass - try: - del node._rexists - except AttributeError: - pass - try: - del node._exists - except AttributeError: - pass - try: - del node._srcnode - except AttributeError: - pass - try: - del node._str_val - except AttributeError: - pass if duplicate != None: node.duplicate=duplicate @@ -1311,15 +1275,13 @@ class Dir(Base): return 0 def rdir(self): - try: - return self._rdir - except AttributeError: - self._rdir = self - if not self.exists(): - n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top) - if n: - self._rdir = n - return self._rdir + "__cacheable__" + rdir = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top) + if n: + rdir = n + return rdir def sconsign(self): """Return the .sconsign file info for this directory, @@ -1420,9 +1382,8 @@ class File(Base): 'RDirs' : self.RDirs} def _morph(self): - """Turn a file system node into a File object.""" + """Turn a file system node into a File object. __cache_reset__""" self.scanner_paths = {} - self.found_includes = {} if not hasattr(self, '_local'): self._local = 0 @@ -1501,14 +1462,7 @@ class File(Base): path = scanner.path(env, target.cwd) target.scanner_paths[scanner] = path - key = str(id(env)) + '|' + str(id(scanner)) + '|' + string.join(map(str,path), ':') - try: - includes = self.found_includes[key] - except KeyError: - includes = scanner(self, env, path) - self.found_includes[key] = includes - - return includes + return scanner(self, env, path) def _createDir(self): # ensure that the directories for this node are @@ -1537,14 +1491,7 @@ class File(Base): # created the directory, depending on whether the -n # option was used or not. Delete the _exists and # _rexists attributes so they can be reevaluated. - try: - delattr(dirnode, '_exists') - except AttributeError: - pass - try: - delattr(dirnode, '_rexists') - except AttributeError: - pass + dirnode.clear() except OSError: pass @@ -1589,22 +1536,14 @@ class File(Base): return None def built(self): - """Called just after this node is sucessfully built.""" + """Called just after this node is successfully built. + __cache_reset__""" # Push this file out to cache before the superclass Node.built() # method has a chance to clear the build signature, which it # will do if this file has a source scanner. if self.fs.CachePath and self.fs.exists(self.path): CachePush(self, [], None) SCons.Node.Node.built(self) - self.found_includes = {} - try: - delattr(self, '_exists') - except AttributeError: - pass - try: - delattr(self, '_rexists') - except AttributeError: - pass def visited(self): if self.fs.CachePath and self.fs.cache_force and self.fs.exists(self.path): @@ -1656,7 +1595,11 @@ class File(Base): def is_pseudo_derived(self): return self.has_src_builder() - + + def _rmv_existing(self): + '__cache_reset__' + Unlink(self, [], None) + def prepare(self): """Prepare for this file to be created.""" SCons.Node.Node.prepare(self) @@ -1664,11 +1607,7 @@ class File(Base): if self.get_state() != SCons.Node.up_to_date: if self.exists(): if self.is_derived() and not self.precious: - Unlink(self, [], None) - try: - delattr(self, '_exists') - except AttributeError: - pass + self._rmv_existing() else: try: self._createDir() @@ -1684,9 +1623,13 @@ class File(Base): return None def exists(self): + "__cacheable__" # Duplicate from source path if we are set up to do this. if self.duplicate and not self.is_derived() and not self.linked: - src=self.srcnode().rfile() + src=self.srcnode() + if src is self: + return Base.exists(self) + src = src.rfile() if src.abspath != self.abspath and src.exists(): self._createDir() try: @@ -1703,14 +1646,7 @@ class File(Base): # created the file, depending on whether the -n # option was used or not. Delete the _exists and # _rexists attributes so they can be reevaluated. - try: - delattr(self, '_exists') - except AttributeError: - pass - try: - delattr(self, '_rexists') - except AttributeError: - pass + self.clear() return Base.exists(self) def new_binfo(self): @@ -1791,23 +1727,20 @@ class File(Base): LocalCopy(self, r, None) self.store_info(self.binfo) return 1 - self._rfile = self return None else: old = self.get_stored_info() return (old == self.binfo) def rfile(self): - try: - return self._rfile - except AttributeError: - self._rfile = self - if not self.exists(): - n = self.fs.Rsearch(self.path, clazz=File, - cwd=self.fs.Top) - if n: - self._rfile = n - return self._rfile + "__cacheable__" + rfile = self + if not self.exists(): + n = self.fs.Rsearch(self.path, clazz=File, + cwd=self.fs.Top) + if n: + rfile = n + return rfile def rstr(self): return str(self.rfile()) @@ -1840,7 +1773,9 @@ def find_file(filename, paths, node_factory=default_fs.File, verbose=None): find_file(str, [Dir()]) -> [nodes] filename - a filename to find - paths - a list of directory path *nodes* to search in + paths - a list of directory path *nodes* to search in. Can be + represented as a list, a tuple, or a callable that is + called with no arguments and returns the list or tuple. returns - the node created from the found file. @@ -1853,6 +1788,10 @@ def find_file(filename, paths, node_factory=default_fs.File, verbose=None): if verbose and not SCons.Util.is_String(verbose): verbose = "find_file" retval = None + + if callable(paths): + paths = paths() + for dir in paths: if verbose: sys.stdout.write(" %s: looking for '%s' in '%s' ...\n" % (verbose, filename, dir)) diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index c829f22..e71093f 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -211,7 +211,7 @@ class BuildDirTestCase(unittest.TestCase): assert str(f2) == os.path.normpath('build/var2/test.in'), str(f2) # Build path exists assert f2.exists() - # ...and should copy the file from src to build path + # ...and exists() should copy the file from src to build path assert test.read(['work', 'build', 'var2', 'test.in']) == 'test.in',\ test.read(['work', 'build', 'var2', 'test.in']) # Since exists() is true, so should rexists() be @@ -898,10 +898,6 @@ class FSTestCase(unittest.TestCase): assert deps == [xyz], deps assert s.call_count == 1, s.call_count - 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_found_includes(env, s, t1) @@ -946,9 +942,9 @@ class FSTestCase(unittest.TestCase): f1 = fs.File(test.workpath("do_i_exist")) assert not f1.exists() test.write("do_i_exist","\n") - assert not f1.exists() + assert not f1.exists(), "exists() call not cached" f1.built() - assert f1.exists() + assert f1.exists(), "exists() call caching not reset" test.unlink("do_i_exist") assert f1.exists() f1.built() @@ -1822,33 +1818,48 @@ class clearTestCase(unittest.TestCase): def runTest(self): """Test clearing FS nodes of cached data.""" fs = SCons.Node.FS.FS() + test = TestCmd(workdir='') e = fs.Entry('e') - e._exists = 1 - e._rexists = 1 - e._str_val = 'e' + assert not e.exists() + assert not e.rexists() + assert str(e) == 'e', str(d) e.clear() - assert not hasattr(e, '_exists') - assert not hasattr(e, '_rexists') - assert not hasattr(e, '_str_val') + assert not e.exists() + assert not e.rexists() + assert str(e) == 'e', str(d) - d = fs.Dir('d') - d._exists = 1 - d._rexists = 1 - d._str_val = 'd' + d = fs.Dir(test.workpath('d')) + test.subdir('d') + assert d.exists() + assert d.rexists() + assert str(d) == test.workpath('d'), str(d) + fs.rename(test.workpath('d'), test.workpath('gone')) + # Verify caching is active + assert d.exists(), 'caching not active' + assert d.rexists() + assert str(d) == test.workpath('d'), str(d) + # Now verify clear() resets the cache d.clear() - assert not hasattr(d, '_exists') - assert not hasattr(d, '_rexists') - assert not hasattr(d, '_str_val') - - f = fs.File('f') - f._exists = 1 - f._rexists = 1 - f._str_val = 'f' + assert not d.exists() + assert not d.rexists() + assert str(d) == test.workpath('d'), str(d) + + f = fs.File(test.workpath('f')) + test.write(test.workpath('f'), 'file f') + assert f.exists() + assert f.rexists() + assert str(f) == test.workpath('f'), str(f) + # Verify caching is active + test.unlink(test.workpath('f')) + assert f.exists() + assert f.rexists() + assert str(f) == test.workpath('f'), str(f) + # Now verify clear() resets the cache f.clear() - assert not hasattr(f, '_exists') - assert not hasattr(f, '_rexists') - assert not hasattr(f, '_str_val') + assert not f.exists() + assert not f.rexists() + assert str(f) == test.workpath('f'), str(f) class postprocessTestCase(unittest.TestCase): def runTest(self): @@ -2066,7 +2077,7 @@ class SaveStringsTestCase(unittest.TestCase): s = map(str, nodes) expect = map(os.path.normpath, ['src/f', 'd1/f', 'd0/b', 'd1/b']) - assert s == expect, s + assert s == expect, 'node str() not cached: %s'%s if __name__ == "__main__": suite = unittest.TestSuite() diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py index 3c402d0..106e44e 100644 --- a/src/engine/SCons/Node/NodeTests.py +++ b/src/engine/SCons/Node/NodeTests.py @@ -525,13 +525,18 @@ class NodeTestCase(unittest.TestCase): def test_explain(self): """Test explaining why a Node must be rebuilt """ - node = SCons.Node.Node() + class testNode(SCons.Node.Node): + def __str__(self): return 'xyzzy' + node = testNode() node.exists = lambda: None - node.__str__ = lambda: 'xyzzy' + # Can't do this with new-style classes (python bug #1066490) + #node.__str__ = lambda: 'xyzzy' result = node.explain() assert result == "building `xyzzy' because it doesn't exist\n", result - node = SCons.Node.Node() + class testNode2(SCons.Node.Node): + def __str__(self): return 'null_binfo' + node = testNode2() result = node.explain() assert result == None, result @@ -540,7 +545,7 @@ class NodeTestCase(unittest.TestCase): pass node.get_stored_info = Null_BInfo - node.__str__ = lambda: 'null_binfo' + #see above: node.__str__ = lambda: 'null_binfo' result = node.explain() assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result @@ -802,7 +807,7 @@ class NodeTestCase(unittest.TestCase): """ target = SCons.Node.Node() source = SCons.Node.Node() - s = target.get_source_scanner(source, None) + s = target.get_source_scanner(source) assert s is None, s ts1 = Scanner() @@ -821,19 +826,19 @@ class NodeTestCase(unittest.TestCase): builder = Builder2(ts1) targets = builder([source]) - s = targets[0].get_source_scanner(source, None) + s = targets[0].get_source_scanner(source) assert s is ts1, s target.builder_set(Builder2(ts1)) target.builder.source_scanner = ts2 - s = target.get_source_scanner(source, None) + s = target.get_source_scanner(source) assert s is ts2, s builder = Builder1(env=Environment(SCANNERS = [ts3])) targets = builder([source]) - s = targets[0].get_source_scanner(source, builder.env) + s = targets[0].get_source_scanner(source) assert s is ts3, s @@ -880,14 +885,13 @@ class NodeTestCase(unittest.TestCase): SCons.Node.implicit_deps_unchanged = None try: sn = StoredNode("eee") - sn._children = ['fake'] sn.builder_set(Builder()) sn.builder.target_scanner = s sn.scan() assert sn.implicit == [], sn.implicit - assert sn._children == [], sn._children + assert sn.children() == [], sn.children() finally: SCons.Sig.default_calc = save_default_calc @@ -1100,7 +1104,6 @@ class NodeTestCase(unittest.TestCase): n.clear() - assert n.get_state() is None, n.get_state() assert not hasattr(n, 'binfo'), n.bsig assert n.includes is None, n.includes assert n.found_includes == {}, n.found_includes @@ -1176,8 +1179,13 @@ class NodeListTestCase(unittest.TestCase): assert s == "['n3', 'n2', 'n1']", s r = repr(nl) - r = re.sub('at (0x)?[0-9A-Fa-f]+', 'at 0x', repr(nl)) - l = string.join(["<__main__.MyNode instance at 0x>"]*3, ", ") + r = re.sub('at (0x)?[0-9a-z]+', 'at 0x', r) + # Don't care about ancestry: just leaf value of MyNode + r = re.sub('<.*?\.MyNode', '<MyNode', r) + # New-style classes report as "object"; classic classes report + # as "instance"... + r = re.sub("object", "instance", r) + l = string.join(["<MyNode instance at 0x>"]*3, ", ") assert r == '[%s]' % l, r diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index c7a652d..e239e93 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -92,6 +92,8 @@ class Node: build, or use to build other Nodes. """ + __metaclass__ = SCons.Memoize.Memoized_Metaclass + class Attrs: pass @@ -147,18 +149,9 @@ class Node: return {} def get_build_env(self): - """Fetch the appropriate Environment to build this node.""" - try: - build_env = self._build_env - except AttributeError: - # This gets called a lot, so cache it. A node gets created - # in the context of a specific environment and it doesn't - # get "moved" to a different environment, so caching this - # value is safe. - executor = self.get_executor() - build_env = executor.get_build_env() - self._build_env = build_env - return self._build_env + """Fetch the appropriate Environment to build this node. + __cacheable__""" + return self.get_executor().get_build_env() def set_executor(self, executor): """Set the action executor for this node.""" @@ -219,7 +212,7 @@ class Node: apply(executor, (self, errfunc), kw) def built(self): - """Called just after this node is sucessfully built.""" + """Called just after this node is successfully built.""" # Clear the implicit dependency caches of any Nodes # waiting for this Node to be built. @@ -237,9 +230,7 @@ class Node: # Reset this Node's cached state since it was just built and # various state has changed. - save_state = self.get_state() self.clear() - self.set_state(save_state) # Had build info, so it should be stored in the signature # cache. However, if the build info included a content @@ -275,8 +266,8 @@ class Node: """Completely clear a Node of all its cached state (so that it can be re-evaluated by interfaces that do continuous integration builds). + __reset_cache__ """ - self.set_state(None) self.del_binfo() self.del_cinfo() try: @@ -299,8 +290,8 @@ class Node: return reduce(lambda D,N,C=self.children(): D or (N in C), nodes, 0) def builder_set(self, builder): + "__cache_reset__" self.builder = builder - self._src_scanners = {} # cached scanners are based on the builder def has_builder(self): """Return whether this Node has a builder or not. @@ -312,6 +303,7 @@ class Node: and __nonzero__ attributes on instances of our Builder Proxy class(es), generating a bazillion extra calls and slowing things down immensely. + __cacheable__ """ try: b = self.builder @@ -328,7 +320,8 @@ class Node: This allows an internal Builder created by SCons to be marked non-explicit, so that it can be overridden by an explicit builder that the user supplies (the canonical example being - directories).""" + directories). + __cacheable__""" return self.has_builder() and self.builder.is_explicit def get_builder(self, default_builder=None): @@ -411,37 +404,28 @@ class Node: return deps - # cache used to make implicit_factory fast. - implicit_factory_cache = {} - def implicit_factory(self, path): """ Turn a cache implicit dependency path into a node. This is called so many times that doing caching here is a significant performance boost. + __cacheable__ """ - try: - return self.implicit_factory_cache[path] - except KeyError: - n = self.builder.source_factory(path) - self.implicit_factory_cache[path] = n - return n + return self.builder.source_factory(path) + - def get_source_scanner(self, node, build_env): + 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. - build_env is the build environment (it's self.get_build_env(), - but the caller always knows this so it can give it - to us). - Implies self.has_builder() is true; again, expect to only be called from locations where this is already verified. This function may be called very often; it attempts to cache the scanner found to improve performance. + __cacheable__ """ # Called from scan() for each child (node) of this node # (self). The scan() may be called multiple times, so this @@ -451,22 +435,12 @@ class Node: # as an optimization of an already-determined value, not as a # changing parameter. - key = str(id(node)) + '|' + str(id(build_env)) - try: - return self._src_scanners[key] - except AttributeError: - self._src_scanners = {} - except KeyError: - pass - if not self.has_builder(): - self._src_scanners[key] = None return None try: scanner = self.builder.source_scanner if scanner: - self._src_scanners[key] = scanner return scanner except AttributeError: pass @@ -475,8 +449,7 @@ class Node: # based on the node's scanner key (usually the file # extension). - scanner = build_env.get_scanner(node.scanner_key()) - self._src_scanners[key] = scanner + scanner = self.get_build_env().get_scanner(node.scanner_key()) return scanner def scan(self): @@ -513,7 +486,7 @@ class Node: self.del_binfo() for child in self.children(scan=0): - scanner = self.get_source_scanner(child, build_env) + scanner = self.get_source_scanner(child) if scanner: deps = child.get_implicit_deps(build_env, scanner, self) self._add_child(self.implicit, self.implicit_dict, deps) @@ -545,28 +518,21 @@ class Node: def calc_signature(self, calc=None): """ Select and calculate the appropriate build signature for a node. + __cacheable__ self - the node calc - the signature calculation module returns - the signature """ - try: - return self._calculated_sig - except AttributeError: - if self.is_derived(): - import SCons.Defaults - - env = self.env or SCons.Defaults.DefaultEnvironment() - if env.use_build_signature(): - sig = self.calc_bsig(calc) - else: - sig = self.calc_csig(calc) - elif not self.rexists(): - sig = None - else: - sig = self.calc_csig(calc) - self._calculated_sig = sig - return sig + if self.is_derived(): + import SCons.Defaults + + env = self.env or SCons.Defaults.DefaultEnvironment() + if env.use_build_signature(): + return self.calc_bsig(calc) + elif not self.rexists(): + return None + return self.calc_csig(calc) def new_binfo(self): return BuildInfo() @@ -769,10 +735,8 @@ class Node: self.wkids.append(wkid) def _children_reset(self): - try: - delattr(self, '_children') - except AttributeError: - pass + "__cache_reset__" + pass def filter_ignore(self, nodelist): ignore = self.ignore @@ -782,17 +746,16 @@ class Node: result.append(node) return result + def _children_get(self): + "__cacheable__" + return self.filter_ignore(self.all_children(scan=0)) + def children(self, scan=1): """Return a list of the node's direct children, minus those that are ignored by this node.""" if scan: self.scan() - try: - return self._children - except AttributeError: - c = self.all_children(scan=0) - self._children = self.filter_ignore(c) - return self._children + return self._children_get() def all_children(self, scan=1): """Return a list of all the node's direct children.""" @@ -875,7 +838,7 @@ class Node: if self.is_derived() and self.env: env = self.get_build_env() for s in self.sources: - scanner = self.get_source_scanner(s, env) + scanner = self.get_source_scanner(s) 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) @@ -1022,6 +985,14 @@ else: del l del ul +if not SCons.Memoize.has_metaclass: + _Base = Node + class Node(SCons.Memoize.Memoizer, _Base): + def __init__(self, *args, **kw): + apply(_Base.__init__, (self,)+args, kw) + SCons.Memoize.Memoizer.__init__(self) + + def get_children(node, parent): return node.children() def ignore_cycle(node, stack): pass def do_nothing(node, parent): pass |