diff options
author | Steven Knight <knight@baldmt.com> | 2002-07-04 21:44:00 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2002-07-04 21:44:00 (GMT) |
commit | f4115620c39f65724c5ef38e386c61141a71e9eb (patch) | |
tree | d0ff010898f5a406c6d5ff6127230bc1024340ee /src/engine | |
parent | 946b8e61e1cea21a700a9646c561e4b871f2caba (diff) | |
download | SCons-f4115620c39f65724c5ef38e386c61141a71e9eb.zip SCons-f4115620c39f65724c5ef38e386c61141a71e9eb.tar.gz SCons-f4115620c39f65724c5ef38e386c61141a71e9eb.tar.bz2 |
Add support for side effect targets. (Anthony Roach)
Diffstat (limited to 'src/engine')
-rw-r--r-- | src/engine/SCons/Node/__init__.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Script/__init__.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/Taskmaster.py | 62 | ||||
-rw-r--r-- | src/engine/SCons/TaskmasterTests.py | 37 |
4 files changed, 79 insertions, 26 deletions
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py index 22d6072..1e23198 100644 --- a/src/engine/SCons/Node/__init__.py +++ b/src/engine/SCons/Node/__init__.py @@ -85,6 +85,8 @@ class Node: self.includes = None self.build_args = {} self.attributes = self.Attrs() # Generic place to stick information about the Node. + self.side_effect = 0 # true iff this node is a side effect + self.side_effects = [] # the side effects of building this target def generate_build_args(self): dict = copy.copy(self.env.Dictionary()) diff --git a/src/engine/SCons/Script/__init__.py b/src/engine/SCons/Script/__init__.py index c43f1ab..3040447 100644 --- a/src/engine/SCons/Script/__init__.py +++ b/src/engine/SCons/Script/__init__.py @@ -129,11 +129,11 @@ class BuildTask(SCons.Taskmaster.Task): class CleanTask(SCons.Taskmaster.Task): """An SCons clean task.""" def show(self): - if self.targets[0].builder: + if self.targets[0].builder or self.targets[0].side_effect: print "Removed " + self.targets[0].path def remove(self): - if self.targets[0].builder: + if self.targets[0].builder or self.targets[0].side_effect: try: os.unlink(self.targets[0].path) except OSError: diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index 3fe96da..3e4dbbc 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -39,7 +39,7 @@ import copy class Task: """Default SCons build engine task. - + This controls the interaction of the actual building of node and the rest of the engine. @@ -50,7 +50,7 @@ class Task: needs to customze something by sub-classing Taskmaster (or some other build engine class), we should first try to migrate that functionality into this class. - + 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.""" @@ -73,11 +73,6 @@ class Task: """ return self.node - def set_tstates(self, state): - """Set all of the target nodes's states.""" - for t in self.targets: - t.set_state(state) - def executed(self): """Called when the task has been successfully executed. @@ -88,8 +83,10 @@ class Task: 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: + for side_effect in t.side_effects: + side_effect.set_state(None) + t.set_state(SCons.Node.executed) t.built() self.tm.executed(self.node) @@ -100,7 +97,8 @@ class Task: def fail_stop(self): """Explicit stop-the-build failure.""" - self.set_tstates(SCons.Node.failed) + for t in self.targets: + t.set_state(SCons.Node.failed) self.tm.stop() def fail_continue(self): @@ -116,7 +114,7 @@ class Task: n = walker.next() while n: n = walker.next() - + self.tm.executed(self.node) def make_ready(self): @@ -126,7 +124,11 @@ class Task: bsig = self.tm.calc.bsig(t) if not self.tm.calc.current(t, bsig): state = SCons.Node.executing - self.set_tstates(state) + for t in self.targets: + if state == SCons.Node.executing: + for side_effect in t.side_effects: + side_effect.set_state(state) + t.set_state(state) class Calc: def bsig(self, node): @@ -136,7 +138,7 @@ class Calc: 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. """ @@ -146,7 +148,7 @@ 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 its thing. + the base class method, so this class can do its thing. """ def __init__(self, targets=[], tasker=Task, calc=Calc()): @@ -164,11 +166,11 @@ class Taskmaster: if self.ready: return - + 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() @@ -191,13 +193,24 @@ class Taskmaster: # Add non-derived files that have not been built # to the candidates list: def derived(node): - return node.builder and node.get_state() == None + return (node.builder or node.side_effect) and node.get_state() == None derived = filter(derived, children) if derived: derived.reverse() self.candidates.extend(derived) continue + # Skip nodes whose side-effects are currently being built: + cont = 0 + for side_effect in node.side_effects: + if side_effect.get_state() == SCons.Node.executing: + self.pending.append(node) + node.set_state(SCons.Node.pending) + self.candidates.pop() + cont = 1 + break + if cont: 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) @@ -211,25 +224,26 @@ class Taskmaster: def next_task(self): """Return the next task to be executed.""" - + self._find_next_ready_node() node = self.ready - + if node is None: return None - + self.executing.append(node) + self.executing.extend(node.side_effects) try: tlist = node.builder.targets(node) except AttributeError: tlist = [node] - task = self.tasker(self, tlist, node in self.targets, 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() @@ -243,7 +257,9 @@ class Taskmaster: def executed(self, node): self.executing.remove(node) - + for side_effect in node.side_effects: + self.executing.remove(side_effect) + # 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) diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py index 3a67aa2..a4a2f47 100644 --- a/src/engine/SCons/TaskmasterTests.py +++ b/src/engine/SCons/TaskmasterTests.py @@ -46,6 +46,8 @@ class Node: self.csig = None self.state = None self.parents = [] + self.side_effect = 0 + self.side_effects = [] for kid in kids: kid.parents.append(self) @@ -292,7 +294,40 @@ class TaskmasterTestCase(unittest.TestCase): t.executed() assert tm.next_task() == None assert scan_called == 5, scan_called - + + n1 = Node("n1") + n2 = Node("n2") + n3 = Node("n3") + n4 = Node("n4", [n1,n2,n3]) + n5 = Node("n5", [n4]) + n3.side_effect = 1 + n1.side_effects = n2.side_effects = n3.side_effects = [n4] + tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5]) + t = tm.next_task() + assert t.get_target() == n1 + assert n4.state == SCons.Node.executing + assert tm.is_blocked() + t.executed() + assert not tm.is_blocked() + t = tm.next_task() + assert t.get_target() == n2 + assert tm.is_blocked() + t.executed() + t = tm.next_task() + assert t.get_target() == n3 + assert tm.is_blocked() + t.executed() + t = tm.next_task() + assert t.get_target() == n4 + assert tm.is_blocked() + t.executed() + t = tm.next_task() + assert t.get_target() == n5 + assert not tm.is_blocked() + assert not tm.next_task() + t.executed() + + def test_cycle_detection(self): n1 = Node("n1") n2 = Node("n2", [n1]) |