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.
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.
11 The heavy lifting is handled by subclasses for the different types of
12 actions we might execute:
15 CommandGeneratorAction
19 The subclasses supply the following public interface methods used by
23 THE public interface, "calling" an Action object executes the
24 command or Python function. This also takes care of printing
25 a pre-substitution command for debugging purposes.
28 Fetches the "contents" of an Action for signature calculation.
29 This is what the Sig/*.py subsystem uses to decide if a target
30 needs to be rebuilt because its action changed.
33 Returns a string representation of the Action *without* command
34 substitution, but allows a CommandGeneratorAction to generate
35 the right action based on the specified target, source and env.
36 This is used by the Signature subsystem (through the Executor)
37 to compare the actions used to build a target last time and
40 Subclasses also supply the following methods for internal use within
44 Returns a string representation of the Action *without* command
45 substitution. This is used by the __call__() methods to display
46 the pre-substitution command whenever the --debug=presub option
50 Returns a substituted string representation of the Action.
51 This is used by the ActionBase.show() command to display the
52 command/function that will be executed to generate the target(s).
55 The internal method that really, truly, actually handles the
56 execution of a command or Python function. This is used so
57 that the __call__() methods can take care of displaying any
58 pre-substitution representations, and *then* execute an action
59 without worrying about the specific Actions involved.
61 There is a related independent ActionCaller class that looks like a
62 regular Action, and which serves as a wrapper for arbitrary functions
63 that we want to let the user specify the arguments to now, but actually
64 execute later (when an out-of-date check determines that it's needed to
65 be executed, for example). Objects of this class are returned by an
66 ActionFactory class that provides a __call__() method as a convenient
67 way for wrapping up the functions.
74 # Permission is hereby granted, free of charge, to any person obtaining
75 # a copy of this software and associated documentation files (the
76 # "Software"), to deal in the Software without restriction, including
77 # without limitation the rights to use, copy, modify, merge, publish,
78 # distribute, sublicense, and/or sell copies of the Software, and to
79 # permit persons to whom the Software is furnished to do so, subject to
80 # the following conditions:
82 # The above copyright notice and this permission notice shall be included
83 # in all copies or substantial portions of the Software.
85 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
86 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
87 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
88 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
89 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
90 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
91 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
94 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
102 from SCons.Debug import logInstanceCreation
113 print_actions_presub = 0
120 except AttributeError:
123 def _actionAppend(act1, act2):
124 # This function knows how to slap two actions together.
125 # Mainly, it handles ListActions by concatenating into
126 # a single ListAction.
129 if a1 is None or a2 is None:
130 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
131 if isinstance(a1, ListAction):
132 if isinstance(a2, ListAction):
133 return ListAction(a1.list + a2.list)
135 return ListAction(a1.list + [ a2 ])
137 if isinstance(a2, ListAction):
138 return ListAction([ a1 ] + a2.list)
140 return ListAction([ a1, a2 ])
142 class CommandGenerator:
144 Wraps a command generator function so the Action() factory
145 function can tell a generator function from a function action.
147 def __init__(self, generator):
148 self.generator = generator
150 def __add__(self, other):
151 return _actionAppend(self, other)
153 def __radd__(self, other):
154 return _actionAppend(other, self)
156 def _do_create_action(act, *args, **kw):
157 """This is the actual "implementation" for the
158 Action factory method, below. This handles the
159 fact that passing lists to Action() itself has
160 different semantics than passing lists as elements
163 The former will create a ListAction, the latter
164 will create a CommandAction by converting the inner
165 list elements to strings."""
167 if isinstance(act, ActionBase):
169 if SCons.Util.is_List(act):
170 return apply(CommandAction, (act,)+args, kw)
171 if isinstance(act, CommandGenerator):
172 return apply(CommandGeneratorAction, (act.generator,)+args, kw)
174 return apply(FunctionAction, (act,)+args, kw)
175 if SCons.Util.is_String(act):
176 var=SCons.Util.get_environment_var(act)
178 # This looks like a string that is purely an Environment
179 # variable reference, like "$FOO" or "${FOO}". We do
180 # something special here...we lazily evaluate the contents
181 # of that Environment variable, so a user could put something
182 # like a function or a CommandGenerator in that variable
183 # instead of a string.
184 lcg = LazyCmdGenerator(var)
185 return apply(CommandGeneratorAction, (lcg,)+args, kw)
186 commands = string.split(str(act), '\n')
187 if len(commands) == 1:
188 return apply(CommandAction, (commands[0],)+args, kw)
190 listCmdActions = map(lambda x: CommandAction(x), commands)
191 return apply(ListAction, (listCmdActions,)+args, kw)
194 def Action(act, strfunction=_null, varlist=[], presub=_null):
195 """A factory for action objects."""
196 if SCons.Util.is_List(act):
197 acts = map(lambda x, s=strfunction, v=varlist, ps=presub:
198 _do_create_action(x, strfunction=s, varlist=v, presub=ps),
200 acts = filter(lambda x: not x is None, acts)
204 return ListAction(acts, strfunction=strfunction, varlist=varlist, presub=presub)
206 return _do_create_action(act, strfunction=strfunction, varlist=varlist, presub=presub)
209 """Base class for actions that create output objects."""
210 def __init__(self, strfunction=_null, presub=_null, **kw):
211 if not strfunction is _null:
212 self.strfunction = strfunction
214 self.presub = print_actions_presub
218 def __cmp__(self, other):
219 return cmp(self.__dict__, other.__dict__)
221 def __call__(self, target, source, env,
226 if not SCons.Util.is_List(target):
228 if not SCons.Util.is_List(source):
230 if presub is _null: presub = self.presub
231 if show is _null: show = print_actions
232 if execute is _null: execute = execute_actions
234 t = string.join(map(str, target), 'and')
235 l = string.join(self.presub_lines(env), '\n ')
236 out = "Building %s with action(s):\n %s\n" % (t, l)
237 sys.stdout.write(out)
238 if show and self.strfunction:
239 s = self.strfunction(target, source, env)
241 sys.stdout.write(s + '\n')
243 stat = self.execute(target, source, env)
250 def presub_lines(self, env):
251 # CommandGeneratorAction needs a real environment
252 # in order to return the proper string here, since
253 # it may call LazyCmdGenerator, which looks up a key
254 # in that env. So we temporarily remember the env here,
255 # and CommandGeneratorAction will use this env
256 # when it calls its __generate method.
257 self.presub_env = env
258 lines = string.split(str(self), '\n')
259 self.presub_env = None # don't need this any more
262 def genstring(self, target, source, env):
265 def get_actions(self):
268 def __add__(self, other):
269 return _actionAppend(self, other)
271 def __radd__(self, other):
272 return _actionAppend(other, self)
274 def _string_from_cmd_list(cmd_list):
275 """Takes a list of command line arguments and returns a pretty
276 representation for printing."""
278 for arg in map(str, cmd_list):
279 if ' ' in arg or '\t' in arg:
280 arg = '"' + arg + '"'
282 return string.join(cl)
284 class CommandAction(ActionBase):
285 """Class for command-execution actions."""
286 def __init__(self, cmd, **kw):
287 # Cmd list can actually be a list or a single item...basically
288 # anything that we could pass in as the first arg to
289 # Environment.subst_list().
290 if __debug__: logInstanceCreation(self)
291 apply(ActionBase.__init__, (self,), kw)
295 return str(self.cmd_list)
297 def strfunction(self, target, source, env):
298 cmd_list = env.subst_list(self.cmd_list, 0, target, source)
299 return string.join(map(_string_from_cmd_list, cmd_list), "\n")
301 def execute(self, target, source, env):
302 """Execute a command action.
304 This will handle lists of commands as well as individual commands,
305 because construction variable substitution may turn a single
306 "command" into a list. This means that this class can actually
307 handle lists of commands, even though that's not how we use it
312 escape = env.get('ESCAPE', lambda x: x)
314 if env.has_key('SHELL'):
317 raise SCons.Errors.UserError('Missing SHELL construction variable.')
319 # for SConf support (by now): check, if we want to pipe the command
320 # output to somewhere else
321 if env.has_key('PIPE_BUILD'):
323 if env.has_key('PSPAWN'):
324 pspawn = env['PSPAWN']
326 raise SCons.Errors.UserError('Missing PSPAWN construction variable.')
327 if env.has_key('PSTDOUT'):
328 pstdout = env['PSTDOUT']
330 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
331 if env.has_key('PSTDERR'):
332 pstderr = env['PSTDERR']
334 raise SCons.Errors.UserError('Missing PSTDOUT construction variable.')
337 if env.has_key('SPAWN'):
340 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
342 cmd_list = env.subst_list(self.cmd_list, 0, target, source)
343 for cmd_line in cmd_list:
350 import SCons.Environment
351 default_ENV = SCons.Environment.Environment()['ENV']
354 # ensure that the ENV values are all strings:
355 for key, value in ENV.items():
356 if SCons.Util.is_List(value):
357 # If the value is a list, then we assume
358 # it is a path list, because that's a pretty
359 # common list like value to stick in an environment
361 ENV[key] = string.join(map(str, value), os.pathsep)
362 elif not SCons.Util.is_String(value):
363 # If it isn't a string or a list, then
364 # we just coerce it to a string, which
365 # is proper way to handle Dir and File instances
366 # and will produce something reasonable for
367 # just about everything else:
368 ENV[key] = str(value)
370 # Escape the command line for the command
371 # interpreter we are using
372 cmd_line = SCons.Util.escape_list(cmd_line, escape)
374 ret = pspawn( shell, escape, cmd_line[0], cmd_line,
375 ENV, pstdout, pstderr )
377 ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
382 def get_contents(self, target, source, env, dict=None):
383 """Return the signature contents of this action's command line.
385 This strips $(-$) and everything in between the string,
386 since those parts don't affect signatures.
389 if SCons.Util.is_List(cmd):
390 cmd = string.join(map(str, cmd))
393 return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
395 class CommandGeneratorAction(ActionBase):
396 """Class for command-generator actions."""
397 def __init__(self, generator, **kw):
398 if __debug__: logInstanceCreation(self)
399 apply(ActionBase.__init__, (self,), kw)
400 self.generator = generator
402 def __generate(self, target, source, env, for_signature):
403 # ensure that target is a list, to make it easier to write
404 # generator functions:
405 if not SCons.Util.is_List(target):
408 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
409 gen_cmd = Action(ret)
411 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
414 def strfunction(self, target, source, env):
415 if not SCons.Util.is_List(source):
417 rsources = map(rfile, source)
418 act = self.__generate(target, source, env, 0)
420 return act.strfunction(target, rsources, env)
426 env = self.presub_env or {}
427 except AttributeError:
429 act = self.__generate([], [], env, 0)
432 def genstring(self, target, source, env):
433 return str(self.__generate(target, source, env, 0))
435 def execute(self, target, source, env):
436 rsources = map(rfile, source)
437 act = self.__generate(target, source, env, 0)
438 return act.execute(target, source, env)
440 def get_contents(self, target, source, env, dict=None):
441 """Return the signature contents of this action's command line.
443 This strips $(-$) and everything in between the string,
444 since those parts don't affect signatures.
446 return self.__generate(target, source, env, 1).get_contents(target, source, env, dict=None)
448 class LazyCmdGenerator:
449 """This is not really an Action, although it kind of looks like one.
450 This is really a simple callable class that acts as a command
451 generator. It holds on to a key into an Environment dictionary,
452 then waits until execution time to see what type it is, then tries
453 to create an Action out of it."""
454 def __init__(self, var):
455 if __debug__: logInstanceCreation(self)
456 self.var = SCons.Util.to_String(var)
458 def strfunction(self, target, source, env):
462 # The variable reference substitutes to nothing.
466 return 'LazyCmdGenerator: %s'%str(self.var)
468 def __call__(self, target, source, env, for_signature):
472 # The variable reference substitutes to nothing.
475 def __cmp__(self, other):
476 return cmp(self.__dict__, other.__dict__)
478 class FunctionAction(ActionBase):
479 """Class for Python function actions."""
481 def __init__(self, execfunction, **kw):
482 if __debug__: logInstanceCreation(self)
483 self.execfunction = execfunction
484 apply(ActionBase.__init__, (self,), kw)
485 self.varlist = kw.get('varlist', [])
487 def function_name(self):
489 return self.execfunction.__name__
490 except AttributeError:
492 return self.execfunction.__class__.__name__
493 except AttributeError:
494 return "unknown_python_function"
496 def strfunction(self, target, source, env):
498 return '"' + str(s) + '"'
499 def array(a, q=quote):
500 return '[' + string.join(map(lambda x, q=q: q(x), a), ", ") + ']'
501 name = self.function_name()
502 tstr = len(target) == 1 and quote(target[0]) or array(target)
503 sstr = len(source) == 1 and quote(source[0]) or array(source)
504 return "%s(%s, %s)" % (name, tstr, sstr)
507 return "%s(env, target, source)" % self.function_name()
509 def execute(self, target, source, env):
510 rsources = map(rfile, source)
511 return self.execfunction(target=target, source=rsources, env=env)
513 def get_contents(self, target, source, env, dict=None):
514 """Return the signature contents of this callable action.
516 By providing direct access to the code object of the
517 function, Python makes this extremely easy. Hooray!
520 # "self.execfunction" is a function.
521 contents = str(self.execfunction.func_code.co_code)
522 except AttributeError:
523 # "self.execfunction" is a callable object.
525 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
526 except AttributeError:
528 # See if execfunction will do the heavy lifting for us.
529 gc = self.execfunction.get_contents
530 except AttributeError:
531 # This is weird, just do the best we can.
532 contents = str(self.execfunction)
534 contents = gc(target, source, env, dict)
535 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
538 class ListAction(ActionBase):
539 """Class for lists of other actions."""
540 def __init__(self, list, **kw):
541 if __debug__: logInstanceCreation(self)
542 apply(ActionBase.__init__, (self,), kw)
543 self.list = map(lambda x: Action(x), list)
545 def get_actions(self):
552 return string.join(s, "\n")
554 def strfunction(self, target, source, env):
558 x = l.strfunction(target, source, env)
559 if not SCons.Util.is_List(x):
562 return string.join(s, "\n")
564 def execute(self, target, source, env):
566 r = l.execute(target, source, env)
571 def get_contents(self, target, source, env, dict=None):
572 """Return the signature contents of this action list.
574 Simple concatenation of the signatures of the elements.
576 dict = SCons.Util.subst_dict(target, source)
577 return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
578 x.get_contents(t, s, e, d),
583 """A class for delaying calling an Action function with specific
584 (positional and keyword) arguments until the Action is actually
587 This class looks to the rest of the world like a normal Action object,
588 but what it's really doing is hanging on to the arguments until we
589 have a target, source and env to use for the expansion.
591 def __init__(self, parent, args, kw):
595 def get_contents(self, target, source, env, dict=None):
596 actfunc = self.parent.actfunc
598 # "self.actfunc" is a function.
599 contents = str(actfunc.func_code.co_code)
600 except AttributeError:
601 # "self.actfunc" is a callable object.
603 contents = str(actfunc.__call__.im_func.func_code.co_code)
604 except AttributeError:
605 # No __call__() method, so it might be a builtin
606 # or something like that. Do the best we can.
607 contents = str(actfunc)
609 def subst_args(self, target, source, env):
610 return map(lambda x, e=env, t=target, s=source:
613 def subst_kw(self, target, source, env):
615 for key in self.kw.keys():
616 kw[key] = env.subst(self.kw[key], 0, target, source)
618 def __call__(self, target, source, env):
619 args = self.subst_args(target, source, env)
620 kw = self.subst_kw(target, source, env)
621 return apply(self.parent.actfunc, args, kw)
622 def strfunction(self, target, source, env):
623 args = self.subst_args(target, source, env)
624 kw = self.subst_kw(target, source, env)
625 return apply(self.parent.strfunc, args, kw)
628 """A factory class that will wrap up an arbitrary function
629 as an SCons-executable Action object.
631 The real heavy lifting here is done by the ActionCaller class.
632 We just collect the (positional and keyword) arguments that we're
633 called with and give them to the ActionCaller object we create,
634 so it can hang onto them until it needs them.
636 def __init__(self, actfunc, strfunc):
637 self.actfunc = actfunc
638 self.strfunc = strfunc
639 def __call__(self, *args, **kw):
640 ac = ActionCaller(self, args, kw)
641 return Action(ac, strfunction=ac.strfunction)