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__"
107 from SCons.Debug import logInstanceCreation
118 print_actions_presub = 0
125 except AttributeError:
128 def default_exitstatfunc(s):
132 SET_LINENO = dis.SET_LINENO
133 HAVE_ARGUMENT = dis.HAVE_ARGUMENT
134 except AttributeError:
135 remove_set_lineno_codes = lambda x: x
137 def remove_set_lineno_codes(code):
144 if op >= HAVE_ARGUMENT:
146 result.append(code[i:i+3])
151 return string.join(result, '')
153 def _actionAppend(act1, act2):
154 # This function knows how to slap two actions together.
155 # Mainly, it handles ListActions by concatenating into
156 # a single ListAction.
159 if a1 is None or a2 is None:
160 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
161 if isinstance(a1, ListAction):
162 if isinstance(a2, ListAction):
163 return ListAction(a1.list + a2.list)
165 return ListAction(a1.list + [ a2 ])
167 if isinstance(a2, ListAction):
168 return ListAction([ a1 ] + a2.list)
170 return ListAction([ a1, a2 ])
172 def _do_create_action(act, *args, **kw):
173 """This is the actual "implementation" for the
174 Action factory method, below. This handles the
175 fact that passing lists to Action() itself has
176 different semantics than passing lists as elements
179 The former will create a ListAction, the latter
180 will create a CommandAction by converting the inner
181 list elements to strings."""
183 if isinstance(act, ActionBase):
185 if SCons.Util.is_List(act):
186 return apply(CommandAction, (act,)+args, kw)
189 gen = kw['generator']
194 action_type = CommandGeneratorAction
196 action_type = FunctionAction
197 return apply(action_type, (act,)+args, kw)
198 if SCons.Util.is_String(act):
199 var=SCons.Util.get_environment_var(act)
201 # This looks like a string that is purely an Environment
202 # variable reference, like "$FOO" or "${FOO}". We do
203 # something special here...we lazily evaluate the contents
204 # of that Environment variable, so a user could put something
205 # like a function or a CommandGenerator in that variable
206 # instead of a string.
207 return apply(LazyAction, (var,)+args, kw)
208 commands = string.split(str(act), '\n')
209 if len(commands) == 1:
210 return apply(CommandAction, (commands[0],)+args, kw)
212 listCmdActions = map(lambda x, args=args, kw=kw:
213 apply(CommandAction, (x,)+args, kw),
215 return ListAction(listCmdActions)
218 def Action(act, *args, **kw):
219 """A factory for action objects."""
220 if SCons.Util.is_List(act):
221 acts = map(lambda a, args=args, kw=kw:
222 apply(_do_create_action, (a,)+args, kw),
224 acts = filter(None, acts)
228 return ListAction(acts)
230 return apply(_do_create_action, (act,)+args, kw)
233 """Base class for all types of action objects that can be held by
234 other objects (Builders, Executors, etc.) This provides the
235 common methods for manipulating and combining those actions."""
237 def __cmp__(self, other):
238 return cmp(self.__dict__, other)
240 def genstring(self, target, source, env):
243 def __add__(self, other):
244 return _actionAppend(self, other)
246 def __radd__(self, other):
247 return _actionAppend(other, self)
249 def presub_lines(self, env):
250 # CommandGeneratorAction needs a real environment
251 # in order to return the proper string here, since
252 # it may call LazyAction, which looks up a key
253 # in that env. So we temporarily remember the env here,
254 # and CommandGeneratorAction will use this env
255 # when it calls its _generate method.
256 self.presub_env = env
257 lines = string.split(str(self), '\n')
258 self.presub_env = None # don't need this any more
261 def get_executor(self, env, overrides, tlist, slist, executor_kw):
262 """Return the Executor for this Action."""
263 return SCons.Executor.Executor(self, env, overrides,
264 tlist, slist, executor_kw)
266 class _ActionAction(ActionBase):
267 """Base class for actions that create output objects."""
268 def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
269 if not strfunction is _null:
270 self.strfunction = strfunction
272 presub = print_actions_presub
276 exitstatfunc = default_exitstatfunc
277 self.exitstatfunc = exitstatfunc
279 def print_cmd_line(self, s, target, source, env):
280 sys.stdout.write(s + "\n")
282 def __call__(self, target, source, env,
288 if not SCons.Util.is_List(target):
290 if not SCons.Util.is_List(source):
292 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
293 if presub is _null: presub = self.presub
294 if show is _null: show = print_actions
295 if execute is _null: execute = execute_actions
296 if chdir is _null: chdir = self.chdir
299 save_cwd = os.getcwd()
301 chdir = str(chdir.abspath)
302 except AttributeError:
303 if not SCons.Util.is_String(chdir):
304 chdir = str(target[0].dir)
306 t = string.join(map(str, target), ' and ')
307 l = string.join(self.presub_lines(env), '\n ')
308 out = "Building %s with action:\n %s\n" % (t, l)
309 sys.stdout.write(out)
311 if show and self.strfunction:
312 s = self.strfunction(target, source, env)
315 s = ('os.chdir(%s)\n' % repr(chdir)) + s
318 except AttributeError:
319 print_func = self.print_cmd_line
321 print_func = get('PRINT_CMD_LINE_FUNC')
323 print_func = self.print_cmd_line
324 print_func(s, target, source, env)
330 stat = self.execute(target, source, env)
331 stat = exitstatfunc(stat)
336 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
340 def _string_from_cmd_list(cmd_list):
341 """Takes a list of command line arguments and returns a pretty
342 representation for printing."""
344 for arg in map(str, cmd_list):
345 if ' ' in arg or '\t' in arg:
346 arg = '"' + arg + '"'
348 return string.join(cl)
350 class CommandAction(_ActionAction):
351 """Class for command-execution actions."""
352 def __init__(self, cmd, cmdstr=None, *args, **kw):
353 # Cmd can actually be a list or a single item; if it's a
354 # single item it should be the command string to execute; if a
355 # list then it should be the words of the command string to
356 # execute. Only a single command should be executed by this
357 # object; lists of commands should be handled by embedding
358 # these objects in a ListAction object (which the Action()
359 # factory above does). cmd will be passed to
360 # Environment.subst_list() for substituting environment
362 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
364 if not cmdstr is None:
366 args = (cmdstr,)+args
367 elif not SCons.Util.is_String(cmdstr):
368 raise SCons.Errors.UserError(\
369 'Invalid command display variable type. ' \
370 'You must either pass a string or a callback which ' \
371 'accepts (target, source, env) as parameters.')
373 apply(_ActionAction.__init__, (self,)+args, kw)
374 if SCons.Util.is_List(cmd):
375 if filter(SCons.Util.is_List, cmd):
376 raise TypeError, "CommandAction should be given only " \
382 if SCons.Util.is_List(self.cmd_list):
383 return string.join(map(str, self.cmd_list), ' ')
384 return str(self.cmd_list)
386 def process(self, target, source, env):
387 result = env.subst_list(self.cmd_list, 0, target, source)
391 try: c = result[0][0][0]
392 except IndexError: c = None
393 if c == '@': silent = 1
394 elif c == '-': ignore = 1
396 result[0][0] = result[0][0][1:]
399 result[0] = result[0][1:]
402 return result, ignore, silent
404 def strfunction(self, target, source, env):
405 if not self.cmdstr is None:
406 c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source)
409 cmd_list, ignore, silent = self.process(target, source, env)
412 return _string_from_cmd_list(cmd_list[0])
414 def execute(self, target, source, env):
415 """Execute a command action.
417 This will handle lists of commands as well as individual commands,
418 because construction variable substitution may turn a single
419 "command" into a list. This means that this class can actually
420 handle lists of commands, even though that's not how we use it
423 from SCons.Subst import escape_list
424 from SCons.Util import is_String, is_List, flatten
429 raise SCons.Errors.UserError('Missing SHELL construction variable.')
434 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
436 escape = env.get('ESCAPE', lambda x: x)
443 import SCons.Environment
444 default_ENV = SCons.Environment.Environment()['ENV']
447 # Ensure that the ENV values are all strings:
448 for key, value in ENV.items():
449 if not is_String(value):
451 # If the value is a list, then we assume it is a
452 # path list, because that's a pretty common list-like
453 # value to stick in an environment variable:
454 value = flatten(value)
455 ENV[key] = string.join(map(str, value), os.pathsep)
457 # If it isn't a string or a list, then we just coerce
458 # it to a string, which is the proper way to handle
459 # Dir and File instances and will produce something
460 # reasonable for just about everything else:
461 ENV[key] = str(value)
463 cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
465 # Use len() to filter out any "command" that's zero-length.
466 for cmd_line in filter(len, cmd_list):
467 # Escape the command line for the interpreter we are using.
468 cmd_line = escape_list(cmd_line, escape)
469 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
470 if not ignore and result:
474 def get_contents(self, target, source, env):
475 """Return the signature contents of this action's command line.
477 This strips $(-$) and everything in between the string,
478 since those parts don't affect signatures.
480 from SCons.Subst import SUBST_SIG
482 if SCons.Util.is_List(cmd):
483 cmd = string.join(map(str, cmd))
486 return env.subst_target_source(cmd, SUBST_SIG, target, source)
488 class CommandGeneratorAction(ActionBase):
489 """Class for command-generator actions."""
490 def __init__(self, generator, *args, **kw):
491 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
492 self.generator = generator
496 def _generate(self, target, source, env, for_signature):
497 # ensure that target is a list, to make it easier to write
498 # generator functions:
499 if not SCons.Util.is_List(target):
502 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
503 gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw)
505 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
510 env = self.presub_env or {}
511 except AttributeError:
513 act = self._generate([], [], env, 1)
516 def genstring(self, target, source, env):
517 return self._generate(target, source, env, 1).genstring(target, source, env)
519 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
520 show=_null, execute=_null, chdir=_null):
521 act = self._generate(target, source, env, 0)
522 return act(target, source, env, exitstatfunc, presub,
523 show, execute, chdir)
525 def get_contents(self, target, source, env):
526 """Return the signature contents of this action's command line.
528 This strips $(-$) and everything in between the string,
529 since those parts don't affect signatures.
531 return self._generate(target, source, env, 1).get_contents(target, source, env)
535 # A LazyAction is a kind of hybrid generator and command action for
536 # strings of the form "$VAR". These strings normally expand to other
537 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
538 # want to be able to replace them with functions in the construction
539 # environment. Consequently, we want lazy evaluation and creation of
540 # an Action in the case of the function, but that's overkill in the more
541 # normal case of expansion to other strings.
543 # So we do this with a subclass that's both a generator *and*
544 # a command action. The overridden methods all do a quick check
545 # of the construction variable, and if it's a string we just call
546 # the corresponding CommandAction method to do the heavy lifting.
547 # If not, then we call the same-named CommandGeneratorAction method.
548 # The CommandGeneratorAction methods work by using the overridden
549 # _generate() method, that is, our own way of handling "generation" of
550 # an action based on what's in the construction variable.
552 class LazyAction(CommandGeneratorAction, CommandAction):
554 def __init__(self, var, *args, **kw):
555 if __debug__: logInstanceCreation(self, 'Action.LazyAction')
556 apply(CommandAction.__init__, (self, '$'+var)+args, kw)
557 self.var = SCons.Util.to_String(var)
561 def get_parent_class(self, env):
562 c = env.get(self.var)
563 if SCons.Util.is_String(c) and not '\n' in c:
565 return CommandGeneratorAction
567 def _generate_cache(self, env):
568 c = env.get(self.var, '')
569 gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw)
571 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
574 def _generate(self, target, source, env, for_signature):
575 return self._generate_cache(env)
577 def __call__(self, target, source, env, *args, **kw):
578 args = (self, target, source, env) + args
579 c = self.get_parent_class(env)
580 return apply(c.__call__, args, kw)
582 def get_contents(self, target, source, env):
583 c = self.get_parent_class(env)
584 return c.get_contents(self, target, source, env)
588 class FunctionAction(_ActionAction):
589 """Class for Python function actions."""
591 def __init__(self, execfunction, cmdstr=_null, *args, **kw):
592 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
594 if not cmdstr is _null:
596 args = (cmdstr,)+args
597 elif not (cmdstr is None or SCons.Util.is_String(cmdstr)):
598 raise SCons.Errors.UserError(\
599 'Invalid function display variable type. ' \
600 'You must either pass a string or a callback which ' \
601 'accepts (target, source, env) as parameters.')
603 self.execfunction = execfunction
604 apply(_ActionAction.__init__, (self,)+args, kw)
605 self.varlist = kw.get('varlist', [])
608 def function_name(self):
610 return self.execfunction.__name__
611 except AttributeError:
613 return self.execfunction.__class__.__name__
614 except AttributeError:
615 return "unknown_python_function"
617 def strfunction(self, target, source, env):
618 if self.cmdstr is None:
620 if not self.cmdstr is _null:
621 c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source)
626 return '"' + str(s) + '"'
627 return '[' + string.join(map(quote, a), ", ") + ']'
629 strfunc = self.execfunction.strfunction
630 except AttributeError:
635 if callable(strfunc):
636 return strfunc(target, source, env)
637 name = self.function_name()
640 return "%s(%s, %s)" % (name, tstr, sstr)
643 name = self.function_name()
644 if name == 'ActionCaller':
645 return str(self.execfunction)
646 return "%s(target, source, env)" % name
648 def execute(self, target, source, env):
649 rsources = map(rfile, source)
651 result = self.execfunction(target=target, source=rsources, env=env)
652 except EnvironmentError, e:
653 # If an IOError/OSError happens, raise a BuildError.
654 # Report the name of the file or directory that caused the
655 # error, which might be different from the target being built
656 # (for example, failure to create the directory in which the
657 # target file will appear).
658 try: filename = e.filename
659 except AttributeError: filename = None
660 raise SCons.Errors.BuildError(node=target,
665 def get_contents(self, target, source, env):
666 """Return the signature contents of this callable action.
668 By providing direct access to the code object of the
669 function, Python makes this extremely easy. Hooray!
671 Unfortunately, older versions of Python include line
672 number indications in the compiled byte code. Boo!
673 So we remove the line number byte codes to prevent
674 recompilations from moving a Python function.
676 execfunction = self.execfunction
678 # Test if execfunction is a function.
679 code = execfunction.func_code.co_code
680 except AttributeError:
682 # Test if execfunction is a method.
683 code = execfunction.im_func.func_code.co_code
684 except AttributeError:
686 # Test if execfunction is a callable object.
687 code = execfunction.__call__.im_func.func_code.co_code
688 except AttributeError:
690 # See if execfunction will do the heavy lifting for us.
691 gc = self.execfunction.get_contents
692 except AttributeError:
693 # This is weird, just do the best we can.
694 contents = str(self.execfunction)
696 contents = gc(target, source, env)
703 contents = remove_set_lineno_codes(contents)
704 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
707 class ListAction(ActionBase):
708 """Class for lists of other actions."""
709 def __init__(self, list):
710 if __debug__: logInstanceCreation(self, 'Action.ListAction')
711 def list_of_actions(x):
712 if isinstance(x, ActionBase):
715 self.list = map(list_of_actions, list)
717 def genstring(self, target, source, env):
718 return string.join(map(lambda a, t=target, s=source, e=env:
719 a.genstring(t, s, e),
724 return string.join(map(str, self.list), '\n')
726 def presub_lines(self, env):
727 return SCons.Util.flatten(map(lambda a, env=env:
731 def get_contents(self, target, source, env):
732 """Return the signature contents of this action list.
734 Simple concatenation of the signatures of the elements.
736 return string.join(map(lambda x, t=target, s=source, e=env:
737 x.get_contents(t, s, e),
741 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
742 show=_null, execute=_null, chdir=_null):
743 for act in self.list:
744 stat = act(target, source, env, exitstatfunc, presub,
745 show, execute, chdir)
751 """A class for delaying calling an Action function with specific
752 (positional and keyword) arguments until the Action is actually
755 This class looks to the rest of the world like a normal Action object,
756 but what it's really doing is hanging on to the arguments until we
757 have a target, source and env to use for the expansion.
759 def __init__(self, parent, args, kw):
763 def get_contents(self, target, source, env):
764 actfunc = self.parent.actfunc
766 # "self.actfunc" is a function.
767 contents = str(actfunc.func_code.co_code)
768 except AttributeError:
769 # "self.actfunc" is a callable object.
771 contents = str(actfunc.__call__.im_func.func_code.co_code)
772 except AttributeError:
773 # No __call__() method, so it might be a builtin
774 # or something like that. Do the best we can.
775 contents = str(actfunc)
776 contents = remove_set_lineno_codes(contents)
778 def subst(self, s, target, source, env):
779 # Special-case hack: Let a custom function wrapped in an
780 # ActionCaller get at the environment through which the action
781 # was called by using this hard-coded value as a special return.
785 return env.subst(s, 0, target, source)
786 def subst_args(self, target, source, env):
787 return map(lambda x, self=self, t=target, s=source, e=env:
788 self.subst(x, t, s, e),
790 def subst_kw(self, target, source, env):
792 for key in self.kw.keys():
793 kw[key] = self.subst(self.kw[key], target, source, env)
795 def __call__(self, target, source, env):
796 args = self.subst_args(target, source, env)
797 kw = self.subst_kw(target, source, env)
798 return apply(self.parent.actfunc, args, kw)
799 def strfunction(self, target, source, env):
800 args = self.subst_args(target, source, env)
801 kw = self.subst_kw(target, source, env)
802 return apply(self.parent.strfunc, args, kw)
804 return apply(self.parent.strfunc, self.args, self.kw)
807 """A factory class that will wrap up an arbitrary function
808 as an SCons-executable Action object.
810 The real heavy lifting here is done by the ActionCaller class.
811 We just collect the (positional and keyword) arguments that we're
812 called with and give them to the ActionCaller object we create,
813 so it can hang onto them until it needs them.
815 def __init__(self, actfunc, strfunc):
816 self.actfunc = actfunc
817 self.strfunc = strfunc
818 def __call__(self, *args, **kw):
819 ac = ActionCaller(self, args, kw)
820 action = Action(ac, strfunction=ac.strfunction)