Commits

Anonymous committed a9338f7

Implement CPPPATH and scanning during builds.

Comments (0)

Files changed (10)

src/engine/SCons/Builder.py

                                            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

src/engine/SCons/BuilderTests.py

 
 __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
 
 	        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)
 	assert n1.env == env
 	assert n1.builder == builder
 	assert n1.sources == [n2]
+        assert n2.env == env
 
     def test_action(self):
 	"""Test Builder creation
         contents = b1.get_contents()
         assert contents == "foo", contents
 
-        def func():
-            pass
+        b2 = SCons.Builder.Builder(action = Func)
+        contents = b2.get_contents()
+        assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
 
-        b2 = SCons.Builder.Builder(action = func)
-        contents = b2.get_contents()
-        assert contents == "\177\340\0\177\341\0d\0\0S", 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

src/engine/SCons/Defaults.py

 
 import os
 import SCons.Builder
-
+import SCons.Scanner.C
 
 
 Object = SCons.Builder.Builder(name = 'Object',
                                 src_suffix = '$OBJSUFFIX',
                                 src_builder = Object)
 
+CScan = SCons.Scanner.C.CScan()
 
 
 if os.name == 'posix':
         'ARFLAGS'    : 'r',
         'ARCOM'      : '$AR $ARFLAGS $TARGET $SOURCES\nranlib $TARGET',
         'BUILDERS'   : [Object, Program, Library],
+        'SCANNERS'   : [CScan],
         'OBJPREFIX'  : '',
         'OBJSUFFIX'  : '.o',
         'PROGPREFIX' : '',
         'ARFLAGS'    : '/nologo',
         'ARCOM'      : '$AR $ARFLAGS /out:$TARGET $SOURCES',
         'BUILDERS'   : [Object, Program, Library],
+        'SCANNERS'   : [CScan],
         'OBJPREFIX'  : '',
         'OBJSUFFIX'  : '.obj',
         'PROGPREFIX' : '',

src/engine/SCons/Environment.py

 import types
 import SCons.Util
 import SCons.Builder
-
+from SCons.Errors import UserError
 
 def Command():
     pass	# XXX
 	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:
 	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)
 	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

src/engine/SCons/EnvironmentTests.py

 
 
 
+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):
 	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
 
     def test_InstallAs(self):
 	pass	# XXX
 
-    def test_Scanners(self):
-	pass	# XXX
-
     def test_Update(self):
 	"""Test updating an Environment with new construction variables
 

src/engine/SCons/Node/FS.py

 
 import os
 import os.path
+import types
 import SCons.Node
 from UserDict import UserDict
 import sys
         .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()

src/engine/SCons/Node/FSTests.py

 import string
 import sys
 import unittest
-
 import SCons.Node.FS
 
 
-
 built_it = None
 
 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):
         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

src/engine/SCons/Node/__init__.py

 	self.depends = []
         self.parents = []
 	self.builder = None
+        self.scanner = None
+        self.scanned = 0
 	self.env = None
         self.state = None
         self.bsig = None
                 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):
         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):

src/engine/SCons/Scanner/C.py

 
 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):
     """
 
     return fullnames
 
-def scan(filename, env):
+def scan(filename, env, node_factory):
     """
     scan(str, Environment) -> [str]
 
     deps = (find_files(angle_includes, paths + [source_dir])
             + find_files(quote_includes, [source_dir] + paths))
 
+    deps = map(node_factory, deps)
     return deps
-
-    
-    
-    
-
-    

src/engine/SCons/Scanner/__init__.py

 
 __version__ = "__VERSION__"
 
+
+from SCons.Util import scons_str2nodes
+
+
 class _Null:
     pass
 
 
 class Scanner:
 
-    def __init__(self, function, argument=_null):
+    def __init__(self, function, argument=_null, skeys=[]):
         """
         Construct a new scanner object given a scanner function.
 
         '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:
 
 
         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