From 605897524db72d49c9c5f577646eb0f05c893934 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Wed, 14 Nov 2001 18:03:57 +0000 Subject: [PATCH] Implement CPPPATH and scanning during builds. git-svn-id: http://scons.tigris.org/svn/scons/trunk@117 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/engine/SCons/Builder.py | 5 +++ src/engine/SCons/BuilderTests.py | 21 ++++++---- src/engine/SCons/Defaults.py | 5 ++- src/engine/SCons/Environment.py | 18 ++++++++- src/engine/SCons/EnvironmentTests.py | 59 ++++++++++++++++++++++++++-- src/engine/SCons/Node/FS.py | 11 ++++++ src/engine/SCons/Node/FSTests.py | 21 ++++++++-- src/engine/SCons/Node/__init__.py | 14 ++++++- src/engine/SCons/Scanner/C.py | 14 +++---- src/engine/SCons/Scanner/__init__.py | 26 ++++++++++-- 10 files changed, 164 insertions(+), 30 deletions(-) diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index 3bb17da3..c9556aaa 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -171,14 +171,19 @@ class BuilderBase: env.subst(self.prefix), env.subst(self.suffix)), self.node_factory) + slist = scons_str2nodes(adjustixes(source, None, env.subst(self.src_suffix)), self.node_factory) + for t in tlist: t.builder_set(self) t.env_set(env) t.add_source(slist) + for s in slist: + s.env_set(env, 1) + if len(tlist) == 1: tlist = tlist[0] return tlist diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 89ba1fc1..5668095e 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -23,6 +23,13 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +# Define a null function for use as a builder action. +# Where this is defined in the file seems to affect its +# byte-code contents, so try to minimize changes by +# defining it here, before we even import anything. +def Func(): + pass + import sys import unittest @@ -73,7 +80,7 @@ class BuilderTestCase(unittest.TestCase): return self.name def builder_set(self, builder): self.builder = builder - def env_set(self, env): + def env_set(self, env, safe=0): self.env = env def add_source(self, source): self.sources.extend(source) @@ -84,6 +91,7 @@ class BuilderTestCase(unittest.TestCase): assert n1.env == env assert n1.builder == builder assert n1.sources == [n2] + assert n2.env == env def test_action(self): """Test Builder creation @@ -221,16 +229,13 @@ class BuilderTestCase(unittest.TestCase): contents = b1.get_contents() assert contents == "foo", contents - def func(): - pass - - b2 = SCons.Builder.Builder(action = func) + b2 = SCons.Builder.Builder(action = Func) contents = b2.get_contents() - assert contents == "\177\340\0\177\341\0d\0\0S", contents + assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents) - b3 = SCons.Builder.Builder(action = ["foo", func, "bar"]) + b3 = SCons.Builder.Builder(action = ["foo", Func, "bar"]) contents = b3.get_contents() - assert contents == "foo\177\340\0\177\341\0d\0\0Sbar", contents + assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents) def test_name(self): """Test Builder creation with a specified name diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 90080262..114216e5 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -35,7 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import SCons.Builder - +import SCons.Scanner.C Object = SCons.Builder.Builder(name = 'Object', @@ -64,6 +64,7 @@ Library = SCons.Builder.Builder(name = 'Library', src_suffix = '$OBJSUFFIX', src_builder = Object) +CScan = SCons.Scanner.C.CScan() if os.name == 'posix': @@ -82,6 +83,7 @@ if os.name == 'posix': 'ARFLAGS' : 'r', 'ARCOM' : '$AR $ARFLAGS $TARGET $SOURCES\nranlib $TARGET', 'BUILDERS' : [Object, Program, Library], + 'SCANNERS' : [CScan], 'OBJPREFIX' : '', 'OBJSUFFIX' : '.o', 'PROGPREFIX' : '', @@ -107,6 +109,7 @@ elif os.name == 'nt': 'ARFLAGS' : '/nologo', 'ARCOM' : '$AR $ARFLAGS /out:$TARGET $SOURCES', 'BUILDERS' : [Object, Program, Library], + 'SCANNERS' : [CScan], 'OBJPREFIX' : '', 'OBJSUFFIX' : '.obj', 'PROGPREFIX' : '', diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index dcac7160..5bf31b40 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -36,7 +36,7 @@ import re import types import SCons.Util import SCons.Builder - +from SCons.Errors import UserError def Command(): pass # XXX @@ -76,6 +76,8 @@ class Environment: self._dict = copy.deepcopy(SCons.Defaults.ConstructionEnvironment) if kw.has_key('BUILDERS') and type(kw['BUILDERS']) != type([]): kw['BUILDERS'] = [kw['BUILDERS']] + if kw.has_key('SCANNERS') and type(kw['SCANNERS']) != type([]): + kw['SCANNERS'] = [kw['SCANNERS']] self._dict.update(copy.deepcopy(kw)) class BuilderWrapper: @@ -103,7 +105,8 @@ class Environment: for b in self._dict['BUILDERS']: setattr(self, b.name, BuilderWrapper(self, b)) - + for s in self._dict['SCANNERS']: + setattr(self, s.name, s) def __cmp__(self, other): return cmp(self._dict, other._dict) @@ -170,3 +173,14 @@ class Environment: trailing characters. """ return SCons.Util.scons_subst(string, self._dict, {}) + + def get_scanner(self, skey): + """Find the appropriate scanner given a key (usually a file suffix). + Does a linear search. Could be sped up by creating a dictionary if + this proves too slow. + """ + if self._dict['SCANNERS']: + for scanner in self._dict['SCANNERS']: + if skey in scanner.skeys: + return scanner + return None diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index b15b1933..aff9b015 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -44,6 +44,24 @@ class Builder: +scanned_it = {} + +class Scanner: + """A dummy Scanner class for testing purposes. "Scanning" + a target is simply setting a value in the dictionary. + """ + def __init__(self, name, skeys=[]): + self.name = name + self.skeys = skeys + + def scan(self, filename): + scanned_it[filename] = 1 + + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + + + class EnvironmentTestCase(unittest.TestCase): def test_Builders(self): @@ -77,6 +95,44 @@ class EnvironmentTestCase(unittest.TestCase): assert built_it['out2'] assert built_it['out3'] + def test_Scanners(self): + """Test Scanner execution through different environments + + One environment is initialized with a single + Scanner object, one with a list of a single Scanner + object, and one with a list of two Scanner objects. + """ + global scanned_it + + s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"]) + s2 = Scanner(name = 'scanner2', skeys = [".m4"]) + + scanned_it = {} + env1 = Environment(SCANNERS = s1) + env1.scanner1.scan(filename = 'out1') + assert scanned_it['out1'] + + scanned_it = {} + env2 = Environment(SCANNERS = [s1]) + env1.scanner1.scan(filename = 'out1') + assert scanned_it['out1'] + + scanned_it = {} + env3 = Environment(SCANNERS = [s1, s2]) + env3.scanner1.scan(filename = 'out1') + env3.scanner2.scan(filename = 'out2') + env3.scanner1.scan(filename = 'out3') + assert scanned_it['out1'] + assert scanned_it['out2'] + assert scanned_it['out3'] + + s = env3.get_scanner(".c") + assert s == s1, s + s = env3.get_scanner(skey=".m4") + assert s == s2, s + s = env3.get_scanner(".cxx") + assert s == None, s + def test_Command(self): pass # XXX @@ -146,9 +202,6 @@ class EnvironmentTestCase(unittest.TestCase): def test_InstallAs(self): pass # XXX - def test_Scanners(self): - pass # XXX - def test_Update(self): """Test updating an Environment with new construction variables diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 860f46b4..7ee769ef 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -35,6 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os import os.path +import types import SCons.Node from UserDict import UserDict import sys @@ -440,5 +441,15 @@ class File(Entry): .sconsign entry.""" return self.dir.sconsign().get(self.name) + def scan(self): + if not self.scanned and self.env: + if self.scanner: + scanner = self.scanner + else: + scanner = self.env.get_scanner(os.path.splitext(self.name)[1]) + if scanner: + self.add_dependency(scanner.scan(self.path_, self.env)) + self.scanned = 1 + default_fs = FS() diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 430d4b43..4305a32a 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -27,11 +27,9 @@ import os import string import sys import unittest - import SCons.Node.FS - built_it = None class Builder: @@ -40,10 +38,17 @@ class Builder: built_it = 1 return 0 +class Scanner: + def scan(self, filename, env): + return [SCons.Node.FS.default_fs.File(filename)] + class Environment: + def __init__(self): + self.scanner = Scanner() def Dictionary(self, *args): pass - + def get_scanner(self, skey): + return self.scanner class FSTestCase(unittest.TestCase): @@ -286,6 +291,16 @@ class FSTestCase(unittest.TestCase): e13 = fs.Entry("subdir/e13") assert e13.path == "subdir/subdir/e13" + # Test scanning + f1.scanner = Scanner() + f1.scan() + assert f1.depends[0].path_ == "d1/f1" + f1.scanner = None + f1.depends = [] + f1.scanned = 0 + f1.scan() + assert f1.depends[0].path_ == "d1/f1" + #XXX test exists() #XXX test current() for directories diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 513b2604..860061c1 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -59,6 +59,8 @@ class Node: self.depends = [] self.parents = [] self.builder = None + self.scanner = None + self.scanned = 0 self.env = None self.state = None self.bsig = None @@ -96,7 +98,15 @@ class Node: return self.node.builder.get_contents(env = env) return Adapter(self) - def env_set(self, env): + def scanner_set(self, scanner): + self.scanner = scanner + + def scan(self): + self.scanned = 1 + + def env_set(self, env, safe=0): + if safe and self.env: + return self.env = env def get_bsig(self): @@ -142,6 +152,8 @@ class Node: if parent not in self.parents: self.parents.append(parent) def children(self): + if not self.scanned: + self.scan() return self.sources + self.depends def get_parents(self): diff --git a/src/engine/SCons/Scanner/C.py b/src/engine/SCons/Scanner/C.py index 29f46dcf..0d74dc53 100644 --- a/src/engine/SCons/Scanner/C.py +++ b/src/engine/SCons/Scanner/C.py @@ -39,7 +39,10 @@ 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) + s = SCons.Scanner.Scanner(scan, SCons.Node.FS.default_fs.File, + [".c", ".C", ".cxx", ".cpp", ".c++"]) + s.name = "CScan" + return s def find_files(filenames, paths): """ @@ -63,7 +66,7 @@ def find_files(filenames, paths): return fullnames -def scan(filename, env): +def scan(filename, env, node_factory): """ scan(str, Environment) -> [str] @@ -101,10 +104,5 @@ def scan(filename, env): deps = (find_files(angle_includes, paths + [source_dir]) + find_files(quote_includes, [source_dir] + paths)) + deps = map(node_factory, deps) return deps - - - - - - diff --git a/src/engine/SCons/Scanner/__init__.py b/src/engine/SCons/Scanner/__init__.py index 683a1460..28fab7ad 100644 --- a/src/engine/SCons/Scanner/__init__.py +++ b/src/engine/SCons/Scanner/__init__.py @@ -31,6 +31,10 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" __version__ = "__VERSION__" + +from SCons.Util import scons_str2nodes + + class _Null: pass @@ -40,7 +44,7 @@ _null = _Null class Scanner: - def __init__(self, function, argument=_null): + def __init__(self, function, argument=_null, skeys=[]): """ Construct a new scanner object given a scanner function. @@ -50,11 +54,15 @@ class Scanner: 'argument' - an optional argument that will be passed to the scanner function if it is given. + '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. + 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 passed into 'argument', and the returned list should contain the - file names of all the direct dependencies of the file. + Nodes for all the direct dependencies of the file. Examples: @@ -73,20 +81,30 @@ class Scanner: self.function = function self.argument = argument + self.name = "NONE" + self.skeys = skeys def scan(self, filename, env): """ This method does the actually scanning. 'filename' is the filename that will be passed to the scanner function, and 'env' is the environment that will be passed to the scanner function. A list of - dependencies will be returned. + dependencies will be returned (i.e. a list of 'Node's). """ if not self.argument is _null: return self.function(filename, env, self.argument) else: return self.function(filename, env) - + def __cmp__(self, other): + return cmp(self.__dict__, other.__dict__) + def __call__(self, sources=None): + slist = scons_str2nodes(source, self.node_factory) + for s in slist: + s.scanner_set(self) + if len(slist) == 1: + slist = slist[0] + return slist -- 2.26.2