Fix --debug=tree for directory targets (Anthony Roach)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 4 Apr 2002 09:07:00 +0000 (09:07 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 4 Apr 2002 09:07:00 +0000 (09:07 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@317 fdb21ef1-2011-0410-befe-b5e4ea1792b1

23 files changed:
doc/SConscript
doc/man/scons.1
src/CHANGES.txt
src/RELEASE.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
src/engine/SCons/Scanner/C.py
src/engine/SCons/Scanner/CTests.py
src/engine/SCons/Scanner/Prog.py
src/engine/SCons/Scanner/ProgTests.py
src/engine/SCons/Scanner/ScannerTests.py
src/engine/SCons/Scanner/__init__.py
src/engine/SCons/Sig/SigTests.py
src/engine/SCons/Sig/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
test/Scanner.py
test/dependency-cycle.py
test/option--debug.py

index f0cc97f6c7e55583885801c9a809e30913a5a455..898f370e161b99f1fd6924e266bea4cef6b89beb 100644 (file)
@@ -71,7 +71,7 @@ format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format=
 # defined as a SYSTEM entity is, in fact, a file included
 # somewhere in the document.
 #
-def scansgml(node, env, argument = None):
+def scansgml(node, env, target):
     includes = []
 
     contents = node.get_contents()
@@ -83,12 +83,22 @@ def scansgml(node, env, argument = None):
         file, format = m
         if format and file[-len(format):] != format:
             file = file + format
-        if argument and not os.path.isabs(file):
-            file = os.path.join(argument, file)
+        if not os.path.isabs(file):
+            a = []
+            f = file
+            while 1:
+                f, tail = os.path.split(f)
+                if tail == 'doc':
+                    break
+                a = [tail] + a
+            file = apply(os.path.join, a, {})
         includes.append(file)
 
     return includes
 
+s = Scanner(name = 'sgml', function = scansgml, skeys = ['.sgml', '.mod'])
+env = env.Copy(SCANNERS = [s])
+
 if jw:
     #
     # Always create a version.sgml file containing the version information
@@ -119,9 +129,6 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE.  DO NOT EDIT.
                 'ps'        : 1,
                 'pdf'       : 1,
                 'text'      : 0,
-                'scan'      : Scanner(name = 'design',
-                                      function = scansgml,
-                                      argument = 'design'),
         },
         'python10' : {
                 'htmlindex' : 't1.html',
@@ -130,19 +137,13 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE.  DO NOT EDIT.
                 'pdf'       : 0,
                 'text'      : 0,
                 'graphics'  : [ 'arch', 'builder', 'job-task', 'node', 'scanner', 'sig' ],
-                'scan'      : Scanner(name = 'python10',
-                                      function = scansgml,
-                                      argument = 'python10'),
         },
         'user' : {
                 'htmlindex' : 'book1.html',
-                'html'      : 0,
-                'ps'        : 0,
-                'pdf'       : 0,
+                'html'      : 1,
+                'ps'        : 1,
+                'pdf'       : 1,
                 'text'      : 0,
-                'scan'      : Scanner(name = 'user',
-                                      function = scansgml,
-                                      argument = 'user'),
         },
     }
 
@@ -155,11 +156,6 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE.  DO NOT EDIT.
         'copyright.sgml',
     ]
 
-    s = Scanner(name = 'sgml', function = scansgml)
-
-    for sgml in included_sgml:
-        File(sgml).scanner_set(s)
-
     #
     # For each document, build the document itself in HTML, Postscript,
     # and PDF formats.
@@ -175,10 +171,6 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE.  DO NOT EDIT.
         pdf = os.path.join('PDF', 'scons-%s.pdf' % doc)
         text = os.path.join('TEXT', 'scons-%s.txt' % doc)
 
-        s = docs[doc].get('scan')
-        if s:
-            File(main).scanner_set(s)
-
         if docs[doc].get('html') and jade:
             cmds = [
                 "rm -f ${TARGET.dir}/*.html",
index 93086f69cbf7e7905cc77e5bf6397de6f6ef37f5..77dedc5c33bd63883fccf61825969f0f2cd0f2ab 100644 (file)
@@ -1665,9 +1665,11 @@ the Node (file)
 and return a list of strings (file names)
 representing the implicit
 dependencies found in the contents.
-The function takes three arguments:
+The function takes three or four arguments:
 
-    def scanner_function(node, env, arg):
+    def scanner_function(node, env, target):
+
+    def scanner_function(node, env, target, arg):
 
 The
 .B node
@@ -1686,10 +1688,15 @@ Fetch values from it using the
 .B env.Dictionary()
 method.
 
+The
+.B target
+argument is the internal
+SCons node representing the target file.
+
 The
 .B arg
 argument is the argument supplied
-when the scanner was created.
+when the scanner was created, if any.
 
 .SH EXAMPLES
 
@@ -1780,7 +1787,7 @@ import re
 
 include_re = re.compile(r'^include\\s+(\\S+)$', re.M)
 
-def kfile_scan(node, env, arg):
+def kfile_scan(node, env, target, arg):
     contents = node.get_contents()
     includes = include_re.findall(contents)
     return includes
index 0fc473cdfb220aa4d668a1e5316c786973e98a0c..fee7642787edab3b5911ec3f94f4e248fb80224f 100644 (file)
@@ -14,6 +14,12 @@ RELEASE 0.07 -
 
   - Fix so that -c -n does *not* remove the targets!
 
+  From Anthony Roach:
+
+  - Fix --debug=tree when used with directory targets.
+
+  - Significant internal restructuring of Scanners and Taskmaster.
+
 
 
 RELEASE 0.06 - Thu, 28 Mar 2002 01:24:29 -0600
index 9a6a5f88ab8b08e23763c9e2cf6cec64f775b3c4..41cd49bbaba9aa6da8a7685acc85bde7842613c4 100644 (file)
@@ -20,15 +20,16 @@ more effectively, please sign up for the scons-users mailing list at:
 
 
 
-RELEASE 0.06 - Thu, 28 Mar 2002 01:24:29 -0600
+RELEASE 0.07 - 
 
-  This is the sixth alpha release of SCons.  Please consult the
+  This is the seventh alpha release of SCons.  Please consult the
   CHANGES.txt file for a list of specific changes since last release.
 
   Please note the following important changes since the previous
   release:
 
-    - Python functions as Builder actions now take Node objects, not
+    - Scanner functions now take four arguments.
+
       strings, as arguments.  The string representation of a Node
       object is the file name, so you should change your function
       actions to use the str() built-in function to fetch the file
index bc90e197d16140ac6dcfdf05802b549d8eb23409..e7af01ce89fbe04f58aa83a1f07075153ec60fe8 100644 (file)
@@ -41,7 +41,6 @@ import SCons.Node.FS
 import SCons.Util
 
 
-
 def Builder(**kw):
     """A factory for builder objects."""
     
@@ -64,77 +63,72 @@ def _init_nodes(builder, env, tlist, slist):
     """Initialize lists of target and source nodes with all of
     the proper Builder information.
     """
-    src_scanner = None
-    if slist:
-        src_key = slist[0].scanner_key()       # the file suffix
-        src_scanner = env.get_scanner(src_key)
-        if src_scanner:
-            src_scanner = src_scanner.instance(env)
-
+    for s in slist:
+        src_key = slist[0].scanner_key()        # the file suffix
+        scanner = env.get_scanner(src_key)
+        if scanner:
+            s.source_scanner = scanner
+            
     for t in tlist:
-        t.cwd = SCons.Node.FS.default_fs.getcwd()      # XXX
+        t.cwd = SCons.Node.FS.default_fs.getcwd()       # XXX
         t.builder_set(builder)
         t.env_set(env)
         t.add_source(slist)
         if builder.scanner:
-            t.scanner_set(builder.scanner.instance(env))
-        if src_scanner:
-            t.src_scanner_set(src_key, src_scanner)
-
-
+            t.target_scanner = builder.scanner
 
 class BuilderBase:
     """Base class for Builders, objects that create output
     nodes (files) from input nodes (files).
     """
 
-    def __init__(self, name = None,
-                       action = None,
-                       prefix = '',
-                       suffix = '',
-                       src_suffix = '',
+    def __init__(self,  name = None,
+                        action = None,
+                        prefix = '',
+                        suffix = '',
+                        src_suffix = '',
                         node_factory = SCons.Node.FS.default_fs.File,
                         target_factory = None,
                         source_factory = None,
                         scanner = None):
         if name is None:
             raise UserError, "You must specify a name for the builder."
-       self.name = name
-       self.action = SCons.Action.Action(action)
+        self.name = name
+        self.action = SCons.Action.Action(action)
 
-       self.prefix = prefix
-       self.suffix = suffix
-       self.src_suffix = src_suffix
+        self.prefix = prefix
+        self.suffix = suffix
+        self.src_suffix = src_suffix
         self.target_factory = target_factory or node_factory
         self.source_factory = source_factory or node_factory
         self.scanner = scanner
         if self.suffix and self.suffix[0] not in '.$':
-           self.suffix = '.' + self.suffix
+            self.suffix = '.' + self.suffix
         if self.src_suffix and self.src_suffix[0] not in '.$':
-           self.src_suffix = '.' + self.src_suffix
+            self.src_suffix = '.' + self.src_suffix
 
     def __cmp__(self, other):
-       return cmp(self.__dict__, other.__dict__)
+        return cmp(self.__dict__, other.__dict__)
 
     def _create_nodes(self, env, target = None, source = None):
         """Create and return lists of target and source nodes.
         """
-       def adjustixes(files, pre, suf):
-           ret = []
+        def adjustixes(files, pre, suf):
+            ret = []
             if SCons.Util.is_String(files):
                 files = string.split(files)
             if not SCons.Util.is_List(files):
-               files = [files]
-           for f in files:
+                files = [files]
+            for f in files:
                 if SCons.Util.is_String(f):
-                   if pre and f[:len(pre)] != pre:
+                    if pre and f[:len(pre)] != pre:
                         path, fn = os.path.split(os.path.normpath(f))
                         f = os.path.join(path, pre + fn)
-                   if suf:
-                       if f[-len(suf):] != suf:
-                           f = f + suf
-               ret.append(f)
-           return ret
+                    if suf:
+                        if f[-len(suf):] != suf:
+                            f = f + suf
+                ret.append(f)
+            return ret
 
         tlist = SCons.Node.arg2nodes(adjustixes(target,
                                                 env.subst(self.prefix),
@@ -160,9 +154,9 @@ class BuilderBase:
 
 
     def execute(self, **kw):
-       """Execute a builder's action to create an output object.
-       """
-       return apply(self.action.execute, (), kw)
+        """Execute a builder's action to create an output object.
+        """
+        return apply(self.action.execute, (), kw)
 
     def get_raw_contents(self, **kw):
         """Fetch the "contents" of the builder's action.
@@ -241,10 +235,10 @@ class MultiStepBuilder(BuilderBase):
     """
     def __init__(self,  src_builder,
                         name = None,
-                       action = None,
-                       prefix = '',
-                       suffix = '',
-                       src_suffix = '',
+                        action = None,
+                        prefix = '',
+                        suffix = '',
+                        src_suffix = '',
                         node_factory = SCons.Node.FS.default_fs.File,
                         target_factory = None,
                         source_factory = None,
index 16c87a4d2571148bb395ea220b61f344bd830357..0527fe0376fc9ac1ed2b8822a205f96a9a463f11 100644 (file)
@@ -65,7 +65,6 @@ outfile = test.workpath('outfile')
 outfile2 = test.workpath('outfile2')
 
 show_string = None
-instanced = None
 env_scanner = None
 count = 0
 
@@ -88,31 +87,31 @@ env = Environment()
 class BuilderTestCase(unittest.TestCase):
 
     def test__call__(self):
-       """Test calling a builder to establish source dependencies
-       """
-       class Node:
-           def __init__(self, name):
-               self.name = name
-               self.sources = []
-               self.builder = None
-           def __str__(self):
-               return self.name
-           def builder_set(self, builder):
-               self.builder = builder
-           def env_set(self, env, safe=0):
-               self.env = env
-           def add_source(self, source):
-               self.sources.extend(source)
+        """Test calling a builder to establish source dependencies
+        """
+        class Node:
+            def __init__(self, name):
+                self.name = name
+                self.sources = []
+                self.builder = None
+            def __str__(self):
+                return self.name
+            def builder_set(self, builder):
+                self.builder = builder
+            def env_set(self, env, safe=0):
+                self.env = env
+            def add_source(self, source):
+                self.sources.extend(source)
             def scanner_key(self):
                 return self.name
         builder = SCons.Builder.Builder(name="builder", action="foo", node_factory=Node)
 
-       n1 = Node("n1");
-       n2 = Node("n2");
-       builder(env, target = n1, source = n2)
-       assert n1.env == env
-       assert n1.builder == builder
-       assert n1.sources == [n2]
+        n1 = Node("n1");
+        n2 = Node("n2");
+        builder(env, target = n1, source = n2)
+        assert n1.env == env
+        assert n1.builder == builder
+        assert n1.sources == [n2]
         assert not hasattr(n2, 'env')
 
         target = builder(env, target = 'n3', source = 'n4')
@@ -159,12 +158,12 @@ class BuilderTestCase(unittest.TestCase):
             assert 0
 
     def test_action(self):
-       """Test Builder creation
+        """Test Builder creation
 
-       Verify that we can retrieve the supplied action attribute.
-       """
-       builder = SCons.Builder.Builder(name="builder", action="foo")
-       assert builder.action.command == "foo"
+        Verify that we can retrieve the supplied action attribute.
+        """
+        builder = SCons.Builder.Builder(name="builder", action="foo")
+        assert builder.action.command == "foo"
 
     def test_generator(self):
         """Test Builder creation given a generator function."""
@@ -176,22 +175,22 @@ class BuilderTestCase(unittest.TestCase):
         assert builder.action.generator == generator
 
     def test_cmp(self):
-       """Test simple comparisons of Builder objects
-       """
-       b1 = SCons.Builder.Builder(name="b1", src_suffix = '.o')
-       b2 = SCons.Builder.Builder(name="b1", src_suffix = '.o')
-       assert b1 == b2
-       b3 = SCons.Builder.Builder(name="b3", src_suffix = '.x')
-       assert b1 != b3
-       assert b2 != b3
+        """Test simple comparisons of Builder objects
+        """
+        b1 = SCons.Builder.Builder(name="b1", src_suffix = '.o')
+        b2 = SCons.Builder.Builder(name="b1", src_suffix = '.o')
+        assert b1 == b2
+        b3 = SCons.Builder.Builder(name="b3", src_suffix = '.x')
+        assert b1 != b3
+        assert b2 != b3
 
     def test_execute(self):
-       """Test execution of simple Builder objects
-       
-       One Builder is a string that executes an external command,
-       one is an internal Python function, one is a list
-       containing one of each.
-       """
+        """Test execution of simple Builder objects
+        
+        One Builder is a string that executes an external command,
+        one is an internal Python function, one is a list
+        containing one of each.
+        """
 
         def MyBuilder(**kw):
             builder = apply(SCons.Builder.Builder, (), kw)
@@ -200,54 +199,54 @@ class BuilderTestCase(unittest.TestCase):
             builder.action.show = no_show
             return builder
 
-       python = sys.executable
+        python = sys.executable
 
-       cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
+        cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
 
         builder = MyBuilder(action = cmd1, name = "cmd1")
-       r = builder.execute()
-       assert r == 0
-       c = test.read(outfile, 'r')
+        r = builder.execute()
+        assert r == 0
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'xyzzy'\n", c
 
-       cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
+        cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
 
         builder = MyBuilder(action = cmd2, name = "cmd2")
-       r = builder.execute(target = 'foo')
-       assert r == 0
-       c = test.read(outfile, 'r')
+        r = builder.execute(target = 'foo')
+        assert r == 0
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'foo'\n", c
 
-       cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
+        cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
 
         builder = MyBuilder(action = cmd3, name = "cmd3")
-       r = builder.execute(target = ['aaa', 'bbb'])
-       assert r == 0
-       c = test.read(outfile, 'r')
+        r = builder.execute(target = ['aaa', 'bbb'])
+        assert r == 0
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'aaa' 'bbb'\n", c
 
-       cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
+        cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
 
         builder = MyBuilder(action = cmd4, name = "cmd4")
-       r = builder.execute(source = ['one', 'two'])
-       assert r == 0
-       c = test.read(outfile, 'r')
+        r = builder.execute(source = ['one', 'two'])
+        assert r == 0
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'one' 'two'\n", c
 
-       cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
+        cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
 
         builder = MyBuilder(action = cmd4, name = "cmd4")
-       r = builder.execute(source = ['three', 'four', 'five'])
-       assert r == 0
-       c = test.read(outfile, 'r')
+        r = builder.execute(source = ['three', 'four', 'five'])
+        assert r == 0
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'three' 'four'\n", c
 
-       cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
+        cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
 
         builder = MyBuilder(action = cmd5, name = "cmd5")
-       r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}})
-       assert r == 0
-       c = test.read(outfile, 'r')
+        r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}})
+        assert r == 0
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c
 
         class Obj:
@@ -285,16 +284,16 @@ class BuilderTestCase(unittest.TestCase):
 
         global count
         count = 0
-       def function1(**kw):
+        def function1(**kw):
             global count
             count = count + 1
             if not type(kw['target']) is type([]):
                 kw['target'] = [ kw['target'] ]
             for t in kw['target']:
-               open(t, 'w').write("function1\n")
-           return 1
+                open(t, 'w').write("function1\n")
+            return 1
 
-       builder = MyBuilder(action = function1, name = "function1")
+        builder = MyBuilder(action = function1, name = "function1")
         try:
             r = builder.execute(target = [outfile, outfile2])
         except SCons.Errors.BuildError:
@@ -302,50 +301,50 @@ class BuilderTestCase(unittest.TestCase):
         assert r == 1
         assert count == 1
         c = test.read(outfile, 'r')
-       assert c == "function1\n", c
+        assert c == "function1\n", c
         c = test.read(outfile2, 'r')
-       assert c == "function1\n", c
-
-       class class1a:
-           def __init__(self, **kw):
-               open(kw['out'], 'w').write("class1a\n")
-
-       builder = MyBuilder(action = class1a, name = "class1a")
-       r = builder.execute(out = outfile)
-       assert r.__class__ == class1a
-       c = test.read(outfile, 'r')
-       assert c == "class1a\n", c
-
-       class class1b:
-           def __call__(self, **kw):
-               open(kw['out'], 'w').write("class1b\n")
-               return 2
-
-       builder = MyBuilder(action = class1b(), name = "class1b")
-       r = builder.execute(out = outfile)
-       assert r == 2
-       c = test.read(outfile, 'r')
-       assert c == "class1b\n", c
-
-       cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
-
-       def function2(**kw):
-           open(kw['out'], 'a').write("function2\n")
-           return 0
-
-       class class2a:
-           def __call__(self, **kw):
-               open(kw['out'], 'a').write("class2a\n")
-               return 0
-
-       class class2b:
-           def __init__(self, **kw):
-               open(kw['out'], 'a').write("class2b\n")
-
-       builder = MyBuilder(action = [cmd2, function2, class2a(), class2b], name = "clist")
-       r = builder.execute(out = outfile)
-       assert r.__class__ == class2b
-       c = test.read(outfile, 'r')
+        assert c == "function1\n", c
+
+        class class1a:
+            def __init__(self, **kw):
+                open(kw['out'], 'w').write("class1a\n")
+
+        builder = MyBuilder(action = class1a, name = "class1a")
+        r = builder.execute(out = outfile)
+        assert r.__class__ == class1a
+        c = test.read(outfile, 'r')
+        assert c == "class1a\n", c
+
+        class class1b:
+            def __call__(self, **kw):
+                open(kw['out'], 'w').write("class1b\n")
+                return 2
+
+        builder = MyBuilder(action = class1b(), name = "class1b")
+        r = builder.execute(out = outfile)
+        assert r == 2
+        c = test.read(outfile, 'r')
+        assert c == "class1b\n", c
+
+        cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
+
+        def function2(**kw):
+            open(kw['out'], 'a').write("function2\n")
+            return 0
+
+        class class2a:
+            def __call__(self, **kw):
+                open(kw['out'], 'a').write("class2a\n")
+                return 0
+
+        class class2b:
+            def __init__(self, **kw):
+                open(kw['out'], 'a').write("class2b\n")
+
+        builder = MyBuilder(action = [cmd2, function2, class2a(), class2b], name = "clist")
+        r = builder.execute(out = outfile)
+        assert r.__class__ == class2b
+        c = test.read(outfile, 'r')
         assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
 
         if os.name == 'nt':
@@ -390,53 +389,53 @@ class BuilderTestCase(unittest.TestCase):
         assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
 
     def test_node_factory(self):
-       """Test a Builder that creates nodes of a specified class
-       """
-       class Foo:
-           pass
-       def FooFactory(target):
+        """Test a Builder that creates nodes of a specified class
+        """
+        class Foo:
+            pass
+        def FooFactory(target):
             global Foo
-           return Foo(target)
-       builder = SCons.Builder.Builder(name = "builder", node_factory = FooFactory)
-       assert builder.target_factory is FooFactory
-       assert builder.source_factory is FooFactory
+            return Foo(target)
+        builder = SCons.Builder.Builder(name = "builder", node_factory = FooFactory)
+        assert builder.target_factory is FooFactory
+        assert builder.source_factory is FooFactory
 
     def test_target_factory(self):
-       """Test a Builder that creates target nodes of a specified class
-       """
-       class Foo:
-           pass
-       def FooFactory(target):
+        """Test a Builder that creates target nodes of a specified class
+        """
+        class Foo:
+            pass
+        def FooFactory(target):
             global Foo
-           return Foo(target)
-       builder = SCons.Builder.Builder(name = "builder", target_factory = FooFactory)
-       assert builder.target_factory is FooFactory
-       assert not builder.source_factory is FooFactory
+            return Foo(target)
+        builder = SCons.Builder.Builder(name = "builder", target_factory = FooFactory)
+        assert builder.target_factory is FooFactory
+        assert not builder.source_factory is FooFactory
 
     def test_source_factory(self):
-       """Test a Builder that creates source nodes of a specified class
-       """
-       class Foo:
-           pass
-       def FooFactory(source):
+        """Test a Builder that creates source nodes of a specified class
+        """
+        class Foo:
+            pass
+        def FooFactory(source):
             global Foo
-           return Foo(source)
-       builder = SCons.Builder.Builder(name = "builder", source_factory = FooFactory)
-       assert not builder.target_factory is FooFactory
-       assert builder.source_factory is FooFactory
+            return Foo(source)
+        builder = SCons.Builder.Builder(name = "builder", source_factory = FooFactory)
+        assert not builder.target_factory is FooFactory
+        assert builder.source_factory is FooFactory
 
     def test_prefix(self):
-       """Test Builder creation with a specified target prefix
-
-       Make sure that there is no '.' separator appended.
-       """
-       builder = SCons.Builder.Builder(name = "builder", prefix = 'lib.')
-       assert builder.prefix == 'lib.'
-       builder = SCons.Builder.Builder(name = "builder", prefix = 'lib')
-       assert builder.prefix == 'lib'
-       tgt = builder(env, target = 'tgt1', source = 'src1')
-       assert tgt.path == 'libtgt1', \
-               "Target has unexpected name: %s" % tgt.path
+        """Test Builder creation with a specified target prefix
+
+        Make sure that there is no '.' separator appended.
+        """
+        builder = SCons.Builder.Builder(name = "builder", prefix = 'lib.')
+        assert builder.prefix == 'lib.'
+        builder = SCons.Builder.Builder(name = "builder", prefix = 'lib')
+        assert builder.prefix == 'lib'
+        tgt = builder(env, target = 'tgt1', source = 'src1')
+        assert tgt.path == 'libtgt1', \
+                "Target has unexpected name: %s" % tgt.path
         tgts = builder(env, target = 'tgt2a tgt2b', source = 'src2')
         assert tgts[0].path == 'libtgt2a', \
                 "Target has unexpected name: %s" % tgts[0].path
@@ -482,18 +481,18 @@ class BuilderTestCase(unittest.TestCase):
         assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
 
     def test_suffix(self):
-       """Test Builder creation with a specified target suffix
-
-       Make sure that the '.' separator is appended to the
-       beginning if it isn't already present.
-       """
-       builder = SCons.Builder.Builder(name = "builder", suffix = '.o')
-       assert builder.suffix == '.o'
-       builder = SCons.Builder.Builder(name = "builder", suffix = 'o')
-       assert builder.suffix == '.o'
-       tgt = builder(env, target = 'tgt3', source = 'src3')
-       assert tgt.path == 'tgt3.o', \
-               "Target has unexpected name: %s" % tgt[0].path
+        """Test Builder creation with a specified target suffix
+
+        Make sure that the '.' separator is appended to the
+        beginning if it isn't already present.
+        """
+        builder = SCons.Builder.Builder(name = "builder", suffix = '.o')
+        assert builder.suffix == '.o'
+        builder = SCons.Builder.Builder(name = "builder", suffix = 'o')
+        assert builder.suffix == '.o'
+        tgt = builder(env, target = 'tgt3', source = 'src3')
+        assert tgt.path == 'tgt3.o', \
+                "Target has unexpected name: %s" % tgt[0].path
         tgts = builder(env, target = 'tgt4a tgt4b', source = 'src4')
         assert tgts[0].path == 'tgt4a.o', \
                 "Target has unexpected name: %s" % tgts[0].path
@@ -517,9 +516,9 @@ class BuilderTestCase(unittest.TestCase):
 
         builder = SCons.Builder.Builder(action = function2, name = "function2")
         tgts = builder(env, target = [outfile, outfile2], source = 'foo')
-       try:
+        try:
             r = tgts[0].builder.execute(target = tgts)
-       except SCons.Errors.BuildError:
+        except SCons.Errors.BuildError:
             pass
         c = test.read(outfile, 'r')
         assert c == "function2\n", c
@@ -652,17 +651,12 @@ class BuilderTestCase(unittest.TestCase):
         """Testing ability to set a target scanner through a builder."""
         global instanced
         class TestScanner:
-            def instance(self, env):
-                global instanced
-                instanced = 1
-                return self
+            pass
         scn = TestScanner()
         builder = SCons.Builder.Builder(name = "builder", scanner=scn)
         tgt = builder(env, target='foo', source='bar')
-        assert tgt.scanner == scn, tgt.scanner
-        assert instanced
+        assert tgt.target_scanner == scn, tgt.target_scanner
 
-        instanced = None
         builder1 = SCons.Builder.Builder(name = "builder1",
                                          action='foo',
                                          src_suffix='.bar',
@@ -672,8 +666,7 @@ class BuilderTestCase(unittest.TestCase):
                                          src_builder = builder1,
                                          scanner = scn)
         tgt = builder2(env, target='baz', source='test.bar test2.foo test3.txt')
-        assert tgt.scanner == scn, tgt.scanner
-        assert instanced
+        assert tgt.target_scanner == scn, tgt.target_scanner
 
     def test_src_scanner(slf):
         """Testing ability to set a source file scanner through a builder."""
@@ -686,10 +679,11 @@ class BuilderTestCase(unittest.TestCase):
         env_scanner = TestScanner()
         builder = SCons.Builder.Builder(name = "builder", action='action')
         tgt = builder(env, target='foo.x', source='bar')
-        assert tgt.scanner != env_scanner, tgt.scanner
-        assert tgt.src_scanners[''] == env_scanner, tgt.src_scanners
+        src = tgt.sources[0]
+        assert tgt.target_scanner != env_scanner, tgt.target_scanner
+        assert src.source_scanner == env_scanner
 
 if __name__ == "__main__":
     suite = unittest.makeSuite(BuilderTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
-       sys.exit(1)
+        sys.exit(1)
index 1be5e8135bc8fd4ed3a1c42599b6b468f061041d..0f8425ff6a9f5ccf222d228ab241821bb8107286 100644 (file)
@@ -114,7 +114,7 @@ class FS:
         
     def getcwd(self):
         self.__setTopLevelDir()
-       return self._cwd
+        return self._cwd
 
     def __checkClass(self, node, klass):
         if klass == Entry:
@@ -132,9 +132,9 @@ class FS:
         """This method differs from the File and Dir factory methods in
         one important way: the meaning of the directory parameter.
         In this method, if directory is None or not supplied, the supplied
-       name is expected to be an absolute path.  If you try to look up a
-       relative path with directory=None, then an AssertionError will be
-       raised."""
+        name is expected to be an absolute path.  If you try to look up a
+        relative path with directory=None, then an AssertionError will be
+        raised."""
 
         if not name:
             # This is a stupid hack to compensate for the fact
@@ -280,12 +280,12 @@ class Entry(SCons.Node.Node):
     """
 
     def __init__(self, name, directory):
-       """Initialize a generic file system Entry.
-       
-       Call the superclass initialization, take care of setting up
-       our relative and absolute paths, identify our parent
-       directory, and indicate that this node should use
-       signatures."""
+        """Initialize a generic file system Entry.
+        
+        Call the superclass initialization, take care of setting up
+        our relative and absolute paths, identify our parent
+        directory, and indicate that this node should use
+        signatures."""
         SCons.Node.Node.__init__(self)
 
         self.name = name
@@ -302,9 +302,10 @@ class Entry(SCons.Node.Node):
         self.path_ = self.path
         self.abspath_ = self.abspath
         self.dir = directory
-       self.use_signature = 1
+        self.use_signature = 1
         self.__doSrcpath(self.duplicate)
         self.srcpath_ = self.srcpath
+        self.cwd = None # will hold the SConscript directory for target nodes
 
     def get_dir(self):
         return self.dir
@@ -320,7 +321,7 @@ class Entry(SCons.Node.Node):
             self.srcpath = self.dir.srcpath_ + self.name
 
     def __str__(self):
-       """A FS node's string representation is its path name."""
+        """A FS node's string representation is its path name."""
         if self.duplicate or self.builder:
             return self.path
         else:
@@ -374,17 +375,17 @@ class Dir(Entry):
 
     def __init__(self, name, directory):
         Entry.__init__(self, name, directory)
-       self._morph()
+        self._morph()
 
     def _morph(self):
-       """Turn a file system node (either a freshly initialized
-       directory object or a separate Entry object) into a
-       proper directory object.
-       
-       Modify our paths to add the trailing slash that indicates
-       a directory.  Set up this directory's entries and hook it
-       into the file system tree.  Specify that directories (this
-       node) don't use signatures for currency calculation."""
+        """Turn a file system node (either a freshly initialized
+        directory object or a separate Entry object) into a
+        proper directory object.
+        
+        Modify our paths to add the trailing slash that indicates
+        a directory.  Set up this directory's entries and hook it
+        into the file system tree.  Specify that directories (this
+        node) don't use signatures for currency calculation."""
 
         self.path_ = self.path + os.sep
         self.abspath_ = self.abspath + os.sep
@@ -423,18 +424,18 @@ class Dir(Entry):
         else:
             return self.entries['..'].root()
 
-    def children(self, scanner):
-       #XXX --random:  randomize "dependencies?"
-       keys = filter(lambda k: k != '.' and k != '..', self.entries.keys())
-       kids = map(lambda x, s=self: s.entries[x], keys)
-       def c(one, two):
+    def all_children(self, scanner):
+        #XXX --random:  randomize "dependencies?"
+        keys = filter(lambda k: k != '.' and k != '..', self.entries.keys())
+        kids = map(lambda x, s=self: s.entries[x], keys)
+        def c(one, two):
             if one.abspath < two.abspath:
                return -1
             if one.abspath > two.abspath:
                return 1
             return 0
-       kids.sort(c)
-       return kids
+        kids.sort(c)
+        return kids
 
     def build(self):
         """A null "builder" for directories."""
@@ -529,14 +530,12 @@ class File(Entry):
         .sconsign entry."""
         return self.dir.sconsign().get(self.name)
 
-    def scan(self, scanner = None):
-        if not scanner:
-            scanner = self.scanner
-        if scanner and not self.scanned.has_key(scanner):
-            deps = scanner.scan(self, self.env)
-            self.add_implicit(deps, scanner)
-            self.scanned[scanner] = 1
-                    
+    def get_implicit_deps(self, env, scanner, target):
+        if scanner:
+            return scanner.scan(self, env, target)
+        else:
+            return []
+
     def exists(self):
         if self.duplicate and not self.created:
             self.created = 1
index 98abcfc75c490fdaa1d10db7eadcfb86f1f02e6a..fe9ea616f92f367d134e09d292b96b502b6842df 100644 (file)
@@ -48,7 +48,7 @@ class Scanner:
         global scanner_count
         scanner_count = scanner_count + 1
         self.hash = scanner_count
-    def scan(self, node, env):
+    def scan(self, node, env, target):
         return [node]
     def __hash__(self):
         return self.hash
@@ -191,7 +191,7 @@ class FSTestCase(unittest.TestCase):
                 up_path = strip_slash(up_path_)
                 name = string.split(abspath, os.sep)[-1]
 
-               assert dir.name == name, \
+                assert dir.name == name, \
                        "dir.name %s != expected name %s" % \
                        (dir.name, name)
                 assert dir.path == path, \
@@ -230,16 +230,16 @@ class FSTestCase(unittest.TestCase):
             try:
                 f2 = fs.File(string.join(['f1', 'f2'], sep), directory = d1)
             except TypeError, x:
-               assert str(x) == ("Tried to lookup File '%s' as a Dir." %
-                                 d1_f1), x
+                assert str(x) == ("Tried to lookup File '%s' as a Dir." %
+                                  d1_f1), x
             except:
                 raise
 
             try:
                 dir = fs.Dir(string.join(['d1', 'f1'], sep))
             except TypeError, x:
-               assert str(x) == ("Tried to lookup File '%s' as a Dir." %
-                                 d1_f1), x
+                assert str(x) == ("Tried to lookup File '%s' as a Dir." %
+                                  d1_f1), x
             except:
                 raise
 
@@ -249,7 +249,7 @@ class FSTestCase(unittest.TestCase):
                 assert str(x) == ("Tried to lookup Dir '%s' as a File." %
                                   'd1'), x
             except:
-               raise
+                raise
 
             # Test Dir.children()
             dir = fs.Dir('ddd')
@@ -262,9 +262,9 @@ class FSTestCase(unittest.TestCase):
             kids = map(lambda x: x.path, dir.children(None))
             kids.sort()
             assert kids == [os.path.join('ddd', 'd1'),
-                           os.path.join('ddd', 'f1'),
-                           os.path.join('ddd', 'f2'),
-                           os.path.join('ddd', 'f3')]
+                            os.path.join('ddd', 'f1'),
+                            os.path.join('ddd', 'f2'),
+                            os.path.join('ddd', 'f3')]
             kids = map(lambda x: x.path_, dir.children(None))
             kids.sort()
             assert kids == [os.path.join('ddd', 'd1', ''),
@@ -302,50 +302,50 @@ class FSTestCase(unittest.TestCase):
             expect = string.replace(expect, '/', os.sep)
             assert path == expect, "path %s != expected %s" % (path, expect)
 
-       e1 = fs.Entry("d1")
-       assert e1.__class__.__name__ == 'Dir'
+        e1 = fs.Entry("d1")
+        assert e1.__class__.__name__ == 'Dir'
         match(e1.path, "d1")
         match(e1.path_, "d1/")
         match(e1.dir.path, ".")
 
-       e2 = fs.Entry("d1/f1")
-       assert e2.__class__.__name__ == 'File'
+        e2 = fs.Entry("d1/f1")
+        assert e2.__class__.__name__ == 'File'
         match(e2.path, "d1/f1")
         match(e2.path_, "d1/f1")
         match(e2.dir.path, "d1")
 
-       e3 = fs.Entry("e3")
-       assert e3.__class__.__name__ == 'Entry'
+        e3 = fs.Entry("e3")
+        assert e3.__class__.__name__ == 'Entry'
         match(e3.path, "e3")
         match(e3.path_, "e3")
         match(e3.dir.path, ".")
 
-       e4 = fs.Entry("d1/e4")
-       assert e4.__class__.__name__ == 'Entry'
+        e4 = fs.Entry("d1/e4")
+        assert e4.__class__.__name__ == 'Entry'
         match(e4.path, "d1/e4")
         match(e4.path_, "d1/e4")
         match(e4.dir.path, "d1")
 
-       e5 = fs.Entry("e3/e5")
-       assert e3.__class__.__name__ == 'Dir'
+        e5 = fs.Entry("e3/e5")
+        assert e3.__class__.__name__ == 'Dir'
         match(e3.path, "e3")
         match(e3.path_, "e3/")
         match(e3.dir.path, ".")
-       assert e5.__class__.__name__ == 'Entry'
+        assert e5.__class__.__name__ == 'Entry'
         match(e5.path, "e3/e5")
         match(e5.path_, "e3/e5")
         match(e5.dir.path, "e3")
 
-       e6 = fs.Dir("d1/e4")
-       assert e6 is e4
-       assert e4.__class__.__name__ == 'Dir'
+        e6 = fs.Dir("d1/e4")
+        assert e6 is e4
+        assert e4.__class__.__name__ == 'Dir'
         match(e4.path, "d1/e4")
         match(e4.path_, "d1/e4/")
         match(e4.dir.path, "d1")
 
-       e7 = fs.File("e3/e5")
-       assert e7 is e5
-       assert e5.__class__.__name__ == 'File'
+        e7 = fs.File("e3/e5")
+        assert e7 is e5
+        assert e5.__class__.__name__ == 'File'
         match(e5.path, "e3/e5")
         match(e5.path_, "e3/e5")
         match(e5.dir.path, "e3")
@@ -383,23 +383,12 @@ class FSTestCase(unittest.TestCase):
         match(e13.path, "subdir/subdir/e13")
 
         # Test scanning
-        scn1 = Scanner()
-        f1.scan(scn1)
-        assert f1.implicit[scn1][0].path_ == os.path.join("d1", "f1")
-        del f1.implicit[scn1]
-        f1.scan(scn1)
-        assert len(f1.implicit) == 0, f1.implicit
-        del f1.scanned[scn1]
-        f1.scan(scn1)
-        assert f1.implicit[scn1][0].path_ == os.path.join("d1", "f1")
-
-        # Test multiple scanners
-        scn2 = Scanner()
-        f2 = fs.File("f2")
-        f2.scan(scn1)
-        f2.scan(scn2)
-        match(f2.implicit[scn1][0].path_, 'subdir/f2')
-        match(f2.implicit[scn2][0].path_, 'subdir/f2')
+        f1.target_scanner = Scanner()
+        f1.scan()
+        assert f1.implicit[0].path_ == os.path.join("d1", "f1")
+        f1.implicit = []
+        f1.scan()
+        assert f1.implicit[0].path_ == os.path.join("d1", "f1")
 
         # Test building a file whose directory is not there yet...
         f1 = fs.File(test.workpath("foo/bar/baz/ack"))
@@ -410,13 +399,13 @@ class FSTestCase(unittest.TestCase):
 
         os.chdir('..')
 
-       # Test getcwd()
+        # Test getcwd()
         fs = SCons.Node.FS.FS()
-       assert str(fs.getcwd()) == ".", str(fs.getcwd())
-       fs.chdir(fs.Dir('subdir'))
-       assert str(fs.getcwd()) == "subdir", str(fs.getcwd())
-       fs.chdir(fs.Dir('../..'))
-       assert str(fs.getcwd()) == test.workdir, str(fs.getcwd())
+        assert str(fs.getcwd()) == ".", str(fs.getcwd())
+        fs.chdir(fs.Dir('subdir'))
+        assert str(fs.getcwd()) == "subdir", str(fs.getcwd())
+        fs.chdir(fs.Dir('../..'))
+        assert str(fs.getcwd()) == test.workdir, str(fs.getcwd())
         
         f1 = fs.File(test.workpath("do_i_exist"))
         assert not f1.exists()
index 16c55485634005ebcc0f38ccc349262960508d8f..ef98a4129d0fd2d8c4df2d57474a77a0ee316ab5 100644 (file)
@@ -41,7 +41,7 @@ cycle_detected = None
 class Builder:
     def execute(self, **kw):
         global built_it, built_target, built_source
-       built_it = 1
+        built_it = 1
         built_target = kw['target']
         built_source = kw['source']
         return 0
@@ -87,17 +87,17 @@ class Environment:
 class NodeTestCase(unittest.TestCase):
 
     def test_BuildException(self):
-       """Test throwing an exception on build failure.
-       """
-       node = SCons.Node.Node()
-       node.builder_set(FailBuilder())
-       node.env_set(Environment())
-       try:
-           node.build()
-       except SCons.Errors.BuildError:
-           pass
-       else:
-           raise TestFailed, "did not catch expected BuildError"
+        """Test throwing an exception on build failure.
+        """
+        node = SCons.Node.Node()
+        node.builder_set(FailBuilder())
+        node.env_set(Environment())
+        try:
+            node.build()
+        except SCons.Errors.BuildError:
+            pass
+        else:
+            raise TestFailed, "did not catch expected BuildError"
 
         node = SCons.Node.Node()
         node.builder_set(ExceptBuilder())
@@ -126,8 +126,8 @@ class NodeTestCase(unittest.TestCase):
             raise TestFailed, "did not catch expected BuildError"
 
     def test_build(self):
-       """Test building a node
-       """
+        """Test building a node
+        """
         global built_it
 
         class MyNode(SCons.Node.Node):
@@ -138,18 +138,18 @@ class NodeTestCase(unittest.TestCase):
                 return self.path
             def prepare(self):
                 self.prepare_count = self.prepare_count+ 1
-       # Make sure it doesn't blow up if no builder is set.
+        # Make sure it doesn't blow up if no builder is set.
         node = MyNode()
-       node.build()
-       assert built_it == None
+        node.build()
+        assert built_it == None
 
         node = MyNode()
-       node.builder_set(Builder())
-       node.env_set(Environment())
+        node.builder_set(Builder())
+        node.env_set(Environment())
         node.path = "xxx"
         node.sources = ["yyy", "zzz"]
-       node.build()
-       assert built_it
+        node.build()
+        assert built_it
         assert type(built_target) == type(MyNode()), type(built_target)
         assert str(built_target) == "xxx", str(built_target)
         assert built_source == ["yyy", "zzz"], built_source
@@ -201,13 +201,19 @@ class NodeTestCase(unittest.TestCase):
         assert str(built_target) == "fff", str(built_target)
         assert built_source == ["hhh", "iii"], built_source
 
+    def test_depends_on(self):
+        parent = SCons.Node.Node()
+        child = SCons.Node.Node()
+        parent.add_dependency([child])
+        assert parent.depends_on([child])
+        
     def test_builder_set(self):
-       """Test setting a Node's Builder
-       """
-       node = SCons.Node.Node()
-       b = Builder()
-       node.builder_set(b)
-       assert node.builder == b
+        """Test setting a Node's Builder
+        """
+        node = SCons.Node.Node()
+        b = Builder()
+        node.builder_set(b)
+        assert node.builder == b
 
     def test_builder_sig_adapter(self):
         """Test the node's adapter for builder signatures
@@ -225,12 +231,12 @@ class NodeTestCase(unittest.TestCase):
         assert node.current() is None
 
     def test_env_set(self):
-       """Test setting a Node's Environment
-       """
-       node = SCons.Node.Node()
-       e = Environment()
-       node.env_set(e)
-       assert node.env == e
+        """Test setting a Node's Environment
+        """
+        node = SCons.Node.Node()
+        e = Environment()
+        node.env_set(e)
+        assert node.env == e
 
     def test_set_bsig(self):
         """Test setting a Node's signature
@@ -276,16 +282,16 @@ class NodeTestCase(unittest.TestCase):
         assert node.precious == 7
 
     def test_add_dependency(self):
-       """Test adding dependencies to a Node's list.
-       """
-       node = SCons.Node.Node()
-       assert node.depends == []
+        """Test adding dependencies to a Node's list.
+        """
+        node = SCons.Node.Node()
+        assert node.depends == []
 
         zero = SCons.Node.Node()
         try:
-           node.add_dependency(zero)
-       except TypeError:
-           pass
+            node.add_dependency(zero)
+        except TypeError:
+            pass
         else:
             assert 0
 
@@ -309,42 +315,10 @@ class NodeTestCase(unittest.TestCase):
 
 
     def test_add_source(self):
-       """Test adding sources to a Node's list.
-       """
-       node = SCons.Node.Node()
-       assert node.sources == []
-
-        zero = SCons.Node.Node()
-       try:
-           node.add_source(zero)
-       except TypeError:
-           pass
-        else:
-            assert 0
-
-        one = SCons.Node.Node()
-        two = SCons.Node.Node()
-        three = SCons.Node.Node()
-        four = SCons.Node.Node()
-
-        node.add_source([one])
-        assert node.sources == [one]
-        node.add_source([two, three])
-        assert node.sources == [one, two, three]
-        node.add_source([three, four, one])
-        assert node.sources == [one, two, three, four]
-
-        assert zero.get_parents() == []
-        assert one.get_parents() == [node]
-        assert two.get_parents() == [node]
-        assert three.get_parents() == [node]
-        assert four.get_parents() == [node]
-
-    def test_add_implicit(self):
-        """Test adding implicit (scanned) dependencies to a Node's list.
+        """Test adding sources to a Node's list.
         """
         node = SCons.Node.Node()
-        assert node.implicit == {}
+        assert node.sources == []
 
         zero = SCons.Node.Node()
         try:
@@ -359,12 +333,12 @@ class NodeTestCase(unittest.TestCase):
         three = SCons.Node.Node()
         four = SCons.Node.Node()
 
-        node.add_implicit([one], 1)
-        assert node.implicit[1] == [one]
-        node.add_implicit([two, three], 1)
-        assert node.implicit[1] == [one, two, three]
-        node.add_implicit([three, four, one], 1)
-        assert node.implicit[1] == [one, two, three, four]
+        node.add_source([one])
+        assert node.sources == [one]
+        node.add_source([two, three])
+        assert node.sources == [one, two, three]
+        node.add_source([three, four, one])
+        assert node.sources == [one, two, three, four]
 
         assert zero.get_parents() == []
         assert one.get_parents() == [node]
@@ -372,15 +346,6 @@ class NodeTestCase(unittest.TestCase):
         assert three.get_parents() == [node]
         assert four.get_parents() == [node]
 
-        node.add_implicit([one], 2)
-        node.add_implicit([two, three], 3)
-        node.add_implicit([three, four, one], 4)
-
-        assert node.implicit[1] == [one, two, three, four]
-        assert node.implicit[2] == [one]
-        assert node.implicit[3] == [two, three]
-        assert node.implicit[4] == [three, four, one]
-
     def test_add_ignore(self):
         """Test adding files whose dependencies should be ignored.
         """
@@ -419,42 +384,9 @@ class NodeTestCase(unittest.TestCase):
             pass
         ds=DummyScanner()
         node = SCons.Node.Node()
-        assert node.scanner == None, node.scanner
-        node.scanner_set(ds)
-        assert node.scanner == ds, node.scanner
-        node.scan(ds)
-        assert node.scanned[ds] == 1, node.scanned
-
-    def test_src_scanner_set(self):
-        """Test setting source-file Scanners"""
-        class DummyScanner:
-            pass
-        ds1=DummyScanner()
-        ds2=DummyScanner()
-        node = SCons.Node.Node()
-        assert node.src_scanners == {}, node.src_scanners
-        node.src_scanner_set('a', ds1)
-        assert node.src_scanners['a'] == ds1, node.src_scanners
-        node.src_scanner_set('b', ds2)
-        assert node.src_scanners['b'] == ds2, node.src_scanners
-
-    def test_src_scanner_set(self):
-        """Test setting source-file Scanners"""
-        class DummyScanner:
-            pass
-        ds1=DummyScanner()
-        ds2=DummyScanner()
-        node = SCons.Node.Node()
-        node.src_scanner_set('a', ds1)
-        node.src_scanner_set('b', ds2)
-        s = node.src_scanner_get(None)
-        assert s == None, s
-        s = node.src_scanner_get('a')
-        assert s == ds1, s
-        s = node.src_scanner_get('b')
-        assert s == ds2, s
-        s = node.src_scanner_get('c')
-        assert s == None, s
+        assert node.target_scanner == None, node.target_scanner
+        node.target_scanner = ds
+        node.scan()
 
     def test_scanner_key(self):
         """Test that a scanner_key() method exists"""
@@ -463,7 +395,7 @@ class NodeTestCase(unittest.TestCase):
     def test_children(self):
         """Test fetching the non-ignored "children" of a Node.
         """
-       node = SCons.Node.Node()
+        node = SCons.Node.Node()
         n1 = SCons.Node.Node()
         n2 = SCons.Node.Node()
         n3 = SCons.Node.Node()
@@ -479,28 +411,16 @@ class NodeTestCase(unittest.TestCase):
 
         node.add_source([n1, n2, n3])
         node.add_dependency([n4, n5, n6])
-        node.add_implicit([n7, n8, n9], 'key1')
-        node.add_implicit([n10, n11, n12], 'key2')
+        node._add_child(node.implicit, [n7, n8, n9])
+        node._add_child(node.implicit, [n10, n11, n12])
         node.add_ignore([n2, n5, n8, n11])
 
-        kids = node.children(None)
+        kids = node.children()
         for kid in [n1, n3, n4, n6, n7, n9, n10, n12]:
             assert kid in kids, kid
         for kid in [n2, n5, n8, n11]:
             assert not kid in kids, kid
 
-        kids = node.children('key1')
-        for kid in [n1, n3, n4, n6, n7, n9]:
-            assert kid in kids, kid
-        for kid in [n2, n5, n8, n10, n11, n12]:
-            assert not kid in kids, kid
-
-        kids = node.children('key2')
-        for kid in [n1, n3, n4, n6, n10, n12]:
-            assert kid in kids, kid
-        for kid in [n2, n5, n7, n8, n9, n11]:
-            assert not kid in kids, kid
-
     def test_all_children(self):
         """Test fetching all the "children" of a Node.
         """
@@ -520,29 +440,17 @@ class NodeTestCase(unittest.TestCase):
 
         node.add_source([n1, n2, n3])
         node.add_dependency([n4, n5, n6])
-        node.add_implicit([n7, n8, n9], 'key1')
-        node.add_implicit([n10, n11, n12], 'key2')
+        node._add_child(node.implicit, [n7, n8, n9])
+        node._add_child(node.implicit, [n10, n11, n12])
         node.add_ignore([n2, n5, n8, n11])
 
-        kids = node.all_children(None)
+        kids = node.all_children()
         for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9, n10, n11, n12]:
             assert kid in kids
 
-        kids = node.all_children('key1')
-        for kid in [n1, n2, n3, n4, n5, n6, n7, n8, n9]:
-            assert kid in kids
-        for kid in [n10, n11, n12]:
-            assert not kid in kids
-
-        kids = node.all_children('key2')
-        for kid in [n1, n2, n3, n4, n5, n6, n10, n11, n12]:
-            assert kid in kids
-        for kid in [n7, n8, n9]:
-            assert not kid in kids
-
     def test_state(self):
-       """Test setting and getting the state of a node
-       """
+        """Test setting and getting the state of a node
+        """
         node = SCons.Node.Node()
         assert node.get_state() == None
         node.set_state(SCons.Node.executing)
@@ -553,51 +461,51 @@ class NodeTestCase(unittest.TestCase):
         assert SCons.Node.executed < SCons.Node.failed
 
     def test_walker(self):
-       """Test walking a Node tree.
-       """
+        """Test walking a Node tree.
+        """
 
-       class MyNode(SCons.Node.Node):
-           def __init__(self, name):
-               SCons.Node.Node.__init__(self)
-               self.name = name
+        class MyNode(SCons.Node.Node):
+            def __init__(self, name):
+                SCons.Node.Node.__init__(self)
+                self.name = name
 
-       n1 = MyNode("n1")
+        n1 = MyNode("n1")
 
-       nw = SCons.Node.Walker(n1)
+        nw = SCons.Node.Walker(n1)
         assert not nw.is_done()
-       assert nw.next().name ==  "n1"
+        assert nw.next().name ==  "n1"
         assert nw.is_done()
-       assert nw.next() == None
-
-       n2 = MyNode("n2")
-       n3 = MyNode("n3")
-       n1.add_source([n2, n3])
-
-       nw = SCons.Node.Walker(n1)
-       assert nw.next().name ==  "n2"
-       assert nw.next().name ==  "n3"
-       assert nw.next().name ==  "n1"
-       assert nw.next() == None
-
-       n4 = MyNode("n4")
-       n5 = MyNode("n5")
-       n6 = MyNode("n6")
-       n7 = MyNode("n7")
-       n2.add_source([n4, n5])
-       n3.add_dependency([n6, n7])
-
-       nw = SCons.Node.Walker(n1)
-       assert nw.next().name ==  "n4"
-       assert nw.next().name ==  "n5"
+        assert nw.next() == None
+
+        n2 = MyNode("n2")
+        n3 = MyNode("n3")
+        n1.add_source([n2, n3])
+
+        nw = SCons.Node.Walker(n1)
+        assert nw.next().name ==  "n2"
+        assert nw.next().name ==  "n3"
+        assert nw.next().name ==  "n1"
+        assert nw.next() == None
+
+        n4 = MyNode("n4")
+        n5 = MyNode("n5")
+        n6 = MyNode("n6")
+        n7 = MyNode("n7")
+        n2.add_source([n4, n5])
+        n3.add_dependency([n6, n7])
+
+        nw = SCons.Node.Walker(n1)
+        assert nw.next().name ==  "n4"
+        assert nw.next().name ==  "n5"
         assert nw.history.has_key(n2)
-       assert nw.next().name ==  "n2"
-       assert nw.next().name ==  "n6"
-       assert nw.next().name ==  "n7"
+        assert nw.next().name ==  "n2"
+        assert nw.next().name ==  "n6"
+        assert nw.next().name ==  "n7"
         assert nw.history.has_key(n3)
-       assert nw.next().name ==  "n3"
+        assert nw.next().name ==  "n3"
         assert nw.history.has_key(n1)
-       assert nw.next().name ==  "n1"
-       assert nw.next() == None
+        assert nw.next().name ==  "n1"
+        assert nw.next() == None
 
         n8 = MyNode("n8")
         n8.add_dependency([n3])
@@ -621,69 +529,6 @@ class NodeTestCase(unittest.TestCase):
         n = nw.next()
         assert nw.next() == None
 
-    def test_children_are_executed(self):
-        n1 = SCons.Node.Node()
-        n2 = SCons.Node.Node()
-        n3 = SCons.Node.Node()
-        n4 = SCons.Node.Node()
-
-        n4.add_source([n3])
-        n3.add_source([n1, n2])
-
-        assert not n4.children_are_executed(None)
-        assert not n3.children_are_executed(None)
-        assert n2.children_are_executed(None)
-        assert n1.children_are_executed(None)
-
-        n1.set_state(SCons.Node.executed)
-        assert not n4.children_are_executed(None)
-        assert not n3.children_are_executed(None)
-        assert n2.children_are_executed(None)
-        assert n1.children_are_executed(None)
-
-        n2.set_state(SCons.Node.executed)
-        assert not n4.children_are_executed(None)
-        assert n3.children_are_executed(None)
-        assert n2.children_are_executed(None)
-        assert n1.children_are_executed(None)
-
-        n3.set_state(SCons.Node.executed)
-        assert n4.children_are_executed(None)
-        assert n3.children_are_executed(None)
-        assert n2.children_are_executed(None)
-        assert n1.children_are_executed(None)
-
-    def test_rescan(self):
-        """Test that built node implicit dependencies are cleared
-       to be rescanned."""
-        class DummyScanner:
-            pass
-        
-        class TestNode(SCons.Node.Node):
-            def scan(self, scanner):
-                if not self.scanned.has_key(scanner):
-                    n=SCons.Node.Node()
-                    n.scanner_set(scanner)
-                    self.add_implicit([ n ], scanner)
-                self.scanned[scanner] = 1
-        tn=TestNode()
-        tn.builder_set(Builder())
-        tn.env_set(Environment())
-        ds = DummyScanner()
-        tn.scan(ds)
-        map(lambda x: x.scan(), tn.depends)
-        assert tn.scanned[ds]
-        assert len(tn.implicit[ds]) == 1, tn.implicit
-        tn.scan(ds)
-        assert tn.scanned[ds]
-        assert len(tn.implicit[ds]) == 1, tn.implicit
-        tn.build()
-        assert not tn.scanned.has_key(ds)
-        assert len(tn.implicit[ds]) == 1, tn.implicit
-        tn.scan(ds)
-        assert tn.scanned[ds]
-        assert len(tn.implicit[ds]) == 2, tn.implicit
-
     def test_arg2nodes(self):
         """Test the arg2nodes function."""
         dict = {}
@@ -779,4 +624,4 @@ class NodeTestCase(unittest.TestCase):
 if __name__ == "__main__":
     suite = unittest.makeSuite(NodeTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
-       sys.exit(1)
+        sys.exit(1)
index 98b2d500f113787a0ba309d682459513dd3fc081..db11e553c1ee27575f7e8837706612105d4145fe 100644 (file)
@@ -51,6 +51,7 @@ executing = 2
 up_to_date = 3
 executed = 4
 failed = 5
+stack = 6 # nodes that are in the current Taskmaster execution stack
 
 class Node:
     """The base Node class, for entities that we know how to
@@ -60,14 +61,13 @@ class Node:
     def __init__(self):
         self.sources = []       # source files used to build node
         self.depends = []       # explicit dependencies (from Depends)
-        self.implicit = {}     # implicit (scanned) dependencies
-        self.ignore = []       # dependencies to ignore
+        self.implicit = []      # implicit (scanned) dependencies
+        self.ignore = []        # dependencies to ignore
         self.parents = {}
         self.wkids = None       # Kids yet to walk, when it's an array
         self.builder = None
-        self.scanner = None     # explicit scanner from this node's Builder
-        self.scanned = {}       # cached scanned values
-        self.src_scanners = {}  # scanners for this node's source files
+        self.source_scanner = None      # implicit scanner from scanner map
+        self.target_scanner = None      # explicit scanner from this node's Builder
         self.env = None
         self.state = None
         self.bsig = None
@@ -75,11 +75,14 @@ class Node:
         self.use_signature = 1
         self.precious = None
         self.found_includes = {}
+        self.includes = None
 
     def build(self):
         """Actually build the node.   Return the status from the build."""
-       if not self.builder:
-           return None
+        # This method is called from multiple threads in a parallel build,
+        # so only do thread safe stuff here. Do thread unsafe stuff in built().
+        if not self.builder:
+            return None
         try:
             # If this Builder instance has already been called,
             # there will already be an associated status.
@@ -103,16 +106,33 @@ class Node:
         if stat:
             raise BuildError(node = self, errstr = "Error %d" % stat)
 
-        self.found_includes = {}
-
-        # If we successfully build a node, then we need to rescan for
-        # implicit dependencies, since it might have changed on us.
-        self.scanned = {}
-
         return stat
 
+    def built(self):
+        """Called just after this node is sucessfully built."""
+        # 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
+            
+            def get_parents(node, parent): return node.get_parents()
+            def clear_cache(node, parent): 
+                node.implicit = []
+            w = Walker(self, get_parents, ignore_cycle, clear_cache)
+            while w.next(): pass
+
+    def depends_on(self, nodes):
+        """Does this node depend on any of 'nodes'?"""
+        for node in nodes:
+            if node in self.children():
+                return 1
+
+        return 0
+
     def builder_set(self, builder):
-       self.builder = builder
+        self.builder = builder
 
     def builder_sig_adapter(self):
         """Create an adapter for calculating a builder's signature.
@@ -136,19 +156,21 @@ class Node:
                 return self.node.builder.get_contents(env = dict)
         return Adapter(self)
 
-    def scanner_set(self, scanner):
-        self.scanner = scanner
-
-    def src_scanner_set(self, key, scanner):
-        self.src_scanners[key] = scanner
-
-    def src_scanner_get(self, key):
-        return self.src_scanners.get(key, None)
-
-    def scan(self, scanner = None):
-        if not scanner:
-            scanner = self.scanner
-        self.scanned[scanner] = 1
+    def get_implicit_deps(self, env, scanner, target):
+        """Return a list of implicit dependencies for this node"""
+        return []
+
+    def scan(self):
+        """Scan this node's dependents for implicit dependencies."""
+        # Don't bother scanning non-derived files, because we don't
+       # care what their dependencies are.
+        # Don't scan again, if we already have scanned.
+        if self.builder and not self.implicit: 
+            for child in self.children(scan=0):
+                self._add_child(self.implicit, child.get_implicit_deps(self.env, child.source_scanner, self))
+            
+            # scan this node itself for implicit dependencies
+            self._add_child(self.implicit, self.get_implicit_deps(self.env, self.target_scanner, self))
 
     def scanner_key(self):
         return None
@@ -156,7 +178,7 @@ class Node:
     def env_set(self, env, safe=0):
         if safe and self.env:
             return
-       self.env = env
+        self.env = env
 
     def get_bsig(self):
         """Get the node's build signature (based on the signatures
@@ -190,7 +212,7 @@ class Node:
         pass
 
     def add_dependency(self, depend):
-       """Adds dependencies. The depend argument must be a list."""
+        """Adds dependencies. The depend argument must be a list."""
         self._add_child(self.depends, depend)
 
     def add_ignore(self, depend):
@@ -198,23 +220,16 @@ class Node:
         self._add_child(self.ignore, depend)
 
     def add_source(self, source):
-       """Adds sources. The source argument must be a list."""
+        """Adds sources. The source argument must be a list."""
         self._add_child(self.sources, source)
 
-    def add_implicit(self, implicit, key):
-        """Adds implicit (scanned) dependencies. The implicit
-        argument must be a list."""
-        if not self.implicit.has_key(key):
-             self.implicit[key] = []
-        self._add_child(self.implicit[key], implicit)
-
     def _add_child(self, collection, child):
         """Adds 'child' to 'collection'. The 'child' argument must be a list"""
         if type(child) is not type([]):
             raise TypeError("child must be a list")
-       child = filter(lambda x, s=collection: x not in s, child)
-       if child:
-           collection.extend(child)
+        child = filter(lambda x, s=collection: x not in s, child)
+        if child:
+            collection.extend(child)
 
         for c in child:
             c.parents[self] = 1
@@ -224,22 +239,18 @@ class Node:
         if self.wkids != None:
             self.wkids.append(wkid)
 
-    def children(self, scanner):
+    def children(self, scan=1):
         """Return a list of the node's direct children, minus those
         that are ignored by this node."""
         return filter(lambda x, i=self.ignore: x not in i,
-                      self.all_children(scanner))
+                      self.all_children(scan))
 
-    def all_children(self, scanner):
+    def all_children(self, scan=1):
         """Return a list of all the node's direct children."""
         #XXX Need to remove duplicates from this
-        if not self.implicit.has_key(scanner):
-            self.scan(scanner)
-        if scanner:
-            implicit = self.implicit[scanner]
-        else:
-            implicit = reduce(lambda x, y: x + y, self.implicit.values(), [])
-        return self.sources + self.depends + implicit
+        if scan and not self.implicit:
+            self.scan()
+        return self.sources + self.depends + self.implicit
 
     def get_parents(self):
         return self.parents.keys()
@@ -253,14 +264,7 @@ class Node:
     def current(self):
         return None
 
-    def children_are_executed(self, scanner):
-        return reduce(lambda x,y: ((y.get_state() == executed
-                                   or y.get_state() == up_to_date)
-                                   and x),
-                      self.children(scanner),
-                      1)
-
-def get_children(node, parent): return node.children(None)
+def get_children(node, parent): return node.children()
 def ignore_cycle(node, stack): pass
 def do_nothing(node, parent): pass
 
@@ -290,13 +294,13 @@ class Walker:
         self.history[node] = None
 
     def next(self):
-       """Return the next node for this walk of the tree.
+        """Return the next node for this walk of the tree.
 
-       This function is intentionally iterative, not recursive,
-       to sidestep any issues of stack size limitations.
-       """
+        This function is intentionally iterative, not recursive,
+        to sidestep any issues of stack size limitations.
+        """
 
-       while self.stack:
+        while self.stack:
             if self.stack[-1].wkids:
                 node = self.stack[-1].wkids.pop(0)
                 if not self.stack[-1].wkids:
index 0a2654eecc327fb871e472279ae9fa4805444c20..c81417a53bc3166fcc17af981ccb83d01900b3bc 100644 (file)
@@ -41,45 +41,16 @@ import SCons.Util
 
 include_re = re.compile('^[ \t]*#[ \t]*include[ \t]+(<|")([\\w./\\\\]+)(>|")', re.M)
 
-include_cache = {}
-
 def CScan(fs = SCons.Node.FS.default_fs):
     """Return a prototype Scanner instance for scanning source files
     that use the C pre-processor"""
-    cs = CScanner(scan, "CScan", [fs, ()],
-                  [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
-                   ".h", ".H", ".hxx", ".hpp", ".hh",
-                   ".F", ".fpp", ".FPP"])
-    cs.fs = fs
+    cs = SCons.Scanner.Recursive(scan, "CScan", fs,
+                                 [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
+                                  ".h", ".H", ".hxx", ".hpp", ".hh",
+                                  ".F", ".fpp", ".FPP"])
     return cs
 
-class CScanner(SCons.Scanner.Recursive):
-    def __init__(self, *args, **kw):
-        apply(SCons.Scanner.Recursive.__init__, (self,) + args, kw)
-        self.hash = None
-        self.pathscanners = {}
-
-    def instance(self, env):
-        """
-        Return a unique instance of a C scanner object for a
-        given environment.
-        """
-        try:
-            dirs = tuple(SCons.Node.arg2nodes(env.Dictionary('CPPPATH'),
-                                              self.fs.Dir))
-        except:
-            dirs = ()
-        if not self.pathscanners.has_key(dirs):
-            clone = copy.copy(self)
-            clone.hash = dirs
-            clone.argument = [self.fs, dirs]   # XXX reaching into object
-            self.pathscanners[dirs] = clone
-        return self.pathscanners[dirs]
-
-    def __hash__(self):
-        return hash(self.hash)
-
-def scan(node, env, args = [SCons.Node.FS.default_fs, ()]):
+def scan(node, env, target, fs = SCons.Node.FS.default_fs):
     """
     scan(node, Environment) -> [node]
 
@@ -100,7 +71,21 @@ def scan(node, env, args = [SCons.Node.FS.default_fs, ()]):
     dependencies.
     """
 
-    fs, cpppath = args
+    # This function caches various information in node and target:
+    # target.cpppath - env['CPPPATH'] converted to nodes
+    # node.found_includes - include files found by previous call to scan, 
+    #     keyed on cpppath
+    # node.includes - the result of include_re.findall()
+
+    if not hasattr(target, 'cpppath'):
+        def Dir(x, dir=target.cwd, fs=fs): return fs.Dir(x,dir)
+        try:
+            target.cpppath = tuple(SCons.Node.arg2nodes(env['CPPPATH'],Dir))
+        except KeyError:
+            target.cpppath = ()
+
+    cpppath = target.cpppath
+
     nodes = []
 
     try:
@@ -109,7 +94,7 @@ def scan(node, env, args = [SCons.Node.FS.default_fs, ()]):
         if node.exists():
 
             # cache the includes list in node so we only scan it once:
-            if hasattr(node, 'includes'):
+            if node.includes != None:
                 includes = node.includes
             else:
                 includes = include_re.findall(node.get_contents())
index 684df992b3b677451348f7e1acd949131f09e6dc..f99af18e811d2a3a940b552cd44638d8d49c30fe 100644 (file)
@@ -119,6 +119,10 @@ test.write('subdir/include/fb.h', "\n")
 
 # define some helpers:
 
+class DummyTarget:
+    def __init__(self, cwd=None):
+        self.cwd = cwd
+
 class DummyEnvironment:
     def __init__(self, listCppPath):
         self.path = listCppPath
@@ -131,6 +135,15 @@ class DummyEnvironment:
         else:
             raise KeyError, "Dummy environment only has CPPPATH attribute."
 
+    def __getitem__(self,key):
+        return self.Dictionary()[key]
+
+    def __setitem__(self,key,value):
+        self.Dictionary()[key] = value
+
+    def __delitem__(self,key):
+        del self.Dictionary()[key]
+
 def deps_match(self, deps, headers):
     scanned = map(os.path.normpath, map(str, deps))
     expect = map(os.path.normpath, headers)
@@ -145,15 +158,15 @@ class CScannerTestCase1(unittest.TestCase):
     def runTest(self):
         env = DummyEnvironment([])
         s = SCons.Scanner.C.CScan()
-        deps = s.instance(env).scan(make_node('f1.cpp'), env)
-       headers = ['f1.h', 'f2.h', 'fi.h']
+        deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
+        headers = ['f1.h', 'f2.h', 'fi.h']
         deps_match(self, deps, map(test.workpath, headers))
 
 class CScannerTestCase2(unittest.TestCase):
     def runTest(self):
         env = DummyEnvironment([test.workpath("d1")])
         s = SCons.Scanner.C.CScan()
-        deps = s.instance(env).scan(make_node('f1.cpp'), env)
+        deps = s.scan(make_node('f1.cpp'), env, DummyTarget())
         headers = ['d1/f2.h', 'f1.h']
         deps_match(self, deps, map(test.workpath, headers))
 
@@ -161,7 +174,7 @@ class CScannerTestCase3(unittest.TestCase):
     def runTest(self):
         env = DummyEnvironment([test.workpath("d1")])
         s = SCons.Scanner.C.CScan()
-        deps = s.instance(env).scan(make_node('f2.cpp'), env)
+        deps = s.scan(make_node('f2.cpp'), env, DummyTarget())
         headers = ['d1/d2/f1.h', 'd1/f1.h', 'f1.h']
         deps_match(self, deps, map(test.workpath, headers))
 
@@ -169,7 +182,7 @@ class CScannerTestCase4(unittest.TestCase):
     def runTest(self):
         env = DummyEnvironment([test.workpath("d1"), test.workpath("d1/d2")])
         s = SCons.Scanner.C.CScan()
-        deps = s.instance(env).scan(make_node('f2.cpp'), env)
+        deps = s.scan(make_node('f2.cpp'), env, DummyTarget())
         headers =  ['d1/d2/f1.h', 'd1/d2/f4.h', 'd1/f1.h', 'f1.h']
         deps_match(self, deps, map(test.workpath, headers))
         
@@ -177,7 +190,7 @@ class CScannerTestCase5(unittest.TestCase):
     def runTest(self):
         env = DummyEnvironment([])
         s = SCons.Scanner.C.CScan()
-        deps = s.instance(env).scan(make_node('f3.cpp'), env)
+        deps = s.scan(make_node('f3.cpp'), env, DummyTarget())
         
         # Make sure exists() gets called on the file node being
         # scanned, essential for cooperation with BuildDir functionality.
@@ -193,35 +206,23 @@ class CScannerTestCase6(unittest.TestCase):
         env2 = DummyEnvironment([test.workpath("d1/d2")])
         env3 = DummyEnvironment([test.workpath("d1/../d1")])
         s = SCons.Scanner.C.CScan()
-        s1 = s.instance(env1)
-        s2 = s.instance(env2)
-        s3 = s.instance(env3)
-        assert not s1 is s2
-        assert s1 is s3
-        deps1 = s1.scan(make_node('f1.cpp'), None)
-        deps2 = s2.scan(make_node('f1.cpp'), None)
+        deps1 = s.scan(make_node('f1.cpp'), env1, DummyTarget())
+        deps2 = s.scan(make_node('f1.cpp'), env2, DummyTarget())
         headers1 =  ['d1/f2.h', 'f1.h']
         headers2 =  ['d1/d2/f2.h', 'f1.h']
         deps_match(self, deps1, map(test.workpath, headers1))
         deps_match(self, deps2, map(test.workpath, headers2))
 
-class CScannerTestCase7(unittest.TestCase):
-    def runTest(self):
-        s = SCons.Scanner.C.CScan()
-        s1 = s.instance(DummyEnvironment([test.workpath("d1")]))
-        s2 = s.instance(DummyEnvironment([test.workpath("d1/../d1")]))
-        dict = {}
-        dict[s1] = 777
-        assert dict[s2] == 777
-
 class CScannerTestCase8(unittest.TestCase):
     def runTest(self):
         fs = SCons.Node.FS.FS(test.workpath(''))
         env = DummyEnvironment(["include"])
         s = SCons.Scanner.C.CScan(fs = fs)
-        deps1 = s.instance(env).scan(fs.File('fa.cpp'), None)
+        deps1 = s.scan(fs.File('fa.cpp'), env, DummyTarget())
         fs.chdir(fs.Dir('subdir'))
-        deps2 = s.instance(env).scan(fs.File('#fa.cpp'), None)
+        target = DummyTarget(fs.getcwd())
+        fs.chdir(fs.Dir('..'))
+        deps2 = s.scan(fs.File('#fa.cpp'), env, target)
         headers1 =  ['include/fa.h', 'include/fb.h']
         headers2 =  ['subdir/include/fa.h', 'subdir/include/fb.h']
         deps_match(self, deps1, headers1)
@@ -233,7 +234,7 @@ class CScannerTestCase9(unittest.TestCase):
         fs = SCons.Node.FS.FS(test.workpath(''))
         s = SCons.Scanner.C.CScan(fs=fs)
         env = DummyEnvironment([])
-        deps = s.instance(env).scan(fs.File('fa.cpp'), None)
+        deps = s.scan(fs.File('fa.cpp'), env, DummyTarget())
         deps_match(self, deps, [ 'fa.h' ])
         test.unlink('fa.h')
 
@@ -244,7 +245,7 @@ class CScannerTestCase10(unittest.TestCase):
         s = SCons.Scanner.C.CScan(fs=fs)
         env = DummyEnvironment([])
         test.write('include/fa.cpp', test.read('fa.cpp'))
-        deps = s.instance(env).scan(fs.File('#include/fa.cpp'), None)
+        deps = s.scan(fs.File('#include/fa.cpp'), env, DummyTarget())
         deps_match(self, deps, [ 'include/fa.h', 'include/fb.h' ])
         test.unlink('include/fa.cpp')
 
@@ -256,7 +257,6 @@ def suite():
     suite.addTest(CScannerTestCase4())
     suite.addTest(CScannerTestCase5())
     suite.addTest(CScannerTestCase6())
-    suite.addTest(CScannerTestCase7())
     suite.addTest(CScannerTestCase8())
     suite.addTest(CScannerTestCase9())
     suite.addTest(CScannerTestCase10())
index 9ecc37b0612165bf5ec904b60decf4b8ba63f008..76567ab3156e7bc4f611c0f296b7a80a8df8ace6 100644 (file)
@@ -31,74 +31,49 @@ import SCons.Node.FS
 import SCons.Scanner
 import SCons.Util
 
-class NullProgScanner:
-    """A do-nothing ProgScanner for Environments that have no LIBS."""
-    def scan(node, env, args = []):
-        return []
-
-null_scanner = NullProgScanner()
-
 def ProgScan(fs = SCons.Node.FS.default_fs):
     """Return a prototype Scanner instance for scanning executable
     files for static-lib dependencies"""
-    ps = ProgScanner(scan, "ProgScan")
-    ps.fs = fs
+    ps = SCons.Scanner.Base(scan, "ProgScan", fs)
     return ps
 
-class ProgScanner(SCons.Scanner.Base):
-    def __init__(self, *args, **kw):
-        apply(SCons.Scanner.Base.__init__, (self,) + args, kw)
-        self.hash = None
-        self.pathscanners = {}
-
-    def instance(self, env):
-        """
-        Return a unique instance of a Prog scanner object for a
-        given environment.
-        """
-        try:
-            libs = env.Dictionary('LIBS')
-        except KeyError:
-            # There are no LIBS in this environment, so just return the
-            # fake "scanner" instance that always returns a null list.
-            return null_scanner
-        if SCons.Util.is_String(libs):
-            libs = string.split(libs)
+def scan(node, env, target, fs):
+    """
+    This scanner scans program files for static-library
+    dependencies.  It will search the LIBPATH environment variable
+    for libraries specified in the LIBS variable, returning any
+    files it finds as dependencies.
+    """
 
-        try:
-            dirs = tuple(SCons.Node.arg2nodes(env.Dictionary('LIBPATH'),
-                                              self.fs.Dir))
-        except:
-            dirs = ()
+    # This function caches information in target:
+    # target.libpath - env['LIBPATH'] converted to nodes
 
+    if not hasattr(target, 'libpath'):
+        def Dir(x, dir=target.cwd, fs=fs): return fs.Dir(x,dir)
         try:
-            prefix = env.Dictionary('LIBPREFIX')
+            target.libpath = tuple(SCons.Node.arg2nodes(env['LIBPATH'],Dir))
         except KeyError:
-            prefix = ''
+            target.libpath = ()
+    libpath = target.libpath
 
-        try:
-            suffix = env.Dictionary('LIBSUFFIX')
-        except KeyError:
-            suffix = ''
+    try:
+        libs = env.Dictionary('LIBS')
+    except KeyError:
+        # There are no LIBS in this environment, so just return a null list:
+        return []
+    if SCons.Util.is_String(libs):
+        libs = string.split(libs)
 
-        key = (dirs, tuple(libs), prefix, suffix)
-        if not self.pathscanners.has_key(key):
-            clone = copy.copy(self)
-            clone.hash = key
-            clone.argument = [self.fs, dirs, libs, prefix, suffix]    # XXX reaching into object
-            self.pathscanners[key] = clone
-        return self.pathscanners[key]
+    try:
+        prefix = env.Dictionary('LIBPREFIX')
+    except KeyError:
+        prefix = ''
 
-    def __hash__(self):
-        return hash(self.hash)
+    try:
+        suffix = env.Dictionary('LIBSUFFIX')
+    except KeyError:
+        suffix = ''
 
-def scan(node, env, args = [SCons.Node.FS.default_fs, (), [], '', '']):
-    """
-    This scanner scans program files for static-library
-    dependencies.  It will search the LIBPATH environment variable
-    for libraries specified in the LIBS variable, returning any
-    files it finds as dependencies.
-    """
-    fs, libpath, libs, prefix, suffix = args
     libs = map(lambda x, s=suffix, p=prefix: p + x + s, libs)
     return SCons.Node.FS.find_files(libs, libpath, fs.File)
index b59e554c0cc9011ba215d66715fdcebad077f7ef..ea3d00d1f1b7886a7e0c187b3fedfdac48e98bf7 100644 (file)
@@ -42,6 +42,10 @@ for h in libs:
 
 # define some helpers:
 
+class DummyTarget:
+    def __init__(self, cwd=None):
+        self.cwd = cwd
+
 class DummyEnvironment:
     def __init__(self, **kw):
         self._dict = kw
@@ -54,6 +58,14 @@ class DummyEnvironment:
             return self._dict[args[0]]
         else:
             return map(lambda x, s=self: s._dict[x], args)
+    def __getitem__(self,key):
+        return self.Dictionary()[key]
+
+    def __setitem__(self,key,value):
+        self.Dictionary()[key] = value
+
+    def __delitem__(self,key):
+        del self.Dictionary()[key]
 
 def deps_match(deps, libs):
     deps=map(str, deps)
@@ -70,7 +82,7 @@ class ProgScanTestCase1(unittest.TestCase):
         env = DummyEnvironment(LIBPATH=[ test.workpath("") ],
                                LIBS=[ 'l1', 'l2', 'l3' ])
         s = SCons.Scanner.Prog.ProgScan()
-        deps = s.instance(env).scan('dummy', env)
+        deps = s.scan('dummy', env, DummyTarget())
         assert deps_match(deps, ['l1.lib']), map(str, deps)
 
 class ProgScanTestCase2(unittest.TestCase):
@@ -79,7 +91,7 @@ class ProgScanTestCase2(unittest.TestCase):
                                            ["", "d1", "d1/d2" ]),
                                LIBS=[ 'l1', 'l2', 'l3' ])
         s = SCons.Scanner.Prog.ProgScan()
-        deps = s.instance(env).scan('dummy', env)
+        deps = s.scan('dummy', env, DummyTarget())
         assert deps_match(deps, ['l1.lib', 'd1/l2.lib', 'd1/d2/l3.lib' ]), map(str, deps)
 
 class ProgScanTestCase3(unittest.TestCase):
@@ -88,7 +100,7 @@ class ProgScanTestCase3(unittest.TestCase):
                                test.workpath("d1"),
                                LIBS='l2 l3')
         s = SCons.Scanner.Prog.ProgScan()
-        deps = s.instance(env).scan('dummy', env)
+        deps = s.scan('dummy', env, DummyTarget())
         assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps)
 
 def suite():
@@ -104,7 +116,7 @@ def suite():
                                            test.workpath("d1"),
                                            LIBS=u'l2 l3')
                     s = SCons.Scanner.Prog.ProgScan()
-                    deps = s.instance(env).scan('dummy', env)
+                    deps = s.scan('dummy', env, DummyTarget())
                     assert deps_match(deps, ['d1/l2.lib', 'd1/d2/l3.lib']), map(str, deps)
             suite.addTest(ProgScanTestCase4())
             \n"""
index 76df18e71c0df45c912406356179f421e3629777..fce4078ef0a5b7045c459ed05cfdaa8378496af8 100644 (file)
@@ -27,11 +27,16 @@ import unittest
 import SCons.Scanner
 import sys
 
+class DummyTarget:
+    cwd = None
+    
+
 class ScannerTestBase:
     
-    def func(self, filename, env, *args):
+    def func(self, filename, env, target, *args):
         self.filename = filename
         self.env = env
+        self.target = target
 
         if len(args) > 0:
             self.arg = args[0]
@@ -41,7 +46,7 @@ class ScannerTestBase:
 
     def test(self, scanner, env, filename, deps, *args):
         self.deps = deps
-        scanned = scanner.scan(filename, env)
+        scanned = scanner.scan(filename, env, DummyTarget())
         scanned_strs = map(lambda x: str(x), scanned)
 
         self.failUnless(self.filename == filename, "the filename was passed incorrectly")
@@ -67,10 +72,9 @@ class ScannerPositionalTestCase(ScannerTestBase, unittest.TestCase):
         env.VARIABLE = "var1"
         self.test(s, env, 'f1.cpp', ['f1.h', 'f1.hpp'])
 
-       env = DummyEnvironment()
-       env.VARIABLE = "i1"
-       i = s.instance(env)
-       self.test(i, env, 'i1.cpp', ['i1.h', 'i1.hpp'])
+        env = DummyEnvironment()
+        env.VARIABLE = "i1"
+        self.test(s, env, 'i1.cpp', ['i1.h', 'i1.hpp'])
 
 class ScannerKeywordTestCase(ScannerTestBase, unittest.TestCase):
     "Test the Scanner.Base class using the keyword argument"
@@ -80,10 +84,9 @@ class ScannerKeywordTestCase(ScannerTestBase, unittest.TestCase):
         env.VARIABLE = "var2"
         self.test(s, env, 'f2.cpp', ['f2.h', 'f2.hpp'])
 
-       env = DummyEnvironment()
-       env.VARIABLE = "i2"
-       i = s.instance(env)
-       self.test(i, env, 'i2.cpp', ['i2.h', 'i2.hpp'])
+        env = DummyEnvironment()
+        env.VARIABLE = "i2"
+        self.test(s, env, 'i2.cpp', ['i2.h', 'i2.hpp'])
 
 class ScannerPositionalArgumentTestCase(ScannerTestBase, unittest.TestCase):
     "Test the Scanner.Base class using both position and optional arguments"
@@ -94,10 +97,9 @@ class ScannerPositionalArgumentTestCase(ScannerTestBase, unittest.TestCase):
         env.VARIABLE = "var3"
         self.test(s, env, 'f3.cpp', ['f3.h', 'f3.hpp'], arg)
 
-       env = DummyEnvironment()
-       env.VARIABLE = "i3"
-       i = s.instance(env)
-       self.test(i, env, 'i3.cpp', ['i3.h', 'i3.hpp'], arg)
+        env = DummyEnvironment()
+        env.VARIABLE = "i3"
+        self.test(s, env, 'i3.cpp', ['i3.h', 'i3.hpp'], arg)
 
 class ScannerKeywordArgumentTestCase(ScannerTestBase, unittest.TestCase):
     "Test the Scanner.Base class using both keyword and optional arguments"
@@ -109,10 +111,9 @@ class ScannerKeywordArgumentTestCase(ScannerTestBase, unittest.TestCase):
         env.VARIABLE = "var4"
         self.test(s, env, 'f4.cpp', ['f4.h', 'f4.hpp'], arg)
 
-       env = DummyEnvironment()
-       env.VARIABLE = "i4"
-       i = s.instance(env)
-       self.test(i, env, 'i4.cpp', ['i4.h', 'i4.hpp'], arg)
+        env = DummyEnvironment()
+        env.VARIABLE = "i4"
+        self.test(s, env, 'i4.cpp', ['i4.h', 'i4.hpp'], arg)
 
 class ScannerHashTestCase(ScannerTestBase, unittest.TestCase):
     "Test the Scanner.Base class __hash__() method"
index 53c67e40d48922cc9a0455097ee5a119192a745e..b2e04a34f71d4bb72fa0563df88b360ae5663ead 100644 (file)
@@ -97,7 +97,7 @@ class Base:
         self.skeys = skeys
         self.node_factory = node_factory
 
-    def scan(self, node, env):
+    def scan(self, node, env, target):
         """
         This method scans a single object. 'node' is the node
         that will be passed to the scanner function, and 'env' is the
@@ -106,9 +106,9 @@ class Base:
         """
 
         if not self.argument is _null:
-            list = self.function(node, env, self.argument)
+            list = self.function(node, env, target, self.argument)
         else:
-            list = self.function(node, env)
+            list = self.function(node, env, target)
         kw = {}
         if hasattr(node, 'dir'):
             kw['directory'] = node.dir
@@ -119,16 +119,6 @@ class Base:
             nodes.append(l)
         return nodes
 
-    def instance(self, env):
-        """
-        Return an instance of a Scanner object for use in scanning.
-
-        In the base class, we just return the scanner itself.
-        Other Scanner classes may use this to clone copies and/or
-        return unique instances as needed.
-        """
-        return self
-
     def __cmp__(self, other):
         return cmp(self.__dict__, other.__dict__)
 
@@ -143,7 +133,7 @@ class Recursive(Base):
     list of all dependencies.
     """
 
-    def scan(self, node, env):
+    def scan(self, node, env, target):
         """
         This method does the actual scanning. 'node' is the node
         that will be passed to the scanner function, and 'env' is the
@@ -158,7 +148,7 @@ class Recursive(Base):
         while nodes:
             n = nodes.pop(0)
             d = filter(lambda x, seen=seen: not seen.has_key(x),
-                       Base.scan(self, n, env))
+                       Base.scan(self, n, env, target))
             if d:
                 deps.extend(d)
                 nodes.extend(d)
index 71d48ce588e10ed825fd64579e44f20a3e476f8c..90488e1516419847c9576ad0b78c6a1b8e71e1be 100644 (file)
@@ -50,7 +50,7 @@ class DummyNode:
         self.file = file
         self.path = file.path
         self.builder = file.builder
-       self.depends = []
+        self.depends = []
         self.ignore = []
         self.use_signature = 1
         self.bsig = None
@@ -81,11 +81,11 @@ class DummyNode:
             self.exists_cache = self.exists()
             return self.exists_cache
         
-    def children(self, scanner):
+    def children(self):
         return filter(lambda x, i=self.ignore: x not in i,
                       self.sources + self.depends)
         
-    def all_children(self, scanner):
+    def all_children(self):
         return self.sources + self.depends
 
     def current(self):
@@ -139,12 +139,12 @@ def create_nodes(files):
 
     nodes[0].sources = []
     nodes[1].sources = [nodes[0]]
-    nodes[2].sources = [nodes[3]]
+    nodes[2].sources = []
     nodes[3].sources = []
-    nodes[4].sources = [nodes[5]]
+    nodes[4].sources = []
     nodes[5].sources = [nodes[6]]
     nodes[6].sources = [nodes[5]]
-    nodes[7].sources = [nodes[2], nodes[4]]
+    nodes[7].sources = [nodes[2], nodes[4], nodes[3], nodes[5]]
     nodes[8].sources = []
     nodes[9].sources = [nodes[8]]
     nodes[10].sources = [nodes[9]]
@@ -296,9 +296,9 @@ class CalcTestCase(unittest.TestCase):
                 self.ignore = []
                 self.builder = None
                 self.use_signature = 1
-            def children(self, scanner):
+            def children(self):
                 return filter(lambda x, i=self.ignore: x not in i, self.kids)
-            def all_children(self, scanner):
+            def all_children(self):
                 return self.kids
             def exists(self):
                 return 1
index 3770adf17cbc8e9ecb289f5ca1f231cea324d2b3..cb03630bcbbd6320d57c863a2ddcec55144a6529 100644 (file)
@@ -192,36 +192,14 @@ class Calculator:
         if not bsig is None:
             return bsig
 
-        # Collect the signatures for ALL the nodes that this
-        # node depends on. Just collecting the direct
-        # dependants is not good enough, because
-        # the signature of a non-derived file does
-        # not include the signatures of its psuedo-sources
-        # (e.g. the signature for a .c file does not include
-        # the signatures of the .h files that it includes).
-
-        # However, we do NOT want to walk dependencies of non-
-        # derived files, because calling get_signature() on the
-        # derived nodes will in turn call bsig() again and do that
-        # for us.  Hence:
         sigs = []
-        def non_derived(n, parent, myself=node):
-            if not n.builder or n is myself:
-                return filter(lambda x, i=myself.ignore: x not in i,
-                              n.all_children(None))
-            return []
-        def get_sig(n, parent, self=self, myself=node, sigs=sigs):
-            if not n is myself:
-                sigs.append(self.get_signature(n))
-        walker = SCons.Node.Walker(node, non_derived, eval_func=get_sig)
-        child = walker.next()
-        while child:
-            child = walker.next()
-
+        for child in node.children():
+            sigs.append(self.get_signature(child))
         if node.builder:
             sigs.append(self.module.signature(node.builder_sig_adapter()))
-        return self.module.collect(filter(lambda x: not x is None, sigs))
 
+        return self.module.collect(filter(lambda x: not x is None, sigs))
+        
     def csig(self, node):
         """
         Generate a node's content signature, the digested signature
@@ -239,7 +217,7 @@ class Calculator:
             return csig
         
         return self.module.signature(node)
-
+        
     def get_signature(self, node):
         """
         Get the appropriate signature for a node.
index c97d28c4a0a616a621def112c3e7700228a84a8b..47707b0635b7b94654be1ee5be2a9b2137c8bd50 100644 (file)
@@ -54,13 +54,16 @@ class Task:
     Note that it's generally a good idea for sub-classes to call
     these methods explicitly to update state, etc., rather than
     roll their own interaction with Taskmaster from scratch."""
-    def __init__(self, tm, targets, top, scanner = None):
+    def __init__(self, tm, targets, top, node):
         self.tm = tm
         self.targets = targets
         self.top = top
-        self.scanner = scanner
+        self.node = node
 
     def execute(self):
+        # This methods is called from multiple threads in
+        # a parallel build, so only do thread safe stuff here.
+        # Do thread unsafe stuff in executed() or failed().
         if self.targets[0].get_state() != SCons.Node.up_to_date:
             self.targets[0].prepare()
             self.targets[0].build()
@@ -68,7 +71,7 @@ class Task:
     def get_target(self):
         """Fetch the target being built or updated by this task.
         """
-        return self.targets[0]
+        return self.node
 
     def set_tstates(self, state):
         """Set all of the target nodes's states."""
@@ -83,21 +86,14 @@ class Task:
         things.  Most importantly, this calls back to the
         Taskmaster to put any node tasks waiting on this one
         back on the pending list."""
+
         if self.targets[0].get_state() == SCons.Node.executing:
             self.set_tstates(SCons.Node.executed)
             for t in self.targets:
                 t.store_sigs()
-            parents = {}
-            for p in reduce(lambda x, y: x + y.get_parents(), self.targets, []):
-                parents[p] = 1
-            ready = filter(lambda x, s=self.scanner:
-                                  (x.get_state() == SCons.Node.pending
-                                   and x.children_are_executed(s)),
-                           parents.keys())
-            tasks = {}
-            for t in map(lambda r: r.task, ready):
-                tasks[t] = 1
-            self.tm.pending_to_ready(tasks.keys())
+                t.built()
+
+        self.tm.executed(self.node)
 
     def failed(self):
         """Default action when a task fails:  stop the build."""
@@ -114,20 +110,15 @@ class Task:
         This sets failure status on the target nodes and all of
         their dependent parent nodes.
         """
-        nodes = {}
         for t in self.targets:
             def get_parents(node, parent): return node.get_parents()
-           def set_nodes(node, parent, nodes=nodes): nodes[node] = 1
-            walker = SCons.Node.Walker(t, get_parents, eval_func=set_nodes)
+            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()
-        pending = filter(lambda x: x.get_state() == SCons.Node.pending,
-                         nodes.keys())
-        tasks = {}
-        for t in map(lambda r: r.task, pending):
-            tasks[t] = 1
-        self.tm.pending_remove(tasks.keys())
+        
+        self.tm.executed(self.node)
 
     def make_ready(self):
         """Make a task ready for execution."""
@@ -138,9 +129,6 @@ class Task:
             if not self.tm.calc.current(t, bsig):
                 state = SCons.Node.executing
         self.set_tstates(state)
-        self.tm.add_ready(self)
-
-
 
 class Calc:
     def bsig(self, node):
@@ -156,8 +144,6 @@ class Calc:
         """
         return 0
 
-
-
 class Taskmaster:
     """A generic Taskmaster for handling a bunch of targets.
 
@@ -166,118 +152,113 @@ class Taskmaster:
     """
 
     def __init__(self, targets=[], tasker=Task, calc=Calc()):
+        self.targets = targets # top level targets
+        self.candidates = targets[:] # nodes that might be ready to be executed
+        self.candidates.reverse()
+        self.executing = [] # nodes that are currently executing
+        self.pending = [] # nodes that depend on a currently executing node
+        self.tasker = tasker
+        self.ready = None # the next task that is ready to be executed
+        self.calc = calc
+
+    def _find_next_ready_node(self):
+        """Find the next node that is ready to be built"""
+
+        if self.ready:
+            return
         
-        def out_of_date(node, parent):
-            if node.get_state():
-                # The state is set, so someone has already been here
-                # (finished or currently executing).  Find another one.
-                return []
-            # Scan the file before fetching its children().
-            if parent:
-                scanner = parent.src_scanner_get(node.scanner_key())
-            else:
-                scanner = None
-            return filter(lambda x: x.get_state() != SCons.Node.up_to_date,
-                          node.children(scanner))
+        while self.candidates:
+            node = self.candidates[-1]
+            state = node.get_state()
+            
+            # Skip nodes that have already been executed:
+            if state != None and state != SCons.Node.stack:
+                self.candidates.pop()
+                continue
+
+            # keep track of which nodes are in the execution stack:
+            node.set_state(SCons.Node.stack)
 
-        def cycle_error(node, stack):
-            if node.builder:
-                nodes = stack + [node]
+            children = node.children()
+
+            # detect dependency cycles:
+            def in_stack(node): return node.get_state() == SCons.Node.stack
+            cycle = filter(in_stack, children)
+            if cycle:
+                nodes = filter(in_stack, self.candidates) + cycle
                 nodes.reverse()
                 desc = "Dependency cycle: " + string.join(map(str, nodes), " -> ")
                 raise SCons.Errors.UserError, desc
 
-        def eval_node(node, parent, self=self):
-            if node.get_state():
-                # The state is set, so someone has already been here
-                # (finished or currently executing).  Find another one.
-                return
-            if not node.builder:
-                # It's a source file, we don't need to build it,
-                # but mark it as "up to date" so targets won't
-                # wait for it.
-                node.set_state(SCons.Node.up_to_date)
-                # set the signature for non-derived files
-                # here so they don't get recalculated over
-                # and over again:
-                node.set_csig(self.calc.csig(node))
-                return
-            try:
-                tlist = node.builder.targets(node)
-            except AttributeError:
-                tlist = [ node ]
-            if parent:
-                scanner = parent.src_scanner_get(node.scanner_key())
+            for child in children:
+                if not child.builder:
+                    # set the signature for non-derived files
+                    # here so they don't get recalculated over
+                    # and over again:
+                    child.set_csig(self.calc.csig(child))
+
+            # Add non-derived files that have not been built
+            # to the candidates list:
+            def derived(node):
+                return node.builder and node.get_state() == None
+            derived = filter(derived, children)
+            if derived:
+                derived.reverse()
+                self.candidates.extend(derived)
+                continue
+
+            # Skip nodes that are pending on a currently executing node:
+            if node.depends_on(self.executing) or node.depends_on(self.pending):
+                self.pending.append(node)
+                node.set_state(SCons.Node.pending)
+                self.candidates.pop()
+                continue
             else:
-                scanner = None
-            task = self.tasker(self, tlist, self.walkers[0].is_done(), scanner)
-            if not tlist[0].children_are_executed(scanner):
-                for t in tlist:
-                    t.set_state(SCons.Node.pending)
-                    t.task = task
-                self.pending = self.pending + 1
-                return
-            task.make_ready()
-
-        #XXX In Python 2.2 we can get rid of f1, f2 and f3:
-        self.walkers = map(lambda x, f1=out_of_date,
-                                     f2=cycle_error,
-                                     f3=eval_node:
-                                  SCons.Node.Walker(x, f1, f2, f3),
-                           targets)
-        self.tasker = tasker
-        self.calc = calc
-        self.ready = []
-        self.pending = 0
-        
-        self._find_next_ready_node()
+                self.candidates.pop()
+                self.ready = node
+                break
 
     def next_task(self):
         """Return the next task to be executed."""
-        if self.ready:
-            task = self.ready.pop()
-            if not self.ready:
-                self._find_next_ready_node()
-            return task
-        else:
-            return None
+        
+        self._find_next_ready_node()
 
-    def _find_next_ready_node(self):
-        """Find the next node that is ready to be built"""
-        while self.walkers:
-            n = self.walkers[0].next()
-            if n == None:
-                self.walkers.pop(0)
-                continue
-            if self.ready:
-                return
+        node = self.ready
+        
+        if node is None:
+            return None
+        
+        self.executing.append(node)
+        try:
+            tlist = node.builder.targets(node)
+        except AttributeError:
+            tlist = [node]
+        task = self.tasker(self, tlist, node in self.targets, node) 
+        task.make_ready()
+        self.ready = None
+        
+        return task
             
     def is_blocked(self):
+        self._find_next_ready_node()
+
         return not self.ready and self.pending
 
     def stop(self):
         """Stop the current build completely."""
-        self.walkers = []
-        self.pending = 0
-        self.ready = []
+        self.candidates = []
+        self.ready = None
+        self.pending = []
 
-    def add_ready(self, task):
-        """Add a task to the ready queue.
-        """
-        self.ready.append(task)
-
-    def pending_to_ready(self, tasks):
-        """Move the specified tasks from the pending count
-        to the 'ready' queue.
-        """
-        self.pending_remove(tasks)
-        for t in tasks:
-            t.make_ready()
-
-    def pending_remove(self, tasks):
-        """Remove tasks from the pending count.
+    def executed(self, node):
+        self.executing.remove(node)
         
-        We assume that the caller has already confirmed that
-        the nodes in this task are in pending state.
-        """
-        self.pending = self.pending - len(tasks)
+        # move the current pending nodes to the candidates list:
+        # (they may not all be ready to build, but _find_next_ready_node()
+        #  will figure out which ones are really ready)
+        for node in self.pending:
+            node.set_state(None)
+        self.pending.reverse()
+        self.candidates.extend(self.pending)
+        self.pending = []
+
index b6fa68568e917feffbef6d5faef3f2c3c3d5cefd..44b1e40b5fb5ceca86cd1cf423d65310adf674e1 100644 (file)
@@ -30,7 +30,7 @@ import SCons.Taskmaster
 import SCons.Errors
 
 
-built = None
+built_text = None
 executed = None
 scan_called = 0
 
@@ -39,8 +39,8 @@ class Node:
         self.name = name
         self.kids = kids
         self.scans = scans
-        self.scanned = {}
-        self.src_scanners = {}
+        self.scanned = 0
+        self.scanner = None
         self.builder = Node.build
         self.bsig = None
         self.csig = None
@@ -51,32 +51,30 @@ class Node:
             kid.parents.append(self)
 
     def build(self):
-        global built
-        built = self.name + " built"
+        global built_text
+        built_text = self.name + " built"
+
+    def built(self):
+        global built_text
+        built_text = built_text + " really"
 
     def prepare(self):
         pass
 
-    def children(self, scanner):
-        if not self.scanned.get(scanner, None):
-            self.scan(scanner)
-            self.scanned[scanner] = 1
+    def children(self):
+        if not self.scanned:
+            self.scan()
+            self.scanned = 1
         return self.kids
 
-    def scan(self, scanner):
-       global scan_called
-       scan_called = scan_called + 1
+    def scan(self):
+        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 src_scanner_set(self, key, scanner):
-        self.src_scanners[key] = scanner
-
-    def src_scanner_get(self, key):
-        return self.src_scanners.get(key, None)
-
     def scanner_key(self):
         return self.name
   
@@ -98,12 +96,13 @@ class Node:
     def store_sigs(self):
         pass
   
-    def children_are_executed(self, scanner):
-        return reduce(lambda x,y: ((y.get_state() == SCons.Node.executed
-                                   or y.get_state() == SCons.Node.up_to_date)
-                                   and x),
-                      self.children(scanner),
-                      1)
+    
+    def depends_on(self, nodes):
+        for node in nodes:
+            if node in self.kids:
+                return 1
+        return 0
+
     def __str__(self):
         return self.name
 
@@ -111,14 +110,14 @@ class Node:
 class TaskmasterTestCase(unittest.TestCase):
 
     def test_next_task(self):
-       """Test fetching the next task
-       """
-       global built
+        """Test fetching the next task
+        """
+        global built_text
         
-       n1 = Node("n1")
+        n1 = Node("n1")
         tm = SCons.Taskmaster.Taskmaster([n1, n1])
         t = tm.next_task()
-        t.executed()
+        t.execute()
         t = tm.next_task()
         assert t == None
 
@@ -126,26 +125,26 @@ class TaskmasterTestCase(unittest.TestCase):
         n2 = Node("n2")
         n3 = Node("n3", [n1, n2])
         
-       tm = SCons.Taskmaster.Taskmaster([n3])
+        tm = SCons.Taskmaster.Taskmaster([n3])
 
         t = tm.next_task()
         t.execute()
-        assert built == "n1 built"
+        assert built_text == "n1 built", built_text
         t.executed()
 
         t = tm.next_task()
         t.execute()
-        assert built == "n2 built"
+        assert built_text == "n2 built", built_text
         t.executed()
 
         t = tm.next_task()
         t.execute()
-        assert built == "n3 built"
+        assert built_text == "n3 built", built_text
         t.executed()
 
         assert tm.next_task() == None
 
-        built = "up to date: "
+        built_text = "up to date: "
         top_node = n3
 
         class MyCalc(SCons.Taskmaster.Calc):
@@ -154,12 +153,12 @@ class TaskmasterTestCase(unittest.TestCase):
 
         class MyTask(SCons.Taskmaster.Task):
             def execute(self):
-                global built
+                global built_text
                 if self.targets[0].get_state() == SCons.Node.up_to_date:
                     if self.top:
-                        built = self.targets[0].name + " up-to-date top"
+                        built_text = self.targets[0].name + " up-to-date top"
                     else:
-                        built = self.targets[0].name + " up-to-date"
+                        built_text = self.targets[0].name + " up-to-date"
                 else:
                     self.targets[0].build()
 
@@ -171,24 +170,24 @@ class TaskmasterTestCase(unittest.TestCase):
 
         t = tm.next_task()
         t.execute()
-        assert built == "n1 up-to-date"
+        assert built_text == "n1 up-to-date", built_text
         t.executed()
 
         t = tm.next_task()
         t.execute()
-        assert built == "n2 up-to-date"
+        assert built_text == "n2 up-to-date", built_text
         t.executed()
 
         t = tm.next_task()
         t.execute()
-        assert built == "n3 up-to-date top"
+        assert built_text == "n3 up-to-date top", built_text
         t.executed()
 
-       assert tm.next_task() == None
+        assert tm.next_task() == None
 
 
         n1 = Node("n1")
-       n2 = Node("n2")
+        n2 = Node("n2")
         n3 = Node("n3", [n1, n2])
         n4 = Node("n4")
         n5 = Node("n5", [n3, n4])
@@ -221,7 +220,7 @@ class TaskmasterTestCase(unittest.TestCase):
         t3.executed()
         assert not tm.is_blocked()
         t5 = tm.next_task()
-        assert t5.get_target() == n5
+        assert t5.get_target() == n5, t5.get_target()
         assert not tm.is_blocked()
 
         assert tm.next_task() == None
@@ -261,35 +260,35 @@ class TaskmasterTestCase(unittest.TestCase):
         t.executed()
         assert tm.next_task() == None
 
-       n1 = Node("n1")
-       n2 = Node("n2")
-       n3 = Node("n3", [n1, n2])
-       n4 = Node("n4", [n3])
-       n5 = Node("n5", [n3])
-       global scan_called
-       scan_called = 0
-       tm = SCons.Taskmaster.Taskmaster([n4])
-       t = tm.next_task()
+        n1 = Node("n1")
+        n2 = Node("n2")
+        n3 = Node("n3", [n1, n2])
+        n4 = Node("n4", [n3])
+        n5 = Node("n5", [n3])
+        global scan_called
+        scan_called = 0
+        tm = SCons.Taskmaster.Taskmaster([n4])
+        t = tm.next_task()
         assert t.get_target() == n1
-       t.executed()
-       t = tm.next_task()
+        t.executed()
+        t = tm.next_task()
         assert t.get_target() == n2
-       t.executed()
-       t = tm.next_task()
+        t.executed()
+        t = tm.next_task()
         assert t.get_target() == n3
-       t.executed()
-       t = tm.next_task()
+        t.executed()
+        t = tm.next_task()
         assert t.get_target() == n4
-       t.executed()
-       assert tm.next_task() == None
-       assert scan_called == 4, scan_called
+        t.executed()
+        assert tm.next_task() == None
+        assert scan_called == 4, scan_called
 
-       tm = SCons.Taskmaster.Taskmaster([n5])
-       t = tm.next_task()
+        tm = SCons.Taskmaster.Taskmaster([n5])
+        t = tm.next_task()
         assert t.get_target() == n5, t.get_target()
-       t.executed()
-       assert tm.next_task() == None
-       assert scan_called == 5, scan_called
+        t.executed()
+        assert tm.next_task() == None
+        assert scan_called == 5, scan_called
     
     def test_cycle_detection(self):
         n1 = Node("n1")
@@ -302,30 +301,30 @@ class TaskmasterTestCase(unittest.TestCase):
             tm = SCons.Taskmaster.Taskmaster([n3])
             t = tm.next_task()
         except SCons.Errors.UserError, e:
-            assert str(e) == "Dependency cycle: n3 -> n1 -> n2 -> n3"
+            assert str(e) == "Dependency cycle: n3 -> n1 -> n2 -> n3", str(e)
         else:
             assert 0
         
     def test_is_blocked(self):
         """Test whether a task is blocked
 
-       Both default and overridden in a subclass.
-       """
-       tm = SCons.Taskmaster.Taskmaster()
-       assert not tm.is_blocked()
+        Both default and overridden in a subclass.
+        """
+        tm = SCons.Taskmaster.Taskmaster()
+        assert not tm.is_blocked()
 
-       class MyTM(SCons.Taskmaster.Taskmaster):
-           def is_blocked(self):
-               return 1
-       tm = MyTM()
-       assert tm.is_blocked() == 1
+        class MyTM(SCons.Taskmaster.Taskmaster):
+            def is_blocked(self):
+                return 1
+        tm = MyTM()
+        assert tm.is_blocked() == 1
 
     def test_stop(self):
         """Test the stop() method
 
         Both default and overridden in a subclass.
         """
-        global built
+        global built_text
 
         n1 = Node("n1")
         n2 = Node("n2")
@@ -334,48 +333,33 @@ class TaskmasterTestCase(unittest.TestCase):
         tm = SCons.Taskmaster.Taskmaster([n3])
         t = tm.next_task()
         t.execute()
-        assert built == "n1 built"
+        assert built_text == "n1 built", built_text
         t.executed()
+        assert built_text == "n1 built really", built_text
 
         tm.stop()
         assert tm.next_task() is None
 
         class MyTM(SCons.Taskmaster.Taskmaster):
             def stop(self):
-                global built
-                built = "MyTM.stop()"
+                global built_text
+                built_text = "MyTM.stop()"
                 SCons.Taskmaster.Taskmaster.stop(self)
 
         n1 = Node("n1")
         n2 = Node("n2")
         n3 = Node("n3", [n1, n2])
 
-        built = None
+        built_text = None
         tm = MyTM([n3])
         tm.next_task().execute()
-        assert built == "n1 built"
+        assert built_text == "n1 built"
 
         tm.stop()
-        assert built == "MyTM.stop()"
+        assert built_text == "MyTM.stop()"
         assert tm.next_task() is None
 
-    def test_add_ready(self):
-        """Test adding a task to the ready queue"""
-        class MyTask:
-            def __init__(self, tm, tlist, top, scanner):
-                pass
-            def make_ready(self):
-                pass
-        n1 = Node("n1")
-        tm = SCons.Taskmaster.Taskmaster([n1], tasker = MyTask)
-        task = MyTask(tm, [], 0, None)
-        tm.add_ready(task)
-        assert tm.ready == [ task ], tm.ready
-
-    def test_pending_to_ready(self):
-        pass
-    
-    def test_pending_remove(self):
+    def test_executed(self):
         pass
 
 
@@ -383,4 +367,4 @@ class TaskmasterTestCase(unittest.TestCase):
 if __name__ == "__main__":
     suite = unittest.makeSuite(TaskmasterTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
-       sys.exit(1)
+        sys.exit(1)
index 1a02447a95cf79f991870b6a771d083121ea033a..dbaa8b557064b9768000298c3ad1412d617696de 100644 (file)
@@ -61,7 +61,7 @@ import re
 
 include_re = re.compile(r'^include\s+(\S+)$', re.M)
 
-def kfile_scan(node, env, arg):
+def kfile_scan(node, env, target, arg):
     contents = node.get_contents()
     includes = include_re.findall(contents)
     return includes
@@ -77,7 +77,7 @@ env.Command('foo', 'foo.k', '%s build.py $SOURCES $TARGET')
 
 bar_in = File('bar.in')
 env.Command('bar', bar_in, '%s build.py $SOURCES  $TARGET')
-bar_in.scanner_set(kscan)
+bar_in.source_scanner = kscan
 """ % (python, python))
 
 test.write('foo.k', 
@@ -132,4 +132,6 @@ test.fail_test(test.read('foo') != "foo.k 1 line 1\nxxx 2\nyyy 2\nfoo.k 1 line 4
 
 test.fail_test(test.read('bar') != "yyy 2\nbar.in 1 line 2\nbar.in 1 line 3\nzzz 2\n")
 
+test.run(arguments = 'foo', stdout='scons: "foo" is up to date.\n')
+
 test.pass_test()
index 3be8da4344c61940e579ef49740fe5f766a63e1b..8116a1fd8c5662ab8847c19f652f1aba82b07a91 100644 (file)
@@ -43,15 +43,17 @@ test.write('f1.c', r"""
 void
 f1(void)
 {
-       printf("f1.c\n");
-}
+        printf("f1.c\n");
+} 
 """)
 
-test.run(arguments = ".", stdout = "", stderr=r"""
+test.run(arguments = ".", stderr=r"""
 SCons error: Dependency cycle: .*foo1.* -> .*foo3.* -> .*foo2.* -> .*foo1.* -> \.
 .*
 """, status=2)
 
+test.fail_test(test.stdout() == "")
+
 
 test.pass_test()
 
index 35a3f066b0746730ea374a48bd31b20b7e0f2d01..42274c45e0ef101807a811a7a6a1289278b5d1c5 100644 (file)
@@ -77,12 +77,12 @@ tree = """
 +-%s
   +-foo%s
   | +-foo.c
-  |   +-foo.h
-  |   +-bar.h
+  | +-foo.h
+  | +-bar.h
   +-bar%s
     +-bar.c
-      +-bar.h
-      +-foo.h
+    +-bar.h
+    +-foo.h
 """ % (foo, obj,obj)
 
 test.fail_test(string.find(test.stdout(), tree) == -1)
@@ -90,6 +90,35 @@ test.fail_test(string.find(test.stdout(), tree) == -1)
 test.run(arguments = "--debug=tree " + foo)
 test.fail_test(string.find(test.stdout(), tree) == -1)
 
+tree = """scons: \".\" is up to date.
+
++-.
+  +-SConstruct
+  +-bar.c
+  +-bar.h
+  +-bar%(obj)s
+  | +-bar.c
+  | +-bar.h
+  | +-foo.h
+  +-%(foo)s
+  | +-foo%(obj)s
+  | | +-foo.c
+  | | +-foo.h
+  | | +-bar.h
+  | +-bar%(obj)s
+  |   +-bar.c
+  |   +-bar.h
+  |   +-foo.h
+  +-foo.c
+  +-foo.h
+  +-foo%(obj)s
+    +-foo.c
+    +-foo.h
+    +-bar.h
+""" % globals()
+test.run(arguments = "--debug=tree .")
+test.fail_test(string.find(test.stdout(), tree) != 0)
+
 test.run(arguments = "--debug=pdb", stdin = "n\ns\nq\n")
 test.fail_test(string.find(test.stdout(), "(Pdb)") == -1)
 test.fail_test(string.find(test.stdout(), "scons") == -1)