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