Support building (sub)directories.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 25 Oct 2001 13:11:10 +0000 (13:11 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Thu, 25 Oct 2001 13:11:10 +0000 (13:11 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@106 fdb21ef1-2011-0410-befe-b5e4ea1792b1

16 files changed:
src/engine/SCons/Job.py
src/engine/SCons/JobTests.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/Sig/SigTests.py
src/engine/SCons/Sig/__init__.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
src/script/scons.py
test/Command.py
test/ENV.py
test/Program.py
test/option-c.py
test/subdir.py [new file with mode: 0644]

index 17315fc37f355d0dd46f41ffa808336d4d4a667f..a3707becd6d486a0ba987650df15f78df579dd46 100644 (file)
@@ -111,9 +111,9 @@ class Serial:
             except:
                 # Let the failed() callback function arrange for the
                 # build to stop if that's appropriate.
-                self.taskmaster.failed(task)
+                task.failed()
             else:
-                self.taskmaster.executed(task)
+                task.executed()
 
     def stop(self):
         """Serial jobs are always finished when start() returns, so there
@@ -246,9 +246,9 @@ class Parallel:
                     # Let the failed() callback function arrange for
                     # calling self.jobs.stop() to to stop the build
                     # if that's appropriate.
-                    self.taskmaster.failed(task)
+                    task.failed()
                 else:
-                    self.taskmaster.executed(task)
+                    task.executed()
 
                 # signal the cv whether the task failed or not,
                 # or otherwise the other Jobs might
index 7026726af5a946c57c57e342df5802889bdf2a9c..e5168a4808d2526dfd48f1326011ae41eedfcc52 100644 (file)
@@ -75,15 +75,39 @@ class Task:
         self.taskmaster.end_list.append(self.i)
         self.taskmaster.guard.release()
 
+    def executed(self):
+        self.taskmaster.num_executed = self.taskmaster.num_executed + 1
+
+        self.taskmaster.test_case.failUnless(self.was_executed,
+                                  "the task wasn't really executed")
+        self.taskmaster.test_case.failUnless(self.__class__ is Task,
+                                  "the task wasn't really a Task instance")
+
+    def failed(self):
+        self.taskmaster.num_failed = self.taskmaster.num_failed + 1
+        self.taskmaster.stop = 1
+
 class ExceptionTask:
     """A dummy task class for testing purposes."""
 
     def __init__(self, i, taskmaster):
-        pass
+        self.taskmaster = taskmaster
         
     def execute(self):
         raise "exception"
 
+    def executed(self):
+        self.taskmaster.num_executed = self.taskmaster.num_executed + 1
+
+        self.taskmaster.test_case.failUnless(self.was_executed,
+                                  "the task wasn't really executed")
+        self.taskmaster.test_case.failUnless(self.__class__ is Task,
+                                  "the task wasn't really a Task instance")
+
+    def failed(self):
+        self.taskmaster.num_failed = self.taskmaster.num_failed + 1
+        self.taskmaster.stop = 1
+
 class Taskmaster:
     """A dummy taskmaster class for testing the job classes."""
 
@@ -123,18 +147,6 @@ class Taskmaster:
 
     def all_tasks_are_iterated(self):
         return self.num_iterated == self.num_tasks
-
-    def executed(self, task):
-        self.num_executed = self.num_executed + 1
-
-        self.test_case.failUnless(task.was_executed,
-                                  "the task wasn't really executed")
-        self.test_case.failUnless(task.__class__ is Task,
-                                  "the task wasn't really a Task instance")
-
-    def failed(self, task):
-        self.num_failed = self.num_failed + 1
-        self.stop = 1
     
     def is_blocked(self):
         # simulate blocking tasks
@@ -241,13 +253,3 @@ if __name__ == "__main__":
         sys.exit(2)
     elif not result.wasSuccessful():
         sys.exit(1)
-
-            
-
-        
-    
-    
-    
-    
-
-
index e44da2266df296968f39d8595f637246248667b6..cfb4142e94972bfcc48342ab1e09e56947bded62 100644 (file)
@@ -35,7 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os
 import os.path
-from SCons.Node import Node
+import SCons.Node
 from UserDict import UserDict
 import sys
 
@@ -119,6 +119,7 @@ class FS:
         self.Root = PathDict()
         self.Top = self.__doLookup(Dir, path)
         self.Top.path = '.'
+        self.Top.path_ = './'
 
     def __doLookup(self, fsclass, name, directory=None):
         """This method differs from the File and Dir factory methods in
@@ -228,7 +229,7 @@ class FS:
 
 
 
-class Entry(Node):
+class Entry(SCons.Node.Node):
     """A generic class for file system entries.  This class if for
     when we don't know yet whether the entry being looked up is a file
     or a directory.  Instances of this class can morph into either
@@ -241,8 +242,9 @@ class Entry(Node):
        our relative and absolute paths, identify our parent
        directory, and indicate that this node should use
        signatures."""
-        Node.__init__(self)
+        SCons.Node.Node.__init__(self)
 
+        self.name = name
         if directory:
             self.abspath = os.path.join(directory.abspath, name)
             if str(directory.path) == '.':
@@ -251,13 +253,18 @@ class Entry(Node):
                 self.path = os.path.join(directory.path, name)
         else:
             self.abspath = self.path = name
-       self.parent = directory
+        self.path_ = self.path
+        self.abspath_ = self.abspath
+        self.dir = directory
        self.use_signature = 1
 
     def __str__(self):
        """A FS node's string representation is its path name."""
        return self.path
 
+    def set_signature(self, sig):
+        SCons.Node.Node.set_signature(self, sig)
+
     def exists(self):
         return os.path.exists(self.path)
 
@@ -299,17 +306,18 @@ class Dir(Entry):
        into the file system tree.  Specify that directories (this
        node) don't use signatures for currency calculation."""
 
-        self.path = os.path.join(self.path, '')
-        self.abspath = os.path.join(self.abspath, '')
+        self.path_ = os.path.join(self.path, '')
+        self.abspath_ = os.path.join(self.abspath, '')
 
         self.entries = PathDict()
         self.entries['.'] = self
-       if hasattr(self, 'parent'):
-            self.entries['..'] = self.parent
-           delattr(self, 'parent')
+        if hasattr(self, 'dir'):
+            self.entries['..'] = self.dir
        else:
            self.entries['..'] = None
         self.use_signature = None
+        self.builder = 1
+        self._sconsign = None
 
     def up(self):
         return self.entries['..']
@@ -321,15 +329,42 @@ class Dir(Entry):
             return self.entries['..'].root()
 
     def children(self):
-       return map(lambda x, s=self: s.entries[x],
+       #XXX --random:  randomize "dependencies?"
+       kids = map(lambda x, s=self: s.entries[x],
                   filter(lambda k: k != '.' and k != '..',
                          self.entries.keys()))
+       kids.sort()
+       return kids
+
+    def build(self):
+        """A null "builder" for directories."""
+        pass
+
+    def set_signature(self, sig):
+        """A directory has no signature."""
+        pass
 
     def current(self):
-        """Always return that a directory node is out-of-date so
-        that it will always be "built" by trying to build all of
-        its directory entries."""
-        return 0
+        """If all of our children were up-to-date, then this
+        directory was up-to-date, too."""
+        state = 0
+        for kid in self.children():
+            s = kid.get_state()
+            if s and (not state or s > state):
+                state = s
+        import SCons.Node
+        if state == SCons.Node.up_to_date:
+            return 1
+        else:
+            return 0
+
+    def sconsign(self):
+        if not self._sconsign:
+            #XXX Rework this to get rid of the hard-coding
+            import SCons.Sig
+            import SCons.Sig.MD5
+            self._sconsign = SCons.Sig.SConsignFile(self.path, SCons.Sig.MD5)
+        return self._sconsign
 
 
 # XXX TODO?
@@ -362,13 +397,25 @@ class File(Entry):
         pass
 
     def root(self):
-        return self.parent.root()
+        return self.dir.root()
 
     def get_contents(self):
         return open(self.path, "r").read()
 
     def get_timestamp(self):
-        return os.path.getmtime(self.path)
+        if self.exists():
+            return os.path.getmtime(self.path)
+        else:
+            return 0
+
+    def set_signature(self, sig):
+        Entry.set_signature(self, sig)
+        #XXX Rework this to get rid of the hard-coding
+        import SCons.Sig.MD5
+        self.dir.sconsign().set(self.name, self.get_timestamp(), sig, SCons.Sig.MD5)
+
+    def get_oldentry(self):
+        return self.dir.sconsign().get(self.name)
 
 
 default_fs = FS()
index 044f83fe6ff3394f4194e2df3e7414d8409c7652..8b1ee5a6790e030079a3f83e558ad6e6138cd117 100644 (file)
@@ -84,36 +84,60 @@ class FSTestCase(unittest.TestCase):
 
         for sep in seps:
 
-            def Dir_test(lpath, path, abspath, up_path, fileSys=fs, s=sep):
+            def Dir_test(lpath, path_, abspath_, up_path_, fileSys=fs, s=sep):
                 dir = fileSys.Dir(string.replace(lpath, '/', s))
 
+                def strip_slash(p):
+                    if p[-1] == '/' and len(p) > 1:
+                        p = p[:-1]
+                    return p
+                path = strip_slash(path_)
+                abspath = strip_slash(abspath_)
+                up_path = strip_slash(up_path_)
+               name = string.split(abspath, '/')[-1]
+
                if os.sep != '/':
                     path = string.replace(path, '/', os.sep)
+                    path_ = string.replace(path_, '/', os.sep)
                     abspath = string.replace(abspath, '/', os.sep)
+                    abspath_ = string.replace(abspath_, '/', os.sep)
                     up_path = string.replace(up_path, '/', os.sep)
+                    up_path_ = string.replace(up_path_, '/', os.sep)
 
+               assert dir.name == name, \
+                       "dir.name %s != expected name %s" % \
+                       (dir.name, name)
                 assert dir.path == path, \
                        "dir.path %s != expected path %s" % \
                        (dir.path, path)
                 assert str(dir) == path, \
                        "str(dir) %s != expected path %s" % \
                        (str(dir), path)
+                assert dir.path_ == path_, \
+                       "dir.path_ %s != expected path_ %s" % \
+                       (dir.path_, path_)
                 assert dir.abspath == abspath, \
                        "dir.abspath %s != expected absolute path %s" % \
                        (dir.abspath, abspath)
+                assert dir.abspath_ == abspath_, \
+                       "dir.abspath_ %s != expected absolute path_ %s" % \
+                       (dir.abspath_, abspath_)
                 assert dir.up().path == up_path, \
                        "dir.up().path %s != expected parent path %s" % \
                        (dir.up().path, up_path)
+                assert dir.up().path_ == up_path_, \
+                       "dir.up().path_ %s != expected parent path_ %s" % \
+                       (dir.up().path_, up_path_)
 
-            Dir_test('foo',         'foo/',        sub_dir_foo,       '.')
+            Dir_test('foo',         'foo/',        sub_dir_foo,       './')
             Dir_test('foo/bar',     'foo/bar/',    sub_dir_foo_bar,   'foo/')
             Dir_test('/foo',        '/foo/',       '/foo/',           '/')
             Dir_test('/foo/bar',    '/foo/bar/',   '/foo/bar/',       '/foo/')
             Dir_test('..',          sub,           sub,               wp)
-            Dir_test('foo/..',      '.',           sub_dir,           sub)
+            Dir_test('foo/..',      './',          sub_dir,           sub)
             Dir_test('../foo',      sub_foo,       sub_foo,           sub)
-            Dir_test('.',           '.',           sub_dir,           sub)
-            Dir_test('./.',         '.',           sub_dir,           sub)
+            Dir_test('.',           './',          sub_dir,           sub)
+            Dir_test('./.',         './',          sub_dir,           sub)
             Dir_test('foo/./bar',   'foo/bar/',    sub_dir_foo_bar,   'foo/')
 
             try:
@@ -136,7 +160,7 @@ class FSTestCase(unittest.TestCase):
                 f2 = fs.File('d1')
             except TypeError, x:
                 assert str(x) == ("Tried to lookup Dir '%s' as a File." %
-                                 os.path.join('d1', '')), x
+                                  'd1'), x
             except:
                raise
 
@@ -150,10 +174,16 @@ class FSTestCase(unittest.TestCase):
             fs.Dir(string.join(['ddd', 'd1', 'f5'], sep))
             kids = map(lambda x: x.path, dir.children())
             kids.sort()
-            assert kids == [os.path.join('ddd', 'd1', ''),
+            assert kids == [os.path.join('ddd', 'd1'),
                            os.path.join('ddd', 'f1'),
                            os.path.join('ddd', 'f2'),
                            os.path.join('ddd', 'f3')]
+            kids = map(lambda x: x.path_, dir.children())
+            kids.sort()
+            assert kids == [os.path.join('ddd', 'd1', ''),
+                            os.path.join('ddd', 'f1'),
+                            os.path.join('ddd', 'f2'),
+                            os.path.join('ddd', 'f3')]
 
         # Test for sub-classing of node building.
         global built_it
@@ -164,7 +194,7 @@ class FSTestCase(unittest.TestCase):
         d1.builder_set(Builder())
         d1.env_set(Environment())
         d1.build()
-        assert built_it
+        assert not built_it
 
         assert d1.get_parents() == [] 
 
@@ -178,35 +208,71 @@ class FSTestCase(unittest.TestCase):
 
        e1 = fs.Entry("d1")
        assert e1.__class__.__name__ == 'Dir'
-       assert e1.path == "d1/", e1.path
+        assert e1.path == "d1", e1.path
+        assert e1.path_ == "d1/", e1.path_
+       assert e1.dir.path == ".", e1.dir.path
 
        e2 = fs.Entry("d1/f1")
        assert e2.__class__.__name__ == 'File'
        assert e2.path == "d1/f1", e2.path
+        assert e2.path_ == "d1/f1", e2.path_
+       assert e2.dir.path == "d1", e2.dir.path
 
        e3 = fs.Entry("e3")
        assert e3.__class__.__name__ == 'Entry'
        assert e3.path == "e3", e3.path
+        assert e3.path_ == "e3", e3.path_
+       assert e3.dir.path == ".", e3.dir.path
 
        e4 = fs.Entry("d1/e4")
        assert e4.__class__.__name__ == 'Entry'
        assert e4.path == "d1/e4", e4.path
+        assert e4.path_ == "d1/e4", e4.path_
+       assert e4.dir.path == "d1", e4.dir.path
 
        e5 = fs.Entry("e3/e5")
        assert e3.__class__.__name__ == 'Dir'
-       assert e3.path == "e3/", e3.path
+        assert e3.path == "e3", e3.path
+        assert e3.path_ == "e3/", e3.path_
+       assert e3.dir.path == ".", e3.dir.path
        assert e5.__class__.__name__ == 'Entry'
        assert e5.path == "e3/e5", e5.path
+        assert e5.path_ == "e3/e5", e5.path_
+       assert e5.dir.path == "e3", e5.dir.path
 
        e6 = fs.Dir("d1/e4")
        assert e6 is e4
        assert e4.__class__.__name__ == 'Dir'
-       assert e4.path == "d1/e4/", e4.path
+        assert e4.path == "d1/e4", e4.path
+        assert e4.path_ == "d1/e4/", e4.path_
+       assert e4.dir.path == "d1", e4.dir.path
 
        e7 = fs.File("e3/e5")
        assert e7 is e5
        assert e5.__class__.__name__ == 'File'
        assert e5.path == "e3/e5", e5.path
+        assert e5.path_ == "e3/e5", e5.path_
+       assert e5.dir.path == "e3", e5.dir.path
+
+        #XXX test set_signature()
+
+        #XXX test exists()
+
+        #XXX test current() for directories
+
+        #XXX test sconsign() for directories
+
+        #XXX test set_signature() for directories
+
+        #XXX test build() for directories
+
+        #XXX test root()
+
+        #XXX test get_contents()
+
+        #XXX test get_timestamp()
+
+        #XXX test get_oldentry()
 
 
 
index b8015c287c99127e27f801d61c57e257cb4d0c40..cf7e59b244756ed2442d2e9deba8c0b5b4dab03c 100644 (file)
@@ -90,6 +90,8 @@ class NodeTestCase(unittest.TestCase):
        assert node.builder == b
 
     def test_current(self):
+        """Test the default current() method
+        """
         node = SCons.Node.Node()
         assert node.current() is None
 
@@ -226,6 +228,10 @@ class NodeTestCase(unittest.TestCase):
         assert node.get_state() == None
         node.set_state(SCons.Node.executing)
         assert node.get_state() == SCons.Node.executing
+        assert SCons.Node.pending < SCons.Node.executing
+        assert SCons.Node.executing < SCons.Node.up_to_date
+        assert SCons.Node.up_to_date < SCons.Node.executed
+        assert SCons.Node.executed < SCons.Node.failed
 
     def test_walker(self):
        """Test walking a Node tree.
index b7bdecf85455d8fb5f99e0f5ee9dc2926da0583d..2995576ab28c1a891ebbcca9892f76d6c6f96e53 100644 (file)
@@ -36,12 +36,18 @@ import string
 import types
 import copy
 
-# Node states:
-executing = 1
-executed = 2
+# Node states
+#
+# These are in "priority" order, so that the maximum value for any
+# child/dependency of a node represents the state of that node if
+# it has no builder of its own.  The canonical example is a file
+# system directory, which is only up to date if all of its children
+# were up to date.
+pending = 1
+executing = 2
 up_to_date = 3
-failed = 4
-pending = 5
+executed = 4
+failed = 5
 
 class Node:
     """The base Node class, for entities that we know how to
index b42e464a874c1f15f465625243ac028c95ba637a..2122fe7223b15dcaa4ef71956effb13e0a177c66 100644 (file)
@@ -52,6 +52,8 @@ class DummyNode:
         self.builder = file.builder
        self.depends = []
         self.use_signature = 1
+        self.oldtime = 0
+        self.oldsig = 0
         
     def get_contents(self):
         # a file that doesn't exist has no contents:
@@ -85,6 +87,9 @@ class DummyNode:
     def get_signature(self):
         return self.sig
 
+    def get_oldentry(self):
+        return (self.oldtime, self.oldsig)
+
 
 def create_files(test):
     args  = [(test.workpath('f1.c'), 'blah blah', 111, 0),     #0
@@ -119,6 +124,15 @@ def create_nodes(files):
     nodes[10].sources = [nodes[9]]
 
     return nodes
+
+def current(calc, node):
+    s = calc.get_signature(node)
+    return calc.current(node, s)
+
+def write(calc, nodes):
+    for node in nodes:
+        node.oldtime = node.file.timestamp
+        node.oldsig = calc.get_signature(node)
         
 
 class SigTestBase:
@@ -140,79 +154,77 @@ class SigTestBase:
         calc = SCons.Sig.Calculator(self.module)
 
         for node in nodes:
-            self.failUnless(not calc.current(node), "none of the nodes should be current")
+            self.failUnless(not current(calc, node), "none of the nodes should be current")
 
         # simulate a build:
         self.files[1].modify('built', 222)
         self.files[7].modify('built', 222)
         self.files[9].modify('built', 222)
         self.files[10].modify('built', 222)
-        
-        calc.write(nodes)
 
     def test_built(self):
 
         nodes = create_nodes(self.files)
 
         calc = SCons.Sig.Calculator(self.module)
+
+        write(calc, nodes)
         
         for node in nodes:
-            self.failUnless(calc.current(node), "all of the nodes should be current")
-
-        calc.write(nodes)
+            self.failUnless(current(calc, node), "all of the nodes should be current")
 
     def test_modify(self):
 
         nodes = create_nodes(self.files)
 
+        calc = SCons.Sig.Calculator(self.module)
+
+        write(calc, nodes)
+
         #simulate a modification of some files
         self.files[0].modify('blah blah blah', 333)
         self.files[3].modify('blah blah blah', 333)
         self.files[6].modify('blah blah blah', 333)
         self.files[8].modify('blah blah blah', 333)
 
-        calc = SCons.Sig.Calculator(self.module)
-
-        self.failUnless(not calc.current(nodes[0]), "modified directly")
-        self.failUnless(not calc.current(nodes[1]), "direct source modified")
-        self.failUnless(calc.current(nodes[2]))
-        self.failUnless(not calc.current(nodes[3]), "modified directly")
-        self.failUnless(calc.current(nodes[4]))
-        self.failUnless(calc.current(nodes[5]))
-        self.failUnless(not calc.current(nodes[6]), "modified directly")
-        self.failUnless(not calc.current(nodes[7]), "indirect source modified")
-        self.failUnless(not calc.current(nodes[8]), "modified directory")
-        self.failUnless(not calc.current(nodes[9]), "direct source modified")
-        self.failUnless(not calc.current(nodes[10]), "indirect source modified")
-
-        calc.write(nodes)
+        self.failUnless(not current(calc, nodes[0]), "modified directly")
+        self.failUnless(not current(calc, nodes[1]), "direct source modified")
+        self.failUnless(current(calc, nodes[2]))
+        self.failUnless(not current(calc, nodes[3]), "modified directly")
+        self.failUnless(current(calc, nodes[4]))
+        self.failUnless(current(calc, nodes[5]))
+        self.failUnless(not current(calc, nodes[6]), "modified directly")
+        self.failUnless(not current(calc, nodes[7]), "indirect source modified")
+        self.failUnless(not current(calc, nodes[8]), "modified directory")
+        self.failUnless(not current(calc, nodes[9]), "direct source modified")
+        self.failUnless(not current(calc, nodes[10]), "indirect source modified")
 
     def test_delete(self):
         
         nodes = create_nodes(self.files)
+        
+        calc = SCons.Sig.Calculator(self.module)
+
+        write(calc, nodes)
 
         #simulate the deletion of some files
         self.files[1].modify(None, 0)
         self.files[7].modify(None, 0)
         self.files[9].modify(None, 0)
-        
-        calc = SCons.Sig.Calculator(self.module)
 
-        self.failUnless(calc.current(nodes[0]))
-        self.failUnless(not calc.current(nodes[1]), "deleted")
-        self.failUnless(calc.current(nodes[2]))
-        self.failUnless(calc.current(nodes[3]))
-        self.failUnless(calc.current(nodes[4]))
-        self.failUnless(calc.current(nodes[5]))
-        self.failUnless(calc.current(nodes[6]))
-        self.failUnless(not calc.current(nodes[7]), "deleted")
-        self.failUnless(calc.current(nodes[8]))
-        self.failUnless(not calc.current(nodes[9]), "deleted")
-        self.failUnless(calc.current(nodes[10]),
+        self.failUnless(current(calc, nodes[0]))
+        self.failUnless(not current(calc, nodes[1]), "deleted")
+        self.failUnless(current(calc, nodes[2]))
+        self.failUnless(current(calc, nodes[3]))
+        self.failUnless(current(calc, nodes[4]))
+        self.failUnless(current(calc, nodes[5]))
+        self.failUnless(current(calc, nodes[6]))
+        self.failUnless(not current(calc, nodes[7]), "deleted")
+        self.failUnless(current(calc, nodes[8]))
+        self.failUnless(not current(calc, nodes[9]), "deleted")
+        self.failUnless(current(calc, nodes[10]),
                         "current even though it's source was deleted") 
 
-        calc.write(nodes)
-
 class MD5TestCase(unittest.TestCase, SigTestBase):
     """Test MD5 signatures"""
 
index 8e4ed56eb85e82dc4c0d20b13f599f9e0bf9aa67..36bcebae01eeb9e20e211a455c546bb9d5caa98c 100644 (file)
@@ -32,6 +32,15 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 import os.path
 import string
 
+
+#XXX Get rid of the global array so this becomes re-entrant.
+sig_files = []
+
+def write():
+    global sig_files
+    for sig_file in sig_files:
+        sig_file.write()
+
 class SConsignFile:
     """
     Encapsulates reading and writing a .sconsign file.
@@ -56,6 +65,9 @@ class SConsignFile:
                 time, signature = map(string.strip, string.split(rest, " "))
                 self.entries[filename] = (int(time), module.from_string(signature))
 
+        global sig_files
+        sig_files.append(self)
+
     def get(self, filename):
         """
         Get the signature for a file
@@ -103,7 +115,6 @@ class Calculator:
         module - the signature module to use for signature calculations
         """
         self.module = module
-        self.sig_files = {}
 
     
     def collect(self, node, signatures):
@@ -116,24 +127,11 @@ class Calculator:
         """
         for source_node in node.children():
             if not signatures.has_key(source_node):
-                signature = self.signature(source_node)
+                signature = self.get_signature(source_node)
                 signatures[source_node] = signature
                 self.collect(source_node, signatures)
 
-    def get_sig_file(self, dir):
-        """
-        Get a sconsign file from the cache, or add it to the cache.
-
-        dir - the dir for the sconsign file
-        returns - the sconsign file
-        """
-        if self.sig_files.has_key(dir):
-            return self.sig_files[dir]
-        else:
-            self.sig_files[dir] = SConsignFile(dir, self.module)
-            return self.sig_files[dir]
-
-    def signature(self, node):
+    def get_signature(self, node):
         """
         Get the signature for a node.
 
@@ -141,7 +139,7 @@ class Calculator:
         returns - the signature or None if the signature could not
         be computed.
 
-        This method also stores the signature in the node and
+        This method does not store the signature in the node and
         in the .sconsign file.
         """
 
@@ -163,22 +161,9 @@ class Calculator:
             # XXX handle nodes that are not under the source root
             sig = self.module.signature(node)
 
-        node.set_signature(sig)
-
-        dir, filename = os.path.split(node.path)
-        if node.exists():
-            timestamp = node.get_timestamp()
-        else:
-            timestamp = 0
-            
-        self.get_sig_file(dir).set(filename,
-                                   timestamp,
-                                   sig,
-                                   self.module)
-
         return sig
 
-    def current(self, node):
+    def current(self, node, newsig):
         """
         Check if a node is up to date.
 
@@ -196,30 +181,11 @@ class Calculator:
             # that doesn't exist, or a directory.
             return c
 
-        dir, filename = os.path.split(node.path)
-        oldtime, oldsig = self.get_sig_file(dir).get(filename)
+        oldtime, oldsig = node.get_oldentry()
 
         newtime = node.get_timestamp()
 
         if not node.builder and newtime == oldtime:
             newsig = oldsig
-        else:
-            newsig = self.signature(node)
         
         return self.module.current(newsig, oldsig)
-
-    def write(self, nodes):
-        """
-        Write out all of the signature files.
-        
-        nodes - the nodes whose signatures may have changed durring
-        the build
-        """
-
-        # make sure all the signatures have been calculated:
-        for node in nodes:
-            self.signature(node)
-            
-        for sig_file in self.sig_files.values():
-            sig_file.write()
-        
index 3b4ee85df524b68cfbeb98c41155ec1bcf90376c..9e8e105497ad91a45f1289d4482f55b52d801bae 100644 (file)
@@ -38,25 +38,68 @@ import SCons.Node
 
 class Task:
     """Default SCons build engine task."""
-    def __init__(self,target):
+    def __init__(self, tm, target, top):
+        self.tm = tm
         self.target = target
+        self.sig = None
+        self.top = top
 
     def execute(self):
         self.target.build()
 
-    def set_state(self, state):
-        return self.target.set_state(state)
-
     def get_target(self):
         return self.target
+
+    def set_sig(self, sig):
+        self.sig = sig
+
+    def set_state(self, state):
+        self.target.set_state(state)
+
+    def up_to_date(self):
+        self.set_state(SCons.Node.up_to_date)
+
+    def executed(self):
+        self.set_state(SCons.Node.executed)
+        self.tm.add_pending(self.target)
+        self.target.set_signature(self.sig)
+
+    def failed(self):
+        self.fail_stop()
+
+    def fail_stop(self):
+        self.set_state(SCons.Node.failed)
+        self.tm.stop()
+
+    def fail_continue(self):
+        def get_parents(node): return node.get_parents()
+        walker = SCons.Node.Walker(self.target, get_parents)
+        while 1:
+            node = walker.next()
+            if node == None: break
+            self.tm.remove_pending(node)
+            node.set_state(SCons.Node.failed)
         
-def current(node):
-    """Default SCons build engine is-it-current function.
 
-    This returns "always out of date," so every node is always
-    built/visited.
-    """
-    return None
+
+class Calc:
+    def get_signature(self, node):
+        """
+        """
+        return None
+
+    def set_signature(self, node):
+        """
+        """
+        pass
+
+    def current(self, node, sig):
+        """Default SCons build engine is-it-current function.
+    
+        This returns "always out of date," so every node is always
+        built/visited.
+        """
+        return 0
 
 
 
@@ -64,92 +107,85 @@ class Taskmaster:
     """A generic Taskmaster for handling a bunch of targets.
 
     Classes that override methods of this class should call
-    the base class method, so this class can do it's thing.    
+    the base class method, so this class can do its thing.    
     """
 
-    def __init__(self,
-                 targets=[],
-                 tasker=Task,
-                 current=current,
-                 ignore_errors=0,
-                 keep_going_on_error=0):
+    def __init__(self, targets=[], tasker=Task, calc=Calc()):
         self.walkers = map(SCons.Node.Walker, targets)
         self.tasker = tasker
-        self.current = current
+        self.calc = calc
         self.targets = targets
         self.ready = []
         self.pending = 0
-        self.ignore_errors = ignore_errors
-        self.keep_going_on_error = keep_going_on_error
 
         self._find_next_ready_node()
-        
+
     def next_task(self):
         if self.ready:
-            n = self.ready.pop()
-            n.set_state(SCons.Node.executing)
+            task = self.ready.pop()
+            task.set_state(SCons.Node.executing)
             if not self.ready:
                 self._find_next_ready_node()
-
-            return self.tasker(n)
+            return task
         else:
             return None
-        
+
     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)
-            elif n.get_state() == SCons.Node.up_to_date:
-                self.up_to_date(n, self.walkers[0].is_done())
-            elif n.get_state() == None:
-                if not n.children_are_executed():
-                    n.set_state(SCons.Node.pending)
-                    self.pending = self.pending + 1
-                elif self.current(n):
-                    n.set_state(SCons.Node.up_to_date)
-                    self.up_to_date(n, self.walkers[0].is_done())
-                else:
-                    self.ready.append(n)
-                    return
-        
+                continue
+            if n.get_state():
+                # The state is set, so someone has already been here
+                # (finished or currently executing).  Find another one.
+                continue
+            if not n.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.
+                n.set_state(SCons.Node.up_to_date)
+                continue
+            task = self.tasker(self, n, self.walkers[0].is_done())
+            if not n.children_are_executed():
+                n.set_state(SCons.Node.pending)
+                n.task = task
+                self.pending = self.pending + 1
+                continue
+            sig = self.calc.get_signature(n)
+            task.set_sig(sig)
+            if self.calc.current(n, sig):
+                task.up_to_date()
+            else:
+                self.ready.append(task)
+                return None
     def is_blocked(self):
         return not self.ready and self.pending
 
-    def up_to_date(self, node):
-        pass
-
-    def executed(self, task):
-        task.set_state(SCons.Node.executed)
+    def stop(self):
+        self.walkers = []
+        self.pending = 0
+        self.ready = []
 
+    def add_pending(self, node):
         # add all the pending parents that are now executable to the 'ready'
         # queue:
-        n = task.get_target()
         ready = filter(lambda x: (x.get_state() == SCons.Node.pending
                                   and x.children_are_executed()),
-                       n.get_parents())
-        self.ready.extend(ready)
-        self.pending = self.pending - len(ready)
-        
-    def failed(self, task):
-        if self.ignore_errors:
-            self.executed(task)
-        else:
-            if self.keep_going_on_error:
-                # mark all the depants of this node as failed:
-                def get_parents(node): return node.get_parents()
-                walker = SCons.Node.Walker(task.get_target(), get_parents)
-                while 1:
-                    node = walker.next()
-                    if node == None: break
-                    if node.get_state() == SCons.Node.pending:
-                        self.pending = self.pending - 1
-                    node.set_state(SCons.Node.failed)
+                       node.get_parents())
+        for n in ready:
+            task = n.task
+            delattr(n, "task")
+            sig = self.calc.get_signature(n)
+            task.set_sig(sig)
+            if self.calc.current(n, sig):
+                task.up_to_date()
             else:
-                # terminate the build:
-                self.walkers = []
-                self.pending = 0
-                self.ready = []
+                self.ready.append(task)
+        self.pending = self.pending - len(ready)
 
-            task.set_state(SCons.Node.failed)
+    def remove_pending(self, node):
+        if node.get_state() == SCons.Node.pending:
+            self.pending = self.pending - 1
index 809df6a385cae30fbf5934ae97a79dce97c55028..a15a673880e7bb1c5aa525e09e6cf7c14f657c90 100644 (file)
@@ -31,41 +31,94 @@ import SCons.Taskmaster
 
 
 built = None
+executed = None
 
 class Node:
     def __init__(self, name, kids = []):
         self.name = name
-       self.kids = kids
+        self.kids = kids
+        self.builder = Node.build
+        self.signature = None
         self.state = None
         self.parents = []
-        
+
         for kid in kids:
             kid.parents.append(self)
-            
+
     def build(self):
         global built
         built = self.name + " built"
 
     def children(self):
        return self.kids
-
+  
     def get_parents(self):
         return self.parents
-    
+
     def get_state(self):
         return self.state
 
     def set_state(self, state):
         self.state = state
 
+    def set_signature(self, sig):
+        self.signature = sig
+  
     def children_are_executed(self):
         return reduce(lambda x,y: ((y.get_state() == SCons.Node.executed
                                    or y.get_state() == SCons.Node.up_to_date)
                                    and x),
                       self.children(),
                       1)
+
+
+
+#class Task(unittest.TestCase):
+#    def test_execute(self):
+#        pass
+#
+#    def test_get_target(self):
+#        pass
+#
+#    def test_set_sig(self):
+#        pass
+#
+#    def test_set_state(self):
+#        pass
+#
+#    def test_up_to_date(self):
+#        pass
+#
+#    def test_executed(self):
+#        pass
+#
+#    def test_failed(self):
+#        pass
+#
+#    def test_fail_stop(self):
+#        pass
+#
+#    def test_fail_continue(self):
+#        pass
+
     
 
+class Task:
+    def __init__(self, target):
+        self.target = target
+
+    def get_target(self):
+        return self.target
+
+    def up_to_date(self):
+        pass
+
+    def executed(self):
+        pass
+
+    def failed(self):
+        pass
+
 
 class TaskmasterTestCase(unittest.TestCase):
 
@@ -73,17 +126,16 @@ class TaskmasterTestCase(unittest.TestCase):
        """Test fetching the next task
        """
        global built
-
-        n1 = Node("n1")
-        tm = SCons.Taskmaster.Taskmaster([n1,n1])
+        
+       n1 = Node("n1")
+        tm = SCons.Taskmaster.Taskmaster([n1, n1])
         t = tm.next_task()
-        tm.executed(t)
+        t.executed()
         t = tm.next_task()
         assert t == None
 
-        
-       n1 = Node("n1")
-       n2 = Node("n2")
+        n1 = Node("n1")
+        n2 = Node("n2")
         n3 = Node("n3", [n1, n2])
         
        tm = SCons.Taskmaster.Taskmaster([n3])
@@ -91,41 +143,42 @@ class TaskmasterTestCase(unittest.TestCase):
         t = tm.next_task()
         t.execute()
         assert built == "n1 built"
-        tm.executed(t)
+        t.executed()
 
         t = tm.next_task()
         t.execute()
         assert built == "n2 built"
-        tm.executed(t)
+        t.executed()
 
         t = tm.next_task()
         t.execute()
         assert built == "n3 built"
-        tm.executed(t)
+        t.executed()
 
         assert tm.next_task() == None
 
-       def current(node):
-           return 1
-
        built = "up to date: "
 
         global top_node
         top_node = n3
-       class MyTM(SCons.Taskmaster.Taskmaster):
-           def up_to_date(self, node, top):
-                if node == top_node:
-                    assert top
+        class MyTask(SCons.Taskmaster.Task):
+            def up_to_date(self):
+                if self.target == top_node:
+                    assert self.top
                 global built
-                built = built + " " + node.name
+                built = built + " " + self.target.name
+                SCons.Taskmaster.Task.up_to_date(self)
 
+        class MyCalc(SCons.Taskmaster.Calc):
+            def current(self, node, sig):
+                return 1
 
         n1.set_state(None)
         n2.set_state(None)
         n3.set_state(None)
-       tm = MyTM(targets = [n3], current = current)
+        tm = SCons.Taskmaster.Taskmaster(targets = [n3],
+                                         tasker = MyTask, calc = MyCalc())
        assert tm.next_task() == None
-        print built
        assert built == "up to date:  n1 n2 n3"
 
 
@@ -149,18 +202,18 @@ class TaskmasterTestCase(unittest.TestCase):
         t4 = tm.next_task()
         assert t4.get_target() == n4
         assert tm.is_blocked()
-        tm.executed(t4)
+        t4.executed()
         assert tm.is_blocked()
         
-        tm.executed(t1)
+        t1.executed()
         assert tm.is_blocked()
-        tm.executed(t2)
+        t2.executed()
         assert not tm.is_blocked()
         t3 = tm.next_task()
         assert t3.get_target() == n3
         assert tm.is_blocked()
 
-        tm.executed(t3)
+        t3.executed()
         assert not tm.is_blocked()
         t5 = tm.next_task()
         assert t5.get_target() == n5
@@ -173,7 +226,7 @@ class TaskmasterTestCase(unittest.TestCase):
         n4.set_state(SCons.Node.executed)
         tm = SCons.Taskmaster.Taskmaster([n4])
         assert tm.next_task() == None
-
+        
     def test_is_blocked(self):
         """Test whether a task is blocked
 
@@ -188,78 +241,50 @@ class TaskmasterTestCase(unittest.TestCase):
        tm = MyTM()
        assert tm.is_blocked() == 1
 
-    def test_executed(self):
-        """Test the executed() method
+    def test_stop(self):
+        """Test the stop() method
 
-       Both default and overridden in a subclass.
-       """
-       tm = SCons.Taskmaster.Taskmaster()
-        foo = Node('foo')
-       tm.executed(SCons.Taskmaster.Task(foo))
-
-       class MyTM(SCons.Taskmaster.Taskmaster):
-           def executed(self, task):
-               return 'x' + task
-       tm = MyTM()
-       assert tm.executed('foo') == 'xfoo'
+        Both default and overridden in a subclass.
+        """
+        global built
 
-    def test_ignore_errors(self):
         n1 = Node("n1")
         n2 = Node("n2")
-        n3 = Node("n3", [n1])
+        n3 = Node("n3", [n1, n2])
         
-        tm = SCons.Taskmaster.Taskmaster([n3, n2],
-                                         SCons.Taskmaster.Task,
-                                         SCons.Taskmaster.current,
-                                         1)
-
-        t = tm.next_task()
-        assert t.get_target() == n1
-        tm.failed(t)
-        t = tm.next_task()
-        assert t.get_target() == n3
-        tm.failed(t)
+        tm = SCons.Taskmaster.Taskmaster([n3])
         t = tm.next_task()
-        assert t.get_target() == n2
-        
+        t.execute()
+        assert built == "n1 built"
+        t.executed()
 
-    def test_keep_going(self):
-        n1 = Node("n1")
-        n2 = Node("n2")
-        n3 = Node("n3", [n1])
-        
-        tm = SCons.Taskmaster.Taskmaster([n3, n2],
-                                         SCons.Taskmaster.Task,
-                                         SCons.Taskmaster.current,
-                                         0,
-                                         1)
+        tm.stop()
+        assert tm.next_task() is None
 
-        tm.failed(tm.next_task())
-        t = tm.next_task()
-        assert t.get_target() == n2
-        tm.executed(t)
-        assert not tm.is_blocked()
-        t = tm.next_task()
-        assert t == None
+        class MyTM(SCons.Taskmaster.Taskmaster):
+            def stop(self):
+                global built
+                built = "MyTM.stop()"
+                SCons.Taskmaster.Taskmaster.stop(self)
 
+        n1 = Node("n1")
+        n2 = Node("n2")
+        n3 = Node("n3", [n1, n2])
 
-    def test_failed(self):
-        """Test the failed() method
+        built = None
+        tm = MyTM([n3])
+        tm.next_task().execute()
+        assert built == "n1 built"
 
-       Both default and overridden in a subclass.
-       """
-        foo = Node('foo')
-        bar = Node('bar')
-        tm = SCons.Taskmaster.Taskmaster([foo,bar])
-        tm.failed(tm.next_task())
-        assert tm.next_task() == None
-        
-       class MyTM(SCons.Taskmaster.Taskmaster):
-           def failed(self, task):
-               return 'y' + task
-       tm = MyTM()
-       assert tm.failed('foo') == 'yfoo'
+        tm.stop()
+        assert built == "MyTM.stop()"
+        assert tm.next_task() is None
 
+    #def test_add_pending(self):
+    #    passs
+    #
+    #def test_remove_pending(self):
+    #    passs
 
 
 
index 84d7f787f061041b7fa74191cc92aa2cf8ea1f7a..5f5d45d8180da5521f741dbbb48620552aa787a5 100644 (file)
@@ -66,9 +66,20 @@ class BuildTask(SCons.Taskmaster.Task):
         except BuildError, e:
             sys.stderr.write("scons: *** [%s] Error %d\n" % (e.node, e.stat))
             raise
+
+    def up_to_date(self):
+        if self.top:
+            print 'scons: "%s" is up to date.' % str(self.target)
+        SCons.Taskmaster.Task.up_to_date(self)
         
-    def set_state(self, state):
-        return self.target.set_state(state)
+    def failed(self):
+        global ignore_errors
+        if ignore_errors:
+            SCons.Taskmaster.Task.executed(self)
+        elif keep_going_on_error:
+            SCons.Taskmaster.Task.fail_continue(self)
+        else:
+            SCons.Taskmaster.Task.fail_stop(self)
 
 class CleanTask(SCons.Taskmaster.Task):
     """An SCons clean task."""
@@ -77,19 +88,6 @@ class CleanTask(SCons.Taskmaster.Task):
            os.unlink(self.target.path)
            print "Removed " + self.target.path
 
-class ScriptTaskmaster(SCons.Taskmaster.Taskmaster):
-    """Controlling logic for tasks.
-    
-    This is the stock Taskmaster from the build engine, except
-    that we override the up_to_date() method to provide our
-    script-specific up-to-date message for command-line targets,
-    and failed to provide the ignore-errors feature.
-    """
-    def up_to_date(self, node, top):
-        if top:
-            print 'scons: "%s" is up to date.' % node
-        SCons.Taskmaster.Taskmaster.up_to_date(self, node)
-
 
 # Global variables
 
@@ -100,6 +98,7 @@ num_jobs = 1
 scripts = []
 task_class = BuildTask # default action is to build targets
 current_func = None
+calc = None
 ignore_errors = 0
 keep_going_on_error = 0
 
@@ -310,9 +309,18 @@ def options_init():
        help = "Ignored for compatibility.")
 
     def opt_c(opt, arg):
-       global task_class, current_func
-       task_class = CleanTask
-       current_func = SCons.Taskmaster.current
+        global task_class, calc
+        task_class = CleanTask
+        class CleanCalculator:
+            def get_signature(self, node):
+                return None
+            def set_signature(self, node, sig):
+                pass
+            def current(self, node, sig):
+                return 0
+            def write(self):
+                pass
+        calc = CleanCalculator()
 
     Option(func = opt_c,
        short = 'c', long = ['clean', 'remove'],
@@ -540,7 +548,7 @@ def UsageString():
 
 
 def main():
-    global scripts, help_option, num_jobs, task_class, current_func
+    global scripts, help_option, num_jobs, task_class, calc
 
     targets = []
 
@@ -629,24 +637,18 @@ def main():
     if not targets:
        targets = default_targets
 
-    nodes = map(lambda x: SCons.Node.FS.default_fs.File(x), targets)
-
-    calc = SCons.Sig.Calculator(SCons.Sig.MD5)
+    nodes = map(lambda x: SCons.Node.FS.default_fs.Entry(x), targets)
 
-    if not current_func:
-        current_func = calc.current
+    if not calc:
+        calc = SCons.Sig.Calculator(SCons.Sig.MD5)
 
-    taskmaster = ScriptTaskmaster(nodes,
-                                  task_class,
-                                  current_func,
-                                  ignore_errors,
-                                  keep_going_on_error)
+    taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, calc)
 
     jobs = SCons.Job.Jobs(num_jobs, taskmaster)
     jobs.start()
     jobs.wait()
 
-    calc.write(nodes)
+    SCons.Sig.write()
 
 if __name__ == "__main__":
     try:
index c3080cbc53c7f49f9c3998f6facb230d49808109..e9d74528472b513761cc82dc0ed44da2d80e353a 100644 (file)
@@ -57,8 +57,7 @@ test.write('f2.in', "f2.in\n")
 
 test.write('f3.in', "f3.in\n")
 
-#XXXtest.run(arguments = '.')
-test.run(arguments = 'f1.out f2.out f3.out')
+test.run(arguments = '.')
 
 test.fail_test(test.read('f1.out') != "f1.in\n")
 test.fail_test(test.read('f2.out') != "f2.in\n")
index 221e7f9655ad3db59a4dfd2d0f02673bb31da058..1ff699d49a245fa1cc9410d5c2d5233794531ef2 100644 (file)
@@ -71,8 +71,7 @@ os.chmod(bin2_build_py, 0755)
 
 test.write('input', "input file\n")
 
-#test.run(arguments = '.')
-test.run(arguments = 'bin1.out bin2.out')
+test.run(arguments = '.')
 
 test.fail_test(test.read('bin1.out') != "bin1/build.py\ninput file\n")
 test.fail_test(test.read('bin2.out') != "bin2/build.py\ninput file\n")
index 2612d1c10f3ac2cbfef0e4bac5a3ce5bdd6bb81a..6a0db7032ed25238416d0919c897165882af9562 100644 (file)
@@ -36,7 +36,7 @@ test.write('SConstruct', """
 env = Environment()
 env.Program(target = 'foo1', source = 'f1.c')
 env.Program(target = 'foo2', source = 'f2a.c f2b.c f2c.c')
-#XXXenv.Program(target = 'foo3', source = ['f3a.c', 'f3b.c', 'f3c.c'])
+env.Program(target = 'foo3', source = ['f3a.c', 'f3b.c', 'f3c.c'])
 """)
 
 test.write('f1.c', """
@@ -109,15 +109,13 @@ main(int argc, char *argv[])
 }
 """)
 
-#XXXtest.run(arguments = '.')
-test.run(arguments = 'foo1 foo2')
+test.run(arguments = '.')
 
 test.run(program = test.workpath('foo1'), stdout = "f1.c\n")
 test.run(program = test.workpath('foo2'), stdout = "f2a.c\nf2b.c\nf2c.c\n")
-#XXXtest.run(program = test.workpath('foo3'), stdout = "f3a.c\nf3b.c\nf3c.c\n")
+test.run(program = test.workpath('foo3'), stdout = "f3a.c\nf3b.c\nf3c.c\n")
 
-#XXXtest.up_to_date(arguments = '.')
-test.up_to_date(arguments = 'foo1 foo2')
+test.up_to_date(arguments = '.')
 
 test.write('f1.c', """
 int
@@ -137,22 +135,22 @@ f3b(void)
 }
 """)
 
-#XXXtest.run(arguments = '.')
-test.run(arguments = 'foo1 foo2')
+test.run(arguments = '.')
 
 test.run(program = test.workpath('foo1'), stdout = "f1.c X\n")
 test.run(program = test.workpath('foo2'), stdout = "f2a.c\nf2b.c\nf2c.c\n")
-#XXXtest.run(program = test.workpath('foo3'), stdout = "f3a.c\nf3b.c X\nf3c.c\n")
+test.run(program = test.workpath('foo3'), stdout = "f3a.c\nf3b.c X\nf3c.c\n")
 
-#XXXtest.up_to_date(arguments = '.')
-test.up_to_date(arguments = 'foo1 foo2')
+test.up_to_date(arguments = '.')
 
 # make sure the programs don't get rebuilt, because nothing changed:
 oldtime1 = os.path.getmtime(test.workpath('foo1'))
 oldtime2 = os.path.getmtime(test.workpath('foo2'))
-time.sleep(1) # introduce a small delay, to make the test valid
-test.run(arguments = 'foo1 foo2')
+oldtime3 = os.path.getmtime(test.workpath('foo3'))
+time.sleep(2) # introduce a small delay, to make the test valid
+test.run(arguments = '.')
 test.fail_test(not (oldtime1 == os.path.getmtime(test.workpath('foo1'))))
 test.fail_test(not (oldtime2 == os.path.getmtime(test.workpath('foo2'))))
+test.fail_test(not (oldtime3 == os.path.getmtime(test.workpath('foo3'))))
 
 test.pass_test()
index 0e41ffd6f053941dd24add5cf5400e4556312083..b188d537dc37b336a5909d08498eeaa040199476 100644 (file)
@@ -78,14 +78,13 @@ test.fail_test(os.path.exists(test.workpath('foo1.out')))
 test.fail_test(os.path.exists(test.workpath('foo2.out')))
 test.fail_test(os.path.exists(test.workpath('foo3.out')))
 
-test.run(arguments = 'foo1.out foo2.out foo3.out')
+test.run(arguments = '.')
 
 test.fail_test(test.read(test.workpath('foo1.out')) != "foo1.in\n")
 test.fail_test(test.read(test.workpath('foo2.out')) != "foo2.in\n")
 test.fail_test(test.read(test.workpath('foo3.out')) != "foo3.in\n")
 
-#XXXtest.run(arguments = '-c .',
-test.run(arguments = '-c foo1.out foo2.out foo3.out',
+test.run(arguments = '-c .',
          stdout = "Removed foo1.out\nRemoved foo2.out\nRemoved foo3.out\n")
 
 test.fail_test(os.path.exists(test.workpath('foo1.out')))
diff --git a/test/subdir.py b/test/subdir.py
new file mode 100644 (file)
index 0000000..7071b54
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/env python
+#
+# Copyright (c) 2001 Steven Knight
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import TestSCons
+import os.path
+
+test = TestSCons.TestSCons()
+
+test.subdir('subdir')
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'r').read()
+file = open(sys.argv[1], 'w')
+file.write(contents)
+file.close()
+""")
+
+test.write('SConstruct', """
+B = Builder(name = "B", action = "python build.py $targets $sources")
+env = Environment(BUILDERS = [B])
+env.B(target = 'subdir/f1.out', source = 'subdir/f1.in')
+env.B(target = 'subdir/f2.out', source = 'subdir/f2.in')
+env.B(target = 'subdir/f3.out', source = 'subdir/f3.in')
+env.B(target = 'subdir/f4.out', source = 'subdir/f4.in')
+""")
+
+test.write('subdir/f1.in', "f1.in\n")
+test.write('subdir/f2.in', "f2.in\n")
+test.write('subdir/f3.in', "f3.in\n")
+test.write('subdir/f4.in', "f4.in\n")
+
+test.run(arguments = 'subdir')
+
+test.fail_test(test.read('subdir/f1.out') != "f1.in\n")
+test.fail_test(test.read('subdir/f2.out') != "f2.in\n")
+test.fail_test(test.read('subdir/f3.out') != "f3.in\n")
+test.fail_test(test.read('subdir/f4.out') != "f4.in\n")
+
+test.up_to_date(arguments = 'subdir')
+
+test.pass_test()