Package SCons :: Module Environment
[hide private]
[frames] | no frames]

Source Code for Module SCons.Environment

   1  """SCons.Environment 
   2   
   3  Base class for construction Environments.  These are 
   4  the primary objects used to communicate dependency and 
   5  construction information to the build engine. 
   6   
   7  Keyword arguments supplied when the construction Environment 
   8  is created are construction variables used to initialize the 
   9  Environment 
  10  """ 
  11   
  12  # 
  13  # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation 
  14  # 
  15  # Permission is hereby granted, free of charge, to any person obtaining 
  16  # a copy of this software and associated documentation files (the 
  17  # "Software"), to deal in the Software without restriction, including 
  18  # without limitation the rights to use, copy, modify, merge, publish, 
  19  # distribute, sublicense, and/or sell copies of the Software, and to 
  20  # permit persons to whom the Software is furnished to do so, subject to 
  21  # the following conditions: 
  22  # 
  23  # The above copyright notice and this permission notice shall be included 
  24  # in all copies or substantial portions of the Software. 
  25  # 
  26  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 
  27  # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 
  28  # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 
  29  # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 
  30  # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 
  31  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 
  32  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 
  33  # 
  34   
  35  __revision__ = "src/engine/SCons/Environment.py 4629 2010/01/17 22:23:21 scons" 
  36   
  37   
  38  import copy 
  39  import os 
  40  import sys 
  41  import re 
  42  import shlex 
  43  import string 
  44  from UserDict import UserDict 
  45   
  46  import SCons.Action 
  47  import SCons.Builder 
  48  from SCons.Debug import logInstanceCreation 
  49  import SCons.Defaults 
  50  import SCons.Errors 
  51  import SCons.Memoize 
  52  import SCons.Node 
  53  import SCons.Node.Alias 
  54  import SCons.Node.FS 
  55  import SCons.Node.Python 
  56  import SCons.Platform 
  57  import SCons.SConf 
  58  import SCons.SConsign 
  59  import SCons.Subst 
  60  import SCons.Tool 
  61  import SCons.Util 
  62  import SCons.Warnings 
  63   
64 -class _Null:
65 pass
66 67 _null = _Null 68 69 _warn_copy_deprecated = True 70 _warn_source_signatures_deprecated = True 71 _warn_target_signatures_deprecated = True 72 73 CleanTargets = {} 74 CalculatorArgs = {} 75 76 semi_deepcopy = SCons.Util.semi_deepcopy 77 78 # Pull UserError into the global name space for the benefit of 79 # Environment().SourceSignatures(), which has some import statements 80 # which seem to mess up its ability to reference SCons directly. 81 UserError = SCons.Errors.UserError 82
83 -def alias_builder(env, target, source):
84 pass
85 86 AliasBuilder = SCons.Builder.Builder(action = alias_builder, 87 target_factory = SCons.Node.Alias.default_ans.Alias, 88 source_factory = SCons.Node.FS.Entry, 89 multi = 1, 90 is_explicit = None, 91 name='AliasBuilder') 92
93 -def apply_tools(env, tools, toolpath):
94 # Store the toolpath in the Environment. 95 if toolpath is not None: 96 env['toolpath'] = toolpath 97 98 if not tools: 99 return 100 # Filter out null tools from the list. 101 for tool in filter(None, tools): 102 if SCons.Util.is_List(tool) or type(tool)==type(()): 103 toolname = tool[0] 104 toolargs = tool[1] # should be a dict of kw args 105 tool = apply(env.Tool, [toolname], toolargs) 106 else: 107 env.Tool(tool)
108 109 # These names are (or will be) controlled by SCons; users should never 110 # set or override them. This warning can optionally be turned off, 111 # but scons will still ignore the illegal variable names even if it's off. 112 reserved_construction_var_names = [ 113 'CHANGED_SOURCES', 114 'CHANGED_TARGETS', 115 'SOURCE', 116 'SOURCES', 117 'TARGET', 118 'TARGETS', 119 'UNCHANGED_SOURCES', 120 'UNCHANGED_TARGETS', 121 ] 122 123 future_reserved_construction_var_names = [ 124 #'HOST_OS', 125 #'HOST_ARCH', 126 #'HOST_CPU', 127 ] 128
129 -def copy_non_reserved_keywords(dict):
130 result = semi_deepcopy(dict) 131 for k in result.keys(): 132 if k in reserved_construction_var_names: 133 msg = "Ignoring attempt to set reserved variable `$%s'" 134 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k) 135 del result[k] 136 return result
137
138 -def _set_reserved(env, key, value):
139 msg = "Ignoring attempt to set reserved variable `$%s'" 140 SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
141
142 -def _set_future_reserved(env, key, value):
143 env._dict[key] = value 144 msg = "`$%s' will be reserved in a future release and setting it will become ignored" 145 SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
146
147 -def _set_BUILDERS(env, key, value):
148 try: 149 bd = env._dict[key] 150 for k in bd.keys(): 151 del bd[k] 152 except KeyError: 153 bd = BuilderDict(kwbd, env) 154 env._dict[key] = bd 155 bd.update(value)
156
157 -def _del_SCANNERS(env, key):
158 del env._dict[key] 159 env.scanner_map_delete()
160
161 -def _set_SCANNERS(env, key, value):
162 env._dict[key] = value 163 env.scanner_map_delete()
164
165 -def _delete_duplicates(l, keep_last):
166 """Delete duplicates from a sequence, keeping the first or last.""" 167 seen={} 168 result=[] 169 if keep_last: # reverse in & out, then keep first 170 l.reverse() 171 for i in l: 172 try: 173 if not seen.has_key(i): 174 result.append(i) 175 seen[i]=1 176 except TypeError: 177 # probably unhashable. Just keep it. 178 result.append(i) 179 if keep_last: 180 result.reverse() 181 return result
182 183 184 185 # The following is partly based on code in a comment added by Peter 186 # Shannon at the following page (there called the "transplant" class): 187 # 188 # ASPN : Python Cookbook : Dynamically added methods to a class 189 # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732 190 # 191 # We had independently been using the idiom as BuilderWrapper, but 192 # factoring out the common parts into this base class, and making 193 # BuilderWrapper a subclass that overrides __call__() to enforce specific 194 # Builder calling conventions, simplified some of our higher-layer code. 195
196 -class MethodWrapper:
197 """ 198 A generic Wrapper class that associates a method (which can 199 actually be any callable) with an object. As part of creating this 200 MethodWrapper object an attribute with the specified (by default, 201 the name of the supplied method) is added to the underlying object. 202 When that new "method" is called, our __call__() method adds the 203 object as the first argument, simulating the Python behavior of 204 supplying "self" on method calls. 205 206 We hang on to the name by which the method was added to the underlying 207 base class so that we can provide a method to "clone" ourselves onto 208 a new underlying object being copied (without which we wouldn't need 209 to save that info). 210 """
211 - def __init__(self, object, method, name=None):
212 if name is None: 213 name = method.__name__ 214 self.object = object 215 self.method = method 216 self.name = name 217 setattr(self.object, name, self)
218
219 - def __call__(self, *args, **kwargs):
220 nargs = (self.object,) + args 221 return apply(self.method, nargs, kwargs)
222
223 - def clone(self, new_object):
224 """ 225 Returns an object that re-binds the underlying "method" to 226 the specified new object. 227 """ 228 return self.__class__(new_object, self.method, self.name)
229
230 -class BuilderWrapper(MethodWrapper):
231 """ 232 A MethodWrapper subclass that that associates an environment with 233 a Builder. 234 235 This mainly exists to wrap the __call__() function so that all calls 236 to Builders can have their argument lists massaged in the same way 237 (treat a lone argument as the source, treat two arguments as target 238 then source, make sure both target and source are lists) without 239 having to have cut-and-paste code to do it. 240 241 As a bit of obsessive backwards compatibility, we also intercept 242 attempts to get or set the "env" or "builder" attributes, which were 243 the names we used before we put the common functionality into the 244 MethodWrapper base class. We'll keep this around for a while in case 245 people shipped Tool modules that reached into the wrapper (like the 246 Tool/qt.py module does, or did). There shouldn't be a lot attribute 247 fetching or setting on these, so a little extra work shouldn't hurt. 248 """
249 - def __call__(self, target=None, source=_null, *args, **kw):
250 if source is _null: 251 source = target 252 target = None 253 if target is not None and not SCons.Util.is_List(target): 254 target = [target] 255 if source is not None and not SCons.Util.is_List(source): 256 source = [source] 257 return apply(MethodWrapper.__call__, (self, target, source) + args, kw)
258
259 - def __repr__(self):
260 return '<BuilderWrapper %s>' % repr(self.name)
261
262 - def __str__(self):
263 return self.__repr__()
264
265 - def __getattr__(self, name):
266 if name == 'env': 267 return self.object 268 elif name == 'builder': 269 return self.method 270 else: 271 raise AttributeError, name
272
273 - def __setattr__(self, name, value):
274 if name == 'env': 275 self.object = value 276 elif name == 'builder': 277 self.method = value 278 else: 279 self.__dict__[name] = value
280 281 # This allows a Builder to be executed directly 282 # through the Environment to which it's attached. 283 # In practice, we shouldn't need this, because 284 # builders actually get executed through a Node. 285 # But we do have a unit test for this, and can't 286 # yet rule out that it would be useful in the 287 # future, so leave it for now. 288 #def execute(self, **kw): 289 # kw['env'] = self.env 290 # apply(self.builder.execute, (), kw) 291
292 -class BuilderDict(UserDict):
293 """This is a dictionary-like class used by an Environment to hold 294 the Builders. We need to do this because every time someone changes 295 the Builders in the Environment's BUILDERS dictionary, we must 296 update the Environment's attributes."""
297 - def __init__(self, dict, env):
298 # Set self.env before calling the superclass initialization, 299 # because it will end up calling our other methods, which will 300 # need to point the values in this dictionary to self.env. 301 self.env = env 302 UserDict.__init__(self, dict)
303
304 - def __semi_deepcopy__(self):
305 return self.__class__(self.data, self.env)
306
307 - def __setitem__(self, item, val):
308 try: 309 method = getattr(self.env, item).method 310 except AttributeError: 311 pass 312 else: 313 self.env.RemoveMethod(method) 314 UserDict.__setitem__(self, item, val) 315 BuilderWrapper(self.env, val, item)
316
317 - def __delitem__(self, item):
318 UserDict.__delitem__(self, item) 319 delattr(self.env, item)
320
321 - def update(self, dict):
322 for i, v in dict.items(): 323 self.__setitem__(i, v)
324 325 326 327 _is_valid_var = re.compile(r'[_a-zA-Z]\w*$') 328
329 -def is_valid_construction_var(varstr):
330 """Return if the specified string is a legitimate construction 331 variable. 332 """ 333 return _is_valid_var.match(varstr)
334 335 336
337 -class SubstitutionEnvironment:
338 """Base class for different flavors of construction environments. 339 340 This class contains a minimal set of methods that handle contruction 341 variable expansion and conversion of strings to Nodes, which may or 342 may not be actually useful as a stand-alone class. Which methods 343 ended up in this class is pretty arbitrary right now. They're 344 basically the ones which we've empirically determined are common to 345 the different construction environment subclasses, and most of the 346 others that use or touch the underlying dictionary of construction 347 variables. 348 349 Eventually, this class should contain all the methods that we 350 determine are necessary for a "minimal" interface to the build engine. 351 A full "native Python" SCons environment has gotten pretty heavyweight 352 with all of the methods and Tools and construction variables we've 353 jammed in there, so it would be nice to have a lighter weight 354 alternative for interfaces that don't need all of the bells and 355 whistles. (At some point, we'll also probably rename this class 356 "Base," since that more reflects what we want this class to become, 357 but because we've released comments that tell people to subclass 358 Environment.Base to create their own flavors of construction 359 environment, we'll save that for a future refactoring when this 360 class actually becomes useful.) 361 """ 362 363 if SCons.Memoize.use_memoizer: 364 __metaclass__ = SCons.Memoize.Memoized_Metaclass 365
366 - def __init__(self, **kw):
367 """Initialization of an underlying SubstitutionEnvironment class. 368 """ 369 if __debug__: logInstanceCreation(self, 'Environment.SubstitutionEnvironment') 370 self.fs = SCons.Node.FS.get_default_fs() 371 self.ans = SCons.Node.Alias.default_ans 372 self.lookup_list = SCons.Node.arg2nodes_lookups 373 self._dict = kw.copy() 374 self._init_special() 375 self.added_methods = []
376 #self._memo = {} 377
378 - def _init_special(self):
379 """Initial the dispatch tables for special handling of 380 special construction variables.""" 381 self._special_del = {} 382 self._special_del['SCANNERS'] = _del_SCANNERS 383 384 self._special_set = {} 385 for key in reserved_construction_var_names: 386 self._special_set[key] = _set_reserved 387 for key in future_reserved_construction_var_names: 388 self._special_set[key] = _set_future_reserved 389 self._special_set['BUILDERS'] = _set_BUILDERS 390 self._special_set['SCANNERS'] = _set_SCANNERS 391 392 # Freeze the keys of self._special_set in a list for use by 393 # methods that need to check. (Empirically, list scanning has 394 # gotten better than dict.has_key() in Python 2.5.) 395 self._special_set_keys = self._special_set.keys()
396
397 - def __cmp__(self, other):
398 return cmp(self._dict, other._dict)
399
400 - def __delitem__(self, key):
401 special = self._special_del.get(key) 402 if special: 403 special(self, key) 404 else: 405 del self._dict[key]
406
407 - def __getitem__(self, key):
408 return self._dict[key]
409
410 - def __setitem__(self, key, value):
411 # This is heavily used. This implementation is the best we have 412 # according to the timings in bench/env.__setitem__.py. 413 # 414 # The "key in self._special_set_keys" test here seems to perform 415 # pretty well for the number of keys we have. A hard-coded 416 # list works a little better in Python 2.5, but that has the 417 # disadvantage of maybe getting out of sync if we ever add more 418 # variable names. Using self._special_set.has_key() works a 419 # little better in Python 2.4, but is worse then this test. 420 # So right now it seems like a good trade-off, but feel free to 421 # revisit this with bench/env.__setitem__.py as needed (and 422 # as newer versions of Python come out). 423 if key in self._special_set_keys: 424 self._special_set[key](self, key, value) 425 else: 426 # If we already have the entry, then it's obviously a valid 427 # key and we don't need to check. If we do check, using a 428 # global, pre-compiled regular expression directly is more 429 # efficient than calling another function or a method. 430 if not self._dict.has_key(key) \ 431 and not _is_valid_var.match(key): 432 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key 433 self._dict[key] = value
434
435 - def get(self, key, default=None):
436 """Emulates the get() method of dictionaries.""" 437 return self._dict.get(key, default)
438
439 - def has_key(self, key):
440 return self._dict.has_key(key)
441
442 - def __contains__(self, key):
443 return self._dict.__contains__(key)
444
445 - def items(self):
446 return self._dict.items()
447
448 - def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
449 if node_factory is _null: 450 node_factory = self.fs.File 451 if lookup_list is _null: 452 lookup_list = self.lookup_list 453 454 if not args: 455 return [] 456 457 args = SCons.Util.flatten(args) 458 459 nodes = [] 460 for v in args: 461 if SCons.Util.is_String(v): 462 n = None 463 for l in lookup_list: 464 n = l(v) 465 if n is not None: 466 break 467 if n is not None: 468 if SCons.Util.is_String(n): 469 # n = self.subst(n, raw=1, **kw) 470 kw['raw'] = 1 471 n = apply(self.subst, (n,), kw) 472 if node_factory: 473 n = node_factory(n) 474 if SCons.Util.is_List(n): 475 nodes.extend(n) 476 else: 477 nodes.append(n) 478 elif node_factory: 479 # v = node_factory(self.subst(v, raw=1, **kw)) 480 kw['raw'] = 1 481 v = node_factory(apply(self.subst, (v,), kw)) 482 if SCons.Util.is_List(v): 483 nodes.extend(v) 484 else: 485 nodes.append(v) 486 else: 487 nodes.append(v) 488 489 return nodes
490
491 - def gvars(self):
492 return self._dict
493
494 - def lvars(self):
495 return {}
496
497 - def subst(self, string, raw=0, target=None, source=None, conv=None, executor=None):
498 """Recursively interpolates construction variables from the 499 Environment into the specified string, returning the expanded 500 result. Construction variables are specified by a $ prefix 501 in the string and begin with an initial underscore or 502 alphabetic character followed by any number of underscores 503 or alphanumeric characters. The construction variable names 504 may be surrounded by curly braces to separate the name from 505 trailing characters. 506 """ 507 gvars = self.gvars() 508 lvars = self.lvars() 509 lvars['__env__'] = self 510 if executor: 511 lvars.update(executor.get_lvars()) 512 return SCons.Subst.scons_subst(string, self, raw, target, source, gvars, lvars, conv)
513
514 - def subst_kw(self, kw, raw=0, target=None, source=None):
515 nkw = {} 516 for k, v in kw.items(): 517 k = self.subst(k, raw, target, source) 518 if SCons.Util.is_String(v): 519 v = self.subst(v, raw, target, source) 520 nkw[k] = v 521 return nkw
522
523 - def subst_list(self, string, raw=0, target=None, source=None, conv=None, executor=None):
524 """Calls through to SCons.Subst.scons_subst_list(). See 525 the documentation for that function.""" 526 gvars = self.gvars() 527 lvars = self.lvars() 528 lvars['__env__'] = self 529 if executor: 530 lvars.update(executor.get_lvars()) 531 return SCons.Subst.scons_subst_list(string, self, raw, target, source, gvars, lvars, conv)
532
533 - def subst_path(self, path, target=None, source=None):
534 """Substitute a path list, turning EntryProxies into Nodes 535 and leaving Nodes (and other objects) as-is.""" 536 537 if not SCons.Util.is_List(path): 538 path = [path] 539 540 def s(obj): 541 """This is the "string conversion" routine that we have our 542 substitutions use to return Nodes, not strings. This relies 543 on the fact that an EntryProxy object has a get() method that 544 returns the underlying Node that it wraps, which is a bit of 545 architectural dependence that we might need to break or modify 546 in the future in response to additional requirements.""" 547 try: 548 get = obj.get 549 except AttributeError: 550 obj = SCons.Util.to_String_for_subst(obj) 551 else: 552 obj = get() 553 return obj
554 555 r = [] 556 for p in path: 557 if SCons.Util.is_String(p): 558 p = self.subst(p, target=target, source=source, conv=s) 559 if SCons.Util.is_List(p): 560 if len(p) == 1: 561 p = p[0] 562 else: 563 # We have an object plus a string, or multiple 564 # objects that we need to smush together. No choice 565 # but to make them into a string. 566 p = string.join(map(SCons.Util.to_String_for_subst, p), '') 567 else: 568 p = s(p) 569 r.append(p) 570 return r
571 572 subst_target_source = subst 573
574 - def backtick(self, command):
575 import subprocess 576 # common arguments 577 kw = { 'stdin' : 'devnull', 578 'stdout' : subprocess.PIPE, 579 'stderr' : subprocess.PIPE, 580 'universal_newlines' : True, 581 } 582 # if the command is a list, assume it's been quoted 583 # othewise force a shell 584 if not SCons.Util.is_List(command): kw['shell'] = True 585 # run constructed command 586 #TODO(1.5) p = SCons.Action._subproc(self, command, **kw) 587 p = apply(SCons.Action._subproc, (self, command), kw) 588 out,err = p.communicate() 589 status = p.wait() 590 if err: 591 sys.stderr.write(err) 592 if status: 593 raise OSError("'%s' exited %d" % (command, status)) 594 return out
595
596 - def AddMethod(self, function, name=None):
597 """ 598 Adds the specified function as a method of this construction 599 environment with the specified name. If the name is omitted, 600 the default name is the name of the function itself. 601 """ 602 method = MethodWrapper(self, function, name) 603 self.added_methods.append(method)
604
605 - def RemoveMethod(self, function):
606 """ 607 Removes the specified function's MethodWrapper from the 608 added_methods list, so we don't re-bind it when making a clone. 609 """ 610 is_not_func = lambda dm, f=function: not dm.method is f 611 self.added_methods = filter(is_not_func, self.added_methods)
612
613 - def Override(self, overrides):
614 """ 615 Produce a modified environment whose variables are overriden by 616 the overrides dictionaries. "overrides" is a dictionary that 617 will override the variables of this environment. 618 619 This function is much more efficient than Clone() or creating 620 a new Environment because it doesn't copy the construction 621 environment dictionary, it just wraps the underlying construction 622 environment, and doesn't even create a wrapper object if there 623 are no overrides. 624 """ 625 if not overrides: return self 626 o = copy_non_reserved_keywords(overrides) 627 if not o: return self 628 overrides = {} 629 merges = None 630 for key, value in o.items(): 631 if key == 'parse_flags': 632 merges = value 633 else: 634 overrides[key] = SCons.Subst.scons_subst_once(value, self, key) 635 env = OverrideEnvironment(self, overrides) 636 if merges: env.MergeFlags(merges) 637 return env
638
639 - def ParseFlags(self, *flags):
640 """ 641 Parse the set of flags and return a dict with the flags placed 642 in the appropriate entry. The flags are treated as a typical 643 set of command-line flags for a GNU-like toolchain and used to 644 populate the entries in the dict immediately below. If one of 645 the flag strings begins with a bang (exclamation mark), it is 646 assumed to be a command and the rest of the string is executed; 647 the result of that evaluation is then added to the dict. 648 """ 649 dict = { 650 'ASFLAGS' : SCons.Util.CLVar(''), 651 'CFLAGS' : SCons.Util.CLVar(''), 652 'CCFLAGS' : SCons.Util.CLVar(''), 653 'CPPDEFINES' : [], 654 'CPPFLAGS' : SCons.Util.CLVar(''), 655 'CPPPATH' : [], 656 'FRAMEWORKPATH' : SCons.Util.CLVar(''), 657 'FRAMEWORKS' : SCons.Util.CLVar(''), 658 'LIBPATH' : [], 659 'LIBS' : [], 660 'LINKFLAGS' : SCons.Util.CLVar(''), 661 'RPATH' : [], 662 } 663 664 # The use of the "me" parameter to provide our own name for 665 # recursion is an egregious hack to support Python 2.1 and before. 666 def do_parse(arg, me, self = self, dict = dict): 667 # if arg is a sequence, recurse with each element 668 if not arg: 669 return 670 671 if not SCons.Util.is_String(arg): 672 for t in arg: me(t, me) 673 return 674 675 # if arg is a command, execute it 676 if arg[0] == '!': 677 arg = self.backtick(arg[1:]) 678 679 # utility function to deal with -D option 680 def append_define(name, dict = dict): 681 t = string.split(name, '=') 682 if len(t) == 1: 683 dict['CPPDEFINES'].append(name) 684 else: 685 dict['CPPDEFINES'].append([t[0], string.join(t[1:], '=')])
686 687 # Loop through the flags and add them to the appropriate option. 688 # This tries to strike a balance between checking for all possible 689 # flags and keeping the logic to a finite size, so it doesn't 690 # check for some that don't occur often. It particular, if the 691 # flag is not known to occur in a config script and there's a way 692 # of passing the flag to the right place (by wrapping it in a -W 693 # flag, for example) we don't check for it. Note that most 694 # preprocessor options are not handled, since unhandled options 695 # are placed in CCFLAGS, so unless the preprocessor is invoked 696 # separately, these flags will still get to the preprocessor. 697 # Other options not currently handled: 698 # -iqoutedir (preprocessor search path) 699 # -u symbol (linker undefined symbol) 700 # -s (linker strip files) 701 # -static* (linker static binding) 702 # -shared* (linker dynamic binding) 703 # -symbolic (linker global binding) 704 # -R dir (deprecated linker rpath) 705 # IBM compilers may also accept -qframeworkdir=foo 706 707 params = shlex.split(arg) 708 append_next_arg_to = None # for multi-word args 709 for arg in params: 710 if append_next_arg_to: 711 if append_next_arg_to == 'CPPDEFINES': 712 append_define(arg) 713 elif append_next_arg_to == '-include': 714 t = ('-include', self.fs.File(arg)) 715 dict['CCFLAGS'].append(t) 716 elif append_next_arg_to == '-isysroot': 717 t = ('-isysroot', arg) 718 dict['CCFLAGS'].append(t) 719 dict['LINKFLAGS'].append(t) 720 elif append_next_arg_to == '-arch': 721 t = ('-arch', arg) 722 dict['CCFLAGS'].append(t) 723 dict['LINKFLAGS'].append(t) 724 else: 725 dict[append_next_arg_to].append(arg) 726 append_next_arg_to = None 727 elif not arg[0] in ['-', '+']: 728 dict['LIBS'].append(self.fs.File(arg)) 729 elif arg[:2] == '-L': 730 if arg[2:]: 731 dict['LIBPATH'].append(arg[2:]) 732 else: 733 append_next_arg_to = 'LIBPATH' 734 elif arg[:2] == '-l': 735 if arg[2:]: 736 dict['LIBS'].append(arg[2:]) 737 else: 738 append_next_arg_to = 'LIBS' 739 elif arg[:2] == '-I': 740 if arg[2:]: 741 dict['CPPPATH'].append(arg[2:]) 742 else: 743 append_next_arg_to = 'CPPPATH' 744 elif arg[:4] == '-Wa,': 745 dict['ASFLAGS'].append(arg[4:]) 746 dict['CCFLAGS'].append(arg) 747 elif arg[:4] == '-Wl,': 748 if arg[:11] == '-Wl,-rpath=': 749 dict['RPATH'].append(arg[11:]) 750 elif arg[:7] == '-Wl,-R,': 751 dict['RPATH'].append(arg[7:]) 752 elif arg[:6] == '-Wl,-R': 753 dict['RPATH'].append(arg[6:]) 754 else: 755 dict['LINKFLAGS'].append(arg) 756 elif arg[:4] == '-Wp,': 757 dict['CPPFLAGS'].append(arg) 758 elif arg[:2] == '-D': 759 if arg[2:]: 760 append_define(arg[2:]) 761 else: 762 append_next_arg_to = 'CPPDEFINES' 763 elif arg == '-framework': 764 append_next_arg_to = 'FRAMEWORKS' 765 elif arg[:14] == '-frameworkdir=': 766 dict['FRAMEWORKPATH'].append(arg[14:]) 767 elif arg[:2] == '-F': 768 if arg[2:]: 769 dict['FRAMEWORKPATH'].append(arg[2:]) 770 else: 771 append_next_arg_to = 'FRAMEWORKPATH' 772 elif arg == '-mno-cygwin': 773 dict['CCFLAGS'].append(arg) 774 dict['LINKFLAGS'].append(arg) 775 elif arg == '-mwindows': 776 dict['LINKFLAGS'].append(arg) 777 elif arg == '-pthread': 778 dict['CCFLAGS'].append(arg) 779 dict['LINKFLAGS'].append(arg) 780 elif arg[:5] == '-std=': 781 dict['CFLAGS'].append(arg) # C only 782 elif arg[0] == '+': 783 dict['CCFLAGS'].append(arg) 784 dict['LINKFLAGS'].append(arg) 785 elif arg in ['-include', '-isysroot', '-arch']: 786 append_next_arg_to = arg 787 else: 788 dict['CCFLAGS'].append(arg) 789 790 for arg in flags: 791 do_parse(arg, do_parse) 792 return dict 793
794 - def MergeFlags(self, args, unique=1, dict=None):
795 """ 796 Merge the dict in args into the construction variables of this 797 env, or the passed-in dict. If args is not a dict, it is 798 converted into a dict using ParseFlags. If unique is not set, 799 the flags are appended rather than merged. 800 """ 801 802 if dict is None: 803 dict = self 804 if not SCons.Util.is_Dict(args): 805 args = self.ParseFlags(args) 806 if not unique: 807 apply(self.Append, (), args) 808 return self 809 for key, value in args.items(): 810 if not value: 811 continue 812 try: 813 orig = self[key] 814 except KeyError: 815 orig = value 816 else: 817 if not orig: 818 orig = value 819 elif value: 820 # Add orig and value. The logic here was lifted from 821 # part of env.Append() (see there for a lot of comments 822 # about the order in which things are tried) and is 823 # used mainly to handle coercion of strings to CLVar to 824 # "do the right thing" given (e.g.) an original CCFLAGS 825 # string variable like '-pipe -Wall'. 826 try: 827 orig = orig + value 828 except (KeyError, TypeError): 829 try: 830 add_to_orig = orig.append 831 except AttributeError: 832 value.insert(0, orig) 833 orig = value 834 else: 835 add_to_orig(value) 836 t = [] 837 if key[-4:] == 'PATH': 838 ### keep left-most occurence 839 for v in orig: 840 if v not in t: 841 t.append(v) 842 else: 843 ### keep right-most occurence 844 orig.reverse() 845 for v in orig: 846 if v not in t: 847 t.insert(0, v) 848 self[key] = t 849 return self
850 851 # def MergeShellPaths(self, args, prepend=1): 852 # """ 853 # Merge the dict in args into the shell environment in env['ENV']. 854 # Shell path elements are appended or prepended according to prepend. 855 856 # Uses Pre/AppendENVPath, so it always appends or prepends uniquely. 857 858 # Example: env.MergeShellPaths({'LIBPATH': '/usr/local/lib'}) 859 # prepends /usr/local/lib to env['ENV']['LIBPATH']. 860 # """ 861 862 # for pathname, pathval in args.items(): 863 # if not pathval: 864 # continue 865 # if prepend: 866 # apply(self.PrependENVPath, (pathname, pathval)) 867 # else: 868 # apply(self.AppendENVPath, (pathname, pathval)) 869 870 871 # Used by the FindSourceFiles() method, below. 872 # Stuck here for support of pre-2.2 Python versions.
873 -def build_source(ss, result):
874 for s in ss: 875 if isinstance(s, SCons.Node.FS.Dir): 876 build_source(s.all_children(), result) 877 elif s.has_builder(): 878 build_source(s.sources, result) 879 elif isinstance(s.disambiguate(), SCons.Node.FS.File): 880 result.append(s)
881
882 -def default_decide_source(dependency, target, prev_ni):
883 f = SCons.Defaults.DefaultEnvironment().decide_source 884 return f(dependency, target, prev_ni)
885
886 -def default_decide_target(dependency, target, prev_ni):
887 f = SCons.Defaults.DefaultEnvironment().decide_target 888 return f(dependency, target, prev_ni)
889
890 -def default_copy_from_cache(src, dst):
891 f = SCons.Defaults.DefaultEnvironment().copy_from_cache 892 return f(src, dst)
893
894 -class Base(SubstitutionEnvironment):
895 """Base class for "real" construction Environments. These are the 896 primary objects used to communicate dependency and construction 897 information to the build engine. 898 899 Keyword arguments supplied when the construction Environment 900 is created are construction variables used to initialize the 901 Environment. 902 """ 903 904 memoizer_counters = [] 905 906 ####################################################################### 907 # This is THE class for interacting with the SCons build engine, 908 # and it contains a lot of stuff, so we're going to try to keep this 909 # a little organized by grouping the methods. 910 ####################################################################### 911 912 ####################################################################### 913 # Methods that make an Environment act like a dictionary. These have 914 # the expected standard names for Python mapping objects. Note that 915 # we don't actually make an Environment a subclass of UserDict for 916 # performance reasons. Note also that we only supply methods for 917 # dictionary functionality that we actually need and use. 918 ####################################################################### 919
920 - def __init__(self, 921 platform=None, 922 tools=None, 923 toolpath=None, 924 variables=None, 925 parse_flags = None, 926 **kw):
927 """ 928 Initialization of a basic SCons construction environment, 929 including setting up special construction variables like BUILDER, 930 PLATFORM, etc., and searching for and applying available Tools. 931 932 Note that we do *not* call the underlying base class 933 (SubsitutionEnvironment) initialization, because we need to 934 initialize things in a very specific order that doesn't work 935 with the much simpler base class initialization. 936 """ 937 if __debug__: logInstanceCreation(self, 'Environment.Base') 938 self._memo = {} 939 self.fs = SCons.Node.FS.get_default_fs() 940 self.ans = SCons.Node.Alias.default_ans 941 self.lookup_list = SCons.Node.arg2nodes_lookups 942 self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) 943 self._init_special() 944 self.added_methods = [] 945 946 # We don't use AddMethod, or define these as methods in this 947 # class, because we *don't* want these functions to be bound 948 # methods. They need to operate independently so that the 949 # settings will work properly regardless of whether a given 950 # target ends up being built with a Base environment or an 951 # OverrideEnvironment or what have you. 952 self.decide_target = default_decide_target 953 self.decide_source = default_decide_source 954 955 self.copy_from_cache = default_copy_from_cache 956 957 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) 958 959 if platform is None: 960 platform = self._dict.get('PLATFORM', None) 961 if platform is None: 962 platform = SCons.Platform.Platform() 963 if SCons.Util.is_String(platform): 964 platform = SCons.Platform.Platform(platform) 965 self._dict['PLATFORM'] = str(platform) 966 platform(self) 967 968 self._dict['HOST_OS'] = self._dict.get('HOST_OS',None) 969 self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None) 970 971 # Now set defaults for TARGET_{OS|ARCH} 972 self._dict['TARGET_OS'] = self._dict.get('HOST_OS',None) 973 self._dict['TARGET_ARCH'] = self._dict.get('HOST_ARCH',None) 974 975 976 # Apply the passed-in and customizable variables to the 977 # environment before calling the tools, because they may use 978 # some of them during initialization. 979 if kw.has_key('options'): 980 # Backwards compatibility: they may stll be using the 981 # old "options" keyword. 982 variables = kw['options'] 983 del kw['options'] 984 apply(self.Replace, (), kw) 985 keys = kw.keys() 986 if variables: 987 keys = keys + variables.keys() 988 variables.Update(self) 989 990 save = {} 991 for k in keys: 992 try: 993 save[k] = self._dict[k] 994 except KeyError: 995 # No value may have been set if they tried to pass in a 996 # reserved variable name like TARGETS. 997 pass 998 999 SCons.Tool.Initializers(self) 1000 1001 if tools is None: 1002 tools = self._dict.get('TOOLS', None) 1003 if tools is None: 1004 tools = ['default'] 1005 apply_tools(self, tools, toolpath) 1006 1007 # Now restore the passed-in and customized variables 1008 # to the environment, since the values the user set explicitly 1009 # should override any values set by the tools. 1010 for key, val in save.items(): 1011 self._dict[key] = val 1012 1013 # Finally, apply any flags to be merged in 1014 if parse_flags: self.MergeFlags(parse_flags)
1015 1016 ####################################################################### 1017 # Utility methods that are primarily for internal use by SCons. 1018 # These begin with lower-case letters. 1019 ####################################################################### 1020
1021 - def get_builder(self, name):
1022 """Fetch the builder with the specified name from the environment. 1023 """ 1024 try: 1025 return self._dict['BUILDERS'][name] 1026 except KeyError: 1027 return None
1028
1029 - def get_CacheDir(self):
1030 try: 1031 path = self._CacheDir_path 1032 except AttributeError: 1033 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path 1034 try: 1035 if path == self._last_CacheDir_path: 1036 return self._last_CacheDir 1037 except AttributeError: 1038 pass 1039 cd = SCons.CacheDir.CacheDir(path) 1040 self._last_CacheDir_path = path 1041 self._last_CacheDir = cd 1042 return cd
1043
1044 - def get_factory(self, factory, default='File'):
1045 """Return a factory function for creating Nodes for this 1046 construction environment. 1047 """ 1048 name = default 1049 try: 1050 is_node = issubclass(factory, SCons.Node.FS.Base) 1051 except TypeError: 1052 # The specified factory isn't a Node itself--it's 1053 # most likely None, or possibly a callable. 1054 pass 1055 else: 1056 if is_node: 1057 # The specified factory is a Node (sub)class. Try to 1058 # return the FS method that corresponds to the Node's 1059 # name--that is, we return self.fs.Dir if they want a Dir, 1060 # self.fs.File for a File, etc. 1061 try: name = factory.__name__ 1062 except AttributeError: pass 1063 else: factory = None 1064 if not factory: 1065 # They passed us None, or we picked up a name from a specified 1066 # class, so return the FS method. (Note that we *don't* 1067 # use our own self.{Dir,File} methods because that would 1068 # cause env.subst() to be called twice on the file name, 1069 # interfering with files that have $$ in them.) 1070 factory = getattr(self.fs, name) 1071 return factory
1072 1073 memoizer_counters.append(SCons.Memoize.CountValue('_gsm')) 1074
1075 - def _gsm(self):
1076 try: 1077 return self._memo['_gsm'] 1078 except KeyError: 1079 pass 1080 1081 result = {} 1082 1083 try: 1084 scanners = self._dict['SCANNERS'] 1085 except KeyError: 1086 pass 1087 else: 1088 # Reverse the scanner list so that, if multiple scanners 1089 # claim they can scan the same suffix, earlier scanners 1090 # in the list will overwrite later scanners, so that 1091 # the result looks like a "first match" to the user. 1092 if not SCons.Util.is_List(scanners): 1093 scanners = [scanners] 1094 else: 1095 scanners = scanners[:] # copy so reverse() doesn't mod original 1096 scanners.reverse() 1097 for scanner in scanners: 1098 for k in scanner.get_skeys(self): 1099 if k and self['PLATFORM'] == 'win32': 1100 k = string.lower(k) 1101 result[k] = scanner 1102 1103 self._memo['_gsm'] = result 1104 1105 return result
1106
1107 - def get_scanner(self, skey):
1108 """Find the appropriate scanner given a key (usually a file suffix). 1109 """ 1110 if skey and self['PLATFORM'] == 'win32': 1111 skey = string.lower(skey) 1112 return self._gsm().get(skey)
1113
1114 - def scanner_map_delete(self, kw=None):
1115 """Delete the cached scanner map (if we need to). 1116 """ 1117 try: 1118 del self._memo['_gsm'] 1119 except KeyError: 1120 pass
1121
1122 - def _update(self, dict):
1123 """Update an environment's values directly, bypassing the normal 1124 checks that occur when users try to set items. 1125 """ 1126 self._dict.update(dict)
1127
1128 - def get_src_sig_type(self):
1129 try: 1130 return self.src_sig_type 1131 except AttributeError: 1132 t = SCons.Defaults.DefaultEnvironment().src_sig_type 1133 self.src_sig_type = t 1134 return t
1135
1136 - def get_tgt_sig_type(self):
1137 try: 1138 return self.tgt_sig_type 1139 except AttributeError: 1140 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type 1141 self.tgt_sig_type = t 1142 return t
1143 1144 ####################################################################### 1145 # Public methods for manipulating an Environment. These begin with 1146 # upper-case letters. The essential characteristic of methods in 1147 # this section is that they do *not* have corresponding same-named 1148 # global functions. For example, a stand-alone Append() function 1149 # makes no sense, because Append() is all about appending values to 1150 # an Environment's construction variables. 1151 ####################################################################### 1152
1153 - def Append(self, **kw):
1154 """Append values to existing construction variables 1155 in an Environment. 1156 """ 1157 kw = copy_non_reserved_keywords(kw) 1158 for key, val in kw.items(): 1159 # It would be easier on the eyes to write this using 1160 # "continue" statements whenever we finish processing an item, 1161 # but Python 1.5.2 apparently doesn't let you use "continue" 1162 # within try:-except: blocks, so we have to nest our code. 1163 try: 1164 orig = self._dict[key] 1165 except KeyError: 1166 # No existing variable in the environment, so just set 1167 # it to the new value. 1168 self._dict[key] = val 1169 else: 1170 try: 1171 # Check if the original looks like a dictionary. 1172 # If it is, we can't just try adding the value because 1173 # dictionaries don't have __add__() methods, and 1174 # things like UserList will incorrectly coerce the 1175 # original dict to a list (which we don't want). 1176 update_dict = orig.update 1177 except AttributeError: 1178 try: 1179 # Most straightforward: just try to add them 1180 # together. This will work in most cases, when the 1181 # original and new values are of compatible types. 1182 self._dict[key] = orig + val 1183 except (KeyError, TypeError): 1184 try: 1185 # Check if the original is a list. 1186 add_to_orig = orig.append 1187 except AttributeError: 1188 # The original isn't a list, but the new 1189 # value is (by process of elimination), 1190 # so insert the original in the new value 1191 # (if there's one to insert) and replace 1192 # the variable with it. 1193 if orig: 1194 val.insert(0, orig) 1195 self._dict[key] = val 1196 else: 1197 # The original is a list, so append the new 1198 # value to it (if there's a value to append). 1199 if val: 1200 add_to_orig(val) 1201 else: 1202 # The original looks like a dictionary, so update it 1203 # based on what we think the value looks like. 1204 if SCons.Util.is_List(val): 1205 for v in val: 1206 orig[v] = None 1207 else: 1208 try: 1209 update_dict(val) 1210 except (AttributeError, TypeError, ValueError): 1211 if SCons.Util.is_Dict(val): 1212 for k, v in val.items(): 1213 orig[k] = v 1214 else: 1215 orig[val] = None 1216 self.scanner_map_delete(kw)
1217 1218 # allow Dirs and strings beginning with # for top-relative 1219 # Note this uses the current env's fs (in self).
1220 - def _canonicalize(self, path):
1221 if not SCons.Util.is_String(path): # typically a Dir 1222 path = str(path) 1223 if path and path[0] == '#': 1224 path = str(self.fs.Dir(path)) 1225 return path
1226
1227 - def AppendENVPath(self, name, newpath, envname = 'ENV', 1228 sep = os.pathsep, delete_existing=1):
1229 """Append path elements to the path 'name' in the 'ENV' 1230 dictionary for this environment. Will only add any particular 1231 path once, and will normpath and normcase all paths to help 1232 assure this. This can also handle the case where the env 1233 variable is a list instead of a string. 1234 1235 If delete_existing is 0, a newpath which is already in the path 1236 will not be moved to the end (it will be left where it is). 1237 """ 1238 1239 orig = '' 1240 if self._dict.has_key(envname) and self._dict[envname].has_key(name): 1241 orig = self._dict[envname][name] 1242 1243 nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing, 1244 canonicalize=self._canonicalize) 1245 1246 if not self._dict.has_key(envname): 1247 self._dict[envname] = {} 1248 1249 self._dict[envname][name] = nv
1250
1251 - def AppendUnique(self, delete_existing=0, **kw):
1252 """Append values to existing construction variables 1253 in an Environment, if they're not already there. 1254 If delete_existing is 1, removes existing values first, so 1255 values move to end. 1256 """ 1257 kw = copy_non_reserved_keywords(kw) 1258 for key, val in kw.items(): 1259 if SCons.Util.is_List(val): 1260 val = _delete_duplicates(val, delete_existing) 1261 if not self._dict.has_key(key) or self._dict[key] in ('', None): 1262 self._dict[key] = val 1263 elif SCons.Util.is_Dict(self._dict[key]) and \ 1264 SCons.Util.is_Dict(val): 1265 self._dict[key].update(val) 1266 elif SCons.Util.is_List(val): 1267 dk = self._dict[key] 1268 if not SCons.Util.is_List(dk): 1269 dk = [dk] 1270 if delete_existing: 1271 dk = filter(lambda x, val=val: x not in val, dk) 1272 else: 1273 val = filter(lambda x, dk=dk: x not in dk, val) 1274 self._dict[key] = dk + val 1275 else: 1276 dk = self._dict[key] 1277 if SCons.Util.is_List(dk): 1278 # By elimination, val is not a list. Since dk is a 1279 # list, wrap val in a list first. 1280 if delete_existing: 1281 dk = filter(lambda x, val=val: x not in val, dk) 1282 self._dict[key] = dk + [val] 1283 else: 1284 if not val in dk: 1285 self._dict[key] = dk + [val] 1286 else: 1287 if delete_existing: 1288 dk = filter(lambda x, val=val: x not in val, dk) 1289 self._dict[key] = dk + val 1290 self.scanner_map_delete(kw)
1291
1292 - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1293 """Return a copy of a construction Environment. The 1294 copy is like a Python "deep copy"--that is, independent 1295 copies are made recursively of each objects--except that 1296 a reference is copied when an object is not deep-copyable 1297 (like a function). There are no references to any mutable 1298 objects in the original Environment. 1299 """ 1300 clone = copy.copy(self) 1301 clone._dict = semi_deepcopy(self._dict) 1302 1303 try: 1304 cbd = clone._dict['BUILDERS'] 1305 except KeyError: 1306 pass 1307 else: 1308 clone._dict['BUILDERS'] = BuilderDict(cbd, clone) 1309 1310 # Check the methods added via AddMethod() and re-bind them to 1311 # the cloned environment. Only do this if the attribute hasn't 1312 # been overwritten by the user explicitly and still points to 1313 # the added method. 1314 clone.added_methods = [] 1315 for mw in self.added_methods: 1316 if mw == getattr(self, mw.name): 1317 clone.added_methods.append(mw.clone(clone)) 1318 1319 clone._memo = {} 1320 1321 # Apply passed-in variables before the tools 1322 # so the tools can use the new variables 1323 kw = copy_non_reserved_keywords(kw) 1324 new = {} 1325 for key, value in kw.items(): 1326 new[key] = SCons.Subst.scons_subst_once(value, self, key) 1327 apply(clone.Replace, (), new) 1328 1329 apply_tools(clone, tools, toolpath) 1330 1331 # apply them again in case the tools overwrote them 1332 apply(clone.Replace, (), new) 1333 1334 # Finally, apply any flags to be merged in 1335 if parse_flags: clone.MergeFlags(parse_flags) 1336 1337 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') 1338 return clone
1339
1340 - def Copy(self, *args, **kw):
1341 global _warn_copy_deprecated 1342 if _warn_copy_deprecated: 1343 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." 1344 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) 1345 _warn_copy_deprecated = False 1346 return apply(self.Clone, args, kw)
1347
1348 - def _changed_build(self, dependency, target, prev_ni):
1349 if dependency.changed_state(target, prev_ni): 1350 return 1 1351 return self.decide_source(dependency, target, prev_ni)
1352
1353 - def _changed_content(self, dependency, target, prev_ni):
1354 return dependency.changed_content(target, prev_ni)
1355
1356 - def _changed_source(self, dependency, target, prev_ni):
1357 target_env = dependency.get_build_env() 1358 type = target_env.get_tgt_sig_type() 1359 if type == 'source': 1360 return target_env.decide_source(dependency, target, prev_ni) 1361 else: 1362 return target_env.decide_target(dependency, target, prev_ni)
1363
1364 - def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1365 return dependency.changed_timestamp_then_content(target, prev_ni)
1366
1367 - def _changed_timestamp_newer(self, dependency, target, prev_ni):
1368 return dependency.changed_timestamp_newer(target, prev_ni)
1369
1370 - def _changed_timestamp_match(self, dependency, target, prev_ni):
1371 return dependency.changed_timestamp_match(target, prev_ni)
1372
1373 - def _copy_from_cache(self, src, dst):
1374 return self.fs.copy(src, dst)
1375
1376 - def _copy2_from_cache(self, src, dst):
1377 return self.fs.copy2(src, dst)
1378
1379 - def Decider(self, function):
1380 copy_function = self._copy2_from_cache 1381 if function in ('MD5', 'content'): 1382 if not SCons.Util.md5: 1383 raise UserError, "MD5 signatures are not available in this version of Python." 1384 function = self._changed_content 1385 elif function == 'MD5-timestamp': 1386 function = self._changed_timestamp_then_content 1387 elif function in ('timestamp-newer', 'make'): 1388 function = self._changed_timestamp_newer 1389 copy_function = self._copy_from_cache 1390 elif function == 'timestamp-match': 1391 function = self._changed_timestamp_match 1392 elif not callable(function): 1393 raise UserError, "Unknown Decider value %s" % repr(function) 1394 1395 # We don't use AddMethod because we don't want to turn the 1396 # function, which only expects three arguments, into a bound 1397 # method, which would add self as an initial, fourth argument. 1398 self.decide_target = function 1399 self.decide_source = function 1400 1401 self.copy_from_cache = copy_function
1402
1403 - def Detect(self, progs):
1404 """Return the first available program in progs. 1405 """ 1406 if not SCons.Util.is_List(progs): 1407 progs = [ progs ] 1408 for prog in progs: 1409 path = self.WhereIs(prog) 1410 if path: return prog 1411 return None
1412
1413 - def Dictionary(self, *args):
1414 if not args: 1415 return self._dict 1416 dlist = map(lambda x, s=self: s._dict[x], args) 1417 if len(dlist) == 1: 1418 dlist = dlist[0] 1419 return dlist
1420
1421 - def Dump(self, key = None):
1422 """ 1423 Using the standard Python pretty printer, dump the contents of the 1424 scons build environment to stdout. 1425 1426 If the key passed in is anything other than None, then that will 1427 be used as an index into the build environment dictionary and 1428 whatever is found there will be fed into the pretty printer. Note 1429 that this key is case sensitive. 1430 """ 1431 import pprint 1432 pp = pprint.PrettyPrinter(indent=2) 1433 if key: 1434 dict = self.Dictionary(key) 1435 else: 1436 dict = self.Dictionary() 1437 return pp.pformat(dict)
1438
1439 - def FindIxes(self, paths, prefix, suffix):
1440 """ 1441 Search a list of paths for something that matches the prefix and suffix. 1442 1443 paths - the list of paths or nodes. 1444 prefix - construction variable for the prefix. 1445 suffix - construction variable for the suffix. 1446 """ 1447 1448 suffix = self.subst('$'+suffix) 1449 prefix = self.subst('$'+prefix) 1450 1451 for path in paths: 1452 dir,name = os.path.split(str(path)) 1453 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 1454 return path
1455
1456 - def ParseConfig(self, command, function=None, unique=1):
1457 """ 1458 Use the specified function to parse the output of the command 1459 in order to modify the current environment. The 'command' can 1460 be a string or a list of strings representing a command and 1461 its arguments. 'Function' is an optional argument that takes 1462 the environment, the output of the command, and the unique flag. 1463 If no function is specified, MergeFlags, which treats the output 1464 as the result of a typical 'X-config' command (i.e. gtk-config), 1465 will merge the output into the appropriate variables. 1466 """ 1467 if function is None: 1468 def parse_conf(env, cmd, unique=unique): 1469 return env.MergeFlags(cmd, unique)
1470 function = parse_conf 1471 if SCons.Util.is_List(command): 1472 command = string.join(command) 1473 command = self.subst(command) 1474 return function(self, self.backtick(command))
1475
1476 - def ParseDepends(self, filename, must_exist=None, only_one=0):
1477 """ 1478 Parse a mkdep-style file for explicit dependencies. This is 1479 completely abusable, and should be unnecessary in the "normal" 1480 case of proper SCons configuration, but it may help make 1481 the transition from a Make hierarchy easier for some people 1482 to swallow. It can also be genuinely useful when using a tool 1483 that can write a .d file, but for which writing a scanner would 1484 be too complicated. 1485 """ 1486 filename = self.subst(filename) 1487 try: 1488 fp = open(filename, 'r') 1489 except IOError: 1490 if must_exist: 1491 raise 1492 return 1493 lines = SCons.Util.LogicalLines(fp).readlines() 1494 lines = filter(lambda l: l[0] != '#', lines) 1495 tdlist = [] 1496 for line in lines: 1497 try: 1498 target, depends = string.split(line, ':', 1) 1499 except (AttributeError, TypeError, ValueError): 1500 # Python 1.5.2 throws TypeError if line isn't a string, 1501 # Python 2.x throws AttributeError because it tries 1502 # to call line.split(). Either can throw ValueError 1503 # if the line doesn't split into two or more elements. 1504 pass 1505 else: 1506 tdlist.append((string.split(target), string.split(depends))) 1507 if only_one: 1508 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist)) 1509 if len(targets) > 1: 1510 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets) 1511 for target, depends in tdlist: 1512 self.Depends(target, depends)
1513
1514 - def Platform(self, platform):
1515 platform = self.subst(platform) 1516 return SCons.Platform.Platform(platform)(self)
1517
1518 - def Prepend(self, **kw):
1519 """Prepend values to existing construction variables 1520 in an Environment. 1521 """ 1522 kw = copy_non_reserved_keywords(kw) 1523 for key, val in kw.items(): 1524 # It would be easier on the eyes to write this using 1525 # "continue" statements whenever we finish processing an item, 1526 # but Python 1.5.2 apparently doesn't let you use "continue" 1527 # within try:-except: blocks, so we have to nest our code. 1528 try: 1529 orig = self._dict[key] 1530 except KeyError: 1531 # No existing variable in the environment, so just set 1532 # it to the new value. 1533 self._dict[key] = val 1534 else: 1535 try: 1536 # Check if the original looks like a dictionary. 1537 # If it is, we can't just try adding the value because 1538 # dictionaries don't have __add__() methods, and 1539 # things like UserList will incorrectly coerce the 1540 # original dict to a list (which we don't want). 1541 update_dict = orig.update 1542 except AttributeError: 1543 try: 1544 # Most straightforward: just try to add them 1545 # together. This will work in most cases, when the 1546 # original and new values are of compatible types. 1547 self._dict[key] = val + orig 1548 except (KeyError, TypeError): 1549 try: 1550 # Check if the added value is a list. 1551 add_to_val = val.append 1552 except AttributeError: 1553 # The added value isn't a list, but the 1554 # original is (by process of elimination), 1555 # so insert the the new value in the original 1556 # (if there's one to insert). 1557 if val: 1558 orig.insert(0, val) 1559 else: 1560 # The added value is a list, so append 1561 # the original to it (if there's a value 1562 # to append). 1563 if orig: 1564 add_to_val(orig) 1565 self._dict[key] = val 1566 else: 1567 # The original looks like a dictionary, so update it 1568 # based on what we think the value looks like. 1569 if SCons.Util.is_List(val): 1570 for v in val: 1571 orig[v] = None 1572 else: 1573 try: 1574 update_dict(val) 1575 except (AttributeError, TypeError, ValueError): 1576 if SCons.Util.is_Dict(val): 1577 for k, v in val.items(): 1578 orig[k] = v 1579 else: 1580 orig[val] = None 1581 self.scanner_map_delete(kw)
1582
1583 - def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, 1584 delete_existing=1):
1585 """Prepend path elements to the path 'name' in the 'ENV' 1586 dictionary for this environment. Will only add any particular 1587 path once, and will normpath and normcase all paths to help 1588 assure this. This can also handle the case where the env 1589 variable is a list instead of a string. 1590 1591 If delete_existing is 0, a newpath which is already in the path 1592 will not be moved to the front (it will be left where it is). 1593 """ 1594 1595 orig = '' 1596 if self._dict.has_key(envname) and self._dict[envname].has_key(name): 1597 orig = self._dict[envname][name] 1598 1599 nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing, 1600 canonicalize=self._canonicalize) 1601 1602 if not self._dict.has_key(envname): 1603 self._dict[envname] = {} 1604 1605 self._dict[envname][name] = nv
1606
1607 - def PrependUnique(self, delete_existing=0, **kw):
1608 """Prepend values to existing construction variables 1609 in an Environment, if they're not already there. 1610 If delete_existing is 1, removes existing values first, so 1611 values move to front. 1612 """ 1613 kw = copy_non_reserved_keywords(kw) 1614 for key, val in kw.items(): 1615 if SCons.Util.is_List(val): 1616 val = _delete_duplicates(val, not delete_existing) 1617 if not self._dict.has_key(key) or self._dict[key] in ('', None): 1618 self._dict[key] = val 1619 elif SCons.Util.is_Dict(self._dict[key]) and \ 1620 SCons.Util.is_Dict(val): 1621 self._dict[key].update(val) 1622 elif SCons.Util.is_List(val): 1623 dk = self._dict[key] 1624 if not SCons.Util.is_List(dk): 1625 dk = [dk] 1626 if delete_existing: 1627 dk = filter(lambda x, val=val: x not in val, dk) 1628 else: 1629 val = filter(lambda x, dk=dk: x not in dk, val) 1630 self._dict[key] = val + dk 1631 else: 1632 dk = self._dict[key] 1633 if SCons.Util.is_List(dk): 1634 # By elimination, val is not a list. Since dk is a 1635 # list, wrap val in a list first. 1636 if delete_existing: 1637 dk = filter(lambda x, val=val: x not in val, dk) 1638 self._dict[key] = [val] + dk 1639 else: 1640 if not val in dk: 1641 self._dict[key] = [val] + dk 1642 else: 1643 if delete_existing: 1644 dk = filter(lambda x, val=val: x not in val, dk) 1645 self._dict[key] = val + dk 1646 self.scanner_map_delete(kw)
1647
1648 - def Replace(self, **kw):
1649 """Replace existing construction variables in an Environment 1650 with new construction variables and/or values. 1651 """ 1652 try: 1653 kwbd = kw['BUILDERS'] 1654 except KeyError: 1655 pass 1656 else: 1657 kwbd = semi_deepcopy(kwbd) 1658 del kw['BUILDERS'] 1659 self.__setitem__('BUILDERS', kwbd) 1660 kw = copy_non_reserved_keywords(kw) 1661 self._update(semi_deepcopy(kw)) 1662 self.scanner_map_delete(kw)
1663
1664 - def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1665 """ 1666 Replace old_prefix with new_prefix and old_suffix with new_suffix. 1667 1668 env - Environment used to interpolate variables. 1669 path - the path that will be modified. 1670 old_prefix - construction variable for the old prefix. 1671 old_suffix - construction variable for the old suffix. 1672 new_prefix - construction variable for the new prefix. 1673 new_suffix - construction variable for the new suffix. 1674 """ 1675 old_prefix = self.subst('$'+old_prefix) 1676 old_suffix = self.subst('$'+old_suffix) 1677 1678 new_prefix = self.subst('$'+new_prefix) 1679 new_suffix = self.subst('$'+new_suffix) 1680 1681 dir,name = os.path.split(str(path)) 1682 if name[:len(old_prefix)] == old_prefix: 1683 name = name[len(old_prefix):] 1684 if name[-len(old_suffix):] == old_suffix: 1685 name = name[:-len(old_suffix)] 1686 return os.path.join(dir, new_prefix+name+new_suffix)
1687
1688 - def SetDefault(self, **kw):
1689 for k in kw.keys(): 1690 if self._dict.has_key(k): 1691 del kw[k] 1692 apply(self.Replace, (), kw)
1693
1694 - def _find_toolpath_dir(self, tp):
1695 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1696
1697 - def Tool(self, tool, toolpath=None, **kw):
1698 if SCons.Util.is_String(tool): 1699 tool = self.subst(tool) 1700 if toolpath is None: 1701 toolpath = self.get('toolpath', []) 1702 toolpath = map(self._find_toolpath_dir, toolpath) 1703 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw) 1704 tool(self)
1705
1706 - def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1707 """Find prog in the path. 1708 """ 1709 if path is None: 1710 try: 1711 path = self['ENV']['PATH'] 1712 except KeyError: 1713 pass 1714 elif SCons.Util.is_String(path): 1715 path = self.subst(path) 1716 if pathext is None: 1717 try: 1718 pathext = self['ENV']['PATHEXT'] 1719 except KeyError: 1720 pass 1721 elif SCons.Util.is_String(pathext): 1722 pathext = self.subst(pathext) 1723 prog = self.subst(prog) 1724 path = SCons.Util.WhereIs(prog, path, pathext, reject) 1725 if path: return path 1726 return None
1727 1728 ####################################################################### 1729 # Public methods for doing real "SCons stuff" (manipulating 1730 # dependencies, setting attributes on targets, etc.). These begin 1731 # with upper-case letters. The essential characteristic of methods 1732 # in this section is that they all *should* have corresponding 1733 # same-named global functions. 1734 ####################################################################### 1735
1736 - def Action(self, *args, **kw):
1737 def subst_string(a, self=self): 1738 if SCons.Util.is_String(a): 1739 a = self.subst(a) 1740 return a
1741 nargs = map(subst_string, args) 1742 nkw = self.subst_kw(kw) 1743 return apply(SCons.Action.Action, nargs, nkw) 1744
1745 - def AddPreAction(self, files, action):
1746 nodes = self.arg2nodes(files, self.fs.Entry) 1747 action = SCons.Action.Action(action) 1748 uniq = {} 1749 for executor in map(lambda n: n.get_executor(), nodes): 1750 uniq[executor] = 1 1751 for executor in uniq.keys(): 1752 executor.add_pre_action(action) 1753 return nodes
1754
1755 - def AddPostAction(self, files, action):
1756 nodes = self.arg2nodes(files, self.fs.Entry) 1757 action = SCons.Action.Action(action) 1758 uniq = {} 1759 for executor in map(lambda n: n.get_executor(), nodes): 1760 uniq[executor] = 1 1761 for executor in uniq.keys(): 1762 executor.add_post_action(action) 1763 return nodes
1764
1765 - def Alias(self, target, source=[], action=None, **kw):
1766 tlist = self.arg2nodes(target, self.ans.Alias) 1767 if not SCons.Util.is_List(source): 1768 source = [source] 1769 source = filter(None, source) 1770 1771 if not action: 1772 if not source: 1773 # There are no source files and no action, so just 1774 # return a target list of classic Alias Nodes, without 1775 # any builder. The externally visible effect is that 1776 # this will make the wrapping Script.BuildTask class 1777 # say that there's "Nothing to be done" for this Alias, 1778 # instead of that it's "up to date." 1779 return tlist 1780 1781 # No action, but there are sources. Re-call all the target 1782 # builders to add the sources to each target. 1783 result = [] 1784 for t in tlist: 1785 bld = t.get_builder(AliasBuilder) 1786 result.extend(bld(self, t, source)) 1787 return result 1788 1789 nkw = self.subst_kw(kw) 1790 nkw.update({ 1791 'action' : SCons.Action.Action(action), 1792 'source_factory' : self.fs.Entry, 1793 'multi' : 1, 1794 'is_explicit' : None, 1795 }) 1796 bld = apply(SCons.Builder.Builder, (), nkw) 1797 1798 # Apply the Builder separately to each target so that the Aliases 1799 # stay separate. If we did one "normal" Builder call with the 1800 # whole target list, then all of the target Aliases would be 1801 # associated under a single Executor. 1802 result = [] 1803 for t in tlist: 1804 # Calling the convert() method will cause a new Executor to be 1805 # created from scratch, so we have to explicitly initialize 1806 # it with the target's existing sources, plus our new ones, 1807 # so nothing gets lost. 1808 b = t.get_builder() 1809 if b is None or b is AliasBuilder: 1810 b = bld 1811 else: 1812 nkw['action'] = b.action + action 1813 b = apply(SCons.Builder.Builder, (), nkw) 1814 t.convert() 1815 result.extend(b(self, t, t.sources + source)) 1816 return result
1817
1818 - def AlwaysBuild(self, *targets):
1819 tlist = [] 1820 for t in targets: 1821 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1822 for t in tlist: 1823 t.set_always_build() 1824 return tlist
1825
1826 - def BuildDir(self, *args, **kw):
1827 if kw.has_key('build_dir'): 1828 kw['variant_dir'] = kw['build_dir'] 1829 del kw['build_dir'] 1830 return apply(self.VariantDir, args, kw)
1831
1832 - def Builder(self, **kw):
1833 nkw = self.subst_kw(kw) 1834 return apply(SCons.Builder.Builder, [], nkw)
1835
1836 - def CacheDir(self, path):
1837 import SCons.CacheDir 1838 if path is not None: 1839 path = self.subst(path) 1840 self._CacheDir_path = path
1841
1842 - def Clean(self, targets, files):
1843 global CleanTargets 1844 tlist = self.arg2nodes(targets, self.fs.Entry) 1845 flist = self.arg2nodes(files, self.fs.Entry) 1846 for t in tlist: 1847 try: 1848 CleanTargets[t].extend(flist) 1849 except KeyError: 1850 CleanTargets[t] = flist
1851
1852 - def Configure(self, *args, **kw):
1853 nargs = [self] 1854 if args: 1855 nargs = nargs + self.subst_list(args)[0] 1856 nkw = self.subst_kw(kw) 1857 nkw['_depth'] = kw.get('_depth', 0) + 1 1858 try: 1859 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) 1860 except KeyError: 1861 pass 1862 return apply(SCons.SConf.SConf, nargs, nkw)
1863
1864 - def Command(self, target, source, action, **kw):
1865 """Builds the supplied target files from the supplied 1866 source files using the supplied action. Action may 1867 be any type that the Builder constructor will accept 1868 for an action.""" 1869 bkw = { 1870 'action' : action, 1871 'target_factory' : self.fs.Entry, 1872 'source_factory' : self.fs.Entry, 1873 } 1874 try: bkw['source_scanner'] = kw['source_scanner'] 1875 except KeyError: pass 1876 else: del kw['source_scanner'] 1877 bld = apply(SCons.Builder.Builder, (), bkw) 1878 return apply(bld, (self, target, source), kw)
1879
1880 - def Depends(self, target, dependency):
1881 """Explicity specify that 'target's depend on 'dependency'.""" 1882 tlist = self.arg2nodes(target, self.fs.Entry) 1883 dlist = self.arg2nodes(dependency, self.fs.Entry) 1884 for t in tlist: 1885 t.add_dependency(dlist) 1886 return tlist
1887
1888 - def Dir(self, name, *args, **kw):
1889 """ 1890 """ 1891 s = self.subst(name) 1892 if SCons.Util.is_Sequence(s): 1893 result=[] 1894 for e in s: 1895 result.append(apply(self.fs.Dir, (e,) + args, kw)) 1896 return result 1897 return apply(self.fs.Dir, (s,) + args, kw)
1898
1899 - def NoClean(self, *targets):
1900 """Tags a target so that it will not be cleaned by -c""" 1901 tlist = [] 1902 for t in targets: 1903 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1904 for t in tlist: 1905 t.set_noclean() 1906 return tlist
1907
1908 - def NoCache(self, *targets):
1909 """Tags a target so that it will not be cached""" 1910 tlist = [] 1911 for t in targets: 1912 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1913 for t in tlist: 1914 t.set_nocache() 1915 return tlist
1916
1917 - def Entry(self, name, *args, **kw):
1918 """ 1919 """ 1920 s = self.subst(name) 1921 if SCons.Util.is_Sequence(s): 1922 result=[] 1923 for e in s: 1924 result.append(apply(self.fs.Entry, (e,) + args, kw)) 1925 return result 1926 return apply(self.fs.Entry, (s,) + args, kw)
1927
1928 - def Environment(self, **kw):
1929 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1930
1931 - def Execute(self, action, *args, **kw):
1932 """Directly execute an action through an Environment 1933 """ 1934 action = apply(self.Action, (action,) + args, kw) 1935 result = action([], [], self) 1936 if isinstance(result, SCons.Errors.BuildError): 1937 errstr = result.errstr 1938 if result.filename: 1939 errstr = result.filename + ': ' + errstr 1940 sys.stderr.write("scons: *** %s\n" % errstr) 1941 return result.status 1942 else: 1943 return result
1944
1945 - def File(self, name, *args, **kw):
1946 """ 1947 """ 1948 s = self.subst(name) 1949 if SCons.Util.is_Sequence(s): 1950 result=[] 1951 for e in s: 1952 result.append(apply(self.fs.File, (e,) + args, kw)) 1953 return result 1954 return apply(self.fs.File, (s,) + args, kw)
1955
1956 - def FindFile(self, file, dirs):
1957 file = self.subst(file) 1958 nodes = self.arg2nodes(dirs, self.fs.Dir) 1959 return SCons.Node.FS.find_file(file, tuple(nodes))
1960
1961 - def Flatten(self, sequence):
1962 return SCons.Util.flatten(sequence)
1963
1964 - def GetBuildPath(self, files):
1965 result = map(str, self.arg2nodes(files, self.fs.Entry)) 1966 if SCons.Util.is_List(files): 1967 return result 1968 else: 1969 return result[0]
1970
1971 - def Glob(self, pattern, ondisk=True, source=False, strings=False):
1972 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1973
1974 - def Ignore(self, target, dependency):
1975 """Ignore a dependency.""" 1976 tlist = self.arg2nodes(target, self.fs.Entry) 1977 dlist = self.arg2nodes(dependency, self.fs.Entry) 1978 for t in tlist: 1979 t.add_ignore(dlist) 1980 return tlist
1981
1982 - def Literal(self, string):
1983 return SCons.Subst.Literal(string)
1984
1985 - def Local(self, *targets):
1986 ret = [] 1987 for targ in targets: 1988 if isinstance(targ, SCons.Node.Node): 1989 targ.set_local() 1990 ret.append(targ) 1991 else: 1992 for t in self.arg2nodes(targ, self.fs.Entry): 1993 t.set_local() 1994 ret.append(t) 1995 return ret
1996
1997 - def Precious(self, *targets):
1998 tlist = [] 1999 for t in targets: 2000 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 2001 for t in tlist: 2002 t.set_precious() 2003 return tlist
2004
2005 - def Repository(self, *dirs, **kw):
2006 dirs = self.arg2nodes(list(dirs), self.fs.Dir) 2007 apply(self.fs.Repository, dirs, kw)
2008
2009 - def Requires(self, target, prerequisite):
2010 """Specify that 'prerequisite' must be built before 'target', 2011 (but 'target' does not actually depend on 'prerequisite' 2012 and need not be rebuilt if it changes).""" 2013 tlist = self.arg2nodes(target, self.fs.Entry) 2014 plist = self.arg2nodes(prerequisite, self.fs.Entry) 2015 for t in tlist: 2016 t.add_prerequisite(plist) 2017 return tlist
2018
2019 - def Scanner(self, *args, **kw):
2020 nargs = [] 2021 for arg in args: 2022 if SCons.Util.is_String(arg): 2023 arg = self.subst(arg) 2024 nargs.append(arg) 2025 nkw = self.subst_kw(kw) 2026 return apply(SCons.Scanner.Base, nargs, nkw)
2027
2028 - def SConsignFile(self, name=".sconsign", dbm_module=None):
2029 if name is not None: 2030 name = self.subst(name) 2031 if not os.path.isabs(name): 2032 name = os.path.join(str(self.fs.SConstruct_dir), name) 2033 if name: 2034 name = os.path.normpath(name) 2035 sconsign_dir = os.path.dirname(name) 2036 if sconsign_dir and not os.path.exists(sconsign_dir): 2037 self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) 2038 SCons.SConsign.File(name, dbm_module)
2039
2040 - def SideEffect(self, side_effect, target):
2041 """Tell scons that side_effects are built as side 2042 effects of building targets.""" 2043 side_effects = self.arg2nodes(side_effect, self.fs.Entry) 2044 targets = self.arg2nodes(target, self.fs.Entry) 2045 2046 for side_effect in side_effects: 2047 if side_effect.multiple_side_effect_has_builder(): 2048 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect) 2049 side_effect.add_source(targets) 2050 side_effect.side_effect = 1 2051 self.Precious(side_effect) 2052 for target in targets: 2053 target.side_effects.append(side_effect) 2054 return side_effects
2055
2056 - def SourceCode(self, entry, builder):
2057 """Arrange for a source code builder for (part of) a tree.""" 2058 entries = self.arg2nodes(entry, self.fs.Entry) 2059 for entry in entries: 2060 entry.set_src_builder(builder) 2061 return entries
2062
2063 - def SourceSignatures(self, type):
2064 global _warn_source_signatures_deprecated 2065 if _warn_source_signatures_deprecated: 2066 msg = "The env.SourceSignatures() method is deprecated;\n" + \ 2067 "\tconvert your build to use the env.Decider() method instead." 2068 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) 2069 _warn_source_signatures_deprecated = False 2070 type = self.subst(type) 2071 self.src_sig_type = type 2072 if type == 'MD5': 2073 if not SCons.Util.md5: 2074 raise UserError, "MD5 signatures are not available in this version of Python." 2075 self.decide_source = self._changed_content 2076 elif type == 'timestamp': 2077 self.decide_source = self._changed_timestamp_match 2078 else: 2079 raise UserError, "Unknown source signature type '%s'" % type
2080
2081 - def Split(self, arg):
2082 """This function converts a string or list into a list of strings 2083 or Nodes. This makes things easier for users by allowing files to 2084 be specified as a white-space separated list to be split. 2085 The input rules are: 2086 - A single string containing names separated by spaces. These will be 2087 split apart at the spaces. 2088 - A single Node instance 2089 - A list containing either strings or Node instances. Any strings 2090 in the list are not split at spaces. 2091 In all cases, the function returns a list of Nodes and strings.""" 2092 if SCons.Util.is_List(arg): 2093 return map(self.subst, arg) 2094 elif SCons.Util.is_String(arg): 2095 return string.split(self.subst(arg)) 2096 else: 2097 return [self.subst(arg)]
2098
2099 - def TargetSignatures(self, type):
2100 global _warn_target_signatures_deprecated 2101 if _warn_target_signatures_deprecated: 2102 msg = "The env.TargetSignatures() method is deprecated;\n" + \ 2103 "\tconvert your build to use the env.Decider() method instead." 2104 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) 2105 _warn_target_signatures_deprecated = False 2106 type = self.subst(type) 2107 self.tgt_sig_type = type 2108 if type in ('MD5', 'content'): 2109 if not SCons.Util.md5: 2110 raise UserError, "MD5 signatures are not available in this version of Python." 2111 self.decide_target = self._changed_content 2112 elif type == 'timestamp': 2113 self.decide_target = self._changed_timestamp_match 2114 elif type == 'build': 2115 self.decide_target = self._changed_build 2116 elif type == 'source': 2117 self.decide_target = self._changed_source 2118 else: 2119 raise UserError, "Unknown target signature type '%s'"%type
2120
2121 - def Value(self, value, built_value=None):
2122 """ 2123 """ 2124 return SCons.Node.Python.Value(value, built_value)
2125
2126 - def VariantDir(self, variant_dir, src_dir, duplicate=1):
2127 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] 2128 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] 2129 self.fs.VariantDir(variant_dir, src_dir, duplicate)
2130
2131 - def FindSourceFiles(self, node='.'):
2132 """ returns a list of all source files. 2133 """ 2134 node = self.arg2nodes(node, self.fs.Entry)[0] 2135 2136 sources = [] 2137 # Uncomment this and get rid of the global definition when we 2138 # drop support for pre-2.2 Python versions. 2139 #def build_source(ss, result): 2140 # for s in ss: 2141 # if isinstance(s, SCons.Node.FS.Dir): 2142 # build_source(s.all_children(), result) 2143 # elif s.has_builder(): 2144 # build_source(s.sources, result) 2145 # elif isinstance(s.disambiguate(), SCons.Node.FS.File): 2146 # result.append(s) 2147 build_source(node.all_children(), sources) 2148 2149 # THIS CODE APPEARS TO HAVE NO EFFECT 2150 # # get the final srcnode for all nodes, this means stripping any 2151 # # attached build node by calling the srcnode function 2152 # for file in sources: 2153 # srcnode = file.srcnode() 2154 # while srcnode != file.srcnode(): 2155 # srcnode = file.srcnode() 2156 2157 # remove duplicates 2158 return list(set(sources))
2159
2160 - def FindInstalledFiles(self):
2161 """ returns the list of all targets of the Install and InstallAs Builder. 2162 """ 2163 from SCons.Tool import install 2164 if install._UNIQUE_INSTALLED_FILES is None: 2165 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) 2166 return install._UNIQUE_INSTALLED_FILES
2167
2168 -class OverrideEnvironment(Base):
2169 """A proxy that overrides variables in a wrapped construction 2170 environment by returning values from an overrides dictionary in 2171 preference to values from the underlying subject environment. 2172 2173 This is a lightweight (I hope) proxy that passes through most use of 2174 attributes to the underlying Environment.Base class, but has just 2175 enough additional methods defined to act like a real construction 2176 environment with overridden values. It can wrap either a Base 2177 construction environment, or another OverrideEnvironment, which 2178 can in turn nest arbitrary OverrideEnvironments... 2179 2180 Note that we do *not* call the underlying base class 2181 (SubsitutionEnvironment) initialization, because we get most of those 2182 from proxying the attributes of the subject construction environment. 2183 But because we subclass SubstitutionEnvironment, this class also 2184 has inherited arg2nodes() and subst*() methods; those methods can't 2185 be proxied because they need *this* object's methods to fetch the 2186 values from the overrides dictionary. 2187 """ 2188
2189 - def __init__(self, subject, overrides={}):
2190 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') 2191 self.__dict__['__subject'] = subject 2192 self.__dict__['overrides'] = overrides
2193 2194 # Methods that make this class act like a proxy.
2195 - def __getattr__(self, name):
2196 return getattr(self.__dict__['__subject'], name)
2197 - def __setattr__(self, name, value):
2198 setattr(self.__dict__['__subject'], name, value)
2199 2200 # Methods that make this class act like a dictionary.
2201 - def __getitem__(self, key):
2202 try: 2203 return self.__dict__['overrides'][key] 2204 except KeyError: 2205 return self.__dict__['__subject'].__getitem__(key)
2206 - def __setitem__(self, key, value):
2207 if not is_valid_construction_var(key): 2208 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key 2209 self.__dict__['overrides'][key] = value
2210 - def __delitem__(self, key):
2211 try: 2212 del self.__dict__['overrides'][key] 2213 except KeyError: 2214 deleted = 0 2215 else: 2216 deleted = 1 2217 try: 2218 result = self.__dict__['__subject'].__delitem__(key) 2219 except KeyError: 2220 if not deleted: 2221 raise 2222 result = None 2223 return result
2224 - def get(self, key, default=None):
2225 """Emulates the get() method of dictionaries.""" 2226 try: 2227 return self.__dict__['overrides'][key] 2228 except KeyError: 2229 return self.__dict__['__subject'].get(key, default)
2230 - def has_key(self, key):
2231 try: 2232 self.__dict__['overrides'][key] 2233 return 1 2234 except KeyError: 2235 return self.__dict__['__subject'].has_key(key)
2236 - def __contains__(self, key):
2237 if self.__dict__['overrides'].__contains__(key): 2238 return 1 2239 return self.__dict__['__subject'].__contains__(key)
2240 - def Dictionary(self):
2241 """Emulates the items() method of dictionaries.""" 2242 d = self.__dict__['__subject'].Dictionary().copy() 2243 d.update(self.__dict__['overrides']) 2244 return d
2245 - def items(self):
2246 """Emulates the items() method of dictionaries.""" 2247 return self.Dictionary().items()
2248 2249 # Overridden private construction environment methods.
2250 - def _update(self, dict):
2251 """Update an environment's values directly, bypassing the normal 2252 checks that occur when users try to set items. 2253 """ 2254 self.__dict__['overrides'].update(dict)
2255
2256 - def gvars(self):
2257 return self.__dict__['__subject'].gvars()
2258
2259 - def lvars(self):
2260 lvars = self.__dict__['__subject'].lvars() 2261 lvars.update(self.__dict__['overrides']) 2262 return lvars
2263 2264 # Overridden public construction environment methods.
2265 - def Replace(self, **kw):
2266 kw = copy_non_reserved_keywords(kw) 2267 self.__dict__['overrides'].update(semi_deepcopy(kw))
2268 2269 # The entry point that will be used by the external world 2270 # to refer to a construction environment. This allows the wrapper 2271 # interface to extend a construction environment for its own purposes 2272 # by subclassing SCons.Environment.Base and then assigning the 2273 # class to SCons.Environment.Environment. 2274 2275 Environment = Base 2276 2277 # An entry point for returning a proxy subclass instance that overrides 2278 # the subst*() methods so they don't actually perform construction 2279 # variable substitution. This is specifically intended to be the shim 2280 # layer in between global function calls (which don't want construction 2281 # variable substitution) and the DefaultEnvironment() (which would 2282 # substitute variables if left to its own devices).""" 2283 # 2284 # We have to wrap this in a function that allows us to delay definition of 2285 # the class until it's necessary, so that when it subclasses Environment 2286 # it will pick up whatever Environment subclass the wrapper interface 2287 # might have assigned to SCons.Environment.Environment. 2288
2289 -def NoSubstitutionProxy(subject):
2290 class _NoSubstitutionProxy(Environment): 2291 def __init__(self, subject): 2292 self.__dict__['__subject'] = subject
2293 def __getattr__(self, name): 2294 return getattr(self.__dict__['__subject'], name) 2295 def __setattr__(self, name, value): 2296 return setattr(self.__dict__['__subject'], name, value) 2297 def raw_to_mode(self, dict): 2298 try: 2299 raw = dict['raw'] 2300 except KeyError: 2301 pass 2302 else: 2303 del dict['raw'] 2304 dict['mode'] = raw 2305 def subst(self, string, *args, **kwargs): 2306 return string 2307 def subst_kw(self, kw, *args, **kwargs): 2308 return kw 2309 def subst_list(self, string, *args, **kwargs): 2310 nargs = (string, self,) + args 2311 nkw = kwargs.copy() 2312 nkw['gvars'] = {} 2313 self.raw_to_mode(nkw) 2314 return apply(SCons.Subst.scons_subst_list, nargs, nkw) 2315 def subst_target_source(self, string, *args, **kwargs): 2316 nargs = (string, self,) + args 2317 nkw = kwargs.copy() 2318 nkw['gvars'] = {} 2319 self.raw_to_mode(nkw) 2320 return apply(SCons.Subst.scons_subst, nargs, nkw) 2321 return _NoSubstitutionProxy(subject) 2322 2323 # Local Variables: 2324 # tab-width:4 2325 # indent-tabs-mode:nil 2326 # End: 2327 # vim: set expandtab tabstop=4 shiftwidth=4: 2328