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