summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Scanner
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-01-06 18:42:37 (GMT)
committerSteven Knight <knight@baldmt.com>2003-01-06 18:42:37 (GMT)
commit7fbd5909a526fc1ad282c7e701b0f7832af2e3ed (patch)
tree04f622a7b8fdab4ca337f20eced35b4d2699beb6 /src/engine/SCons/Scanner
parent6702e9dce5182eaa012da9dc511fcf85cf205049 (diff)
downloadSCons-7fbd5909a526fc1ad282c7e701b0f7832af2e3ed.zip
SCons-7fbd5909a526fc1ad282c7e701b0f7832af2e3ed.tar.gz
SCons-7fbd5909a526fc1ad282c7e701b0f7832af2e3ed.tar.bz2
Refactor the Scanner interface to eliminate unnecessary scanning and make it easier to write efficient scanners.
Diffstat (limited to 'src/engine/SCons/Scanner')
-rw-r--r--src/engine/SCons/Scanner/C.py122
-rw-r--r--src/engine/SCons/Scanner/CTests.py64
-rw-r--r--src/engine/SCons/Scanner/Fortran.py86
-rw-r--r--src/engine/SCons/Scanner/FortranTests.py95
-rw-r--r--src/engine/SCons/Scanner/Prog.py24
-rw-r--r--src/engine/SCons/Scanner/ProgTests.py23
-rw-r--r--src/engine/SCons/Scanner/ScannerTests.py13
-rw-r--r--src/engine/SCons/Scanner/__init__.py53
8 files changed, 257 insertions, 223 deletions
diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py
index 6e7db58..cbcf1c6 100644
--- a/src/engine/SCons/Scanner/C.py
+++ b/src/engine/SCons/Scanner/C.py
@@ -48,10 +48,20 @@ def CScan(fs = SCons.Node.FS.default_fs):
cs = SCons.Scanner.Recursive(scan, "CScan", fs,
[".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
".h", ".H", ".hxx", ".hpp", ".hh",
- ".F", ".fpp", ".FPP"])
+ ".F", ".fpp", ".FPP"],
+ path_function = path)
return cs
-def scan(node, env, target, fs = SCons.Node.FS.default_fs):
+def path(env, dir, fs = SCons.Node.FS.default_fs):
+ try:
+ cpppath = env['CPPPATH']
+ except KeyError:
+ return ()
+ return tuple(fs.Rsearchall(SCons.Util.mapPaths(cpppath, dir, env),
+ clazz = SCons.Node.FS.Dir,
+ must_exist = 0))
+
+def scan(node, env, cpppath = (), fs = SCons.Node.FS.default_fs):
"""
scan(node, Environment) -> [node]
@@ -72,70 +82,54 @@ def scan(node, env, target, fs = SCons.Node.FS.default_fs):
dependencies.
"""
- # This function caches various information in node and target:
- # target.cpppath - env['CPPPATH'] converted to nodes
- # node.found_includes - include files found by previous call to scan,
- # keyed on cpppath
- # node.includes - the result of include_re.findall()
-
- if not hasattr(target, 'cpppath'):
- try:
- target.cpppath = tuple(fs.Rsearchall(SCons.Util.mapPaths(env['CPPPATH'], target.cwd, env), clazz=SCons.Node.FS.Dir, must_exist=0))
- except KeyError:
- target.cpppath = ()
-
- cpppath = target.cpppath
-
node = node.rfile()
- if not node.found_includes.has_key(cpppath):
- if node.exists():
-
- # cache the includes list in node so we only scan it once:
- if node.includes != None:
- includes = node.includes
- else:
- includes = include_re.findall(node.get_contents())
- node.includes = includes
-
- nodes = []
- source_dir = node.get_dir()
- for include in includes:
- if include[0] == '"':
- n = SCons.Node.FS.find_file(include[1],
- (source_dir,) + cpppath,
- fs.File)
- else:
- n = SCons.Node.FS.find_file(include[1],
- cpppath + (source_dir,),
- fs.File)
-
- if not n is None:
- nodes.append(n)
- else:
- SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
- "No dependency generated for file: %s (included from: %s) -- file not found" % (include[1], node))
-
- # Schwartzian transform from the Python FAQ Wizard
- def st(List, Metric):
- def pairing(element, M = Metric):
- return (M(element), element)
- def stripit(pair):
- return pair[1]
- paired = map(pairing, List)
- paired.sort()
- return map(stripit, paired)
-
- def normalize(node):
- # We don't want the order of includes to be
- # modified by case changes on case insensitive OSes, so
- # normalize the case of the filename here:
- # (see test/win32pathmadness.py for a test of this)
- return SCons.Node.FS._my_normcase(str(node))
- node.found_includes[cpppath] = st(nodes, normalize)
+ # This function caches the following information:
+ # node.includes - the result of include_re.findall()
+ if not node.exists():
+ return []
+
+ # cache the includes list in node so we only scan it once:
+ if node.includes != None:
+ includes = node.includes
+ else:
+ includes = include_re.findall(node.get_contents())
+ node.includes = includes
+
+ nodes = []
+ source_dir = node.get_dir()
+ for include in includes:
+ if include[0] == '"':
+ n = SCons.Node.FS.find_file(include[1],
+ (source_dir,) + cpppath,
+ fs.File)
else:
+ n = SCons.Node.FS.find_file(include[1],
+ cpppath + (source_dir,),
+ fs.File)
- node.found_includes[cpppath] = []
-
- return node.found_includes[cpppath]
+ if not n is None:
+ nodes.append(n)
+ else:
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (include[1], node))
+
+ # Schwartzian transform from the Python FAQ Wizard
+ def st(List, Metric):
+ def pairing(element, M = Metric):
+ return (M(element), element)
+ def stripit(pair):
+ return pair[1]
+ paired = map(pairing, List)
+ paired.sort()
+ return map(stripit, paired)
+
+ def normalize(node):
+ # We don't want the order of includes to be
+ # modified by case changes on case insensitive OSes, so
+ # normalize the case of the filename here:
+ # (see test/win32pathmadness.py for a test of this)
+ return SCons.Node.FS._my_normcase(str(node))
+
+ return st(nodes, normalize)
diff --git a/src/engine/SCons/Scanner/CTests.py b/src/engine/SCons/Scanner/CTests.py
index 28a5f52..f02474c 100644
--- a/src/engine/SCons/Scanner/CTests.py
+++ b/src/engine/SCons/Scanner/CTests.py
@@ -159,10 +159,6 @@ test.write([ 'repository', 'src', 'ddd.h'], "\n")
# define some helpers:
-class DummyTarget:
- def __init__(self, cwd=None):
- self.cwd = cwd
-
class DummyEnvironment:
def __init__(self, listCppPath):
self.path = listCppPath
@@ -178,6 +174,9 @@ class DummyEnvironment:
def subst(self, arg):
return arg
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
def __getitem__(self,key):
return self.Dictionary()[key]
@@ -207,7 +206,8 @@ class CScannerTestCase1(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([])
s = SCons.Scanner.C.CScan()
- deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
+ path = s.path(env)
+ deps = s(make_node('f1.cpp'), env, path)
headers = ['f1.h', 'f2.h', 'fi.h']
deps_match(self, deps, map(test.workpath, headers))
@@ -215,7 +215,8 @@ class CScannerTestCase2(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.C.CScan()
- deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
+ path = s.path(env)
+ deps = s(make_node('f1.cpp'), env, path)
headers = ['d1/f2.h', 'f1.h']
deps_match(self, deps, map(test.workpath, headers))
@@ -223,7 +224,8 @@ class CScannerTestCase3(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.C.CScan()
- deps = s.scan(make_node('f2.cpp'), env, DummyTarget())
+ path = s.path(env)
+ deps = s(make_node('f2.cpp'), env, path)
headers = ['d1/d2/f1.h', 'd1/f1.h', 'f1.h']
deps_match(self, deps, map(test.workpath, headers))
@@ -231,7 +233,8 @@ class CScannerTestCase4(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
s = SCons.Scanner.C.CScan()
- deps = s.scan(make_node('f2.cpp'), env, DummyTarget())
+ path = s.path(env)
+ deps = s(make_node('f2.cpp'), env, path)
headers = ['d1/d2/f1.h', 'd1/d2/f4.h', 'd1/f1.h', 'f1.h']
deps_match(self, deps, map(test.workpath, headers))
@@ -239,6 +242,7 @@ class CScannerTestCase5(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([])
s = SCons.Scanner.C.CScan()
+ path = s.path(env)
n = make_node('f3.cpp')
def my_rexists(s=n):
@@ -247,7 +251,7 @@ class CScannerTestCase5(unittest.TestCase):
setattr(n, 'old_rexists', n.rexists)
setattr(n, 'rexists', my_rexists)
- deps = s.scan(n, env, DummyTarget())
+ deps = s(n, env, path)
# Make sure rexists() got called on the file node being
# scanned, essential for cooperation with BuildDir functionality.
@@ -261,10 +265,11 @@ class CScannerTestCase6(unittest.TestCase):
def runTest(self):
env1 = DummyEnvironment([test.workpath("d1")])
env2 = DummyEnvironment([test.workpath("d1/d2")])
- env3 = DummyEnvironment([test.workpath("d1/../d1")])
s = SCons.Scanner.C.CScan()
- deps1 = s.scan(make_node('f1.cpp'), env1, DummyTarget())
- deps2 = s.scan(make_node('f1.cpp'), env2, DummyTarget())
+ path1 = s.path(env1)
+ path2 = s.path(env2)
+ deps1 = s(make_node('f1.cpp'), env1, path1)
+ deps2 = s(make_node('f1.cpp'), env2, path2)
headers1 = ['d1/f2.h', 'f1.h']
headers2 = ['d1/d2/f2.h', 'f1.h']
deps_match(self, deps1, map(test.workpath, headers1))
@@ -275,11 +280,13 @@ class CScannerTestCase8(unittest.TestCase):
fs = SCons.Node.FS.FS(test.workpath(''))
env = DummyEnvironment(["include"])
s = SCons.Scanner.C.CScan(fs = fs)
- deps1 = s.scan(fs.File('fa.cpp'), env, DummyTarget())
+ path = s.path(env)
+ deps1 = s(fs.File('fa.cpp'), env, path)
fs.chdir(fs.Dir('subdir'))
- target = DummyTarget(fs.getcwd())
+ dir = fs.getcwd()
fs.chdir(fs.Dir('..'))
- deps2 = s.scan(fs.File('#fa.cpp'), env, target)
+ path = s.path(env, dir)
+ deps2 = s(fs.File('#fa.cpp'), env, path)
headers1 = ['include/fa.h', 'include/fb.h']
headers2 = ['subdir/include/fa.h', 'subdir/include/fb.h']
deps_match(self, deps1, headers1)
@@ -297,9 +304,10 @@ class CScannerTestCase9(unittest.TestCase):
SCons.Warnings._warningOut = to
test.write('fa.h','\n')
fs = SCons.Node.FS.FS(test.workpath(''))
- s = SCons.Scanner.C.CScan(fs=fs)
env = DummyEnvironment([])
- deps = s.scan(fs.File('fa.cpp'), env, DummyTarget())
+ s = SCons.Scanner.C.CScan(fs=fs)
+ path = s.path(env)
+ deps = s(fs.File('fa.cpp'), env, path)
# Did we catch the warning associated with not finding fb.h?
assert to.out
@@ -311,10 +319,11 @@ class CScannerTestCase10(unittest.TestCase):
def runTest(self):
fs = SCons.Node.FS.FS(test.workpath(''))
fs.chdir(fs.Dir('include'))
- s = SCons.Scanner.C.CScan(fs=fs)
env = DummyEnvironment([])
+ s = SCons.Scanner.C.CScan(fs=fs)
+ path = s.path(env)
test.write('include/fa.cpp', test.read('fa.cpp'))
- deps = s.scan(fs.File('#include/fa.cpp'), env, DummyTarget())
+ deps = s(fs.File('#include/fa.cpp'), env, path)
deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
test.unlink('include/fa.cpp')
@@ -328,9 +337,10 @@ class CScannerTestCase11(unittest.TestCase):
# This was a bug at one time.
f1=fs.File('include2/jjj.h')
f1.builder=1
- s = SCons.Scanner.C.CScan(fs=fs)
env = DummyEnvironment(['include', 'include2'])
- deps = s.scan(fs.File('src/fff.c'), env, DummyTarget())
+ s = SCons.Scanner.C.CScan(fs=fs)
+ path = s.path(env)
+ deps = s(fs.File('src/fff.c'), env, path)
deps_match(self, deps, [ test.workpath('repository/include/iii.h'), 'include2/jjj.h' ])
os.chdir(test.workpath(''))
@@ -343,13 +353,14 @@ class CScannerTestCase12(unittest.TestCase):
fs.Repository(test.workpath('repository'))
env = DummyEnvironment([])
s = SCons.Scanner.C.CScan(fs = fs)
- deps1 = s.scan(fs.File('build1/aaa.c'), env, DummyTarget())
+ path = s.path(env)
+ deps1 = s(fs.File('build1/aaa.c'), env, path)
deps_match(self, deps1, [ 'build1/bbb.h' ])
- deps2 = s.scan(fs.File('build2/aaa.c'), env, DummyTarget())
+ deps2 = s(fs.File('build2/aaa.c'), env, path)
deps_match(self, deps2, [ 'src/bbb.h' ])
- deps3 = s.scan(fs.File('build1/ccc.c'), env, DummyTarget())
+ deps3 = s(fs.File('build1/ccc.c'), env, path)
deps_match(self, deps3, [ 'build1/ddd.h' ])
- deps4 = s.scan(fs.File('build2/ccc.c'), env, DummyTarget())
+ deps4 = s(fs.File('build2/ccc.c'), env, path)
deps_match(self, deps4, [ test.workpath('repository/src/ddd.h') ])
os.chdir(test.workpath(''))
@@ -360,7 +371,8 @@ class CScannerTestCase13(unittest.TestCase):
return test.workpath("d1")
env = SubstEnvironment(["blah"])
s = SCons.Scanner.C.CScan()
- deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
+ path = s.path(env)
+ deps = s(make_node('f1.cpp'), env, path)
headers = ['d1/f2.h', 'f1.h']
deps_match(self, deps, map(test.workpath, headers))
diff --git a/src/engine/SCons/Scanner/Fortran.py b/src/engine/SCons/Scanner/Fortran.py
index 5d908f7..e23c7a5 100644
--- a/src/engine/SCons/Scanner/Fortran.py
+++ b/src/engine/SCons/Scanner/Fortran.py
@@ -46,71 +46,53 @@ 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"])
+ [".f", ".F", ".for", ".FOR"],
+ path_function = path)
return scanner
-def scan(node, env, target, fs = SCons.Node.FS.default_fs):
+def path(env, dir, fs = SCons.Node.FS.default_fs):
+ try:
+ f77path = env['F77PATH']
+ except KeyError:
+ return ()
+ return tuple(fs.Rsearchall(SCons.Util.mapPaths(f77path, dir, env),
+ clazz = SCons.Node.FS.Dir,
+ must_exist = 0))
+
+def scan(node, env, f77path = (), fs = SCons.Node.FS.default_fs):
"""
scan(node, Environment) -> [node]
the Fortran dependency scanner function
-
- This function is intentionally simple. There are two rules it
- follows:
-
- 1) #include <foo.h> - search for foo.h in F77PATH followed by the
- directory 'filename' is in
- 2) #include \"foo.h\" - search for foo.h in the directory 'filename' is
- in followed by F77PATH
-
- These rules approximate the behaviour of most C/C++ compilers.
-
- This scanner also ignores #ifdef and other preprocessor conditionals, so
- it may find more depencies than there really are, but it never misses
- dependencies.
"""
- # This function caches various information in node and target:
- # target.f77path - env['F77PATH'] converted to nodes
- # node.found_includes - include files found by previous call to scan,
- # keyed on f77path
+ node = node.rfile()
+
+ # This function caches the following information:
# node.includes - the result of include_re.findall()
- if not hasattr(target, 'f77path'):
- try:
- target.f77path = tuple(fs.Rsearchall(SCons.Util.mapPaths(env['F77PATH'], target.cwd, env), clazz=SCons.Node.FS.Dir, must_exist=0))
- except KeyError:
- target.f77path = ()
+ if not node.exists():
+ return []
- f77path = target.f77path
+ # cache the includes list in node so we only scan it once:
+ if node.includes != None:
+ includes = node.includes
+ else:
+ includes = include_re.findall(node.get_contents())
+ node.includes = includes
+ source_dir = node.get_dir()
+
nodes = []
-
- node = node.rfile()
- try:
- nodes = node.found_includes[f77path]
- except KeyError:
- if node.rexists():
-
- # cache the includes list in node so we only scan it once:
- if node.includes != None:
- includes = node.includes
- else:
- includes = include_re.findall(node.get_contents())
- node.includes = includes
-
- source_dir = node.get_dir()
-
- for include in includes:
- n = SCons.Node.FS.find_file(include,
- (source_dir,) + f77path,
- fs.File)
- if not n is None:
- nodes.append(n)
- else:
- SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
- "No dependency generated for file: %s (included from: %s) -- file not found" % (include, node))
- node.found_includes[f77path] = nodes
+ for include in includes:
+ n = SCons.Node.FS.find_file(include,
+ (source_dir,) + f77path,
+ fs.File)
+ if not n is None:
+ nodes.append(n)
+ else:
+ SCons.Warnings.warn(SCons.Warnings.DependencyWarning,
+ "No dependency generated for file: %s (included from: %s) -- file not found" % (include, node))
# Schwartzian transform from the Python FAQ Wizard
def st(List, Metric):
diff --git a/src/engine/SCons/Scanner/FortranTests.py b/src/engine/SCons/Scanner/FortranTests.py
index e2cfcd6..f721d89 100644
--- a/src/engine/SCons/Scanner/FortranTests.py
+++ b/src/engine/SCons/Scanner/FortranTests.py
@@ -127,10 +127,6 @@ test.write([ 'repository', 'src', 'ddd.f'], "\n")
# define some helpers:
-class DummyTarget:
- def __init__(self, cwd=None):
- self.cwd = cwd
-
class DummyEnvironment:
def __init__(self, listCppPath):
self.path = listCppPath
@@ -143,6 +139,9 @@ class DummyEnvironment:
else:
raise KeyError, "Dummy environment only has F77PATH attribute."
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
def __getitem__(self,key):
return self.Dictionary()[key]
@@ -171,12 +170,13 @@ class FortranScannerTestCase1(unittest.TestCase):
test.write('f2.f', " INCLUDE 'fi.f'\n")
env = DummyEnvironment([])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+ 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']
deps_match(self, deps, map(test.workpath, headers))
- test.unlink('f1.f')
- test.unlink('f2.f')
+ test.unlink('f1.f')
+ test.unlink('f2.f')
class FortranScannerTestCase2(unittest.TestCase):
def runTest(self):
@@ -184,19 +184,21 @@ class FortranScannerTestCase2(unittest.TestCase):
test.write('f2.f', " INCLUDE 'fi.f'\n")
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+ 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']
deps_match(self, deps, map(test.workpath, headers))
- test.unlink('f1.f')
- test.unlink('f2.f')
+ test.unlink('f1.f')
+ test.unlink('f2.f')
class FortranScannerTestCase3(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff1.f', fs), env, path)
headers = ['d1/f1.f', 'd1/f2.f']
deps_match(self, deps, map(test.workpath, headers))
@@ -205,8 +207,9 @@ class FortranScannerTestCase4(unittest.TestCase):
test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n")
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff1.f', fs), env, path)
headers = ['d1/f1.f', 'd1/f2.f']
deps_match(self, deps, map(test.workpath, headers))
test.write(['d1', 'f2.f'], "\n")
@@ -215,8 +218,9 @@ class FortranScannerTestCase5(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff2.f', fs), env, path)
headers = ['d1/d2/f2.f', 'd1/f2.f', 'd1/f2.f']
deps_match(self, deps, map(test.workpath, headers))
@@ -225,8 +229,9 @@ class FortranScannerTestCase6(unittest.TestCase):
test.write('f2.f', "\n")
env = DummyEnvironment([test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff2.f', fs), env, path)
headers = ['d1/d2/f2.f', 'd1/f2.f', 'f2.f']
deps_match(self, deps, map(test.workpath, headers))
test.unlink('f2.f')
@@ -235,8 +240,9 @@ class FortranScannerTestCase7(unittest.TestCase):
def runTest(self):
env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff2.f', fs), env, path)
headers = ['d1/d2/f2.f', 'd1/d2/f2.f', 'd1/f2.f']
deps_match(self, deps, map(test.workpath, headers))
@@ -245,8 +251,9 @@ class FortranScannerTestCase8(unittest.TestCase):
test.write('f2.f', "\n")
env = DummyEnvironment([test.workpath("d1/d2"), test.workpath("d1")])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff2.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff2.f', fs), env, path)
headers = ['d1/d2/f2.f', 'd1/f2.f', 'f2.f']
deps_match(self, deps, map(test.workpath, headers))
test.unlink('f2.f')
@@ -256,6 +263,7 @@ class FortranScannerTestCase9(unittest.TestCase):
test.write('f3.f', "\n")
env = DummyEnvironment([])
s = SCons.Scanner.Fortran.FortranScan()
+ path = s.path(env)
n = make_node('fff3.f')
def my_rexists(s=n):
@@ -264,7 +272,7 @@ class FortranScannerTestCase9(unittest.TestCase):
setattr(n, 'old_rexists', n.rexists)
setattr(n, 'rexists', my_rexists)
- deps = s.scan(n, env, DummyTarget())
+ deps = s(n, env, path)
# Make sure rexists() got called on the file node being
# scanned, essential for cooperation with BuildDir functionality.
@@ -279,11 +287,13 @@ class FortranScannerTestCase10(unittest.TestCase):
fs = SCons.Node.FS.FS(test.workpath(''))
env = DummyEnvironment(["include"])
s = SCons.Scanner.Fortran.FortranScan(fs = fs)
- deps1 = s.scan(fs.File('fff4.f'), env, DummyTarget())
+ path = s.path(env)
+ deps1 = s(fs.File('fff4.f'), env, path)
fs.chdir(fs.Dir('subdir'))
- target = DummyTarget(fs.getcwd())
+ dir = fs.getcwd()
fs.chdir(fs.Dir('..'))
- deps2 = s.scan(fs.File('#fff4.f'), env, target)
+ path = s.path(env, dir)
+ deps2 = s(fs.File('#fff4.f'), env, path)
headers1 = ['include/f4.f']
headers2 = ['subdir/include/f4.f']
deps_match(self, deps1, headers1)
@@ -301,9 +311,10 @@ class FortranScannerTestCase11(unittest.TestCase):
SCons.Warnings._warningOut = to
test.write('f4.f'," INCLUDE 'not_there.f'\n")
fs = SCons.Node.FS.FS(test.workpath(''))
- s = SCons.Scanner.Fortran.FortranScan(fs=fs)
env = DummyEnvironment([])
- deps = s.scan(fs.File('fff4.f'), env, DummyTarget())
+ s = SCons.Scanner.Fortran.FortranScan(fs=fs)
+ path = s.path(env)
+ deps = s(fs.File('fff4.f'), env, path)
# Did we catch the warning from not finding not_there.f?
assert to.out
@@ -315,10 +326,11 @@ class FortranScannerTestCase12(unittest.TestCase):
def runTest(self):
fs = SCons.Node.FS.FS(test.workpath(''))
fs.chdir(fs.Dir('include'))
- s = SCons.Scanner.Fortran.FortranScan(fs=fs)
env = DummyEnvironment([])
+ s = SCons.Scanner.Fortran.FortranScan(fs=fs)
+ path = s.path(env)
test.write('include/fff4.f', test.read('fff4.f'))
- deps = s.scan(fs.File('#include/fff4.f'), env, DummyTarget())
+ deps = s(fs.File('#include/fff4.f'), env, path)
deps_match(self, deps, ['include/f4.f'])
test.unlink('include/fff4.f')
@@ -332,9 +344,10 @@ class FortranScannerTestCase13(unittest.TestCase):
# This was a bug at one time.
f1=fs.File('include2/jjj.f')
f1.builder=1
- s = SCons.Scanner.Fortran.FortranScan(fs=fs)
env = DummyEnvironment(['include','include2'])
- deps = s.scan(fs.File('src/fff.f'), env, DummyTarget())
+ s = SCons.Scanner.Fortran.FortranScan(fs=fs)
+ path = s.path(env)
+ deps = s(fs.File('src/fff.f'), env, path)
deps_match(self, deps, [test.workpath('repository/include/iii.f'), 'include2/jjj.f'])
os.chdir(test.workpath(''))
@@ -347,13 +360,14 @@ class FortranScannerTestCase14(unittest.TestCase):
fs.Repository(test.workpath('repository'))
env = DummyEnvironment([])
s = SCons.Scanner.Fortran.FortranScan(fs = fs)
- deps1 = s.scan(fs.File('build1/aaa.f'), env, DummyTarget())
+ path = s.path(env)
+ deps1 = s(fs.File('build1/aaa.f'), env, path)
deps_match(self, deps1, [ 'build1/bbb.f' ])
- deps2 = s.scan(fs.File('build2/aaa.f'), env, DummyTarget())
+ deps2 = s(fs.File('build2/aaa.f'), env, path)
deps_match(self, deps2, [ 'src/bbb.f' ])
- deps3 = s.scan(fs.File('build1/ccc.f'), env, DummyTarget())
+ deps3 = s(fs.File('build1/ccc.f'), env, path)
deps_match(self, deps3, [ 'build1/ddd.f' ])
- deps4 = s.scan(fs.File('build2/ccc.f'), env, DummyTarget())
+ deps4 = s(fs.File('build2/ccc.f'), env, path)
deps_match(self, deps4, [ test.workpath('repository/src/ddd.f') ])
os.chdir(test.workpath(''))
@@ -365,8 +379,9 @@ class FortranScannerTestCase15(unittest.TestCase):
test.write(['d1', 'f2.f'], " INCLUDE 'fi.f'\n")
env = SubstEnvironment(["junk"])
s = SCons.Scanner.Fortran.FortranScan()
- fs = SCons.Node.FS.FS(original)
- deps = s.scan(make_node('fff1.f', fs), env, DummyTarget())
+ path = s.path(env)
+ fs = SCons.Node.FS.FS(original)
+ deps = s(make_node('fff1.f', fs), env, path)
headers = ['d1/f1.f', 'd1/f2.f']
deps_match(self, deps, map(test.workpath, headers))
test.write(['d1', 'f2.f'], "\n")
diff --git a/src/engine/SCons/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py
index 7cfd9f4..081fd5c 100644
--- a/src/engine/SCons/Scanner/Prog.py
+++ b/src/engine/SCons/Scanner/Prog.py
@@ -34,10 +34,19 @@ import SCons.Util
def ProgScan(fs = SCons.Node.FS.default_fs):
"""Return a prototype Scanner instance for scanning executable
files for static-lib dependencies"""
- ps = SCons.Scanner.Base(scan, "ProgScan", fs)
+ ps = SCons.Scanner.Base(scan, "ProgScan", fs, path_function = path)
return ps
-def scan(node, env, target, fs):
+def path(env, dir, fs = SCons.Node.FS.default_fs):
+ try:
+ libpath = env['LIBPATH']
+ except KeyError:
+ return ()
+ return tuple(fs.Rsearchall(SCons.Util.mapPaths(libpath, dir, env),
+ clazz = SCons.Node.FS.Dir,
+ must_exist = 0))
+
+def scan(node, env, libpath = (), fs = SCons.Node.FS.default_fs):
"""
This scanner scans program files for static-library
dependencies. It will search the LIBPATH environment variable
@@ -45,17 +54,6 @@ def scan(node, env, target, fs):
files it finds as dependencies.
"""
- # This function caches information in target:
- # target.libpath - env['LIBPATH'] converted to nodes
-
- if not hasattr(target, 'libpath'):
- try:
- target.libpath = tuple(fs.Rsearchall(SCons.Util.mapPaths(env['LIBPATH'], target.cwd, env), clazz=SCons.Node.FS.Dir, must_exist=0))
- except KeyError:
- target.libpath = ()
-
- libpath = target.libpath
-
try:
libs = env.Dictionary('LIBS')
except KeyError:
diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py
index 1302e46..0d0e5ad 100644
--- a/src/engine/SCons/Scanner/ProgTests.py
+++ b/src/engine/SCons/Scanner/ProgTests.py
@@ -43,10 +43,6 @@ for h in libs:
# define some helpers:
-class DummyTarget:
- def __init__(self, cwd=None):
- self.cwd = cwd
-
class DummyEnvironment:
def __init__(self, **kw):
self._dict = kw
@@ -59,6 +55,10 @@ class DummyEnvironment:
return self._dict[args[0]]
else:
return map(lambda x, s=self: s._dict[x], args)
+
+ def has_key(self, key):
+ return self.Dictionary().has_key(key)
+
def __getitem__(self,key):
return self.Dictionary()[key]
@@ -86,7 +86,8 @@ class ProgScanTestCase1(unittest.TestCase):
env = DummyEnvironment(LIBPATH=[ test.workpath("") ],
LIBS=[ 'l1', 'l2', 'l3' ])
s = SCons.Scanner.Prog.ProgScan()
- deps = s.scan('dummy', env, DummyTarget())
+ path = s.path(env)
+ deps = s('dummy', env, path)
assert deps_match(deps, ['l1.lib']), map(str, deps)
class ProgScanTestCase2(unittest.TestCase):
@@ -95,7 +96,8 @@ class ProgScanTestCase2(unittest.TestCase):
["", "d1", "d1/d2" ]),
LIBS=[ 'l1', 'l2', 'l3' ])
s = SCons.Scanner.Prog.ProgScan()
- deps = s.scan('dummy', env, DummyTarget())
+ path = s.path(env)
+ deps = s('dummy', env, path)
assert deps_match(deps, ['l1.lib', 'd1/l2.lib', 'd1/d2/l3.lib' ]), map(str, deps)
class ProgScanTestCase3(unittest.TestCase):
@@ -104,7 +106,8 @@ class ProgScanTestCase3(unittest.TestCase):
test.workpath("d1")],
LIBS=string.split('l2 l3'))
s = SCons.Scanner.Prog.ProgScan()
- deps = s.scan('dummy', env, DummyTarget())
+ path = s.path(env)
+ deps = s('dummy', env, path)
assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps)
class ProgScanTestCase5(unittest.TestCase):
@@ -118,7 +121,8 @@ class ProgScanTestCase5(unittest.TestCase):
env = SubstEnvironment(LIBPATH=[ "blah" ],
LIBS=string.split('l2 l3'))
s = SCons.Scanner.Prog.ProgScan()
- deps = s.scan('dummy', env, DummyTarget())
+ path = s.path(env)
+ deps = s('dummy', env, path)
assert deps_match(deps, [ 'd1/l2.lib' ]), map(str, deps)
def suite():
@@ -135,7 +139,8 @@ def suite():
test.workpath("d1")],
LIBS=string.split(u'l2 l3'))
s = SCons.Scanner.Prog.ProgScan()
- deps = s.scan('dummy', env, DummyTarget())
+ path = s.path(env)
+ deps = s('dummy', env, path)
assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps)
suite.addTest(ProgScanTestCase4())
\n"""
diff --git a/src/engine/SCons/Scanner/ScannerTests.py b/src/engine/SCons/Scanner/ScannerTests.py
index 2d0e47a..7280c2f 100644
--- a/src/engine/SCons/Scanner/ScannerTests.py
+++ b/src/engine/SCons/Scanner/ScannerTests.py
@@ -27,10 +27,6 @@ import unittest
import SCons.Scanner
import sys
-class DummyTarget:
- cwd = None
-
-
class ScannerTestBase:
def func(self, filename, env, target, *args):
@@ -46,7 +42,8 @@ class ScannerTestBase:
def test(self, scanner, env, filename, deps, *args):
self.deps = deps
- scanned = scanner.scan(filename, env, DummyTarget())
+ path = scanner.path(env)
+ scanned = scanner(filename, env, path)
scanned_strs = map(lambda x: str(x), scanned)
self.failUnless(self.filename == filename, "the filename was passed incorrectly")
@@ -121,7 +118,7 @@ class ScannerHashTestCase(ScannerTestBase, unittest.TestCase):
s = SCons.Scanner.Base(self.func, "Hash")
dict = {}
dict[s] = 777
- self.failUnless(hash(dict.keys()[0]) == hash(None),
+ self.failUnless(hash(dict.keys()[0]) == hash(repr(s)),
"did not hash Scanner base class as expected")
class ScannerCheckTestCase(unittest.TestCase):
@@ -134,8 +131,10 @@ class ScannerCheckTestCase(unittest.TestCase):
def check(node, s=self):
s.checked[node] = 1
return 1
+ env = DummyEnvironment()
s = SCons.Scanner.Base(my_scan, "Check", scan_check = check)
- scanned = s.scan('x', DummyEnvironment(), DummyTarget())
+ path = s.path(env)
+ scanned = s('x', env, path)
self.failUnless(self.checked['x'] == 1,
"did not call check function")
diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py
index aef5d72..c27c762 100644
--- a/src/engine/SCons/Scanner/__init__.py
+++ b/src/engine/SCons/Scanner/__init__.py
@@ -52,23 +52,42 @@ class Base:
name = "NONE",
argument = _null,
skeys = [],
+ path_function = None,
+ node_class = SCons.Node.FS.Entry,
node_factory = SCons.Node.FS.default_fs.File,
scan_check = None):
"""
Construct a new scanner object given a scanner function.
- 'function' - a scanner function taking two or three arguments and
- returning a list of strings.
+ 'function' - a scanner function taking two or three
+ arguments and returning a list of strings.
'name' - a name for identifying this scanner object.
- 'argument' - an optional argument that will be passed to the
- scanner function if it is given.
+ 'argument' - an optional argument that, if specified, will be
+ passed to both the scanner function and the path_function.
- 'skeys; - an optional list argument that can be used to determine
+ 'skeys' - an optional list argument that can be used to determine
which scanner should be used for a given Node. In the case of File
nodes, for example, the 'skeys' would be file suffixes.
+ 'path_function' - a function that takes one to three arguments
+ (a construction environment, optional directory, and optional
+ argument for this instance) and returns a tuple of the
+ directories that can be searched for implicit dependency files.
+
+ 'node_class' - the class of Nodes which this scan will return.
+ If node_class is None, then this scanner will not enforce any
+ Node conversion and will return the raw results from the
+ underlying scanner function.
+
+ 'node_factory' - the factory function to be called to translate
+ the raw results returned by the scanner function into the
+ expected node_class objects.
+
+ 'scan_check' - a function to be called to first check whether
+ this node really needs to be scanned.
+
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
@@ -91,13 +110,23 @@ class Base:
# would need to be changed is the documentation.
self.function = function
+ self.path_function = path_function
self.name = name
self.argument = argument
self.skeys = skeys
+ self.node_class = node_class
self.node_factory = node_factory
self.scan_check = scan_check
- def scan(self, node, env, target):
+ def path(self, env, dir = None):
+ if not self.path_function:
+ return ()
+ if not self.argument is _null:
+ return self.path_function(env, dir, self.argument)
+ else:
+ return self.path_function(env, dir)
+
+ def __call__(self, node, env, path = ()):
"""
This method scans a single object. 'node' is the node
that will be passed to the scanner function, and 'env' is the
@@ -108,15 +137,15 @@ class Base:
return []
if not self.argument is _null:
- list = self.function(node, env, target, self.argument)
+ list = self.function(node, env, path, self.argument)
else:
- list = self.function(node, env, target)
+ list = self.function(node, env, path)
kw = {}
if hasattr(node, 'dir'):
kw['directory'] = node.dir
nodes = []
for l in list:
- if not isinstance(l, SCons.Node.FS.Entry):
+ if self.node_class and not isinstance(l, self.node_class):
l = apply(self.node_factory, (l,), kw)
nodes.append(l)
return nodes
@@ -125,7 +154,7 @@ class Base:
return cmp(self.__dict__, other.__dict__)
def __hash__(self):
- return hash(None)
+ return hash(repr(self))
def add_skey(self, skey):
"""Add a skey to the list of skeys"""
@@ -149,7 +178,7 @@ class Recursive(RExists):
list of all dependencies.
"""
- def scan(self, node, env, target):
+ 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
@@ -164,7 +193,7 @@ class Recursive(RExists):
while nodes:
n = nodes.pop(0)
d = filter(lambda x, seen=seen: not seen.has_key(x),
- Base.scan(self, n, env, target))
+ Base.__call__(self, n, env, path))
if d:
deps.extend(d)
nodes.extend(d)