diff options
author | Steven Knight <knight@baldmt.com> | 2001-08-10 02:30:24 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2001-08-10 02:30:24 (GMT) |
commit | 0f394bfb1e41965678937bfbc3e7cb52651ae731 (patch) | |
tree | 928fec9fba39d312fb420c1bfe21326be2e0bad4 | |
parent | 824289d0bc3048b996c815f67e5fd100b529dc94 (diff) | |
download | SCons-0f394bfb1e41965678937bfbc3e7cb52651ae731.zip SCons-0f394bfb1e41965678937bfbc3e7cb52651ae731.tar.gz SCons-0f394bfb1e41965678937bfbc3e7cb52651ae731.tar.bz2 |
add the C scanner
-rw-r--r-- | runtest.sh | 2 | ||||
-rw-r--r-- | src/MANIFEST | 1 | ||||
-rw-r--r-- | src/scons/Scanner/C.py | 87 | ||||
-rw-r--r-- | src/scons/Scanner/CTests.py | 131 |
4 files changed, 220 insertions, 1 deletions
@@ -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 <foo.h> - 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 <f2.h> + +int main() +{ + return 0; +} +""") + +test.write('f2.cpp',""" +#include \"d1/f1.h\" +#include <d2/f1.h> +#include \"f1.h\" +#include <f4.h> + +int main() +{ + return 0; +} +""") + +test.write('f3.cpp',""" +#include \t "f1.h" + \t #include "f2.h" +# \t include "f3.h" + +#include \t <d1/f1.h> + \t #include <d1/f2.h> +# \t include <d1/f3.h> + +// #include "never.h" + +const char* x = "#include <never.h>" + +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) |