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 default_exitstatfunc(s):
130 def _actionAppend(act1, act2):
131 # This function knows how to slap two actions together.
132 # Mainly, it handles ListActions by concatenating into
133 # a single ListAction.
136 if a1 is None or a2 is None:
137 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
138 if isinstance(a1, ListAction):
139 if isinstance(a2, ListAction):
140 return ListAction(a1.list + a2.list)
142 return ListAction(a1.list + [ a2 ])
144 if isinstance(a2, ListAction):
145 return ListAction([ a1 ] + a2.list)
147 return ListAction([ a1, a2 ])
149 def _do_create_action(act, *args, **kw):
150 """This is the actual "implementation" for the
151 Action factory method, below. This handles the
152 fact that passing lists to Action() itself has
153 different semantics than passing lists as elements
156 The former will create a ListAction, the latter
157 will create a CommandAction by converting the inner
158 list elements to strings."""
160 if isinstance(act, ActionBase):
162 if SCons.Util.is_List(act):
163 return apply(CommandAction, (act,)+args, kw)
166 gen = kw['generator']
171 action_type = CommandGeneratorAction
173 action_type = FunctionAction
174 return apply(action_type, (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 return apply(LazyAction, (var,)+args, kw)
185 commands = string.split(str(act), '\n')
186 if len(commands) == 1:
187 return apply(CommandAction, (commands[0],)+args, kw)
189 listCmdActions = map(lambda x, args=args, kw=kw:
190 apply(CommandAction, (x,)+args, kw),
192 return ListAction(listCmdActions)
195 def Action(act, *args, **kw):
196 """A factory for action objects."""
197 if SCons.Util.is_List(act):
198 acts = map(lambda a, args=args, kw=kw:
199 apply(_do_create_action, (a,)+args, kw),
201 acts = filter(None, acts)
205 return ListAction(acts)
207 return apply(_do_create_action, (act,)+args, kw)
210 """Base class for all types of action objects that can be held by
211 other objects (Builders, Executors, etc.) This provides the
212 common methods for manipulating and combining those actions."""
214 __metaclass__ = SCons.Memoize.Memoized_Metaclass
216 def __cmp__(self, other):
217 return cmp(self.__dict__, other)
219 def genstring(self, target, source, env):
222 def __add__(self, other):
223 return _actionAppend(self, other)
225 def __radd__(self, other):
226 return _actionAppend(other, self)
228 def presub_lines(self, env):
229 # CommandGeneratorAction needs a real environment
230 # in order to return the proper string here, since
231 # it may call LazyAction, which looks up a key
232 # in that env. So we temporarily remember the env here,
233 # and CommandGeneratorAction will use this env
234 # when it calls its _generate method.
235 self.presub_env = env
236 lines = string.split(str(self), '\n')
237 self.presub_env = None # don't need this any more
240 if not SCons.Memoize.has_metaclass:
242 class ActionBase(SCons.Memoize.Memoizer, _Base):
243 "Cache-backed version of ActionBase"
244 def __init__(self, *args, **kw):
245 apply(_Base.__init__, (self,)+args, kw)
246 SCons.Memoize.Memoizer.__init__(self)
249 class _ActionAction(ActionBase):
250 """Base class for actions that create output objects."""
251 def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
252 if not strfunction is _null:
253 self.strfunction = strfunction
255 presub = print_actions_presub
259 exitstatfunc = default_exitstatfunc
260 self.exitstatfunc = exitstatfunc
262 def print_cmd_line(self, s, target, source, env):
263 sys.stdout.write(s + "\n")
265 def __call__(self, target, source, env,
271 if not SCons.Util.is_List(target):
273 if not SCons.Util.is_List(source):
275 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
276 if presub is _null: presub = self.presub
277 if show is _null: show = print_actions
278 if execute is _null: execute = execute_actions
279 if chdir is _null: chdir = self.chdir
282 save_cwd = os.getcwd()
284 chdir = str(chdir.abspath)
285 except AttributeError:
286 if not SCons.Util.is_String(chdir):
287 chdir = str(target[0].dir)
289 t = string.join(map(str, target), ' and ')
290 l = string.join(self.presub_lines(env), '\n ')
291 out = "Building %s with action:\n %s\n" % (t, l)
292 sys.stdout.write(out)
294 if show and self.strfunction:
295 s = self.strfunction(target, source, env)
298 s = ('os.chdir(%s)\n' % repr(chdir)) + s
301 except AttributeError:
302 print_func = self.print_cmd_line
304 print_func = get('PRINT_CMD_LINE_FUNC')
306 print_func = self.print_cmd_line
307 print_func(s, target, source, env)
313 stat = self.execute(target, source, env)
314 stat = exitstatfunc(stat)
319 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
323 def _string_from_cmd_list(cmd_list):
324 """Takes a list of command line arguments and returns a pretty
325 representation for printing."""
327 for arg in map(str, cmd_list):
328 if ' ' in arg or '\t' in arg:
329 arg = '"' + arg + '"'
331 return string.join(cl)
333 class CommandAction(_ActionAction):
334 """Class for command-execution actions."""
335 def __init__(self, cmd, cmdstr=None, *args, **kw):
336 # Cmd can actually be a list or a single item; if it's a
337 # single item it should be the command string to execute; if a
338 # list then it should be the words of the command string to
339 # execute. Only a single command should be executed by this
340 # object; lists of commands should be handled by embedding
341 # these objects in a ListAction object (which the Action()
342 # factory above does). cmd will be passed to
343 # Environment.subst_list() for substituting environment
345 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
346 apply(_ActionAction.__init__, (self,)+args, kw)
347 if SCons.Util.is_List(cmd):
348 if filter(SCons.Util.is_List, cmd):
349 raise TypeError, "CommandAction should be given only " \
355 if SCons.Util.is_List(self.cmd_list):
356 return string.join(map(str, self.cmd_list), ' ')
357 return str(self.cmd_list)
359 def process(self, target, source, env):
360 result = env.subst_list(self.cmd_list, 0, target, source)
364 try: c = result[0][0][0]
365 except IndexError: c = None
366 if c == '@': silent = 1
367 elif c == '-': ignore = 1
369 result[0][0] = result[0][0][1:]
372 result[0] = result[0][1:]
375 return result, ignore, silent
377 def strfunction(self, target, source, env):
378 if not self.cmdstr is None:
379 c = env.subst(self.cmdstr, 0, target, source)
382 cmd_list, ignore, silent = self.process(target, source, env)
385 return _string_from_cmd_list(cmd_list[0])
387 def execute(self, target, source, env):
388 """Execute a command action.
390 This will handle lists of commands as well as individual commands,
391 because construction variable substitution may turn a single
392 "command" into a list. This means that this class can actually
393 handle lists of commands, even though that's not how we use it
396 from SCons.Util import is_String, is_List, flatten, escape_list
401 raise SCons.Errors.UserError('Missing SHELL construction variable.')
406 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
408 escape = env.get('ESCAPE', lambda x: x)
415 import SCons.Environment
416 default_ENV = SCons.Environment.Environment()['ENV']
419 # Ensure that the ENV values are all strings:
420 for key, value in ENV.items():
421 if not is_String(value):
423 # If the value is a list, then we assume it is a
424 # path list, because that's a pretty common list-like
425 # value to stick in an environment variable:
426 value = flatten(value)
427 ENV[key] = string.join(map(str, value), os.pathsep)
429 # If it isn't a string or a list, then we just coerce
430 # it to a string, which is the proper way to handle
431 # Dir and File instances and will produce something
432 # reasonable for just about everything else:
433 ENV[key] = str(value)
435 cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
437 # Use len() to filter out any "command" that's zero-length.
438 for cmd_line in filter(len, cmd_list):
439 # Escape the command line for the interpreter we are using.
440 cmd_line = escape_list(cmd_line, escape)
441 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
442 if not ignore and result:
446 def get_contents(self, target, source, env):
447 """Return the signature contents of this action's command line.
449 This strips $(-$) and everything in between the string,
450 since those parts don't affect signatures.
453 if SCons.Util.is_List(cmd):
454 cmd = string.join(map(str, cmd))
457 return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source)
459 class CommandGeneratorAction(ActionBase):
460 """Class for command-generator actions."""
461 def __init__(self, generator, *args, **kw):
462 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
463 self.generator = generator
466 def _generate(self, target, source, env, for_signature):
467 # ensure that target is a list, to make it easier to write
468 # generator functions:
469 if not SCons.Util.is_List(target):
472 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
473 gen_cmd = apply(Action, (ret,), self.gen_kw)
475 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
480 env = self.presub_env or {}
481 except AttributeError:
483 act = self._generate([], [], env, 1)
486 def genstring(self, target, source, env):
487 return self._generate(target, source, env, 1).genstring(target, source, env)
489 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
490 show=_null, execute=_null, chdir=_null):
491 act = self._generate(target, source, env, 0)
492 return act(target, source, env, exitstatfunc, presub,
493 show, execute, chdir)
495 def get_contents(self, target, source, env):
496 """Return the signature contents of this action's command line.
498 This strips $(-$) and everything in between the string,
499 since those parts don't affect signatures.
501 return self._generate(target, source, env, 1).get_contents(target, source, env)
505 # A LazyAction is a kind of hybrid generator and command action for
506 # strings of the form "$VAR". These strings normally expand to other
507 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
508 # want to be able to replace them with functions in the construction
509 # environment. Consequently, we want lazy evaluation and creation of
510 # an Action in the case of the function, but that's overkill in the more
511 # normal case of expansion to other strings.
513 # So we do this with a subclass that's both a generator *and*
514 # a command action. The overridden methods all do a quick check
515 # of the construction variable, and if it's a string we just call
516 # the corresponding CommandAction method to do the heavy lifting.
517 # If not, then we call the same-named CommandGeneratorAction method.
518 # The CommandGeneratorAction methods work by using the overridden
519 # _generate() method, that is, our own way of handling "generation" of
520 # an action based on what's in the construction variable.
522 class LazyAction(CommandGeneratorAction, CommandAction):
524 __metaclass__ = SCons.Memoize.Memoized_Metaclass
526 def __init__(self, var, *args, **kw):
527 if __debug__: logInstanceCreation(self, 'Action.LazyAction')
528 apply(CommandAction.__init__, (self, '$'+var)+args, kw)
529 self.var = SCons.Util.to_String(var)
532 def get_parent_class(self, env):
533 c = env.get(self.var)
534 if SCons.Util.is_String(c) and not '\n' in c:
536 return CommandGeneratorAction
538 def _generate_cache(self, env):
540 c = env.get(self.var, '')
541 gen_cmd = apply(Action, (c,), self.gen_kw)
543 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
546 def _generate(self, target, source, env, for_signature):
547 return self._generate_cache(env)
549 def __call__(self, target, source, env, *args, **kw):
550 args = (self, target, source, env) + args
551 c = self.get_parent_class(env)
552 return apply(c.__call__, args, kw)
554 def get_contents(self, target, source, env):
555 c = self.get_parent_class(env)
556 return c.get_contents(self, target, source, env)
558 if not SCons.Memoize.has_metaclass:
560 class LazyAction(SCons.Memoize.Memoizer, _Base):
561 def __init__(self, *args, **kw):
562 SCons.Memoize.Memoizer.__init__(self)
563 apply(_Base.__init__, (self,)+args, kw)
567 class FunctionAction(_ActionAction):
568 """Class for Python function actions."""
570 def __init__(self, execfunction, *args, **kw):
571 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
572 self.execfunction = execfunction
573 apply(_ActionAction.__init__, (self,)+args, kw)
574 self.varlist = kw.get('varlist', [])
576 def function_name(self):
578 return self.execfunction.__name__
579 except AttributeError:
581 return self.execfunction.__class__.__name__
582 except AttributeError:
583 return "unknown_python_function"
585 def strfunction(self, target, source, env):
588 return '"' + str(s) + '"'
589 return '[' + string.join(map(quote, a), ", ") + ']'
591 strfunc = self.execfunction.strfunction
592 except AttributeError:
597 if callable(strfunc):
598 return strfunc(target, source, env)
599 name = self.function_name()
602 return "%s(%s, %s)" % (name, tstr, sstr)
605 name = self.function_name()
606 if name == 'ActionCaller':
607 return str(self.execfunction)
608 return "%s(target, source, env)" % name
610 def execute(self, target, source, env):
611 rsources = map(rfile, source)
613 result = self.execfunction(target=target, source=rsources, env=env)
614 except EnvironmentError, e:
615 # If an IOError/OSError happens, raise a BuildError.
616 raise SCons.Errors.BuildError(node=target, errstr=e.strerror)
619 def get_contents(self, target, source, env):
620 """Return the signature contents of this callable action.
622 By providing direct access to the code object of the
623 function, Python makes this extremely easy. Hooray!
626 # "self.execfunction" is a function.
627 contents = str(self.execfunction.func_code.co_code)
628 except AttributeError:
629 # "self.execfunction" is a callable object.
631 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
632 except AttributeError:
634 # See if execfunction will do the heavy lifting for us.
635 gc = self.execfunction.get_contents
636 except AttributeError:
637 # This is weird, just do the best we can.
638 contents = str(self.execfunction)
640 contents = gc(target, source, env)
641 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
644 class ListAction(ActionBase):
645 """Class for lists of other actions."""
646 def __init__(self, list):
647 if __debug__: logInstanceCreation(self, 'Action.ListAction')
648 def list_of_actions(x):
649 if isinstance(x, ActionBase):
652 self.list = map(list_of_actions, list)
654 def genstring(self, target, source, env):
655 return string.join(map(lambda a, t=target, s=source, e=env:
656 a.genstring(t, s, e),
661 return string.join(map(str, self.list), '\n')
663 def presub_lines(self, env):
664 return SCons.Util.flatten(map(lambda a, env=env:
668 def get_contents(self, target, source, env):
669 """Return the signature contents of this action list.
671 Simple concatenation of the signatures of the elements.
673 return string.join(map(lambda x, t=target, s=source, e=env:
674 x.get_contents(t, s, e),
678 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
679 show=_null, execute=_null, chdir=_null):
680 for act in self.list:
681 stat = act(target, source, env, exitstatfunc, presub,
682 show, execute, chdir)
688 """A class for delaying calling an Action function with specific
689 (positional and keyword) arguments until the Action is actually
692 This class looks to the rest of the world like a normal Action object,
693 but what it's really doing is hanging on to the arguments until we
694 have a target, source and env to use for the expansion.
696 def __init__(self, parent, args, kw):
700 def get_contents(self, target, source, env):
701 actfunc = self.parent.actfunc
703 # "self.actfunc" is a function.
704 contents = str(actfunc.func_code.co_code)
705 except AttributeError:
706 # "self.actfunc" is a callable object.
708 contents = str(actfunc.__call__.im_func.func_code.co_code)
709 except AttributeError:
710 # No __call__() method, so it might be a builtin
711 # or something like that. Do the best we can.
712 contents = str(actfunc)
714 def subst(self, s, target, source, env):
715 # Special-case hack: Let a custom function wrapped in an
716 # ActionCaller get at the environment through which the action
717 # was called by using this hard-coded value as a special return.
721 return env.subst(s, 0, target, source)
722 def subst_args(self, target, source, env):
723 return map(lambda x, self=self, t=target, s=source, e=env:
724 self.subst(x, t, s, e),
726 def subst_kw(self, target, source, env):
728 for key in self.kw.keys():
729 kw[key] = self.subst(self.kw[key], target, source, env)
731 def __call__(self, target, source, env):
732 args = self.subst_args(target, source, env)
733 kw = self.subst_kw(target, source, env)
734 return apply(self.parent.actfunc, args, kw)
735 def strfunction(self, target, source, env):
736 args = self.subst_args(target, source, env)
737 kw = self.subst_kw(target, source, env)
738 return apply(self.parent.strfunc, args, kw)
740 return apply(self.parent.strfunc, self.args, self.kw)
743 """A factory class that will wrap up an arbitrary function
744 as an SCons-executable Action object.
746 The real heavy lifting here is done by the ActionCaller class.
747 We just collect the (positional and keyword) arguments that we're
748 called with and give them to the ActionCaller object we create,
749 so it can hang onto them until it needs them.
751 def __init__(self, actfunc, strfunc):
752 self.actfunc = actfunc
753 self.strfunc = strfunc
754 def __call__(self, *args, **kw):
755 ac = ActionCaller(self, args, kw)
756 action = Action(ac, strfunction=ac.strfunction)