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 - 2015 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.4.1:3453:73fefd3ea0b0 2015/11/09 03:25:05 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 try: 239 contents.append(",%s,%s" % (len(code.co_cellvars), len(code.co_freevars))) 240 except AttributeError: 241 # Older versions of Python do not support closures. 242 contents.append(",0,0") 243 244 # The code contents depends on any constants accessed by the 245 # function. Note that we have to call _object_contents on each 246 # constants because the code object of nested functions can 247 # show-up among the constants. 248 # 249 # Note that we also always ignore the first entry of co_consts 250 # which contains the function doc string. We assume that the 251 # function does not access its doc string. 252 contents.append(',(' + ','.join(map(_object_contents,code.co_consts[1:])) + ')') 253 254 # The code contents depends on the variable names used to 255 # accessed global variable, as changing the variable name changes 256 # the variable actually accessed and therefore changes the 257 # function result. 258 contents.append(',(' + ','.join(map(_object_contents,code.co_names)) + ')') 259 260 261 # The code contents depends on its actual code!!! 262 contents.append(',(' + str(remove_set_lineno_codes(code.co_code)) + ')') 263 264 return ''.join(contents)
265 266
267 -def _function_contents(func):
268 """Return the signature contents of a function.""" 269 270 contents = [_code_contents(func.func_code)] 271 272 # The function contents depends on the value of defaults arguments 273 if func.func_defaults: 274 contents.append(',(' + ','.join(map(_object_contents,func.func_defaults)) + ')') 275 else: 276 contents.append(',()') 277 278 # The function contents depends on the closure captured cell values. 279 try: 280 closure = func.func_closure or [] 281 except AttributeError: 282 # Older versions of Python do not support closures. 283 closure = [] 284 285 #xxx = [_object_contents(x.cell_contents) for x in closure] 286 try: 287 xxx = [_object_contents(x.cell_contents) for x in closure] 288 except AttributeError: 289 xxx = [] 290 contents.append(',(' + ','.join(xxx) + ')') 291 292 return ''.join(contents)
293 294
295 -def _actionAppend(act1, act2):
296 # This function knows how to slap two actions together. 297 # Mainly, it handles ListActions by concatenating into 298 # a single ListAction. 299 a1 = Action(act1) 300 a2 = Action(act2) 301 if a1 is None: 302 return a2 303 if a2 is None: 304 return a1 305 if isinstance(a1, ListAction): 306 if isinstance(a2, ListAction): 307 return ListAction(a1.list + a2.list) 308 else: 309 return ListAction(a1.list + [ a2 ]) 310 else: 311 if isinstance(a2, ListAction): 312 return ListAction([ a1 ] + a2.list) 313 else: 314 return ListAction([ a1, a2 ])
315
316 -def _do_create_keywords(args, kw):
317 """This converts any arguments after the action argument into 318 their equivalent keywords and adds them to the kw argument. 319 """ 320 v = kw.get('varlist', ()) 321 # prevent varlist="FOO" from being interpreted as ['F', 'O', 'O'] 322 if is_String(v): v = (v,) 323 kw['varlist'] = tuple(v) 324 if args: 325 # turn positional args into equivalent keywords 326 cmdstrfunc = args[0] 327 if cmdstrfunc is None or is_String(cmdstrfunc): 328 kw['cmdstr'] = cmdstrfunc 329 elif callable(cmdstrfunc): 330 kw['strfunction'] = cmdstrfunc 331 else: 332 raise SCons.Errors.UserError( 333 'Invalid command display variable type. ' 334 'You must either pass a string or a callback which ' 335 'accepts (target, source, env) as parameters.') 336 if len(args) > 1: 337 kw['varlist'] = tuple(SCons.Util.flatten(args[1:])) + kw['varlist'] 338 if kw.get('strfunction', _null) is not _null \ 339 and kw.get('cmdstr', _null) is not _null: 340 raise SCons.Errors.UserError( 341 'Cannot have both strfunction and cmdstr args to Action()')
342
343 -def _do_create_action(act, kw):
344 """This is the actual "implementation" for the 345 Action factory method, below. This handles the 346 fact that passing lists to Action() itself has 347 different semantics than passing lists as elements 348 of lists. 349 350 The former will create a ListAction, the latter 351 will create a CommandAction by converting the inner 352 list elements to strings.""" 353 354 if isinstance(act, ActionBase): 355 return act 356 357 if is_String(act): 358 var=SCons.Util.get_environment_var(act) 359 if var: 360 # This looks like a string that is purely an Environment 361 # variable reference, like "$FOO" or "${FOO}". We do 362 # something special here...we lazily evaluate the contents 363 # of that Environment variable, so a user could put something 364 # like a function or a CommandGenerator in that variable 365 # instead of a string. 366 return LazyAction(var, kw) 367 commands = str(act).split('\n') 368 if len(commands) == 1: 369 return CommandAction(commands[0], **kw) 370 # The list of string commands may include a LazyAction, so we 371 # reprocess them via _do_create_list_action. 372 return _do_create_list_action(commands, kw) 373 374 if is_List(act): 375 return CommandAction(act, **kw) 376 377 if callable(act): 378 try: 379 gen = kw['generator'] 380 del kw['generator'] 381 except KeyError: 382 gen = 0 383 if gen: 384 action_type = CommandGeneratorAction 385 else: 386 action_type = FunctionAction 387 return action_type(act, kw) 388 389 # Catch a common error case with a nice message: 390 if isinstance(act, int) or isinstance(act, float): 391 raise TypeError("Don't know how to create an Action from a number (%s)"%act) 392 # Else fail silently (???) 393 return None
394
395 -def _do_create_list_action(act, kw):
396 """A factory for list actions. Convert the input list into Actions 397 and then wrap them in a ListAction.""" 398 acts = [] 399 for a in act: 400 aa = _do_create_action(a, kw) 401 if aa is not None: acts.append(aa) 402 if not acts: 403 return ListAction([]) 404 elif len(acts) == 1: 405 return acts[0] 406 else: 407 return ListAction(acts)
408
409 -def Action(act, *args, **kw):
410 """A factory for action objects.""" 411 # Really simple: the _do_create_* routines do the heavy lifting. 412 _do_create_keywords(args, kw) 413 if is_List(act): 414 return _do_create_list_action(act, kw) 415 return _do_create_action(act, kw)
416
417 -class ActionBase(object):
418 """Base class for all types of action objects that can be held by 419 other objects (Builders, Executors, etc.) This provides the 420 common methods for manipulating and combining those actions.""" 421
422 - def __cmp__(self, other):
423 return cmp(self.__dict__, other)
424
425 - def no_batch_key(self, env, target, source):
426 return None
427 428 batch_key = no_batch_key 429
430 - def genstring(self, target, source, env):
431 return str(self)
432
433 - def get_contents(self, target, source, env):
434 result = [ self.get_presig(target, source, env) ] 435 # This should never happen, as the Action() factory should wrap 436 # the varlist, but just in case an action is created directly, 437 # we duplicate this check here. 438 vl = self.get_varlist(target, source, env) 439 if is_String(vl): vl = (vl,) 440 for v in vl: 441 # do the subst this way to ignore $(...$) parts: 442 result.append(env.subst_target_source('${'+v+'}', SCons.Subst.SUBST_SIG, target, source)) 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.get_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,input=None): 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 def __iter__(self): return iter(())
687 stdout = stderr = f() 688 return dummyPopen(e) 689
690 -class CommandAction(_ActionAction):
691 """Class for command-execution actions."""
692 - def __init__(self, cmd, **kw):
693 # Cmd can actually be a list or a single item; if it's a 694 # single item it should be the command string to execute; if a 695 # list then it should be the words of the command string to 696 # execute. Only a single command should be executed by this 697 # object; lists of commands should be handled by embedding 698 # these objects in a ListAction object (which the Action() 699 # factory above does). cmd will be passed to 700 # Environment.subst_list() for substituting environment 701 # variables. 702 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandAction') 703 704 _ActionAction.__init__(self, **kw) 705 if is_List(cmd): 706 if list(filter(is_List, cmd)): 707 raise TypeError("CommandAction should be given only " \ 708 "a single command") 709 self.cmd_list = cmd
710
711 - def __str__(self):
712 if is_List(self.cmd_list): 713 return ' '.join(map(str, self.cmd_list)) 714 return str(self.cmd_list)
715
716 - def process(self, target, source, env, executor=None):
717 if executor: 718 result = env.subst_list(self.cmd_list, 0, executor=executor) 719 else: 720 result = env.subst_list(self.cmd_list, 0, target, source) 721 silent = None 722 ignore = None 723 while True: 724 try: c = result[0][0][0] 725 except IndexError: c = None 726 if c == '@': silent = 1 727 elif c == '-': ignore = 1 728 else: break 729 result[0][0] = result[0][0][1:] 730 try: 731 if not result[0][0]: 732 result[0] = result[0][1:] 733 except IndexError: 734 pass 735 return result, ignore, silent
736
737 - def strfunction(self, target, source, env, executor=None):
738 if self.cmdstr is None: 739 return None 740 if self.cmdstr is not _null: 741 from SCons.Subst import SUBST_RAW 742 if executor: 743 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) 744 else: 745 c = env.subst(self.cmdstr, SUBST_RAW, target, source) 746 if c: 747 return c 748 cmd_list, ignore, silent = self.process(target, source, env, executor) 749 if silent: 750 return '' 751 return _string_from_cmd_list(cmd_list[0])
752
753 - def execute(self, target, source, env, executor=None):
754 """Execute a command action. 755 756 This will handle lists of commands as well as individual commands, 757 because construction variable substitution may turn a single 758 "command" into a list. This means that this class can actually 759 handle lists of commands, even though that's not how we use it 760 externally. 761 """ 762 escape_list = SCons.Subst.escape_list 763 flatten_sequence = SCons.Util.flatten_sequence 764 765 try: 766 shell = env['SHELL'] 767 except KeyError: 768 raise SCons.Errors.UserError('Missing SHELL construction variable.') 769 770 try: 771 spawn = env['SPAWN'] 772 except KeyError: 773 raise SCons.Errors.UserError('Missing SPAWN construction variable.') 774 else: 775 if is_String(spawn): 776 spawn = env.subst(spawn, raw=1, conv=lambda x: x) 777 778 escape = env.get('ESCAPE', lambda x: x) 779 780 ENV = get_default_ENV(env) 781 782 # Ensure that the ENV values are all strings: 783 for key, value in ENV.items(): 784 if not is_String(value): 785 if is_List(value): 786 # If the value is a list, then we assume it is a 787 # path list, because that's a pretty common list-like 788 # value to stick in an environment variable: 789 value = flatten_sequence(value) 790 ENV[key] = os.pathsep.join(map(str, value)) 791 else: 792 # If it isn't a string or a list, then we just coerce 793 # it to a string, which is the proper way to handle 794 # Dir and File instances and will produce something 795 # reasonable for just about everything else: 796 ENV[key] = str(value) 797 798 if executor: 799 target = executor.get_all_targets() 800 source = executor.get_all_sources() 801 cmd_list, ignore, silent = self.process(target, list(map(rfile, source)), env, executor) 802 803 # Use len() to filter out any "command" that's zero-length. 804 for cmd_line in filter(len, cmd_list): 805 # Escape the command line for the interpreter we are using. 806 cmd_line = escape_list(cmd_line, escape) 807 result = spawn(shell, escape, cmd_line[0], cmd_line, ENV) 808 if not ignore and result: 809 msg = "Error %s" % result 810 return SCons.Errors.BuildError(errstr=msg, 811 status=result, 812 action=self, 813 command=cmd_line) 814 return 0
815
816 - def get_presig(self, target, source, env, executor=None):
817 """Return the signature contents of this action's command line. 818 819 This strips $(-$) and everything in between the string, 820 since those parts don't affect signatures. 821 """ 822 from SCons.Subst import SUBST_SIG 823 cmd = self.cmd_list 824 if is_List(cmd): 825 cmd = ' '.join(map(str, cmd)) 826 else: 827 cmd = str(cmd) 828 if executor: 829 return env.subst_target_source(cmd, SUBST_SIG, executor=executor) 830 else: 831 return env.subst_target_source(cmd, SUBST_SIG, target, source)
832
833 - def get_implicit_deps(self, target, source, env, executor=None):
834 icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) 835 if is_String(icd) and icd[:1] == '$': 836 icd = env.subst(icd) 837 if not icd or icd in ('0', 'None'): 838 return [] 839 from SCons.Subst import SUBST_SIG 840 if executor: 841 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) 842 else: 843 cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, target, source) 844 res = [] 845 for cmd_line in cmd_list: 846 if cmd_line: 847 d = str(cmd_line[0]) 848 m = strip_quotes.match(d) 849 if m: 850 d = m.group(1) 851 d = env.WhereIs(d) 852 if d: 853 res.append(env.fs.File(d)) 854 return res
855
856 -class CommandGeneratorAction(ActionBase):
857 """Class for command-generator actions."""
858 - def __init__(self, generator, kw):
859 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.CommandGeneratorAction') 860 self.generator = generator 861 self.gen_kw = kw 862 self.varlist = kw.get('varlist', ()) 863 self.targets = kw.get('targets', '$TARGETS')
864
865 - def _generate(self, target, source, env, for_signature, executor=None):
866 # ensure that target is a list, to make it easier to write 867 # generator functions: 868 if not is_List(target): 869 target = [target] 870 871 if executor: 872 target = executor.get_all_targets() 873 source = executor.get_all_sources() 874 ret = self.generator(target=target, 875 source=source, 876 env=env, 877 for_signature=for_signature) 878 gen_cmd = Action(ret, **self.gen_kw) 879 if not gen_cmd: 880 raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret)) 881 return gen_cmd
882
883 - def __str__(self):
884 try: 885 env = self.presub_env 886 except AttributeError: 887 env = None 888 if env is None: 889 env = SCons.Defaults.DefaultEnvironment() 890 act = self._generate([], [], env, 1) 891 return str(act)
892
893 - def batch_key(self, env, target, source):
894 return self._generate(target, source, env, 1).batch_key(env, target, source)
895
896 - def genstring(self, target, source, env, executor=None):
897 return self._generate(target, source, env, 1, executor).genstring(target, source, env)
898
899 - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, 900 show=_null, execute=_null, chdir=_null, executor=None):
901 act = self._generate(target, source, env, 0, executor) 902 if act is None: 903 raise SCons.Errors.UserError("While building `%s': " 904 "Cannot deduce file extension from source files: %s" 905 % (repr(list(map(str, target))), repr(list(map(str, source))))) 906 return act(target, source, env, exitstatfunc, presub, 907 show, execute, chdir, executor)
908
909 - def get_presig(self, target, source, env, executor=None):
910 """Return the signature contents of this action's command line. 911 912 This strips $(-$) and everything in between the string, 913 since those parts don't affect signatures. 914 """ 915 return self._generate(target, source, env, 1, executor).get_presig(target, source, env)
916
917 - def get_implicit_deps(self, target, source, env, executor=None):
918 return self._generate(target, source, env, 1, executor).get_implicit_deps(target, source, env)
919
920 - def get_varlist(self, target, source, env, executor=None):
921 return self._generate(target, source, env, 1, executor).get_varlist(target, source, env, executor)
922
923 - def get_targets(self, env, executor):
924 return self._generate(None, None, env, 1, executor).get_targets(env, executor)
925 926 927 928 # A LazyAction is a kind of hybrid generator and command action for 929 # strings of the form "$VAR". These strings normally expand to other 930 # strings (think "$CCCOM" to "$CC -c -o $TARGET $SOURCE"), but we also 931 # want to be able to replace them with functions in the construction 932 # environment. Consequently, we want lazy evaluation and creation of 933 # an Action in the case of the function, but that's overkill in the more 934 # normal case of expansion to other strings. 935 # 936 # So we do this with a subclass that's both a generator *and* 937 # a command action. The overridden methods all do a quick check 938 # of the construction variable, and if it's a string we just call 939 # the corresponding CommandAction method to do the heavy lifting. 940 # If not, then we call the same-named CommandGeneratorAction method. 941 # The CommandGeneratorAction methods work by using the overridden 942 # _generate() method, that is, our own way of handling "generation" of 943 # an action based on what's in the construction variable. 944
945 -class LazyAction(CommandGeneratorAction, CommandAction):
946
947 - def __init__(self, var, kw):
948 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.LazyAction') 949 #FUTURE CommandAction.__init__(self, '${'+var+'}', **kw) 950 CommandAction.__init__(self, '${'+var+'}', **kw) 951 self.var = SCons.Util.to_String(var) 952 self.gen_kw = kw
953
954 - def get_parent_class(self, env):
955 c = env.get(self.var) 956 if is_String(c) and not '\n' in c: 957 return CommandAction 958 return CommandGeneratorAction
959
960 - def _generate_cache(self, env):
961 if env: 962 c = env.get(self.var, '') 963 else: 964 c = '' 965 gen_cmd = Action(c, **self.gen_kw) 966 if not gen_cmd: 967 raise SCons.Errors.UserError("$%s value %s cannot be used to create an Action." % (self.var, repr(c))) 968 return gen_cmd
969
970 - def _generate(self, target, source, env, for_signature, executor=None):
971 return self._generate_cache(env)
972
973 - def __call__(self, target, source, env, *args, **kw):
974 c = self.get_parent_class(env) 975 return c.__call__(self, target, source, env, *args, **kw)
976
977 - def get_presig(self, target, source, env):
978 c = self.get_parent_class(env) 979 return c.get_presig(self, target, source, env)
980
981 - def get_varlist(self, target, source, env, executor=None):
982 c = self.get_parent_class(env) 983 return c.get_varlist(self, target, source, env, executor)
984 985
986 -class FunctionAction(_ActionAction):
987 """Class for Python function actions.""" 988
989 - def __init__(self, execfunction, kw):
990 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.FunctionAction') 991 992 self.execfunction = execfunction 993 try: 994 self.funccontents = _callable_contents(execfunction) 995 except AttributeError: 996 try: 997 # See if execfunction will do the heavy lifting for us. 998 self.gc = execfunction.get_contents 999 except AttributeError: 1000 # This is weird, just do the best we can. 1001 self.funccontents = _object_contents(execfunction) 1002 1003 _ActionAction.__init__(self, **kw)
1004
1005 - def function_name(self):
1006 try: 1007 return self.execfunction.__name__ 1008 except AttributeError: 1009 try: 1010 return self.execfunction.__class__.__name__ 1011 except AttributeError: 1012 return "unknown_python_function"
1013
1014 - def strfunction(self, target, source, env, executor=None):
1015 if self.cmdstr is None: 1016 return None 1017 if self.cmdstr is not _null: 1018 from SCons.Subst import SUBST_RAW 1019 if executor: 1020 c = env.subst(self.cmdstr, SUBST_RAW, executor=executor) 1021 else: 1022 c = env.subst(self.cmdstr, SUBST_RAW, target, source) 1023 if c: 1024 return c 1025 def array(a): 1026 def quote(s): 1027 try: 1028 str_for_display = s.str_for_display 1029 except AttributeError: 1030 s = repr(s) 1031 else: 1032 s = str_for_display() 1033 return s
1034 return '[' + ", ".join(map(quote, a)) + ']'
1035 try: 1036 strfunc = self.execfunction.strfunction 1037 except AttributeError: 1038 pass 1039 else: 1040 if strfunc is None: 1041 return None 1042 if callable(strfunc): 1043 return strfunc(target, source, env) 1044 name = self.function_name() 1045 tstr = array(target) 1046 sstr = array(source) 1047 return "%s(%s, %s)" % (name, tstr, sstr) 1048
1049 - def __str__(self):
1050 name = self.function_name() 1051 if name == 'ActionCaller': 1052 return str(self.execfunction) 1053 return "%s(target, source, env)" % name
1054
1055 - def execute(self, target, source, env, executor=None):
1056 exc_info = (None,None,None) 1057 try: 1058 if executor: 1059 target = executor.get_all_targets() 1060 source = executor.get_all_sources() 1061 rsources = list(map(rfile, source)) 1062 try: 1063 result = self.execfunction(target=target, source=rsources, env=env) 1064 except KeyboardInterrupt, e: 1065 raise 1066 except SystemExit, e: 1067 raise 1068 except Exception, e: 1069 result = e 1070 exc_info = sys.exc_info() 1071 1072 if result: 1073 result = SCons.Errors.convert_to_BuildError(result, exc_info) 1074 result.node=target 1075 result.action=self 1076 try: 1077 result.command=self.strfunction(target, source, env, executor) 1078 except TypeError: 1079 result.command=self.strfunction(target, source, env) 1080 1081 # FIXME: This maintains backward compatibility with respect to 1082 # which type of exceptions were returned by raising an 1083 # exception and which ones were returned by value. It would 1084 # probably be best to always return them by value here, but 1085 # some codes do not check the return value of Actions and I do 1086 # not have the time to modify them at this point. 1087 if (exc_info[1] and 1088 not isinstance(exc_info[1],EnvironmentError)): 1089 raise result 1090 1091 return result 1092 finally: 1093 # Break the cycle between the traceback object and this 1094 # function stack frame. See the sys.exc_info() doc info for 1095 # more information about this issue. 1096 del exc_info
1097 1098
1099 - def get_presig(self, target, source, env):
1100 """Return the signature contents of this callable action.""" 1101 try: 1102 return self.gc(target, source, env) 1103 except AttributeError: 1104 return self.funccontents
1105
1106 - def get_implicit_deps(self, target, source, env):
1107 return []
1108
1109 -class ListAction(ActionBase):
1110 """Class for lists of other actions."""
1111 - def __init__(self, actionlist):
1112 if SCons.Debug.track_instances: logInstanceCreation(self, 'Action.ListAction') 1113 def list_of_actions(x): 1114 if isinstance(x, ActionBase): 1115 return x 1116 return Action(x)
1117 self.list = list(map(list_of_actions, actionlist)) 1118 # our children will have had any varlist 1119 # applied; we don't need to do it again 1120 self.varlist = () 1121 self.targets = '$TARGETS'
1122
1123 - def genstring(self, target, source, env):
1124 return '\n'.join([a.genstring(target, source, env) for a in self.list])
1125
1126 - def __str__(self):
1127 return '\n'.join(map(str, self.list))
1128
1129 - def presub_lines(self, env):
1130 return SCons.Util.flatten_sequence( 1131 [a.presub_lines(env) for a in self.list])
1132
1133 - def get_presig(self, target, source, env):
1134 """Return the signature contents of this action list. 1135 1136 Simple concatenation of the signatures of the elements. 1137 """ 1138 return "".join([x.get_contents(target, source, env) for x in self.list])
1139
1140 - def __call__(self, target, source, env, exitstatfunc=_null, presub=_null, 1141 show=_null, execute=_null, chdir=_null, executor=None):
1142 if executor: 1143 target = executor.get_all_targets() 1144 source = executor.get_all_sources() 1145 for act in self.list: 1146 stat = act(target, source, env, exitstatfunc, presub, 1147 show, execute, chdir, executor) 1148 if stat: 1149 return stat 1150 return 0
1151
1152 - def get_implicit_deps(self, target, source, env):
1153 result = [] 1154 for act in self.list: 1155 result.extend(act.get_implicit_deps(target, source, env)) 1156 return result
1157
1158 - def get_varlist(self, target, source, env, executor=None):
1159 result = SCons.Util.OrderedDict() 1160 for act in self.list: 1161 for var in act.get_varlist(target, source, env, executor): 1162 result[var] = True 1163 return list(result.keys())
1164
1165 -class ActionCaller(object):
1166 """A class for delaying calling an Action function with specific 1167 (positional and keyword) arguments until the Action is actually 1168 executed. 1169 1170 This class looks to the rest of the world like a normal Action object, 1171 but what it's really doing is hanging on to the arguments until we 1172 have a target, source and env to use for the expansion. 1173 """
1174 - def __init__(self, parent, args, kw):
1175 self.parent = parent 1176 self.args = args 1177 self.kw = kw
1178
1179 - def get_contents(self, target, source, env):
1180 actfunc = self.parent.actfunc 1181 try: 1182 # "self.actfunc" is a function. 1183 contents = str(actfunc.func_code.co_code) 1184 except AttributeError: 1185 # "self.actfunc" is a callable object. 1186 try: 1187 contents = str(actfunc.__call__.im_func.func_code.co_code) 1188 except AttributeError: 1189 # No __call__() method, so it might be a builtin 1190 # or something like that. Do the best we can. 1191 contents = str(actfunc) 1192 contents = remove_set_lineno_codes(contents) 1193 return contents
1194
1195 - def subst(self, s, target, source, env):
1196 # If s is a list, recursively apply subst() 1197 # to every element in the list 1198 if is_List(s): 1199 result = [] 1200 for elem in s: 1201 result.append(self.subst(elem, target, source, env)) 1202 return self.parent.convert(result) 1203 1204 # Special-case hack: Let a custom function wrapped in an 1205 # ActionCaller get at the environment through which the action 1206 # was called by using this hard-coded value as a special return. 1207 if s == '$__env__': 1208 return env 1209 elif is_String(s): 1210 return env.subst(s, 1, target, source) 1211 return self.parent.convert(s)
1212
1213 - def subst_args(self, target, source, env):
1214 return [self.subst(x, target, source, env) for x in self.args]
1215
1216 - def subst_kw(self, target, source, env):
1217 kw = {} 1218 for key in self.kw.keys(): 1219 kw[key] = self.subst(self.kw[key], target, source, env) 1220 return kw
1221
1222 - def __call__(self, target, source, env, executor=None):
1223 args = self.subst_args(target, source, env) 1224 kw = self.subst_kw(target, source, env) 1225 return self.parent.actfunc(*args, **kw)
1226
1227 - def strfunction(self, target, source, env):
1228 args = self.subst_args(target, source, env) 1229 kw = self.subst_kw(target, source, env) 1230 return self.parent.strfunc(*args, **kw)
1231
1232 - def __str__(self):
1233 return self.parent.strfunc(*self.args, **self.kw)
1234
1235 -class ActionFactory(object):
1236 """A factory class that will wrap up an arbitrary function 1237 as an SCons-executable Action object. 1238 1239 The real heavy lifting here is done by the ActionCaller class. 1240 We just collect the (positional and keyword) arguments that we're 1241 called with and give them to the ActionCaller object we create, 1242 so it can hang onto them until it needs them. 1243 """
1244 - def __init__(self, actfunc, strfunc, convert=lambda x: x):
1245 self.actfunc = actfunc 1246 self.strfunc = strfunc 1247 self.convert = convert
1248
1249 - def __call__(self, *args, **kw):
1250 ac = ActionCaller(self, args, kw) 1251 action = Action(ac, strfunction=ac.strfunction) 1252 return action
1253 1254 # Local Variables: 1255 # tab-width:4 1256 # indent-tabs-mode:nil 1257 # End: 1258 # vim: set expandtab tabstop=4 shiftwidth=4: 1259