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__"
107 # compat layer imports "cPickle" for us if it's available.
113 from SCons.Debug import logInstanceCreation
115 import SCons.Executor
119 # we use these a lot, so try to optimize them
120 is_String = SCons.Util.is_String
121 is_List = SCons.Util.is_List
128 print_actions_presub = 0
133 except AttributeError:
136 def default_exitstatfunc(s):
140 SET_LINENO = dis.SET_LINENO
141 HAVE_ARGUMENT = dis.HAVE_ARGUMENT
142 except AttributeError:
143 remove_set_lineno_codes = lambda x: x
145 def remove_set_lineno_codes(code):
152 if op >= HAVE_ARGUMENT:
154 result.append(code[i:i+3])
159 return ''.join(result)
161 strip_quotes = re.compile('^[\'"](.*)[\'"]$')
164 def _callable_contents(obj):
165 """Return the signature contents of a callable Python object.
168 # Test if obj is a method.
169 return _function_contents(obj.im_func)
171 except AttributeError:
173 # Test if obj is a callable object.
174 return _function_contents(obj.__call__.im_func)
176 except AttributeError:
178 # Test if obj is a code object.
179 return _code_contents(obj)
181 except AttributeError:
182 # Test if obj is a function object.
183 return _function_contents(obj)
186 def _object_contents(obj):
187 """Return the signature contents of any Python object.
189 We have to handle the case where object contains a code object
190 since it can be pickled directly.
193 # Test if obj is a method.
194 return _function_contents(obj.im_func)
196 except AttributeError:
198 # Test if obj is a callable object.
199 return _function_contents(obj.__call__.im_func)
201 except AttributeError:
203 # Test if obj is a code object.
204 return _code_contents(obj)
206 except AttributeError:
208 # Test if obj is a function object.
209 return _function_contents(obj)
211 except AttributeError:
212 # Should be a pickable Python object.
214 return pickle.dumps(obj)
215 except (pickle.PicklingError, TypeError):
216 # This is weird, but it seems that nested classes
217 # are unpickable. The Python docs say it should
218 # always be a PicklingError, but some Python
219 # versions seem to return TypeError. Just do
224 def _code_contents(code):
225 """Return the signature contents of a code object.
227 By providing direct access to the code object of the
228 function, Python makes this extremely easy. Hooray!
230 Unfortunately, older versions of Python include line
231 number indications in the compiled byte code. Boo!
232 So we remove the line number byte codes to prevent
233 recompilations from moving a Python function.
238 # The code contents depends on the number of local variables
239 # but not their actual names.
240 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
242 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
243 except AttributeError:
244 # Older versions of Python do not support closures.
245 contents.append(",0,0")
247 # The code contents depends on any constants accessed by the
248 # function. Note that we have to call _object_contents on each
249 # constants because the code object of nested functions can
250 # show-up among the constants.
252 # Note that we also always ignore the first entry of co_consts
253 # which contains the function doc string. We assume that the
254 # function does not access its doc string.
255 contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')')
257 # The code contents depends on the variable names used to
258 # accessed global variable, as changing the variable name changes
259 # the variable actually accessed and therefore changes the
261 contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')')
264 # The code contents depends on its actual code!!!
265 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
267 return ''.join(contents)
270 def _function_contents(func):
271 """Return the signature contents of a function."""
273 contents = [_code_contents(func.func_code)]
275 # The function contents depends on the value of defaults arguments
276 if func.func_defaults:
277 contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')')
279 contents.append(',()')
281 # The function contents depends on the closure captured cell values.
283 closure = func.func_closure or []
284 except AttributeError:
285 # Older versions of Python do not support closures.
288 #xxx = [_object_contents(x.cell_contents) for x in closure]
290 xxx = [_object_contents(x.cell_contents) for x in closure]
291 except AttributeError:
293 contents.append(',(' + ','.join(xxx) + ')')
295 return ''.join(contents)
298 def _actionAppend(act1, act2):
299 # This function knows how to slap two actions together.
300 # Mainly, it handles ListActions by concatenating into
301 # a single ListAction.
304 if a1 is None or a2 is None:
305 raise TypeError("Cannot append %s to %s" % (type(act1), type(act2)))
306 if isinstance(a1, ListAction):
307 if isinstance(a2, ListAction):
308 return ListAction(a1.list + a2.list)
310 return ListAction(a1.list + [ a2 ])
312 if isinstance(a2, ListAction):
313 return ListAction([ a1 ] + a2.list)
315 return ListAction([ a1, a2 ])
317 def _do_create_keywords(args, kw):
318 """This converts any arguments after the action argument into
319 their equivalent keywords and adds them to the kw argument.
321 v = kw.get('varlist', ())
322 # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
323 if is_String(v): v = (v,)
324 kw['varlist'] = tuple(v)
326 # turn positional args into equivalent keywords
328 if cmdstrfunc is None or is_String(cmdstrfunc):
329 kw['cmdstr'] = cmdstrfunc
330 elif callable(cmdstrfunc):
331 kw['strfunction'] = cmdstrfunc
333 raise SCons.Errors.UserError(
334 'Invalid command display variable type. '
335 'You must either pass a string or a callback which '
336 'accepts (target, source, env) as parameters.')
338 kw['varlist'] = args[1:] + kw['varlist']
339 if kw.get('strfunction', _null) is not _null \
340 and kw.get('cmdstr', _null) is not _null:
341 raise SCons.Errors.UserError(
342 'Cannot have both strfunction and cmdstr args to Action()')
344 def _do_create_action(act, kw):
345 """This is the actual "implementation" for the
346 Action factory method, below. This handles the
347 fact that passing lists to Action() itself has
348 different semantics than passing lists as elements
351 The former will create a ListAction, the latter
352 will create a CommandAction by converting the inner
353 list elements to strings."""
355 if isinstance(act, ActionBase):
359 #TODO(1.5) return CommandAction(act, **kw)
360 return CommandAction(act, **kw)
364 gen = kw['generator']
369 action_type = CommandGeneratorAction
371 action_type = FunctionAction
372 return action_type(act, kw)
375 var=SCons.Util.get_environment_var(act)
377 # This looks like a string that is purely an Environment
378 # variable reference, like "$FOO" or "${FOO}". We do
379 # something special here...we lazily evaluate the contents
380 # of that Environment variable, so a user could put something
381 # like a function or a CommandGenerator in that variable
382 # instead of a string.
383 return LazyAction(var, kw)
384 commands = str(act).split('\n')
385 if len(commands) == 1:
386 #TODO(1.5) return CommandAction(commands[0], **kw)
387 return CommandAction(commands[0], **kw)
388 # The list of string commands may include a LazyAction, so we
389 # reprocess them via _do_create_list_action.
390 return _do_create_list_action(commands, kw)
393 def _do_create_list_action(act, kw):
394 """A factory for list actions. Convert the input list into Actions
395 and then wrap them in a ListAction."""
398 aa = _do_create_action(a, kw)
399 if aa is not None: acts.append(aa)
401 return ListAction([])
405 return ListAction(acts)
407 def Action(act, *args, **kw):
408 """A factory for action objects."""
409 # Really simple: the _do_create_* routines do the heavy lifting.
410 _do_create_keywords(args, kw)
412 return _do_create_list_action(act, kw)
413 return _do_create_action(act, kw)
416 """Base class for all types of action objects that can be held by
417 other objects (Builders, Executors, etc.) This provides the
418 common methods for manipulating and combining those actions."""
420 def __cmp__(self, other):
421 return cmp(self.__dict__, other)
423 def no_batch_key(self, env, target, source):
426 batch_key = no_batch_key
428 def genstring(self, target, source, env):
431 def get_contents(self, target, source, env):
432 result = [ self.get_presig(target, source, env) ]
433 # This should never happen, as the Action() factory should wrap
434 # the varlist, but just in case an action is created directly,
435 # we duplicate this check here.
436 vl = self.get_varlist(target, source, env)
437 if is_String(vl): vl = (vl,)
439 result.append(env.subst('${'+v+'}'))
440 return ''.join(result)
442 def __add__(self, other):
443 return _actionAppend(self, other)
445 def __radd__(self, other):
446 return _actionAppend(other, self)
448 def presub_lines(self, env):
449 # CommandGeneratorAction needs a real environment
450 # in order to return the proper string here, since
451 # it may call LazyAction, which looks up a key
452 # in that env. So we temporarily remember the env here,
453 # and CommandGeneratorAction will use this env
454 # when it calls its _generate method.
455 self.presub_env = env
456 lines = str(self).split('\n')
457 self.presub_env = None # don't need this any more
460 def get_varlist(self, target, source, env, executor=None):
463 def get_targets(self, env, executor):
465 Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
470 class _ActionAction(ActionBase):
471 """Base class for actions that create output objects."""
472 def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
473 presub=_null, chdir=None, exitstatfunc=None,
474 batch_key=None, targets='$TARGETS',
477 if strfunction is not _null:
478 if strfunction is None:
481 self.strfunction = strfunction
482 self.varlist = varlist
486 exitstatfunc = default_exitstatfunc
487 self.exitstatfunc = exitstatfunc
489 self.targets = targets
492 if not callable(batch_key):
493 # They have set batch_key, but not to their own
494 # callable. The default behavior here will batch
495 # *all* targets+sources using this action, separated
496 # for each construction environment.
497 def default_batch_key(self, env, target, source):
498 return (id(self), id(env))
499 batch_key = default_batch_key
500 SCons.Util.AddMethod(self, batch_key, 'batch_key')
502 def print_cmd_line(self, s, target, source, env):
503 sys.stdout.write(s + u"\n")
505 def __call__(self, target, source, env,
512 if not is_List(target):
514 if not is_List(source):
520 presub = print_actions_presub
521 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
522 if show is _null: show = print_actions
523 if execute is _null: execute = execute_actions
524 if chdir is _null: chdir = self.chdir
527 save_cwd = os.getcwd()
529 chdir = str(chdir.abspath)
530 except AttributeError:
531 if not is_String(chdir):
533 chdir = str(executor.batches[0].targets[0].dir)
535 chdir = str(target[0].dir)
538 target = executor.get_all_targets()
539 source = executor.get_all_sources()
540 t = ' and '.join(map(str, target))
541 l = '\n '.join(self.presub_lines(env))
542 out = u"Building %s with action:\n %s\n" % (t, l)
543 sys.stdout.write(out)
545 if show and self.strfunction:
547 target = executor.get_all_targets()
548 source = executor.get_all_sources()
550 cmd = self.strfunction(target, source, env, executor)
552 cmd = self.strfunction(target, source, env)
555 cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
558 except AttributeError:
559 print_func = self.print_cmd_line
561 print_func = get('PRINT_CMD_LINE_FUNC')
563 print_func = self.print_cmd_line
564 print_func(cmd, target, source, env)
570 stat = self.execute(target, source, env, executor=executor)
571 if isinstance(stat, SCons.Errors.BuildError):
572 s = exitstatfunc(stat.status)
578 stat = exitstatfunc(stat)
583 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
588 def _string_from_cmd_list(cmd_list):
589 """Takes a list of command line arguments and returns a pretty
590 representation for printing."""
592 for arg in map(str, cmd_list):
593 if ' ' in arg or '\t' in arg:
594 arg = '"' + arg + '"'
598 # A fiddlin' little function that has an 'import SCons.Environment' which
599 # can't be moved to the top level without creating an import loop. Since
600 # this import creates a local variable named 'SCons', it blocks access to
601 # the global variable, so we move it here to prevent complaints about local
602 # variables being used uninitialized.
604 def get_default_ENV(env):
610 import SCons.Environment
611 # This is a hideously expensive way to get a default shell
612 # environment. What it really should do is run the platform
613 # setup to get the default ENV. Fortunately, it's incredibly
614 # rare for an Environment not to have a shell environment, so
615 # we're not going to worry about it overmuch.
616 default_ENV = SCons.Environment.Environment()['ENV']
619 # This function is still in draft mode. We're going to need something like
620 # it in the long run as more and more places use subprocess, but I'm sure
621 # it'll have to be tweaked to get the full desired functionality.
622 # one special arg (so far?), 'error', to tell what to do with exceptions.
623 def _subproc(env, cmd, error = 'ignore', **kw):
624 """Do common setup for a subprocess.Popen() call"""
625 # allow std{in,out,err} to be "'devnull'"
627 if is_String(io) and io == 'devnull':
628 kw['stdin'] = open(os.devnull)
629 io = kw.get('stdout')
630 if is_String(io) and io == 'devnull':
631 kw['stdout'] = open(os.devnull, 'w')
632 io = kw.get('stderr')
633 if is_String(io) and io == 'devnull':
634 kw['stderr'] = open(os.devnull, 'w')
636 # Figure out what shell environment to use
637 ENV = kw.get('env', None)
638 if ENV is None: ENV = get_default_ENV(env)
640 # Ensure that the ENV values are all strings:
642 for key, value in ENV.items():
644 # If the value is a list, then we assume it is a path list,
645 # because that's a pretty common list-like value to stick
646 # in an environment variable:
647 value = SCons.Util.flatten_sequence(value)
648 new_env[key] = os.pathsep.join(map(str, value))
650 # It's either a string or something else. If it's a string,
651 # we still want to call str() because it might be a *Unicode*
652 # string, which makes subprocess.Popen() gag. If it isn't a
653 # string or a list, then we just coerce it to a string, which
654 # is the proper way to handle Dir and File instances and will
655 # produce something reasonable for just about everything else:
656 new_env[key] = str(value)
660 #FUTURE return subprocess.Popen(cmd, **kw)
661 return subprocess.Popen(cmd, **kw)
662 except EnvironmentError, e:
663 if error == 'raise': raise
664 # return a dummy Popen instance that only returns error
666 def __init__(self, e): self.exception = e
667 def communicate(self): return ('','')
668 def wait(self): return -self.exception.errno
671 def read(self): return ''
672 def readline(self): return ''
673 stdout = stderr = f()
676 class CommandAction(_ActionAction):
677 """Class for command-execution actions."""
678 def __init__(self, cmd, **kw):
679 # Cmd can actually be a list or a single item; if it's a
680 # single item it should be the command string to execute; if a
681 # list then it should be the words of the command string to
682 # execute. Only a single command should be executed by this
683 # object; lists of commands should be handled by embedding
684 # these objects in a ListAction object (which the Action()
685 # factory above does). cmd will be passed to
686 # Environment.subst_list() for substituting environment
688 if __debug__: logInstanceCreation(self, 'Action.CommandAction')
690 #TODO(1.5) _ActionAction.__init__(self, **kw)
691 _ActionAction.__init__(self, **kw)
693 if list(filter(is_List, cmd)):
694 raise TypeError("CommandAction should be given only " \
699 if is_List(self.cmd_list):
700 return ' '.join(map(str, self.cmd_list))
701 return str(self.cmd_list)
703 def process(self, target, source, env, executor=None):
705 result = env.subst_list(self.cmd_list, 0, executor=executor)
707 result = env.subst_list(self.cmd_list, 0, target, source)
711 try: c = result[0][0][0]
712 except IndexError: c = None
713 if c == '@': silent = 1
714 elif c == '-': ignore = 1
716 result[0][0] = result[0][0][1:]
719 result[0] = result[0][1:]
722 return result, ignore, silent
724 def strfunction(self, target, source, env, executor=None):
725 if self.cmdstr is None:
727 if self.cmdstr is not _null:
728 from SCons.Subst import SUBST_RAW
730 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
732 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
735 cmd_list, ignore, silent = self.process(target, source, env, executor)
738 return _string_from_cmd_list(cmd_list[0])
740 def execute(self, target, source, env, executor=None):
741 """Execute a command action.
743 This will handle lists of commands as well as individual commands,
744 because construction variable substitution may turn a single
745 "command" into a list. This means that this class can actually
746 handle lists of commands, even though that's not how we use it
749 escape_list = SCons.Subst.escape_list
750 flatten_sequence = SCons.Util.flatten_sequence
755 raise SCons.Errors.UserError('Missing SHELL construction variable.')
760 raise SCons.Errors.UserError('Missing SPAWN construction variable.')
763 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
765 escape = env.get('ESCAPE', lambda x: x)
767 ENV = get_default_ENV(env)
769 # Ensure that the ENV values are all strings:
770 for key, value in ENV.items():
771 if not is_String(value):
773 # If the value is a list, then we assume it is a
774 # path list, because that's a pretty common list-like
775 # value to stick in an environment variable:
776 value = flatten_sequence(value)
777 ENV[key] = os.pathsep.join(map(str, value))
779 # If it isn't a string or a list, then we just coerce
780 # it to a string, which is the proper way to handle
781 # Dir and File instances and will produce something
782 # reasonable for just about everything else:
783 ENV[key] = str(value)
786 target = executor.get_all_targets()
787 source = executor.get_all_sources()
788 cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
790 # Use len() to filter out any "command" that's zero-length.
791 for cmd_line in filter(len, cmd_list):
792 # Escape the command line for the interpreter we are using.
793 cmd_line = escape_list(cmd_line, escape)
794 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
795 if not ignore and result:
796 msg = "Error %s" % result
797 return SCons.Errors.BuildError(errstr=msg,
803 def get_presig(self, target, source, env, executor=None):
804 """Return the signature contents of this action's command line.
806 This strips $(-$) and everything in between the string,
807 since those parts don't affect signatures.
809 from SCons.Subst import SUBST_SIG
812 cmd = ' '.join(map(str, cmd))
816 return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
818 return env.subst_target_source(cmd, SUBST_SIG, target, source)
820 def get_implicit_deps(self, target, source, env, executor=None):
821 icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
822 if is_String(icd) and icd[:1] == '$':
824 if not icd or icd in ('0', 'None'):
826 from SCons.Subst import SUBST_SIG
828 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
830 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
832 for cmd_line in cmd_list:
835 m = strip_quotes.match(d)
840 res.append(env.fs.File(d))
843 class CommandGeneratorAction(ActionBase):
844 """Class for command-generator actions."""
845 def __init__(self, generator, kw):
846 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
847 self.generator = generator
849 self.varlist = kw.get('varlist', ())
850 self.targets = kw.get('targets', '$TARGETS')
852 def _generate(self, target, source, env, for_signature, executor=None):
853 # ensure that target is a list, to make it easier to write
854 # generator functions:
855 if not is_List(target):
859 target = executor.get_all_targets()
860 source = executor.get_all_sources()
861 ret = self.generator(target=target,
864 for_signature=for_signature)
865 #TODO(1.5) gen_cmd = Action(ret, **self.gen_kw)
866 gen_cmd = Action(ret, **self.gen_kw)
868 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
873 env = self.presub_env
874 except AttributeError:
877 env = SCons.Defaults.DefaultEnvironment()
878 act = self._generate([], [], env, 1)
881 def batch_key(self, env, target, source):
882 return self._generate(target, source, env, 1).batch_key(env, target, source)
884 def genstring(self, target, source, env, executor=None):
885 return self._generate(target, source, env, 1, executor).genstring(target, source, env)
887 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
888 show=_null, execute=_null, chdir=_null, executor=None):
889 act = self._generate(target, source, env, 0, executor)
891 raise UserError("While building `%s': "
892 "Cannot deduce file extension from source files: %s"
893 % (repr(list(map(str, target))), repr(list(map(str, source)))))
894 return act(target, source, env, exitstatfunc, presub,
895 show, execute, chdir, executor)
897 def get_presig(self, target, source, env, executor=None):
898 """Return the signature contents of this action's command line.
900 This strips $(-$) and everything in between the string,
901 since those parts don't affect signatures.
903 return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
905 def get_implicit_deps(self, target, source, env, executor=None):
906 return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
908 def get_varlist(self, target, source, env, executor=None):
909 return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
911 def get_targets(self, env, executor):
912 return self._generate(None, None, env, 1, executor).get_targets(env, executor)
916 # A LazyAction is a kind of hybrid generator and command action for
917 # strings of the form "$VAR". These strings normally expand to other
918 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
919 # want to be able to replace them with functions in the construction
920 # environment. Consequently, we want lazy evaluation and creation of
921 # an Action in the case of the function, but that's overkill in the more
922 # normal case of expansion to other strings.
924 # So we do this with a subclass that's both a generator *and*
925 # a command action. The overridden methods all do a quick check
926 # of the construction variable, and if it's a string we just call
927 # the corresponding CommandAction method to do the heavy lifting.
928 # If not, then we call the same-named CommandGeneratorAction method.
929 # The CommandGeneratorAction methods work by using the overridden
930 # _generate() method, that is, our own way of handling "generation" of
931 # an action based on what's in the construction variable.
933 class LazyAction(CommandGeneratorAction, CommandAction):
935 def __init__(self, var, kw):
936 if __debug__: logInstanceCreation(self, 'Action.LazyAction')
937 #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
938 CommandAction.__init__(self, '${'+var+'}', **kw)
939 self.var = SCons.Util.to_String(var)
942 def get_parent_class(self, env):
943 c = env.get(self.var)
944 if is_String(c) and not '\n' in c:
946 return CommandGeneratorAction
948 def _generate_cache(self, env):
950 c = env.get(self.var, '')
953 #TODO(1.5) gen_cmd = Action(c, **self.gen_kw)
954 gen_cmd = Action(c, **self.gen_kw)
956 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
959 def _generate(self, target, source, env, for_signature, executor=None):
960 return self._generate_cache(env)
962 def __call__(self, target, source, env, *args, **kw):
963 c = self.get_parent_class(env)
964 return c.__call__(self, target, source, env, *args, **kw)
966 def get_presig(self, target, source, env):
967 c = self.get_parent_class(env)
968 return c.get_presig(self, target, source, env)
970 def get_varlist(self, target, source, env, executor=None):
971 c = self.get_parent_class(env)
972 return c.get_varlist(self, target, source, env, executor)
975 class FunctionAction(_ActionAction):
976 """Class for Python function actions."""
978 def __init__(self, execfunction, kw):
979 if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
981 self.execfunction = execfunction
983 self.funccontents = _callable_contents(execfunction)
984 except AttributeError:
986 # See if execfunction will do the heavy lifting for us.
987 self.gc = execfunction.get_contents
988 except AttributeError:
989 # This is weird, just do the best we can.
990 self.funccontents = _object_contents(execfunction)
992 #TODO(1.5) _ActionAction.__init__(self, **kw)
993 _ActionAction.__init__(self, **kw)
995 def function_name(self):
997 return self.execfunction.__name__
998 except AttributeError:
1000 return self.execfunction.__class__.__name__
1001 except AttributeError:
1002 return "unknown_python_function"
1004 def strfunction(self, target, source, env, executor=None):
1005 if self.cmdstr is None:
1007 if self.cmdstr is not _null:
1008 from SCons.Subst import SUBST_RAW
1010 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
1012 c = env.subst(self.cmdstr, SUBST_RAW, target, source)
1018 str_for_display = s.str_for_display
1019 except AttributeError:
1022 s = str_for_display()
1024 return '[' + ", ".join(map(quote, a)) + ']'
1026 strfunc = self.execfunction.strfunction
1027 except AttributeError:
1032 if callable(strfunc):
1033 return strfunc(target, source, env)
1034 name = self.function_name()
1035 tstr = array(target)
1036 sstr = array(source)
1037 return "%s(%s, %s)" % (name, tstr, sstr)
1040 name = self.function_name()
1041 if name == 'ActionCaller':
1042 return str(self.execfunction)
1043 return "%s(target, source, env)" % name
1045 def execute(self, target, source, env, executor=None):
1046 exc_info = (None,None,None)
1049 target = executor.get_all_targets()
1050 source = executor.get_all_sources()
1051 rsources = list(map(rfile, source))
1053 result = self.execfunction(target=target, source=rsources, env=env)
1054 except KeyboardInterrupt, e:
1056 except SystemExit, e:
1058 except Exception, e:
1060 exc_info = sys.exc_info()
1063 result = SCons.Errors.convert_to_BuildError(result, exc_info)
1067 result.command=self.strfunction(target, source, env, executor)
1069 result.command=self.strfunction(target, source, env)
1071 # FIXME: This maintains backward compatibility with respect to
1072 # which type of exceptions were returned by raising an
1073 # exception and which ones were returned by value. It would
1074 # probably be best to always return them by value here, but
1075 # some codes do not check the return value of Actions and I do
1076 # not have the time to modify them at this point.
1078 not isinstance(exc_info[1],EnvironmentError)):
1083 # Break the cycle between the traceback object and this
1084 # function stack frame. See the sys.exc_info() doc info for
1085 # more information about this issue.
1089 def get_presig(self, target, source, env):
1090 """Return the signature contents of this callable action."""
1092 return self.gc(target, source, env)
1093 except AttributeError:
1094 return self.funccontents
1096 def get_implicit_deps(self, target, source, env):
1099 class ListAction(ActionBase):
1100 """Class for lists of other actions."""
1101 def __init__(self, actionlist):
1102 if __debug__: logInstanceCreation(self, 'Action.ListAction')
1103 def list_of_actions(x):
1104 if isinstance(x, ActionBase):
1107 self.list = list(map(list_of_actions, actionlist))
1108 # our children will have had any varlist
1109 # applied; we don't need to do it again
1111 self.targets = '$TARGETS'
1113 def genstring(self, target, source, env):
1114 return '\n'.join([a.genstring(target, source, env) for a in self.list])
1117 return '\n'.join(map(str, self.list))
1119 def presub_lines(self, env):
1120 return SCons.Util.flatten_sequence(
1121 [a.presub_lines(env) for a in self.list])
1123 def get_presig(self, target, source, env):
1124 """Return the signature contents of this action list.
1126 Simple concatenation of the signatures of the elements.
1128 return "".join([x.get_contents(target, source, env) for x in self.list])
1130 def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
1131 show=_null, execute=_null, chdir=_null, executor=None):
1133 target = executor.get_all_targets()
1134 source = executor.get_all_sources()
1135 for act in self.list:
1136 stat = act(target, source, env, exitstatfunc, presub,
1137 show, execute, chdir, executor)
1142 def get_implicit_deps(self, target, source, env):
1144 for act in self.list:
1145 result.extend(act.get_implicit_deps(target, source, env))
1148 def get_varlist(self, target, source, env, executor=None):
1149 result = SCons.Util.OrderedDict()
1150 for act in self.list:
1151 for var in act.get_varlist(target, source, env, executor):
1153 return result.keys()
1156 """A class for delaying calling an Action function with specific
1157 (positional and keyword) arguments until the Action is actually
1160 This class looks to the rest of the world like a normal Action object,
1161 but what it's really doing is hanging on to the arguments until we
1162 have a target, source and env to use for the expansion.
1164 def __init__(self, parent, args, kw):
1165 self.parent = parent
1169 def get_contents(self, target, source, env):
1170 actfunc = self.parent.actfunc
1172 # "self.actfunc" is a function.
1173 contents = str(actfunc.func_code.co_code)
1174 except AttributeError:
1175 # "self.actfunc" is a callable object.
1177 contents = str(actfunc.__call__.im_func.func_code.co_code)
1178 except AttributeError:
1179 # No __call__() method, so it might be a builtin
1180 # or something like that. Do the best we can.
1181 contents = str(actfunc)
1182 contents = remove_set_lineno_codes(contents)
1185 def subst(self, s, target, source, env):
1186 # If s is a list, recursively apply subst()
1187 # to every element in the list
1191 result.append(self.subst(elem, target, source, env))
1192 return self.parent.convert(result)
1194 # Special-case hack: Let a custom function wrapped in an
1195 # ActionCaller get at the environment through which the action
1196 # was called by using this hard-coded value as a special return.
1200 return env.subst(s, 1, target, source)
1201 return self.parent.convert(s)
1203 def subst_args(self, target, source, env):
1204 return [self.subst(x, target, source, env) for x in self.args]
1206 def subst_kw(self, target, source, env):
1208 for key in self.kw.keys():
1209 kw[key] = self.subst(self.kw[key], target, source, env)
1212 def __call__(self, target, source, env, executor=None):
1213 args = self.subst_args(target, source, env)
1214 kw = self.subst_kw(target, source, env)
1215 #TODO(1.5) return self.parent.actfunc(*args, **kw)
1216 return self.parent.actfunc(*args, **kw)
1218 def strfunction(self, target, source, env):
1219 args = self.subst_args(target, source, env)
1220 kw = self.subst_kw(target, source, env)
1221 #TODO(1.5) return self.parent.strfunc(*args, **kw)
1222 return self.parent.strfunc(*args, **kw)
1225 #TODO(1.5) return self.parent.strfunc(*self.args, **self.kw)
1226 return self.parent.strfunc(*self.args, **self.kw)
1228 class ActionFactory:
1229 """A factory class that will wrap up an arbitrary function
1230 as an SCons-executable Action object.
1232 The real heavy lifting here is done by the ActionCaller class.
1233 We just collect the (positional and keyword) arguments that we're
1234 called with and give them to the ActionCaller object we create,
1235 so it can hang onto them until it needs them.
1237 def __init__(self, actfunc, strfunc, convert=lambda x: x):
1238 self.actfunc = actfunc
1239 self.strfunc = strfunc
1240 self.convert = convert
1242 def __call__(self, *args, **kw):
1243 ac = ActionCaller(self, args, kw)
1244 action = Action(ac, strfunction=ac.strfunction)
1249 # indent-tabs-mode:nil
1251 # vim: set expandtab tabstop=4 shiftwidth=4: