summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Node
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Node')
-rw-r--r--src/engine/SCons/Node/FS.py23
-rw-r--r--src/engine/SCons/Node/FSTests.py33
-rw-r--r--src/engine/SCons/Node/__init__.py24
3 files changed, 72 insertions, 8 deletions
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 826307b..aa7f973 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -790,6 +790,8 @@ class File(Entry):
def _morph(self):
"""Turn a file system node into a File object."""
self.linked = 0
+ self.scanner_paths = {}
+ self.found_includes = {}
if not hasattr(self, '_local'):
self._local = 0
@@ -856,11 +858,23 @@ class File(Entry):
return self.dir.sconsign().get_implicit(self.name)
def get_implicit_deps(self, env, scanner, target):
- if scanner:
- return scanner.scan(self, env, target)
- else:
+ if not scanner:
return []
-
+
+ try:
+ path = target.scanner_paths[scanner]
+ except KeyError:
+ path = scanner.path(env, target.cwd)
+ target.scanner_paths[scanner] = path
+
+ try:
+ includes = self.found_includes[path]
+ except KeyError:
+ includes = scanner(self, env, path)
+ self.found_includes[path] = includes
+
+ return includes
+
def scanner_key(self):
return os.path.splitext(self.name)[1]
@@ -895,6 +909,7 @@ class File(Entry):
def built(self):
SCons.Node.Node.built(self)
+ self.found_includes = {}
if hasattr(self, '_exists'):
delattr(self, '_exists')
if hasattr(self, '_rexists'):
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index a624d97..e23178c 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -69,7 +69,9 @@ class Scanner:
global scanner_count
scanner_count = scanner_count + 1
self.hash = scanner_count
- def scan(self, node, env, target):
+ def path(self, env, target):
+ return ()
+ def __call__(self, node, env, path):
return [node]
def __hash__(self):
return self.hash
@@ -669,6 +671,35 @@ class FSTestCase(unittest.TestCase):
f1.store_implicit()
assert f1.get_stored_implicit()[0] == os.path.join("d1", "f1")
+ # Test underlying scanning functionality in get_implicit_deps()
+ env = Environment()
+ f12 = fs.File("f12")
+ t1 = fs.File("t1")
+
+ deps = f12.get_implicit_deps(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()
+
+ deps = f12.get_implicit_deps(env, s, t1)
+ assert deps == [f12], deps
+ assert s.call_count == 1, s.call_count
+
+ deps = f12.get_implicit_deps(env, s, t1)
+ assert deps == [f12], deps
+ assert s.call_count == 1, s.call_count
+
+ f12.built()
+
+ deps = f12.get_implicit_deps(env, s, t1)
+ assert deps == [f12], deps
+ assert s.call_count == 2, s.call_count
+
# Test building a file whose directory is not there yet...
f1 = fs.File(test.workpath("foo/bar/baz/ack"))
assert not f1.dir.exists()
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 3bafb9c..16e28e2 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -221,15 +221,17 @@ class Node:
self.implicit = []
self.del_bsig()
+ build_env = self.generate_build_env()
+
for child in self.children(scan=0):
self._add_child(self.implicit,
- child.get_implicit_deps(self.generate_build_env(),
+ child.get_implicit_deps(build_env,
child.source_scanner,
self))
# scan this node itself for implicit dependencies
self._add_child(self.implicit,
- self.get_implicit_deps(self.generate_build_env(),
+ self.get_implicit_deps(build_env,
self.target_scanner,
self))
@@ -384,7 +386,23 @@ class Node:
def all_children(self, scan=1):
"""Return a list of all the node's direct children."""
- #XXX Need to remove duplicates from this
+ # The return list may contain duplicate Nodes, especially in
+ # source trees where there are a lot of repeated #includes
+ # of a tangle of .h files. Profiling shows, however, that
+ # eliminating the duplicates with a brute-force approach that
+ # preserves the order (that is, something like:
+ #
+ # u = []
+ # for n in list:
+ # if n not in u:
+ # u.append(n)"
+ #
+ # takes more cycles than just letting the underlying methods
+ # hand back cached values if a Node's information is requested
+ # multiple times. (Other methods of removing duplicates, like
+ # using dictionary keys, lose the order, and the only ordered
+ # dictionary patterns I found all ended up using "not in"
+ # internally anyway...)
if scan:
self.scan()
if self.implicit is None: