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 plus the varlist. This is what gets MD5 checksummed to decide
35 if a target 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 Fetches the "contents" of a subclass for signature calculation.
62 The varlist is added to this to produce the Action's contents.
65 Returns a substituted string representation of the Action.
66 This is used by the _ActionAction.show() command to display the
67 command/function that will be executed to generate the target(s).
69 There is a related independent ActionCaller class that looks like a
70 regular Action, and which serves as a wrapper for arbitrary functions
71 that we want to let the user specify the arguments to now, but actually
72 execute later (when an out-of-date check determines that it's needed to
73 be executed, for example). Objects of this class are returned by an
74 ActionFactory class that provides a __call__() method as a convenient
75 way for wrapping up the functions.
81 # Permission is hereby granted, free of charge, to any person obtaining
82 # a copy of this software and associated documentation files (the
83 # "Software"), to deal in the Software without restriction, including
84 # without limitation the rights to use, copy, modify, merge, publish,
85 # distribute, sublicense, and/or sell copies of the Software, and to
86 # permit persons to whom the Software is furnished to do so, subject to
87 # the following conditions:
89 # The above copyright notice and this permission notice shall be included
90 # in all copies or substantial portions of the Software.
92 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
93 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
94 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
95 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
96 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
97 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
98 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
99 from __future__ import generators ### KEEP FOR COMPATIBILITY FIXERS
101 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
110 from SCons.Debug import logInstanceCreation
112 import SCons.Executor
116 # we use these a lot, so try to optimize them
117 is_String = SCons.Util.is_String
118 is_List = SCons.Util.is_List
125 print_actions_presub = 0
130 except AttributeError:
133 def default_exitstatfunc(s):
137 SET_LINENO = dis.SET_LINENO
138 HAVE_ARGUMENT = dis.HAVE_ARGUMENT
139 except AttributeError:
140 remove_set_lineno_codes = lambda x: x
142 def remove_set_lineno_codes(code):
149 if op >= HAVE_ARGUMENT:
151 result.append(code[i:i+3])
156 return ''.join(result)
158 strip_quotes = re.compile('^[\'"](.*)[\'"]$')
161 def _callable_contents(obj):
162 """Return the signature contents of a callable Python object.
165 # Test if obj is a method.
166 return _function_contents(obj.im_func)
168 except AttributeError:
170 # Test if obj is a callable object.
171 return _function_contents(obj.__call__.im_func)
173 except AttributeError:
175 # Test if obj is a code object.
176 return _code_contents(obj)
178 except AttributeError:
179 # Test if obj is a function object.
180 return _function_contents(obj)
183 def _object_contents(obj):
184 """Return the signature contents of any Python object.
186 We have to handle the case where object contains a code object
187 since it can be pickled directly.
190 # Test if obj is a method.
191 return _function_contents(obj.im_func)
193 except AttributeError:
195 # Test if obj is a callable object.
196 return _function_contents(obj.__call__.im_func)
198 except AttributeError:
200 # Test if obj is a code object.
201 return _code_contents(obj)
203 except AttributeError:
205 # Test if obj is a function object.
206 return _function_contents(obj)
208 except AttributeError:
209 # Should be a pickable Python object.
211 return cPickle.dumps(obj)
212 except (cPickle.PicklingError, TypeError):
213 # This is weird, but it seems that nested classes
214 # are unpickable. The Python docs say it should
215 # always be a PicklingError, but some Python
216 # versions seem to return TypeError. Just do
221 def _code_contents(code):
222 """Return the signature contents of a code object.
224 By providing direct access to the code object of the
225 function, Python makes this extremely easy. Hooray!
227 Unfortunately, older versions of Python include line
228 number indications in the compiled byte code. Boo!
229 So we remove the line number byte codes to prevent
230 recompilations from moving a Python function.
235 # The code contents depends on the number of local variables
236 # but not their actual names.
237 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
239 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
240 except AttributeError:
241 # Older versions of Python do not support closures.
242 contents.append(",0,0")
244 # The code contents depends on any constants accessed by the
245 # function. Note that we have to call _object_contents on each
246 # constants because the code object of nested functions can
247 # show-up among the constants.
249 # Note that we also always ignore the first entry of co_consts
250 # which contains the function doc string. We assume that the
251 # function does not access its doc string.
252 contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')')
254 # The code contents depends on the variable names used to
255 # accessed global variable, as changing the variable name changes
256 # the variable actually accessed and therefore changes the
258 contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')')
261 # The code contents depends on its actual code!!!
262 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
264 return ''.join(contents)
267 def _function_contents(func):
268 """Return the signature contents of a function."""
270 contents = [_code_contents(func.func_code)]
272 # The function contents depends on the value of defaults arguments
273 if func.func_defaults:
274 contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')')
276 contents.append(',()')
278 # The function contents depends on the closure captured cell values.
280 closure = func.func_closure or []
281 except AttributeError:
282 # Older versions of Python do not support closures.
285 #xxx = [_object_contents(x.cell_contents) for x in closure]
287 xxx = [_object_contents(x.cell_contents) for x in closure]
288 except AttributeError:
290 contents.append(',(' + ','.join(xxx) + ')')
292 return ''.join(contents)
295 def _actionAppend(act1, act2):
296 # This function knows how to slap two actions together.
297 # Mainly, it handles ListActions by concatenating into
298 # a single ListAction.
301 if a1 is None or a2 is None:
302 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
303 if isinstance(a1, ListAction):
304 if isinstance(a2, ListAction):
305 return ListAction(a1.list + a2.list)
307 return ListAction(a1.list + [ a2 ])
309 if isinstance(a2, ListAction):
310 return ListAction([ a1 ] + a2.list)
312 return ListAction([ a1, a2 ])
314 def _do_create_keywords(args, kw):
315 """This converts any arguments after the action argument into
316 their equivalent keywords and adds them to the kw argument.
318 v = kw.get('varlist', ())
319 # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
320 if is_String(v): v = (v,)
321 kw['varlist'] = tuple(v)
323 # turn positional args into equivalent keywords
325 if cmdstrfunc is None or is_String(cmdstrfunc):
326 kw['cmdstr'] = cmdstrfunc
327 elif callable(cmdstrfunc):
328 kw['strfunction'] = cmdstrfunc
330 raise SCons.Errors.UserError(
331 'Invalid command display variable type. '
332 'You must either pass a string or a callback which '
333 'accepts (target, source, env) as parameters.')
335 kw['varlist'] = args[1:] + kw['varlist']
336 if kw.get('strfunction', _null) is not _null \
337 and kw.get('cmdstr', _null) is not _null:
338 raise SCons.Errors.UserError(
339 'Cannot have both strfunction and cmdstr args to Action()')
341 def _do_create_action(act, kw):
342 """This is the actual "implementation" for the
343 Action factory method, below. This handles the
344 fact that passing lists to Action() itself has
345 different semantics than passing lists as elements
348 The former will create a ListAction, the latter
349 will create a CommandAction by converting the inner
350 list elements to strings."""
352 if isinstance(act, ActionBase):
356 #TODO(1.5) return CommandAction(act, **kw)
357 return CommandAction(act, **kw)
361 gen = kw['generator']
366 action_type = CommandGeneratorAction
368 action_type = FunctionAction
369 return action_type(act, kw)
372 var=SCons.Util.get_environment_var(act)
374 # This looks like a string that is purely an Environment
375 # variable reference, like "$FOO" or "${FOO}". We do
376 # something special here...we lazily evaluate the contents
377 # of that Environment variable, so a user could put something
378 # like a function or a CommandGenerator in that variable
379 # instead of a string.
380 return LazyAction(var, kw)
381 commands = str(act).split('\n')
382 if len(commands) == 1:
383 #TODO(1.5) return CommandAction(commands[0], **kw)
384 return CommandAction(commands[0], **kw)
385 # The list of string commands may include a LazyAction, so we
386 # reprocess them via _do_create_list_action.
387 return _do_create_list_action(commands, kw)
390 def _do_create_list_action(act, kw):
391 """A factory for list actions. Convert the input list into Actions
392 and then wrap them in a ListAction."""
395 aa = _do_create_action(a, kw)
396 if aa is not None: acts.append(aa)
398 return ListAction([])
402 return ListAction(acts)
404 def Action(act, *args, **kw):
405 """A factory for action objects."""
406 # Really simple: the _do_create_* routines do the heavy lifting.
407 _do_create_keywords(args, kw)
409 return _do_create_list_action(act, kw)
410 return _do_create_action(act, kw)
413 """Base class for all types of action objects that can be held by
414 other objects (Builders, Executors, etc.) This provides the
415 common methods for manipulating and combining those actions."""
417 def __cmp__(self, other):
418 return cmp(self.__dict__, other)
420 def no_batch_key(self, env, target, source):
423 batch_key = no_batch_key
425 def genstring(self, target, source, env):
428 def get_contents(self, target, source, env):
429 result = [ self.get_presig(target, source, env) ]
430 # This should never happen, as the Action() factory should wrap
431 # the varlist, but just in case an action is created directly,
432 # we duplicate this check here.
433 vl = self.get_varlist(target, source, env)
434 if is_String(vl): vl = (vl,)
436 result.append(env.subst('${'+v+'}'))
437 return ''.join(result)
439 def __add__(self, other):
440 return _actionAppend(self, other)
442 def __radd__(self, other):
443 return _actionAppend(other, self)
445 def presub_lines(self, env):
446 # CommandGeneratorAction needs a real environment
447 # in order to return the proper string here, since
448 # it may call LazyAction, which looks up a key
449 # in that env. So we temporarily remember the env here,
450 # and CommandGeneratorAction will use this env
451 # when it calls its _generate method.
452 self.presub_env = env
453 lines = str(self).split('\n')
454 self.presub_env = None # don't need this any more
457 def get_varlist(self, target, source, env, executor=None):
460 def get_targets(self, env, executor):
462 Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
467 class _ActionAction(ActionBase):
468 """Base class for actions that create output objects."""
469 def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
470 presub=_null, chdir=None, exitstatfunc=None,
471 batch_key=None, targets='$TARGETS',
474 if strfunction is not _null:
475 if strfunction is None:
478 self.strfunction = strfunction
479 self.varlist = varlist
483 exitstatfunc = default_exitstatfunc
484 self.exitstatfunc = exitstatfunc
486 self.targets = targets
489 if not callable(batch_key):
490 # They have set batch_key, but not to their own
491 # callable. The default behavior here will batch
492 # *all* targets+sources using this action, separated
493 # for each construction environment.
494 def default_batch_key(self, env, target, source):
495 return (id(self), id(env))
496 batch_key = default_batch_key
497 SCons.Util.AddMethod(self, batch_key, 'batch_key')
499 def print_cmd_line(self, s, target, source, env):
500 sys.stdout.write(s + "\n")
502 def __call__(self, target, source, env,
509 if not is_List(target):
511 if not is_List(source):
517 presub = print_actions_presub
518 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
519 if show is _null: show = print_actions
520 if execute is _null: execute = execute_actions
521 if chdir is _null: chdir = self.chdir
524 save_cwd = os.getcwd()
526 chdir = str(chdir.abspath)
527 except AttributeError:
528 if not is_String(chdir):
530 chdir = str(executor.batches[0].targets[0].dir)
532 chdir = str(target[0].dir)
535 target = executor.get_all_targets()
536 source = executor.get_all_sources()
537 t = ' and '.join(map(str, target))
538 l = '\n '.join(self.presub_lines(env))
539 out = "Building %s with action:\n %s\n" % (t, l)
540 sys.stdout.write(out)
542 if show and self.strfunction:
544 target = executor.get_all_targets()
545 source = executor.get_all_sources()
547 cmd = self.strfunction(target, source, env, executor)
549 cmd = self.strfunction(target, source, env)
552 cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
555 except AttributeError:
556 print_func = self.print_cmd_line
558 print_func = get('PRINT_CMD_LINE_FUNC')
560 print_func = self.print_cmd_line
561 print_func(cmd, target, source, env)
567 stat = self.execute(target, source, env, executor=executor)
568 if isinstance(stat, SCons.Errors.BuildError):
569 s = exitstatfunc(stat.status)
575 stat = exitstatfunc(stat)
580 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
585 def _string_from_cmd_list(cmd_list):
586 """Takes a list of command line arguments and returns a pretty
587 representation for printing."""
589 for arg in map(str, cmd_list):
590 if ' ' in arg or '\t' in arg:
591 arg = '"' + arg + '"'
595 # A fiddlin' little function that has an 'import SCons.Environment' which
596 # can't be moved to the top level without creating an import loop. Since
597 # this import creates a local variable named 'SCons', it blocks access to
598 # the global variable, so we move it here to prevent complaints about local
599 # variables being used uninitialized.
601 def get_default_ENV(env):
607 import SCons.Environment
608 # This is a hideously expensive way to get a default shell
609 # environment. What it really should do is run the platform
610 # setup to get the default ENV. Fortunately, it's incredibly
611 # rare for an Environment not to have a shell environment, so
612 # we're not going to worry about it overmuch.
613 default_ENV = SCons.Environment.Environment()['ENV']
616 # This function is still in draft mode. We're going to need something like
617 # it in the long run as more and more places use subprocess, but I'm sure
618 # it'll have to be tweaked to get the full desired functionality.
619 # one special arg (so far?), 'error', to tell what to do with exceptions.
620 def _subproc(env, cmd, error = 'ignore', **kw):
621 """Do common setup for a subprocess.Popen() call"""
622 # allow std{in,out,err} to be "'devnull'"
624 if is_String(io) and io == 'devnull':
625 kw['stdin'] = open(os.devnull)
626 io = kw.get('stdout')
627 if is_String(io) and io == 'devnull':
628 kw['stdout'] = open(os.devnull, 'w')
629 io = kw.get('stderr')
630 if is_String(io) and io == 'devnull':
631 kw['stderr'] = open(os.devnull, 'w')
633 # Figure out what shell environment to use
634 ENV = kw.get('env', None)
635 if ENV is None: ENV = get_default_ENV(env)
637 # Ensure that the ENV values are all strings:
639 for key, value in ENV.items():
641 # If the value is a list, then we assume it is a path list,
642 # because that's a pretty common list-like value to stick
643 # in an environment variable:
644 value = SCons.Util.flatten_sequence(value)
645 new_env[key] = os.pathsep.join(map(str, value))
647 # It's either a string or something else. If it's a string,
648 # we still want to call str() because it might be a *Unicode*
649 # string, which makes subprocess.Popen() gag. If it isn't a
650 # string or a list, then we just coerce it to a string, which
651 # is the proper way to handle Dir and File instances and will
652 # produce something reasonable for just about everything else:
653 new_env[key] = str(value)
657 #FUTURE return subprocess.Popen(cmd, **kw)
658 return subprocess.Popen(cmd, **kw)
659 except EnvironmentError, e:
660 if error == 'raise': raise
661 # return a dummy Popen instance that only returns error
663 def __init__(self, e): self.exception = e
664 def communicate(self): return ('','')
665 def wait(self): return -self.exception.errno
668 def read(self): return ''
669 def readline(self): return ''
670 stdout = stderr = f()
673 class CommandAction(_ActionAction):
674 """Class for command-execution actions."""
675 def __init__(self, cmd, **kw):
676 # Cmd can actually be a list or a single item; if it's a
677 # single item it should be the command string to execute; if a
678 # list then it should be the words of the command string to
679 # execute. Only a single command should be executed by this
680 # object; lists of commands should be handled by embedding
681 # these objects in a ListAction object (which the Action()
682 # factory above does). cmd will be passed to
683 # Environment.subst_list() for substituting environment
685 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
687 #TODO(1.5) _ActionAction.__init__(self, **kw)
688 _ActionAction.__init__(self, **kw)
690 if list(filter(is_List, cmd)):
691 raise TypeError, "CommandAction should be given only " \
696 if is_List(self.cmd_list):
697 return ' '.join(map(str, self.cmd_list))
698 return str(self.cmd_list)
700 def process(self, target, source, env, executor=None):
702 result = env.subst_list(self.cmd_list, 0, executor=executor)
704 result = env.subst_list(self.cmd_list, 0, target, source)
708 try: c = result[0][0][0]
709 except IndexError: c = None
710 if c == '@': silent = 1
711 elif c == '-': ignore = 1
713 result[0][0] = result[0][0][1:]
716 result[0] = result[0][1:]
719 return result, ignore, silent
721 def strfunction(self, target, source, env, executor=None):
722 if self.cmdstr is None:
724 if self.cmdstr is not _null:
725 from SCons.Subst import SUBST_RAW
727 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
729 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
732 cmd_list, ignore, silent = self.process(target, source, env, executor)
735 return _string_from_cmd_list(cmd_list[0])
737 def execute(self, target, source, env, executor=None):
738 """Execute a command action.
740 This will handle lists of commands as well as individual commands,
741 because construction variable substitution may turn a single
742 "command" into a list. This means that this class can actually
743 handle lists of commands, even though that's not how we use it
746 escape_list = SCons.Subst.escape_list
747 flatten_sequence = SCons.Util.flatten_sequence
752 raise SCons.Errors.UserError('Missing SHELL construction variable.')
757 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
760 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
762 escape = env.get('ESCAPE', lambda x: x)
764 ENV = get_default_ENV(env)
766 # Ensure that the ENV values are all strings:
767 for key, value in ENV.items():
768 if not is_String(value):
770 # If the value is a list, then we assume it is a
771 # path list, because that's a pretty common list-like
772 # value to stick in an environment variable:
773 value = flatten_sequence(value)
774 ENV[key] = os.pathsep.join(map(str, value))
776 # If it isn't a string or a list, then we just coerce
777 # it to a string, which is the proper way to handle
778 # Dir and File instances and will produce something
779 # reasonable for just about everything else:
780 ENV[key] = str(value)
783 target = executor.get_all_targets()
784 source = executor.get_all_sources()
785 cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
787 # Use len() to filter out any "command" that's zero-length.
788 for cmd_line in filter(len, cmd_list):
789 # Escape the command line for the interpreter we are using.
790 cmd_line = escape_list(cmd_line, escape)
791 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
792 if not ignore and result:
793 msg = "Error %s" % result
794 return SCons.Errors.BuildError(errstr=msg,
800 def get_presig(self, target, source, env, executor=None):
801 """Return the signature contents of this action's command line.
803 This strips $(-$) and everything in between the string,
804 since those parts don't affect signatures.
806 from SCons.Subst import SUBST_SIG
809 cmd = ' '.join(map(str, cmd))
813 return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
815 return env.subst_target_source(cmd, SUBST_SIG, target, source)
817 def get_implicit_deps(self, target, source, env, executor=None):
818 icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
819 if is_String(icd) and icd[:1] == '$':
821 if not icd or icd in ('0', 'None'):
823 from SCons.Subst import SUBST_SIG
825 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
827 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
829 for cmd_line in cmd_list:
832 m = strip_quotes.match(d)
837 res.append(env.fs.File(d))
840 class CommandGeneratorAction(ActionBase):
841 """Class for command-generator actions."""
842 def __init__(self, generator, kw):
843 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
844 self.generator = generator
846 self.varlist = kw.get('varlist', ())
847 self.targets = kw.get('targets', '$TARGETS')
849 def _generate(self, target, source, env, for_signature, executor=None):
850 # ensure that target is a list, to make it easier to write
851 # generator functions:
852 if not is_List(target):
856 target = executor.get_all_targets()
857 source = executor.get_all_sources()
858 ret = self.generator(target=target,
861 for_signature=for_signature)
862 #TODO(1.5) gen_cmd = Action(ret, **self.gen_kw)
863 gen_cmd = Action(ret, **self.gen_kw)
865 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
870 env = self.presub_env
871 except AttributeError:
874 env = SCons.Defaults.DefaultEnvironment()
875 act = self._generate([], [], env, 1)
878 def batch_key(self, env, target, source):
879 return self._generate(target, source, env, 1).batch_key(env, target, source)
881 def genstring(self, target, source, env, executor=None):
882 return self._generate(target, source, env, 1, executor).genstring(target, source, env)
884 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
885 show=_null, execute=_null, chdir=_null, executor=None):
886 act = self._generate(target, source, env, 0, executor)
888 raise UserError("While building `%s': "
889 "Cannot deduce file extension from source files: %s"
890 % (repr(list(map(str, target))), repr(list(map(str, source)))))
891 return act(target, source, env, exitstatfunc, presub,
892 show, execute, chdir, executor)
894 def get_presig(self, target, source, env, executor=None):
895 """Return the signature contents of this action's command line.
897 This strips $(-$) and everything in between the string,
898 since those parts don't affect signatures.
900 return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
902 def get_implicit_deps(self, target, source, env, executor=None):
903 return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
905 def get_varlist(self, target, source, env, executor=None):
906 return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
908 def get_targets(self, env, executor):
909 return self._generate(None, None, env, 1, executor).get_targets(env, executor)
913 # A LazyAction is a kind of hybrid generator and command action for
914 # strings of the form "$VAR". These strings normally expand to other
915 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
916 # want to be able to replace them with functions in the construction
917 # environment. Consequently, we want lazy evaluation and creation of
918 # an Action in the case of the function, but that's overkill in the more
919 # normal case of expansion to other strings.
921 # So we do this with a subclass that's both a generator *and*
922 # a command action. The overridden methods all do a quick check
923 # of the construction variable, and if it's a string we just call
924 # the corresponding CommandAction method to do the heavy lifting.
925 # If not, then we call the same-named CommandGeneratorAction method.
926 # The CommandGeneratorAction methods work by using the overridden
927 # _generate() method, that is, our own way of handling "generation" of
928 # an action based on what's in the construction variable.
930 class LazyAction(CommandGeneratorAction, CommandAction):
932 def __init__(self, var, kw):
933 if __debug__: logInstanceCreation(self, 'Action.LazyAction')
934 #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
935 CommandAction.__init__(self, '${'+var+'}', **kw)
936 self.var = SCons.Util.to_String(var)
939 def get_parent_class(self, env):
940 c = env.get(self.var)
941 if is_String(c) and not '\n' in c:
943 return CommandGeneratorAction
945 def _generate_cache(self, env):
947 c = env.get(self.var, '')
950 #TODO(1.5) gen_cmd = Action(c, **self.gen_kw)
951 gen_cmd = Action(c, **self.gen_kw)
953 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
956 def _generate(self, target, source, env, for_signature, executor=None):
957 return self._generate_cache(env)
959 def __call__(self, target, source, env, *args, **kw):
960 c = self.get_parent_class(env)
961 return c.__call__(self, target, source, env, *args, **kw)
963 def get_presig(self, target, source, env):
964 c = self.get_parent_class(env)
965 return c.get_presig(self, target, source, env)
967 def get_varlist(self, target, source, env, executor=None):
968 c = self.get_parent_class(env)
969 return c.get_varlist(self, target, source, env, executor)
972 class FunctionAction(_ActionAction):
973 """Class for Python function actions."""
975 def __init__(self, execfunction, kw):
976 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
978 self.execfunction = execfunction
980 self.funccontents = _callable_contents(execfunction)
981 except AttributeError:
983 # See if execfunction will do the heavy lifting for us.
984 self.gc = execfunction.get_contents
985 except AttributeError:
986 # This is weird, just do the best we can.
987 self.funccontents = _object_contents(execfunction)
989 #TODO(1.5) _ActionAction.__init__(self, **kw)
990 _ActionAction.__init__(self, **kw)
992 def function_name(self):
994 return self.execfunction.__name__
995 except AttributeError:
997 return self.execfunction.__class__.__name__
998 except AttributeError:
999 return "unknown_python_function"
1001 def strfunction(self, target, source, env, executor=None):
1002 if self.cmdstr is None:
1004 if self.cmdstr is not _null:
1005 from SCons.Subst import SUBST_RAW
1007 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
1009 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
1015 str_for_display = s.str_for_display
1016 except AttributeError:
1019 s = str_for_display()
1021 return '[' + ", ".join(map(quote, a)) + ']'
1023 strfunc = self.execfunction.strfunction
1024 except AttributeError:
1029 if callable(strfunc):
1030 return strfunc(target, source, env)
1031 name = self.function_name()
1032 tstr = array(target)
1033 sstr = array(source)
1034 return "%s(%s, %s)" % (name, tstr, sstr)
1037 name = self.function_name()
1038 if name == 'ActionCaller':
1039 return str(self.execfunction)
1040 return "%s(target, source, env)" % name
1042 def execute(self, target, source, env, executor=None):
1043 exc_info = (None,None,None)
1046 target = executor.get_all_targets()
1047 source = executor.get_all_sources()
1048 rsources = list(map(rfile, source))
1050 result = self.execfunction(target=target, source=rsources, env=env)
1051 except KeyboardInterrupt, e:
1053 except SystemExit, e:
1055 except Exception, e:
1057 exc_info = sys.exc_info()
1060 result = SCons.Errors.convert_to_BuildError(result, exc_info)
1064 result.command=self.strfunction(target, source, env, executor)
1066 result.command=self.strfunction(target, source, env)
1068 # FIXME: This maintains backward compatibility with respect to
1069 # which type of exceptions were returned by raising an
1070 # exception and which ones were returned by value. It would
1071 # probably be best to always return them by value here, but
1072 # some codes do not check the return value of Actions and I do
1073 # not have the time to modify them at this point.
1075 not isinstance(exc_info[1],EnvironmentError)):
1080 # Break the cycle between the traceback object and this
1081 # function stack frame. See the sys.exc_info() doc info for
1082 # more information about this issue.
1086 def get_presig(self, target, source, env):
1087 """Return the signature contents of this callable action."""
1089 return self.gc(target, source, env)
1090 except AttributeError:
1091 return self.funccontents
1093 def get_implicit_deps(self, target, source, env):
1096 class ListAction(ActionBase):
1097 """Class for lists of other actions."""
1098 def __init__(self, actionlist):
1099 if __debug__: logInstanceCreation(self, 'Action.ListAction')
1100 def list_of_actions(x):
1101 if isinstance(x, ActionBase):
1104 self.list = list(map(list_of_actions, actionlist))
1105 # our children will have had any varlist
1106 # applied; we don't need to do it again
1108 self.targets = '$TARGETS'
1110 def genstring(self, target, source, env):
1111 return '\n'.join([a.genstring(target, source, env) for a in self.list])
1114 return '\n'.join(map(str, self.list))
1116 def presub_lines(self, env):
1117 return SCons.Util.flatten_sequence(
1118 [a.presub_lines(env) for a in self.list])
1120 def get_presig(self, target, source, env):
1121 """Return the signature contents of this action list.
1123 Simple concatenation of the signatures of the elements.
1125 return "".join([x.get_contents(target, source, env) for x in self.list])
1127 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
1128 show=_null, execute=_null, chdir=_null, executor=None):
1130 target = executor.get_all_targets()
1131 source = executor.get_all_sources()
1132 for act in self.list:
1133 stat = act(target, source, env, exitstatfunc, presub,
1134 show, execute, chdir, executor)
1139 def get_implicit_deps(self, target, source, env):
1141 for act in self.list:
1142 result.extend(act.get_implicit_deps(target, source, env))
1145 def get_varlist(self, target, source, env, executor=None):
1146 result = SCons.Util.OrderedDict()
1147 for act in self.list:
1148 for var in act.get_varlist(target, source, env, executor):
1150 return result.keys()
1153 """A class for delaying calling an Action function with specific
1154 (positional and keyword) arguments until the Action is actually
1157 This class looks to the rest of the world like a normal Action object,
1158 but what it's really doing is hanging on to the arguments until we
1159 have a target, source and env to use for the expansion.
1161 def __init__(self, parent, args, kw):
1162 self.parent = parent
1166 def get_contents(self, target, source, env):
1167 actfunc = self.parent.actfunc
1169 # "self.actfunc" is a function.
1170 contents = str(actfunc.func_code.co_code)
1171 except AttributeError:
1172 # "self.actfunc" is a callable object.
1174 contents = str(actfunc.__call__.im_func.func_code.co_code)
1175 except AttributeError:
1176 # No __call__() method, so it might be a builtin
1177 # or something like that. Do the best we can.
1178 contents = str(actfunc)
1179 contents = remove_set_lineno_codes(contents)
1182 def subst(self, s, target, source, env):
1183 # If s is a list, recursively apply subst()
1184 # to every element in the list
1188 result.append(self.subst(elem, target, source, env))
1189 return self.parent.convert(result)
1191 # Special-case hack: Let a custom function wrapped in an
1192 # ActionCaller get at the environment through which the action
1193 # was called by using this hard-coded value as a special return.
1197 return env.subst(s, 1, target, source)
1198 return self.parent.convert(s)
1200 def subst_args(self, target, source, env):
1201 return [self.subst(x, target, source, env) for x in self.args]
1203 def subst_kw(self, target, source, env):
1205 for key in self.kw.keys():
1206 kw[key] = self.subst(self.kw[key], target, source, env)
1209 def __call__(self, target, source, env, executor=None):
1210 args = self.subst_args(target, source, env)
1211 kw = self.subst_kw(target, source, env)
1212 #TODO(1.5) return self.parent.actfunc(*args, **kw)
1213 return self.parent.actfunc(*args, **kw)
1215 def strfunction(self, target, source, env):
1216 args = self.subst_args(target, source, env)
1217 kw = self.subst_kw(target, source, env)
1218 #TODO(1.5) return self.parent.strfunc(*args, **kw)
1219 return self.parent.strfunc(*args, **kw)
1222 #TODO(1.5) return self.parent.strfunc(*self.args, **self.kw)
1223 return self.parent.strfunc(*self.args, **self.kw)
1225 class ActionFactory:
1226 """A factory class that will wrap up an arbitrary function
1227 as an SCons-executable Action object.
1229 The real heavy lifting here is done by the ActionCaller class.
1230 We just collect the (positional and keyword) arguments that we're
1231 called with and give them to the ActionCaller object we create,
1232 so it can hang onto them until it needs them.
1234 def __init__(self, actfunc, strfunc, convert=lambda x: x):
1235 self.actfunc = actfunc
1236 self.strfunc = strfunc
1237 self.convert = convert
1239 def __call__(self, *args, **kw):
1240 ac = ActionCaller(self, args, kw)
1241 action = Action(ac, strfunction=ac.strfunction)
1246 # indent-tabs-mode:nil
1248 # vim: set expandtab tabstop=4 shiftwidth=4: