Action.py
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:38k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. """SCons.Action
  2. This encapsulates information about executing any sort of action that
  3. can build one or more target Nodes (typically files) from one or more
  4. source Nodes (also typically files) given a specific Environment.
  5. The base class here is ActionBase.  The base class supplies just a few
  6. OO utility methods and some generic methods for displaying information
  7. about an Action in response to the various commands that control printing.
  8. A second-level base class is _ActionAction.  This extends ActionBase
  9. by providing the methods that can be used to show and perform an
  10. action.  True Action objects will subclass _ActionAction; Action
  11. factory class objects will subclass ActionBase.
  12. The heavy lifting is handled by subclasses for the different types of
  13. actions we might execute:
  14.     CommandAction
  15.     CommandGeneratorAction
  16.     FunctionAction
  17.     ListAction
  18. The subclasses supply the following public interface methods used by
  19. other modules:
  20.     __call__()
  21.         THE public interface, "calling" an Action object executes the
  22.         command or Python function.  This also takes care of printing
  23.         a pre-substitution command for debugging purposes.
  24.     get_contents()
  25.         Fetches the "contents" of an Action for signature calculation.
  26.         This is what gets MD5 checksumm'ed to decide if a target needs
  27.         to be rebuilt because its action changed.
  28.     genstring()
  29.         Returns a string representation of the Action *without*
  30.         command substitution, but allows a CommandGeneratorAction to
  31.         generate the right action based on the specified target,
  32.         source and env.  This is used by the Signature subsystem
  33.         (through the Executor) to obtain an (imprecise) representation
  34.         of the Action operation for informative purposes.
  35. Subclasses also supply the following methods for internal use within
  36. this module:
  37.     __str__()
  38.         Returns a string approximation of the Action; no variable
  39.         substitution is performed.
  40.         
  41.     execute()
  42.         The internal method that really, truly, actually handles the
  43.         execution of a command or Python function.  This is used so
  44.         that the __call__() methods can take care of displaying any
  45.         pre-substitution representations, and *then* execute an action
  46.         without worrying about the specific Actions involved.
  47.     strfunction()
  48.         Returns a substituted string representation of the Action.
  49.         This is used by the _ActionAction.show() command to display the
  50.         command/function that will be executed to generate the target(s).
  51. There is a related independent ActionCaller class that looks like a
  52. regular Action, and which serves as a wrapper for arbitrary functions
  53. that we want to let the user specify the arguments to now, but actually
  54. execute later (when an out-of-date check determines that it's needed to
  55. be executed, for example).  Objects of this class are returned by an
  56. ActionFactory class that provides a __call__() method as a convenient
  57. way for wrapping up the functions.
  58. """
  59. #
  60. # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation
  61. #
  62. # Permission is hereby granted, free of charge, to any person obtaining
  63. # a copy of this software and associated documentation files (the
  64. # "Software"), to deal in the Software without restriction, including
  65. # without limitation the rights to use, copy, modify, merge, publish,
  66. # distribute, sublicense, and/or sell copies of the Software, and to
  67. # permit persons to whom the Software is furnished to do so, subject to
  68. # the following conditions:
  69. #
  70. # The above copyright notice and this permission notice shall be included
  71. # in all copies or substantial portions of the Software.
  72. #
  73. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  74. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  75. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  76. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  77. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  78. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  79. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  80. #
  81. __revision__ = "src/engine/SCons/Action.py 3057 2008/06/09 22:21:00 knight"
  82. import cPickle
  83. import dis
  84. import os
  85. import os.path
  86. import string
  87. import sys
  88. from SCons.Debug import logInstanceCreation
  89. import SCons.Errors
  90. import SCons.Executor
  91. import SCons.Util
  92. class _Null:
  93.     pass
  94. _null = _Null
  95. print_actions = 1
  96. execute_actions = 1
  97. print_actions_presub = 0
  98. default_ENV = None
  99. def rfile(n):
  100.     try:
  101.         return n.rfile()
  102.     except AttributeError:
  103.         return n
  104. def default_exitstatfunc(s):
  105.     return s
  106. try:
  107.     SET_LINENO = dis.SET_LINENO
  108.     HAVE_ARGUMENT = dis.HAVE_ARGUMENT
  109. except AttributeError:
  110.     remove_set_lineno_codes = lambda x: x
  111. else:
  112.     def remove_set_lineno_codes(code):
  113.         result = []
  114.         n = len(code)
  115.         i = 0
  116.         while i < n:
  117.             c = code[i]
  118.             op = ord(c)
  119.             if op >= HAVE_ARGUMENT:
  120.                 if op != SET_LINENO:
  121.                     result.append(code[i:i+3])
  122.                 i = i+3
  123.             else:
  124.                 result.append(c)
  125.                 i = i+1
  126.         return string.join(result, '')
  127. def _callable_contents(obj):
  128.     """Return the signature contents of a callable Python object.
  129.     """
  130.     try:
  131.         # Test if obj is a method.
  132.         return _function_contents(obj.im_func)
  133.     except AttributeError:
  134.         try:
  135.             # Test if obj is a callable object.
  136.             return _function_contents(obj.__call__.im_func)
  137.         except AttributeError:
  138.             try:
  139.                 # Test if obj is a code object.
  140.                 return _code_contents(obj)
  141.             except AttributeError:
  142.                     # Test if obj is a function object.
  143.                     return _function_contents(obj)
  144. def _object_contents(obj):
  145.     """Return the signature contents of any Python object.
  146.     
  147.     We have to handle the case where object contains a code object
  148.     since it can be pickled directly.
  149.     """
  150.     try:
  151.         # Test if obj is a method.
  152.         return _function_contents(obj.im_func)
  153.     except AttributeError:
  154.         try:
  155.             # Test if obj is a callable object.
  156.             return _function_contents(obj.__call__.im_func)
  157.         except AttributeError:
  158.             try:
  159.                 # Test if obj is a code object.
  160.                 return _code_contents(obj)
  161.             except AttributeError:
  162.                 try:
  163.                     # Test if obj is a function object.
  164.                     return _function_contents(obj)
  165.                 except AttributeError:
  166.                     # Should be a pickable Python object. 
  167.                     try:
  168.                         return cPickle.dumps(obj)
  169.                     except (cPickle.PicklingError, TypeError):
  170.                         # This is weird, but it seems that nested classes
  171.                         # are unpickable. The Python docs say it should
  172.                         # always be a PicklingError, but some Python
  173.                         # versions seem to return TypeError.  Just do
  174.                         # the best we can.
  175.                         return str(obj)
  176. def _code_contents(code):
  177.     """Return the signature contents of a code object.
  178.     By providing direct access to the code object of the
  179.     function, Python makes this extremely easy.  Hooray!
  180.     
  181.     Unfortunately, older versions of Python include line
  182.     number indications in the compiled byte code.  Boo!
  183.     So we remove the line number byte codes to prevent
  184.     recompilations from moving a Python function.
  185.     """
  186.     contents = []
  187.     # The code contents depends on the number of local variables
  188.     # but not their actual names.
  189.     contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames)))
  190.     try:
  191.         contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars)))
  192.     except AttributeError:
  193.         # Older versions of Python do not support closures.
  194.         contents.append(",0,0")
  195.     # The code contents depends on any constants accessed by the
  196.     # function. Note that we have to call _object_contents on each
  197.     # constants because the code object of nested functions can
  198.     # show-up among the constants. 
  199.     # 
  200.     # Note that we also always ignore the first entry of co_consts
  201.     # which contains the function doc string. We assume that the
  202.     # function does not access its doc string.
  203.     contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')')
  204.                                  
  205.     # The code contents depends on the variable names used to
  206.     # accessed global variable, as changing the variable name changes
  207.     # the variable actually accessed and therefore changes the
  208.     # function result.
  209.     contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')')
  210.     # The code contents depends on its actual code!!!
  211.     contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')')
  212.     return string.join(contents, '')
  213. def _function_contents(func):
  214.     """Return the signature contents of a function."""
  215.     contents = [_code_contents(func.func_code)]
  216.     # The function contents depends on the value of defaults arguments
  217.     if func.func_defaults:
  218.         contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')')
  219.     else:
  220.         contents.append(',()')
  221.     # The function contents depends on the closure captured cell values.
  222.     try:
  223.         closure = func.func_closure or []
  224.     except AttributeError:
  225.         # Older versions of Python do not support closures.
  226.         closure = []
  227.     #xxx = [_object_contents(x.cell_contents) for x in closure]
  228.     try:
  229.         xxx = map(lambda x: _object_contents(x.cell_contents), closure)
  230.     except AttributeError:
  231.         xxx = []
  232.     contents.append(',(' + string.join(xxx, ',') + ')')
  233.     return string.join(contents, '')
  234.         
  235. def _actionAppend(act1, act2):
  236.     # This function knows how to slap two actions together.
  237.     # Mainly, it handles ListActions by concatenating into
  238.     # a single ListAction.
  239.     a1 = Action(act1)
  240.     a2 = Action(act2)
  241.     if a1 is None or a2 is None:
  242.         raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2))
  243.     if isinstance(a1, ListAction):
  244.         if isinstance(a2, ListAction):
  245.             return ListAction(a1.list + a2.list)
  246.         else:
  247.             return ListAction(a1.list + [ a2 ])
  248.     else:
  249.         if isinstance(a2, ListAction):
  250.             return ListAction([ a1 ] + a2.list)
  251.         else:
  252.             return ListAction([ a1, a2 ])
  253. def _do_create_action(act, *args, **kw):
  254.     """This is the actual "implementation" for the
  255.     Action factory method, below.  This handles the
  256.     fact that passing lists to Action() itself has
  257.     different semantics than passing lists as elements
  258.     of lists.
  259.     The former will create a ListAction, the latter
  260.     will create a CommandAction by converting the inner
  261.     list elements to strings."""
  262.     if isinstance(act, ActionBase):
  263.         return act
  264.     if SCons.Util.is_List(act):
  265.         return apply(CommandAction, (act,)+args, kw)
  266.     if callable(act):
  267.         try:
  268.             gen = kw['generator']
  269.             del kw['generator']
  270.         except KeyError:
  271.             gen = 0
  272.         if gen:
  273.             action_type = CommandGeneratorAction
  274.         else:
  275.             action_type = FunctionAction
  276.         return apply(action_type, (act,)+args, kw)
  277.     if SCons.Util.is_String(act):
  278.         var=SCons.Util.get_environment_var(act)
  279.         if var:
  280.             # This looks like a string that is purely an Environment
  281.             # variable reference, like "$FOO" or "${FOO}".  We do
  282.             # something special here...we lazily evaluate the contents
  283.             # of that Environment variable, so a user could put something
  284.             # like a function or a CommandGenerator in that variable
  285.             # instead of a string.
  286.             return apply(LazyAction, (var,)+args, kw)
  287.         commands = string.split(str(act), 'n')
  288.         if len(commands) == 1:
  289.             return apply(CommandAction, (commands[0],)+args, kw)
  290.         else:
  291.             listCmdActions = map(lambda x, args=args, kw=kw:
  292.                                  apply(CommandAction, (x,)+args, kw),
  293.                                  commands)
  294.             return ListAction(listCmdActions)
  295.     return None
  296. def Action(act, *args, **kw):
  297.     """A factory for action objects."""
  298.     if SCons.Util.is_List(act):
  299.         acts = map(lambda a, args=args, kw=kw:
  300.                           apply(_do_create_action, (a,)+args, kw),
  301.                    act)
  302.         acts = filter(None, acts)
  303.         if len(acts) == 1:
  304.             return acts[0]
  305.         else:
  306.             return ListAction(acts)
  307.     else:
  308.         return apply(_do_create_action, (act,)+args, kw)
  309. class ActionBase:
  310.     """Base class for all types of action objects that can be held by
  311.     other objects (Builders, Executors, etc.)  This provides the
  312.     common methods for manipulating and combining those actions."""
  313.     def __cmp__(self, other):
  314.         return cmp(self.__dict__, other)
  315.     def genstring(self, target, source, env):
  316.         return str(self)
  317.     def __add__(self, other):
  318.         return _actionAppend(self, other)
  319.     def __radd__(self, other):
  320.         return _actionAppend(other, self)
  321.     def presub_lines(self, env):
  322.         # CommandGeneratorAction needs a real environment
  323.         # in order to return the proper string here, since
  324.         # it may call LazyAction, which looks up a key
  325.         # in that env.  So we temporarily remember the env here,
  326.         # and CommandGeneratorAction will use this env
  327.         # when it calls its _generate method.
  328.         self.presub_env = env
  329.         lines = string.split(str(self), 'n')
  330.         self.presub_env = None      # don't need this any more
  331.         return lines
  332.     def get_executor(self, env, overrides, tlist, slist, executor_kw):
  333.         """Return the Executor for this Action."""
  334.         return SCons.Executor.Executor(self, env, overrides,
  335.                                        tlist, slist, executor_kw)
  336. class _ActionAction(ActionBase):
  337.     """Base class for actions that create output objects."""
  338.     def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
  339.         if not strfunction is _null:
  340.             self.strfunction = strfunction
  341.         self.presub = presub
  342.         self.chdir = chdir
  343.         if not exitstatfunc:
  344.             exitstatfunc = default_exitstatfunc
  345.         self.exitstatfunc = exitstatfunc
  346.     def print_cmd_line(self, s, target, source, env):
  347.         sys.stdout.write(s + "n")
  348.     def __call__(self, target, source, env,
  349.                                exitstatfunc=_null,
  350.                                presub=_null,
  351.                                show=_null,
  352.                                execute=_null,
  353.                                chdir=_null):
  354.         if not SCons.Util.is_List(target):
  355.             target = [target]
  356.         if not SCons.Util.is_List(source):
  357.             source = [source]
  358.         if exitstatfunc is _null: exitstatfunc = self.exitstatfunc
  359.         if presub is _null:
  360.             presub = self.presub
  361.         if presub is _null:
  362.             presub = print_actions_presub
  363.         if show is _null:  show = print_actions
  364.         if execute is _null:  execute = execute_actions
  365.         if chdir is _null: chdir = self.chdir
  366.         save_cwd = None
  367.         if chdir:
  368.             save_cwd = os.getcwd()
  369.             try:
  370.                 chdir = str(chdir.abspath)
  371.             except AttributeError:
  372.                 if not SCons.Util.is_String(chdir):
  373.                     chdir = str(target[0].dir)
  374.         if presub:
  375.             t = string.join(map(str, target), ' and ')
  376.             l = string.join(self.presub_lines(env), 'n  ')
  377.             out = "Building %s with action:n  %sn" % (t, l)
  378.             sys.stdout.write(out)
  379.         s = None
  380.         if show and self.strfunction:
  381.             s = self.strfunction(target, source, env)
  382.             if s:
  383.                 if chdir:
  384.                     s = ('os.chdir(%s)n' % repr(chdir)) + s
  385.                 try:
  386.                     get = env.get
  387.                 except AttributeError:
  388.                     print_func = self.print_cmd_line
  389.                 else:
  390.                     print_func = get('PRINT_CMD_LINE_FUNC')
  391.                     if not print_func:
  392.                         print_func = self.print_cmd_line
  393.                 print_func(s, target, source, env)
  394.         stat = 0
  395.         if execute:
  396.             if chdir:
  397.                 os.chdir(chdir)
  398.             try:
  399.                 stat = self.execute(target, source, env)
  400.                 if isinstance(stat, SCons.Errors.BuildError):
  401.                     s = exitstatfunc(stat.status)
  402.                     if s:
  403.                         stat.status = s
  404.                     else:
  405.                         stat = s
  406.                 else:
  407.                     stat = exitstatfunc(stat)
  408.             finally:
  409.                 if save_cwd:
  410.                     os.chdir(save_cwd)
  411.         if s and save_cwd:
  412.             print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
  413.         return stat
  414. def _string_from_cmd_list(cmd_list):
  415.     """Takes a list of command line arguments and returns a pretty
  416.     representation for printing."""
  417.     cl = []
  418.     for arg in map(str, cmd_list):
  419.         if ' ' in arg or 't' in arg:
  420.             arg = '"' + arg + '"'
  421.         cl.append(arg)
  422.     return string.join(cl)
  423. class CommandAction(_ActionAction):
  424.     """Class for command-execution actions."""
  425.     def __init__(self, cmd, cmdstr=None, *args, **kw):
  426.         # Cmd can actually be a list or a single item; if it's a
  427.         # single item it should be the command string to execute; if a
  428.         # list then it should be the words of the command string to
  429.         # execute.  Only a single command should be executed by this
  430.         # object; lists of commands should be handled by embedding
  431.         # these objects in a ListAction object (which the Action()
  432.         # factory above does).  cmd will be passed to
  433.         # Environment.subst_list() for substituting environment
  434.         # variables.
  435.         if __debug__: logInstanceCreation(self, 'Action.CommandAction')
  436.         if not cmdstr is None:
  437.             if callable(cmdstr):
  438.                 args = (cmdstr,)+args
  439.             elif not SCons.Util.is_String(cmdstr):
  440.                 raise SCons.Errors.UserError(
  441.                     'Invalid command display variable type. ' 
  442.                     'You must either pass a string or a callback which ' 
  443.                     'accepts (target, source, env) as parameters.')
  444.         apply(_ActionAction.__init__, (self,)+args, kw)
  445.         if SCons.Util.is_List(cmd):
  446.             if filter(SCons.Util.is_List, cmd):
  447.                 raise TypeError, "CommandAction should be given only " 
  448.                       "a single command"
  449.         self.cmd_list = cmd
  450.         self.cmdstr = cmdstr
  451.     def __str__(self):
  452.         if SCons.Util.is_List(self.cmd_list):
  453.             return string.join(map(str, self.cmd_list), ' ')
  454.         return str(self.cmd_list)
  455.     def process(self, target, source, env):
  456.         result = env.subst_list(self.cmd_list, 0, target, source)
  457.         silent = None
  458.         ignore = None
  459.         while 1:
  460.             try: c = result[0][0][0]
  461.             except IndexError: c = None
  462.             if c == '@': silent = 1
  463.             elif c == '-': ignore = 1
  464.             else: break
  465.             result[0][0] = result[0][0][1:]
  466.         try:
  467.             if not result[0][0]:
  468.                 result[0] = result[0][1:]
  469.         except IndexError:
  470.             pass
  471.         return result, ignore, silent
  472.     def strfunction(self, target, source, env):
  473.         if not self.cmdstr is None:
  474.             from SCons.Subst import SUBST_RAW
  475.             c = env.subst(self.cmdstr, SUBST_RAW, target, source)
  476.             if c:
  477.                 return c
  478.         cmd_list, ignore, silent = self.process(target, source, env)
  479.         if silent:
  480.             return ''
  481.         return _string_from_cmd_list(cmd_list[0])
  482.     def execute(self, target, source, env):
  483.         """Execute a command action.
  484.         This will handle lists of commands as well as individual commands,
  485.         because construction variable substitution may turn a single
  486.         "command" into a list.  This means that this class can actually
  487.         handle lists of commands, even though that's not how we use it
  488.         externally.
  489.         """
  490.         from SCons.Subst import escape_list
  491.         import SCons.Util
  492.         flatten_sequence = SCons.Util.flatten_sequence
  493.         is_String = SCons.Util.is_String
  494.         is_List = SCons.Util.is_List
  495.         try:
  496.             shell = env['SHELL']
  497.         except KeyError:
  498.             raise SCons.Errors.UserError('Missing SHELL construction variable.')
  499.         try:
  500.             spawn = env['SPAWN']
  501.         except KeyError:
  502.             raise SCons.Errors.UserError('Missing SPAWN construction variable.')
  503.         else:
  504.             if is_String(spawn):
  505.                 spawn = env.subst(spawn, raw=1, conv=lambda x: x)
  506.         escape = env.get('ESCAPE', lambda x: x)
  507.         try:
  508.             ENV = env['ENV']
  509.         except KeyError:
  510.             global default_ENV
  511.             if not default_ENV:
  512.                 import SCons.Environment
  513.                 default_ENV = SCons.Environment.Environment()['ENV']
  514.             ENV = default_ENV
  515.         # Ensure that the ENV values are all strings:
  516.         for key, value in ENV.items():
  517.             if not is_String(value):
  518.                 if is_List(value):
  519.                     # If the value is a list, then we assume it is a
  520.                     # path list, because that's a pretty common list-like
  521.                     # value to stick in an environment variable:
  522.                     value = flatten_sequence(value)
  523.                     ENV[key] = string.join(map(str, value), os.pathsep)
  524.                 else:
  525.                     # If it isn't a string or a list, then we just coerce
  526.                     # it to a string, which is the proper way to handle
  527.                     # Dir and File instances and will produce something
  528.                     # reasonable for just about everything else:
  529.                     ENV[key] = str(value)
  530.         cmd_list, ignore, silent = self.process(target, map(rfile, source), env)
  531.         # Use len() to filter out any "command" that's zero-length.
  532.         for cmd_line in filter(len, cmd_list):
  533.             # Escape the command line for the interpreter we are using.
  534.             cmd_line = escape_list(cmd_line, escape)
  535.             result = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
  536.             if not ignore and result:
  537.                 msg = "Error %s" % result
  538.                 return SCons.Errors.BuildError(errstr=msg,
  539.                                                status=result,
  540.                                                action=self,
  541.                                                command=cmd_line)
  542.         return 0
  543.     def get_contents(self, target, source, env):
  544.         """Return the signature contents of this action's command line.
  545.         This strips $(-$) and everything in between the string,
  546.         since those parts don't affect signatures.
  547.         """
  548.         from SCons.Subst import SUBST_SIG
  549.         cmd = self.cmd_list
  550.         if SCons.Util.is_List(cmd):
  551.             cmd = string.join(map(str, cmd))
  552.         else:
  553.             cmd = str(cmd)
  554.         return env.subst_target_source(cmd, SUBST_SIG, target, source)
  555.     def get_implicit_deps(self, target, source, env):
  556.         icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True)
  557.         if SCons.Util.is_String(icd) and icd[:1] == '$':
  558.             icd = env.subst(icd)
  559.         if not icd or icd in ('0', 'None'):
  560.             return []
  561.         from SCons.Subst import SUBST_SIG
  562.         cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source)
  563.         res = []
  564.         for cmd_line in cmd_list:
  565.             if cmd_line:
  566.                 d = env.WhereIs(str(cmd_line[0]))
  567.                 if d:
  568.                     res.append(env.fs.File(d))
  569.         return res
  570. class CommandGeneratorAction(ActionBase):
  571.     """Class for command-generator actions."""
  572.     def __init__(self, generator, *args, **kw):
  573.         if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction')
  574.         self.generator = generator
  575.         self.gen_args = args
  576.         self.gen_kw = kw
  577.     def _generate(self, target, source, env, for_signature):
  578.         # ensure that target is a list, to make it easier to write
  579.         # generator functions:
  580.         if not SCons.Util.is_List(target):
  581.             target = [target]
  582.         ret = self.generator(target=target, source=source, env=env, for_signature=for_signature)
  583.         gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw)
  584.         if not gen_cmd:
  585.             raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
  586.         return gen_cmd
  587.     def __str__(self):
  588.         try:
  589.             env = self.presub_env
  590.         except AttributeError:
  591.             env = None
  592.         if env is None:
  593.             env = SCons.Defaults.DefaultEnvironment()
  594.         act = self._generate([], [], env, 1)
  595.         return str(act)
  596.     def genstring(self, target, source, env):
  597.         return self._generate(target, source, env, 1).genstring(target, source, env)
  598.     def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
  599.                  show=_null, execute=_null, chdir=_null):
  600.         act = self._generate(target, source, env, 0)
  601.         return act(target, source, env, exitstatfunc, presub,
  602.                    show, execute, chdir)
  603.     def get_contents(self, target, source, env):
  604.         """Return the signature contents of this action's command line.
  605.         This strips $(-$) and everything in between the string,
  606.         since those parts don't affect signatures.
  607.         """
  608.         return self._generate(target, source, env, 1).get_contents(target, source, env)
  609.     def get_implicit_deps(self, target, source, env):
  610.         return self._generate(target, source, env, 1).get_implicit_deps(target, source, env)
  611. # A LazyAction is a kind of hybrid generator and command action for
  612. # strings of the form "$VAR".  These strings normally expand to other
  613. # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also
  614. # want to be able to replace them with functions in the construction
  615. # environment.  Consequently, we want lazy evaluation and creation of
  616. # an Action in the case of the function, but that's overkill in the more
  617. # normal case of expansion to other strings.
  618. #
  619. # So we do this with a subclass that's both a generator *and*
  620. # a command action.  The overridden methods all do a quick check
  621. # of the construction variable, and if it's a string we just call
  622. # the corresponding CommandAction method to do the heavy lifting.
  623. # If not, then we call the same-named CommandGeneratorAction method.
  624. # The CommandGeneratorAction methods work by using the overridden
  625. # _generate() method, that is, our own way of handling "generation" of
  626. # an action based on what's in the construction variable.
  627. class LazyAction(CommandGeneratorAction, CommandAction):
  628.     def __init__(self, var, *args, **kw):
  629.         if __debug__: logInstanceCreation(self, 'Action.LazyAction')
  630.         apply(CommandAction.__init__, (self, '$'+var)+args, kw)
  631.         self.var = SCons.Util.to_String(var)
  632.         self.gen_args = args
  633.         self.gen_kw = kw
  634.     def get_parent_class(self, env):
  635.         c = env.get(self.var)
  636.         if SCons.Util.is_String(c) and not 'n' in c:
  637.             return CommandAction
  638.         return CommandGeneratorAction
  639.     def _generate_cache(self, env):
  640.         c = env.get(self.var, '')
  641.         gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw)
  642.         if not gen_cmd:
  643.             raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c)))
  644.         return gen_cmd
  645.     def _generate(self, target, source, env, for_signature):
  646.         return self._generate_cache(env)
  647.     def __call__(self, target, source, env, *args, **kw):
  648.         args = (self, target, source, env) + args
  649.         c = self.get_parent_class(env)
  650.         return apply(c.__call__, args, kw)
  651.     def get_contents(self, target, source, env):
  652.         c = self.get_parent_class(env)
  653.         return c.get_contents(self, target, source, env)
  654. class FunctionAction(_ActionAction):
  655.     """Class for Python function actions."""
  656.     def __init__(self, execfunction, cmdstr=_null, *args, **kw):
  657.         if __debug__: logInstanceCreation(self, 'Action.FunctionAction')
  658.         if not cmdstr is _null:
  659.             if callable(cmdstr):
  660.                 args = (cmdstr,)+args
  661.             elif not (cmdstr is None or SCons.Util.is_String(cmdstr)):
  662.                 raise SCons.Errors.UserError(
  663.                     'Invalid function display variable type. ' 
  664.                     'You must either pass a string or a callback which ' 
  665.                     'accepts (target, source, env) as parameters.')
  666.         self.execfunction = execfunction
  667.         try:
  668.             self.funccontents = _callable_contents(execfunction)
  669.         except AttributeError:
  670.             try:
  671.                 # See if execfunction will do the heavy lifting for us.
  672.                 self.gc = execfunction.get_contents
  673.             except AttributeError:
  674.                 # This is weird, just do the best we can.
  675.                 self.funccontents = _object_contents(execfunction)
  676.         apply(_ActionAction.__init__, (self,)+args, kw)
  677.         self.varlist = kw.get('varlist', [])
  678.         self.cmdstr = cmdstr
  679.     def function_name(self):
  680.         try:
  681.             return self.execfunction.__name__
  682.         except AttributeError:
  683.             try:
  684.                 return self.execfunction.__class__.__name__
  685.             except AttributeError:
  686.                 return "unknown_python_function"
  687.     def strfunction(self, target, source, env):
  688.         if self.cmdstr is None:
  689.             return None
  690.         if not self.cmdstr is _null:
  691.             from SCons.Subst import SUBST_RAW
  692.             c = env.subst(self.cmdstr, SUBST_RAW, target, source)
  693.             if c:
  694.                 return c
  695.         def array(a):
  696.             def quote(s):
  697.                 try:
  698.                     str_for_display = s.str_for_display
  699.                 except AttributeError:
  700.                     s = repr(s)
  701.                 else:
  702.                     s = str_for_display()
  703.                 return s
  704.             return '[' + string.join(map(quote, a), ", ") + ']'
  705.         try:
  706.             strfunc = self.execfunction.strfunction
  707.         except AttributeError:
  708.             pass
  709.         else:
  710.             if strfunc is None:
  711.                 return None
  712.             if callable(strfunc):
  713.                 return strfunc(target, source, env)
  714.         name = self.function_name()
  715.         tstr = array(target)
  716.         sstr = array(source)
  717.         return "%s(%s, %s)" % (name, tstr, sstr)
  718.     def __str__(self):
  719.         name = self.function_name()
  720.         if name == 'ActionCaller':
  721.             return str(self.execfunction)
  722.         return "%s(target, source, env)" % name
  723.     def execute(self, target, source, env):
  724.         rsources = map(rfile, source)
  725.         try:
  726.             result = self.execfunction(target=target, source=rsources, env=env)
  727.         except EnvironmentError, e:
  728.             # If an IOError/OSError happens, raise a BuildError.
  729.             # Report the name of the file or directory that caused the
  730.             # error, which might be different from the target being built
  731.             # (for example, failure to create the directory in which the
  732.             # target file will appear).
  733.             try: filename = e.filename
  734.             except AttributeError: filename = None
  735.             result = SCons.Errors.BuildError(node=target,
  736.                                              errstr=e.strerror,
  737.                                              status=1,
  738.                                              filename=filename,
  739.                                              action=self,
  740.                                              command=self.strfunction(target, source, env))
  741.         else:
  742.             if result:
  743.                 msg = "Error %s" % result
  744.                 result = SCons.Errors.BuildError(errstr=msg,
  745.                                                  status=result,
  746.                                                  action=self,
  747.                                                  command=self.strfunction(target, source, env))
  748.         return result
  749.     def get_contents(self, target, source, env):
  750.         """Return the signature contents of this callable action."""
  751.         try:
  752.             contents = self.gc(target, source, env)
  753.         except AttributeError:
  754.             contents = self.funccontents
  755.         result = [contents]
  756.         for v in self.varlist:
  757.             result.append(env.subst('${'+v+'}'))
  758.         return string.join(result, '')
  759.     def get_implicit_deps(self, target, source, env):
  760.         return []
  761. class ListAction(ActionBase):
  762.     """Class for lists of other actions."""
  763.     def __init__(self, list):
  764.         if __debug__: logInstanceCreation(self, 'Action.ListAction')
  765.         def list_of_actions(x):
  766.             if isinstance(x, ActionBase):
  767.                 return x
  768.             return Action(x)
  769.         self.list = map(list_of_actions, list)
  770.     def genstring(self, target, source, env):
  771.         return string.join(map(lambda a, t=target, s=source, e=env:
  772.                                   a.genstring(t, s, e),
  773.                                self.list),
  774.                            'n')
  775.     def __str__(self):
  776.         return string.join(map(str, self.list), 'n')
  777.     
  778.     def presub_lines(self, env):
  779.         return SCons.Util.flatten_sequence(
  780.             map(lambda a, env=env: a.presub_lines(env), self.list))
  781.     def get_contents(self, target, source, env):
  782.         """Return the signature contents of this action list.
  783.         Simple concatenation of the signatures of the elements.
  784.         """
  785.         return string.join(map(lambda x, t=target, s=source, e=env:
  786.                                       x.get_contents(t, s, e),
  787.                                self.list),
  788.                            "")
  789.     def __call__(self, target, source, env, exitstatfunc=_null, presub=_null,
  790.                  show=_null, execute=_null, chdir=_null):
  791.         for act in self.list:
  792.             stat = act(target, source, env, exitstatfunc, presub,
  793.                        show, execute, chdir)
  794.             if stat:
  795.                 return stat
  796.         return 0
  797.     def get_implicit_deps(self, target, source, env):
  798.         result = []
  799.         for act in self.list:
  800.             result.extend(act.get_implicit_deps(target, source, env))
  801.         return result
  802. class ActionCaller:
  803.     """A class for delaying calling an Action function with specific
  804.     (positional and keyword) arguments until the Action is actually
  805.     executed.
  806.     This class looks to the rest of the world like a normal Action object,
  807.     but what it's really doing is hanging on to the arguments until we
  808.     have a target, source and env to use for the expansion.
  809.     """
  810.     def __init__(self, parent, args, kw):
  811.         self.parent = parent
  812.         self.args = args
  813.         self.kw = kw
  814.     def get_contents(self, target, source, env):
  815.         actfunc = self.parent.actfunc
  816.         try:
  817.             # "self.actfunc" is a function.
  818.             contents = str(actfunc.func_code.co_code)
  819.         except AttributeError:
  820.             # "self.actfunc" is a callable object.
  821.             try:
  822.                 contents = str(actfunc.__call__.im_func.func_code.co_code)
  823.             except AttributeError:
  824.                 # No __call__() method, so it might be a builtin
  825.                 # or something like that.  Do the best we can.
  826.                 contents = str(actfunc)
  827.         contents = remove_set_lineno_codes(contents)
  828.         return contents
  829.     def subst(self, s, target, source, env):
  830.         # If s is a list, recursively apply subst()
  831.         # to every element in the list
  832.         if SCons.Util.is_List(s):
  833.             result = []
  834.             for elem in s:
  835.                 result.append(self.subst(elem, target, source, env))
  836.             return self.parent.convert(result)
  837.         # Special-case hack:  Let a custom function wrapped in an
  838.         # ActionCaller get at the environment through which the action
  839.         # was called by using this hard-coded value as a special return.
  840.         if s == '$__env__':
  841.             return env
  842.         elif SCons.Util.is_String(s):
  843.             return env.subst(s, 1, target, source)
  844.         return self.parent.convert(s)
  845.     def subst_args(self, target, source, env):
  846.         return map(lambda x, self=self, t=target, s=source, e=env:
  847.                           self.subst(x, t, s, e),
  848.                    self.args)
  849.     def subst_kw(self, target, source, env):
  850.         kw = {}
  851.         for key in self.kw.keys():
  852.             kw[key] = self.subst(self.kw[key], target, source, env)
  853.         return kw
  854.     def __call__(self, target, source, env):
  855.         args = self.subst_args(target, source, env)
  856.         kw = self.subst_kw(target, source, env)
  857.         return apply(self.parent.actfunc, args, kw)
  858.     def strfunction(self, target, source, env):
  859.         args = self.subst_args(target, source, env)
  860.         kw = self.subst_kw(target, source, env)
  861.         return apply(self.parent.strfunc, args, kw)
  862.     def __str__(self):
  863.         return apply(self.parent.strfunc, self.args, self.kw)
  864. class ActionFactory:
  865.     """A factory class that will wrap up an arbitrary function
  866.     as an SCons-executable Action object.
  867.     The real heavy lifting here is done by the ActionCaller class.
  868.     We just collect the (positional and keyword) arguments that we're
  869.     called with and give them to the ActionCaller object we create,
  870.     so it can hang onto them until it needs them.
  871.     """
  872.     def __init__(self, actfunc, strfunc, convert=lambda x: x):
  873.         self.actfunc = actfunc
  874.         self.strfunc = strfunc
  875.         self.convert = convert
  876.     def __call__(self, *args, **kw):
  877.         ac = ActionCaller(self, args, kw)
  878.         action = Action(ac, strfunction=ac.strfunction)
  879.         return action