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 __metaclass__ = SCons.Memoize.Memoized_Metaclass
220 def __cmp__(self, other):
221 return cmp(self.__dict__, other)
223 def genstring(self, target, source, env):
226 def __add__(self, other):
227 return _actionAppend(self, other)
229 def __radd__(self, other):
230 return _actionAppend(other, self)
232 def presub_lines(self, env):
233 # CommandGeneratorAction needs a real environment
234 # in order to return the proper string here, since
235 # it may call LazyAction, which looks up a key
236 # in that env. So we temporarily remember the env here,
237 # and CommandGeneratorAction will use this env
238 # when it calls its _generate method.
239 self.presub_env = env
240 lines = string.split(str(self), '\n')
241 self.presub_env = None # don't need this any more
244 if not SCons.Memoize.has_metaclass:
246 class ActionBase(SCons.Memoize.Memoizer, _Base):
247 "Cache-backed version of ActionBase"
248 def __init__(self, *args, **kw):
249 apply(_Base.__init__, (self,)+args, kw)
250 SCons.Memoize.Memoizer.__init__(self)
253 class _ActionAction(ActionBase):
254 """Base class for actions that create output objects."""
255 def __init__(self, strfunction=_null, presub=_null, chdir=None, **kw):
256 if not strfunction is _null:
257 self.strfunction = strfunction
259 presub = print_actions_presub
263 def print_cmd_line(self, s, target, source, env):
264 sys.stdout.write(s + "\n")
266 def __call__(self, target, source, env,
272 if not SCons.Util.is_List(target):
274 if not SCons.Util.is_List(source):
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)
320 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
324 def _string_from_cmd_list(cmd_list):
325 """Takes a list of command line arguments and returns a pretty
326 representation for printing."""
328 for arg in map(str, cmd_list):
329 if ' ' in arg or '\t' in arg:
330 arg = '"' + arg + '"'
332 return string.join(cl)
334 class CommandAction(_ActionAction):
335 """Class for command-execution actions."""
336 def __init__(self, cmd, cmdstr=None, *args, **kw):
337 # Cmd can actually be a list or a single item; if it's a
338 # single item it should be the command string to execute; if a
339 # list then it should be the words of the command string to
340 # execute. Only a single command should be executed by this
341 # object; lists of commands should be handled by embedding
342 # these objects in a ListAction object (which the Action()
343 # factory above does). cmd will be passed to
344 # Environment.subst_list() for substituting environment
346 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
347 apply(_ActionAction.__init__, (self,)+args, kw)
348 if SCons.Util.is_List(cmd):
349 if filter(SCons.Util.is_List, cmd):
350 raise TypeError, "CommandAction should be given only " \
356 if SCons.Util.is_List(self.cmd_list):
357 return string.join(map(str, self.cmd_list), ' ')
358 return str(self.cmd_list)
360 def strfunction(self, target, source, env):
361 if not self.cmdstr is None:
362 c = env.subst(self.cmdstr, 0, target, source)
365 cmd_list = env.subst_list(self.cmd_list, 0, target, source)
366 return _string_from_cmd_list(cmd_list[0])
368 def execute(self, target, source, env):
369 """Execute a command action.
371 This will handle lists of commands as well as individual commands,
372 because construction variable substitution may turn a single
373 "command" into a list. This means that this class can actually
374 handle lists of commands, even though that's not how we use it
377 from SCons.Util import is_String, is_List, flatten, escape_list
382 raise SCons.Errors.UserError('Missing SHELL construction variable.')
387 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
389 escape = env.get('ESCAPE', lambda x: x)
396 import SCons.Environment
397 default_ENV = SCons.Environment.Environment()['ENV']
400 # Ensure that the ENV values are all strings:
401 for key, value in ENV.items():
402 if not is_String(value):
404 # If the value is a list, then we assume it is a
405 # path list, because that's a pretty common list-like
406 # value to stick in an environment variable:
407 value = flatten(value)
408 ENV[key] = string.join(map(str, value), os.pathsep)
410 # If it isn't a string or a list, then we just coerce
411 # it to a string, which is the proper way to handle
412 # Dir and File instances and will produce something
413 # reasonable for just about everything else:
414 ENV[key] = str(value)
416 cmd_list = env.subst_list(self.cmd_list, 0, target,
419 # Use len() to filter out any "command" that's zero-length.
420 for cmd_line in filter(len, cmd_list):
421 # Escape the command line for the interpreter we are using.
422 cmd_line = escape_list(cmd_line, escape)
423 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
428 def get_contents(self, target, source, env):
429 """Return the signature contents of this action's command line.
431 This strips $(-$) and everything in between the string,
432 since those parts don't affect signatures.
435 if SCons.Util.is_List(cmd):
436 cmd = string.join(map(str, cmd))
439 return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source)
441 class CommandGeneratorAction(ActionBase):
442 """Class for command-generator actions."""
443 def __init__(self, generator, *args, **kw):
444 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
445 self.generator = generator
448 def _generate(self, target, source, env, for_signature):
449 # ensure that target is a list, to make it easier to write
450 # generator functions:
451 if not SCons.Util.is_List(target):
454 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
455 gen_cmd = apply(Action, (ret,), self.gen_kw)
457 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
462 env = self.presub_env or {}
463 except AttributeError:
465 act = self._generate([], [], env, 1)
468 def genstring(self, target, source, env):
469 return self._generate(target, source, env, 1).genstring(target, source, env)
471 def __call__(self, target, source, env, errfunc=None, presub=_null,
472 show=_null, execute=_null, chdir=_null):
473 act = self._generate(target, source, env, 0)
474 return act(target, source, env, errfunc, presub,
475 show, execute, chdir)
477 def get_contents(self, target, source, env):
478 """Return the signature contents of this action's command line.
480 This strips $(-$) and everything in between the string,
481 since those parts don't affect signatures.
483 return self._generate(target, source, env, 1).get_contents(target, source, env)
487 # A LazyAction is a kind of hybrid generator and command action for
488 # strings of the form "$VAR". These strings normally expand to other
489 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
490 # want to be able to replace them with functions in the construction
491 # environment. Consequently, we want lazy evaluation and creation of
492 # an Action in the case of the function, but that's overkill in the more
493 # normal case of expansion to other strings.
495 # So we do this with a subclass that's both a generator *and*
496 # a command action. The overridden methods all do a quick check
497 # of the construction variable, and if it's a string we just call
498 # the corresponding CommandAction method to do the heavy lifting.
499 # If not, then we call the same-named CommandGeneratorAction method.
500 # The CommandGeneratorAction methods work by using the overridden
501 # _generate() method, that is, our own way of handling "generation" of
502 # an action based on what's in the construction variable.
504 class LazyAction(CommandGeneratorAction, CommandAction):
506 __metaclass__ = SCons.Memoize.Memoized_Metaclass
508 def __init__(self, var, *args, **kw):
509 if __debug__: logInstanceCreation(self, 'Action.LazyAction')
510 apply(CommandAction.__init__, (self, '$'+var)+args, kw)
511 self.var = SCons.Util.to_String(var)
514 def get_parent_class(self, env):
515 c = env.get(self.var)
516 if SCons.Util.is_String(c) and not '\n' in c:
518 return CommandGeneratorAction
520 def _generate_cache(self, env):
522 c = env.get(self.var, '')
523 gen_cmd = apply(Action, (c,), self.gen_kw)
525 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
528 def _generate(self, target, source, env, for_signature):
529 return self._generate_cache(env)
531 def __call__(self, target, source, env, *args, **kw):
532 args = (self, target, source, env) + args
533 c = self.get_parent_class(env)
534 return apply(c.__call__, args, kw)
536 def get_contents(self, target, source, env):
537 c = self.get_parent_class(env)
538 return c.get_contents(self, target, source, env)
540 if not SCons.Memoize.has_metaclass:
542 class LazyAction(SCons.Memoize.Memoizer, _Base):
543 def __init__(self, *args, **kw):
544 SCons.Memoize.Memoizer.__init__(self)
545 apply(_Base.__init__, (self,)+args, kw)
549 class FunctionAction(_ActionAction):
550 """Class for Python function actions."""
552 def __init__(self, execfunction, *args, **kw):
553 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
554 self.execfunction = execfunction
555 apply(_ActionAction.__init__, (self,)+args, kw)
556 self.varlist = kw.get('varlist', [])
558 def function_name(self):
560 return self.execfunction.__name__
561 except AttributeError:
563 return self.execfunction.__class__.__name__
564 except AttributeError:
565 return "unknown_python_function"
567 def strfunction(self, target, source, env):
570 return '"' + str(s) + '"'
571 return '[' + string.join(map(quote, a), ", ") + ']'
573 strfunc = self.execfunction.strfunction
574 except AttributeError:
579 if callable(strfunc):
580 return strfunc(target, source, env)
581 name = self.function_name()
584 return "%s(%s, %s)" % (name, tstr, sstr)
587 name = self.function_name()
588 if name == 'ActionCaller':
589 return str(self.execfunction)
590 return "%s(target, source, env)" % name
592 def execute(self, target, source, env):
593 rsources = map(rfile, source)
595 result = self.execfunction(target=target, source=rsources, env=env)
596 except EnvironmentError, e:
597 # If an IOError/OSError happens, raise a BuildError.
598 raise SCons.Errors.BuildError(node=target, errstr=e.strerror)
601 def get_contents(self, target, source, env):
602 """Return the signature contents of this callable action.
604 By providing direct access to the code object of the
605 function, Python makes this extremely easy. Hooray!
608 # "self.execfunction" is a function.
609 contents = str(self.execfunction.func_code.co_code)
610 except AttributeError:
611 # "self.execfunction" is a callable object.
613 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
614 except AttributeError:
616 # See if execfunction will do the heavy lifting for us.
617 gc = self.execfunction.get_contents
618 except AttributeError:
619 # This is weird, just do the best we can.
620 contents = str(self.execfunction)
622 contents = gc(target, source, env)
623 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
626 class ListAction(ActionBase):
627 """Class for lists of other actions."""
628 def __init__(self, list):
629 if __debug__: logInstanceCreation(self, 'Action.ListAction')
630 def list_of_actions(x):
631 if isinstance(x, ActionBase):
634 self.list = map(list_of_actions, list)
636 def genstring(self, target, source, env):
637 return string.join(map(lambda a, t=target, s=source, e=env:
638 a.genstring(t, s, e),
643 return string.join(map(str, self.list), '\n')
645 def presub_lines(self, env):
646 return SCons.Util.flatten(map(lambda a, env=env:
650 def get_contents(self, target, source, env):
651 """Return the signature contents of this action list.
653 Simple concatenation of the signatures of the elements.
655 return string.join(map(lambda x, t=target, s=source, e=env:
656 x.get_contents(t, s, e),
660 def __call__(self, target, source, env, errfunc=None, presub=_null,
661 show=_null, execute=_null, chdir=_null):
662 for act in self.list:
663 stat = act(target, source, env, errfunc, presub,
664 show, execute, chdir)
670 """A class for delaying calling an Action function with specific
671 (positional and keyword) arguments until the Action is actually
674 This class looks to the rest of the world like a normal Action object,
675 but what it's really doing is hanging on to the arguments until we
676 have a target, source and env to use for the expansion.
678 def __init__(self, parent, args, kw):
682 def get_contents(self, target, source, env):
683 actfunc = self.parent.actfunc
685 # "self.actfunc" is a function.
686 contents = str(actfunc.func_code.co_code)
687 except AttributeError:
688 # "self.actfunc" is a callable object.
690 contents = str(actfunc.__call__.im_func.func_code.co_code)
691 except AttributeError:
692 # No __call__() method, so it might be a builtin
693 # or something like that. Do the best we can.
694 contents = str(actfunc)
696 def subst(self, s, target, source, env):
697 # Special-case hack: Let a custom function wrapped in an
698 # ActionCaller get at the environment through which the action
699 # was called by using this hard-coded value as a special return.
703 return env.subst(s, 0, target, source)
704 def subst_args(self, target, source, env):
705 return map(lambda x, self=self, t=target, s=source, e=env:
706 self.subst(x, t, s, e),
708 def subst_kw(self, target, source, env):
710 for key in self.kw.keys():
711 kw[key] = self.subst(self.kw[key], target, source, env)
713 def __call__(self, target, source, env):
714 args = self.subst_args(target, source, env)
715 kw = self.subst_kw(target, source, env)
716 return apply(self.parent.actfunc, args, kw)
717 def strfunction(self, target, source, env):
718 args = self.subst_args(target, source, env)
719 kw = self.subst_kw(target, source, env)
720 return apply(self.parent.strfunc, args, kw)
722 return apply(self.parent.strfunc, self.args, self.kw)
725 """A factory class that will wrap up an arbitrary function
726 as an SCons-executable Action object.
728 The real heavy lifting here is done by the ActionCaller class.
729 We just collect the (positional and keyword) arguments that we're
730 called with and give them to the ActionCaller object we create,
731 so it can hang onto them until it needs them.
733 def __init__(self, actfunc, strfunc):
734 self.actfunc = actfunc
735 self.strfunc = strfunc
736 def __call__(self, *args, **kw):
737 ac = ActionCaller(self, args, kw)
738 action = Action(ac, strfunction=ac.strfunction)