summaryrefslogtreecommitdiffstats
path: root/third_party/scons/scons-local/SCons/Action.py
diff options
context:
space:
mode:
authorsgk@google.com <sgk@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-16 23:07:48 +0000
committersgk@google.com <sgk@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-16 23:07:48 +0000
commit84a0c0920ad28474b76d2030f57623e67f09d0a6 (patch)
tree48199dc282d49606741c31962473f4a49dc89d83 /third_party/scons/scons-local/SCons/Action.py
parent02460eeab5722da83aca8ebcf04885de6cd2a8ab (diff)
downloadchromium_src-84a0c0920ad28474b76d2030f57623e67f09d0a6.zip
chromium_src-84a0c0920ad28474b76d2030f57623e67f09d0a6.tar.gz
chromium_src-84a0c0920ad28474b76d2030f57623e67f09d0a6.tar.bz2
Update to the latest 1.0.1 SCons checkpoint.
The build engine (library) is getting added in a scons-local subdirectory, without version number, so we can track future changes, and more easily merge any local mods we've had to make in a pinch. Removing the old scons-local-0.98.3 directory will come seprately to avoid Rietveld limitations. Review URL: http://codereview.chromium.org/2902 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@2288 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/scons/scons-local/SCons/Action.py')
-rw-r--r--third_party/scons/scons-local/SCons/Action.py1075
1 files changed, 1075 insertions, 0 deletions
diff --git a/third_party/scons/scons-local/SCons/Action.py b/third_party/scons/scons-local/SCons/Action.py
new file mode 100644
index 0000000..bd89470
--- /dev/null
+++ b/third_party/scons/scons-local/SCons/Action.py
@@ -0,0 +1,1075 @@
+"""SCons.Action
+
+This encapsulates information about executing any sort of action that
+can build one or more target Nodes (typically files) from one or more
+source Nodes (also typically files) given a specific Environment.
+
+The base class here is ActionBase. The base class supplies just a few
+OO utility methods and some generic methods for displaying information
+about an Action in response to the various commands that control printing.
+
+A second-level base class is _ActionAction. This extends ActionBase
+by providing the methods that can be used to show and perform an
+action. True Action objects will subclass _ActionAction; Action
+factory class objects will subclass ActionBase.
+
+The heavy lifting is handled by subclasses for the different types of
+actions we might execute:
+
+ CommandAction
+ CommandGeneratorAction
+ FunctionAction
+ ListAction
+
+The subclasses supply the following public interface methods used by
+other modules:
+
+ __call__()
+ THE public interface, "calling" an Action object executes the
+ command or Python function. This also takes care of printing
+ a pre-substitution command for debugging purposes.
+
+ get_contents()
+ Fetches the "contents" of an Action for signature calculation.
+ This is what gets MD5 checksumm'ed to decide if a target needs
+ to be rebuilt because its action changed.
+
+ genstring()
+ Returns a string representation of the Action *without*
+ command substitution, but allows a CommandGeneratorAction to
+ generate the right action based on the specified target,
+ source and env. This is used by the Signature subsystem
+ (through the Executor) to obtain an (imprecise) representation
+ of the Action operation for informative purposes.
+
+
+Subclasses also supply the following methods for internal use within
+this module:
+
+ __str__()
+ Returns a string approximation of the Action; no variable
+ substitution is performed.
+
+ execute()
+ The internal method that really, truly, actually handles the
+ execution of a command or Python function. This is used so
+ that the __call__() methods can take care of displaying any
+ pre-substitution representations, and *then* execute an action
+ without worrying about the specific Actions involved.
+
+ strfunction()
+ Returns a substituted string representation of the Action.
+ This is used by the _ActionAction.show() command to display the
+ command/function that will be executed to generate the target(s).
+
+There is a related independent ActionCaller class that looks like a
+regular Action, and which serves as a wrapper for arbitrary functions
+that we want to let the user specify the arguments to now, but actually
+execute later (when an out-of-date check determines that it's needed to
+be executed, for example). Objects of this class are returned by an
+ActionFactory class that provides a __call__() method as a convenient
+way for wrapping up the functions.
+
+"""
+
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "src/engine/SCons/Action.py 3424 2008/09/15 11:22:20 scons"
+
+import cPickle
+import dis
+import os
+import os.path
+import string
+import sys
+import subprocess
+
+from SCons.Debug import logInstanceCreation
+import SCons.Errors
+import SCons.Executor
+import SCons.Util
+
+class _Null:
+ pass
+
+_null = _Null
+
+print_actions = 1
+execute_actions = 1
+print_actions_presub = 0
+
+def rfile(n):
+ try:
+ return n.rfile()
+ except AttributeError:
+ return n
+
+def default_exitstatfunc(s):
+ return s
+
+try:
+ SET_LINENO = dis.SET_LINENO
+ HAVE_ARGUMENT = dis.HAVE_ARGUMENT
+except AttributeError:
+ remove_set_lineno_codes = lambda x: x
+else:
+ def remove_set_lineno_codes(code):
+ result = []
+ n = len(code)
+ i = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ if op >= HAVE_ARGUMENT:
+ if op != SET_LINENO:
+ result.append(code[i:i+3])
+ i = i+3
+ else:
+ result.append(c)
+ i = i+1
+ return string.join(result, '')
+
+
+def _callable_contents(obj):
+ """Return the signature contents of a callable Python object.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+
+def _object_contents(obj):
+ """Return the signature contents of any Python object.
+
+ We have to handle the case where object contains a code object
+ since it can be pickled directly.
+ """
+ try:
+ # Test if obj is a method.
+ return _function_contents(obj.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a callable object.
+ return _function_contents(obj.__call__.im_func)
+
+ except AttributeError:
+ try:
+ # Test if obj is a code object.
+ return _code_contents(obj)
+
+ except AttributeError:
+ try:
+ # Test if obj is a function object.
+ return _function_contents(obj)
+
+ except AttributeError:
+ # Should be a pickable Python object.
+ try:
+ return cPickle.dumps(obj)
+ except (cPickle.PicklingError, TypeError):
+ # This is weird, but it seems that nested classes
+ # are unpickable. The Python docs say it should
+ # always be a PicklingError, but some Python
+ # versions seem to return TypeError. Just do
+ # the best we can.
+ return str(obj)
+
+
+def _code_contents(code):
+ """Return the signature contents of a code object.
+
+ By providing direct access to the code object of the
+ function, Python makes this extremely easy. Hooray!
+
+ Unfortunately, older versions of Python include line
+ number indications in the compiled byte code. Boo!
+ So we remove the line number byte codes to prevent
+ recompilations from moving a Python function.
+ """
+
+ contents = []
+
+ # The code contents depends on the number of local variables
+ # but not their actual names.
+ contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
+ try:
+ contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ contents.append(",0,0")
+
+ # The code contents depends on any constants accessed by the
+ # function. Note that we have to call _object_contents on each
+ # constants because the code object of nested functions can
+ # show-up among the constants.
+ #
+ # Note that we also always ignore the first entry of co_consts
+ # which contains the function doc string. We assume that the
+ # function does not access its doc string.
+ contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
+
+ # The code contents depends on the variable names used to
+ # accessed global variable, as changing the variable name changes
+ # the variable actually accessed and therefore changes the
+ # function result.
+ contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
+
+
+ # The code contents depends on its actual code!!!
+ contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
+
+ return string.join(contents, '')
+
+
+def _function_contents(func):
+ """Return the signature contents of a function."""
+
+ contents = [_code_contents(func.func_code)]
+
+ # The function contents depends on the value of defaults arguments
+ if func.func_defaults:
+ contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
+ else:
+ contents.append(',()')
+
+ # The function contents depends on the closure captured cell values.
+ try:
+ closure = func.func_closure or []
+ except AttributeError:
+ # Older versions of Python do not support closures.
+ closure = []
+
+ #xxx = [_object_contents(x.cell_contents) for x in closure]
+ try:
+ xxx = map(lambda x: _object_contents(x.cell_contents), closure)
+ except AttributeError:
+ xxx = []
+ contents.append(',(' + string.join(xxx, ',') + ')')
+
+ return string.join(contents, '')
+
+
+def _actionAppend(act1, act2):
+ # This function knows how to slap two actions together.
+ # Mainly, it handles ListActions by concatenating into
+ # a single ListAction.
+ a1 = Action(act1)
+ a2 = Action(act2)
+ if a1 is None or a2 is None:
+ raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
+ if isinstance(a1, ListAction):
+ if isinstance(a2, ListAction):
+ return ListAction(a1.list + a2.list)
+ else:
+ return ListAction(a1.list + [ a2 ])
+ else:
+ if isinstance(a2, ListAction):
+ return ListAction([ a1 ] + a2.list)
+ else:
+ return ListAction([ a1, a2 ])
+
+def _do_create_action(act, *args, **kw):
+ """This is the actual "implementation" for the
+ Action factory method, below. This handles the
+ fact that passing lists to Action() itself has
+ different semantics than passing lists as elements
+ of lists.
+
+ The former will create a ListAction, the latter
+ will create a CommandAction by converting the inner
+ list elements to strings."""
+
+ if isinstance(act, ActionBase):
+ return act
+ if SCons.Util.is_List(act):
+ return apply(CommandAction, (act,)+args, kw)
+ if callable(act):
+ try:
+ gen = kw['generator']
+ del kw['generator']
+ except KeyError:
+ gen = 0
+ if gen:
+ action_type = CommandGeneratorAction
+ else:
+ action_type = FunctionAction
+ return apply(action_type, (act,)+args, kw)
+ if SCons.Util.is_String(act):
+ var=SCons.Util.get_environment_var(act)
+ if var:
+ # This looks like a string that is purely an Environment
+ # variable reference, like "$FOO" or "${FOO}". We do
+ # something special here...we lazily evaluate the contents
+ # of that Environment variable, so a user could put something
+ # like a function or a CommandGenerator in that variable
+ # instead of a string.
+ return apply(LazyAction, (var,)+args, kw)
+ commands = string.split(str(act), '\n')
+ if len(commands) == 1:
+ return apply(CommandAction, (commands[0],)+args, kw)
+ else:
+ listCmdActions = map(lambda x, args=args, kw=kw:
+ apply(CommandAction, (x,)+args, kw),
+ commands)
+ return ListAction(listCmdActions)
+ return None
+
+def Action(act, *args, **kw):
+ """A factory for action objects."""
+ if SCons.Util.is_List(act):
+ acts = map(lambda a, args=args, kw=kw:
+ apply(_do_create_action, (a,)+args, kw),
+ act)
+ acts = filter(None, acts)
+ if len(acts) == 1:
+ return acts[0]
+ else:
+ return ListAction(acts)
+ else:
+ return apply(_do_create_action, (act,)+args, kw)
+
+class ActionBase:
+ """Base class for all types of action objects that can be held by
+ other objects (Builders, Executors, etc.) This provides the
+ common methods for manipulating and combining those actions."""
+
+ def __cmp__(self, other):
+ return cmp(self.__dict__, other)
+
+ def genstring(self, target, source, env):
+ return str(self)
+
+ def __add__(self, other):
+ return _actionAppend(self, other)
+
+ def __radd__(self, other):
+ return _actionAppend(other, self)
+
+ def presub_lines(self, env):
+ # CommandGeneratorAction needs a real environment
+ # in order to return the proper string here, since
+ # it may call LazyAction, which looks up a key
+ # in that env. So we temporarily remember the env here,
+ # and CommandGeneratorAction will use this env
+ # when it calls its _generate method.
+ self.presub_env = env
+ lines = string.split(str(self), '\n')
+ self.presub_env = None # don't need this any more
+ return lines
+
+ def get_executor(self, env, overrides, tlist, slist, executor_kw):
+ """Return the Executor for this Action."""
+ return SCons.Executor.Executor(self, env, overrides,
+ tlist, slist, executor_kw)
+
+class _ActionAction(ActionBase):
+ """Base class for actions that create output objects."""
+ def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
+ if not strfunction is _null:
+ self.strfunction = strfunction
+ self.presub = presub
+ self.chdir = chdir
+ if not exitstatfunc:
+ exitstatfunc = default_exitstatfunc
+ self.exitstatfunc = exitstatfunc
+
+ def print_cmd_line(self, s, target, source, env):
+ sys.stdout.write(s + "\n")
+
+ def __call__(self, target, source, env,
+ exitstatfunc=_null,
+ presub=_null,
+ show=_null,
+ execute=_null,
+ chdir=_null):
+ if not SCons.Util.is_List(target):
+ target = [target]
+ if not SCons.Util.is_List(source):
+ source = [source]
+
+ if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
+ if presub is _null:
+ presub = self.presub
+ if presub is _null:
+ presub = print_actions_presub
+ if show is _null: show = print_actions
+ if execute is _null: execute = execute_actions
+ if chdir is _null: chdir = self.chdir
+ save_cwd = None
+ if chdir:
+ save_cwd = os.getcwd()
+ try:
+ chdir = str(chdir.abspath)
+ except AttributeError:
+ if not SCons.Util.is_String(chdir):
+ chdir = str(target[0].dir)
+ if presub:
+ t = string.join(map(str, target), ' and ')
+ l = string.join(self.presub_lines(env), '\n ')
+ out = "Building %s with action:\n %s\n" % (t, l)
+ sys.stdout.write(out)
+ s = None
+ if show and self.strfunction:
+ s = self.strfunction(target, source, env)
+ if s:
+ if chdir:
+ s = ('os.chdir(%s)\n' % repr(chdir)) + s
+ try:
+ get = env.get
+ except AttributeError:
+ print_func = self.print_cmd_line
+ else:
+ print_func = get('PRINT_CMD_LINE_FUNC')
+ if not print_func:
+ print_func = self.print_cmd_line
+ print_func(s, target, source, env)
+ stat = 0
+ if execute:
+ if chdir:
+ os.chdir(chdir)
+ try:
+ stat = self.execute(target, source, env)
+ if isinstance(stat, SCons.Errors.BuildError):
+ s = exitstatfunc(stat.status)
+ if s:
+ stat.status = s
+ else:
+ stat = s
+ else:
+ stat = exitstatfunc(stat)
+ finally:
+ if save_cwd:
+ os.chdir(save_cwd)
+ if s and save_cwd:
+ print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
+
+ return stat
+
+
+def _string_from_cmd_list(cmd_list):
+ """Takes a list of command line arguments and returns a pretty
+ representation for printing."""
+ cl = []
+ for arg in map(str, cmd_list):
+ if ' ' in arg or '\t' in arg:
+ arg = '"' + arg + '"'
+ cl.append(arg)
+ return string.join(cl)
+
+# this function is still in draft mode. We're going to need something like
+# it in the long run as more and more places use it, but I'm sure it'll have
+# to be tweaked to get the full desired functionality.
+default_ENV = None
+# one special arg, 'error', to tell what to do with exceptions.
+def _subproc(env, cmd, error = 'ignore', **kw):
+ """Do setup for a subprocess.Popen() call"""
+
+ # If the env has no ENV, get a default
+ try:
+ ENV = env['ENV']
+ except KeyError:
+ global default_ENV
+ if default_ENV is None:
+ # Unbelievably expensive. What it really should do
+ # is run the platform setup to get the default ENV.
+ # Fortunately, it should almost never happen.
+ default_ENV = SCons.Environment.Environment(tools=[])['ENV']
+ ENV = default_ENV
+
+ # Ensure that the ENV values are all strings:
+ new_env = {}
+ # It's a string 99.44% of the time, so optimize this
+ is_String = SCons.Util.is_String
+ for key, value in ENV.items():
+ if is_String(value):
+ new_env[key] = value
+ elif SCons.Util.is_List(value):
+ # If the value is a list, then we assume it is a
+ # path list, because that's a pretty common list-like
+ # value to stick in an environment variable:
+ value = SCons.Util.flatten_sequence(value)
+ ENV[key] = string.join(map(str, value), os.pathsep)
+ else:
+ # If it isn't a string or a list, then we just coerce
+ # it to a string, which is the proper way to handle
+ # Dir and File instances and will produce something
+ # reasonable for just about everything else:
+ ENV[key] = str(value)
+ kw['env'] = new_env
+
+ try:
+ #FUTURE return subprocess.Popen(cmd, **kw)
+ return apply(subprocess.Popen, (cmd,), kw)
+ except EnvironmentError, e:
+ if error == 'raise': raise
+ # return a dummy Popen instance that only returns error
+ class popen:
+ def __init__(self, e): self.exception = e
+ def communicate(): return ('','')
+ def wait(): return -self.exception.errno
+ stdin = None
+ class f:
+ def read(self): return ''
+ def readline(self): return ''
+ stdout = stderr = f()
+ return popen(e)
+
+class CommandAction(_ActionAction):
+ """Class for command-execution actions."""
+ def __init__(self, cmd, cmdstr=None, *args, **kw):
+ # Cmd can actually be a list or a single item; if it's a
+ # single item it should be the command string to execute; if a
+ # list then it should be the words of the command string to
+ # execute. Only a single command should be executed by this
+ # object; lists of commands should be handled by embedding
+ # these objects in a ListAction object (which the Action()
+ # factory above does). cmd will be passed to
+ # Environment.subst_list() for substituting environment
+ # variables.
+ if __debug__: logInstanceCreation(self, 'Action.CommandAction')
+
+ if not cmdstr is None:
+ if callable(cmdstr):
+ args = (cmdstr,)+args
+ elif not SCons.Util.is_String(cmdstr):
+ raise SCons.Errors.UserError(\
+ 'Invalid command display variable type. ' \
+ 'You must either pass a string or a callback which ' \
+ 'accepts (target, source, env) as parameters.')
+
+ apply(_ActionAction.__init__, (self,)+args, kw)
+ if SCons.Util.is_List(cmd):
+ if filter(SCons.Util.is_List, cmd):
+ raise TypeError, "CommandAction should be given only " \
+ "a single command"
+ self.cmd_list = cmd
+ self.cmdstr = cmdstr
+
+ def __str__(self):
+ if SCons.Util.is_List(self.cmd_list):
+ return string.join(map(str, self.cmd_list), ' ')
+ return str(self.cmd_list)
+
+ def process(self, target, source, env):
+ result = env.subst_list(self.cmd_list, 0, target, source)
+ silent = None
+ ignore = None
+ while 1:
+ try: c = result[0][0][0]
+ except IndexError: c = None
+ if c == '@': silent = 1
+ elif c == '-': ignore = 1
+ else: break
+ result[0][0] = result[0][0][1:]
+ try:
+ if not result[0][0]:
+ result[0] = result[0][1:]
+ except IndexError:
+ pass
+ return result, ignore, silent
+
+ def strfunction(self, target, source, env):
+ if not self.cmdstr is None:
+ from SCons.Subst import SUBST_RAW
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ if c:
+ return c
+ cmd_list, ignore, silent = self.process(target, source, env)
+ if silent:
+ return ''
+ return _string_from_cmd_list(cmd_list[0])
+
+ def execute(self, target, source, env):
+ """Execute a command action.
+
+ This will handle lists of commands as well as individual commands,
+ because construction variable substitution may turn a single
+ "command" into a list. This means that this class can actually
+ handle lists of commands, even though that's not how we use it
+ externally.
+ """
+ from SCons.Subst import escape_list
+ import SCons.Util
+ flatten_sequence = SCons.Util.flatten_sequence
+ is_String = SCons.Util.is_String
+ is_List = SCons.Util.is_List
+
+ try:
+ shell = env['SHELL']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SHELL construction variable.')
+
+ try:
+ spawn = env['SPAWN']
+ except KeyError:
+ raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+ else:
+ if is_String(spawn):
+ spawn = env.subst(spawn, raw=1, conv=lambda x: x)
+
+ escape = env.get('ESCAPE', lambda x: x)
+
+ try:
+ ENV = env['ENV']
+ except KeyError:
+ global default_ENV
+ if not default_ENV:
+ import SCons.Environment
+ default_ENV = SCons.Environment.Environment()['ENV']
+ ENV = default_ENV
+
+ # Ensure that the ENV values are all strings:
+ for key, value in ENV.items():
+ if not is_String(value):
+ if is_List(value):
+ # If the value is a list, then we assume it is a
+ # path list, because that's a pretty common list-like
+ # value to stick in an environment variable:
+ value = flatten_sequence(value)
+ ENV[key] = string.join(map(str, value), os.pathsep)
+ else:
+ # If it isn't a string or a list, then we just coerce
+ # it to a string, which is the proper way to handle
+ # Dir and File instances and will produce something
+ # reasonable for just about everything else:
+ ENV[key] = str(value)
+
+ cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
+
+ # Use len() to filter out any "command" that's zero-length.
+ for cmd_line in filter(len, cmd_list):
+ # Escape the command line for the interpreter we are using.
+ cmd_line = escape_list(cmd_line, escape)
+ result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
+ if not ignore and result:
+ msg = "Error %s" % result
+ return SCons.Errors.BuildError(errstr=msg,
+ status=result,
+ action=self,
+ command=cmd_line)
+ return 0
+
+ def get_contents(self, target, source, env):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ from SCons.Subst import SUBST_SIG
+ cmd = self.cmd_list
+ if SCons.Util.is_List(cmd):
+ cmd = string.join(map(str, cmd))
+ else:
+ cmd = str(cmd)
+ return env.subst_target_source(cmd, SUBST_SIG, target, source)
+
+ def get_implicit_deps(self, target, source, env):
+ icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
+ if SCons.Util.is_String(icd) and icd[:1] == '$':
+ icd = env.subst(icd)
+ if not icd or icd in ('0', 'None'):
+ return []
+ from SCons.Subst import SUBST_SIG
+ cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
+ res = []
+ for cmd_line in cmd_list:
+ if cmd_line:
+ d = env.WhereIs(str(cmd_line[0]))
+ if d:
+ res.append(env.fs.File(d))
+ return res
+
+class CommandGeneratorAction(ActionBase):
+ """Class for command-generator actions."""
+ def __init__(self, generator, *args, **kw):
+ if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
+ self.generator = generator
+ self.gen_args = args
+ self.gen_kw = kw
+
+ def _generate(self, target, source, env, for_signature):
+ # ensure that target is a list, to make it easier to write
+ # generator functions:
+ if not SCons.Util.is_List(target):
+ target = [target]
+
+ ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
+ gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
+ return gen_cmd
+
+ def __str__(self):
+ try:
+ env = self.presub_env
+ except AttributeError:
+ env = None
+ if env is None:
+ env = SCons.Defaults.DefaultEnvironment()
+ act = self._generate([], [], env, 1)
+ return str(act)
+
+ def genstring(self, target, source, env):
+ return self._generate(target, source, env, 1).genstring(target, source, env)
+
+ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
+ show=_null, execute=_null, chdir=_null):
+ act = self._generate(target, source, env, 0)
+ return act(target, source, env, exitstatfunc, presub,
+ show, execute, chdir)
+
+ def get_contents(self, target, source, env):
+ """Return the signature contents of this action's command line.
+
+ This strips $(-$) and everything in between the string,
+ since those parts don't affect signatures.
+ """
+ return self._generate(target, source, env, 1).get_contents(target, source, env)
+
+ def get_implicit_deps(self, target, source, env):
+ return self._generate(target, source, env, 1).get_implicit_deps(target, source, env)
+
+
+
+# A LazyAction is a kind of hybrid generator and command action for
+# strings of the form "$VAR". These strings normally expand to other
+# strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
+# want to be able to replace them with functions in the construction
+# environment. Consequently, we want lazy evaluation and creation of
+# an Action in the case of the function, but that's overkill in the more
+# normal case of expansion to other strings.
+#
+# So we do this with a subclass that's both a generator *and*
+# a command action. The overridden methods all do a quick check
+# of the construction variable, and if it's a string we just call
+# the corresponding CommandAction method to do the heavy lifting.
+# If not, then we call the same-named CommandGeneratorAction method.
+# The CommandGeneratorAction methods work by using the overridden
+# _generate() method, that is, our own way of handling "generation" of
+# an action based on what's in the construction variable.
+
+class LazyAction(CommandGeneratorAction, CommandAction):
+
+ def __init__(self, var, *args, **kw):
+ if __debug__: logInstanceCreation(self, 'Action.LazyAction')
+ apply(CommandAction.__init__, (self, '$'+var)+args, kw)
+ self.var = SCons.Util.to_String(var)
+ self.gen_args = args
+ self.gen_kw = kw
+
+ def get_parent_class(self, env):
+ c = env.get(self.var)
+ if SCons.Util.is_String(c) and not '\n' in c:
+ return CommandAction
+ return CommandGeneratorAction
+
+ def _generate_cache(self, env):
+ c = env.get(self.var, '')
+ gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
+ return gen_cmd
+
+ def _generate(self, target, source, env, for_signature):
+ return self._generate_cache(env)
+
+ def __call__(self, target, source, env, *args, **kw):
+ args = (self, target, source, env) + args
+ c = self.get_parent_class(env)
+ return apply(c.__call__, args, kw)
+
+ def get_contents(self, target, source, env):
+ c = self.get_parent_class(env)
+ return c.get_contents(self, target, source, env)
+
+
+
+class FunctionAction(_ActionAction):
+ """Class for Python function actions."""
+
+ def __init__(self, execfunction, cmdstr=_null, *args, **kw):
+ if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
+
+ if not cmdstr is _null:
+ if callable(cmdstr):
+ args = (cmdstr,)+args
+ elif not (cmdstr is None or SCons.Util.is_String(cmdstr)):
+ raise SCons.Errors.UserError(\
+ 'Invalid function display variable type. ' \
+ 'You must either pass a string or a callback which ' \
+ 'accepts (target, source, env) as parameters.')
+
+ self.execfunction = execfunction
+ try:
+ self.funccontents = _callable_contents(execfunction)
+ except AttributeError:
+ try:
+ # See if execfunction will do the heavy lifting for us.
+ self.gc = execfunction.get_contents
+ except AttributeError:
+ # This is weird, just do the best we can.
+ self.funccontents = _object_contents(execfunction)
+
+ apply(_ActionAction.__init__, (self,)+args, kw)
+ self.varlist = kw.get('varlist', [])
+ if SCons.Util.is_String(self.varlist):
+ # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
+ self.varlist=[self.varlist]
+ self.cmdstr = cmdstr
+
+ def function_name(self):
+ try:
+ return self.execfunction.__name__
+ except AttributeError:
+ try:
+ return self.execfunction.__class__.__name__
+ except AttributeError:
+ return "unknown_python_function"
+
+ def strfunction(self, target, source, env):
+ if self.cmdstr is None:
+ return None
+ if not self.cmdstr is _null:
+ from SCons.Subst import SUBST_RAW
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
+ if c:
+ return c
+ def array(a):
+ def quote(s):
+ try:
+ str_for_display = s.str_for_display
+ except AttributeError:
+ s = repr(s)
+ else:
+ s = str_for_display()
+ return s
+ return '[' + string.join(map(quote, a), ", ") + ']'
+ try:
+ strfunc = self.execfunction.strfunction
+ except AttributeError:
+ pass
+ else:
+ if strfunc is None:
+ return None
+ if callable(strfunc):
+ return strfunc(target, source, env)
+ name = self.function_name()
+ tstr = array(target)
+ sstr = array(source)
+ return "%s(%s, %s)" % (name, tstr, sstr)
+
+ def __str__(self):
+ name = self.function_name()
+ if name == 'ActionCaller':
+ return str(self.execfunction)
+ return "%s(target, source, env)" % name
+
+ def execute(self, target, source, env):
+ rsources = map(rfile, source)
+ try:
+ result = self.execfunction(target=target, source=rsources, env=env)
+ except EnvironmentError, e:
+ # If an IOError/OSError happens, raise a BuildError.
+ # Report the name of the file or directory that caused the
+ # error, which might be different from the target being built
+ # (for example, failure to create the directory in which the
+ # target file will appear).
+ try: filename = e.filename
+ except AttributeError: filename = None
+ result = SCons.Errors.BuildError(node=target,
+ errstr=e.strerror,
+ status=1,
+ filename=filename,
+ action=self,
+ command=self.strfunction(target, source, env))
+ else:
+ if result:
+ msg = "Error %s" % result
+ result = SCons.Errors.BuildError(errstr=msg,
+ status=result,
+ action=self,
+ command=self.strfunction(target, source, env))
+ return result
+
+ def get_contents(self, target, source, env):
+ """Return the signature contents of this callable action."""
+ try:
+ contents = self.gc(target, source, env)
+ except AttributeError:
+ contents = self.funccontents
+
+ result = [contents]
+ for v in self.varlist:
+ result.append(env.subst('${'+v+'}'))
+
+ return string.join(result, '')
+
+ def get_implicit_deps(self, target, source, env):
+ return []
+
+class ListAction(ActionBase):
+ """Class for lists of other actions."""
+ def __init__(self, list):
+ if __debug__: logInstanceCreation(self, 'Action.ListAction')
+ def list_of_actions(x):
+ if isinstance(x, ActionBase):
+ return x
+ return Action(x)
+ self.list = map(list_of_actions, list)
+
+ def genstring(self, target, source, env):
+ return string.join(map(lambda a, t=target, s=source, e=env:
+ a.genstring(t, s, e),
+ self.list),
+ '\n')
+
+ def __str__(self):
+ return string.join(map(str, self.list), '\n')
+
+ def presub_lines(self, env):
+ return SCons.Util.flatten_sequence(
+ map(lambda a, env=env: a.presub_lines(env), self.list))
+
+ def get_contents(self, target, source, env):
+ """Return the signature contents of this action list.
+
+ Simple concatenation of the signatures of the elements.
+ """
+ return string.join(map(lambda x, t=target, s=source, e=env:
+ x.get_contents(t, s, e),
+ self.list),
+ "")
+
+ def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
+ show=_null, execute=_null, chdir=_null):
+ for act in self.list:
+ stat = act(target, source, env, exitstatfunc, presub,
+ show, execute, chdir)
+ if stat:
+ return stat
+ return 0
+
+ def get_implicit_deps(self, target, source, env):
+ result = []
+ for act in self.list:
+ result.extend(act.get_implicit_deps(target, source, env))
+ return result
+
+class ActionCaller:
+ """A class for delaying calling an Action function with specific
+ (positional and keyword) arguments until the Action is actually
+ executed.
+
+ This class looks to the rest of the world like a normal Action object,
+ but what it's really doing is hanging on to the arguments until we
+ have a target, source and env to use for the expansion.
+ """
+ def __init__(self, parent, args, kw):
+ self.parent = parent
+ self.args = args
+ self.kw = kw
+ def get_contents(self, target, source, env):
+ actfunc = self.parent.actfunc
+ try:
+ # "self.actfunc" is a function.
+ contents = str(actfunc.func_code.co_code)
+ except AttributeError:
+ # "self.actfunc" is a callable object.
+ try:
+ contents = str(actfunc.__call__.im_func.func_code.co_code)
+ except AttributeError:
+ # No __call__() method, so it might be a builtin
+ # or something like that. Do the best we can.
+ contents = str(actfunc)
+ contents = remove_set_lineno_codes(contents)
+ return contents
+ def subst(self, s, target, source, env):
+ # If s is a list, recursively apply subst()
+ # to every element in the list
+ if SCons.Util.is_List(s):
+ result = []
+ for elem in s:
+ result.append(self.subst(elem, target, source, env))
+ return self.parent.convert(result)
+
+ # Special-case hack: Let a custom function wrapped in an
+ # ActionCaller get at the environment through which the action
+ # was called by using this hard-coded value as a special return.
+ if s == '$__env__':
+ return env
+ elif SCons.Util.is_String(s):
+ return env.subst(s, 1, target, source)
+ return self.parent.convert(s)
+ def subst_args(self, target, source, env):
+ return map(lambda x, self=self, t=target, s=source, e=env:
+ self.subst(x, t, s, e),
+ self.args)
+ def subst_kw(self, target, source, env):
+ kw = {}
+ for key in self.kw.keys():
+ kw[key] = self.subst(self.kw[key], target, source, env)
+ return kw
+ def __call__(self, target, source, env):
+ args = self.subst_args(target, source, env)
+ kw = self.subst_kw(target, source, env)
+ return apply(self.parent.actfunc, args, kw)
+ def strfunction(self, target, source, env):
+ args = self.subst_args(target, source, env)
+ kw = self.subst_kw(target, source, env)
+ return apply(self.parent.strfunc, args, kw)
+ def __str__(self):
+ return apply(self.parent.strfunc, self.args, self.kw)
+
+class ActionFactory:
+ """A factory class that will wrap up an arbitrary function
+ as an SCons-executable Action object.
+
+ The real heavy lifting here is done by the ActionCaller class.
+ We just collect the (positional and keyword) arguments that we're
+ called with and give them to the ActionCaller object we create,
+ so it can hang onto them until it needs them.
+ """
+ def __init__(self, actfunc, strfunc, convert=lambda x: x):
+ self.actfunc = actfunc
+ self.strfunc = strfunc
+ self.convert = convert
+ def __call__(self, *args, **kw):
+ ac = ActionCaller(self, args, kw)
+ action = Action(ac, strfunction=ac.strfunction)
+ return action