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