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 3603 2008/10/10 05:46:45 scons" 
  99   
 100  import cPickle 
 101  import dis 
 102  import os 
 103  import os.path 
 104  import string 
 105  import sys 
 106  import subprocess 
 107   
 108  from SCons.Debug import logInstanceCreation 
 109  import SCons.Errors 
 110  import SCons.Executor 
 111  import SCons.Util 
 112   
113 -class _Null:
114 pass
115 116 _null = _Null 117 118 print_actions = 1 119 execute_actions = 1 120 print_actions_presub = 0 121
122 -def rfile(n):
123 try: 124 return n.rfile() 125 except AttributeError: 126 return n
127
128 -def default_exitstatfunc(s):
129 return s
130 131 try: 132 SET_LINENO = dis.SET_LINENO 133 HAVE_ARGUMENT = dis.HAVE_ARGUMENT 134 except AttributeError: 135 remove_set_lineno_codes = lambda x: x 136 else:
137 - def remove_set_lineno_codes(code):
138 result = [] 139 n = len(code) 140 i = 0 141 while i < n: 142 c = code[i] 143 op = ord(c) 144 if op >= HAVE_ARGUMENT: 145 if op != SET_LINENO: 146 result.append(code[i:i+3]) 147 i = i+3 148 else: 149 result.append(c) 150 i = i+1 151 return string.join(result, '')
152 153
154 -def _callable_contents(obj):
155 """Return the signature contents of a callable Python object. 156 """ 157 try: 158 # Test if obj is a method. 159 return _function_contents(obj.im_func) 160 161 except AttributeError: 162 try: 163 # Test if obj is a callable object. 164 return _function_contents(obj.__call__.im_func) 165 166 except AttributeError: 167 try: 168 # Test if obj is a code object. 169 return _code_contents(obj) 170 171 except AttributeError: 172 # Test if obj is a function object. 173 return _function_contents(obj)
174 175
176 -def _object_contents(obj):
177 """Return the signature contents of any Python object. 178 179 We have to handle the case where object contains a code object 180 since it can be pickled directly. 181 """ 182 try: 183 # Test if obj is a method. 184 return _function_contents(obj.im_func) 185 186 except AttributeError: 187 try: 188 # Test if obj is a callable object. 189 return _function_contents(obj.__call__.im_func) 190 191 except AttributeError: 192 try: 193 # Test if obj is a code object. 194 return _code_contents(obj) 195 196 except AttributeError: 197 try: 198 # Test if obj is a function object. 199 return _function_contents(obj) 200 201 except AttributeError: 202 # Should be a pickable Python object. 203 try: 204 return cPickle.dumps(obj) 205 except (cPickle.PicklingError, TypeError): 206 # This is weird, but it seems that nested classes 207 # are unpickable. The Python docs say it should 208 # always be a PicklingError, but some Python 209 # versions seem to return TypeError. Just do 210 # the best we can. 211 return str(obj)
212 213
214 -def _code_contents(code):
215 """Return the signature contents of a code object. 216 217 By providing direct access to the code object of the 218 function, Python makes this extremely easy. Hooray! 219 220 Unfortunately, older versions of Python include line 221 number indications in the compiled byte code. Boo! 222 So we remove the line number byte codes to prevent 223 recompilations from moving a Python function. 224 """ 225 226 contents = [] 227 228 # The code contents depends on the number of local variables 229 # but not their actual names. 230 contents.append("%s,%s" % (code.co_argcount, len(code.co_varnames))) 231 try: 232 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) 233 except AttributeError: 234 # Older versions of Python do not support closures. 235 contents.append(",0,0") 236 237 # The code contents depends on any constants accessed by the 238 # function. Note that we have to call _object_contents on each 239 # constants because the code object of nested functions can 240 # show-up among the constants. 241 # 242 # Note that we also always ignore the first entry of co_consts 243 # which contains the function doc string. We assume that the 244 # function does not access its doc string. 245 contents.append(',(' + string.join(map(_object_contents,code.co_consts[1:]),',') + ')') 246 247 # The code contents depends on the variable names used to 248 # accessed global variable, as changing the variable name changes 249 # the variable actually accessed and therefore changes the 250 # function result. 251 contents.append(',(' + string.join(map(_object_contents,code.co_names),',') + ')') 252 253 254 # The code contents depends on its actual code!!! 255 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') 256 257 return string.join(contents, '')
258 259
260 -def _function_contents(func):
261 """Return the signature contents of a function.""" 262 263 contents = [_code_contents(func.func_code)] 264 265 # The function contents depends on the value of defaults arguments 266 if func.func_defaults: 267 contents.append(',(' + string.join(map(_object_contents,func.func_defaults),',') + ')') 268 else: 269 contents.append(',()') 270 271 # The function contents depends on the closure captured cell values. 272 try: 273 closure = func.func_closure or [] 274 except AttributeError: 275 # Older versions of Python do not support closures. 276 closure = [] 277 278 #xxx = [_object_contents(x.cell_contents) for x in closure] 279 try: 280 xxx = map(lambda x: _object_contents(x.cell_contents), closure) 281 except AttributeError: 282 xxx = [] 283 contents.append(',(' + string.join(xxx, ',') + ')') 284 285 return string.join(contents, '')
286 287
288 -def _actionAppend(act1, act2):
289 # This function knows how to slap two actions together. 290 # Mainly, it handles ListActions by concatenating into 291 # a single ListAction. 292 a1 = Action(act1) 293 a2 = Action(act2) 294 if a1 is None or a2 is None: 295 raise TypeError, "Cannot append %s to %s" % (type(act1), type(act2)) 296 if isinstance(a1, ListAction): 297 if isinstance(a2, ListAction): 298 return ListAction(a1.list + a2.list) 299 else: 300 return ListAction(a1.list + [ a2 ]) 301 else: 302 if isinstance(a2, ListAction): 303 return ListAction([ a1 ] + a2.list) 304 else: 305 return ListAction([ a1, a2 ])
306
307 -def _do_create_action(act, *args, **kw):
308 """This is the actual "implementation" for the 309 Action factory method, below. This handles the 310 fact that passing lists to Action() itself has 311 different semantics than passing lists as elements 312 of lists. 313 314 The former will create a ListAction, the latter 315 will create a CommandAction by converting the inner 316 list elements to strings.""" 317 318 if isinstance(act, ActionBase): 319 return act 320 if SCons.Util.is_List(act): 321 return apply(CommandAction, (act,)+args, kw) 322 if callable(act): 323 try: 324 gen = kw['generator'] 325 del kw['generator'] 326 except KeyError: 327 gen = 0 328 if gen: 329 action_type = CommandGeneratorAction 330 else: 331 action_type = FunctionAction 332 return apply(action_type, (act,)+args, kw) 333 if SCons.Util.is_String(act): 334 var=SCons.Util.get_environment_var(act) 335 if var: 336 # This looks like a string that is purely an Environment 337 # variable reference, like "$FOO" or "${FOO}". We do 338 # something special here...we lazily evaluate the contents 339 # of that Environment variable, so a user could put something 340 # like a function or a CommandGenerator in that variable 341 # instead of a string. 342 return apply(LazyAction, (var,)+args, kw) 343 commands = string.split(str(act), '\n') 344 if len(commands) == 1: 345 return apply(CommandAction, (commands[0],)+args, kw) 346 else: 347 listCmdActions = map(lambda x, args=args, kw=kw: 348 apply(CommandAction, (x,)+args, kw), 349 commands) 350 return ListAction(listCmdActions) 351 return None
352
353 -def Action(act, *args, **kw):
354 """A factory for action objects.""" 355 if SCons.Util.is_List(act): 356 acts = map(lambda a, args=args, kw=kw: 357 apply(_do_create_action, (a,)+args, kw), 358 act) 359 acts = filter(None, acts) 360 if len(acts) == 1: 361 return acts[0] 362 else: 363 return ListAction(acts) 364 else: 365 return apply(_do_create_action, (act,)+args, kw)
366
367 -class ActionBase:
368 """Base class for all types of action objects that can be held by 369 other objects (Builders, Executors, etc.) This provides the 370 common methods for manipulating and combining those actions.""" 371
372 - def __cmp__(self, other):
373 return cmp(self.__dict__, other)
374
375 - def genstring(self, target, source, env):
376 return str(self)
377
378 - def __add__(self, other):
379 return _actionAppend(self, other)
380
381 - def __radd__(self, other):
382 return _actionAppend(other, self)
383
384 - def presub_lines(self, env):
385 # CommandGeneratorAction needs a real environment 386 # in order to return the proper string here, since 387 # it may call LazyAction, which looks up a key 388 # in that env. So we temporarily remember the env here, 389 # and CommandGeneratorAction will use this env 390 # when it calls its _generate method. 391 self.presub_env = env 392 lines = string.split(str(self), '\n') 393 self.presub_env = None # don't need this any more 394 return lines
395
396 - def get_executor(self, env, overrides, tlist, slist, executor_kw):
397 """Return the Executor for this Action.""" 398 return SCons.Executor.Executor(self, env, overrides, 399 tlist, slist, executor_kw)
400
401 -class _ActionAction(ActionBase):
402 """Base class for actions that create output objects."""
403 - def __init__(self, strfunction=_null, presub=_null, chdir=None, exitstatfunc=None, **kw):
404 if not strfunction is _null: 405 self.strfunction = strfunction 406 self.presub = presub 407 self.chdir = chdir 408 if not exitstatfunc: 409 exitstatfunc = default_exitstatfunc 410 self.exitstatfunc = exitstatfunc
411
412 - def print_cmd_line(self, s, target, source, env):
413 sys.stdout.write(s + "\n")
414
415 - def __call__(self, target, source, env, 416 exitstatfunc=_null, 417 presub=_null, 418 show=_null, 419 execute=_null, 420 chdir=_null):
421 if not SCons.Util.is_List(target): 422 target = [target] 423 if not SCons.Util.is_List(source): 424 source = [source] 425 426 if exitstatfunc is _null: exitstatfunc = self.exitstatfunc 427 if presub is _null: 428 presub = self.presub 429 if presub is _null: 430 presub = print_actions_presub 431 if show is _null: show = print_actions 432 if execute is _null: execute = execute_actions 433 if chdir is _null: chdir = self.chdir 434 save_cwd = None 435 if chdir: 436 save_cwd = os.getcwd() 437 try: 438 chdir = str(chdir.abspath) 439 except AttributeError: 440 if not SCons.Util.is_String(chdir): 441 chdir = str(target[0].dir) 442 if presub: 443 t = string.join(map(str, target), ' and ') 444 l = string.join(self.presub_lines(env), '\n ') 445 out = "Building %s with action:\n %s\n" % (t, l) 446 sys.stdout.write(out) 447 s = None 448 if show and self.strfunction: 449 s = self.strfunction(target, source, env) 450 if s: 451 if chdir: 452 s = ('os.chdir(%s)\n' % repr(chdir)) + s 453 try: 454 get = env.get 455 except AttributeError: 456 print_func = self.print_cmd_line 457 else: 458 print_func = get('PRINT_CMD_LINE_FUNC') 459 if not print_func: 460 print_func = self.print_cmd_line 461 print_func(s, target, source, env) 462 stat = 0 463 if execute: 464 if chdir: 465 os.chdir(chdir) 466 try: 467 stat = self.execute(target, source, env) 468 if isinstance(stat, SCons.Errors.BuildError): 469 s = exitstatfunc(stat.status) 470 if s: 471 stat.status = s 472 else: 473 stat = s 474 else: 475 stat = exitstatfunc(stat) 476 finally: 477 if save_cwd: 478 os.chdir(save_cwd) 479 if s and save_cwd: 480 print_func('os.chdir(%s)' % repr(save_cwd), target, source, env) 481 482 return stat
483 484
485 -def _string_from_cmd_list(cmd_list):
486 """Takes a list of command line arguments and returns a pretty 487 representation for printing.""" 488 cl = [] 489 for arg in map(str, cmd_list): 490 if ' ' in arg or '\t' in arg: 491 arg = '"' + arg + '"' 492 cl.append(arg) 493 return string.join(cl)
494 495 # this function is still in draft mode. We're going to need something like 496 # it in the long run as more and more places use it, but I'm sure it'll have 497 # to be tweaked to get the full desired functionality. 498 default_ENV = None 499 # one special arg, 'error', to tell what to do with exceptions.
500 -def _subproc(env, cmd, error = 'ignore', **kw):
501 """Do setup for a subprocess.Popen() call""" 502 503 # If the env has no ENV, get a default 504 try: 505 ENV = env['ENV'] 506 except KeyError: 507 global default_ENV 508 if default_ENV is None: 509 # Unbelievably expensive. What it really should do 510 # is run the platform setup to get the default ENV. 511 # Fortunately, it should almost never happen. 512 default_ENV = SCons.Environment.Environment(tools=[])['ENV'] 513 ENV = default_ENV 514 515 # Ensure that the ENV values are all strings: 516 new_env = {} 517 # It's a string 99.44% of the time, so optimize this 518 is_String = SCons.Util.is_String 519 for key, value in ENV.items(): 520 if is_String(value): 521 # Call str() even though it's a "string" because it might be 522 # a *Unicode* string, which makes subprocess.Popen() gag. 523 new_env[key] = str(value) 524 elif SCons.Util.is_List(value): 525 # If the value is a list, then we assume it is a 526 # path list, because that's a pretty common list-like 527 # value to stick in an environment variable: 528 value = SCons.Util.flatten_sequence(value) 529 ENV[key] = string.join(map(str, value), os.pathsep) 530 else: 531 # If it isn't a string or a list, then we just coerce 532 # it to a string, which is the proper way to handle 533 # Dir and File instances and will produce something 534 # reasonable for just about everything else: 535 ENV[key] = str(value) 536 kw['env'] = new_env 537 538 try: 539 #FUTURE return subprocess.Popen(cmd, **kw) 540 return apply(subprocess.Popen, (cmd,), kw) 541 except EnvironmentError, e: 542 if error == 'raise': raise 543 # return a dummy Popen instance that only returns error 544 class popen: 545 def __init__(self, e): self.exception = e 546 def communicate(self): return ('','') 547 def wait(self): return -self.exception.errno 548 stdin = None 549 class f: 550 def read(self): return '' 551 def readline(self): return ''
552 stdout = stderr = f() 553 return popen(e) 554
555 -class CommandAction(_ActionAction):
556 """Class for command-execution actions."""
557 - def __init__(self, cmd, cmdstr=None, *args, **kw):
558 # Cmd can actually be a list or a single item; if it's a 559 # single item it should be the command string to execute; if a 560 # list then it should be the words of the command string to 561 # execute. Only a single command should be executed by this 562 # object; lists of commands should be handled by embedding 563 # these objects in a ListAction object (which the Action() 564 # factory above does). cmd will be passed to 565 # Environment.subst_list() for substituting environment 566 # variables. 567 if __debug__: logInstanceCreation(self, 'Action.CommandAction') 568 569 if not cmdstr is None: 570 if callable(cmdstr): 571 args = (cmdstr,)+args 572 elif not SCons.Util.is_String(cmdstr): 573 raise SCons.Errors.UserError(\ 574 'Invalid command display variable type. ' \ 575 'You must either pass a string or a callback which ' \ 576 'accepts (target, source, env) as parameters.') 577 578 apply(_ActionAction.__init__, (self,)+args, kw) 579 if SCons.Util.is_List(cmd): 580 if filter(SCons.Util.is_List, cmd): 581 raise TypeError, "CommandAction should be given only " \ 582 "a single command" 583 self.cmd_list = cmd 584 self.cmdstr = cmdstr
585
586 - def __str__(self):
587 if SCons.Util.is_List(self.cmd_list): 588 return string.join(map(str, self.cmd_list), ' ') 589 return str(self.cmd_list)
590
591 - def process(self, target, source, env):
592 result = env.subst_list(self.cmd_list, 0, target, source) 593 silent = None 594 ignore = None 595 while 1: 596 try: c = result[0][0][0] 597 except IndexError: c = None 598 if c == '@': silent = 1 599 elif c == '-': ignore = 1 600 else: break 601 result[0][0] = result[0][0][1:] 602 try: 603 if not result[0][0]: 604 result[0] = result[0][1:] 605 except IndexError: 606 pass 607 return result, ignore, silent
608
609 - def strfunction(self, target, source, env):
610 if not self.cmdstr is None: 611 from SCons.Subst import SUBST_RAW 612 c = env.subst(self.cmdstr, SUBST_RAW, target, source) 613 if c: 614 return c 615 cmd_list, ignore, silent = self.process(target, source, env) 616 if silent: 617 return '' 618 return _string_from_cmd_list(cmd_list[0])
619
620 - def execute(self, target, source, env):
621 """Execute a command action. 622 623 This will handle lists of commands as well as individual commands, 624 because construction variable substitution may turn a single 625 "command" into a list. This means that this class can actually 626 handle lists of commands, even though that's not how we use it 627 externally. 628 """ 629 from SCons.Subst import escape_list 630 import SCons.Util 631 flatten_sequence = SCons.Util.flatten_sequence 632 is_String = SCons.Util.is_String 633 is_List = SCons.Util.is_List 634 635 try: 636 shell = env['SHELL'] 637 except KeyError: 638 raise SCons.Errors.UserError('Missing SHELL construction variable.') 639 640 try: 641 spawn = env['SPAWN'] 642 except KeyError: 643 raise SCons.Errors.UserError('Missing SPAWN construction variable.') 644 else: 645 if is_String(spawn): 646 spawn = env.subst(spawn, raw=1, conv=lambda x: x) 647 648 escape = env.get('ESCAPE', lambda x: x) 649 650 try: 651 ENV = env['ENV'] 652 except KeyError: 653 global default_ENV 654 if not default_ENV: 655 import SCons.Environment 656 default_ENV = SCons.Environment.Environment()['ENV'] 657 ENV = default_ENV 658 659 # Ensure that the ENV values are all strings: 660 for key, value in ENV.items(): 661 if not is_String(value): 662 if is_List(value): 663 # If the value is a list, then we assume it is a 664 # path list, because that's a pretty common list-like 665 # value to stick in an environment variable: 666 value = flatten_sequence(value) 667 ENV[key] = string.join(map(str, value), os.pathsep) 668 else: 669 # If it isn't a string or a list, then we just coerce 670 # it to a string, which is the proper way to handle 671 # Dir and File instances and will produce something 672 # reasonable for just about everything else: 673 ENV[key] = str(value) 674 675 cmd_list, ignore, silent = self.process(target, map(rfile, source), env) 676 677 # Use len() to filter out any "command" that's zero-length. 678 for cmd_line in filter(len, cmd_list): 679 # Escape the command line for the interpreter we are using. 680 cmd_line = escape_list(cmd_line, escape) 681 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) 682 if not ignore and result: 683 msg = "Error %s" % result 684 return SCons.Errors.BuildError(errstr=msg, 685 status=result, 686 action=self, 687 command=cmd_line) 688 return 0
689
690 - def get_contents(self, target, source, env):
691 """Return the signature contents of this action's command line. 692 693 This strips $(-$) and everything in between the string, 694 since those parts don't affect signatures. 695 """ 696 from SCons.Subst import SUBST_SIG 697 cmd = self.cmd_list 698 if SCons.Util.is_List(cmd): 699 cmd = string.join(map(str, cmd)) 700 else: 701 cmd = str(cmd) 702 return env.subst_target_source(cmd, SUBST_SIG, target, source)
703
704 - def get_implicit_deps(self, target, source, env):
705 icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) 706 if SCons.Util.is_String(icd) and icd[:1] == '$': 707 icd = env.subst(icd) 708 if not icd or icd in ('0', 'None'): 709 return [] 710 from SCons.Subst import SUBST_SIG 711 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) 712 res = [] 713 for cmd_line in cmd_list: 714 if cmd_line: 715 d = env.WhereIs(str(cmd_line[0])) 716 if d: 717 res.append(env.fs.File(d)) 718 return res
719
720 -class CommandGeneratorAction(ActionBase):
721 """Class for command-generator actions."""
722 - def __init__(self, generator, *args, **kw):
723 if __debug__: logInstanceCreation(self, 'Action.CommandGeneratorAction') 724 self.generator = generator 725 self.gen_args = args 726 self.gen_kw = kw
727
728 - def _generate(self, target, source, env, for_signature):
729 # ensure that target is a list, to make it easier to write 730 # generator functions: 731 if not SCons.Util.is_List(target): 732 target = [target] 733 734 ret = self.generator(target=target, source=source, env=env, for_signature=for_signature) 735 gen_cmd = apply(Action, (ret,)+self.gen_args, self.gen_kw) 736 if not gen_cmd: 737 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) 738 return gen_cmd
739
740 - def __str__(self):
741 try: 742 env = self.presub_env 743 except AttributeError: 744 env = None 745 if env is None: 746 env = SCons.Defaults.DefaultEnvironment() 747 act = self._generate([], [], env, 1) 748 return str(act)
749
750 - def genstring(self, target, source, env):
751 return self._generate(target, source, env, 1).genstring(target, source, env)
752
753 - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, 754 show=_null, execute=_null, chdir=_null):
755 act = self._generate(target, source, env, 0) 756 return act(target, source, env, exitstatfunc, presub, 757 show, execute, chdir)
758
759 - def get_contents(self, target, source, env):
760 """Return the signature contents of this action's command line. 761 762 This strips $(-$) and everything in between the string, 763 since those parts don't affect signatures. 764 """ 765 return self._generate(target, source, env, 1).get_contents(target, source, env)
766
767 - def get_implicit_deps(self, target, source, env):
768 return self._generate(target, source, env, 1).get_implicit_deps(target, source, env)
769 770 771 772 # A LazyAction is a kind of hybrid generator and command action for 773 # strings of the form "$VAR". These strings normally expand to other 774 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also 775 # want to be able to replace them with functions in the construction 776 # environment. Consequently, we want lazy evaluation and creation of 777 # an Action in the case of the function, but that's overkill in the more 778 # normal case of expansion to other strings. 779 # 780 # So we do this with a subclass that's both a generator *and* 781 # a command action. The overridden methods all do a quick check 782 # of the construction variable, and if it's a string we just call 783 # the corresponding CommandAction method to do the heavy lifting. 784 # If not, then we call the same-named CommandGeneratorAction method. 785 # The CommandGeneratorAction methods work by using the overridden 786 # _generate() method, that is, our own way of handling "generation" of 787 # an action based on what's in the construction variable. 788
789 -class LazyAction(CommandGeneratorAction, CommandAction):
790
791 - def __init__(self, var, *args, **kw):
792 if __debug__: logInstanceCreation(self, 'Action.LazyAction') 793 apply(CommandAction.__init__, (self, '$'+var)+args, kw) 794 self.var = SCons.Util.to_String(var) 795 self.gen_args = args 796 self.gen_kw = kw
797
798 - def get_parent_class(self, env):
799 c = env.get(self.var) 800 if SCons.Util.is_String(c) and not '\n' in c: 801 return CommandAction 802 return CommandGeneratorAction
803
804 - def _generate_cache(self, env):
805 c = env.get(self.var, '') 806 gen_cmd = apply(Action, (c,)+self.gen_args, self.gen_kw) 807 if not gen_cmd: 808 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) 809 return gen_cmd
810
811 - def _generate(self, target, source, env, for_signature):
812 return self._generate_cache(env)
813
814 - def __call__(self, target, source, env, *args, **kw):
815 args = (self, target, source, env) + args 816 c = self.get_parent_class(env) 817 return apply(c.__call__, args, kw)
818
819 - def get_contents(self, target, source, env):
820 c = self.get_parent_class(env) 821 return c.get_contents(self, target, source, env)
822 823 824
825 -class FunctionAction(_ActionAction):
826 """Class for Python function actions.""" 827
828 - def __init__(self, execfunction, cmdstr=_null, *args, **kw):
829 if __debug__: logInstanceCreation(self, 'Action.FunctionAction') 830 831 if not cmdstr is _null: 832 if callable(cmdstr): 833 args = (cmdstr,)+args 834 elif not (cmdstr is None or SCons.Util.is_String(cmdstr)): 835 raise SCons.Errors.UserError(\ 836 'Invalid function display variable type. ' \ 837 'You must either pass a string or a callback which ' \ 838 'accepts (target, source, env) as parameters.') 839 840 self.execfunction = execfunction 841 try: 842 self.funccontents = _callable_contents(execfunction) 843 except AttributeError: 844 try: 845 # See if execfunction will do the heavy lifting for us. 846 self.gc = execfunction.get_contents 847 except AttributeError: 848 # This is weird, just do the best we can. 849 self.funccontents = _object_contents(execfunction) 850 851 apply(_ActionAction.__init__, (self,)+args, kw) 852 self.varlist = kw.get('varlist', []) 853 if SCons.Util.is_String(self.varlist): 854 # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] 855 self.varlist=[self.varlist] 856 self.cmdstr = cmdstr
857
858 - def function_name(self):
859 try: 860 return self.execfunction.__name__ 861 except AttributeError: 862 try: 863 return self.execfunction.__class__.__name__ 864 except AttributeError: 865 return "unknown_python_function"
866
867 - def strfunction(self, target, source, env):
868 if self.cmdstr is None: 869 return None 870 if not self.cmdstr is _null: 871 from SCons.Subst import SUBST_RAW 872 c = env.subst(self.cmdstr, SUBST_RAW, target, source) 873 if c: 874 return c 875 def array(a): 876 def quote(s): 877 try: 878 str_for_display = s.str_for_display 879 except AttributeError: 880 s = repr(s) 881 else: 882 s = str_for_display() 883 return s
884 return '[' + string.join(map(quote, a), ", ") + ']'
885 try: 886 strfunc = self.execfunction.strfunction 887 except AttributeError: 888 pass 889 else: 890 if strfunc is None: 891 return None 892 if callable(strfunc): 893 return strfunc(target, source, env) 894 name = self.function_name() 895 tstr = array(target) 896 sstr = array(source) 897 return "%s(%s, %s)" % (name, tstr, sstr) 898
899 - def __str__(self):
900 name = self.function_name() 901 if name == 'ActionCaller': 902 return str(self.execfunction) 903 return "%s(target, source, env)" % name
904
905 - def execute(self, target, source, env):
906 rsources = map(rfile, source) 907 try: 908 result = self.execfunction(target=target, source=rsources, env=env) 909 except EnvironmentError, e: 910 # If an IOError/OSError happens, raise a BuildError. 911 # Report the name of the file or directory that caused the 912 # error, which might be different from the target being built 913 # (for example, failure to create the directory in which the 914 # target file will appear). 915 try: filename = e.filename 916 except AttributeError: filename = None 917 result = SCons.Errors.BuildError(node=target, 918 errstr=e.strerror, 919 status=1, 920 filename=filename, 921 action=self, 922 command=self.strfunction(target, source, env)) 923 else: 924 if result: 925 msg = "Error %s" % result 926 result = SCons.Errors.BuildError(errstr=msg, 927 status=result, 928 action=self, 929 command=self.strfunction(target, source, env)) 930 return result
931
932 - def get_contents(self, target, source, env):
933 """Return the signature contents of this callable action.""" 934 try: 935 contents = self.gc(target, source, env) 936 except AttributeError: 937 contents = self.funccontents 938 939 result = [contents] 940 for v in self.varlist: 941 result.append(env.subst('${'+v+'}')) 942 943 return string.join(result, '')
944
945 - def get_implicit_deps(self, target, source, env):
946 return []
947
948 -class ListAction(ActionBase):
949 """Class for lists of other actions."""
950 - def __init__(self, list):
951 if __debug__: logInstanceCreation(self, 'Action.ListAction') 952 def list_of_actions(x): 953 if isinstance(x, ActionBase): 954 return x 955 return Action(x)
956 self.list = map(list_of_actions, list)
957
958 - def genstring(self, target, source, env):
959 return string.join(map(lambda a, t=target, s=source, e=env: 960 a.genstring(t, s, e), 961 self.list), 962 '\n')
963
964 - def __str__(self):
965 return string.join(map(str, self.list), '\n')
966
967 - def presub_lines(self, env):
968 return SCons.Util.flatten_sequence( 969 map(lambda a, env=env: a.presub_lines(env), self.list))
970
971 - def get_contents(self, target, source, env):
972 """Return the signature contents of this action list. 973 974 Simple concatenation of the signatures of the elements. 975 """ 976 return string.join(map(lambda x, t=target, s=source, e=env: 977 x.get_contents(t, s, e), 978 self.list), 979 "")
980
981 - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, 982 show=_null, execute=_null, chdir=_null):
983 for act in self.list: 984 stat = act(target, source, env, exitstatfunc, presub, 985 show, execute, chdir) 986 if stat: 987 return stat 988 return 0
989
990 - def get_implicit_deps(self, target, source, env):
991 result = [] 992 for act in self.list: 993 result.extend(act.get_implicit_deps(target, source, env)) 994 return result
995
996 -class ActionCaller:
997 """A class for delaying calling an Action function with specific 998 (positional and keyword) arguments until the Action is actually 999 executed. 1000 1001 This class looks to the rest of the world like a normal Action object, 1002 but what it's really doing is hanging on to the arguments until we 1003 have a target, source and env to use for the expansion. 1004 """
1005 - def __init__(self, parent, args, kw):
1006 self.parent = parent 1007 self.args = args 1008 self.kw = kw
1009 - def get_contents(self, target, source, env):
1010 actfunc = self.parent.actfunc 1011 try: 1012 # "self.actfunc" is a function. 1013 contents = str(actfunc.func_code.co_code) 1014 except AttributeError: 1015 # "self.actfunc" is a callable object. 1016 try: 1017 contents = str(actfunc.__call__.im_func.func_code.co_code) 1018 except AttributeError: 1019 # No __call__() method, so it might be a builtin 1020 # or something like that. Do the best we can. 1021 contents = str(actfunc) 1022 contents = remove_set_lineno_codes(contents) 1023 return contents
1024 - def subst(self, s, target, source, env):
1025 # If s is a list, recursively apply subst() 1026 # to every element in the list 1027 if SCons.Util.is_List(s): 1028 result = [] 1029 for elem in s: 1030 result.append(self.subst(elem, target, source, env)) 1031 return self.parent.convert(result) 1032 1033 # Special-case hack: Let a custom function wrapped in an 1034 # ActionCaller get at the environment through which the action 1035 # was called by using this hard-coded value as a special return. 1036 if s == '$__env__': 1037 return env 1038 elif SCons.Util.is_String(s): 1039 return env.subst(s, 1, target, source) 1040 return self.parent.convert(s)
1041 - def subst_args(self, target, source, env):
1042 return map(lambda x, self=self, t=target, s=source, e=env: 1043 self.subst(x, t, s, e), 1044 self.args)
1045 - def subst_kw(self, target, source, env):
1046 kw = {} 1047 for key in self.kw.keys(): 1048 kw[key] = self.subst(self.kw[key], target, source, env) 1049 return kw
1050 - def __call__(self, target, source, env):
1051 args = self.subst_args(target, source, env) 1052 kw = self.subst_kw(target, source, env) 1053 return apply(self.parent.actfunc, args, kw)
1054 - def strfunction(self, target, source, env):
1055 args = self.subst_args(target, source, env) 1056 kw = self.subst_kw(target, source, env) 1057 return apply(self.parent.strfunc, args, kw)
1058 - def __str__(self):
1059 return apply(self.parent.strfunc, self.args, self.kw)
1060
1061 -class ActionFactory:
1062 """A factory class that will wrap up an arbitrary function 1063 as an SCons-executable Action object. 1064 1065 The real heavy lifting here is done by the ActionCaller class. 1066 We just collect the (positional and keyword) arguments that we're 1067 called with and give them to the ActionCaller object we create, 1068 so it can hang onto them until it needs them. 1069 """
1070 - def __init__(self, actfunc, strfunc, convert=lambda x: x):
1071 self.actfunc = actfunc 1072 self.strfunc = strfunc 1073 self.convert = convert
1074 - def __call__(self, *args, **kw):
1075 ac = ActionCaller(self, args, kw) 1076 action = Action(ac, strfunction=ac.strfunction) 1077 return action
1078