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
108 import SCons.Executor
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
274 exitstatfunc = default_exitstatfunc
275 self.exitstatfunc = exitstatfunc
277 def print_cmd_line(self, s, target, source, env):
278 sys.stdout.write(s + "\n")
280 def __call__(self, target, source, env,
286 if not SCons.Util.is_List(target):
288 if not SCons.Util.is_List(source):
290 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
294 presub = print_actions_presub
295 if show is _null: show = print_actions
296 if execute is _null: execute = execute_actions
297 if chdir is _null: chdir = self.chdir
300 save_cwd = os.getcwd()
302 chdir = str(chdir.abspath)
303 except AttributeError:
304 if not SCons.Util.is_String(chdir):
305 chdir = str(target[0].dir)
307 t = string.join(map(str, target), ' and ')
308 l = string.join(self.presub_lines(env), '\n ')
309 out = "Building %s with action:\n %s\n" % (t, l)
310 sys.stdout.write(out)
312 if show and self.strfunction:
313 s = self.strfunction(target, source, env)
316 s = ('os.chdir(%s)\n' % repr(chdir)) + s
319 except AttributeError:
320 print_func = self.print_cmd_line
322 print_func = get('PRINT_CMD_LINE_FUNC')
324 print_func = self.print_cmd_line
325 print_func(s, target, source, env)
331 stat = self.execute(target, source, env)
332 stat = exitstatfunc(stat)
337 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
341 def _string_from_cmd_list(cmd_list):
342 """Takes a list of command line arguments and returns a pretty
343 representation for printing."""
345 for arg in map(str, cmd_list):
346 if ' ' in arg or '\t' in arg:
347 arg = '"' + arg + '"'
349 return string.join(cl)
351 class CommandAction(_ActionAction):
352 """Class for command-execution actions."""
353 def __init__(self, cmd, cmdstr=None, *args, **kw):
354 # Cmd can actually be a list or a single item; if it's a
355 # single item it should be the command string to execute; if a
356 # list then it should be the words of the command string to
357 # execute. Only a single command should be executed by this
358 # object; lists of commands should be handled by embedding
359 # these objects in a ListAction object (which the Action()
360 # factory above does). cmd will be passed to
361 # Environment.subst_list() for substituting environment
363 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
365 if not cmdstr is None:
367 args = (cmdstr,)+args
368 elif not SCons.Util.is_String(cmdstr):
369 raise SCons.Errors.UserError(\
370 'Invalid command display variable type. ' \
371 'You must either pass a string or a callback which ' \
372 'accepts (target, source, env) as parameters.')
374 apply(_ActionAction.__init__, (self,)+args, kw)
375 if SCons.Util.is_List(cmd):
376 if filter(SCons.Util.is_List, cmd):
377 raise TypeError, "CommandAction should be given only " \
383 if SCons.Util.is_List(self.cmd_list):
384 return string.join(map(str, self.cmd_list), ' ')
385 return str(self.cmd_list)
387 def process(self, target, source, env):
388 result = env.subst_list(self.cmd_list, 0, target, source)
392 try: c = result[0][0][0]
393 except IndexError: c = None
394 if c == '@': silent = 1
395 elif c == '-': ignore = 1
397 result[0][0] = result[0][0][1:]
400 result[0] = result[0][1:]
403 return result, ignore, silent
405 def strfunction(self, target, source, env):
406 if not self.cmdstr is None:
407 from SCons.Subst import SUBST_RAW
408 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
411 cmd_list, ignore, silent = self.process(target, source, env)
414 return _string_from_cmd_list(cmd_list[0])
416 def execute(self, target, source, env):
417 """Execute a command action.
419 This will handle lists of commands as well as individual commands,
420 because construction variable substitution may turn a single
421 "command" into a list. This means that this class can actually
422 handle lists of commands, even though that's not how we use it
425 from SCons.Subst import escape_list
427 flatten = SCons.Util.flatten
428 is_String = SCons.Util.is_String
429 is_List = SCons.Util.is_List
434 raise SCons.Errors.UserError('Missing SHELL construction variable.')
439 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
442 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
444 escape = env.get('ESCAPE', lambda x: x)
451 import SCons.Environment
452 default_ENV = SCons.Environment.Environment()['ENV']
455 # Ensure that the ENV values are all strings:
456 for key, value in ENV.items():
457 if not is_String(value):
459 # If the value is a list, then we assume it is a
460 # path list, because that's a pretty common list-like
461 # value to stick in an environment variable:
462 value = flatten(value)
463 ENV[key] = string.join(map(str, value), os.pathsep)
465 # If it isn't a string or a list, then we just coerce
466 # it to a string, which is the proper way to handle
467 # Dir and File instances and will produce something
468 # reasonable for just about everything else:
469 ENV[key] = str(value)
471 cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
473 # Use len() to filter out any "command" that's zero-length.
474 for cmd_line in filter(len, cmd_list):
475 # Escape the command line for the interpreter we are using.
476 cmd_line = escape_list(cmd_line, escape)
477 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
478 if not ignore and result:
482 def get_contents(self, target, source, env):
483 """Return the signature contents of this action's command line.
485 This strips $(-$) and everything in between the string,
486 since those parts don't affect signatures.
488 from SCons.Subst import SUBST_SIG
490 if SCons.Util.is_List(cmd):
491 cmd = string.join(map(str, cmd))
494 return env.subst_target_source(cmd, SUBST_SIG, target, source)
496 class CommandGeneratorAction(ActionBase):
497 """Class for command-generator actions."""
498 def __init__(self, generator, *args, **kw):
499 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
500 self.generator = generator
504 def _generate(self, target, source, env, for_signature):
505 # ensure that target is a list, to make it easier to write
506 # generator functions:
507 if not SCons.Util.is_List(target):
510 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
511 gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw)
513 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
518 env = self.presub_env or {}
519 except AttributeError:
521 act = self._generate([], [], env, 1)
524 def genstring(self, target, source, env):
525 return self._generate(target, source, env, 1).genstring(target, source, env)
527 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
528 show=_null, execute=_null, chdir=_null):
529 act = self._generate(target, source, env, 0)
530 return act(target, source, env, exitstatfunc, presub,
531 show, execute, chdir)
533 def get_contents(self, target, source, env):
534 """Return the signature contents of this action's command line.
536 This strips $(-$) and everything in between the string,
537 since those parts don't affect signatures.
539 return self._generate(target, source, env, 1).get_contents(target, source, env)
543 # A LazyAction is a kind of hybrid generator and command action for
544 # strings of the form "$VAR". These strings normally expand to other
545 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
546 # want to be able to replace them with functions in the construction
547 # environment. Consequently, we want lazy evaluation and creation of
548 # an Action in the case of the function, but that's overkill in the more
549 # normal case of expansion to other strings.
551 # So we do this with a subclass that's both a generator *and*
552 # a command action. The overridden methods all do a quick check
553 # of the construction variable, and if it's a string we just call
554 # the corresponding CommandAction method to do the heavy lifting.
555 # If not, then we call the same-named CommandGeneratorAction method.
556 # The CommandGeneratorAction methods work by using the overridden
557 # _generate() method, that is, our own way of handling "generation" of
558 # an action based on what's in the construction variable.
560 class LazyAction(CommandGeneratorAction, CommandAction):
562 def __init__(self, var, *args, **kw):
563 if __debug__: logInstanceCreation(self, 'Action.LazyAction')
564 apply(CommandAction.__init__, (self, '$'+var)+args, kw)
565 self.var = SCons.Util.to_String(var)
569 def get_parent_class(self, env):
570 c = env.get(self.var)
571 if SCons.Util.is_String(c) and not '\n' in c:
573 return CommandGeneratorAction
575 def _generate_cache(self, env):
576 c = env.get(self.var, '')
577 gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw)
579 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
582 def _generate(self, target, source, env, for_signature):
583 return self._generate_cache(env)
585 def __call__(self, target, source, env, *args, **kw):
586 args = (self, target, source, env) + args
587 c = self.get_parent_class(env)
588 return apply(c.__call__, args, kw)
590 def get_contents(self, target, source, env):
591 c = self.get_parent_class(env)
592 return c.get_contents(self, target, source, env)
596 class FunctionAction(_ActionAction):
597 """Class for Python function actions."""
599 def __init__(self, execfunction, cmdstr=_null, *args, **kw):
600 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
602 if not cmdstr is _null:
604 args = (cmdstr,)+args
605 elif not (cmdstr is None or SCons.Util.is_String(cmdstr)):
606 raise SCons.Errors.UserError(\
607 'Invalid function display variable type. ' \
608 'You must either pass a string or a callback which ' \
609 'accepts (target, source, env) as parameters.')
611 self.execfunction = execfunction
612 apply(_ActionAction.__init__, (self,)+args, kw)
613 self.varlist = kw.get('varlist', [])
616 def function_name(self):
618 return self.execfunction.__name__
619 except AttributeError:
621 return self.execfunction.__class__.__name__
622 except AttributeError:
623 return "unknown_python_function"
625 def strfunction(self, target, source, env):
626 if self.cmdstr is None:
628 if not self.cmdstr is _null:
629 from SCons.Subst import SUBST_RAW
630 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
635 return '"' + str(s) + '"'
636 return '[' + string.join(map(quote, a), ", ") + ']'
638 strfunc = self.execfunction.strfunction
639 except AttributeError:
644 if callable(strfunc):
645 return strfunc(target, source, env)
646 name = self.function_name()
649 return "%s(%s, %s)" % (name, tstr, sstr)
652 name = self.function_name()
653 if name == 'ActionCaller':
654 return str(self.execfunction)
655 return "%s(target, source, env)" % name
657 def execute(self, target, source, env):
658 rsources = map(rfile, source)
660 result = self.execfunction(target=target, source=rsources, env=env)
661 except EnvironmentError, e:
662 # If an IOError/OSError happens, raise a BuildError.
663 # Report the name of the file or directory that caused the
664 # error, which might be different from the target being built
665 # (for example, failure to create the directory in which the
666 # target file will appear).
667 try: filename = e.filename
668 except AttributeError: filename = None
669 raise SCons.Errors.BuildError(node=target,
674 def get_contents(self, target, source, env):
675 """Return the signature contents of this callable action.
677 By providing direct access to the code object of the
678 function, Python makes this extremely easy. Hooray!
680 Unfortunately, older versions of Python include line
681 number indications in the compiled byte code. Boo!
682 So we remove the line number byte codes to prevent
683 recompilations from moving a Python function.
685 execfunction = self.execfunction
687 # Test if execfunction is a function.
688 code = execfunction.func_code.co_code
689 except AttributeError:
691 # Test if execfunction is a method.
692 code = execfunction.im_func.func_code.co_code
693 except AttributeError:
695 # Test if execfunction is a callable object.
696 code = execfunction.__call__.im_func.func_code.co_code
697 except AttributeError:
699 # See if execfunction will do the heavy lifting for us.
700 gc = self.execfunction.get_contents
701 except AttributeError:
702 # This is weird, just do the best we can.
703 contents = str(self.execfunction)
705 contents = gc(target, source, env)
712 contents = remove_set_lineno_codes(contents)
713 return contents + env.subst(string.join(map(lambda v: '${'+v+'}',
716 class ListAction(ActionBase):
717 """Class for lists of other actions."""
718 def __init__(self, list):
719 if __debug__: logInstanceCreation(self, 'Action.ListAction')
720 def list_of_actions(x):
721 if isinstance(x, ActionBase):
724 self.list = map(list_of_actions, list)
726 def genstring(self, target, source, env):
727 return string.join(map(lambda a, t=target, s=source, e=env:
728 a.genstring(t, s, e),
733 return string.join(map(str, self.list), '\n')
735 def presub_lines(self, env):
736 return SCons.Util.flatten(map(lambda a, env=env:
740 def get_contents(self, target, source, env):
741 """Return the signature contents of this action list.
743 Simple concatenation of the signatures of the elements.
745 return string.join(map(lambda x, t=target, s=source, e=env:
746 x.get_contents(t, s, e),
750 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
751 show=_null, execute=_null, chdir=_null):
752 for act in self.list:
753 stat = act(target, source, env, exitstatfunc, presub,
754 show, execute, chdir)
760 """A class for delaying calling an Action function with specific
761 (positional and keyword) arguments until the Action is actually
764 This class looks to the rest of the world like a normal Action object,
765 but what it's really doing is hanging on to the arguments until we
766 have a target, source and env to use for the expansion.
768 def __init__(self, parent, args, kw):
772 def get_contents(self, target, source, env):
773 actfunc = self.parent.actfunc
775 # "self.actfunc" is a function.
776 contents = str(actfunc.func_code.co_code)
777 except AttributeError:
778 # "self.actfunc" is a callable object.
780 contents = str(actfunc.__call__.im_func.func_code.co_code)
781 except AttributeError:
782 # No __call__() method, so it might be a builtin
783 # or something like that. Do the best we can.
784 contents = str(actfunc)
785 contents = remove_set_lineno_codes(contents)
787 def subst(self, s, target, source, env):
788 # Special-case hack: Let a custom function wrapped in an
789 # ActionCaller get at the environment through which the action
790 # was called by using this hard-coded value as a special return.
794 return env.subst(s, 0, target, source)
795 def subst_args(self, target, source, env):
796 return map(lambda x, self=self, t=target, s=source, e=env:
797 self.subst(x, t, s, e),
799 def subst_kw(self, target, source, env):
801 for key in self.kw.keys():
802 kw[key] = self.subst(self.kw[key], target, source, env)
804 def __call__(self, target, source, env):
805 args = self.subst_args(target, source, env)
806 kw = self.subst_kw(target, source, env)
807 return apply(self.parent.actfunc, args, kw)
808 def strfunction(self, target, source, env):
809 args = self.subst_args(target, source, env)
810 kw = self.subst_kw(target, source, env)
811 return apply(self.parent.strfunc, args, kw)
813 return apply(self.parent.strfunc, self.args, self.kw)
816 """A factory class that will wrap up an arbitrary function
817 as an SCons-executable Action object.
819 The real heavy lifting here is done by the ActionCaller class.
820 We just collect the (positional and keyword) arguments that we're
821 called with and give them to the ActionCaller object we create,
822 so it can hang onto them until it needs them.
824 def __init__(self, actfunc, strfunc):
825 self.actfunc = actfunc
826 self.strfunc = strfunc
827 def __call__(self, *args, **kw):
828 ac = ActionCaller(self, args, kw)
829 action = Action(ac, strfunction=ac.strfunction)