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 if execute is _null: execute = execute_actions 431 if chdir is _null: chdir = self.chdir 432 save_cwd = None 433 if chdir: 434 save_cwd = os.getcwd() 435 try: 436 chdir = str(chdir.abspath) 437 except AttributeError: 438 if not SCons.Util.is_String(chdir): 439 chdir = str(target[0].dir) 440 if presub: 441 t = string.join(map(str, target), ' and ') 442 l = string.join(self.presub_lines(env), '\n ') 443 out = "Building %s with action:\n %s\n" % (t, l) 444 sys.stdout.write(out) 445 s = None 446 if show and self.strfunction: 447 s = self.strfunction(target, source, env) 448 if s: 449 if chdir: 450 s = ('os.chdir(%s)\n' % repr(chdir)) + s 451 try: 452 get = env.get 453 except AttributeError: 454 print_func = self.print_cmd_line 455 else: 456 print_func = get('PRINT_CMD_LINE_FUNC') 457 if not print_func: 458 print_func = self.print_cmd_line 459 print_func(s, target, source, env) 460 stat = 0 461 if execute: 462 if chdir: 463 os.chdir(chdir) 464 try: 465 stat = self.execute(target, source, env) 466 if isinstance(stat, SCons.Errors.BuildError): 467 s = exitstatfunc(stat.status) 468 if s: 469 stat.status = s 470 else: 471 stat = s 472 else: 473 stat = exitstatfunc(stat) 474 finally: 475 if save_cwd: 476 os.chdir(save_cwd) 477 if s and save_cwd: 478 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) 479 480 return stat
481 482
483 -def _string_from_cmd_list(cmd_list):
484 """Takes a list of command line arguments and returns a pretty 485 representation for printing.""" 486 cl = [] 487 for arg in map(str, cmd_list): 488 if ' ' in arg or '\t' in arg: 489 arg = '"' + arg + '"' 490 cl.append(arg) 491 return string.join(cl)
492
493 -class CommandAction(_ActionAction):
494 """Class for command-execution actions."""
495 - def __init__(self, cmd, cmdstr=None, *args, **kw):
496 # Cmd can actually be a list or a single item; if it's a 497 # single item it should be the command string to execute; if a 498 # list then it should be the words of the command string to 499 # execute. Only a single command should be executed by this 500 # object; lists of commands should be handled by embedding 501 # these objects in a ListAction object (which the Action() 502 # factory above does). cmd will be passed to 503 # Environment.subst_list() for substituting environment 504 # variables. 505 if __debug__: logInstanceCreation(self, 'Action.CommandAction') 506 507 if not cmdstr is None: 508 if callable(cmdstr): 509 args = (cmdstr,)+args 510 elif not SCons.Util.is_String(cmdstr): 511 raise SCons.Errors.UserError(\ 512 'Invalid command display variable type. ' \ 513 'You must either pass a string or a callback which ' \ 514 'accepts (target, source, env) as parameters.') 515 516 apply(_ActionAction.__init__, (self,)+args, kw) 517 if SCons.Util.is_List(cmd): 518 if filter(SCons.Util.is_List, cmd): 519 raise TypeError, "CommandAction should be given only " \ 520 "a single command" 521 self.cmd_list = cmd 522 self.cmdstr = cmdstr
523
524 - def __str__(self):
525 if SCons.Util.is_List(self.cmd_list): 526 return string.join(map(str, self.cmd_list), ' ') 527 return str(self.cmd_list)
528
529 - def process(self, target, source, env):
530 result = env.subst_list(self.cmd_list, 0, target, source) 531 silent = None 532 ignore = None 533 while 1: 534 try: c = result[0][0][0] 535 except IndexError: c = None 536 if c == '@': silent = 1 537 elif c == '-': ignore = 1 538 else: break 539 result[0][0] = result[0][0][1:] 540 try: 541 if not result[0][0]: 542 result[0] = result[0][1:] 543 except IndexError: 544 pass 545 return result, ignore, silent
546
547 - def strfunction(self, target, source, env):
548 if not self.cmdstr is None: 549 from SCons.Subst import SUBST_RAW 550 c = env.subst(self.cmdstr, SUBST_RAW, target, source) 551 if c: 552 return c 553 cmd_list, ignore, silent = self.process(target, source, env) 554 if silent: 555 return '' 556 return _string_from_cmd_list(cmd_list[0])
557
558 - def execute(self, target, source, env):
559 """Execute a command action. 560 561 This will handle lists of commands as well as individual commands, 562 because construction variable substitution may turn a single 563 "command" into a list. This means that this class can actually 564 handle lists of commands, even though that's not how we use it 565 externally. 566 """ 567 from SCons.Subst import escape_list 568 import SCons.Util 569 flatten_sequence = SCons.Util.flatten_sequence 570 is_String = SCons.Util.is_String 571 is_List = SCons.Util.is_List 572 573 try: 574 shell = env['SHELL'] 575 except KeyError: 576 raise SCons.Errors.UserError('Missing SHELL construction variable.') 577 578 try: 579 spawn = env['SPAWN'] 580 except KeyError: 581 raise SCons.Errors.UserError('Missing SPAWN construction variable.') 582 else: 583 if is_String(spawn): 584 spawn = env.subst(spawn, raw=1, conv=lambda x: x) 585 586 escape = env.get('ESCAPE', lambda x: x) 587 588 try: 589 ENV = env['ENV'] 590 except KeyError: 591 global default_ENV 592 if not default_ENV: 593 import SCons.Environment 594 default_ENV = SCons.Environment.Environment()['ENV'] 595 ENV = default_ENV 596 597 # Ensure that the ENV values are all strings: 598 for key, value in ENV.items(): 599 if not is_String(value): 600 if is_List(value): 601 # If the value is a list, then we assume it is a 602 # path list, because that's a pretty common list-like 603 # value to stick in an environment variable: 604 value = flatten_sequence(value) 605 ENV[key] = string.join(map(str, value), os.pathsep) 606 else: 607 # If it isn't a string or a list, then we just coerce 608 # it to a string, which is the proper way to handle 609 # Dir and File instances and will produce something 610 # reasonable for just about everything else: 611 ENV[key] = str(value) 612 613 cmd_list, ignore, silent = self.process(target, map(rfile, source), env) 614 615 # Use len() to filter out any "command" that's zero-length. 616 for cmd_line in filter(len, cmd_list): 617 # Escape the command line for the interpreter we are using. 618 cmd_line = escape_list(cmd_line, escape) 619 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) 620 if not ignore and result: 621 msg = "Error %s" % result 622 return SCons.Errors.BuildError(errstr=msg, 623 status=result, 624 action=self, 625 command=cmd_line) 626 return 0
627
628 - def get_contents(self, target, source, env):
629 """Return the signature contents of this action's command line. 630 631 This strips $(-$) and everything in between the string, 632 since those parts don't affect signatures. 633 """ 634 from SCons.Subst import SUBST_SIG 635 cmd = self.cmd_list 636 if SCons.Util.is_List(cmd): 637 cmd = string.join(map(str, cmd)) 638 else: 639 cmd = str(cmd) 640 return env.subst_target_source(cmd, SUBST_SIG, target, source)
641
642 - def get_implicit_deps(self, target, source, env):
643 icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) 644 if SCons.Util.is_String(icd) and icd[:1] == '$': 645 icd = env.subst(icd) 646 if not icd or icd in ('0', 'None'): 647 return [] 648 from SCons.Subst import SUBST_SIG 649 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) 650 res = [] 651 for cmd_line in cmd_list: 652 if cmd_line: 653 d = env.WhereIs(str(cmd_line[0])) 654 if d: 655 res.append(env.fs.File(d)) 656 return res
657
658 -class CommandGeneratorAction(ActionBase):
659 """Class for command-generator actions."""
660 - def __init__(self, generator, *args, **kw):
661 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') 662 self.generator = generator 663 self.gen_args = args 664 self.gen_kw = kw
665
666 - def _generate(self, target, source, env, for_signature):
667 # ensure that target is a list, to make it easier to write 668 # generator functions: 669 if not SCons.Util.is_List(target): 670 target = [target] 671 672 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) 673 gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw) 674 if not gen_cmd: 675 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) 676 return gen_cmd
677
678 - def __str__(self):
679 try: 680 env = self.presub_env 681 except AttributeError: 682 env = None 683 if env is None: 684 env = SCons.Defaults.DefaultEnvironment() 685 act = self._generate([], [], env, 1) 686 return str(act)
687
688 - def genstring(self, target, source, env):
689 return self._generate(target, source, env, 1).genstring(target, source, env)
690
691 - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, 692 show=_null, execute=_null, chdir=_null):
693 act = self._generate(target, source, env, 0) 694 return act(target, source, env, exitstatfunc, presub, 695 show, execute, chdir)
696
697 - def get_contents(self, target, source, env):
698 """Return the signature contents of this action's command line. 699 700 This strips $(-$) and everything in between the string, 701 since those parts don't affect signatures. 702 """ 703 return self._generate(target, source, env, 1).get_contents(target, source, env)
704
705 - def get_implicit_deps(self, target, source, env):
706 return self._generate(target, source, env, 1).get_implicit_deps(target, source, env)
707 708 709 710 # A LazyAction is a kind of hybrid generator and command action for 711 # strings of the form "$VAR". These strings normally expand to other 712 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also 713 # want to be able to replace them with functions in the construction 714 # environment. Consequently, we want lazy evaluation and creation of 715 # an Action in the case of the function, but that's overkill in the more 716 # normal case of expansion to other strings. 717 # 718 # So we do this with a subclass that's both a generator *and* 719 # a command action. The overridden methods all do a quick check 720 # of the construction variable, and if it's a string we just call 721 # the corresponding CommandAction method to do the heavy lifting. 722 # If not, then we call the same-named CommandGeneratorAction method. 723 # The CommandGeneratorAction methods work by using the overridden 724 # _generate() method, that is, our own way of handling "generation" of 725 # an action based on what's in the construction variable. 726
727 -class LazyAction(CommandGeneratorAction, CommandAction):
728
729 - def __init__(self, var, *args, **kw):
730 if __debug__: logInstanceCreation(self, 'Action.LazyAction') 731 apply(CommandAction.__init__, (self, '$'+var)+args, kw) 732 self.var = SCons.Util.to_String(var) 733 self.gen_args = args 734 self.gen_kw = kw
735
736 - def get_parent_class(self, env):
737 c = env.get(self.var) 738 if SCons.Util.is_String(c) and not '\n' in c: 739 return CommandAction 740 return CommandGeneratorAction
741
742 - def _generate_cache(self, env):
743 c = env.get(self.var, '') 744 gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw) 745 if not gen_cmd: 746 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) 747 return gen_cmd
748
749 - def _generate(self, target, source, env, for_signature):
750 return self._generate_cache(env)
751
752 - def __call__(self, target, source, env, *args, **kw):
753 args = (self, target, source, env) + args 754 c = self.get_parent_class(env) 755 return apply(c.__call__, args, kw)
756
757 - def get_contents(self, target, source, env):
758 c = self.get_parent_class(env) 759 return c.get_contents(self, target, source, env)
760 761 762
763 -class FunctionAction(_ActionAction):
764 """Class for Python function actions.""" 765
766 - def __init__(self, execfunction, cmdstr=_null, *args, **kw):
767 if __debug__: logInstanceCreation(self, 'Action.FunctionAction') 768 769 if not cmdstr is _null: 770 if callable(cmdstr): 771 args = (cmdstr,)+args 772 elif not (cmdstr is None or SCons.Util.is_String(cmdstr)): 773 raise SCons.Errors.UserError(\ 774 'Invalid function display variable type. ' \ 775 'You must either pass a string or a callback which ' \ 776 'accepts (target, source, env) as parameters.') 777 778 self.execfunction = execfunction 779 try: 780 self.funccontents = _callable_contents(execfunction) 781 except AttributeError: 782 try: 783 # See if execfunction will do the heavy lifting for us. 784 self.gc = execfunction.get_contents 785 except AttributeError: 786 # This is weird, just do the best we can. 787 self.funccontents = _object_contents(execfunction) 788 789 apply(_ActionAction.__init__, (self,)+args, kw) 790 self.varlist = kw.get('varlist', []) 791 self.cmdstr = cmdstr
792
793 - def function_name(self):
794 try: 795 return self.execfunction.__name__ 796 except AttributeError: 797 try: 798 return self.execfunction.__class__.__name__ 799 except AttributeError: 800 return "unknown_python_function"
801
802 - def strfunction(self, target, source, env):
803 if self.cmdstr is None: 804 return None 805 if not self.cmdstr is _null: 806 from SCons.Subst import SUBST_RAW 807 c = env.subst(self.cmdstr, SUBST_RAW, target, source) 808 if c: 809 return c 810 def array(a): 811 def quote(s): 812 return '"' + str(s) + '"'
813 return '[' + string.join(map(quote, a), ", ") + ']'
814 try: 815 strfunc = self.execfunction.strfunction 816 except AttributeError: 817 pass 818 else: 819 if strfunc is None: 820 return None 821 if callable(strfunc): 822 return strfunc(target, source, env) 823 name = self.function_name() 824 tstr = array(target) 825 sstr = array(source) 826 return "%s(%s, %s)" % (name, tstr, sstr) 827
828 - def __str__(self):
829 name = self.function_name() 830 if name == 'ActionCaller': 831 return str(self.execfunction) 832 return "%s(target, source, env)" % name
833
834 - def execute(self, target, source, env):
835 rsources = map(rfile, source) 836 try: 837 result = self.execfunction(target=target, source=rsources, env=env) 838 except EnvironmentError, e: 839 # If an IOError/OSError happens, raise a BuildError. 840 # Report the name of the file or directory that caused the 841 # error, which might be different from the target being built 842 # (for example, failure to create the directory in which the 843 # target file will appear). 844 try: filename = e.filename 845 except AttributeError: filename = None 846 result = SCons.Errors.BuildError(node=target, 847 errstr=e.strerror, 848 status=1, 849 filename=filename, 850 action=self, 851 command=self.strfunction(target, source, env)) 852 else: 853 if result: 854 msg = "Error %s" % result 855 result = SCons.Errors.BuildError(errstr=msg, 856 status=result, 857 action=self, 858 command=self.strfunction(target, source, env)) 859 return result
860
861 - def get_contents(self, target, source, env):
862 """Return the signature contents of this callable action.""" 863 try: 864 contents = self.gc(target, source, env) 865 except AttributeError: 866 contents = self.funccontents 867 868 result = [contents] 869 for v in self.varlist: 870 result.append(env.subst('${'+v+'}')) 871 872 return string.join(result, '')
873
874 - def get_implicit_deps(self, target, source, env):
875 return []
876
877 -class ListAction(ActionBase):
878 """Class for lists of other actions."""
879 - def __init__(self, list):
880 if __debug__: logInstanceCreation(self, 'Action.ListAction') 881 def list_of_actions(x): 882 if isinstance(x, ActionBase): 883 return x 884 return Action(x)
885 self.list = map(list_of_actions, list)
886
887 - def genstring(self, target, source, env):
888 return string.join(map(lambda a, t=target, s=source, e=env: 889 a.genstring(t, s, e), 890 self.list), 891 '\n')
892
893 - def __str__(self):
894 return string.join(map(str, self.list), '\n')
895
896 - def presub_lines(self, env):
897 return SCons.Util.flatten_sequence( 898 map(lambda a, env=env: a.presub_lines(env), self.list))
899
900 - def get_contents(self, target, source, env):
901 """Return the signature contents of this action list. 902 903 Simple concatenation of the signatures of the elements. 904 """ 905 return string.join(map(lambda x, t=target, s=source, e=env: 906 x.get_contents(t, s, e), 907 self.list), 908 "")
909
910 - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, 911 show=_null, execute=_null, chdir=_null):
912 for act in self.list: 913 stat = act(target, source, env, exitstatfunc, presub, 914 show, execute, chdir) 915 if stat: 916 return stat 917 return 0
918
919 - def get_implicit_deps(self, target, source, env):
920 result = [] 921 for act in self.list: 922 result.extend(act.get_implicit_deps(target, source, env)) 923 return result
924
925 -class ActionCaller:
926 """A class for delaying calling an Action function with specific 927 (positional and keyword) arguments until the Action is actually 928 executed. 929 930 This class looks to the rest of the world like a normal Action object, 931 but what it's really doing is hanging on to the arguments until we 932 have a target, source and env to use for the expansion. 933 """
934 - def __init__(self, parent, args, kw):
935 self.parent = parent 936 self.args = args 937 self.kw = kw
938 - def get_contents(self, target, source, env):
939 actfunc = self.parent.actfunc 940 try: 941 # "self.actfunc" is a function. 942 contents = str(actfunc.func_code.co_code) 943 except AttributeError: 944 # "self.actfunc" is a callable object. 945 try: 946 contents = str(actfunc.__call__.im_func.func_code.co_code) 947 except AttributeError: 948 # No __call__() method, so it might be a builtin 949 # or something like that. Do the best we can. 950 contents = str(actfunc) 951 contents = remove_set_lineno_codes(contents) 952 return contents
953 - def subst(self, s, target, source, env):
954 # If s is a list, recursively apply subst() 955 # to every element in the list 956 if SCons.Util.is_List(s): 957 result = [] 958 for elem in s: 959 result.append(self.subst(elem, target, source, env)) 960 return self.parent.convert(result) 961 962 # Special-case hack: Let a custom function wrapped in an 963 # ActionCaller get at the environment through which the action 964 # was called by using this hard-coded value as a special return. 965 if s == '$__env__': 966 return env 967 elif SCons.Util.is_String(s): 968 return env.subst(s, 1, target, source) 969 return self.parent.convert(s)
970 - def subst_args(self, target, source, env):
971 return map(lambda x, self=self, t=target, s=source, e=env: 972 self.subst(x, t, s, e), 973 self.args)
974 - def subst_kw(self, target, source, env):
975 kw = {} 976 for key in self.kw.keys(): 977 kw[key] = self.subst(self.kw[key], target, source, env) 978 return kw
979 - def __call__(self, target, source, env):
980 args = self.subst_args(target, source, env) 981 kw = self.subst_kw(target, source, env) 982 return apply(self.parent.actfunc, args, kw)
983 - def strfunction(self, target, source, env):
984 args = self.subst_args(target, source, env) 985 kw = self.subst_kw(target, source, env) 986 return apply(self.parent.strfunc, args, kw)
987 - def __str__(self):
988 return apply(self.parent.strfunc, self.args, self.kw)
989
990 -class ActionFactory:
991 """A factory class that will wrap up an arbitrary function 992 as an SCons-executable Action object. 993 994 The real heavy lifting here is done by the ActionCaller class. 995 We just collect the (positional and keyword) arguments that we're 996 called with and give them to the ActionCaller object we create, 997 so it can hang onto them until it needs them. 998 """
999 - def __init__(self, actfunc, strfunc, convert=lambda x: x):
1000 self.actfunc = actfunc 1001 self.strfunc = strfunc 1002 self.convert = convert
1003 - def __call__(self, *args, **kw):
1004 ac = ActionCaller(self, args, kw) 1005 action = Action(ac, strfunction=ac.strfunction) 1006 return action
1007