Remove scan=0 calls to Node.current() for implicit cache. Remove last vestiges of...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 7 Oct 2004 23:15:13 +0000 (23:15 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 7 Oct 2004 23:15:13 +0000 (23:15 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1120 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.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
test/Scanner.py

index a7f606f320a7b567a7f863483519263a28428cc8..1a97d15a8554946587aafd9718ef6829a30d6e49 100644 (file)
@@ -156,8 +156,6 @@ RELEASE 0.97 - XXX
   - Allow optional names to be attached to Builders, for default
     Builders that don't get attached to construction environments.
 
-  - Test enhancements in SourceCode.py and option-n.py.
-
   - Fix problems with Parallel Task Exception handling.
 
   - Build targets in an associated BuildDir even if there are targets
@@ -173,8 +171,6 @@ RELEASE 0.97 - XXX
   - Catch incidents of bad builder creation (without an action) and
     supply meaningful error messages.
 
-  - Better test infrastructure for -c output.
-
   - Fix handling of src_suffix values that aren't extensions (don't
     begin with a '.').
 
@@ -196,6 +192,13 @@ RELEASE 0.97 - XXX
 
   - Properly reset cached state when a Node subclass has been built.
 
+  - Remove the old target_scanner and source_scanner attributes from
+    Nodes, we're now getting these from the Builder(s).
+
+  - Test enhancements in SourceCode.py, option-n.py, midl.py.  Better
+    Command() and Scanner test coverage.  Improved test infrastructure
+    for -c output.
+
   From Christoph Wiedemann:
 
   - Add an Environment.SetDefault() method that only sets values if
index e9d4e117ba1e0540a7efc2db59c83085f031c627..f3d69771eb98b4b85d54b270b400e318fd83f705 100644 (file)
@@ -299,9 +299,6 @@ def _init_nodes(builder, env, overrides, executor_kw, tlist, slist):
             elif t.overrides != overrides:
                 raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
 
-            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:
                 if t.builder != builder:
                     if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
@@ -346,22 +343,6 @@ def _init_nodes(builder, env, overrides, executor_kw, tlist, slist):
         t.env_set(env)
         t.add_source(slist)
         t.set_executor(executor)
-        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 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
index a67d22a26b45165a99cb7376eb90d24bb2d8f6e3..8850ab6cda17772a7bff4946a3a7bd5dfc8ee322 100644 (file)
@@ -82,6 +82,14 @@ class Environment:
         except IndexError:
             pass
         return self.d.get(s, s)
+    def subst_target_source(self, string, raw=0, target=None,
+                            source=None, dict=None, conv=None):
+        return SCons.Util.scons_subst(string, self, raw, target,
+                                      source, dict, conv)
+    def subst_list(self, string, raw=0, target=None,
+                   source=None, dict=None, conv=None):
+        return SCons.Util.scons_subst_list(string, self, raw, target,
+                                           source, dict, conv)
     def arg2nodes(self, args, factory):
         global env_arg2nodes_called
         env_arg2nodes_called = 1
@@ -112,6 +120,7 @@ class Environment:
     def Override(self, overrides):
         env = apply(Environment, (), self.d)
         env.d.update(overrides)
+        env.scanner = self.scanner
         return env
     def _update(self, dict):
         self.d.update(dict)
@@ -125,6 +134,8 @@ class Environment:
         d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
         d['SOURCE'] = d['SOURCES'][0]
         return d
+    def __cmp__(self, other):
+        return cmp(self.scanner, other.scanner) or cmp(self.d, other.d)
 
 class MyNode_without_target_from_source:
     def __init__(self, name):
@@ -132,8 +143,6 @@ class MyNode_without_target_from_source:
         self.sources = []
         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):
@@ -850,8 +859,8 @@ class BuilderTestCase(unittest.TestCase):
                                         source_scanner=sscan,
                                         action='')
         tgt = builder(env, target='foo2', source='bar')[0]
-        assert tgt.target_scanner == tscan, tgt.target_scanner
-        assert tgt.source_scanner == sscan, tgt.source_scanner
+        assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+        assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner
 
         builder1 = SCons.Builder.Builder(action='foo',
                                          src_suffix='.bar',
@@ -861,8 +870,8 @@ class BuilderTestCase(unittest.TestCase):
                                          target_scanner = tscan,
                                          source_scanner = tscan)
         tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
-        assert tgt.target_scanner == tscan, tgt.target_scanner
-        assert tgt.source_scanner == tscan, tgt.source_scanner
+        assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+        assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner
 
     def test_actual_scanner(self):
         """Test usage of actual Scanner objects."""
@@ -888,28 +897,82 @@ class BuilderTestCase(unittest.TestCase):
                  return 'TestScannerkey'
             def instance(self, env):
                  return self
+            name = 'TestScanner'
+            def __str__(self):
+                return self.name
 
         scanner = TestScanner()
         builder = SCons.Builder.Builder(action='action')
 
         # With no scanner specified, source_scanner and
         # backup_source_scanner are None.
+        bar_y = MyNode('bar.y')
         env1 = Environment()
         tgt = builder(env1, target='foo1.x', source='bar.y')[0]
         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.
-        env2 = Environment()
-        env2.scanner = scanner
-        tgt = builder(env2, target='foo2.x', source='bar.y')[0]
+        assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+        assert tgt.builder.source_scanner is None, tgt.builder.source_scanner
+        assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y)
+        assert not src.has_builder(), src.has_builder()
+        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+        # An Environment that has suffix-specified SCANNERS should
+        # provide a source scanner to the target.
+        class EnvTestScanner:
+            def key(self, env):
+                 return '.y'
+            def instance(self, env):
+                 return self
+            name = 'EnvTestScanner'
+            def __str__(self):
+                return self.name
+            def select(self, scanner):
+                return self
+            def path(self, env, dir=None):
+                return ()
+            def __call__(self, node, env, path):
+                return []
+        env3 = Environment(SCANNERS = [EnvTestScanner()])
+        env3.scanner = EnvTestScanner() # test env's version of SCANNERS
+        tgt = builder(env3, target='foo2.x', source='bar.y')[0]
+        src = tgt.sources[0]
+        assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+        assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+        assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+        assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+        assert not src.has_builder(), src.has_builder()
+        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+        # Can't simply specify the scanner as a builder argument; it's
+        # global to all invocations of this builder.
+        tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0]
         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 == scanner, src.backup_source_scanner
+        assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+        assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+        assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+        assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+        assert not src.has_builder(), src.has_builder()
+        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+        # Now use a builder that actually has scanners and ensure that
+        # the target is set accordingly (using the specified scanner
+        # instead of the Environment's scanner)
+        builder = SCons.Builder.Builder(action='action',
+                                        source_scanner=scanner,
+                                        target_scanner=scanner)
+        tgt = builder(env3, target='foo4.x', source='bar.y')[0]
+        src = tgt.sources[0]
+        assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner
+        assert tgt.builder.source_scanner, tgt.builder.source_scanner
+        assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner
+        assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner)
+        assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+        assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y)
+        assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y)
+        assert not src.has_builder(), src.has_builder()
+        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
+
+
 
     def test_Builder_API(self):
         """Test Builder interface.
index 2bd68e1372df680469de7c5d48713e73caa6f261..947963fe1911bf87d1ef055eda2dbfbd39909f90 100644 (file)
@@ -1769,7 +1769,7 @@ class File(Base):
 
         return csig
 
-    def current(self, calc=None, scan=1):
+    def current(self, calc=None):
         self.binfo = self.gen_binfo(calc)
         if self.always_build:
             return None
index 2259b7b652dba2d8f6f38f1d1c922e1ee783a976..ebaf3b9623d22b8c4554fc52fe662071f8485343 100644 (file)
@@ -93,6 +93,7 @@ class Builder:
         self.env = Environment()
         self.overrides = {}
         self.action = action
+        self.target_scanner = None
 
     def targets(self, t):
         return [t]
@@ -801,7 +802,7 @@ class FSTestCase(unittest.TestCase):
         f1.builder_set(Builder(fs.File))
         f1.env_set(Environment())
         xyz = fs.File("xyz")
-        f1.target_scanner = Scanner(xyz)
+        f1.builder.target_scanner = Scanner(xyz)
 
         f1.scan()
         assert f1.implicit[0].path == "xyz"
index f8cd62b7617c4250310a015998f0993f548d08f4..b9b1b1f793cf6448dd65b6e6d37577aff6b68ac0 100644 (file)
@@ -91,12 +91,18 @@ class Environment:
         return apply(Environment, (), d)
     def _update(self, dict):
         self._dict.update(dict)
+    def get_calculator(self):
+        return SCons.Sig.default_calc
+    def get_scanner(self, scanner_key):
+        return self._dict['SCANNERS'][0]
 
 class Builder:
-    def __init__(self):
-        self.env = Environment()
+    def __init__(self, env=None):
+        if env is None: env = Environment()
+        self.env = env
         self.overrides = {}
         self.action = MyAction()
+        self.source_factory = MyNode
     def targets(self, t):
         return [t]
     def get_actions(self):
@@ -704,8 +710,6 @@ class NodeTestCase(unittest.TestCase):
     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)
@@ -715,19 +719,34 @@ class NodeTestCase(unittest.TestCase):
         ts2 = Scanner()
         ts3 = Scanner()
 
-        source.backup_source_scanner = ts1
-        s = target.get_source_scanner(source)
+        class Builder1(Builder):
+            def __call__(self, source):
+                r = SCons.Node.Node()
+                r.builder = self
+                return [r]
+        class Builder2 (Builder1):
+            def __init__(self, source_scanner):
+                self.source_scanner = source_scanner
+
+        builder = Builder2(ts1)
+            
+        targets = builder([source])
+        s = targets[0].get_source_scanner(source)
         assert s is ts1, s
 
-        target.builder = Builder()
+        target.builder_set(Builder2(ts1))
         target.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)
+        builder = Builder1(env=Environment(SCANNERS = [ts3]))
+
+        targets = builder([source])
+        
+        s = targets[0].get_source_scanner(source)
         assert s is ts3, s
 
+
     def test_scan(self):
         """Test Scanner functionality
         """
@@ -739,8 +758,7 @@ class NodeTestCase(unittest.TestCase):
         d = MyNode("ddd")
         node.found_includes = [d]
 
-        assert node.target_scanner == None, node.target_scanner
-        node.target_scanner = s
+        node.builder.target_scanner = s
         assert node.implicit is None
 
         node.scan()
@@ -773,12 +791,14 @@ class NodeTestCase(unittest.TestCase):
         try:
             sn = StoredNode("eee")
             sn._children = ['fake']
-            sn.target_scanner = s
+            sn.builder_set(Builder())
+            sn.builder.target_scanner = s
 
             sn.scan()
 
             assert sn.implicit == [], sn.implicit
-            assert not hasattr(sn, '_children'), "unexpected _children attribute"
+            assert sn._children == [], sn._children
+
         finally:
             SCons.Sig.default_calc = save_default_calc
             SCons.Node.implicit_cache = save_implicit_cache
index f994c6741657cc8f4098f113ff7c861910ccacf6..06bf6d650e2ce9283f33df71f5cecb059a72593f 100644 (file)
@@ -67,7 +67,7 @@ executed = 4
 failed = 5
 stack = 6 # nodes that are in the current Taskmaster execution stack
 
-# controls whether implicit depedencies are cached:
+# controls whether implicit dependencies are cached:
 implicit_cache = 0
 
 # controls whether implicit dep changes are ignored:
@@ -118,9 +118,6 @@ class Node:
         self.implicit = None    # implicit (scanned) dependencies (None means not scanned yet)
         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
-        self.backup_source_scanner = None
 
         self.env = None
         self.state = None
@@ -402,15 +399,17 @@ class 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
+        if not self.has_builder():
+            return None  # if not buildable, can't have sources...
         try:
             scanner = self.builder.source_scanner
             if scanner:
                 return scanner
         except AttributeError:
             pass
-        return node.backup_source_scanner or None
+
+        # No scanner specified by builder, try env['SCANNERS']
+        return self.get_build_env().get_scanner(node.scanner_key())
 
     def scan(self):
         """Scan this node's dependents for implicit dependencies."""
@@ -434,7 +433,7 @@ class Node:
                 implicit = map(self.implicit_factory, implicit)
                 self._add_child(self.implicit, self.implicit_dict, implicit)
                 calc = build_env.get_calculator()
-                if implicit_deps_unchanged or self.current(calc, scan=0):
+                if implicit_deps_unchanged or self.current(calc):
                     return
                 else:
                     # one of this node's sources has changed, so
@@ -452,8 +451,10 @@ class Node:
                 self._add_child(self.implicit, self.implicit_dict, deps)
 
         # scan this node itself for implicit dependencies
-        deps = self.get_implicit_deps(build_env, self.target_scanner, self)
-        self._add_child(self.implicit, self.implicit_dict, deps)
+        scanner = self.builder.target_scanner
+        if scanner:
+            deps = self.get_implicit_deps(build_env, scanner, self)
+            self._add_child(self.implicit, self.implicit_dict, deps)
 
         # XXX See note above re: --implicit-cache.
         #if implicit_cache:
index 281475972944bc75496e2976681eea71d682c0ed..828d198081fbeafff30ea8b4cc32ce4a9c26cd80 100644 (file)
@@ -88,7 +88,6 @@ k2scan = env.Scanner(name = 'k2',
 
 ##########################################################
 # Test scanner as found automatically from the environment
-# (backup_source_scanner)
 
 env = Environment()
 env.Append(SCANNERS = kscan)
@@ -103,14 +102,15 @@ env2.Append(SCANNERS = [k2scan])
 env2.Command('junk', 'junk.k2', r'%(python)s build.py $SOURCES $TARGET')
 
 ##########################################################
-# Test specifying a specific source scanner for a target Node
+# Test specifying a specific source scanner for a Builder
 
-bar = env.Command('bar', 'bar.in', r'%(python)s build.py $SOURCES  $TARGET')
-bar[0].source_scanner = kscan
+bar = env.Command('bar', 'bar.in',
+                  r'%(python)s build.py $SOURCES  $TARGET',
+                  source_scanner=kscan)
 
 ##########################################################
-# Test specifying a source scanner for a Builder that gets
-# automatically applied to targets generated from that Builder
+# Test specifying a source scanner for an intermediary Builder to
+# ensure that the right scanner gets used for the right nodes.
 
 import string