summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Scanner
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-01-12 16:25:20 (GMT)
committerSteven Knight <knight@baldmt.com>2003-01-12 16:25:20 (GMT)
commit5d3b3d8891c805fe334be3cd71a2670b5c76d65a (patch)
tree02c6faa63bafe79bd10f4b51aaba813f86725aa9 /src/engine/SCons/Scanner
parente1c2427eeba11db45c65fbefb38d7f4b603121f0 (diff)
downloadSCons-5d3b3d8891c805fe334be3cd71a2670b5c76d65a.zip
SCons-5d3b3d8891c805fe334be3cd71a2670b5c76d65a.tar.gz
SCons-5d3b3d8891c805fe334be3cd71a2670b5c76d65a.tar.bz2
Eliminate unnecessary scanning before a Node is rebuilt.
Diffstat (limited to 'src/engine/SCons/Scanner')
-rw-r--r--src/engine/SCons/Scanner/C.py11
-rw-r--r--src/engine/SCons/Scanner/CTests.py4
-rw-r--r--src/engine/SCons/Scanner/Fortran.py7
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py19
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py65
-rw-r--r--src/engine/SCons/Scanner/__init__.py54
6 files changed, 106 insertions, 54 deletions
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
index cbcf1c6..b9f2d8f 100644
--- a/src/engine/SCons/Scanner/C.py
+++ b/src/engine/SCons/Scanner/C.py
@@ -45,11 +45,12 @@ include_re = re.compile('^[ \t]*#[ \t]*include[ \t]+(<|")([^>"]+)(>|")', re.M)
def CScan(fs = SCons.Node.FS.default_fs):
"""Return a prototype Scanner instance for scanning source files
that use the C pre-processor"""
- cs = SCons.Scanner.Recursive(scan, "CScan", fs,
- [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
- ".h", ".H", ".hxx", ".hpp", ".hh",
- ".F", ".fpp", ".FPP"],
- path_function = path)
+ cs = SCons.Scanner.Current(scan, "CScan", fs,
+ [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+ ".h", ".H", ".hxx", ".hpp", ".hh",
+ ".F", ".fpp", ".FPP"],
+ path_function = path,
+ recursive = 1)
return cs
def path(env, dir, fs = SCons.Node.FS.default_fs):
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index f02474c..8f5a7dd 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -208,7 +208,7 @@ class CScannerTestCase1(unittest.TestCase):
s = SCons.Scanner.C.CScan()
path = s.path(env)
deps = s(make_node('f1.cpp'), env, path)
- headers = ['f1.h', 'f2.h', 'fi.h']
+ headers = ['f1.h', 'f2.h']
deps_match(self, deps, map(test.workpath, headers))
class CScannerTestCase2(unittest.TestCase):
@@ -258,7 +258,7 @@ class CScannerTestCase5(unittest.TestCase):
assert n.rexists_called
headers = ['d1/f1.h', 'd1/f2.h', 'd1/f3-test.h',
- 'f1.h', 'f2.h', 'f3-test.h', 'fi.h', 'fj.h']
+ 'f1.h', 'f2.h', 'f3-test.h']
deps_match(self, deps, map(test.workpath, headers))
class CScannerTestCase6(unittest.TestCase):
diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py
index e23c7a5..034d5ac 100644
--- a/src/engine/SCons/Scanner/Fortran.py
+++ b/src/engine/SCons/Scanner/Fortran.py
@@ -45,9 +45,10 @@ include_re = re.compile("INCLUDE[ \t]+'([\\w./\\\\]+)'", re.M)
def FortranScan(fs = SCons.Node.FS.default_fs):
"""Return a prototype Scanner instance for scanning source files
for Fortran INCLUDE statements"""
- scanner = SCons.Scanner.Recursive(scan, "FortranScan", fs,
- [".f", ".F", ".for", ".FOR"],
- path_function = path)
+ scanner = SCons.Scanner.Current(scan, "FortranScan", fs,
+ [".f", ".F", ".for", ".FOR"],
+ path_function = path,
+ recursive = 1)
return scanner
def path(env, dir, fs = SCons.Node.FS.default_fs):
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
index f721d89..03165df 100644
--- a/src/engine/SCons/Scanner/FortranTests.py
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -92,6 +92,15 @@ test.write('fff4.f',"""
test.write('include/f4.f', "\n")
test.write('subdir/include/f4.f', "\n")
+test.write('fff5.f',"""
+ PROGRAM FOO
+ INCLUDE 'f5.f'
+ INCLUDE 'not_there.f'
+ STOP
+ END
+""")
+
+test.write('f5.f', "\n")
test.subdir('repository', ['repository', 'include'],
[ 'repository', 'src' ])
@@ -173,7 +182,7 @@ class FortranScannerTestCase1(unittest.TestCase):
path = s.path(env)
fs = SCons.Node.FS.FS(original)
deps = s(make_node('fff1.f', fs), env, path)
- headers = ['f1.f', 'f2.f', 'fi.f']
+ headers = ['f1.f', 'f2.f']
deps_match(self, deps, map(test.workpath, headers))
test.unlink('f1.f')
test.unlink('f2.f')
@@ -187,7 +196,7 @@ class FortranScannerTestCase2(unittest.TestCase):
path = s.path(env)
fs = SCons.Node.FS.FS(original)
deps = s(make_node('fff1.f', fs), env, path)
- headers = ['f1.f', 'f2.f', 'fi.f']
+ headers = ['f1.f', 'f2.f']
deps_match(self, deps, map(test.workpath, headers))
test.unlink('f1.f')
test.unlink('f2.f')
@@ -309,18 +318,16 @@ class FortranScannerTestCase11(unittest.TestCase):
to = TestOut()
to.out = None
SCons.Warnings._warningOut = to
- test.write('f4.f'," INCLUDE 'not_there.f'\n")
fs = SCons.Node.FS.FS(test.workpath(''))
env = DummyEnvironment([])
s = SCons.Scanner.Fortran.FortranScan(fs=fs)
path = s.path(env)
- deps = s(fs.File('fff4.f'), env, path)
+ deps = s(fs.File('fff5.f'), env, path)
# Did we catch the warning from not finding not_there.f?
assert to.out
- deps_match(self, deps, [ 'f4.f' ])
- test.unlink('f4.f')
+ deps_match(self, deps, [ 'f5.f' ])
class FortranScannerTestCase12(unittest.TestCase):
def runTest(self):
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
index 7280c2f..431e7ae 100644
--- a/src/engine/SCons/Scanner/ScannerTests.py
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -122,7 +122,7 @@ class ScannerHashTestCase(ScannerTestBase, unittest.TestCase):
"did not hash Scanner base class as expected")
class ScannerCheckTestCase(unittest.TestCase):
- "Test the Scanner.Base class __hash__() method"
+ "Test the Scanner.Base class scan_check method"
def setUp(self):
self.checked = {}
def runTest(self):
@@ -138,6 +138,67 @@ class ScannerCheckTestCase(unittest.TestCase):
self.failUnless(self.checked['x'] == 1,
"did not call check function")
+class ScannerRecursiveTestCase(ScannerTestBase, unittest.TestCase):
+ "Test the Scanner.Base class recursive flag"
+ def runTest(self):
+ s = SCons.Scanner.Base(function = self.func)
+ self.failUnless(s.recursive == None,
+ "incorrect default recursive value")
+ s = SCons.Scanner.Base(function = self.func, recursive = None)
+ self.failUnless(s.recursive == None,
+ "did not set recursive flag to None")
+ s = SCons.Scanner.Base(function = self.func, recursive = 1)
+ self.failUnless(s.recursive == 1,
+ "did not set recursive flag to 1")
+
+class CurrentTestCase(ScannerTestBase, unittest.TestCase):
+ "Test the Scanner.Current class"
+ def runTest(self):
+ class MyNode:
+ def __init__(self):
+ self.called_has_builder = None
+ self.called_current = None
+ self.func_called = None
+ class HasNoBuilder(MyNode):
+ def has_builder(self):
+ self.called_has_builder = 1
+ return None
+ class IsNotCurrent(MyNode):
+ def has_builder(self):
+ self.called_has_builder = 1
+ return 1
+ def current(self, sig):
+ self.called_current = 1
+ return None
+ class IsCurrent(MyNode):
+ def has_builder(self):
+ self.called_has_builder = 1
+ return 1
+ def current(self, sig):
+ self.called_current = 1
+ return 1
+ def func(node, env, path):
+ node.func_called = 1
+ return []
+ env = DummyEnvironment()
+ s = SCons.Scanner.Current(func)
+ path = s.path(env)
+ hnb = HasNoBuilder()
+ s(hnb, env, path)
+ self.failUnless(hnb.called_has_builder, "did not call has_builder()")
+ self.failUnless(not hnb.called_current, "did call current()")
+ self.failUnless(hnb.func_called, "did not call func()")
+ inc = IsNotCurrent()
+ s(inc, env, path)
+ self.failUnless(inc.called_has_builder, "did not call has_builder()")
+ self.failUnless(inc.called_current, "did not call current()")
+ self.failUnless(not inc.func_called, "did call func()")
+ ic = IsCurrent()
+ s(ic, env, path)
+ self.failUnless(ic.called_has_builder, "did not call has_builder()")
+ self.failUnless(ic.called_current, "did not call current()")
+ self.failUnless(ic.func_called, "did not call func()")
+
def suite():
suite = unittest.TestSuite()
suite.addTest(ScannerPositionalTestCase())
@@ -146,6 +207,8 @@ def suite():
suite.addTest(ScannerKeywordArgumentTestCase())
suite.addTest(ScannerHashTestCase())
suite.addTest(ScannerCheckTestCase())
+ suite.addTest(ScannerRecursiveTestCase())
+ suite.addTest(CurrentTestCase())
return suite
if __name__ == "__main__":
diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py
index c27c762..cd157af 100644
--- a/src/engine/SCons/Scanner/__init__.py
+++ b/src/engine/SCons/Scanner/__init__.py
@@ -31,6 +31,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Node.FS
+import SCons.Sig
import SCons.Util
@@ -55,7 +56,8 @@ class Base:
path_function = None,
node_class = SCons.Node.FS.Entry,
node_factory = SCons.Node.FS.default_fs.File,
- scan_check = None):
+ scan_check = None,
+ recursive = None):
"""
Construct a new scanner object given a scanner function.
@@ -88,6 +90,10 @@ class Base:
'scan_check' - a function to be called to first check whether
this node really needs to be scanned.
+ 'recursive' - specifies that this scanner should be invoked
+ recursively on the implicit dependencies it returns (the
+ canonical example being #include lines in C source files).
+
The scanner function's first argument will be the name of a file
that should be scanned for dependencies, the second argument will
be an Environment object, the third argument will be the value
@@ -117,6 +123,7 @@ class Base:
self.node_class = node_class
self.node_factory = node_factory
self.scan_check = scan_check
+ self.recursive = recursive
def path(self, env, dir = None):
if not self.path_function:
@@ -160,43 +167,16 @@ class Base:
"""Add a skey to the list of skeys"""
self.skeys.append(skey)
-class RExists(Base):
+class Current(Base):
"""
- Scan a node only if it exists (locally or in a Repository).
+ A class for scanning files that are source files (have no builder)
+ or are derived files and are current (which implies that they exist,
+ either locally or in a repository).
"""
+
def __init__(self, *args, **kw):
- def rexists_check(node):
- return node.rexists()
- kw['scan_check'] = rexists_check
+ def current_check(node):
+ c = not node.has_builder() or node.current(SCons.Sig.default_calc)
+ return c
+ kw['scan_check'] = current_check
apply(Base.__init__, (self,) + args, kw)
-
-class Recursive(RExists):
- """
- The class for recursive dependency scanning. This will
- re-scan any new files returned by each call to the
- underlying scanning function, and return the aggregate
- list of all dependencies.
- """
-
- def __call__(self, node, env, path = ()):
- """
- This method does the actual scanning. 'node' is the node
- that will be passed to the scanner function, and 'env' is the
- environment that will be passed to the scanner function. An
- aggregate list of dependency nodes for the specified filename
- and any of its scanned dependencies will be returned.
- """
-
- nodes = [node]
- seen = {node : 0}
- deps = []
- while nodes:
- n = nodes.pop(0)
- d = filter(lambda x, seen=seen: not seen.has_key(x),
- Base.__call__(self, n, env, path))
- if d:
- deps.extend(d)
- nodes.extend(d)
- for n in d:
- seen[n] = 0
- return deps