summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-07-04 21:44:00 (GMT)
committerSteven Knight <knight@baldmt.com>2002-07-04 21:44:00 (GMT)
commitf4115620c39f65724c5ef38e386c61141a71e9eb (patch)
treed0ff010898f5a406c6d5ff6127230bc1024340ee /src/engine
parent946b8e61e1cea21a700a9646c561e4b871f2caba (diff)
downloadSCons-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__.py2
-rw-r--r--src/engine/SCons/Script/__init__.py4
-rw-r--r--src/engine/SCons/Taskmaster.py62
-rw-r--r--src/engine/SCons/TaskmasterTests.py37
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])