Implement CPPPATH and scanning during builds.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 14 Nov 2001 18:03:57 +0000 (18:03 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 14 Nov 2001 18:03:57 +0000 (18:03 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@117 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Scanner/C.py
src/engine/SCons/Scanner/__init__.py

index 3bb17da3f456de450450da73fb77cf52d824e987..c9556aaa44c36c95527f42402fcddd9f3d935f6f 100644 (file)
@@ -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
index 89ba1fc1fe78ab15c394ca5fbc3e7668c413543d..5668095ed85073b81e32da30e3e54cd16bdd5701 100644 (file)
 
 __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
index 9008026250eb58e41ebf4a7c0001da4e8f4f2455..114216e5b874eafda952db1aa6831ca469bcbb28 100644 (file)
@@ -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' : '',
index dcac71605383bf9c9494fbb374168f3855b98a86..5bf31b40f8a825134b5fce99bf278c401bc981b3 100644 (file)
@@ -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
index b15b19330991cd7c1e10262527d7a8f330f5ffe8..aff9b01531ca11c17e8d47b5c3cb6c42fe3205dc 100644 (file)
@@ -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
 
index 860f46b45531401db08f0abe9006f3359421f9b0..7ee769efa5a1735ab822f31396b302ae57b0e02e 100644 (file)
@@ -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()
index 430d4b43154afcc6383fd341dbf72cb3509c5ff3..4305a32a62176b9e294415c473728fcca3299b9c 100644 (file)
@@ -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
index 513b2604acf5a69a7a733298d144663ce32d2241..860061c1379313c11a7dddec70db2160e35628e8 100644 (file)
@@ -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):
index 29f46dcf6de12a5038817c485126438eb17c7486..0d74dc535121a4cdf52d1bb7368e68f92fc44984 100644 (file)
@@ -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
-
-    
-    
-    
-
-    
index 683a1460528e184ee373bf8c705fb477d696508a..28fab7ad7ccbaddf947d8a37a1a199daae4c2861 100644 (file)
@@ -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