1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 __doc__ = """
25 Generic Taskmaster module for the SCons build engine.
26
27 This module contains the primary interface(s) between a wrapping user
28 interface and the SCons build engine. There are two key classes here:
29
30 Taskmaster
31 This is the main engine for walking the dependency graph and
32 calling things to decide what does or doesn't need to be built.
33
34 Task
35 This is the base class for allowing a wrapping interface to
36 decide what does or doesn't actually need to be done. The
37 intention is for a wrapping interface to subclass this as
38 appropriate for different types of behavior it may need.
39
40 The canonical example is the SCons native Python interface,
41 which has Task subclasses that handle its specific behavior,
42 like printing "`foo' is up to date" when a top-level target
43 doesn't need to be built, and handling the -c option by removing
44 targets as its "build" action. There is also a separate subclass
45 for suppressing this output when the -q option is used.
46
47 The Taskmaster instantiates a Task object for each (set of)
48 target(s) that it decides need to be evaluated and/or built.
49 """
50
51 __revision__ = "src/engine/SCons/Taskmaster.py 3795 2008/11/25 22:04:43 scons"
52
53 from itertools import chain
54 import operator
55 import string
56 import sys
57 import traceback
58
59 import SCons.Errors
60 import SCons.Node
61
62 StateString = SCons.Node.StateString
63 NODE_NO_STATE = SCons.Node.no_state
64 NODE_PENDING = SCons.Node.pending
65 NODE_EXECUTING = SCons.Node.executing
66 NODE_UP_TO_DATE = SCons.Node.up_to_date
67 NODE_EXECUTED = SCons.Node.executed
68 NODE_FAILED = SCons.Node.failed
69
70
71
72
73
74
75 CollectStats = None
76
78 """
79 A simple class for holding statistics about the disposition of a
80 Node by the Taskmaster. If we're collecting statistics, each Node
81 processed by the Taskmaster gets one of these attached, in which case
82 the Taskmaster records its decision each time it processes the Node.
83 (Ideally, that's just once per Node.)
84 """
86 """
87 Instantiates a Taskmaster.Stats object, initializing all
88 appropriate counters to zero.
89 """
90 self.considered = 0
91 self.already_handled = 0
92 self.problem = 0
93 self.child_failed = 0
94 self.not_built = 0
95 self.side_effects = 0
96 self.build = 0
97
98 StatsNodes = []
99
100 fmt = "%(considered)3d "\
101 "%(already_handled)3d " \
102 "%(problem)3d " \
103 "%(child_failed)3d " \
104 "%(not_built)3d " \
105 "%(side_effects)3d " \
106 "%(build)3d "
107
112
113
114
116 """
117 Default SCons build engine task.
118
119 This controls the interaction of the actual building of node
120 and the rest of the engine.
121
122 This is expected to handle all of the normally-customizable
123 aspects of controlling a build, so any given application
124 *should* be able to do what it wants by sub-classing this
125 class and overriding methods as appropriate. If an application
126 needs to customze something by sub-classing Taskmaster (or
127 some other build engine class), we should first try to migrate
128 that functionality into this class.
129
130 Note that it's generally a good idea for sub-classes to call
131 these methods explicitly to update state, etc., rather than
132 roll their own interaction with Taskmaster from scratch.
133 """
134 - def __init__(self, tm, targets, top, node):
135 self.tm = tm
136 self.targets = targets
137 self.top = top
138 self.node = node
139 self.exc_clear()
140
142 """
143 Hook to allow the calling interface to display a message.
144
145 This hook gets called as part of preparing a task for execution
146 (that is, a Node to be built). As part of figuring out what Node
147 should be built next, the actually target list may be altered,
148 along with a message describing the alteration. The calling
149 interface can subclass Task and provide a concrete implementation
150 of this method to see those messages.
151 """
152 pass
153
155 """
156 Called just before the task is executed.
157
158 This is mainly intended to give the target Nodes a chance to
159 unlink underlying files and make all necessary directories before
160 the Action is actually called to build the targets.
161 """
162
163
164
165
166 self.exception_raise()
167
168 if self.tm.message:
169 self.display(self.tm.message)
170 self.tm.message = None
171
172
173
174
175
176
177
178
179
180
181
182 self.targets[0].get_executor().prepare()
183 for t in self.targets:
184 t.prepare()
185 for s in t.side_effects:
186 s.prepare()
187
189 """Fetch the target being built or updated by this task.
190 """
191 return self.node
192
194 """
195 Called to determine whether the task's execute() method should
196 be run.
197
198 This method allows one to skip the somethat costly execution
199 of the execute() method in a seperate thread. For example,
200 that would be unnecessary for up-to-date targets.
201 """
202 return True
203
205 """
206 Called to execute the task.
207
208 This method is called from multiple threads in a parallel build,
209 so only do thread safe stuff here. Do thread unsafe stuff in
210 prepare(), executed() or failed().
211 """
212
213 try:
214 everything_was_cached = 1
215 for t in self.targets:
216 if not t.retrieve_from_cache():
217 everything_was_cached = 0
218 break
219 if not everything_was_cached:
220 self.targets[0].build()
221 except SystemExit:
222 exc_value = sys.exc_info()[1]
223 raise SCons.Errors.ExplicitExit(self.targets[0], exc_value.code)
224 except SCons.Errors.UserError:
225 raise
226 except SCons.Errors.BuildError:
227 raise
228 except Exception, e:
229 buildError = SCons.Errors.convert_to_BuildError(e)
230 buildError.node = self.targets[0]
231 buildError.exc_info = sys.exc_info()
232 raise buildError
233
235 """
236 Called when the task has been successfully executed
237 and the Taskmaster instance doesn't want to call
238 the Node's callback methods.
239 """
240 for t in self.targets:
241 if t.get_state() == NODE_EXECUTING:
242 for side_effect in t.side_effects:
243 side_effect.set_state(NODE_NO_STATE)
244 t.set_state(NODE_EXECUTED)
245
247 """
248 Called when the task has been successfully executed and
249 the Taskmaster instance wants to call the Node's callback
250 methods.
251
252 This may have been a do-nothing operation (to preserve build
253 order), so we must check the node's state before deciding whether
254 it was "built", in which case we call the appropriate Node method.
255 In any event, we always call "visited()", which will handle any
256 post-visit actions that must take place regardless of whether
257 or not the target was an actual built target or a source Node.
258 """
259 for t in self.targets:
260 if t.get_state() == NODE_EXECUTING:
261 for side_effect in t.side_effects:
262 side_effect.set_state(NODE_NO_STATE)
263 t.set_state(NODE_EXECUTED)
264 t.built()
265 t.visited()
266
267 executed = executed_with_callbacks
268
270 """
271 Default action when a task fails: stop the build.
272 """
273 self.fail_stop()
274
276 """
277 Explicit stop-the-build failure.
278 """
279
280
281
282 self.tm.will_not_build(self.targets)
283
284
285 self.tm.stop()
286
287
288
289
290 self.targets = [self.tm.current_top]
291 self.top = 1
292
294 """
295 Explicit continue-the-build failure.
296
297 This sets failure status on the target nodes and all of
298 their dependent parent nodes.
299 """
300 self.tm.will_not_build(self.targets)
301
303 """
304 Marks all targets in a task ready for execution.
305
306 This is used when the interface needs every target Node to be
307 visited--the canonical example being the "scons -c" option.
308 """
309 self.out_of_date = self.targets[:]
310 for t in self.targets:
311 t.disambiguate().set_state(NODE_EXECUTING)
312 for s in t.side_effects:
313 s.set_state(NODE_EXECUTING)
314
349
350 make_ready = make_ready_current
351
352 - def postprocess(self):
353 """
354 Post-processes a task after it's been executed.
355
356 This examines all the targets just built (or not, we don't care
357 if the build was successful, or even if there was no build
358 because everything was up-to-date) to see if they have any
359 waiting parent Nodes, or Nodes waiting on a common side effect,
360 that can be put back on the candidates list.
361 """
362
363
364
365
366
367
368
369
370 targets = set(self.targets)
371
372 parents = {}
373 for t in targets:
374 for p in t.waiting_parents:
375 parents[p] = parents.get(p, 0) + 1
376
377 for t in targets:
378 for s in t.side_effects:
379 if s.get_state() == NODE_EXECUTING:
380 s.set_state(NODE_NO_STATE)
381 for p in s.waiting_parents:
382 parents[p] = parents.get(p, 0) + 1
383 for p in s.waiting_s_e:
384 if p.ref_count == 0:
385 self.tm.candidates.append(p)
386 self.tm.pending_children.discard(p)
387
388 for p, subtract in parents.items():
389 p.ref_count = p.ref_count - subtract
390 if p.ref_count == 0:
391 self.tm.candidates.append(p)
392 self.tm.pending_children.discard(p)
393
394 for t in targets:
395 t.postprocess()
396
397
398
399
400
401
402
403
404
405
407 """
408 Returns info about a recorded exception.
409 """
410 return self.exception
411
413 """
414 Clears any recorded exception.
415
416 This also changes the "exception_raise" attribute to point
417 to the appropriate do-nothing method.
418 """
419 self.exception = (None, None, None)
420 self.exception_raise = self._no_exception_to_raise
421
423 """
424 Records an exception to be raised at the appropriate time.
425
426 This also changes the "exception_raise" attribute to point
427 to the method that will, in fact
428 """
429 if not exception:
430 exception = sys.exc_info()
431 self.exception = exception
432 self.exception_raise = self.