summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2004-12-29 21:04:56 (GMT)
committerSteven Knight <knight@baldmt.com>2004-12-29 21:04:56 (GMT)
commita2b119edf2fdd972c426f08f9898fb2efbe36646 (patch)
tree12b6722f049211b37574477e82ab5c49a0521052 /src/engine/SCons/Node
parent9113805b081ef58fdf56bd5b5a9be6afad0b7a41 (diff)
downloadSCons-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.py245
-rw-r--r--src/engine/SCons/Node/FSTests.py69
-rw-r--r--src/engine/SCons/Node/NodeTests.py34
-rw-r--r--src/engine/SCons/Node/__init__.py117
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