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 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.
16 The heavy lifting is handled by subclasses for the different types of
17 actions we might execute:
20 CommandGeneratorAction
24 The subclasses supply the following public interface methods used by
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.
33 Fetches the "contents" of an Action for signature calculation.
34 This is what the Sig/*.py subsystem uses to decide if a target
35 needs to be rebuilt because its action changed.
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.
46 Subclasses also supply the following methods for internal use within
50 Returns a string approximation of the Action; no variable
51 substitution is performed.
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.
61 Returns a substituted string representation of the Action.
62 This is used by the _ActionAction.show() command to display the
63 command/function that will be executed to generate the target(s).
65 There is a related independent ActionCaller class that looks like a
66 regular Action, and which serves as a wrapper for arbitrary functions
67 that we want to let the user specify the arguments to now, but actually
68 execute later (when an out-of-date check determines that it's needed to
69 be executed, for example). Objects of this class are returned by an
70 ActionFactory class that provides a __call__() method as a convenient
71 way for wrapping up the functions.
78 # Permission is hereby granted, free of charge, to any person obtaining
79 # a copy of this software and associated documentation files (the
80 # "Software"), to deal in the Software without restriction, including
81 # without limitation the rights to use, copy, modify, merge, publish,
82 # distribute, sublicense, and/or sell copies of the Software, and to
83 # permit persons to whom the Software is furnished to do so, subject to
84 # the following conditions:
86 # The above copyright notice and this permission notice shall be included
87 # in all copies or substantial portions of the Software.
89 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
90 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
91 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
92 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
93 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
94 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
95 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
98 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
106 from SCons.Debug import logInstanceCreation
117 print_actions_presub = 0
124 except AttributeError:
127 def _actionAppend(act1, act2):
128 # This function knows how to slap two actions together.
129 # Mainly, it handles ListActions by concatenating into
130 # a single ListAction.
133 if a1 is None or a2 is None:
134 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
135 if isinstance(a1, ListAction):
136 if isinstance(a2, ListAction):
137 return ListAction(a1.list + a2.list)
139 return ListAction(a1.list + [ a2 ])
141 if isinstance(a2, ListAction):
142 return ListAction([ a1 ] + a2.list)
144 return ListAction([ a1, a2 ])
146 class CommandGenerator:
148 Wraps a command generator function so the Action() factory
149 function can tell a generator function from a function action.
151 def __init__(self, generator):
152 self.generator = generator
154 def __add__(self, other):
155 return _actionAppend(self, other)
157 def __radd__(self, other):
158 return _actionAppend(other, self)
160 def _do_create_action(act, *args, **kw):
161 """This is the actual "implementation" for the
162 Action factory method, below. This handles the
163 fact that passing lists to Action() itself has
164 different semantics than passing lists as elements
167 The former will create a ListAction, the latter
168 will create a CommandAction by converting the inner
169 list elements to strings."""
171 if isinstance(act, ActionBase):
173 if SCons.Util.is_List(act):
174 return apply(CommandAction, (act,)+args, kw)
175 if isinstance(act, CommandGenerator):
176 return apply(CommandGeneratorAction, (act.generator,)+args, kw)
178 return apply(FunctionAction, (act,)+args, kw)
179 if SCons.Util.is_String(act):
180 var=SCons.Util.get_environment_var(act)
182 # This looks like a string that is purely an Environment
183 # variable reference, like "$FOO" or "${FOO}". We do
184 # something special here...we lazily evaluate the contents
185 # of that Environment variable, so a user could put something
186 # like a function or a CommandGenerator in that variable
187 # instead of a string.
188 return apply(LazyAction, (var,)+args, kw)
189 commands = string.split(str(act), '\n')
190 if len(commands) == 1:
191 return apply(CommandAction, (commands[0],)+args, kw)
193 listCmdActions = map(lambda x, args=args, kw=kw:
194 apply(CommandAction, (x,)+args, kw),
196 return ListAction(listCmdActions)
199 def Action(act, *args, **kw):
200 """A factory for action objects."""
201 if SCons.Util.is_List(act):
202 acts = map(lambda a, args=args, kw=kw:
203 apply(_do_create_action, (a,)+args, kw),
205 acts = filter(None, acts)
209 return ListAction(acts)
211 return apply(_do_create_action, (act,)+args, kw)
214 """Base class for all types of action objects that can be held by
215 other objects (Builders, Executors, etc.) This provides the
216 common methods for manipulating and combining those actions."""
218 def __cmp__(self, other):
219 return cmp(self.__dict__, other)
221 def genstring(self, target, source, env):
224 def __add__(self, other):
225 return _actionAppend(self, other)
227 def __radd__(self, other):
228 return _actionAppend(other, self)
230 def presub_lines(self, env):
231 # CommandGeneratorAction needs a real environment
232 # in order to return the proper string here, since
233 # it may call LazyCmdGenerator, which looks up a key
234 # in that env. So we temporarily remember the env here,
235 # and CommandGeneratorAction will use this env
236 # when it calls its _generate method.
237 self.presub_env = env
238 lines = string.split(str(self), '\n')
239 self.presub_env = None # don't need this any more
243 class _ActionAction(ActionBase):
244 """Base class for actions that create output objects."""
245 def __init__(self, strfunction=_null, presub=_null, chdir=None, **kw):
246 if not strfunction is _null:
247 self.strfunction = strfunction
249 presub = print_actions_presub
253 def print_cmd_line(self, s, target, source, env):
254 sys.stdout.write(s + "\n")
256 def __call__(self, target, source, env,
262 if not SCons.Util.is_List(target):
264 if not SCons.Util.is_List(source):
266 if presub is _null: presub = self.presub
267 if show is _null: show = print_actions
268 if execute is _null: execute = execute_actions
269 if chdir is _null: chdir = self.chdir
272 save_cwd = os.getcwd()
274 chdir = str(chdir.abspath)
275 except AttributeError:
276 if not SCons.Util.is_String(chdir):
277 chdir = str(target[0].dir)
279 t = string.join(map(str, target), ' and ')
280 l = string.join(self.presub_lines(env), '\n ')
281 out = "Building %s with action:\n %s\n" % (t, l)
282 sys.stdout.write(out)
284 if show and self.strfunction:
285 s = self.strfunction(target, source, env)
288 s = ('os.chdir(%s)\n' % repr(chdir)) + s
291 except AttributeError:
292 print_func = self.print_cmd_line
294 print_func = get('PRINT_CMD_LINE_FUNC')
296 print_func = self.print_cmd_line
297 print_func(s, target, source, env)
303 stat = self.execute(target, source, env)
310 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
314 def _string_from_cmd_list(cmd_list):
315 """Takes a list of command line arguments and returns a pretty
316 representation for printing."""
318 for arg in map(str, cmd_list):
319 if ' ' in arg or '\t' in arg:
320 arg = '"' + arg + '"'
322 return string.join(cl)
324 class CommandAction(_ActionAction):
325 """Class for command-execution actions."""
326 def __init__(self, cmd, cmdstr=None, *args, **kw):
327 # Cmd can actually be a list or a single item; if it's a
328 # single item it should be the command string to execute; if a
329 # list then it should be the words of the command string to
330 # execute. Only a single command should be executed by this
331 # object; lists of commands should be handled by embedding
332 # these objects in a ListAction object (which the Action()
333 # factory above does). cmd will be passed to
334 # Environment.subst_list() for substituting environment
336 if __debug__: logInstanceCreation(self)
337 apply(_ActionAction.__init__, (self,)+args, kw)
338 if SCons.Util.is_List(cmd):
339 if filter(SCons.Util.is_List, cmd):
340 raise TypeError, "CommandAction should be given only " \
346 if SCons.Util.is_List(self.cmd_list):
347 return string.join(map(str, self.cmd_list), ' ')
348 return str(self.cmd_list)
350 def strfunction(self, target, source, env):
351 if not self.cmdstr is None:
352 c = env.subst(self.cmdstr, 0, target, source)
355 cmd_list = env.subst_list(self.cmd_list, 0, target, source)
356 return _string_from_cmd_list(cmd_list[0])
358 def execute(self, target, source, env):
359 """Execute a command action.
361 This will handle lists of commands as well as individual commands,
362 because construction variable substitution may turn a single
363 "command" into a list. This means that this class can actually
364 handle lists of commands, even though that's not how we use it
367 from SCons.Util import is_String, is_List, flatten, escape_list
372 raise SCons.Errors.UserError('Missing SHELL construction variable.')
377 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
379 escape = env.get('ESCAPE', lambda x: x)
386 import SCons.Environment
387 default_ENV = SCons.Environment.Environment()['ENV']
390 # Ensure that the ENV values are all strings:
391 for key, value in ENV.items():
392 if not is_String(value):
394 # If the value is a list, then we assume it is a
395 # path list, because that's a pretty common list-like
396 # value to stick in an environment variable:
397 value = flatten(value)
398 ENV[key] = string.join(map(str, value), os.pathsep)
400 # If it isn't a string or a list, then we just coerce
401 # it to a string, which is the proper way to handle
402 # Dir and File instances and will produce something
403 # reasonable for just about everything else:
404 ENV[key] = str(value)
406 cmd_list = env.subst_list(self.cmd_list, 0, target,
409 # Use len() to filter out any "command" that's zero-length.
410 for cmd_line in filter(len, cmd_list):
411 # Escape the command line for the interpreter we are using.
412 cmd_line = escape_list(cmd_line, escape)
413 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
418 def get_contents(self, target, source, env, dict=None):
419 """Return the signature contents of this action's command line.
421 This strips $(-$) and everything in between the string,
422 since those parts don't affect signatures.
425 if SCons.Util.is_List(cmd):
426 cmd = string.join(map(str, cmd))
429 return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
431 class CommandGeneratorAction(ActionBase):
432 """Class for command-generator actions."""
433 def __init__(self, generator, *args, **kw):
434 if __debug__: logInstanceCreation(self)
435 self.generator = generator
438 def _generate(self, target, source, env, for_signature):
439 # ensure that target is a list, to make it easier to write
440 # generator functions:
441 if not SCons.Util.is_List(target):
444 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
445 gen_cmd = apply(Action, (ret,), self.gen_kw)
447 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
452 env = self.presub_env or {}
453 except AttributeError:
455 act = self._generate([], [], env, 1)
458 def genstring(self, target, source, env):
459 return self._generate(target, source, env, 1).genstring(target, source, env)
461 def __call__(self, target, source, env, errfunc=None, presub=_null,
462 show=_null, execute=_null, chdir=_null):
463 act = self._generate(target, source, env, 0)
464 return act(target, source, env, errfunc, presub,
465 show, execute, chdir)
467 def get_contents(self, target, source, env, dict=None):
468 """Return the signature contents of this action's command line.
470 This strips $(-$) and everything in between the string,
471 since those parts don't affect signatures.
473 return self._generate(target, source, env, 1).get_contents(target, source, env, dict=None)
475 # Ooh, polymorphism -- pretty scary, eh, kids?
477 # A LazyCmdAction is a kind of hybrid generator and command action for
478 # strings of the form "$VAR". These strings normally expand to other
479 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
480 # want to be able to replace them with functions in the construction
481 # environment. Consequently, we want lazy evaluation and creation of
482 # an Action in the case of the function, but that's overkill in the more
483 # normal case of expansion to other strings.
485 # So we do this with a subclass that's both a generator *and*
486 # a command action. The overridden methods all do a quick check
487 # of the construction variable, and if it's a string we just call
488 # the corresponding CommandAction method to do the heavy lifting.
489 # If not, then we call the same-named CommandGeneratorAction method.
490 # The CommandGeneratorAction methods work by using the overridden
491 # _generate() method, uses our own way of handling "generation" of an
492 # action based on what's in the construction variable.
494 class LazyAction(CommandGeneratorAction, CommandAction):
495 def __init__(self, var, *args, **kw):
496 if __debug__: logInstanceCreation(self)
497 apply(CommandAction.__init__, (self, '$'+var)+args, kw)
498 self.var = SCons.Util.to_String(var)
501 def get_parent_class(self, env):
502 c = env.get(self.var)
503 if SCons.Util.is_String(c) and not '\n' in c:
505 return CommandGeneratorAction
507 def _generate(self, target, source, env, for_signature):
508 c = env.get(self.var, '')
509 gen_cmd = apply(Action, (c,), self.gen_kw)
511 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
514 def __call__(self, target, source, env, *args, **kw):
515 args = (self, target, source, env) + args
516 c = self.get_parent_class(env)
517 return apply(c.__call__, args, kw)
519 def get_contents(self, target, source, env, dict=None):
520 c = self.get_parent_class(env)
521 return c.get_contents(self, target, source, env, dict)
523 class FunctionAction(_ActionAction):
524 """Class for Python function actions."""
526 def __init__(self, execfunction, *args, **kw):
527 if __debug__: logInstanceCreation(self)
528 self.execfunction = execfunction
529 apply(_ActionAction.__init__, (self,)+args, kw)
530 self.varlist = kw.get('varlist', [])
532 def function_name(self):
534 return self.execfunction.__name__
535 except AttributeError:
537 return self.execfunction.__class__.__name__
538 except AttributeError:
539 return "unknown_python_function"
541 def strfunction(self, target, source, env):
544 return '"' + str(s) + '"'
545 return '[' + string.join(map(quote, a), ", ") + ']'
547 strfunc = self.execfunction.strfunction
548 except AttributeError:
553 if callable(strfunc):
554 return strfunc(target, source, env)
555 name = self.function_name()
558 return "%s(%s, %s)" % (name, tstr, sstr)
561 return "%s(target, source, env)" % self.function_name()
563 def execute(self, target, source, env):
564 rsources = map(rfile, source)
566 result = self.execfunction(target=target, source=rsources, env=env)
567 except EnvironmentError, e:
568 # If an IOError/OSError happens, raise a BuildError.
569 raise SCons.Errors.BuildError(node=target, errstr=e.strerror)
572 def get_contents(self, target, source, env, dict=None):
573 """Return the signature contents of this callable action.
575 By providing direct access to the code object of the
576 function, Python makes this extremely easy. Hooray!
579 # "self.execfunction" is a function.
580 contents = str(self.execfunction.func_code.co_code)
581 except AttributeError:
582 # "self.execfunction" is a callable object.
584 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
585 except AttributeError:
587 # See if execfunction will do the heavy lifting for us.
588 gc = self.execfunction.get_contents
589 except AttributeError:
590 # This is weird, just do the best we can.
591 contents = str(self.execfunction)
593 contents = gc(target, source, env, dict)
594 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
597 class ListAction(ActionBase):
598 """Class for lists of other actions."""
599 def __init__(self, list):
600 if __debug__: logInstanceCreation(self)
601 def list_of_actions(x):
602 if isinstance(x, ActionBase):
605 self.list = map(list_of_actions, list)
607 def genstring(self, target, source, env):
608 return string.join(map(lambda a, t=target, s=source, e=env:
609 a.genstring(t, s, e),
614 return string.join(map(str, self.list), '\n')
616 def presub_lines(self, env):
617 return SCons.Util.flatten(map(lambda a, env=env:
621 def get_contents(self, target, source, env, dict=None):
622 """Return the signature contents of this action list.
624 Simple concatenation of the signatures of the elements.
626 dict = SCons.Util.subst_dict(target, source)
627 return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
628 x.get_contents(t, s, e, d),
632 def __call__(self, target, source, env, errfunc=None, presub=_null,
633 show=_null, execute=_null, chdir=_null):
634 for act in self.list:
635 stat = act(target, source, env, errfunc, presub,
636 show, execute, chdir)
642 """A class for delaying calling an Action function with specific
643 (positional and keyword) arguments until the Action is actually
646 This class looks to the rest of the world like a normal Action object,
647 but what it's really doing is hanging on to the arguments until we
648 have a target, source and env to use for the expansion.
650 def __init__(self, parent, args, kw):
654 def get_contents(self, target, source, env, dict=None):
655 actfunc = self.parent.actfunc
657 # "self.actfunc" is a function.
658 contents = str(actfunc.func_code.co_code)
659 except AttributeError:
660 # "self.actfunc" is a callable object.
662 contents = str(actfunc.__call__.im_func.func_code.co_code)
663 except AttributeError:
664 # No __call__() method, so it might be a builtin
665 # or something like that. Do the best we can.
666 contents = str(actfunc)
668 def subst(self, s, target, source, env):
669 # Special-case hack: Let a custom function wrapped in an
670 # ActionCaller get at the environment through which the action
671 # was called by using this hard-coded value as a special return.
675 return env.subst(s, 0, target, source)
676 def subst_args(self, target, source, env):
677 return map(lambda x, self=self, t=target, s=source, e=env:
678 self.subst(x, t, s, e),
680 def subst_kw(self, target, source, env):
682 for key in self.kw.keys():
683 kw[key] = self.subst(self.kw[key], target, source, env)
685 def __call__(self, target, source, env):
686 args = self.subst_args(target, source, env)
687 kw = self.subst_kw(target, source, env)
688 return apply(self.parent.actfunc, args, kw)
689 def strfunction(self, target, source, env):
690 args = self.subst_args(target, source, env)
691 kw = self.subst_kw(target, source, env)
692 return apply(self.parent.strfunc, args, kw)
695 """A factory class that will wrap up an arbitrary function
696 as an SCons-executable Action object.
698 The real heavy lifting here is done by the ActionCaller class.
699 We just collect the (positional and keyword) arguments that we're
700 called with and give them to the ActionCaller object we create,
701 so it can hang onto them until it needs them.
703 def __init__(self, actfunc, strfunc):
704 self.actfunc = actfunc
705 self.strfunc = strfunc
706 def __call__(self, *args, **kw):
707 ac = ActionCaller(self, args, kw)
708 action = Action(ac, strfunction=ac.strfunction)
709 # action will be a FunctionAction; if left to its own devices,
710 # a genstr or str of this action will just show
711 # "ActionCaller(target, source, env)". Override that with the
712 # description from strfunc. Note that the apply is evaluated
713 # right now; __str__ is set to a (lambda) function that just
714 # returns the stored result of the evaluation whenever called.
715 action.__str__ = lambda name=apply(self.strfunc, args, kw): name