From 0f394bfb1e41965678937bfbc3e7cb52651ae731 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 10 Aug 2001 02:30:24 +0000 Subject: add the C scanner --- runtest.sh | 2 +- src/MANIFEST | 1 + src/scons/Scanner/C.py | 87 +++++++++++++++++++++++++++++ src/scons/Scanner/CTests.py | 131 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 src/scons/Scanner/C.py create mode 100644 src/scons/Scanner/CTests.py diff --git a/runtest.sh b/runtest.sh index 34bf004..571a478 100644 --- a/runtest.sh +++ b/runtest.sh @@ -1,3 +1,3 @@ # This script makes it possible to run a test without building first -export PYTHONPATH=`pwd`/src +export PYTHONPATH="`pwd`/src:`pwd`/etc" python $1 diff --git a/src/MANIFEST b/src/MANIFEST index c09fc4a..64585a1 100644 --- a/src/MANIFEST +++ b/src/MANIFEST @@ -7,6 +7,7 @@ scons/Job.py scons/Node/__init__.py scons/Node/FS.py scons/Scanner/__init__.py +scons/Scanner/C.py scons/Sig/__init__.py scons/Sig/MD5.py scons/Sig/TimeStamp.py diff --git a/src/scons/Scanner/C.py b/src/scons/Scanner/C.py new file mode 100644 index 0000000..fcb3a46 --- /dev/null +++ b/src/scons/Scanner/C.py @@ -0,0 +1,87 @@ +"""scons.Scanner.C + +This module implements the depenency scanner for C/C++ code. + +""" + +__revision__ = "Scanner/C.py __REVISION__ __DATE__ __DEVELOPER__" + + +import scons.Scanner +import re +import os.path + +angle_re = re.compile('^[ \t]*#[ \t]*include[ \t]+<([\\w./\\\\]+)>', re.M) +quote_re = re.compile('^[ \t]*#[ \t]*include[ \t]+"([\\w./\\\\]+)"', re.M) + +def CScan(): + "Return a Scanner instance for scanning C/C++ source files" + return scons.Scanner.Scanner(scan) + +def find_files(filenames, paths): + """ + find_files([str], [str]) -> [str] + + filenames - a list of filenames to find + paths - a list of paths to search in + + returns - the fullnames of the files + + Only the first fullname found is returned for each filename, and any + file that aren't found are ignored. + """ + fullnames = [] + for filename in filenames: + for path in paths: + fullname = os.path.join(path, filename) + if os.path.exists(fullname): + fullnames.append(fullname) + break + + return fullnames + +def scan(filename, env): + """ + scan(str, Environment) -> [str] + + the C/C++ dependency scanner function + + This function is intentionally simple. There are two rules it + follows: + + 1) #include - search for foo.h in CPPPATH followed by the + directory 'filename' is in + 2) #include \"foo.h\" - search for foo.h in the directory 'filename' is + in followed by CPPPATH + + 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. + """ + + if hasattr(env, "CPPPATH"): + paths = env.CPPPATH + else: + paths = [] + + file = open(filename) + contents = file.read() + file.close() + + angle_includes = angle_re.findall(contents) + quote_includes = quote_re.findall(contents) + + source_dir = os.path.dirname(filename) + + deps = (find_files(angle_includes, paths + [source_dir]) + + find_files(quote_includes, [source_dir] + paths)) + + return deps + + + + + + diff --git a/src/scons/Scanner/CTests.py b/src/scons/Scanner/CTests.py new file mode 100644 index 0000000..326e9b8 --- /dev/null +++ b/src/scons/Scanner/CTests.py @@ -0,0 +1,131 @@ +__revision__ = "Scanner/CTests.py __REVISION__ __DATE__ __DEVELOPER__" + +from TestCmd import TestCmd +import scons.Scanner.C +import unittest +import sys + +test = TestCmd(workdir = '') + +# create some source files and headers: + +test.write('f1.cpp',""" +#include \"f1.h\" +#include + +int main() +{ + return 0; +} +""") + +test.write('f2.cpp',""" +#include \"d1/f1.h\" +#include +#include \"f1.h\" +#include + +int main() +{ + return 0; +} +""") + +test.write('f3.cpp',""" +#include \t "f1.h" + \t #include "f2.h" +# \t include "f3.h" + +#include \t + \t #include +# \t include + +// #include "never.h" + +const char* x = "#include " + +int main() +{ + return 0; +} +""") + + +# for Emacs -> " + +test.subdir('d1', ['d1', 'd2']) + +headers = ['f1.h','f2.h', 'f3.h', 'never.h', + 'd1/f1.h', 'd1/f2.h', 'd1/f3.h', + 'd1/d2/f1.h', 'd1/d2/f2.h', 'd1/d2/f3.h', 'd1/d2/f4.h'] + +for h in headers: + test.write(h, " ") + +# define some helpers: + +class DummyEnvironment: + pass + +def deps_match(deps, headers): + return deps.sort() == map(test.workpath, headers).sort() + +# define some tests: + +class CScannerTestCase1(unittest.TestCase): + def runTest(self): + env = DummyEnvironment + s = scons.Scanner.C.CScan() + deps = s.scan(test.workpath('f1.cpp'), env) + self.failUnless(deps_match(deps, ['f1.h', 'f2.h'])) + +class CScannerTestCase2(unittest.TestCase): + def runTest(self): + env = DummyEnvironment + env.CPPPATH = [test.workpath("d1")] + s = scons.Scanner.C.CScan() + deps = s.scan(test.workpath('f1.cpp'), env) + headers = ['f1.h', 'd1/f2.h'] + self.failUnless(deps_match(deps, headers)) + +class CScannerTestCase3(unittest.TestCase): + def runTest(self): + env = DummyEnvironment + env.CPPPATH = [test.workpath("d1")] + s = scons.Scanner.C.CScan() + deps = s.scan(test.workpath('f2.cpp'), env) + headers = ['f1.h', 'd1/f2.h', 'd1/d2/f1.h'] + self.failUnless(deps_match(deps, headers)) + + +class CScannerTestCase4(unittest.TestCase): + def runTest(self): + env = DummyEnvironment + env.CPPPATH = [test.workpath("d1"), test.workpath("d1/d2")] + s = scons.Scanner.C.CScan() + deps = s.scan(test.workpath('f2.cpp'), env) + headers = ['f1.h', 'd1/f2.h', 'd1/d2/f1.h', 'd1/d2/f4.h'] + self.failUnless(deps_match(deps, headers)) + +class CScannerTestCase5(unittest.TestCase): + def runTest(self): + env = DummyEnvironment + s = scons.Scanner.C.CScan() + deps = s.scan(test.workpath('f3.cpp'), env) + headers = ['f1.h', 'f2.h', 'f3.h', 'd1/f1.h', 'd1/f2.h', 'd1/f3.h'] + self.failUnless(deps_match(deps, headers)) + +def suite(): + suite = unittest.TestSuite() + suite.addTest(CScannerTestCase1()) + suite.addTest(CScannerTestCase2()) + suite.addTest(CScannerTestCase3()) + suite.addTest(CScannerTestCase4()) + suite.addTest(CScannerTestCase5()) + return suite + +if __name__ == "__main__": + runner = unittest.TextTestRunner() + result = runner.run(suite()) + if not result.wasSuccessful(): + sys.exit(1) -- cgit v0.12