Package SCons :: Module Action
[hide private]
[frames] | no frames]

Source Code for Module SCons.Action

   1  """SCons.Action 
   2   
   3  This encapsulates information about executing any sort of action that 
   4  can build one or more target Nodes (typically files) from one or more 
   5  source Nodes (also typically files) given a specific Environment. 
   6   
   7  The base class here is ActionBase.  The base class supplies just a few 
   8  OO utility methods and some generic methods for displaying information 
   9  about an Action in response to the various commands that control printing. 
  10   
  11  A second-level base class is _ActionAction.  This extends ActionBase 
  12  by providing the methods that can be used to show and perform an 
  13  action.  True Action objects will subclass _ActionAction; Action 
  14  factory class objects will subclass ActionBase. 
  15   
  16  The heavy lifting is handled by subclasses for the different types of 
  17  actions we might execute: 
  18   
  19      CommandAction 
  20      CommandGeneratorAction 
  21      FunctionAction 
  22      ListAction 
  23   
  24  The subclasses supply the following public interface methods used by 
  25  other modules: 
  26   
  27      __call__() 
  28          THE public interface, "calling" an Action object executes the 
  29          command or Python function.  This also takes care of printing 
  30          a pre-substitution command for debugging purposes. 
  31   
  32      get_contents() 
  33          Fetches the "contents" of an Action for signature calculation. 
  34          This is what gets MD5 checksumm'ed to decide if a target needs 
  35          to be rebuilt because its action changed. 
  36   
  37      genstring() 
  38          Returns a string representation of the Action *without* 
  39          command substitution, but allows a CommandGeneratorAction to 
  40          generate the right action based on the specified target, 
  41          source and env.  This is used by the Signature subsystem 
  42          (through the Executor) to obtain an (imprecise) representation 
  43          of the Action operation for informative purposes. 
  44   
  45   
  46  Subclasses also supply the following methods for internal use within 
  47  this module: 
  48   
  49      __str__() 
  50          Returns a string approximation of the Action; no variable 
  51          substitution is performed. 
  52           
  53      execute() 
  54          The internal method that really, truly, actually handles the 
  55          execution of a command or Python function.  This is used so 
  56          that the __call__() methods can take care of displaying any 
  57          pre-substitution representations, and *then* execute an action 
  58          without worrying about the specific Actions involved. 
  59   
  60      strfunction() 
  61          Returns a substituted string representation of the Action. 
  62          This is used by the _ActionAction.show() command to display the 
  63          command/function that will be executed to generate the target(s). 
  64   
  65  There is a related independent ActionCaller class that looks like a 
  66  regular Action, and which serves as a wrapper for arbitrary functions 
  67  that we want to let the user specify the arguments to now, but actually 
  68  execute later (when an out-of-date check determines that it's needed to 
  69  be executed, for example).  Objects of this class are returned by an 
  70  ActionFactory class that provides a __call__() method as a convenient 
  71  way for wrapping up the functions. 
  72   
  73  """ 
  74   
  75  # 
  76  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008 The SCons Foundation 
  77  # 
  78  # Permission is hereby granted, free of charge, to any person obtaining 
  79  # a copy of this software and associated documentation files (the 
  80  # "Software"), to deal in the Software without restriction, including 
  81  # without limitation the rights to use, copy, modify, merge, publish, 
  82  # distribute, sublicense, and/or sell copies of the Software, and to 
  83  # permit persons to whom the Software is furnished to do so, subject to 
  84  # the following conditions: 
  85  # 
  86  # The above copyright notice and this permission notice shall be included 
  87  # in all copies or substantial portions of the Software. 
  88  # 
  89  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  90  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  91  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  92  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  93  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  94  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  95  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  96  # 
  97   
  98  __revision__ = "src/engine/SCons/Action.py 2928 2008/04/29 22:44:09 knight" 
  99   
 100  import cPickle 
 101  import dis 
 102  import os 
 103  import os.path 
 104  import string 
 105  import sys 
 106   
 107  from SCons.Debug import logInstanceCreation 
 108  import SCons.Errors 
 109  import SCons.Executor 
 110  import SCons.Util 
 111   
112 -class _Null:
113 pass
114 115 _null = _Null 116 117 print_actions = 1 118 execute_actions = 1 119 print_actions_presub = 0 120 121 default_ENV = None 122
123 -def rfile(n):
124 try: 125 return n.rfile() 126 except AttributeError: 127 return n
128
129 -def default_exitstatfunc(s):
130 return s
131 132 try: 133 SET_LINENO = dis.SET_LINENO 134 HAVE_ARGUMENT = dis.HAVE_ARGUMENT 135 except AttributeError: 136 remove_set_lineno_codes = lambda x: x 137 else:
138 - def remove_set_lineno_codes(code):
139 result = [] 140 n = len(code) 141 i = 0 142 while i < n: 143 c = code[i] 144 op = ord(c) 145 if op >= HAVE_ARGUMENT: 146 if op != SET_LINENO: 147 result.append(code[i:i+3]) 148 i = i+3 149 else: 150 result.append(c) 151 i = i+1 152 return string.join(result, '')
153 154
155 -def _callable_contents(obj):
156 """Return the signature contents of a callable Python object. 157 """ 158 try: 159 # Test if obj is a method. 160 return _function_contents(obj.im_func) 161 162 except AttributeError: 163 try: 164 # Test if obj is a callable object. 165 return _function_contents(obj.__call__.im_func) 166 167 except AttributeError: 168 try: 169 # Test if obj is a code object. 170 return _code_contents(obj) 171 172 except AttributeError: 173 # Test if obj is a function object. 174 return _function_contents(obj)
175 176
177 -def _object_contents(obj):
178 """Return the signature contents of any Python object. 179 180 We have to handle the case where object contains a code object 181 since it can be pickled directly. 182 """ 183 try: 184 # Test if obj is a method. 185 return _function_contents(obj.im_func) 186 187 except AttributeError: 188 try: 189 # Test if obj is a callable object. 190 return _function_contents(obj.__call__.im_func) 191 192 except AttributeError: 193 try: 194 # Test if obj is a code object. 195 return _code_contents(obj) 196 197 except AttributeError: 198 try: 199 # Test if obj is a function object. 200 return _function_contents(obj) 201 202 except AttributeError: 203 # Should be a pickable Python object. 204 try: 205 return cPickle.dumps(obj) 206 except (cPickle.PicklingError, TypeError): 207 # This is weird, but it seems that nested classes 208 # are unpickable. The Python docs say it should 209 # always be a PicklingError, but some Python 210 # versions seem to return TypeError. Just do 211 # the best we can. 212 return str(obj)
213 214
215 -def _code_contents(code):
216 """Return the signature contents of a code object. 217 218 By providing direct access to the code object of the 219 function, Python makes this extremely easy. Hooray! 220 221 Unfortunately, older versions of Python include line 222 number indications in the compiled byte code. Boo! 223 So we remove the line number byte codes to prevent 224 recompilations from moving a Python function. 225 """ 226 227 contents = [] 228 229 # The code contents depends on the number of local variables 230 # but not their actual names. 231 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) 232 try: 233 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) 234 except AttributeError: 235 # Older versions of Python do not support closures. 236 contents.append(",0,0") 237 238 # The code contents depends on any constants accessed by the 239 # function. Note that we have to call _object_contents on each 240 # constants because the code object of nested functions can 241 # show-up among the constants. 242 # 243 # Note that we also always ignore the first entry of co_consts 244 # which contains the function doc string. We assume that the 245 # function does not access its doc string. 246 contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')') 247 248 # The code contents depends on the variable names used to 249 # accessed global variable, as changing the variable name changes 250 # the variable actually accessed and therefore changes the 251 # function result. 252 contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')') 253 254 255 # The code contents depends on its actual code!!! 256 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') 257 258 return string.join(contents, '')
259 260
261 -def _function_contents(func):
262 """Return the signature contents of a function.""" 263 264 contents = [_code_contents(func.func_code)] 265 266 # The function contents depends on the value of defaults arguments 267 if func.func_defaults: 268 contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')') 269 else: 270 contents.append(',()') 271 272 # The function contents depends on the closure captured cell values. 273 try: 274 closure = func.func_closure or [] 275 except AttributeError: 276 # Older versions of Python do not support closures. 277 closure = [] 278 279 #xxx = [_object_contents(x.cell_contents) for x in closure] 280 xxx = map(lambda x: _object_contents(x.cell_contents), closure) 281 contents.append(',(' + string.join(xxx, ',') + ')') 282 283 return string.join(contents, '')
284 285
286 -def _actionAppend(act1, act2):
287 # This function knows how to slap two actions together. 288 # Mainly, it handles ListActions by concatenating into 289 # a single ListAction. 290 a1 = Action(act1) 291 a2 = Action(act2) 292 if a1 is None or a2 is None: 293 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2)) 294 if isinstance(a1, ListAction): 295 if isinstance(a2, ListAction): 296 return ListAction(a1.list + a2.list) 297 else: 298 return ListAction(a1.list + [ a2 ]) 299 else: 300 if isinstance(a2, ListAction): 301 return ListAction([ a1 ] + a2.list) 302 else: 303 return ListAction([ a1, a2 ])
304
305 -def _do_create_action(act, *args, **kw):
306 """This is the actual "implementation" for the 307 Action factory method, below. This handles the 308 fact that passing lists to Action() itself has 309 different semantics than passing lists as elements 310 of lists. 311 312 The former will create a ListAction, the latter 313 will create a CommandAction by converting the inner 314 list elements to strings.""" 315 316 if isinstance(act, ActionBase): 317 return act 318 if SCons.Util.is_List(act): 319 return apply(CommandAction, (act,)+args, kw) 320 if callable(act): 321 try: 322 gen = kw['generator'] 323 del kw['generator'] 324 except KeyError: 325 gen = 0 326 if gen: 327 action_type = CommandGeneratorAction 328 else: 329 action_type = FunctionAction 330 return apply(action_type, (act,)+args, kw) 331 if SCons.Util.is_String(act): 332 var=SCons.Util.get_environment_var(act) 333 if var: 334 # This looks like a string that is purely an Environment 335 # variable reference, like "$FOO" or "${FOO}". We do 336 # something special here...we lazily evaluate the contents 337 # of that Environment variable, so a user could put something 338 # like a function or a CommandGenerator in that variable 339 # instead of a string. 340 return apply(LazyAction, (var,)+args, kw) 341 commands = string.split(str(act), '\n') 342 if len(commands) == 1: 343 return apply(CommandAction, (commands[0],)+args, kw) 344 else: 345 listCmdActions = map(lambda x, args=args, kw=kw: 346 apply(CommandAction, (x,)+args, kw), 347 commands) 348 return ListAction(listCmdActions) 349 return None
350
351 -def Action(act, *args, **kw):
352 """A factory for action objects.""" 353 if SCons.Util.is_List(act): 354 acts = map(lambda a, args=args, kw=kw: 355 apply(_do_create_action, (a,)+args, kw), 356 act) 357 acts = filter(None, acts) 358 if len(acts) == 1: 359 return acts[0] 360 else: 361 return ListAction(acts) 362 else: 363 return apply(_do_create_action, (act,)+args, kw)
364
365 -class ActionBase:
366 """Base class for all types of action objects that can be held by 367 other objects (Builders, Executors, etc.) This provides the 368 common methods for manipulating and combining those actions.""" 369
370 - def __cmp__(self, other):
371 return cmp(self.__dict__, other)
372
373 - def genstring(self, target, source, env):
374 return str(self)
375
376 - def __add__(self, other):
377 return _actionAppend(self, other)
378
379 - def __radd__(self, other):
380 return _actionAppend(other, self)
381
382 - def presub_lines(self, env):
383 # CommandGeneratorAction needs a real environment 384 # in order to return the proper string here, since 385 # it may call LazyAction, which looks up a key 386 # in that env. So we temporarily remember the env here, 387 # and CommandGeneratorAction will use this env 388 # when it calls its _generate method. 389 self.presub_env = env 390 lines = string.split(str(self), '\n') 391 self.presub_env = None # don't need this any more 392 return lines
393
394 - def get_executor(self, env, overrides, tlist, slist, executor_kw):
395 """Return the Executor for this Action.""" 396 return SCons.Executor.Executor(self, env, overrides, 397 tlist, slist, executor_kw)
398
399 -class _ActionAction(ActionBase):
400 """Base class for actions that create output objects."""
401 - def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
402 if not strfunction is _null: 403 self.strfunction = strfunction 404 self.presub = presub 405 self.chdir = chdir 406 if not exitstatfunc: 407 exitstatfunc = default_exitstatfunc 408 self.exitstatfunc = exitstatfunc
409
410 - def print_cmd_line(self, s, target, source, env):
411 sys.stdout.write(s + "\n")
412
413 - def __call__(self, target, source, env, 414 exitstatfunc=_null, 415 presub=_null, 416 show=_null, 417 execute=_null, 418 chdir=_null):
419 if not SCons.Util.is_List(target): 420 target = [target] 421 if not SCons.Util.is_List(source): 422 source = [source] 423 424 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc 425 if presub is _null: 426 presub = self.presub 427 if presub is _null: 428 presub = print_actions_presub 429 if show is _null: show = print_actions 430