Fix spurious rebuilds/reinstalls of header files and circular dependencies with gener...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 19 May 2004 17:49:55 +0000 (17:49 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Wed, 19 May 2004 17:49:55 +0000 (17:49 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@980 fdb21ef1-2011-0410-befe-b5e4ea1792b1

30 files changed:
doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Defaults.py
src/engine/SCons/Environment.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/SConf.py
src/engine/SCons/SConfTests.py
src/engine/SCons/Scanner/CTests.py
src/engine/SCons/Scanner/FortranTests.py
src/engine/SCons/Scanner/IDLTests.py
src/engine/SCons/Scanner/ScannerTests.py
src/engine/SCons/Scanner/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/mingw.py
src/engine/SCons/Tool/msvc.py
src/engine/SCons/Tool/qt.py
test/CPPSUFFIXES.py
test/DSUFFIXES.py
test/FORTRANSUFFIXES.py
test/HeaderInstall.py
test/Scanner.py
test/option--warn.py
test/scan-once.py

index 88e5b47130217e5dc73e64e56b823208a3d84ca7..ea0cf811950ac266b1b454cd5f364c21c66c31c1 100644 (file)
@@ -2197,9 +2197,9 @@ SConscript file.)
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP 
-.RI Builder( action ", [" multi ", " prefix ", " suffix ", " src_suffix ", " src_builder ", " emitter ])
+.RI Builder( action ", [" arguments ])
 .TP 
-.RI env.Builder( action ", [" multi ", " prefix ", " suffix ", " src_suffix ", " src_builder ", " emitter ])
+.RI env.Builder( action ", [" arguments ])
 Creates a Builder object for
 the specified
 .IR action .
@@ -5173,6 +5173,14 @@ path, you must make it absolute yourself.
 
 .IP SCANNERS
 A list of the available implicit dependency scanners.
+New file scanners may be added by
+appending to this list,
+although the more flexible approach
+is to associate scanners
+with a specific Builder.
+See the sections "Builder Objects"
+and "Scanner Objects,"
+below, for more information.
 
 .IP SCCS
 The SCCS executable.
@@ -6257,15 +6265,6 @@ takes three arguments:
 .I env
 - the construction environment.
 
-.IP multi
-Specifies whether this builder is allowed to be called multiple times for
-the same target file(s). The default is 0, which means the builder
-can not be called multiple times for the same target file(s). Calling a
-builder multiple times for the same target simply adds additional source
-files to the target; it is not allowed to change the environment associated
-with the target, specify addition environment overrides, or associate a different
-builder with the target. 
-
 .IP prefix 
 The prefix that will be prepended to the target file name.
 This may be a simple string,  or a callable object that takes
@@ -6308,10 +6307,33 @@ b = Builder("build_it < $SOURCE > $TARGET"
 .IP src_suffix
 The expected source file name suffix.
 
-.IP src_builder
-Specifies a builder to use when a source file name suffix does not match
-any of the suffixes of the builder. Using this argument produces a
-multi-stage builder.
+.IP target_scanner
+A Scanner object that
+will be invoked to find
+implicit dependencies for this target file.
+This keyword argument should be used
+for Scanner objects that find
+implicit dependencies
+based only on the target file
+and the construction environment,
+.I not 
+for implicit
+(See the section "Scanner Objects," below,
+for information about creating Scanner objects.)
+
+.IP source_scanner
+A Scanner object that
+will be invoked to
+find implicit dependences in
+any source files
+used to build this target file.
+This is where you would
+specify a scanner to
+find things like
+.B #include
+lines in source files.
+(See the section "Scanner Objects," below,
+for information about creating Scanner objects.)
 
 .IP emitter
 A function or list of functions to manipulate the target and source
@@ -6376,6 +6398,24 @@ b = Builder("my_build < $TARGET > $SOURCE",
                        '.suf2' : e_suf2})
 .EE
 
+.IP multi
+Specifies whether this builder is allowed to be called multiple times for
+the same target file(s). The default is 0, which means the builder
+can not be called multiple times for the same target file(s). Calling a
+builder multiple times for the same target simply adds additional source
+files to the target; it is not allowed to change the environment associated
+with the target, specify addition environment overrides, or associate a different
+builder with the target. 
+
+.IP env
+A construction environment that can be used
+to fetch source code using this Builder.
+(Note that this environment is
+.I not
+used for normal builds of normal target files,
+which use the environment that was
+used to call the Builder for the target file.)
+
 .IP generator
 A function that returns a list of actions that will be executed to build
 the target(s) from the source(s).
@@ -6406,21 +6446,18 @@ def g(source, target, env, for_signature):
 b = Builder(generator=g)
 .EE
 
+.IP src_builder
+Specifies a builder to use when a source file name suffix does not match
+any of the suffixes of the builder. Using this argument produces a
+multi-stage builder.
+
+.RE
 The 
 .I generator
 and
 .I action
 arguments must not both be used for the same Builder.
 
-.IP env
-A construction environment that can be used
-to fetch source code using this Builder.
-(Note that this environment is
-.I not
-used for normal builds of normal target files,
-which use the environment that was
-used to call the Builder for the target file.)
-
 Any additional keyword arguments supplied
 when a Builder object is created
 (that is, when the Builder() function is called)
index b1d311983fc356b98a177d96722bd414d65bfc7e..7aaabf5d90097e77616b1474e2d90658ffcdb7e4 100644 (file)
@@ -119,6 +119,14 @@ RELEASE 0.96 - XXX
     the parents's implicit dependencies, let returning up the normal
     Taskmaster descent take care of it for us.
 
+  - Add documented support for separate target_scanner and source_scanner
+    arguments to Builder creation, which allows different scanners to
+    be applied to source files
+
+  - Don't re-install or (re-generate) .h files when a subsidiary #included
+    .h file changes.  This eliminates incorrect circular dependencies
+    with .h files generated from other source files.
+
   From Gary Oberbrunner:
 
   - Add a --debug=presub option to print actions prior to substitution.
index 5c75a0f8056cb97baa52bd79884dd568acffc555..4aa518f383c0b6e8e93503ef0638318f0ca14a6f 100644 (file)
@@ -300,7 +300,7 @@ def _init_nodes(builder, env, overrides, tlist, slist):
             elif t.overrides != overrides:
                 raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
 
-            elif builder.scanner and t.target_scanner and builder.scanner != t.target_scanner:
+            elif builder.target_scanner and t.target_scanner and builder.target_scanner != t.target_scanner:
                 raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
 
             if builder.multi:
@@ -338,15 +338,22 @@ def _init_nodes(builder, env, overrides, tlist, slist):
         t.env_set(env)
         t.add_source(slist)
         t.set_executor(executor)
-        if builder.scanner:
-            t.target_scanner = builder.scanner
-        if not t.source_scanner:
-            t.source_scanner = env.get_scanner(t.scanner_key())
-
-    # Last, add scanners from the Environment to the source Nodes.
+        if builder.target_scanner:
+            t.target_scanner = builder.target_scanner
+        if t.source_scanner is None:
+            t.source_scanner = builder.source_scanner
+
+    # Add backup source scanners from the environment to the source
+    # nodes.  This may not be necessary if the node will have a real
+    # source scanner added later (which is why these are the "backup"
+    # source scanners, not the real ones), but because source nodes may
+    # be used multiple times for different targets, it ends up being
+    # more efficient to do this calculation once here, as opposed to
+    # delaying it until later when we potentially have to calculate it
+    # over and over and over.
     for s in slist:
-        if not s.source_scanner:
-            s.source_scanner = env.get_scanner(s.scanner_key())
+        if s.source_scanner is None and s.backup_source_scanner is None:
+            s.backup_source_scanner = env.get_scanner(s.scanner_key())
 
 class EmitterProxy:
     """This is a callable class that can act as a
@@ -389,7 +396,8 @@ class BuilderBase:
                         src_suffix = '',
                         target_factory = SCons.Node.FS.default_fs.File,
                         source_factory = SCons.Node.FS.default_fs.File,
-                        scanner = None,
+                        target_scanner = None,
+                        source_scanner = None,
                         emitter = None,
                         multi = 0,
                         env = None,
@@ -416,7 +424,8 @@ class BuilderBase:
 
         self.target_factory = target_factory
         self.source_factory = source_factory
-        self.scanner = scanner
+        self.target_scanner = target_scanner
+        self.source_scanner = source_scanner
 
         self.emitter = emitter
 
@@ -592,7 +601,8 @@ class ListBuilder(SCons.Util.Proxy):
         if __debug__: logInstanceCreation(self)
         SCons.Util.Proxy.__init__(self, builder)
         self.builder = builder
-        self.scanner = builder.scanner
+        self.target_scanner = builder.target_scanner
+        self.source_scanner = builder.source_scanner
         self.env = env
         self.tlist = tlist
         self.multi = builder.multi
@@ -628,12 +638,13 @@ class MultiStepBuilder(BuilderBase):
                         src_suffix = '',
                         target_factory = SCons.Node.FS.default_fs.File,
                         source_factory = SCons.Node.FS.default_fs.File,
-                        scanner=None,
+                        target_scanner = None,
+                        source_scanner = None,
                         emitter=None):
         if __debug__: logInstanceCreation(self)
         BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
                              target_factory, source_factory,
-                             scanner, emitter)
+                             target_scanner, source_scanner, emitter)
         if not SCons.Util.is_List(src_builder):
             src_builder = [ src_builder ]
         self.src_builder = src_builder
index e33d95c04c395f1a470a2e5e4ce4930c2c34677c..e592250b4cf1187d23a03e544c5c09b67e0629d9 100644 (file)
@@ -133,6 +133,7 @@ class MyNode_without_target_from_source:
         self.builder = None
         self.side_effect = 0
         self.source_scanner = None
+        self.backup_source_scanner = None
     def __str__(self):
         return self.name
     def builder_set(self, builder):
@@ -753,25 +754,30 @@ class BuilderTestCase(unittest.TestCase):
         match = str(e) == "While building `['t8']': Don't know how to build a file with suffix `.unknown'."
         assert match, e
 
-    def test_build_scanner(self):
-        """Testing ability to set a target scanner through a builder."""
+    def test_target_scanner(self):
+        """Testing ability to set target and source scanners through a builder."""
         global instanced
         class TestScanner:
             pass
-        scn = TestScanner()
+        tscan = TestScanner()
+        sscan = TestScanner()
         env = Environment()
-        builder = SCons.Builder.Builder(scanner=scn)
+        builder = SCons.Builder.Builder(target_scanner=tscan,
+                                        source_scanner=sscan)
         tgt = builder(env, target='foo2', source='bar')
-        assert tgt.target_scanner == scn, tgt.target_scanner
+        assert tgt.target_scanner == tscan, tgt.target_scanner
+        assert tgt.source_scanner == sscan, tgt.source_scanner
 
         builder1 = SCons.Builder.Builder(action='foo',
                                          src_suffix='.bar',
                                          suffix='.foo')
         builder2 = SCons.Builder.Builder(action='foo',
                                          src_builder = builder1,
-                                         scanner = scn)
+                                         target_scanner = tscan,
+                                         source_scanner = tscan)
         tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')
-        assert tgt.target_scanner == scn, tgt.target_scanner
+        assert tgt.target_scanner == tscan, tgt.target_scanner
+        assert tgt.source_scanner == tscan, tgt.source_scanner
 
     def test_src_scanner(slf):
         """Testing ability to set a source file scanner through a builder."""
@@ -784,12 +790,14 @@ class BuilderTestCase(unittest.TestCase):
         scanner = TestScanner()
         builder = SCons.Builder.Builder(action='action')
 
-        # With no scanner specified, source_scanner is None.
+        # With no scanner specified, source_scanner and
+        # backup_source_scanner are None.
         env1 = Environment()
         tgt = builder(env1, target='foo1.x', source='bar.y')
         src = tgt.sources[0]
         assert tgt.target_scanner != scanner, tgt.target_scanner
         assert src.source_scanner is None, src.source_scanner
+        assert src.backup_source_scanner is None, src.backup_source_scanner
 
         # Later use of the same source file with an environment that
         # has a scanner must still set the scanner.
@@ -798,7 +806,8 @@ class BuilderTestCase(unittest.TestCase):
         tgt = builder(env2, target='foo2.x', source='bar.y')
         src = tgt.sources[0]
         assert tgt.target_scanner != scanner, tgt.target_scanner
-        assert src.source_scanner == scanner, src.source_scanner
+        assert src.source_scanner is None, src.source_scanner
+        assert src.backup_source_scanner == scanner, src.backup_source_scanner
 
     def test_Builder_Args(self):
         """Testing passing extra args to a builder."""
index 7a47b21521bbde8d815fabe4ded698bc69f1bf7e..4c42f2811aeed00746506110f846becba6e1d5c6 100644 (file)
@@ -96,6 +96,8 @@ def SharedFlagChecker(source, target, env):
 SharedCheck = SCons.Action.Action(SharedFlagChecker, None)
 
 # Scanners and suffixes for common languages.
+ObjSourceScan = SCons.Scanner.Scanner({})
+
 CScan = SCons.Scanner.C.CScan()
 
 CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
@@ -103,14 +105,23 @@ CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
              ".F", ".fpp", ".FPP",
              ".S", ".spp", ".SPP"]
 
+for suffix in CSuffixes:
+    ObjSourceScan.add_scanner(suffix, CScan)
+
 DScan = SCons.Scanner.D.DScan()
 
 DSuffixes = ['.d']
 
+for suffix in DSuffixes:
+    ObjSourceScan.add_scanner(suffix, DScan)
+
 FortranScan = SCons.Scanner.Fortran.FortranScan()
 
 FortranSuffixes = [".f", ".F", ".for", ".FOR"]
 
+for suffix in FortranSuffixes:
+    ObjSourceScan.add_scanner(suffix, FortranScan)
+
 IDLSuffixes = [".idl", ".IDL"]
 
 # Actions for common languages.
@@ -330,7 +341,7 @@ class NullCmdGenerator:
 
 ConstructionEnvironment = {
     'BUILDERS'   : {},
-    'SCANNERS'   : [CScan, FortranScan, DScan],
+    'SCANNERS'   : [],
     'CPPSUFFIXES': CSuffixes,
     'DSUFFIXES'  : DSuffixes,
     'FORTRANSUFFIXES': FortranSuffixes,
index b92064388dd9600d44e8b5e8defbd3719e193ad0..d43a99f7109a5f324cd3e536aaf905a5b31de265 100644 (file)
@@ -395,6 +395,7 @@ class Base:
             try:
                 scanners = self._dict['SCANNERS']
             except KeyError:
+                self.scanner_map = {}
                 return None
             else:
                 self.scanner_map = sm = {}
@@ -402,6 +403,8 @@ class Base:
                 # claim they can scan the same suffix, earlier scanners
                 # in the list will overwrite later scanners, so that
                 # the result looks like a "first match" to the user.
+                if not SCons.Util.is_List(scanners):
+                    scanners = [scanners]
                 scanners.reverse()
                 for scanner in scanners:
                     for k in scanner.get_skeys(self):
@@ -1159,7 +1162,7 @@ class Base:
                 arg = self.subst(arg)
             nargs.append(arg)
         nkw = self.subst_kw(kw)
-        return apply(SCons.Scanner.Base, nargs, nkw)
+        return apply(SCons.Scanner.Scanner, nargs, nkw)
 
     def SConsignFile(self, name=".sconsign", dbm_module=None):
         name = self.subst(name)
index 15434cbf7e9c974d23a79aa0b7d3b84fedab2560..0d158a1825e89fa14285e78d3bd95e218064571e 100644 (file)
@@ -488,12 +488,6 @@ class Base(SCons.Node.Node):
             self._rexists = self.rfile().exists()
             return self._rexists
 
-    def get_parents(self):
-        parents = SCons.Node.Node.get_parents(self)
-        if self.dir and not isinstance(self.dir, ParentOfRoot):
-            parents.append(self.dir)
-        return parents
-
     def is_under(self, dir):
         if self is dir:
             return 1
@@ -1117,8 +1111,7 @@ class Dir(Base):
         self.abspath_ = self.abspath + os.sep
         self.repositories = []
         self.srcdir = None
-        self.source_scanner = None
-        
+
         self.entries = {}
         self.entries['.'] = self
         self.entries['..'] = self.dir
index fb2e0fa782f99d7e15523ef1f141acbd917a4c83..f69dd4e110e8529a128bf66a101bfa6b243aab87 100644 (file)
@@ -60,6 +60,8 @@ class Scanner:
         return [self.node]
     def __hash__(self):
         return self.hash
+    def select(self, node):
+        return self
 
 class Environment:
     def __init__(self):
@@ -1034,12 +1036,6 @@ class FSTestCase(unittest.TestCase):
         skey = fs.Dir('ddd.x').scanner_key()
         assert skey is None, skey
 
-        d1 = fs.Dir('dir')
-        f1 = fs.File('dir/file')
-        assert f1.dir == d1, f1.dir
-        parents = f1.get_parents()
-        assert parents == [ d1 ], parents
-
         test.write("i_am_not_a_directory", "\n")
         try:
             exc_caught = 0
index 535c84e0a2c0cbc2ee2a879eb0133824b892acea..e36d6ce8c09bcb9218e4aa21f413fa8697c5dced 100644 (file)
@@ -138,6 +138,8 @@ class Scanner:
     def __call__(self, node):
         self.called = 1
         return node.found_includes
+    def select(self, node):
+        return self
 
 class MyNode(SCons.Node.Node):
     """The base Node class contains a number of do-nothing methods that
@@ -543,12 +545,6 @@ class NodeTestCase(unittest.TestCase):
         node.add_dependency([three, four, one])
         assert node.depends == [zero, one, two, three, four]
 
-        assert zero.get_parents() == [node]
-        assert one.get_parents() == [node]
-        assert two.get_parents() == [node]
-        assert three.get_parents() == [node]
-        assert four.get_parents() == [node]
-
         try:
             node.add_depends([[five, six]])
         except:
@@ -556,8 +552,6 @@ class NodeTestCase(unittest.TestCase):
         else:
             raise "did not catch expected exception"
         assert node.depends == [zero, one, two, three, four]
-        assert five.get_parents() == []
-        assert six.get_parents() == []
 
 
     def test_add_source(self):
@@ -583,12 +577,6 @@ class NodeTestCase(unittest.TestCase):
         node.add_source([three, four, one])
         assert node.sources == [zero, one, two, three, four]
 
-        assert zero.get_parents() == [node]
-        assert one.get_parents() == [node]
-        assert two.get_parents() == [node]
-        assert three.get_parents() == [node]
-        assert four.get_parents() == [node]
-
         try:
             node.add_source([[five, six]])
         except:
@@ -596,8 +584,6 @@ class NodeTestCase(unittest.TestCase):
         else:
             raise "did not catch expected exception"
         assert node.sources == [zero, one, two, three, four]
-        assert five.get_parents() == []
-        assert six.get_parents() == []
 
     def test_add_ignore(self):
         """Test adding files whose dependencies should be ignored.
@@ -622,12 +608,6 @@ class NodeTestCase(unittest.TestCase):
         node.add_ignore([three, four, one])
         assert node.ignore == [zero, one, two, three, four]
 
-        assert zero.get_parents() == [node]
-        assert one.get_parents() == [node]
-        assert two.get_parents() == [node]
-        assert three.get_parents() == [node]
-        assert four.get_parents() == [node]
-
         try:
             node.add_ignore([[five, six]])
         except:
@@ -635,8 +615,6 @@ class NodeTestCase(unittest.TestCase):
         else:
             raise "did not catch expected exception"
         assert node.ignore == [zero, one, two, three, four]
-        assert five.get_parents() == []
-        assert six.get_parents() == []
 
     def test_get_found_includes(self):
         """Test the default get_found_includes() method
@@ -689,6 +667,33 @@ class NodeTestCase(unittest.TestCase):
         deps = node.get_implicit_deps(env, s, target)
         assert deps == [d, e, f], map(str, deps)
 
+    def test_get_source_scanner(self):
+        """Test fetching the source scanner for a Node
+        """
+        class Builder:
+            pass
+        target = SCons.Node.Node()
+        source = SCons.Node.Node()
+        s = target.get_source_scanner(source)
+        assert s is None, s
+
+        ts1 = Scanner()
+        ts2 = Scanner()
+        ts3 = Scanner()
+
+        source.backup_source_scanner = ts1
+        s = target.get_source_scanner(source)
+        assert s is ts1, s
+
+        source.builder = Builder()
+        source.builder.source_scanner = ts2
+        s = target.get_source_scanner(source)
+        assert s is ts2, s
+
+        target.source_scanner = ts3
+        s = target.get_source_scanner(source)
+        assert s is ts3, s
+
     def test_scan(self):
         """Test Scanner functionality
         """
@@ -947,6 +952,7 @@ class NodeTestCase(unittest.TestCase):
         n.includes = 'testincludes'
         n.found_include = {'testkey':'testvalue'}
         n.implicit = 'testimplicit'
+        n.waiting_parents = ['foo', 'bar']
 
         n.clear()
 
@@ -955,6 +961,7 @@ class NodeTestCase(unittest.TestCase):
         assert n.includes is None, n.includes
         assert n.found_includes == {}, n.found_includes
         assert n.implicit is None, n.implicit
+        assert n.waiting_parents == [], n.waiting_parents
 
     def test_get_subst_proxy(self):
         """Test the get_subst_proxy method."""
@@ -985,6 +992,25 @@ class NodeTestCase(unittest.TestCase):
         n = SCons.Node.Node()
         n.postprocess()
 
+    def test_add_to_waiting_parents(self):
+        """Test the add_to_waiting_parents() method"""
+        n1 = SCons.Node.Node()
+        n2 = SCons.Node.Node()
+        assert n1.waiting_parents == [], n1.waiting_parents
+        n1.add_to_waiting_parents(n2)
+        assert n1.waiting_parents == [n2], n1.waiting_parents
+
+    def test_call_for_all_waiting_parents(self):
+        """Test the call_for_all_waiting_parents() method"""
+        n1 = SCons.Node.Node()
+        n2 = SCons.Node.Node()
+        n1.add_to_waiting_parents(n2)
+        result = []
+        def func(node, result=result):
+            result.append(node)
+        n1.call_for_all_waiting_parents(func)
+        assert result == [n1, n2], result
+
 
 
 if __name__ == "__main__":
index 28417597e94104fd713e76a19dc9591d2c2ec1b6..75b3a6dc0aaabd442626d937108bc710f525e43f 100644 (file)
@@ -116,10 +116,11 @@ class Node:
         self.ignore = []        # dependencies to ignore
         self.ignore_dict = {}
         self.implicit = None    # implicit (scanned) dependencies (None means not scanned yet)
-        self.parents = {}
+        self.waiting_parents = []
         self.wkids = None       # Kids yet to walk, when it's an array
         self.target_scanner = None      # explicit scanner from this node's Builder
-        self.source_scanner = None      # source scanner
+        self.source_scanner = None
+        self.backup_source_scanner = None
 
         self.env = None
         self.state = None
@@ -208,20 +209,29 @@ class Node:
         else:
             self.store_info(new_binfo)
 
-        # Clear out the implicit dependency caches:
-        # XXX this really should somehow be made more general and put
-        #     under the control of the scanners.
-        if self.source_scanner:
-            self.found_includes = {}
-            self.includes = None
-            for parent in self.get_parents():
-                parent.implicit = None
-                parent.del_binfo()
+        # Clear our scanned included files.
+        self.found_includes = {}
+        self.includes = None
+
+        # Clear the implicit dependency caches of any Nodes
+        # waiting for this Node to be built.
+        for parent in self.waiting_parents:
+            parent.implicit = None
+            parent.del_binfo()
+        self.waiting_parents = []
 
         # The content just changed, delete any cached info
         # so it will get recalculated.
         self.del_cinfo()
 
+    def add_to_waiting_parents(self, node):
+        self.waiting_parents.append(node)
+
+    def call_for_all_waiting_parents(self, func):
+        func(self)
+        for parent in self.waiting_parents:
+            parent.call_for_all_waiting_parents(func)
+
     def postprocess(self):
         """Clean up anything we don't need to hang onto after we've
         been built."""
@@ -248,6 +258,8 @@ class Node:
         self.found_includes = {}
         self.implicit = None
 
+        self.waiting_parents = []
+
     def visited(self):
         """Called just after this node has been visited
         without requiring a build.."""
@@ -329,6 +341,10 @@ class Node:
         if not scanner:
             return []
 
+        # Give the scanner a chance to select a more specific scanner
+        # for this Node.
+        scanner = scanner.select(self)
+
         try:
             recurse = scanner.recursive
         except AttributeError:
@@ -367,6 +383,22 @@ class Node:
             self.implicit_factory_cache[path] = n
             return n
 
+    def get_source_scanner(self, node):
+        """Fetch the source scanner for the specified node
+
+        NOTE:  "self" is the target being built, "node" is
+        the source file for which we want to fetch the scanner.
+        """
+        if self.source_scanner:
+            return self.source_scanner
+        try:
+            scanner = node.builder.source_scanner
+            if scanner:
+                return scanner
+        except AttributeError:
+            pass
+        return node.backup_source_scanner or None
+
     def scan(self):
         """Scan this node's dependents for implicit dependencies."""
         # Don't bother scanning non-derived files, because we don't
@@ -405,20 +437,14 @@ class Node:
         #            self.del_binfo()
 
         for child in self.children(scan=0):
-            scanner = child.source_scanner
+            scanner = self.get_source_scanner(child)
             if scanner:
-                self._add_child(self.implicit,
-                                self.implicit_dict,
-                                child.get_implicit_deps(build_env,
-                                                        scanner,
-                                                        self))
+                deps = child.get_implicit_deps(build_env, scanner, self)
+                self._add_child(self.implicit, self.implicit_dict, deps)
 
         # scan this node itself for implicit dependencies
-        self._add_child(self.implicit,
-                        self.implicit_dict,
-                        self.get_implicit_deps(build_env,
-                                               self.target_scanner,
-                                               self))
+        deps = self.get_implicit_deps(build_env, self.target_scanner, self)
+        self._add_child(self.implicit, self.implicit_dict, deps)
 
         # XXX See note above re: --implicit-cache.
         #if implicit_cache:
@@ -632,7 +658,6 @@ class Node:
                 collection.append(c)
                 dict[c] = 1
                 added = 1
-            c.parents[self] = 1
         if added:
             self._children_reset()
 
@@ -686,9 +711,6 @@ class Node:
         else:
             return self.sources + self.depends + self.implicit
 
-    def get_parents(self):
-        return self.parents.keys()
-
     def set_state(self, state):
         self.state = state
 
@@ -738,7 +760,8 @@ class Node:
         if self.is_derived() and self.env:
             env = self.get_build_env()
             for s in self.sources:
-                def f(node, env=env, scanner=s.source_scanner, target=self):
+                scanner = s.get_source_scanner(self)
+                def f(node, env=env, scanner=scanner, target=self):
                     return node.get_found_includes(env, scanner, target)
                 return SCons.Util.render_tree(s, f, 1)
         else:
index db4e375de4ccbc5845b2bbfac05c2927d9c5c567..196b426fc24f06d4c2fef494d6782b080a212822 100644 (file)
@@ -155,6 +155,17 @@ class SConf:
                 already_done.append( n )
             self._setCache(n.children())
 
+            # Calling children() has set up the implicit cache (and
+            # other state), but we're not really building things yet,
+            # so generated files won't have been generated.  Clear the
+            # state so we will, in fact, build everything that's necessary
+            # when we do the build.
+            #
+            # XXX - it would be good to find a better way to do this,
+            # maybe by doing something with the actions in the actual
+            # Taskmaster...?
+            n.clear()
+
     def BuildNodes(self, nodes):
         """
         Tries to build the given nodes immediately. Returns 1 on success,
index 8bc11ad5a6e906288fa1a63ab6d6dd064d158417..39af3a18d12537612e640a6d40be45bc2a9198a3 100644 (file)
@@ -178,6 +178,8 @@ class SConfTestCase(unittest.TestCase):
                         return None
                     def postprocess(self):
                         pass
+                    def clear(self):
+                        pass
                 return [MyNode('n1'), MyNode('n2')]
         self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
         sconf.TryBuild(self.scons_env.SConfActionBuilder)
index f63060a330d2868659a5e364de806ef3d0b0c397..693ec18da7e0932d08a0588e710dde124590115d 100644 (file)
@@ -193,6 +193,9 @@ class DummyEnvironment(UserDict.UserDict):
             path = [path]
         return map(self.subst, path)
 
+    def get_calculator(self):
+        return None
+
 if os.path.normcase('foo') == os.path.normcase('FOO'):
     my_normpath = os.path.normcase
 else:
index e69f99e754135381bdac6b3e386dd602687f8f0b..766f0ee6eaf1da2e7b64ff5dc65510308fe105b9 100644 (file)
@@ -168,6 +168,9 @@ class DummyEnvironment:
             path = [path]
         return map(self.subst, path)
 
+    def get_calculator(self):
+        return None
+
 def deps_match(self, deps, headers):
     scanned = map(os.path.normpath, map(str, deps))
     expect = map(os.path.normpath, headers)
index c20dedc3fddfab535cc5992ce8e82b54b7db5dd7..876f4920facb8dd27a042d3f5eeb82b31a8ea203 100644 (file)
@@ -218,6 +218,9 @@ class DummyEnvironment:
     def __delitem__(self,key):
         del self.Dictionary()[key]
 
+    def get_calculator(self):
+        return None
+
 global my_normpath
 my_normpath = os.path.normpath
 
index 4e662a2ae5e3ba1b147f1212053f46c422006a70..a0c6f01f3f19d460e6d123e45f7fcb3ee1426acd 100644 (file)
@@ -65,6 +65,17 @@ class FindPathDirsTestCase(unittest.TestCase):
 
 class ScannerTestCase(unittest.TestCase):
 
+    def test_creation(self):
+        """Test creation of Scanner objects through the Scanner() function"""
+        def func(self):
+            pass
+        s = SCons.Scanner.Scanner(func)
+        assert isinstance(s, SCons.Scanner.Base), s
+        s = SCons.Scanner.Scanner({})
+        assert isinstance(s, SCons.Scanner.Selector), s
+
+class BaseTestCase(unittest.TestCase):
+
     def func(self, filename, env, target, *args):
         self.filename = filename
         self.env = env
@@ -192,6 +203,66 @@ class ScannerTestCase(unittest.TestCase):
         self.failUnless(sk == ['.3', '.4'],
                         "sk was %s, not ['.3', '.4']")
 
+    def test_select(self):
+        """Test the Scanner.Base select() method"""
+        scanner = SCons.Scanner.Base(function = self.func)
+        s = scanner.select('.x')
+        assert s is scanner, s
+
+class SelectorTestCase(unittest.TestCase):
+    class skey:
+        def __init__(self, key):
+            self.key = key
+        def scanner_key(self):
+            return self.key
+
+    def test___init__(self):
+        """Test creation of Scanner.Selector object"""
+        s = SCons.Scanner.Selector({})
+        assert isinstance(s, SCons.Scanner.Selector), s
+        assert s.dict == {}, s.dict
+
+    def test___call__(self):
+        """Test calling Scanner.Selector objects"""
+        called = []
+        def s1func(node, env, path, called=called):
+            called.append('s1func')
+            called.append(node)
+            return []
+        def s2func(node, env, path, called=called):
+            called.append('s2func')
+            called.append(node)
+            return []
+        s1 = SCons.Scanner.Base(s1func)
+        s2 = SCons.Scanner.Base(s2func)
+        selector = SCons.Scanner.Selector({'.x' : s1, '.y' : s2})
+        nx = self.skey('.x')
+        selector(nx, None, [])
+        assert called == ['s1func', nx], called
+        del called[:]
+        ny = self.skey('.y')
+        selector(ny, None, [])
+        assert called == ['s2func', ny], called
+
+    def test_select(self):
+        """Test the Scanner.Selector select() method"""
+        selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
+        s = selector.select(self.skey('.x'))
+        assert s == 1, s
+        s = selector.select(self.skey('.y'))
+        assert s == 2, s
+        s = selector.select(self.skey('.z'))
+        assert s is None, s
+
+    def test_add_scanner(self):
+        """Test the Scanner.Selector add_scanner() method"""
+        selector = SCons.Scanner.Selector({'.x' : 1, '.y' : 2})
+        s = selector.select(self.skey('.z'))
+        assert s is None, s
+        selector.add_scanner('.z', 3)
+        s = selector.select(self.skey('.z'))
+        assert s == 3, s
+
 class CurrentTestCase(unittest.TestCase):
     def test_class(self):
         """Test the Scanner.Current class"""
@@ -355,6 +426,8 @@ def suite():
     tclasses = [
                  FindPathDirsTestCase,
                  ScannerTestCase,
+                 BaseTestCase,
+                 SelectorTestCase,
                  CurrentTestCase,
                  ClassicTestCase,
                  ClassicCPPTestCase,
index 8f6c9e6a9eea1f1d778837cf98e27af497853eda..8432638e41dd4bb3d36d0374e4c2b9164346c156 100644 (file)
@@ -43,6 +43,15 @@ class _Null:
 # used as an actual argument value.
 _null = _Null
 
+def Scanner(function, *args, **kw):
+    """Public interface factory function for creating different types
+    of Scanners based on the different types of "functions" that may
+    be supplied."""
+    if SCons.Util.is_Dict(function):
+        return apply(Selector, (function,) + args, kw)
+    else:
+        return apply(Base, (function,) + args, kw)
+
 class FindPathDirs:
     """A class to bind a specific *PATH variable name and the fs object
     to a function that will return all of the *path directories."""
@@ -190,6 +199,31 @@ class Base:
             return env.subst_list(self.skeys)[0]
         return self.skeys
 
+    def select(self, node):
+        return self
+
+
+class Selector(Base):
+    """
+    A class for selecting a more specific scanner based on the
+    scanner_key() (suffix) for a specific Node.
+    """
+    def __init__(self, dict, *args, **kw):
+        Base.__init__(self, (None,)+args, kw)
+        self.dict = dict
+
+    def __call__(self, node, env, path = ()):
+        return self.select(node)(node, env, path)
+
+    def select(self, node):
+        try:
+            return self.dict[node.scanner_key()]
+        except KeyError:
+            return None
+
+    def add_scanner(self, skey, scanner):
+        self.dict[skey] = scanner
+
 
 class Current(Base):
     """
@@ -200,6 +234,7 @@ class Current(Base):
 
     def __init__(self, *args, **kw):
         def current_check(node, env):
+            calc = env.get_calculator()
             c = not node.has_builder() or node.current(env.get_calculator())
             return c
         kw['scan_check'] = current_check
index e5b35483b6460735420de084722957da9ed5aa17..a253ddfae4c4cfde585f5111d71d9c6ffca08716 100644 (file)
@@ -158,12 +158,10 @@ class Task:
         their dependent parent nodes.
         """
         for t in self.targets:
-            def get_parents(node, parent): return node.get_parents()
-            def set_state(node, parent): node.set_state(SCons.Node.failed)
-            walker = SCons.Node.Walker(t, get_parents, eval_func=set_state)
-            n = walker.next()
-            while n:
-                n = walker.next()
+            # Set failure state on all of the parents that were dependent
+            # on this failed build.
+            def set_state(node): node.set_state(SCons.Node.failed)
+            t.call_for_all_waiting_parents(set_state)
 
         self.tm.executed(self.node)
 
@@ -340,6 +338,12 @@ class Taskmaster:
             def unbuilt_nodes(node): return node.get_state() == None
             not_built = filter(unbuilt_nodes, derived)
             if not_built:
+                # We're waiting on one more derived files that have not
+                # yet been built.  Add this node to the waiting_parents
+                # list of each of those derived files.
+                def add_to_waiting_parents(child, parent=node):
+                    child.add_to_waiting_parents(parent)
+                map(add_to_waiting_parents, not_built)
                 not_built.reverse()
                 self.candidates.extend(self.order(not_built))
                 continue
index 8ca7e86f035b093204b127b1804daf21413e45dd..7010edbcabad05ba68d48e6557aec451673200e8 100644 (file)
@@ -50,15 +50,12 @@ class Node:
         self.csig = None
         self.state = None
         self.prepared = None
-        self.parents = []
+        self.waiting_parents = []
         self.side_effect = 0
         self.side_effects = []
         self.alttargets = []
         self.postprocessed = None
 
-        for kid in kids:
-            kid.parents.append(self)
-
     def retrieve_from_cache(self):
         global cache_text
         if self.cached:
@@ -99,15 +96,18 @@ class Node:
         global scan_called
         scan_called = scan_called + 1
         self.kids = self.kids + self.scans
-        for scan in self.scans:
-            scan.parents.append(self)
         self.scans = []
 
     def scanner_key(self):
         return self.name
   
-    def get_parents(self):
-        return self.parents
+    def add_to_waiting_parents(self, node):
+        self.waiting_parents.append(node)
+  
+    def call_for_all_waiting_parents(self, func):
+        func(self)
+        for parent in self.waiting_parents:
+            parent.call_for_all_waiting_parents(func)
 
     def get_state(self):
         return self.state
@@ -569,7 +569,6 @@ class TaskmasterTestCase(unittest.TestCase):
         n2 = Node("n2", [n1])
         n3 = Node("n3", [n2])
         n1.kids = [n3]
-        n3.parents.append(n1)
 
         try:
             tm = SCons.Taskmaster.Taskmaster([n3])
index 667e085367d59e552dca21415483f83850315257..4364cf3e0ba47380a303324cc835a5b875004681 100644 (file)
@@ -104,7 +104,7 @@ def createProgBuilder(env):
                                         suffix = '$PROGSUFFIX',
                                         src_suffix = '$OBJSUFFIX',
                                         src_builder = 'Object',
-                                        scanner = SCons.Defaults.ProgScan)
+                                        target_scanner = SCons.Defaults.ProgScan)
         env['BUILDERS']['Program'] = program
 
     return program
@@ -146,7 +146,7 @@ def createSharedLibBuilder(env):
                                            emitter = "$SHLIBEMITTER",
                                            prefix = '$SHLIBPREFIX',
                                            suffix = '$SHLIBSUFFIX',
-                                           scanner = SCons.Defaults.ProgScan,
+                                           target_scanner = SCons.Defaults.ProgScan,
                                            src_suffix = '$SHOBJSUFFIX',
                                            src_builder = 'SharedObject')
         env['BUILDERS']['SharedLibrary'] = shared_lib
@@ -173,7 +173,8 @@ def createObjBuilders(env):
                                            emitter = "$OBJEMITTER",
                                            prefix = '$OBJPREFIX',
                                            suffix = '$OBJSUFFIX',
-                                           src_builder = ['CFile', 'CXXFile'])
+                                           src_builder = ['CFile', 'CXXFile'],
+                                           source_scanner = SCons.Defaults.ObjSourceScan)
         env['BUILDERS']['StaticObject'] = static_obj
         env['BUILDERS']['Object'] = static_obj
         env['OBJEMITTER'] = SCons.Defaults.StaticObjectEmitter
@@ -185,7 +186,8 @@ def createObjBuilders(env):
                                            emitter = "$SHOBJEMITTER",
                                            prefix = '$SHOBJPREFIX',
                                            suffix = '$SHOBJSUFFIX',
-                                           src_builder = ['CFile', 'CXXFile'])
+                                           src_builder = ['CFile', 'CXXFile'],
+                                           source_scanner = SCons.Defaults.ObjSourceScan)
         env['BUILDERS']['SharedObject'] = shared_obj
         env['SHOBJEMITTER'] = SCons.Defaults.SharedObjectEmitter
 
index 1369a8bb7c5d2093726edd0a32ce8233cf3289e7..22df3d4ad52776a80b3090c5806b07963d1604ec 100644 (file)
@@ -137,7 +137,7 @@ def generate(env):
     env['RCINCPREFIX'] = '--include-dir '
     env['RCINCSUFFIX'] = ''
     env['RCCOM'] = '$RC $RCINCFLAGS $RCFLAGS -i $SOURCE -o $TARGET'
-    env.Append(CPPSUFFIXES = ['.rc'])
+    SCons.Defaults.ObjSourceScan.add_scanner('.rc', SCons.Defaults.CScan)
     env['BUILDERS']['RES'] = res_builder
     
     # Some setting from the platform also have to be overridden:
index 3d498026d99fdf69771659c69fd25ae8c2c36a01..17da70233dac53d14ed42c9800c0345433e72ac6 100644 (file)
@@ -448,7 +448,7 @@ def generate(env):
     env['RC'] = 'rc'
     env['RCFLAGS'] = SCons.Util.CLVar('')
     env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
-    env.Append(CPPSUFFIXES = ['.rc'])
+    SCons.Defaults.ObjSourceScan.add_scanner('.rc', SCons.Defaults.CScan)
     env['BUILDERS']['RES'] = res_builder
 
     try:
index f8234ba1facf645ab54babe3dcb7efd161962fb3..a0dfed882219c1681d29b57ff5700864367c23a8 100644 (file)
@@ -116,7 +116,7 @@ class _Automoc:
                     out_sources.append(moc_o)
                     objBuilder(moc_o, moc_cpp)
                 self.mocFromHBld(env, moc_cpp, h)
-                moc_cpp.target_scanner = SCons.Defaults.CScan
+                #moc_cpp.target_scanner = SCons.Defaults.CScan
             if cpp and q_object_search.search(cpp.get_contents()):
                 # cpp file with Q_OBJECT macro found -> add moc
                 # (to be included in cpp)
@@ -126,7 +126,7 @@ class _Automoc:
                     env['QT_MOCNAMEGENERATOR'](base, src_ext, env)))
                 self.mocFromCppBld(env, moc, cpp)
                 env.Ignore(moc, moc)
-                moc.source_scanner = SCons.Defaults.CScan
+                #moc.source_scanner = SCons.Defaults.CScan
 
         os.chdir(old_os_cwd)
         FS.chdir(old_fs_cwd)
index e64dff27d4143f4bfcc6d82d116034a9f43d758c..a340a4a071e31803f6a7494ab2d3d4fbdb9904a5 100644 (file)
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 """
-Test that we can add filesuffixes to $CPPSUFFIXES.
+Test the ability to scan additional filesuffixes added to $CPPSUFFIXES.
 """
 
 import TestSCons
 
+python = TestSCons.python
+
 test = TestSCons.TestSCons()
 
+test.write('mycc.py', r"""
+import string
+import sys
+def do_file(outf, inf):
+    for line in open(inf, 'rb').readlines():
+        if line[:10] == '#include <':
+            do_file(outf, line[10:-2])
+        else:
+            outf.write(line)
+outf = open(sys.argv[1], 'wb')
+for f in sys.argv[2:]:
+    do_file(outf, f)
+sys.exit(0)
+""")
+
 test.write('SConstruct', """
-env = Environment(CPPPATH = ['.'])
+env = Environment(CPPPATH = ['.'],
+                  CC = r'%s mycc.py',
+                  CCFLAGS = [],
+                  CCCOM = '$CC $TARGET $SOURCES',
+                  OBJSUFFIX = '.o')
 env.Append(CPPSUFFIXES = ['.x'])
-env.InstallAs('foo_c', 'foo.c')
-env.InstallAs('foo_x', 'foo.x')
+env.Object(target = 'test1', source = 'test1.c')
+env.InstallAs('test1_c', 'test1.c')
+env.InstallAs('test1_h', 'test1.h')
+env.InstallAs('test1_x', 'test1.x')
+""" % (python,))
+
+test.write('test1.c', """\
+test1.c 1
+#include <test1.h>
+#include <test1.x>
 """)
 
-test.write('foo.c', """\
+test.write('test1.h', """\
+test1.h 1
 #include <foo.h>
 """)
 
-test.write('foo.x', """\
+test.write('test1.x', """\
+test1.x 1
 #include <foo.h>
 """)
 
 test.write('foo.h', "foo.h 1\n")
 
 test.run(arguments='.', stdout=test.wrap_stdout("""\
-Install file: "foo.c" as "foo_c"
-Install file: "foo.x" as "foo_x"
-"""))
+%s mycc.py test1.o test1.c
+Install file: "test1.c" as "test1_c"
+Install file: "test1.h" as "test1_h"
+Install file: "test1.x" as "test1_x"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.c 1
+test1.h 1
+foo.h 1
+test1.x 1
+foo.h 1
+""")
 
 test.up_to_date(arguments='.')
 
 test.write('foo.h', "foo.h 2\n")
 
 test.run(arguments='.', stdout=test.wrap_stdout("""\
-Install file: "foo.c" as "foo_c"
-Install file: "foo.x" as "foo_x"
-"""))
+%s mycc.py test1.o test1.c
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.c 1
+test1.h 1
+foo.h 2
+test1.x 1
+foo.h 2
+""")
+
+test.up_to_date(arguments='.')
+
+test.write('test1.x', """\
+test1.x 2
+#include <foo.h>
+""")
+
+test.run(arguments='.', stdout=test.wrap_stdout("""\
+%s mycc.py test1.o test1.c
+Install file: "test1.x" as "test1_x"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.c 1
+test1.h 1
+foo.h 2
+test1.x 2
+foo.h 2
+""")
+
+test.up_to_date(arguments='.')
+
+test.write('test1.h', """\
+test1.h 2
+#include <foo.h>
+""")
+
+test.run(arguments='.', stdout=test.wrap_stdout("""\
+%s mycc.py test1.o test1.c
+Install file: "test1.h" as "test1_h"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.c 1
+test1.h 2
+foo.h 2
+test1.x 2
+foo.h 2
+""")
 
 test.up_to_date(arguments='.')
 
index 6f10439ce69c948604215a90b2114691501b79d0..188da9bd72c350fcc92706aa2f5e2bd853810f77 100644 (file)
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 """
-Test that we can add filesuffixes to $DSUFFIXES.
+Test the ability to scan additional filesuffixes added to $DSUFFIXES.
 """
 
 import TestSCons
 
+python = TestSCons.python
+
 test = TestSCons.TestSCons()
 
+test.write('mydc.py', r"""
+import string
+import sys
+def do_file(outf, inf):
+    for line in open(inf, 'rb').readlines():
+        if line[:7] == 'import ':
+            do_file(outf, line[7:-2]+'.d')
+        else:
+            outf.write(line)
+outf = open(sys.argv[1], 'wb')
+for f in sys.argv[2:]:
+    do_file(outf, f)
+sys.exit(0)
+""")
+
 test.write('SConstruct', """
-env = Environment(DPATH=['.'])
-env.Append(DSUFFIXES = ['.x'])
-env.InstallAs('foo_d', 'foo.d')
-env.InstallAs('foo_x', 'foo.x')
+env = Environment(DPATH = ['.'],
+                  DC = r'%s mydc.py',
+                  DFLAGS = [],
+                  DCOM = '$DC $TARGET $SOURCES',
+                  OBJSUFFIX = '.o')
+env.Append(CPPSUFFIXES = ['.x'])
+env.Object(target = 'test1', source = 'test1.d')
+env.InstallAs('test1_d', 'test1.d')
+env.InstallAs('test2_d', 'test2.d')
+env.InstallAs('test3_d', 'test3.d')
+""" % (python,))
+
+test.write('test1.d', """\
+test1.d 1
+import test2;
+import test3;
+""")
+
+test.write('test2.d', """\
+test2.d 1
+import foo;
 """)
 
-test.write('foo.d', """\
-import inc;
+test.write('test3.d', """\
+test3.d 1
+import foo;
 """)
 
-test.write('foo.x', """\
-import inc;
+test.write('foo.d', "foo.d 1\n")
+
+test.run(arguments='.', stdout=test.wrap_stdout("""\
+%s mydc.py test1.o test1.d
+Install file: "test1.d" as "test1_d"
+Install file: "test2.d" as "test2_d"
+Install file: "test3.d" as "test3_d"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.d 1
+test2.d 1
+foo.d 1
+test3.d 1
+foo.d 1
+""")
+
+test.up_to_date(arguments='.')
+
+test.write('foo.d', "foo.d 2\n")
+
+test.run(arguments='.', stdout=test.wrap_stdout("""\
+%s mydc.py test1.o test1.d
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.d 1
+test2.d 1
+foo.d 2
+test3.d 1
+foo.d 2
 """)
 
-test.write('inc.d', "inc.d 1\n")
+test.up_to_date(arguments='.')
+
+test.write('test3.d', """\
+test3.d 2
+import foo;
+""")
 
 test.run(arguments='.', stdout=test.wrap_stdout("""\
-Install file: "foo.d" as "foo_d"
-Install file: "foo.x" as "foo_x"
-"""))
+%s mydc.py test1.o test1.d
+Install file: "test3.d" as "test3_d"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.d 1
+test2.d 1
+foo.d 2
+test3.d 2
+foo.d 2
+""")
 
 test.up_to_date(arguments='.')
 
-test.write('inc.d', "inc 2\n")
+test.write('test2.d', """\
+test2.d 2
+import foo;
+""")
 
 test.run(arguments='.', stdout=test.wrap_stdout("""\
-Install file: "foo.d" as "foo_d"
-Install file: "foo.x" as "foo_x"
-"""))
+%s mydc.py test1.o test1.d
+Install file: "test2.d" as "test2_d"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+test1.d 1
+test2.d 2
+foo.d 2
+test3.d 2
+foo.d 2
+""")
 
 test.up_to_date(arguments='.')
 
index 1ab7306206868d68cde8ef36037a8b0f8a076d03..c5047f7b385d79bdbf6ff30aff219bdb4f08346b 100644 (file)
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 """
-Test that we can add filesuffixes to $FORTRANSUFFIXES.
+Test the ability to scan additional filesuffixes added to $FORTRANSUFFIXES.
 """
 
 import TestSCons
 
+python = TestSCons.python
+
 test = TestSCons.TestSCons()
 
+test.write('myfc.py', r"""
+import string
+import sys
+def do_file(outf, inf):
+    for line in open(inf, 'rb').readlines():
+        if line[:15] == "      INCLUDE '":
+            do_file(outf, line[15:-2])
+        else:
+            outf.write(line)
+outf = open(sys.argv[1], 'wb')
+for f in sys.argv[2:]:
+    do_file(outf, f)
+sys.exit(0)
+""")
+
 test.write('SConstruct', """
-env = Environment()
+env = Environment(F77PATH = ['.'],
+                  F77 = r'%s myfc.py',
+                  F77FLAGS = [],
+                  F77COM = '$F77 $TARGET $SOURCES',
+                  OBJSUFFIX = '.o')
 env.Append(FORTRANSUFFIXES = ['.x'])
-env.InstallAs('foo_f', 'foo.f')
-env.InstallAs('foo_x', 'foo.x')
+env.Object(target = 'test1', source = 'test1.f')
+env.InstallAs('test1_f', 'test1.f')
+env.InstallAs('test1_h', 'test1.h')
+env.InstallAs('test1_x', 'test1.x')
+""" % (python,))
+
+test.write('test1.f', """\
+      test1.f 1
+      INCLUDE 'test1.h'
+      INCLUDE 'test1.x'
+""")
+
+test.write('test1.h', """\
+      test1.h 1
+      INCLUDE 'foo.h'
+""")
+
+test.write('test1.x', """\
+      test1.x 1
+      INCLUDE 'foo.h'
+""")
+
+test.write('foo.h', """\
+      foo.h 1
+""")
+
+test.run(arguments='.', stdout=test.wrap_stdout("""\
+%s myfc.py test1.o test1.f
+Install file: "test1.f" as "test1_f"
+Install file: "test1.h" as "test1_h"
+Install file: "test1.x" as "test1_x"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+      test1.f 1
+      test1.h 1
+      foo.h 1
+      test1.x 1
+      foo.h 1
 """)
 
-test.write('foo.f', """\
-INCLUDE 'foo.h'
+test.up_to_date(arguments='.')
+
+test.write('foo.h', """\
+      foo.h 2
 """)
 
-test.write('foo.x', """\
-INCLUDE 'foo.h'
+test.run(arguments='.', stdout=test.wrap_stdout("""\
+%s myfc.py test1.o test1.f
+""" % (python,)))
+
+test.must_match('test1.o', """\
+      test1.f 1
+      test1.h 1
+      foo.h 2
+      test1.x 1
+      foo.h 2
 """)
 
-test.write('foo.h', "foo.h 1\n")
+test.up_to_date(arguments='.')
+
+test.write('test1.x', """\
+      test1.x 2
+      INCLUDE 'foo.h'
+""")
 
 test.run(arguments='.', stdout=test.wrap_stdout("""\
-Install file: "foo.f" as "foo_f"
-Install file: "foo.x" as "foo_x"
-"""))
+%s myfc.py test1.o test1.f
+Install file: "test1.x" as "test1_x"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+      test1.f 1
+      test1.h 1
+      foo.h 2
+      test1.x 2
+      foo.h 2
+""")
 
 test.up_to_date(arguments='.')
 
-test.write('foo.h', "foo.h 2\n")
+test.write('test1.h', """\
+      test1.h 2
+      INCLUDE 'foo.h'
+""")
 
 test.run(arguments='.', stdout=test.wrap_stdout("""\
-Install file: "foo.f" as "foo_f"
-Install file: "foo.x" as "foo_x"
-"""))
+%s myfc.py test1.o test1.f
+Install file: "test1.h" as "test1_h"
+""" % (python,)))
+
+test.must_match('test1.o', """\
+      test1.f 1
+      test1.h 2
+      foo.h 2
+      test1.x 2
+      foo.h 2
+""")
 
 test.up_to_date(arguments='.')
 
index 523044cff2b4ddcc1ce16d44fd92dc95fa45cf81..cccf4d6b737563eae520b1f346679468a6311acc 100644 (file)
@@ -27,11 +27,15 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 Test that dependencies in installed header files get re-scanned correctly.
 """
 
+import os.path
+
 import TestSCons
 
 test = TestSCons.TestSCons()
 
-test.write('SConstruct', """
+test.subdir('work1', ['work1', 'dist'])
+
+test.write(['work1', 'SConstruct'], """
 env = Environment(CPPPATH=['#include'])
 Export('env')
 SConscript('dist/SConscript')
@@ -39,31 +43,72 @@ libfoo = env.StaticLibrary('foo', ['foo.c'])
 Default(libfoo)
 """)
 
-test.write('foo.c', """
+test.write(['work1', 'foo.c'], """
 #include <h1.h>
 """)
 
-test.subdir('dist')
-
-test.write(['dist', 'SConscript'], """\
+test.write(['work1', 'dist', 'SConscript'], """\
 Import('env')
 env.Install('#include', ['h1.h', 'h2.h', 'h3.h'])
 """)
 
-test.write(['dist', 'h1.h'], """\
+test.write(['work1', 'dist', 'h1.h'], """\
 #include "h2.h"
 """)
 
-test.write(['dist', 'h2.h'], """\
+test.write(['work1', 'dist', 'h2.h'], """\
 #include "h3.h"
 """)
 
-test.write(['dist', 'h3.h'], """\
+test.write(['work1', 'dist', 'h3.h'], """\
 int foo = 3;
 """)
 
-test.run(arguments = ".")
+test.run(chdir = 'work1', arguments = ".")
+
+test.up_to_date(chdir = 'work1', arguments = ".")
+
+#
+test.subdir('ref', 'work2', ['work2', 'src'])
+
+test.write(['work2', 'SConstruct'], """
+env = Environment(CPPPATH=['build', r'%s'])
+env.Install('build', 'src/in1.h')
+env.Install('build', 'src/in2.h')
+env.Install('build', 'src/in3.h')
+""" % test.workpath('ref'))
+
+test.write(['ref', 'in1.h'], '#define FILE "ref/in1.h"\n#include <in2.h>\n')
+test.write(['ref', 'in2.h'], '#define FILE "ref/in2.h"\n#include <in3.h>\n')
+test.write(['ref', 'in3.h'], '#define FILE "ref/in3.h"\n#define FOO 0\n')
+
+src_in1_h = '#define FILE "src/in1.h"\n#include <in2.h>\n'
+src_in2_h = '#define FILE "src/in2.h"\n#include <in3.h>\n'
+src_in3_h = '#define FILE "src/in3.h"\n#define FOO 0\n'
+test.write(['work2', 'src', 'in1.h'], src_in1_h)
+test.write(['work2', 'src', 'in2.h'], src_in2_h)
+test.write(['work2', 'src', 'in3.h'], src_in3_h)
+
+test.run(chdir = 'work2', arguments = 'build')
+
+test.must_match(['work2', 'build', 'in1.h'], src_in1_h)
+test.must_match(['work2', 'build', 'in2.h'], src_in2_h)
+test.must_match(['work2', 'build', 'in3.h'], src_in3_h)
+
+test.up_to_date(chdir = 'work2', arguments = 'build')
+
+src_in3_h = '#define FILE "src/in3.h"\n#define FOO 1\n'
+test.write(['work2', 'src', 'in3.h'], src_in3_h)
+
+test.run(chdir = 'work2', arguments = 'build', stdout=test.wrap_stdout("""\
+Install file: "%s" as "%s"
+""" % (os.path.join('src', 'in3.h'),
+       os.path.join('build', 'in3.h'))))
+
+test.must_match(['work2', 'build', 'in1.h'], src_in1_h)
+test.must_match(['work2', 'build', 'in2.h'], src_in2_h)
+test.must_match(['work2', 'build', 'in3.h'], src_in3_h)
 
-test.up_to_date(arguments = ".")
+test.up_to_date(chdir = 'work2', arguments = 'build')
 
 test.pass_test()
index 61d9e391894bb2e3bc4bf058a25b317e90d8aa98..0bb3d8bb6b162c55f119469485cb17c3b7c09643 100644 (file)
@@ -91,9 +91,8 @@ env2 = env.Copy()
 env2.Append(SCANNERS = [k2scan])
 env2.Command('junk', 'junk.k2', r'%s build.py $SOURCES $TARGET')
 
-bar_in = File('bar.in')
-env.Command('bar', bar_in, r'%s build.py $SOURCES  $TARGET')
-bar_in.source_scanner = kscan
+bar = env.Command('bar', 'bar.in', r'%s build.py $SOURCES  $TARGET')
+bar.source_scanner = kscan
 """ % (python, python, python))
 
 test.write('foo.k', 
index 41a37e1aeead301e405cfe4e3679eabf033041a4..8c84b6da1d9060578a761980e626ce9c6f0b2c2a 100644 (file)
@@ -54,12 +54,15 @@ test = TestSCons.TestSCons(match = TestCmd.match_re_dotall)
 
 
 
-test.write("SConstruct","""
+test.write("SConstruct", """\
+import SCons.Defaults
+
 def build(target, source, env):
     pass
 
 env=Environment()
-env['BUILDERS']['test'] = Builder(action=build)
+env['BUILDERS']['test'] = Builder(action=build,
+                                  source_scanner=SCons.Defaults.ObjSourceScan)
 env.test(target='foo', source='foo.c')
 """)
 
index 52b45050909778c2277da7f4425aab6d0640af99..606590b2d69efa8a6172e90580c880543f1c9267 100644 (file)
@@ -110,38 +110,6 @@ builders["StaticLibMerge"] = BStaticLibMerge
 env = Environment(BUILDERS = builders)
 e = env.Dictionary()   # Slightly easier to type
 
-Scanned = {}
-
-def write_out(file, dict):
-    keys = dict.keys()
-    keys.sort()
-    f = open(file, 'wb')
-    for k in keys:
-        file = os.path.split(k)[1]
-        f.write(file + ": " + str(dict[k]) + "\\n")
-    f.close()
-
-import SCons.Scanner.C
-c_scanner = SCons.Scanner.C.CScan()
-def MyCScan(node, env, target):
-    deps = c_scanner(node, env, target)
-
-    global Scanned
-    n = str(node)
-    try:
-        Scanned[n] = Scanned[n] + 1
-    except KeyError:
-        Scanned[n] = 1
-    write_out('MyCScan.out', Scanned)
-
-    return deps
-S_MyCScan = SCons.Scanner.Current(skeys = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
-                             ".h", ".H", ".hxx", ".hpp", ".h++", ".hh"],
-                    function = MyCScan,
-                    recursive = 1)
-# QQQ Yes, this is manner of fixing the SCANNERS list is fragile.
-env["SCANNERS"] = [S_MyCScan] + env["SCANNERS"][1:]
-
 global_env = env
 e["GlobalEnv"] = global_env
 
@@ -167,7 +135,6 @@ test.write(['SLF', 'Mylib.py'], """\
 import os
 import string
 import re
-import SCons.Environment
 
 def Subdirs(env, dirlist):
     for file in _subconf_list(dirlist):
@@ -376,10 +343,40 @@ for h in ['libg_gx.h', 'libg_gy.h', 'libg_gz.h']:
 """)
 
 test.write(['SLF', 'src', 'lib_geng', 'SConstruct'], """\
+import os
+
+Scanned = {}
+
+def write_out(file, dict):
+    keys = dict.keys()
+    keys.sort()
+    f = open(file, 'wb')
+    for k in keys:
+        file = os.path.split(k)[1]
+        f.write(file + ": " + str(dict[k]) + "\\n")
+    f.close()
+
+orig_function = CScan.function
+
+def MyCScan(node, env, target, orig_function=orig_function):
+    deps = orig_function(node, env, target)
+
+    global Scanned
+    n = str(node)
+    try:
+        Scanned[n] = Scanned[n] + 1
+    except KeyError:
+        Scanned[n] = 1
+    write_out(r'%s', Scanned)
+
+    return deps
+
+CScan.function = MyCScan
+
 env = Environment(CPPPATH = ".")
 l = env.StaticLibrary("g", Split("libg_1.c libg_2.c libg_3.c"))
 Default(l)
-""")
+""" % test.workpath('MyCScan.out'))
 
 # These were the original shell script and Makefile from SLF's original
 # bug report.  We're not using them--in order to make this script as