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