Use the Node Walker to build dependencies in order.
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 5 Oct 2001 17:37:48 +0000 (17:37 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Fri, 5 Oct 2001 17:37:48 +0000 (17:37 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@87 fdb21ef1-2011-0410-befe-b5e4ea1792b1

src/engine/MANIFEST
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/Taskmaster.py [new file with mode: 0644]
src/engine/SCons/TaskmasterTests.py [new file with mode: 0644]
src/script/scons.py

index e0857550394a95280ea5900b435c9ed4c6c73105..af9d5312b32e218efc37ec3cab449c0b5a45d14c 100644 (file)
@@ -14,5 +14,6 @@ SCons/Scanner/C.py
 SCons/Sig/__init__.py
 SCons/Sig/MD5.py
 SCons/Sig/TimeStamp.py
+SCons/Taskmaster.py
 SCons/Util.py
 setup.py
index 309229e997e97f4fc04000843dff2bc2ef5b409f..7dfa23e5058b93b163ad4752a9c7bde6464f7e40 100644 (file)
@@ -68,6 +68,11 @@ class NodeTestCase(unittest.TestCase):
     def test_build(self):
        """Test building a node
        """
+       # Make sure it doesn't blow up if no builder is set.
+       node = SCons.Node.Node()
+       node.build()
+       assert built_it == None
+
        node = SCons.Node.Node()
        node.builder_set(Builder())
        node.env_set(Environment())
index 8e1760dc358dd296ae01e36e1265a90810ca3e52..b6922dc045b9a763d10974feccdb9d3cb2dfb592 100644 (file)
@@ -48,6 +48,8 @@ class Node:
        self.env = None
 
     def build(self):
+       if not hasattr(self, "builder"):
+           return None
        sources_str = string.join(map(lambda x: str(x), self.sources))
        stat = self.builder.execute(ENV = self.env.Dictionary('ENV'),
                                    target = str(self), source = sources_str)
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
new file mode 100644 (file)
index 0000000..651769d
--- /dev/null
@@ -0,0 +1,106 @@
+"""SCons.Taskmaster
+
+Generic Taskmaster.
+
+"""
+
+#
+# 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 SCons.Node
+
+
+
+class Task:
+    """Default SCons build engine task."""
+    def __init__(self,target):
+        self.target = target
+
+    def execute(self):
+        self.target.build()
+
+
+
+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 Taskmaster:
+    """A generic Taskmaster for handling a bunch of targets.
+    """
+
+    def __init__(self, targets=[], tasker=Task, current=current):
+        self.targets = targets
+        self.tasker = tasker
+        self.current = current
+        self.num_iterated = 0
+        self.walker = None
+
+    def next_node(self):
+        t = None
+        if self.walker:
+            t = self.walker.next()
+        if t == None and self.num_iterated < len(self.targets):
+            t = self.targets[self.num_iterated]
+            self.num_iterated = self.num_iterated + 1
+            t.top_target = 1
+            self.walker = SCons.Node.Walker(t)
+            t = self.walker.next()
+        top = None
+        if hasattr(t, "top_target"):
+            top = 1
+        return t, top
+
+    def next_task(self):
+        n, top = self.next_node()
+        while n != None:
+            if self.current(n):
+                self.up_to_date(n)
+            else:
+                return self.tasker(n)
+            n, top = self.next_node()
+        return None
+
+    def is_blocked(self):
+        return 0
+
+    def up_to_date(self, node):
+        pass
+
+    def executed(self, task):
+        pass
+
+    def failed(self, task):
+        self.walker = None
+        self.num_iterated = len(self.targets)
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
new file mode 100644 (file)
index 0000000..2af65f6
--- /dev/null
@@ -0,0 +1,154 @@
+#
+# 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 sys
+import unittest
+
+import SCons.Taskmaster
+
+
+
+built = None
+
+class Node:
+    def __init__(self, name, kids = []):
+        self.name = name
+       self.kids = kids
+
+    def build(self):
+        global built
+        built = self.name + " built"
+
+    def children(self):
+       return self.kids
+
+
+
+class TaskmasterTestCase(unittest.TestCase):
+
+    def test_next_node(self):
+       """Test fetching the next node
+       """
+       n1 = Node("n1")
+       n2 = Node("n2")
+       n3 = Node("n3", [n1, n2])
+
+       tm = SCons.Taskmaster.Taskmaster([n3])
+       n, top = tm.next_node()
+       assert n.name == "n1"
+       assert top == None
+       n, top = tm.next_node()
+       assert n.name == "n2"
+       assert top == None
+       n, top = tm.next_node()
+       assert n.name == "n3"
+       assert top == 1
+       n, top = tm.next_node()
+       assert n == None
+       assert top == None
+
+    def test_next_task(self):
+       """Test fetching the next task
+       """
+       global built
+
+       n1 = Node("n1")
+       n2 = Node("n2")
+       n3 = Node("n3", [n1, n2])
+
+       tm = SCons.Taskmaster.Taskmaster([n3])
+       tm.next_task().execute()
+       assert built == "n1 built"
+
+       tm.next_task().execute()
+       assert built == "n2 built"
+
+       tm.next_task().execute()
+       assert built == "n3 built"
+
+       def current(node):
+           return 1
+
+       built = "up to date: "
+
+       class MyTM(SCons.Taskmaster.Taskmaster):
+           def up_to_date(self, node):
+               global built
+               built = built + " " + node.name
+
+       tm = MyTM(targets = [n3], current = current)
+       assert tm.next_task() == None
+       assert built == "up to date:  n1 n2 n3"
+
+    def test_is_blocked(self):
+        """Test whether a task is blocked
+
+       Both default and overridden in a subclass.
+       """
+       tm = SCons.Taskmaster.Taskmaster()
+       assert tm.is_blocked() == 0
+
+       class MyTM(SCons.Taskmaster.Taskmaster):
+           def is_blocked(self):
+               return 1
+       tm = MyTM()
+       assert tm.is_blocked() == 1
+
+    def test_executed(self):
+        """Test the executed() method
+
+       Both default and overridden in a subclass.
+       """
+       tm = SCons.Taskmaster.Taskmaster()
+       tm.executed('foo')
+
+       class MyTM(SCons.Taskmaster.Taskmaster):
+           def executed(self, task):
+               return 'x' + task
+       tm = MyTM()
+       assert tm.executed('foo') == 'xfoo'
+
+    def test_failed(self):
+        """Test the failed() method
+
+       Both default and overridden in a subclass.
+       """
+       tm = SCons.Taskmaster.Taskmaster()
+       #XXX
+       tm.failed('foo')
+
+       class MyTM(SCons.Taskmaster.Taskmaster):
+           def failed(self, task):
+               return 'y' + task
+       tm = MyTM()
+       assert tm.failed('foo') == 'yfoo'
+
+
+
+
+if __name__ == "__main__":
+    suite = unittest.makeSuite(TaskmasterTestCase, 'test_')
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+       sys.exit(1)
index cfb0a9f3bf7016d8e8417f2f6ac135c036f0ad90..da907adedb09e60798f68516c29bd2ba490bb8f4 100644 (file)
@@ -33,11 +33,13 @@ import string
 import sys
 import traceback
 
+import SCons.Node
 import SCons.Node.FS
 import SCons.Job
 from SCons.Errors import *
 import SCons.Sig
 import SCons.Sig.MD5
+from SCons.Taskmaster import Taskmaster
 
 #
 # Modules and classes that we don't use directly in this script, but
@@ -46,8 +48,13 @@ import SCons.Sig.MD5
 from SCons.Environment import Environment
 from SCons.Builder import Builder
 
+
+
+#
+# Task control.
+#
 class Task:
-    "XXX: this is here only until the build engine is implemented"
+    """An SCons build task."""
 
     def __init__(self, target):
         self.target = target
@@ -59,35 +66,23 @@ class Task:
             sys.stderr.write("scons: *** [%s] Error %d\n" % (e.node, e.stat))
             raise
 
-
-
-class Taskmaster:
-    "XXX: this is here only until the build engine is implemented"
-
-    def __init__(self, targets, calc):
-        self.targets = targets
-        self.calc = calc
-        self.num_iterated = 0
-
-
+class ScriptTaskmaster(SCons.Taskmaster.Taskmaster):
+    """Controlling logic for tasks.
+    
+    This is the stock Taskmaster from the build engine, except
+    that we override the next_task() method to provide our
+    script-specific up-to-date message for command-line targets.
+    """
     def next_task(self):
-        while self.num_iterated < len(self.targets):
-            t = self.targets[self.num_iterated]
-            self.num_iterated = self.num_iterated + 1
-            if self.calc.current(t):
+        t, top = SCons.Taskmaster.Taskmaster.next_node(self)
+        while t != None:
+            if not self.current(t):
+                return self.tasker(t)
+            elif top:
                 print 'scons: "%s" is up to date.' % t
-            else:
-                return Task(t)
+            t, top = SCons.Taskmaster.Taskmaster.next_node(self)
         return None
 
-    def is_blocked(self):
-        return 0
-
-    def executed(self, task):
-        pass
-
-    def failed(self, task):
-        self.num_iterated = len(self.targets)
 
 
 # Global variables
@@ -614,7 +609,7 @@ def main():
     nodes = map(lambda x: SCons.Node.FS.default_fs.File(x), targets)
     calc = SCons.Sig.Calculator(SCons.Sig.MD5)
 
-    taskmaster = Taskmaster(nodes, calc)
+    taskmaster = ScriptTaskmaster(nodes, Task, calc.current)
 
     jobs = SCons.Job.Jobs(num_jobs, taskmaster)
     jobs.start()