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