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