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, *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 " \
345 if SCons.Util.is_List(self.cmd_list):
346 return string.join(map(str, self.cmd_list), ' ')
347 return str(self.cmd_list)
349 def strfunction(self, target, source, env):
350 cmd_list = env.subst_list(self.cmd_list, 0, target, source)
351 return _string_from_cmd_list(cmd_list[0])
353 def execute(self, target, source, env):
354 """Execute a command action.
356 This will handle lists of commands as well as individual commands,
357 because construction variable substitution may turn a single
358 "command" into a list. This means that this class can actually
359 handle lists of commands, even though that's not how we use it
362 from SCons.Util import is_String, is_List, flatten, escape_list
367 raise SCons.Errors.UserError('Missing SHELL construction variable.')
372 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
374 escape = env.get('ESCAPE', lambda x: x)
381 import SCons.Environment
382 default_ENV = SCons.Environment.Environment()['ENV']
385 # Ensure that the ENV values are all strings:
386 for key, value in ENV.items():
387 if not is_String(value):
389 # If the value is a list, then we assume it is a
390 # path list, because that's a pretty common list-like
391 # value to stick in an environment variable:
392 value = flatten(value)
393 ENV[key] = string.join(map(str, value), os.pathsep)
395 # If it isn't a string or a list, then we just coerce
396 # it to a string, which is the proper way to handle
397 # Dir and File instances and will produce something
398 # reasonable for just about everything else:
399 ENV[key] = str(value)
401 cmd_list = env.subst_list(self.cmd_list, 0, target,
404 # Use len() to filter out any "command" that's zero-length.
405 for cmd_line in filter(len, cmd_list):
406 # Escape the command line for the interpreter we are using.
407 cmd_line = escape_list(cmd_line, escape)
408 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
413 def get_contents(self, target, source, env, dict=None):
414 """Return the signature contents of this action's command line.
416 This strips $(-$) and everything in between the string,
417 since those parts don't affect signatures.
420 if SCons.Util.is_List(cmd):
421 cmd = string.join(map(str, cmd))
424 return env.subst_target_source(cmd, SCons.Util.SUBST_SIG, target, source, dict)
426 class CommandGeneratorAction(ActionBase):
427 """Class for command-generator actions."""
428 def __init__(self, generator, *args, **kw):
429 if __debug__: logInstanceCreation(self)
430 self.generator = generator
433 def _generate(self, target, source, env, for_signature):
434 # ensure that target is a list, to make it easier to write
435 # generator functions:
436 if not SCons.Util.is_List(target):
439 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
440 gen_cmd = apply(Action, (ret,), self.gen_kw)
442 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
447 env = self.presub_env or {}
448 except AttributeError:
450 act = self._generate([], [], env, 1)
453 def genstring(self, target, source, env):
454 return self._generate(target, source, env, 1).genstring(target, source, env)
456 def __call__(self, target, source, env, errfunc=None, presub=_null,
457 show=_null, execute=_null, chdir=_null):
458 act = self._generate(target, source, env, 0)
459 return act(target, source, env, errfunc, presub,
460 show, execute, chdir)
462 def get_contents(self, target, source, env, dict=None):
463 """Return the signature contents of this action's command line.
465 This strips $(-$) and everything in between the string,
466 since those parts don't affect signatures.
468 return self._generate(target, source, env, 1).get_contents(target, source, env, dict=None)
470 # Ooh, polymorphism -- pretty scary, eh, kids?
472 # A LazyCmdAction is a kind of hybrid generator and command action for
473 # strings of the form "$VAR". These strings normally expand to other
474 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
475 # want to be able to replace them with functions in the construction
476 # environment. Consequently, we want lazy evaluation and creation of
477 # an Action in the case of the function, but that's overkill in the more
478 # normal case of expansion to other strings.
480 # So we do this with a subclass that's both a generator *and*
481 # a command action. The overridden methods all do a quick check
482 # of the construction variable, and if it's a string we just call
483 # the corresponding CommandAction method to do the heavy lifting.
484 # If not, then we call the same-named CommandGeneratorAction method.
485 # The CommandGeneratorAction methods work by using the overridden
486 # _generate() method, uses our own way of handling "generation" of an
487 # action based on what's in the construction variable.
489 class LazyAction(CommandGeneratorAction, CommandAction):
490 def __init__(self, var, *args, **kw):
491 if __debug__: logInstanceCreation(self)
492 apply(CommandAction.__init__, (self, '$'+var)+args, kw)
493 self.var = SCons.Util.to_String(var)
496 def get_parent_class(self, env):
497 c = env.get(self.var)
498 if SCons.Util.is_String(c) and not '\n' in c:
500 return CommandGeneratorAction
502 def _generate(self, target, source, env, for_signature):
503 c = env.get(self.var, '')
504 gen_cmd = apply(Action, (c,), self.gen_kw)
506 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
509 def __call__(self, target, source, env, *args, **kw):
510 args = (self, target, source, env) + args
511 c = self.get_parent_class(env)
512 return apply(c.__call__, args, kw)
514 def get_contents(self, target, source, env, dict=None):
515 c = self.get_parent_class(env)
516 return c.get_contents(self, target, source, env, dict)
518 class FunctionAction(_ActionAction):
519 """Class for Python function actions."""
521 def __init__(self, execfunction, *args, **kw):
522 if __debug__: logInstanceCreation(self)
523 self.execfunction = execfunction
524 apply(_ActionAction.__init__, (self,)+args, kw)
525 self.varlist = kw.get('varlist', [])
527 def function_name(self):
529 return self.execfunction.__name__
530 except AttributeError:
532 return self.execfunction.__class__.__name__
533 except AttributeError:
534 return "unknown_python_function"
536 def strfunction(self, target, source, env):
539 return '"' + str(s) + '"'
540 return '[' + string.join(map(quote, a), ", ") + ']'
542 strfunc = self.execfunction.strfunction
543 except AttributeError:
548 if callable(strfunc):
549 return strfunc(target, source, env)
550 name = self.function_name()
553 return "%s(%s, %s)" % (name, tstr, sstr)
556 return "%s(target, source, env)" % self.function_name()
558 def execute(self, target, source, env):
559 rsources = map(rfile, source)
561 result = self.execfunction(target=target, source=rsources, env=env)
562 except EnvironmentError, e:
563 # If an IOError/OSError happens, raise a BuildError.
564 raise SCons.Errors.BuildError(node=target, errstr=e.strerror)
567 def get_contents(self, target, source, env, dict=None):
568 """Return the signature contents of this callable action.
570 By providing direct access to the code object of the
571 function, Python makes this extremely easy. Hooray!
574 # "self.execfunction" is a function.
575 contents = str(self.execfunction.func_code.co_code)
576 except AttributeError:
577 # "self.execfunction" is a callable object.
579 contents = str(self.execfunction.__call__.im_func.func_code.co_code)
580 except AttributeError:
582 # See if execfunction will do the heavy lifting for us.
583 gc = self.execfunction.get_contents
584 except AttributeError:
585 # This is weird, just do the best we can.
586 contents = str(self.execfunction)
588 contents = gc(target, source, env, dict)
589 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
592 class ListAction(ActionBase):
593 """Class for lists of other actions."""
594 def __init__(self, list):
595 if __debug__: logInstanceCreation(self)
596 def list_of_actions(x):
597 if isinstance(x, ActionBase):
600 self.list = map(list_of_actions, list)
602 def genstring(self, target, source, env):
603 return string.join(map(lambda a, t=target, s=source, e=env:
604 a.genstring(t, s, e),
609 return string.join(map(str, self.list), '\n')
611 def presub_lines(self, env):
612 return SCons.Util.flatten(map(lambda a, env=env:
616 def get_contents(self, target, source, env, dict=None):
617 """Return the signature contents of this action list.
619 Simple concatenation of the signatures of the elements.
621 dict = SCons.Util.subst_dict(target, source)
622 return string.join(map(lambda x, t=target, s=source, e=env, d=dict:
623 x.get_contents(t, s, e, d),
627 def __call__(self, target, source, env, errfunc=None, presub=_null,
628 show=_null, execute=_null, chdir=_null):
629 for act in self.list:
630 stat = act(target, source, env, errfunc, presub,
631 show, execute, chdir)
637 """A class for delaying calling an Action function with specific
638 (positional and keyword) arguments until the Action is actually
641 This class looks to the rest of the world like a normal Action object,
642 but what it's really doing is hanging on to the arguments until we
643 have a target, source and env to use for the expansion.
645 def __init__(self, parent, args, kw):
649 def get_contents(self, target, source, env, dict=None):
650 actfunc = self.parent.actfunc
652 # "self.actfunc" is a function.
653 contents = str(actfunc.func_code.co_code)
654 except AttributeError:
655 # "self.actfunc" is a callable object.
657 contents = str(actfunc.__call__.im_func.func_code.co_code)
658 except AttributeError:
659 # No __call__() method, so it might be a builtin
660 # or something like that. Do the best we can.
661 contents = str(actfunc)
663 def subst_args(self, target, source, env):
664 return map(lambda x, e=env, t=target, s=source:
667 def subst_kw(self, target, source, env):
669 for key in self.kw.keys():
670 kw[key] = env.subst(self.kw[key], 0, target, source)
672 def __call__(self, target, source, env):
673 args = self.subst_args(target, source, env)
674 kw = self.subst_kw(target, source, env)
675 return apply(self.parent.actfunc, args, kw)
676 def strfunction(self, target, source, env):
677 args = self.subst_args(target, source, env)
678 kw = self.subst_kw(target, source, env)
679 return apply(self.parent.strfunc, args, kw)
682 """A factory class that will wrap up an arbitrary function
683 as an SCons-executable Action object.
685 The real heavy lifting here is done by the ActionCaller class.
686 We just collect the (positional and keyword) arguments that we're
687 called with and give them to the ActionCaller object we create,
688 so it can hang onto them until it needs them.
690 def __init__(self, actfunc, strfunc):
691 self.actfunc = actfunc
692 self.strfunc = strfunc
693 def __call__(self, *args, **kw):
694 ac = ActionCaller(self, args, kw)
695 action = Action(ac, strfunction=ac.strfunction)
696 # action will be a FunctionAction; if left to its own devices,
697 # a genstr or str of this action will just show
698 # "ActionCaller(target, source, env)". Override that with the
699 # description from strfunc. Note that the apply is evaluated
700 # right now; __str__ is set to a (lambda) function that just
701 # returns the stored result of the evaluation whenever called.
702 action.__str__ = lambda name=apply(self.strfunc, args, kw): name