From c1f2d76c5acf145dc3e11624461e6d9533d613d2 Mon Sep 17 00:00:00 2001 From: stevenknight Date: Fri, 5 Oct 2001 17:37:48 +0000 Subject: [PATCH] Use the Node Walker to build dependencies in order. git-svn-id: http://scons.tigris.org/svn/scons/trunk@87 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- src/engine/MANIFEST | 1 + src/engine/SCons/Node/NodeTests.py | 5 + src/engine/SCons/Node/__init__.py | 2 + src/engine/SCons/Taskmaster.py | 106 +++++++++++++++++++ src/engine/SCons/TaskmasterTests.py | 154 ++++++++++++++++++++++++++++ src/script/scons.py | 49 ++++----- 6 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 src/engine/SCons/Taskmaster.py create mode 100644 src/engine/SCons/TaskmasterTests.py diff --git a/src/engine/MANIFEST b/src/engine/MANIFEST index e0857550..af9d5312 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 309229e9..7dfa23e5 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 8e1760dc..b6922dc0 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 00000000..651769d6 --- /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 00000000..2af65f6d --- /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 cfb0a9f3..da907ade 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() -- 2.26.2