summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2001-10-05 17:37:48 (GMT)
committerSteven Knight <knight@baldmt.com>2001-10-05 17:37:48 (GMT)
commita86827b3933c2861d2e5b9df319630ce04559fe0 (patch)
tree4172953c924c5d23c8b1622fd0d9d5e980f79cc3
parenta0e380faef8af1b62213a0ea1840b38fa0ecf6e4 (diff)
downloadSCons-a86827b3933c2861d2e5b9df319630ce04559fe0.zip
SCons-a86827b3933c2861d2e5b9df319630ce04559fe0.tar.gz
SCons-a86827b3933c2861d2e5b9df319630ce04559fe0.tar.bz2
Use the Node Walker to build dependencies in order.
-rw-r--r--src/engine/MANIFEST1
-rw-r--r--src/engine/SCons/Node/NodeTests.py5
-rw-r--r--src/engine/SCons/Node/__init__.py2
-rw-r--r--src/engine/SCons/Taskmaster.py106
-rw-r--r--src/engine/SCons/TaskmasterTests.py154
-rw-r--r--src/script/scons.py49
6 files changed, 290 insertions, 27 deletions
diff --git a/src/engine/MANIFEST b/src/engine/MANIFEST
index e085755..af9d531 100644
--- a/src/engine/MANIFEST
+++ b/src/engine/MANIFEST
@@ -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
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 309229e..7dfa23e 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -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())
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index 8e1760d..b6922dc 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -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
index 0000000..651769d
--- /dev/null
+++ b/src/engine/SCons/Taskmaster.py
@@ -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
index 0000000..2af65f6
--- /dev/null
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -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)
diff --git a/src/script/scons.py b/src/script/scons.py
index cfb0a9f..da907ad 100644
--- a/src/script/scons.py
+++ b/src/script/scons.py
@@ -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()