1 """SCons.Action
2
3 This encapsulates information about executing any sort of action that
4 can build one or more target Nodes (typically files) from one or more
5 source Nodes (also typically files) given a specific Environment.
6
7 The base class here is ActionBase. The base class supplies just a few
8 OO utility methods and some generic methods for displaying information
9 about an Action in response to the various commands that control printing.
10
11 A second-level base class is _ActionAction. This extends ActionBase
12 by providing the methods that can be used to show and perform an
13 action. True Action objects will subclass _ActionAction; Action
14 factory class objects will subclass ActionBase.
15
16 The heavy lifting is handled by subclasses for the different types of
17 actions we might execute:
18
19 CommandAction
20 CommandGeneratorAction
21 FunctionAction
22 ListAction
23
24 The subclasses supply the following public interface methods used by
25 other modules:
26
27 __call__()
28 THE public interface, "calling" an Action object executes the
29 command or Python function. This also takes care of printing
30 a pre-substitution command for debugging purposes.
31
32 get_contents()
33 Fetches the "contents" of an Action for signature calculation
34 plus the varlist. This is what gets MD5 checksummed to decide
35 if a target needs to be rebuilt because its action changed.
36
37 genstring()
38 Returns a string representation of the Action *without*
39 command substitution, but allows a CommandGeneratorAction to
40 generate the right action based on the specified target,
41 source and env. This is used by the Signature subsystem
42 (through the Executor) to obtain an (imprecise) representation
43 of the Action operation for informative purposes.
44
45
46 Subclasses also supply the following methods for internal use within
47 this module:
48
49 __str__()
50 Returns a string approximation of the Action; no variable
51 substitution is performed.
52
53 execute()
54 The internal method that really, truly, actually handles the
55 execution of a command or Python function. This is used so
56 that the __call__() methods can take care of displaying any
57 pre-substitution representations, and *then* execute an action
58 without worrying about the specific Actions involved.
59
60 get_presig()
61 Fetches the "contents" of a subclass for signature calculation.
62 The varlist is added to this to produce the Action's contents.
63
64 strfunction()
65 Returns a substituted string representation of the Action.
66 This is used by the _ActionAction.show() command to display the
67 command/function that will be executed to generate the target(s).
68
69 There is a related independent ActionCaller class that looks like a
70 regular Action, and which serves as a wrapper for arbitrary functions
71 that we want to let the user specify the arguments to now, but actually
72 execute later (when an out-of-date check determines that it's needed to
73 be executed, for example). Objects of this class are returned by an
74 ActionFactory class that provides a __call__() method as a convenient
75 way for wrapping up the functions.
76
77 """
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 __revision__ = "src/engine/SCons/Action.py 3765 2008/11/04 08:12:16 scons"
101
102 import cPickle
103 import dis
104 import os
105 import os.path
106 import string
107 import sys
108 import subprocess
109
110 from SCons.Debug import logInstanceCreation
111 import SCons.Errors
112 import SCons.Executor
113 import SCons.Util
114 import SCons.Subst
115
116
117 is_String = SCons.Util.is_String
118 is_List = SCons.Util.is_List
119
122
123 print_actions = 1
124 execute_actions = 1
125 print_actions_presub = 0
126
128 try:
129 return n.rfile()
130 except AttributeError:
131 return n
132
135
136 try:
137 SET_LINENO = dis.SET_LINENO
138 HAVE_ARGUMENT = dis.HAVE_ARGUMENT
139 except AttributeError:
140 remove_set_lineno_codes = lambda x: x
141 else:
157
158
160 """Return the signature contents of a callable Python object.
161 """
162 try:
163
164 return _function_contents(obj.im_func)
165
166 except AttributeError:
167 try:
168
169 return _function_contents(obj.__call__.im_func)
170
171 except AttributeError:
172 try:
173
174 return _code_contents(obj)
175
176 except AttributeError:
177
178 return _function_contents(obj)
179
180
182 """Return the signature contents of any Python object.
183
184 We have to handle the case where object contains a code object
185 since it can be pickled directly.
186 """
187 try:
188
189 return _function_contents(obj.im_func)
190
191 except AttributeError:
192 try:
193
194 return _function_contents(obj.__call__.im_func)
195
196 except AttributeError:
197 try:
198
199 return _code_contents(obj)
200
201 except AttributeError:
202 try:
203
204 return _function_contents(obj)
205
206 except AttributeError:
207
208 try:
209 return cPickle.dumps(obj)
210 except (cPickle.PicklingError, TypeError):
211
212
213
214
215
216 return str(obj)
217
218
219 -def _code_contents(code):
220 """Return the signature contents of a code object.
221
222 By providing direct access to the code object of the
223 function, Python makes this extremely easy. Hooray!
224
225 Unfortunately, older versions of Python include line
226 number indications in the compiled byte code. Boo!
227 So we remove the line number byte codes to prevent
228 recompilations from moving a Python function.
229 """
230
231 contents = []
232
233
234
235 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
236 try:
237 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
238 except AttributeError:
239
240 contents.append(",0,0")
241
242
243
244
245
246
247
248
249
250 contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
251
252
253
254
255
256 contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
257
258
259
260 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
261
262 return string.join(contents, '')
263
264
266 """Return the signature contents of a function."""
267
268 contents = [_code_contents(func.func_code)]
269
270
271 if func.func_defaults:
272 contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
273 else:
274 contents.append(',()')
275
276
277 try:
278 closure = func.func_closure or []
279 except AttributeError:
280
281 closure = []
282
283
284 try:
285 xxx = map(lambda x: _object_contents(x.cell_contents), closure)
286 except AttributeError:
287 xxx = []
288 contents.append(',(' + string.join(xxx, ',') + ')')
289
290 return string.join(contents, '')
291
292
294
295
296
297 a1 = Action(act1)
298 a2 = Action(act2)
299 if a1 is None or a2 is None:
300 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
301 if isinstance(a1, ListAction):
302 if isinstance(a2, ListAction):
303 return ListAction(a1.list + a2.list)
304 else:
305 return ListAction(a1.list + [ a2 ])
306 else:
307 if isinstance(a2, ListAction):
308 return ListAction([ a1 ] + a2.list)
309 else:
310 return ListAction([ a1, a2 ])
311
313 """This converts any arguments after the action argument into
314 their equivalent keywords and adds them to the kw argument.
315 """
316 v = kw.get('varlist', ())
317
318 if is_String(v): v = (v,)
319 kw['varlist'] = tuple(v)
320 if args:
321
322 cmdstrfunc = args[0]
323 if cmdstrfunc is None or is_String(cmdstrfunc):
324 kw['cmdstr'] = cmdstrfunc
325 elif callable(cmdstrfunc):
326 kw['strfunction'] = cmdstrfunc
327 else:
328 raise SCons.Errors.UserError(
329 'Invalid command display variable type. '
330 'You must either pass a string or a callback which '
331 'accepts (target, source, env) as parameters.')
332 if len(args) > 1:
333 kw['varlist'] = args[1:] + kw['varlist']
334 if kw.get('strfunction', _null) is not _null \
335 and kw.get('cmdstr', _null) is not _null:
336 raise SCons.Errors.UserError(
337 'Cannot have both strfunction and cmdstr args to Action()')
338
340 """This is the actual "implementation" for the
341 Action factory method, below. This handles the
342 fact that passing lists to Action() itself has
343 different semantics than passing lists as elements
344 of lists.
345
346 The former will create a ListAction, the latter
347 will create a CommandAction by converting the inner
348 list elements to strings."""
349
350 if isinstance(act, ActionBase):
351 return act
352
353 if is_List(act):
354
355 return apply(CommandAction, (act,), kw)
356
357 if callable(act):
358 try:
359 gen = kw['generator']
360 del kw['generator']
361 except KeyError:
362 gen = 0
363 if gen:
364 action_type = CommandGeneratorAction
365 else:
366 action_type = FunctionAction
367 return action_type(act, kw)
368
369 if is_String(act):
370 var=SCons.Util.get_environment_var(act)
371 if var:
372
373
374
375
376
377
378 return LazyAction(var, kw)
379 commands = string.split(str(act), '\n')
380 if len(commands) == 1:
381
382 return apply(CommandAction, (commands[0],), kw)
383
384
385 return _do_create_list_action(commands, kw)
386 return None
387
389 """A factory for list actions. Convert the input list into Actions
390 and then wrap them in a ListAction."""
391 acts = []
392 for a in act:
393 aa = _do_create_action(a, kw)
394 if aa is not None: acts.append(aa)
395 if not acts:
396 return None
397 elif len(acts) == 1:
398 return acts[0]
399 else:
400 return ListAction(acts)
401
403 """A factory for action objects."""
404
405 _do_create_keywords(args, kw)
406 if is_List(act):
407 return _do_create_list_action(act, kw)
408 return _do_create_action(act, kw)
409
411 """Base class for all types of action objects that can be held by
412 other objects (Builders, Executors, etc.) This provides the
413 common methods for manipulating and combining those actions."""
414
416 return cmp(self.__dict__, other)
417
420
421 - def get_contents(self, target, source, env):
422 result = [ self.get_presig(target, source, env) ]
423
424
425
426 vl = self.varlist
427 if is_String(vl): vl = (vl,)
428 for v in vl:
429 result.append(env.subst('${'+v+'}'))
430 return string.join(result, '')
431
433 return _actionAppend(self, other)
434
436 return _actionAppend(other, self)
437
439
440
441
442
443
444
445 self.presub_env = env
446 lines = string.split(str(self), '\n')
447 self.presub_env = None