| Index: scons-2.0.1/engine/SCons/Action.py
|
| ===================================================================
|
| --- scons-2.0.1/engine/SCons/Action.py (revision 0)
|
| +++ scons-2.0.1/engine/SCons/Action.py (revision 0)
|
| @@ -0,0 +1,1241 @@
|
| +"""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
|
| + plus the varlist. This is what gets MD5 checksummed 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.
|
| +
|
| + get_presig()
|
| + Fetches the "contents" of a subclass for signature calculation.
|
| + The varlist is added to this to produce the Action's contents.
|
| +
|
| + 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, 2009, 2010 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 5134 2010/08/16 23:02:40 bdeegan"
|
| +
|
| +import SCons.compat
|
| +
|
| +import dis
|
| +import os
|
| +# compat layer imports "cPickle" for us if it's available.
|
| +import pickle
|
| +import re
|
| +import sys
|
| +import subprocess
|
| +
|
| +from SCons.Debug import logInstanceCreation
|
| +import SCons.Errors
|
| +import SCons.Executor
|
| +import SCons.Util
|
| +import SCons.Subst
|
| +
|
| +# we use these a lot, so try to optimize them
|
| +is_String = SCons.Util.is_String
|
| +is_List = SCons.Util.is_List
|
| +
|
| +class _null(object):
|
| + pass
|
| +
|
| +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 ''.join(result)
|
| +
|
| +strip_quotes = re.compile('^[\'"](.*)[\'"]$')
|
| +
|
| +
|
| +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 pickle.dumps(obj)
|
| + except (pickle.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(',(' + ','.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(',(' + ','.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 ''.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(',(' + ','.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 = [_object_contents(x.cell_contents) for x in closure]
|
| + except AttributeError:
|
| + xxx = []
|
| + contents.append(',(' + ','.join(xxx) + ')')
|
| +
|
| + return ''.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_keywords(args, kw):
|
| + """This converts any arguments after the action argument into
|
| + their equivalent keywords and adds them to the kw argument.
|
| + """
|
| + v = kw.get('varlist', ())
|
| + # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O']
|
| + if is_String(v): v = (v,)
|
| + kw['varlist'] = tuple(v)
|
| + if args:
|
| + # turn positional args into equivalent keywords
|
| + cmdstrfunc = args[0]
|
| + if cmdstrfunc is None or is_String(cmdstrfunc):
|
| + kw['cmdstr'] = cmdstrfunc
|
| + elif callable(cmdstrfunc):
|
| + kw['strfunction'] = cmdstrfunc
|
| + else:
|
| + 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.')
|
| + if len(args) > 1:
|
| + kw['varlist'] = args[1:] + kw['varlist']
|
| + if kw.get('strfunction', _null) is not _null \
|
| + and kw.get('cmdstr', _null) is not _null:
|
| + raise SCons.Errors.UserError(
|
| + 'Cannot have both strfunction and cmdstr args to Action()')
|
| +
|
| +def _do_create_action(act, 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 is_List(act):
|
| + return CommandAction(act, **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 action_type(act, kw)
|
| +
|
| + if 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 LazyAction(var, kw)
|
| + commands = str(act).split('\n')
|
| + if len(commands) == 1:
|
| + return CommandAction(commands[0], **kw)
|
| + # The list of string commands may include a LazyAction, so we
|
| + # reprocess them via _do_create_list_action.
|
| + return _do_create_list_action(commands, kw)
|
| + return None
|
| +
|
| +def _do_create_list_action(act, kw):
|
| + """A factory for list actions. Convert the input list into Actions
|
| + and then wrap them in a ListAction."""
|
| + acts = []
|
| + for a in act:
|
| + aa = _do_create_action(a, kw)
|
| + if aa is not None: acts.append(aa)
|
| + if not acts:
|
| + return ListAction([])
|
| + elif len(acts) == 1:
|
| + return acts[0]
|
| + else:
|
| + return ListAction(acts)
|
| +
|
| +def Action(act, *args, **kw):
|
| + """A factory for action objects."""
|
| + # Really simple: the _do_create_* routines do the heavy lifting.
|
| + _do_create_keywords(args, kw)
|
| + if is_List(act):
|
| + return _do_create_list_action(act, kw)
|
| + return _do_create_action(act, kw)
|
| +
|
| +class ActionBase(object):
|
| + """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 no_batch_key(self, env, target, source):
|
| + return None
|
| +
|
| + batch_key = no_batch_key
|
| +
|
| + def genstring(self, target, source, env):
|
| + return str(self)
|
| +
|
| + def get_contents(self, target, source, env):
|
| + result = [ self.get_presig(target, source, env) ]
|
| + # This should never happen, as the Action() factory should wrap
|
| + # the varlist, but just in case an action is created directly,
|
| + # we duplicate this check here.
|
| + vl = self.get_varlist(target, source, env)
|
| + if is_String(vl): vl = (vl,)
|
| + for v in vl:
|
| + result.append(env.subst('${'+v+'}'))
|
| + return ''.join(result)
|
| +
|
| + 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 = str(self).split('\n')
|
| + self.presub_env = None # don't need this any more
|
| + return lines
|
| +
|
| + def get_varlist(self, target, source, env, executor=None):
|
| + return self.varlist
|
| +
|
| + def get_targets(self, env, executor):
|
| + """
|
| + Returns the type of targets ($TARGETS, $CHANGED_TARGETS) used
|
| + by this action.
|
| + """
|
| + return self.targets
|
| +
|
| +class _ActionAction(ActionBase):
|
| + """Base class for actions that create output objects."""
|
| + def __init__(self, cmdstr=_null, strfunction=_null, varlist=(),
|
| + presub=_null, chdir=None, exitstatfunc=None,
|
| + batch_key=None, targets='$TARGETS',
|
| + **kw):
|
| + self.cmdstr = cmdstr
|
| + if strfunction is not _null:
|
| + if strfunction is None:
|
| + self.cmdstr = None
|
| + else:
|
| + self.strfunction = strfunction
|
| + self.varlist = varlist
|
| + self.presub = presub
|
| + self.chdir = chdir
|
| + if not exitstatfunc:
|
| + exitstatfunc = default_exitstatfunc
|
| + self.exitstatfunc = exitstatfunc
|
| +
|
| + self.targets = targets
|
| +
|
| + if batch_key:
|
| + if not callable(batch_key):
|
| + # They have set batch_key, but not to their own
|
| + # callable. The default behavior here will batch
|
| + # *all* targets+sources using this action, separated
|
| + # for each construction environment.
|
| + def default_batch_key(self, env, target, source):
|
| + return (id(self), id(env))
|
| + batch_key = default_batch_key
|
| + SCons.Util.AddMethod(self, batch_key, 'batch_key')
|
| +
|
| + def print_cmd_line(self, s, target, source, env):
|
| + sys.stdout.write(s + u"\n")
|
| +
|
| + def __call__(self, target, source, env,
|
| + exitstatfunc=_null,
|
| + presub=_null,
|
| + show=_null,
|
| + execute=_null,
|
| + chdir=_null,
|
| + executor=None):
|
| + if not is_List(target):
|
| + target = [target]
|
| + if not is_List(source):
|
| + source = [source]
|
| +
|
| + if presub is _null:
|
| + presub = self.presub
|
| + if presub is _null:
|
| + presub = print_actions_presub
|
| + if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
|
| + 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 is_String(chdir):
|
| + if executor:
|
| + chdir = str(executor.batches[0].targets[0].dir)
|
| + else:
|
| + chdir = str(target[0].dir)
|
| + if presub:
|
| + if executor:
|
| + target = executor.get_all_targets()
|
| + source = executor.get_all_sources()
|
| + t = ' and '.join(map(str, target))
|
| + l = '\n '.join(self.presub_lines(env))
|
| + out = u"Building %s with action:\n %s\n" % (t, l)
|
| + sys.stdout.write(out)
|
| + cmd = None
|
| + if show and self.strfunction:
|
| + if executor:
|
| + target = executor.get_all_targets()
|
| + source = executor.get_all_sources()
|
| + try:
|
| + cmd = self.strfunction(target, source, env, executor)
|
| + except TypeError:
|
| + cmd = self.strfunction(target, source, env)
|
| + if cmd:
|
| + if chdir:
|
| + cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
|
| + 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(cmd, target, source, env)
|
| + stat = 0
|
| + if execute:
|
| + if chdir:
|
| + os.chdir(chdir)
|
| + try:
|
| + stat = self.execute(target, source, env, executor=executor)
|
| + 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 cmd 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 ' '.join(cl)
|
| +
|
| +# A fiddlin' little function that has an 'import SCons.Environment' which
|
| +# can't be moved to the top level without creating an import loop. Since
|
| +# this import creates a local variable named 'SCons', it blocks access to
|
| +# the global variable, so we move it here to prevent complaints about local
|
| +# variables being used uninitialized.
|
| +default_ENV = None
|
| +def get_default_ENV(env):
|
| + global default_ENV
|
| + try:
|
| + return env['ENV']
|
| + except KeyError:
|
| + if not default_ENV:
|
| + import SCons.Environment
|
| + # This is a hideously expensive way to get a default shell
|
| + # environment. What it really should do is run the platform
|
| + # setup to get the default ENV. Fortunately, it's incredibly
|
| + # rare for an Environment not to have a shell environment, so
|
| + # we're not going to worry about it overmuch.
|
| + default_ENV = SCons.Environment.Environment()['ENV']
|
| + return default_ENV
|
| +
|
| +# 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 subprocess, but I'm sure
|
| +# it'll have to be tweaked to get the full desired functionality.
|
| +# one special arg (so far?), 'error', to tell what to do with exceptions.
|
| +def _subproc(scons_env, cmd, error = 'ignore', **kw):
|
| + """Do common setup for a subprocess.Popen() call"""
|
| + # allow std{in,out,err} to be "'devnull'"
|
| + io = kw.get('stdin')
|
| + if is_String(io) and io == 'devnull':
|
| + kw['stdin'] = open(os.devnull)
|
| + io = kw.get('stdout')
|
| + if is_String(io) and io == 'devnull':
|
| + kw['stdout'] = open(os.devnull, 'w')
|
| + io = kw.get('stderr')
|
| + if is_String(io) and io == 'devnull':
|
| + kw['stderr'] = open(os.devnull, 'w')
|
| +
|
| + # Figure out what shell environment to use
|
| + ENV = kw.get('env', None)
|
| + if ENV is None: ENV = get_default_ENV(scons_env)
|
| +
|
| + # Ensure that the ENV values are all strings:
|
| + new_env = {}
|
| + for key, value in ENV.items():
|
| + 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 = SCons.Util.flatten_sequence(value)
|
| + new_env[key] = os.pathsep.join(map(str, value))
|
| + else:
|
| + # It's either a string or something else. If it's a string,
|
| + # we still want to call str() because it might be a *Unicode*
|
| + # string, which makes subprocess.Popen() gag. 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:
|
| + new_env[key] = str(value)
|
| + kw['env'] = new_env
|
| +
|
| + try:
|
| + #FUTURE return subprocess.Popen(cmd, **kw)
|
| + return subprocess.Popen(cmd, **kw)
|
| + except EnvironmentError, e:
|
| + if error == 'raise': raise
|
| + # return a dummy Popen instance that only returns error
|
| + class dummyPopen(object):
|
| + def __init__(self, e): self.exception = e
|
| + def communicate(self): return ('','')
|
| + def wait(self): return -self.exception.errno
|
| + stdin = None
|
| + class f(object):
|
| + def read(self): return ''
|
| + def readline(self): return ''
|
| + stdout = stderr = f()
|
| + return dummyPopen(e)
|
| +
|
| +class CommandAction(_ActionAction):
|
| + """Class for command-execution actions."""
|
| + def __init__(self, cmd, **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')
|
| +
|
| + _ActionAction.__init__(self, **kw)
|
| + if is_List(cmd):
|
| + if list(filter(is_List, cmd)):
|
| + raise TypeError("CommandAction should be given only " \
|
| + "a single command")
|
| + self.cmd_list = cmd
|
| +
|
| + def __str__(self):
|
| + if is_List(self.cmd_list):
|
| + return ' '.join(map(str, self.cmd_list))
|
| + return str(self.cmd_list)
|
| +
|
| + def process(self, target, source, env, executor=None):
|
| + if executor:
|
| + result = env.subst_list(self.cmd_list, 0, executor=executor)
|
| + else:
|
| + result = env.subst_list(self.cmd_list, 0, target, source)
|
| + silent = None
|
| + ignore = None
|
| + while True:
|
| + 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, executor=None):
|
| + if self.cmdstr is None:
|
| + return None
|
| + if self.cmdstr is not _null:
|
| + from SCons.Subst import SUBST_RAW
|
| + if executor:
|
| + c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
|
| + else:
|
| + c = env.subst(self.cmdstr, SUBST_RAW, target, source)
|
| + if c:
|
| + return c
|
| + cmd_list, ignore, silent = self.process(target, source, env, executor)
|
| + if silent:
|
| + return ''
|
| + return _string_from_cmd_list(cmd_list[0])
|
| +
|
| + def execute(self, target, source, env, executor=None):
|
| + """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.
|
| + """
|
| + escape_list = SCons.Subst.escape_list
|
| + flatten_sequence = SCons.Util.flatten_sequence
|
| +
|
| + 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)
|
| +
|
| + ENV = get_default_ENV(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] = os.pathsep.join(map(str, value))
|
| + 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)
|
| +
|
| + if executor:
|
| + target = executor.get_all_targets()
|
| + source = executor.get_all_sources()
|
| + cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor)
|
| +
|
| + # 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_presig(self, target, source, env, executor=None):
|
| + """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 is_List(cmd):
|
| + cmd = ' '.join(map(str, cmd))
|
| + else:
|
| + cmd = str(cmd)
|
| + if executor:
|
| + return env.subst_target_source(cmd, SUBST_SIG, executor=executor)
|
| + else:
|
| + return env.subst_target_source(cmd, SUBST_SIG, target, source)
|
| +
|
| + def get_implicit_deps(self, target, source, env, executor=None):
|
| + icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
|
| + if 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
|
| + if executor:
|
| + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor)
|
| + else:
|
| + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
|
| + res = []
|
| + for cmd_line in cmd_list:
|
| + if cmd_line:
|
| + d = str(cmd_line[0])
|
| + m = strip_quotes.match(d)
|
| + if m:
|
| + d = m.group(1)
|
| + d = env.WhereIs(d)
|
| + if d:
|
| + res.append(env.fs.File(d))
|
| + return res
|
| +
|
| +class CommandGeneratorAction(ActionBase):
|
| + """Class for command-generator actions."""
|
| + def __init__(self, generator, kw):
|
| + if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
|
| + self.generator = generator
|
| + self.gen_kw = kw
|
| + self.varlist = kw.get('varlist', ())
|
| + self.targets = kw.get('targets', '$TARGETS')
|
| +
|
| + def _generate(self, target, source, env, for_signature, executor=None):
|
| + # ensure that target is a list, to make it easier to write
|
| + # generator functions:
|
| + if not is_List(target):
|
| + target = [target]
|
| +
|
| + if executor:
|
| + target = executor.get_all_targets()
|
| + source = executor.get_all_sources()
|
| + ret = self.generator(target=target,
|
| + source=source,
|
| + env=env,
|
| + for_signature=for_signature)
|
| + gen_cmd = Action(ret, **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 batch_key(self, env, target, source):
|
| + return self._generate(target, source, env, 1).batch_key(env, target, source)
|
| +
|
| + def genstring(self, target, source, env, executor=None):
|
| + return self._generate(target, source, env, 1, executor).genstring(target, source, env)
|
| +
|
| + def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
|
| + show=_null, execute=_null, chdir=_null, executor=None):
|
| + act = self._generate(target, source, env, 0, executor)
|
| + if act is None:
|
| + raise UserError("While building `%s': "
|
| + "Cannot deduce file extension from source files: %s"
|
| + % (repr(list(map(str, target))), repr(list(map(str, source)))))
|
| + return act(target, source, env, exitstatfunc, presub,
|
| + show, execute, chdir, executor)
|
| +
|
| + def get_presig(self, target, source, env, executor=None):
|
| + """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, executor).get_presig(target, source, env)
|
| +
|
| + def get_implicit_deps(self, target, source, env, executor=None):
|
| + return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
|
| +
|
| + def get_varlist(self, target, source, env, executor=None):
|
| + return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
|
| +
|
| + def get_targets(self, env, executor):
|
| + return self._generate(None, None, env, 1, executor).get_targets(env, executor)
|
| +
|
| +
|
| +
|
| +# 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, kw):
|
| + if __debug__: logInstanceCreation(self, 'Action.LazyAction')
|
| + #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw)
|
| + CommandAction.__init__(self, '${'+var+'}', **kw)
|
| + self.var = SCons.Util.to_String(var)
|
| + self.gen_kw = kw
|
| +
|
| + def get_parent_class(self, env):
|
| + c = env.get(self.var)
|
| + if is_String(c) and not '\n' in c:
|
| + return CommandAction
|
| + return CommandGeneratorAction
|
| +
|
| + def _generate_cache(self, env):
|
| + if env:
|
| + c = env.get(self.var, '')
|
| + else:
|
| + c = ''
|
| + gen_cmd = Action(c, **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, executor=None):
|
| + return self._generate_cache(env)
|
| +
|
| + def __call__(self, target, source, env, *args, **kw):
|
| + c = self.get_parent_class(env)
|
| + return c.__call__(self, target, source, env, *args, **kw)
|
| +
|
| + def get_presig(self, target, source, env):
|
| + c = self.get_parent_class(env)
|
| + return c.get_presig(self, target, source, env)
|
| +
|
| + def get_varlist(self, target, source, env, executor=None):
|
| + c = self.get_parent_class(env)
|
| + return c.get_varlist(self, target, source, env, executor)
|
| +
|
| +
|
| +class FunctionAction(_ActionAction):
|
| + """Class for Python function actions."""
|
| +
|
| + def __init__(self, execfunction, kw):
|
| + if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
|
| +
|
| + 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)
|
| +
|
| + _ActionAction.__init__(self, **kw)
|
| +
|
| + 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, executor=None):
|
| + if self.cmdstr is None:
|
| + return None
|
| + if self.cmdstr is not _null:
|
| + from SCons.Subst import SUBST_RAW
|
| + if executor:
|
| + c = env.subst(self.cmdstr, SUBST_RAW, executor=executor)
|
| + else:
|
| + 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 '[' + ", ".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, executor=None):
|
| + exc_info = (None,None,None)
|
| + try:
|
| + if executor:
|
| + target = executor.get_all_targets()
|
| + source = executor.get_all_sources()
|
| + rsources = list(map(rfile, source))
|
| + try:
|
| + result = self.execfunction(target=target, source=rsources, env=env)
|
| + except KeyboardInterrupt, e:
|
| + raise
|
| + except SystemExit, e:
|
| + raise
|
| + except Exception, e:
|
| + result = e
|
| + exc_info = sys.exc_info()
|
| +
|
| + if result:
|
| + result = SCons.Errors.convert_to_BuildError(result, exc_info)
|
| + result.node=target
|
| + result.action=self
|
| + try:
|
| + result.command=self.strfunction(target, source, env, executor)
|
| + except TypeError:
|
| + result.command=self.strfunction(target, source, env)
|
| +
|
| + # FIXME: This maintains backward compatibility with respect to
|
| + # which type of exceptions were returned by raising an
|
| + # exception and which ones were returned by value. It would
|
| + # probably be best to always return them by value here, but
|
| + # some codes do not check the return value of Actions and I do
|
| + # not have the time to modify them at this point.
|
| + if (exc_info[1] and
|
| + not isinstance(exc_info[1],EnvironmentError)):
|
| + raise result
|
| +
|
| + return result
|
| + finally:
|
| + # Break the cycle between the traceback object and this
|
| + # function stack frame. See the sys.exc_info() doc info for
|
| + # more information about this issue.
|
| + del exc_info
|
| +
|
| +
|
| + def get_presig(self, target, source, env):
|
| + """Return the signature contents of this callable action."""
|
| + try:
|
| + return self.gc(target, source, env)
|
| + except AttributeError:
|
| + return self.funccontents
|
| +
|
| + def get_implicit_deps(self, target, source, env):
|
| + return []
|
| +
|
| +class ListAction(ActionBase):
|
| + """Class for lists of other actions."""
|
| + def __init__(self, actionlist):
|
| + if __debug__: logInstanceCreation(self, 'Action.ListAction')
|
| + def list_of_actions(x):
|
| + if isinstance(x, ActionBase):
|
| + return x
|
| + return Action(x)
|
| + self.list = list(map(list_of_actions, actionlist))
|
| + # our children will have had any varlist
|
| + # applied; we don't need to do it again
|
| + self.varlist = ()
|
| + self.targets = '$TARGETS'
|
| +
|
| + def genstring(self, target, source, env):
|
| + return '\n'.join([a.genstring(target, source, env) for a in self.list])
|
| +
|
| + def __str__(self):
|
| + return '\n'.join(map(str, self.list))
|
| +
|
| + def presub_lines(self, env):
|
| + return SCons.Util.flatten_sequence(
|
| + [a.presub_lines(env) for a in self.list])
|
| +
|
| + def get_presig(self, target, source, env):
|
| + """Return the signature contents of this action list.
|
| +
|
| + Simple concatenation of the signatures of the elements.
|
| + """
|
| + return "".join([x.get_contents(target, source, env) for x in self.list])
|
| +
|
| + def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
|
| + show=_null, execute=_null, chdir=_null, executor=None):
|
| + if executor:
|
| + target = executor.get_all_targets()
|
| + source = executor.get_all_sources()
|
| + for act in self.list:
|
| + stat = act(target, source, env, exitstatfunc, presub,
|
| + show, execute, chdir, executor)
|
| + 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
|
| +
|
| + def get_varlist(self, target, source, env, executor=None):
|
| + result = SCons.Util.OrderedDict()
|
| + for act in self.list:
|
| + for var in act.get_varlist(target, source, env, executor):
|
| + result[var] = True
|
| + return list(result.keys())
|
| +
|
| +class ActionCaller(object):
|
| + """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 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 is_String(s):
|
| + return env.subst(s, 1, target, source)
|
| + return self.parent.convert(s)
|
| +
|
| + def subst_args(self, target, source, env):
|
| + return [self.subst(x, target, source, env) for x in 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, executor=None):
|
| + args = self.subst_args(target, source, env)
|
| + kw = self.subst_kw(target, source, env)
|
| + return 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 self.parent.strfunc(*args, **kw)
|
| +
|
| + def __str__(self):
|
| + return self.parent.strfunc(*self.args, **self.kw)
|
| +
|
| +class ActionFactory(object):
|
| + """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
|
| +
|
| +# Local Variables:
|
| +# tab-width:4
|
| +# indent-tabs-mode:nil
|
| +# End:
|
| +# vim: set expandtab tabstop=4 shiftwidth=4:
|
|
|
| Property changes on: scons-2.0.1/engine/SCons/Action.py
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|