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