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 2725 2008/03/31 12:52:02 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 options=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 variables and customizable options to the 905 # environment before calling the tools, because they may use 906 # some of them during initialization. 907 apply(self.Replace, (), kw) 908 keys = kw.keys() 909 if options: 910 keys = keys + options.keys() 911 options.Update(self) 912 913 save = {} 914 for k in keys: 915 try: 916 save[k] = self._dict[k] 917 except KeyError: 918 # No value may have been set if they tried to pass in a 919 # reserved variable name like TARGETS. 920 pass 921 922 SCons.Tool.Initializers(self) 923 924 if tools is None: 925 tools = self._dict.get('TOOLS', None) 926 if tools is None: 927 tools = ['default'] 928 apply_tools(self, tools, toolpath) 929 930 # Now restore the passed-in variables and customized options 931 # to the environment, since the values the user set explicitly 932 # should override any values set by the tools. 933 for key, val in save.items(): 934 self._dict[key] = val 935 936 # Finally, apply any flags to be merged in 937 if parse_flags: self.MergeFlags(parse_flags)
938 939 ####################################################################### 940 # Utility methods that are primarily for internal use by SCons. 941 # These begin with lower-case letters. 942 ####################################################################### 943
944 - def get_builder(self, name):
945 """Fetch the builder with the specified name from the environment. 946 """ 947 try: 948 return self._dict['BUILDERS'][name] 949 except KeyError: 950 return None
951
952 - def get_CacheDir(self):
953 try: 954 path = self._CacheDir_path 955 except AttributeError: 956 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path 957 try: 958 if path == self._last_CacheDir_path: 959 return self._last_CacheDir 960 except AttributeError: 961 pass 962 cd = SCons.CacheDir.CacheDir(path) 963 self._last_CacheDir_path = path 964 self._last_CacheDir = cd 965 return cd
966
967 - def get_factory(self, factory, default='File'):
968 """Return a factory function for creating Nodes for this 969 construction environment. 970 """ 971 name = default 972 try: 973 is_node = issubclass(factory, SCons.Node.Node) 974 except TypeError: 975 # The specified factory isn't a Node itself--it's 976 # most likely None, or possibly a callable. 977 pass 978 else: 979 if is_node: 980 # The specified factory is a Node (sub)class. Try to 981 # return the FS method that corresponds to the Node's 982 # name--that is, we return self.fs.Dir if they want a Dir, 983 # self.fs.File for a File, etc. 984 try: name = factory.__name__ 985 except AttributeError: pass 986 else: factory = None 987 if not factory: 988 # They passed us None, or we picked up a name from a specified 989 # class, so return the FS method. (Note that we *don't* 990 # use our own self.{Dir,File} methods because that would 991 # cause env.subst() to be called twice on the file name, 992 # interfering with files that have $$ in them.) 993 factory = getattr(self.fs, name) 994 return factory
995 996 memoizer_counters.append(SCons.Memoize.CountValue('_gsm')) 997
998 - def _gsm(self):
999 try: 1000 return self._memo['_gsm'] 1001 except KeyError: 1002 pass 1003 1004 result = {} 1005 1006 try: 1007 scanners = self._dict['SCANNERS'] 1008 except KeyError: 1009 pass 1010 else: 1011 # Reverse the scanner list so that, if multiple scanners 1012 # claim they can scan the same suffix, earlier scanners 1013 # in the list will overwrite later scanners, so that 1014 # the result looks like a "first match" to the user. 1015 if not SCons.Util.is_List(scanners): 1016 scanners = [scanners] 1017 else: 1018 scanners = scanners[:] # copy so reverse() doesn't mod original 1019 scanners.reverse() 1020 for scanner in scanners: 1021 for k in scanner.get_skeys(self): 1022 result[k] = scanner 1023 1024 self._memo['_gsm'] = result 1025 1026 return result
1027
1028 - def get_scanner(self, skey):
1029 """Find the appropriate scanner given a key (usually a file suffix). 1030 """ 1031 return self._gsm().get(skey)
1032
1033 - def scanner_map_delete(self, kw=None):
1034 """Delete the cached scanner map (if we need to). 1035 """ 1036 try: 1037 del self._memo['_gsm'] 1038 except KeyError: 1039 pass
1040
1041 - def _update(self, dict):
1042 """Update an environment's values directly, bypassing the normal 1043 checks that occur when users try to set items. 1044 """ 1045 self._dict.update(dict)
1046
1047 - def get_src_sig_type(self):
1048 try: 1049 return self.src_sig_type 1050 except AttributeError: 1051 t = SCons.Defaults.DefaultEnvironment().src_sig_type 1052 self.src_sig_type = t 1053 return t
1054
1055 - def get_tgt_sig_type(self):
1056 try: 1057 return self.tgt_sig_type 1058 except AttributeError: 1059 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type 1060 self.tgt_sig_type = t 1061 return t
1062 1063 ####################################################################### 1064 # Public methods for manipulating an Environment. These begin with 1065 # upper-case letters. The essential characteristic of methods in 1066 # this section is that they do *not* have corresponding same-named 1067 # global functions. For example, a stand-alone Append() function 1068 # makes no sense, because Append() is all about appending values to 1069 # an Environment's construction variables. 1070 ####################################################################### 1071
1072 - def Append(self, **kw):
1073 """Append values to existing construction variables 1074 in an Environment. 1075 """ 1076 kw = copy_non_reserved_keywords(kw) 1077 for key, val in kw.items(): 1078 # It would be easier on the eyes to write this using 1079 # "continue" statements whenever we finish processing an item, 1080 # but Python 1.5.2 apparently doesn't let you use "continue" 1081 # within try:-except: blocks, so we have to nest our code. 1082 try: 1083 orig = self._dict[key] 1084 except KeyError: 1085 # No existing variable in the environment, so just set 1086 # it to the new value. 1087 self._dict[key] = val 1088 else: 1089 try: 1090 # Check if the original looks like a dictionary. 1091 # If it is, we can't just try adding the value because 1092 # dictionaries don't have __add__() methods, and 1093 # things like UserList will incorrectly coerce the 1094 # original dict to a list (which we don't want). 1095 update_dict = orig.update 1096 except AttributeError: 1097 try: 1098 # Most straightforward: just try to add them 1099 # together. This will work in most cases, when the 1100 # original and new values are of compatible types. 1101 self._dict[key] = orig + val 1102 except (KeyError, TypeError): 1103 try: 1104 # Check if the original is a list. 1105 add_to_orig = orig.append 1106 except AttributeError: 1107 # The original isn't a list, but the new 1108 # value is (by process of elimination), 1109 # so insert the original in the new value 1110 # (if there's one to insert) and replace 1111 # the variable with it. 1112 if orig: 1113 val.insert(0, orig) 1114 self._dict[key] = val 1115 else: 1116 # The original is a list, so append the new 1117 # value to it (if there's a value to append). 1118 if val: 1119 add_to_orig(val) 1120 else: 1121 # The original looks like a dictionary, so update it 1122 # based on what we think the value looks like. 1123 if SCons.Util.is_List(val): 1124 for v in val: 1125 orig[v] = None 1126 else: 1127 try: 1128 update_dict(val) 1129 except (AttributeError, TypeError, ValueError): 1130 if SCons.Util.is_Dict(val): 1131 for k, v in val.items(): 1132 orig[k] = v 1133 else: 1134 orig[val] = None 1135 self.scanner_map_delete(kw)
1136
1137 - def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1138 """Append path elements to the path 'name' in the 'ENV' 1139 dictionary for this environment. Will only add any particular 1140 path once, and will normpath and normcase all paths to help 1141 assure this. This can also handle the case where the env 1142 variable is a list instead of a string. 1143 """ 1144 1145 orig = '' 1146 if self._dict.has_key(envname) and self._dict[envname].has_key(name): 1147 orig = self._dict[envname][name] 1148 1149 nv = SCons.Util.AppendPath(orig, newpath, sep) 1150 1151 if not self._dict.has_key(envname): 1152 self._dict[envname] = {} 1153 1154 self._dict[envname][name] = nv
1155
1156 - def AppendUnique(self, **kw):
1157 """Append values to existing construction variables 1158 in an Environment, if they're not already there. 1159 """ 1160 kw = copy_non_reserved_keywords(kw) 1161 for key, val in kw.items(): 1162 if not self._dict.has_key(key) or self._dict[key] in ('', None): 1163 self._dict[key] = val 1164 elif SCons.Util.is_Dict(self._dict[key]) and \ 1165 SCons.Util.is_Dict(val): 1166 self._dict[key].update(val) 1167 elif SCons.Util.is_List(val): 1168 dk = self._dict[key] 1169 if not SCons.Util.is_List(dk): 1170 dk = [dk] 1171 val = filter(lambda x, dk=dk: x not in dk, val) 1172 self._dict[key] = dk + val 1173 else: 1174 dk = self._dict[key] 1175 if SCons.Util.is_List(dk): 1176 # By elimination, val is not a list. Since dk is a 1177 # list, wrap val in a list first. 1178 if not val in dk: 1179 self._dict[key] = dk + [val] 1180 else: 1181 self._dict[key] = self._dict[key] + val 1182 self.scanner_map_delete(kw)
1183
1184 - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1185 """Return a copy of a construction Environment. The 1186 copy is like a Python "deep copy"--that is, independent 1187 copies are made recursively of each objects--except that 1188 a reference is copied when an object is not deep-copyable 1189 (like a function). There are no references to any mutable 1190 objects in the original Environment. 1191 """ 1192 clone = copy.copy(self) 1193 clone._dict = semi_deepcopy(self._dict) 1194 1195 try: 1196 cbd = clone._dict['BUILDERS'] 1197 except KeyError: 1198 pass 1199 else: 1200 clone._dict['BUILDERS'] = BuilderDict(cbd, clone) 1201 1202 # Check the methods added via AddMethod() and re-bind them to 1203 # the cloned environment. Only do this if the attribute hasn't 1204 # been overwritten by the user explicitly and still points to 1205 # the added method. 1206 clone.added_methods = [] 1207 for mw in self.added_methods: 1208 if mw == getattr(self, mw.name): 1209 clone.added_methods.append(mw.clone(clone)) 1210 1211 clone._memo = {} 1212 1213 # Apply passed-in variables before the tools 1214 # so the tools can use the new variables 1215 kw = copy_non_reserved_keywords(kw) 1216 new = {} 1217 for key, value in kw.items(): 1218 new[key] = SCons.Subst.scons_subst_once(value, self, key) 1219 apply(clone.Replace, (), new) 1220 1221 apply_tools(clone, tools, toolpath) 1222 1223 # apply them again in case the tools overwrote them 1224 apply(clone.Replace, (), new) 1225 1226 # Finally, apply any flags to be merged in 1227 if parse_flags: clone.MergeFlags(parse_flags) 1228 1229 if __debug__: logInstanceCreation(self, 'Environment.EnvironmentClone') 1230 return clone
1231
1232 - def Copy(self, *args, **kw):
1233 global _warn_copy_deprecated 1234 if _warn_copy_deprecated: 1235 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." 1236 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) 1237 _warn_copy_deprecated = False 1238 return apply(self.Clone, args, kw)
1239
1240 - def _changed_build(self, dependency, target, prev_ni):
1241 if dependency.changed_state(target, prev_ni): 1242 return 1 1243 return self.decide_source(dependency, target, prev_ni)
1244
1245 - def _changed_content(self, dependency, target, prev_ni):
1246 return dependency.changed_content(target, prev_ni)
1247
1248 - def _changed_source(self, dependency, target, prev_ni):
1249 target_env = dependency.get_build_env() 1250 type = target_env.get_tgt_sig_type() 1251 if type == 'source': 1252 return target_env.decide_source(dependency, target, prev_ni) 1253 else: 1254 return target_env.decide_target(dependency, target, prev_ni)
1255
1256 - def _changed_timestamp_then_content(self, dependency, target, prev_ni):
1257 return dependency.changed_timestamp_then_content(target, prev_ni)
1258
1259 - def _changed_timestamp_newer(self, dependency, target, prev_ni):
1260 return dependency.changed_timestamp_newer(target, prev_ni)
1261
1262 - def _changed_timestamp_match(self, dependency, target, prev_ni):
1263 return dependency.changed_timestamp_match(target, prev_ni)
1264
1265 - def _copy_from_cache(self, src, dst):
1266 return self.fs.copy(src, dst)
1267
1268 - def _copy2_from_cache(self, src, dst):
1269 return self.fs.copy2(src, dst)
1270
1271 - def Decider(self, function):
1272 copy_function = self._copy2_from_cache 1273 if function in ('MD5', 'content'): 1274 if not SCons.Util.md5: 1275 raise UserError, "MD5 signatures are not available in this version of Python." 1276 function = self._changed_content 1277 elif function == 'MD5-timestamp': 1278 function = self._changed_timestamp_then_content 1279 elif function in ('timestamp-newer', 'make'): 1280 function = self._changed_timestamp_newer 1281 copy_function = self._copy_from_cache 1282 elif function == 'timestamp-match': 1283 function = self._changed_timestamp_match 1284 elif not callable(function): 1285 raise UserError, "Unknown Decider value %s" % repr(function) 1286 1287 # We don't use AddMethod because we don't want to turn the 1288 # function, which only expects three arguments, into a bound 1289 # method, which would add self as an initial, fourth argument. 1290 self.decide_target = function 1291 self.decide_source = function 1292 1293 self.copy_from_cache = copy_function
1294
1295 - def Detect(self, progs):
1296 """Return the first available program in progs. 1297 """ 1298 if not SCons.Util.is_List(progs): 1299 progs = [ progs ] 1300 for prog in progs: 1301 path = self.WhereIs(prog) 1302 if path: return prog 1303 return None
1304
1305 - def Dictionary(self, *args):
1306 if not args: 1307 return self._dict 1308 dlist = map(lambda x, s=self: s._dict[x], args) 1309 if len(dlist) == 1: 1310 dlist = dlist[0] 1311 return dlist
1312
1313 - def Dump(self, key = None):
1314 """ 1315 Using the standard Python pretty printer, dump the contents of the 1316 scons build environment to stdout. 1317 1318 If the key passed in is anything other than None, then that will 1319 be used as an index into the build environment dictionary and 1320 whatever is found there will be fed into the pretty printer. Note 1321 that this key is case sensitive. 1322 """ 1323 import pprint 1324 pp = pprint.PrettyPrinter(indent=2) 1325 if key: 1326 dict = self.Dictionary(key) 1327 else: 1328 dict = self.Dictionary() 1329 return pp.pformat(dict)
1330
1331 - def FindIxes(self, paths, prefix, suffix):
1332 """ 1333 Search a list of paths for something that matches the prefix and suffix. 1334 1335 paths - the list of paths or nodes. 1336 prefix - construction variable for the prefix. 1337 suffix - construction variable for the suffix. 1338 """ 1339 1340 suffix = self.subst('$'+suffix) 1341 prefix = self.subst('$'+prefix) 1342 1343 for path in paths: 1344 dir,name = os.path.split(str(path)) 1345 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 1346 return path
1347
1348 - def ParseConfig(self, command, function=None, unique=1):
1349 """ 1350 Use the specified function to parse the output of the command 1351 in order to modify the current environment. The 'command' can 1352 be a string or a list of strings representing a command and 1353 its arguments. 'Function' is an optional argument that takes 1354 the environment, the output of the command, and the unique flag. 1355 If no function is specified, MergeFlags, which treats the output 1356 as the result of a typical 'X-config' command (i.e. gtk-config), 1357 will merge the output into the appropriate variables. 1358 """ 1359 if function is None: 1360 def parse_conf(env, cmd, unique=unique): 1361 return env.MergeFlags(cmd, unique)
1362 function = parse_conf 1363 if SCons.Util.is_List(command): 1364 command = string.join(command) 1365 command = self.subst(command) 1366 return function(self, self.backtick(command))
1367
1368 - def ParseDepends(self, filename, must_exist=None, only_one=0):
1369 """ 1370 Parse a mkdep-style file for explicit dependencies. This is 1371 completely abusable, and should be unnecessary in the "normal" 1372 case of proper SCons configuration, but it may help make 1373 the transition from a Make hierarchy easier for some people 1374 to swallow. It can also be genuinely useful when using a tool 1375 that can write a .d file, but for which writing a scanner would 1376 be too complicated. 1377 """ 1378 filename = self.subst(filename) 1379 try: 1380 fp = open(filename, 'r') 1381 except IOError: 1382 if must_exist: 1383 raise 1384 return 1385 lines = SCons.Util.LogicalLines(fp).readlines() 1386 lines = filter(lambda l: l[0] != '#', lines) 1387 tdlist = [] 1388 for line in lines: 1389 try: 1390 target, depends = string.split(line, ':', 1) 1391 except (AttributeError, TypeError, ValueError): 1392 # Python 1.5.2 throws TypeError if line isn't a string, 1393 # Python 2.x throws AttributeError because it tries 1394 # to call line.split(). Either can throw ValueError 1395 # if the line doesn't split into two or more elements. 1396 pass 1397 else: 1398 tdlist.append((string.split(target), string.split(depends))) 1399 if only_one: 1400 targets = reduce(lambda x, y: x+y, map(lambda p: p[0], tdlist)) 1401 if len(targets) > 1: 1402 raise SCons.Errors.UserError, "More than one dependency target found in `%s': %s" % (filename, targets) 1403 for target, depends in tdlist: 1404 self.Depends(target, depends)
1405
1406 - def Platform(self, platform):
1407 platform = self.subst(platform) 1408 return SCons.Platform.Platform(platform)(self)
1409
1410 - def Prepend(self, **kw):
1411 """Prepend values to existing construction variables 1412 in an Environment. 1413 """ 1414 kw = copy_non_reserved_keywords(kw) 1415 for key, val in kw.items(): 1416 # It would be easier on the eyes to write this using 1417 # "continue" statements whenever we finish processing an item, 1418 # but Python 1.5.2 apparently doesn't let you use "continue" 1419 # within try:-except: blocks, so we have to nest our code. 1420 try: 1421 orig = self._dict[key] 1422 except KeyError: 1423 # No existing variable in the environment, so just set 1424 # it to the new value. 1425 self._dict[key] = val 1426 else: 1427 try: 1428 # Check if the original looks like a dictionary. 1429 # If it is, we can't just try adding the value because 1430 # dictionaries don't have __add__() methods, and 1431 # things like UserList will incorrectly coerce the 1432 # original dict to a list (which we don't want). 1433 update_dict = orig.update 1434 except AttributeError: 1435 try: 1436 # Most straightforward: just try to add them 1437 # together. This will work in most cases, when the 1438 # original and new values are of compatible types. 1439 self._dict[key] = val + orig 1440 except (KeyError, TypeError): 1441 try: 1442 # Check if the added value is a list. 1443 add_to_val = val.append 1444 except AttributeError: 1445 # The added value isn't a list, but the 1446 # original is (by process of elimination), 1447 # so insert the the new value in the original 1448 # (if there's one to insert). 1449 if val: 1450 orig.insert(0, val) 1451 else: 1452 # The added value is a list, so append 1453 # the original to it (if there's a value 1454 # to append). 1455 if orig: 1456 add_to_val(orig) 1457 self._dict[key] = val 1458 else: 1459 # The original looks like a dictionary, so update it 1460 # based on what we think the value looks like. 1461 if SCons.Util.is_List(val): 1462 for v in val: 1463 orig[v] = None 1464 else: 1465 try: 1466 update_dict(val) 1467 except (AttributeError, TypeError, ValueError): 1468 if SCons.Util.is_Dict(val): 1469 for k, v in val.items(): 1470 orig[k] = v 1471 else: 1472 orig[val] = None 1473 self.scanner_map_delete(kw)
1474
1475 - def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
1476 """Prepend path elements to the path 'name' in the 'ENV' 1477 dictionary for this environment. Will only add any particular 1478 path once, and will normpath and normcase all paths to help 1479 assure this. This can also handle the case where the env 1480 variable is a list instead of a string. 1481 """ 1482 1483 orig = '' 1484 if self._dict.has_key(envname) and self._dict[envname].has_key(name): 1485 orig = self._dict[envname][name] 1486 1487 nv = SCons.Util.PrependPath(orig, newpath, sep) 1488 1489 if not self._dict.has_key(envname): 1490 self._dict[envname] = {} 1491 1492 self._dict[envname][name] = nv
1493
1494 - def PrependUnique(self, **kw):
1495 """Append values to existing construction variables 1496 in an Environment, if they're not already there. 1497 """ 1498 kw = copy_non_reserved_keywords(kw) 1499 for key, val in kw.items(): 1500 if not self._dict.has_key(key) or self._dict[key] in ('', None): 1501 self._dict[key] = val 1502 elif SCons.Util.is_Dict(self._dict[key]) and \ 1503 SCons.Util.is_Dict(val): 1504 self._dict[key].update(val) 1505 elif SCons.Util.is_List(val): 1506 dk = self._dict[key] 1507 if not SCons.Util.is_List(dk): 1508 dk = [dk] 1509 val = filter(lambda x, dk=dk: x not in dk, val) 1510 self._dict[key] = val + dk 1511 else: 1512 dk = self._dict[key] 1513 if SCons.Util.is_List(dk): 1514 # By elimination, val is not a list. Since dk is a 1515 # list, wrap val in a list first. 1516 if not val in dk: 1517 self._dict[key] = [val] + dk 1518 else: 1519 self._dict[key] = val + dk 1520 self.scanner_map_delete(kw)
1521
1522 - def Replace(self, **kw):
1523 """Replace existing construction variables in an Environment 1524 with new construction variables and/or values. 1525 """ 1526 try: 1527 kwbd = kw['BUILDERS'] 1528 except KeyError: 1529 pass 1530 else: 1531 kwbd = semi_deepcopy(kwbd) 1532 del kw['BUILDERS'] 1533 self.__setitem__('BUILDERS', kwbd) 1534 kw = copy_non_reserved_keywords(kw) 1535 self._update(semi_deepcopy(kw)) 1536 self.scanner_map_delete(kw)
1537
1538 - def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1539 """ 1540 Replace old_prefix with new_prefix and old_suffix with new_suffix. 1541 1542 env - Environment used to interpolate variables. 1543 path - the path that will be modified. 1544 old_prefix - construction variable for the old prefix. 1545 old_suffix - construction variable for the old suffix. 1546 new_prefix - construction variable for the new prefix. 1547 new_suffix - construction variable for the new suffix. 1548 """ 1549 old_prefix = self.subst('$'+old_prefix) 1550 old_suffix = self.subst('$'+old_suffix) 1551 1552 new_prefix = self.subst('$'+new_prefix) 1553 new_suffix = self.subst('$'+new_suffix) 1554 1555 dir,name = os.path.split(str(path)) 1556 if name[:len(old_prefix)] == old_prefix: 1557 name = name[len(old_prefix):] 1558 if name[-len(old_suffix):] == old_suffix: 1559 name = name[:-len(old_suffix)] 1560 return os.path.join(dir, new_prefix+name+new_suffix)
1561
1562 - def SetDefault(self, **kw):
1563 for k in kw.keys(): 1564 if self._dict.has_key(k): 1565 del kw[k] 1566 apply(self.Replace, (), kw)
1567
1568 - def _find_toolpath_dir(self, tp):
1569 return self.fs.Dir(self.subst(tp)).srcnode().abspath
1570
1571 - def Tool(self, tool, toolpath=None, **kw):
1572 if SCons.Util.is_String(tool): 1573 tool = self.subst(tool) 1574 if toolpath is None: 1575 toolpath = self.get('toolpath', []) 1576 toolpath = map(self._find_toolpath_dir, toolpath) 1577 tool = apply(SCons.Tool.Tool, (tool, toolpath), kw) 1578 tool(self)
1579
1580 - def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1581 """Find prog in the path. 1582 """ 1583 if path is None: 1584 try: 1585 path = self['ENV']['PATH'] 1586 except KeyError: 1587 pass 1588 elif SCons.Util.is_String(path): 1589 path = self.subst(path) 1590 if pathext is None: 1591 try: 1592 pathext = self['ENV']['PATHEXT'] 1593 except KeyError: 1594 pass 1595 elif SCons.Util.is_String(pathext): 1596 pathext = self.subst(pathext) 1597 path = SCons.Util.WhereIs(prog, path, pathext, reject) 1598 if path: return path 1599 return None
1600 1601 ####################################################################### 1602 # Public methods for doing real "SCons stuff" (manipulating 1603 # dependencies, setting attributes on targets, etc.). These begin 1604 # with upper-case letters. The essential characteristic of methods 1605 # in this section is that they all *should* have corresponding 1606 # same-named global functions. 1607 ####################################################################### 1608
1609 - def Action(self, *args, **kw):
1610 def subst_string(a, self=self): 1611 if SCons.Util.is_String(a): 1612 a = self.subst(a) 1613 return a
1614 nargs = map(subst_string, args) 1615 nkw = self.subst_kw(kw) 1616 return apply(SCons.Action.Action, nargs, nkw) 1617
1618 - def AddPreAction(self, files, action):
1619 nodes = self.arg2nodes(files, self.fs.Entry) 1620 action = SCons.Action.Action(action) 1621 uniq = {} 1622 for executor in map(lambda n: n.get_executor(), nodes): 1623 uniq[executor] = 1 1624 for executor in uniq.keys(): 1625 executor.add_pre_action(action) 1626 return nodes
1627
1628 - def AddPostAction(self, files, action):
1629 nodes = self.arg2nodes(files, self.fs.Entry) 1630 action = SCons.Action.Action(action) 1631 uniq = {} 1632 for executor in map(lambda n: n.get_executor(), nodes): 1633 uniq[executor] = 1 1634 for executor in uniq.keys(): 1635 executor.add_post_action(action) 1636 return nodes
1637
1638 - def Alias(self, target, source=[], action=None, **kw):
1639 tlist = self.arg2nodes(target, self.ans.Alias) 1640 if not SCons.Util.is_List(source): 1641 source = [source] 1642 source = filter(None, source) 1643 1644 if not action: 1645 if not source: 1646 # There are no source files and no action, so just 1647 # return a target list of classic Alias Nodes, without 1648 # any builder. The externally visible effect is that 1649 # this will make the wrapping Script.BuildTask class 1650 # say that there's "Nothing to be done" for this Alias, 1651 # instead of that it's "up to date." 1652 return tlist 1653 1654 # No action, but there are sources. Re-call all the target 1655 # builders to add the sources to each target. 1656 result = [] 1657 for t in tlist: 1658 bld = t.get_builder(AliasBuilder) 1659 result.extend(bld(self, t, source)) 1660 return result 1661 1662 nkw = self.subst_kw(kw) 1663 nkw.update({ 1664 'action' : SCons.Action.Action(action), 1665 'source_factory' : self.fs.Entry, 1666 'multi' : 1, 1667 'is_explicit' : None, 1668 }) 1669 bld = apply(SCons.Builder.Builder, (), nkw) 1670 1671 # Apply the Builder separately to each target so that the Aliases 1672 # stay separate. If we did one "normal" Builder call with the 1673 # whole target list, then all of the target Aliases would be 1674 # associated under a single Executor. 1675 result = [] 1676 for t in tlist: 1677 # Calling the convert() method will cause a new Executor to be 1678 # created from scratch, so we have to explicitly initialize 1679 # it with the target's existing sources, plus our new ones, 1680 # so nothing gets lost. 1681 b = t.get_builder() 1682 if b is None or b is AliasBuilder: 1683 b = bld 1684 else: 1685 nkw['action'] = b.action + action 1686 b = apply(SCons.Builder.Builder, (), nkw) 1687 t.convert() 1688 result.extend(b(self, t, t.sources + source)) 1689 return result
1690
1691 - def AlwaysBuild(self, *targets):
1692 tlist = [] 1693 for t in targets: 1694 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1695 for t in tlist: 1696 t.set_always_build() 1697 return tlist
1698
1699 - def BuildDir(self, *args, **kw):
1700 if kw.has_key('build_dir'): 1701 kw['variant_dir'] = kw['build_dir'] 1702 del kw['build_dir'] 1703 return apply(self.VariantDir, args, kw)
1704
1705 - def Builder(self, **kw):
1706 nkw = self.subst_kw(kw) 1707 return apply(SCons.Builder.Builder, [], nkw)
1708
1709 - def CacheDir(self, path):
1710 import SCons.CacheDir 1711 if not path is None: 1712 path = self.subst(path) 1713 self._CacheDir_path = path
1714
1715 - def Clean(self, targets, files):
1716 global CleanTargets 1717 tlist = self.arg2nodes(targets, self.fs.Entry) 1718 flist = self.arg2nodes(files, self.fs.Entry) 1719 for t in tlist: 1720 try: 1721 CleanTargets[t].extend(flist) 1722 except KeyError: 1723 CleanTargets[t] = flist
1724
1725 - def Configure(self, *args, **kw):
1726 nargs = [self] 1727 if args: 1728 nargs = nargs + self.subst_list(args)[0] 1729 nkw = self.subst_kw(kw) 1730 nkw['_depth'] = kw.get('_depth', 0) + 1 1731 try: 1732 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) 1733 except KeyError: 1734 pass 1735 return apply(SCons.SConf.SConf, nargs, nkw)
1736
1737 - def Command(self, target, source, action, **kw):
1738 """Builds the supplied target files from the supplied 1739 source files using the supplied action. Action may 1740 be any type that the Builder constructor will accept 1741 for an action.""" 1742 bkw = { 1743 'action' : action, 1744 'target_factory' : self.fs.Entry, 1745 'source_factory' : self.fs.Entry, 1746 } 1747 try: bkw['source_scanner'] = kw['source_scanner'] 1748 except KeyError: pass 1749 else: del kw['source_scanner'] 1750 bld = apply(SCons.Builder.Builder, (), bkw) 1751 return apply(bld, (self, target, source), kw)
1752
1753 - def Depends(self, target, dependency):
1754 """Explicity specify that 'target's depend on 'dependency'.""" 1755 tlist = self.arg2nodes(target, self.fs.Entry) 1756 dlist = self.arg2nodes(dependency, self.fs.Entry) 1757 for t in tlist: 1758 t.add_dependency(dlist) 1759 return tlist
1760
1761 - def Dir(self, name, *args, **kw):
1762 """ 1763 """ 1764 s = self.subst(name) 1765 if SCons.Util.is_Sequence(s): 1766 result=[] 1767 for e in s: 1768 result.append(apply(self.fs.Dir, (e,) + args, kw)) 1769 return result 1770 return apply(self.fs.Dir, (s,) + args, kw)
1771
1772 - def NoClean(self, *targets):
1773 """Tags a target so that it will not be cleaned by -c""" 1774 tlist = [] 1775 for t in targets: 1776 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1777 for t in tlist: 1778 t.set_noclean() 1779 return tlist
1780
1781 - def NoCache(self, *targets):
1782 """Tags a target so that it will not be cached""" 1783 tlist = [] 1784 for t in targets: 1785 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1786 for t in tlist: 1787 t.set_nocache() 1788 return tlist
1789
1790 - def Entry(self, name, *args, **kw):
1791 """ 1792 """ 1793 s = self.subst(name) 1794 if SCons.Util.is_Sequence(s): 1795 result=[] 1796 for e in s: 1797 result.append(apply(self.fs.Entry, (e,) + args, kw)) 1798 return result 1799 return apply(self.fs.Entry, (s,) + args, kw)
1800
1801 - def Environment(self, **kw):
1802 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1803
1804 - def Execute(self, action, *args, **kw):
1805 """Directly execute an action through an Environment 1806 """ 1807 action = apply(self.Action, (action,) + args, kw) 1808 result = action([], [], self) 1809 if isinstance(result, SCons.Errors.BuildError): 1810 return result.status 1811 else: 1812 return result
1813
1814 - def File(self, name, *args, **kw):
1815 """ 1816 """ 1817 s = self.subst(name) 1818 if SCons.Util.is_Sequence(s): 1819 result=[] 1820 for e in s: 1821 result.append(apply(self.fs.File, (e,) + args, kw)) 1822 return result 1823 return apply(self.fs.File, (s,) + args, kw)
1824
1825 - def FindFile(self, file, dirs):
1826 file = self.subst(file) 1827 nodes = self.arg2nodes(dirs, self.fs.Dir) 1828 return SCons.Node.FS.find_file(file, tuple(nodes))
1829
1830 - def Flatten(self, sequence):
1831 return SCons.Util.flatten(sequence)
1832
1833 - def GetBuildPath(self, files):
1834 result = map(str, self.arg2nodes(files, self.fs.Entry)) 1835 if SCons.Util.is_List(files): 1836 return result 1837 else: 1838 return result[0]
1839
1840 - def Glob(self, pattern, ondisk=True, source=False, strings=False):
1841 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1842
1843 - def Ignore(self, target, dependency):
1844 """Ignore a dependency.""" 1845 tlist = self.arg2nodes(target, self.fs.Entry) 1846 dlist = self.arg2nodes(dependency, self.fs.Entry) 1847 for t in tlist: 1848 t.add_ignore(dlist) 1849 return tlist
1850
1851 - def Literal(self, string):
1852 return SCons.Subst.Literal(string)
1853
1854 - def Local(self, *targets):
1855 ret = [] 1856 for targ in targets: 1857 if isinstance(targ, SCons.Node.Node): 1858 targ.set_local() 1859 ret.append(targ) 1860 else: 1861 for t in self.arg2nodes(targ, self.fs.Entry): 1862 t.set_local() 1863 ret.append(t) 1864 return ret
1865
1866 - def Precious(self, *targets):
1867 tlist = [] 1868 for t in targets: 1869 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1870 for t in tlist: 1871 t.set_precious() 1872 return tlist
1873
1874 - def Repository(self, *dirs, **kw):
1875 dirs = self.arg2nodes(list(dirs), self.fs.Dir) 1876 apply(self.fs.Repository, dirs, kw)
1877
1878 - def Requires(self, target, prerequisite):
1879 """Specify that 'prerequisite' must be built before 'target', 1880 (but 'target' does not actually depend on 'prerequisite' 1881 and need not be rebuilt if it changes).""" 1882 tlist = self.arg2nodes(target, self.fs.Entry) 1883 plist = self.arg2nodes(prerequisite, self.fs.Entry) 1884 for t in tlist: 1885 t.add_prerequisite(plist) 1886 return tlist
1887
1888 - def Scanner(self, *args, **kw):
1889 nargs = [] 1890 for arg in args: 1891 if SCons.Util.is_String(arg): 1892 arg = self.subst(arg) 1893 nargs.append(arg) 1894 nkw = self.subst_kw(kw) 1895 return apply(SCons.Scanner.Base, nargs, nkw)
1896
1897 - def SConsignFile(self, name=".sconsign", dbm_module=None):
1898 if not name is None: 1899 name = self.subst(name) 1900 if not os.path.isabs(name): 1901 name = os.path.join(str(self.fs.SConstruct_dir), name) 1902 SCons.SConsign.File(name, dbm_module)
1903
1904 - def SideEffect(self, side_effect, target):
1905 """Tell scons that side_effects are built as side 1906 effects of building targets.""" 1907 side_effects = self.arg2nodes(side_effect, self.fs.Entry) 1908 targets = self.arg2nodes(target, self.fs.Entry) 1909 1910 for side_effect in side_effects: 1911 if side_effect.multiple_side_effect_has_builder(): 1912 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect) 1913 side_effect.add_source(targets) 1914 side_effect.side_effect = 1 1915 self.Precious(side_effect) 1916 for target in targets: 1917 target.side_effects.append(side_effect) 1918 return side_effects
1919
1920 - def SourceCode(self, entry, builder):
1921 """Arrange for a source code builder for (part of) a tree.""" 1922 entries = self.arg2nodes(entry, self.fs.Entry) 1923 for entry in entries: 1924 entry.set_src_builder(builder) 1925 return entries
1926
1927 - def SourceSignatures(self, type):
1928 global _warn_source_signatures_deprecated 1929 if _warn_source_signatures_deprecated: 1930 msg = "The env.SourceSignatures() method is deprecated;\n" + \ 1931 "\tconvert your build to use the env.Decider() method instead." 1932 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) 1933 _warn_source_signatures_deprecated = False 1934 type = self.subst(type) 1935 self.src_sig_type = type 1936 if type == 'MD5': 1937 if not SCons.Util.md5: 1938 raise UserError, "MD5 signatures are not available in this version of Python." 1939 self.decide_source = self._changed_content 1940 elif type == 'timestamp': 1941 self.decide_source = self._changed_timestamp_match 1942 else: 1943 raise UserError, "Unknown source signature type '%s'" % type
1944
1945 - def Split(self, arg):
1946 """This function converts a string or list into a list of strings 1947 or Nodes. This makes things easier for users by allowing files to 1948 be specified as a white-space separated list to be split. 1949 The input rules are: 1950 - A single string containing names separated by spaces. These will be 1951 split apart at the spaces. 1952 - A single Node instance 1953 - A list containing either strings or Node instances. Any strings 1954 in the list are not split at spaces. 1955 In all cases, the function returns a list of Nodes and strings.""" 1956 if SCons.Util.is_List(arg): 1957 return map(self.subst, arg) 1958 elif SCons.Util.is_String(arg): 1959 return string.split(self.subst(arg)) 1960 else: 1961 return [self.subst(arg)]
1962
1963 - def TargetSignatures(self, type):
1964 global _warn_target_signatures_deprecated 1965 if _warn_target_signatures_deprecated: 1966 msg = "The env.TargetSignatures() method is deprecated;\n" + \ 1967 "\tconvert your build to use the env.Decider() method instead." 1968 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) 1969 _warn_target_signatures_deprecated = False 1970 type = self.subst(type) 1971 self.tgt_sig_type = type 1972 if type in ('MD5', 'content'): 1973 if not SCons.Util.md5: 1974 raise UserError, "MD5 signatures are not available in this version of Python." 1975 self.decide_target = self._changed_content 1976 elif type == 'timestamp': 1977 self.decide_target = self._changed_timestamp_match 1978 elif type == 'build': 1979 self.decide_target = self._changed_build 1980 elif type == 'source': 1981 self.decide_target = self._changed_source 1982 else: 1983 raise UserError, "Unknown target signature type '%s'"%type
1984
1985 - def Value(self, value, built_value=None):
1986 """ 1987 """ 1988 return SCons.Node.Python.Value(value, built_value)
1989
1990 - def VariantDir(self, variant_dir, src_dir, duplicate=1):
1991 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] 1992 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] 1993 self.fs.VariantDir(variant_dir, src_dir, duplicate)
1994
1995 - def FindSourceFiles(self, node='.'):
1996 """ returns a list of all source files. 1997 """ 1998 node = self.arg2nodes(node, self.fs.Entry)[0] 1999 2000 sources = [] 2001 # Uncomment this and get rid of the global definition when we 2002 # drop support for pre-2.2 Python versions. 2003 #def build_source(ss, result): 2004 # for s in ss: 2005 # if isinstance(s, SCons.Node.FS.Dir): 2006 # build_source(s.all_children(), result) 2007 # elif s.has_builder(): 2008 # build_source(s.sources, result) 2009 # elif isinstance(s.disambiguate(), SCons.Node.FS.File): 2010 # result.append(s) 2011 build_source(node.all_children(), sources) 2012 2013 # now strip the build_node from the sources by calling the srcnode 2014 # function 2015 def get_final_srcnode(file): 2016 srcnode = file.srcnode() 2017 while srcnode != file.srcnode(): 2018 srcnode = file.srcnode() 2019 return srcnode
2020 2021 # get the final srcnode for all nodes, this means stripping any 2022 # attached build node. 2023 map( get_final_srcnode, sources ) 2024 2025 # remove duplicates 2026 return list(set(sources)) 2027
2028 - def FindInstalledFiles(self):
2029 """ returns the list of all targets of the Install and InstallAs Builder. 2030 """ 2031 from SCons.Tool import install 2032 if install._UNIQUE_INSTALLED_FILES is None: 2033 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) 2034 return install._UNIQUE_INSTALLED_FILES
2035
2036 -class OverrideEnvironment(Base):
2037 """A proxy that overrides variables in a wrapped construction 2038 environment by returning values from an overrides dictionary in 2039 preference to values from the underlying subject environment. 2040 2041 This is a lightweight (I hope) proxy that passes through most use of 2042 attributes to the underlying Environment.Base class, but has just 2043 enough additional methods defined to act like a real construction 2044 environment with overridden values. It can wrap either a Base 2045 construction environment, or another OverrideEnvironment, which 2046 can in turn nest arbitrary OverrideEnvironments... 2047 2048 Note that we do *not* call the underlying base class 2049 (SubsitutionEnvironment) initialization, because we get most of those 2050 from proxying the attributes of the subject construction environment. 2051 But because we subclass SubstitutionEnvironment, this class also 2052 has inherited arg2nodes() and subst*() methods; those methods can't 2053 be proxied because they need *this* object's methods to fetch the 2054 values from the overrides dictionary. 2055 """ 2056 2057 if SCons.Memoize.use_memoizer: 2058 __metaclass__ = SCons.Memoize.Memoized_Metaclass 2059
2060 - def __init__(self, subject, overrides={}):
2061 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') 2062 self.__dict__['__subject'] = subject 2063 self.__dict__['overrides'] = overrides
2064 2065 # Methods that make this class act like a proxy.
2066 - def __getattr__(self, name):
2067 return getattr(self.__dict__['__subject'], name)
2068 - def __setattr__(self, name, value):
2069 setattr(self.__dict__['__subject'], name, value)
2070 2071 # Methods that make this class act like a dictionary.
2072 - def __getitem__(self, key):
2073 try: 2074 return self.__dict__['overrides'][key] 2075 except KeyError: 2076 return self.__dict__['__subject'].__getitem__(key)
2077 - def __setitem__(self, key, value):
2078 if not is_valid_construction_var(key): 2079 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key 2080 self.__dict__['overrides'][key] = value
2081 - def __delitem__(self, key):
2082 try: 2083 del self.__dict__['overrides'][key] 2084 except KeyError: 2085 deleted = 0 2086 else: 2087 deleted = 1 2088 try: 2089 result = self.__dict__['__subject'].__delitem__(key) 2090 except KeyError: 2091 if not deleted: 2092 raise 2093 result = None 2094 return result
2095 - def get(self, key, default=None):
2096 """Emulates the get() method of dictionaries.""" 2097 try: 2098 return self.__dict__['overrides'][key] 2099 except KeyError: 2100 return self.__dict__['__subject'].get(key, default)
2101 - def has_key(self, key):
2102 try: 2103 self.__dict__['overrides'][key] 2104 return 1 2105 except KeyError: 2106 return self.__dict__['__subject'].has_key(key)
2107 - def Dictionary(self):
2108 """Emulates the items() method of dictionaries.""" 2109 d = self.__dict__['__subject'].Dictionary().copy() 2110 d.update(self.__dict__['overrides']) 2111 return d
2112 - def items(self):
2113 """Emulates the items() method of dictionaries.""" 2114 return self.Dictionary().items()
2115 2116 # Overridden private construction environment methods.
2117 - def _update(self, dict):
2118 """Update an environment's values directly, bypassing the normal 2119 checks that occur when users try to set items. 2120 """ 2121 self.__dict__['overrides'].update(dict)
2122
2123 - def gvars(self):
2124 return self.__dict__['__subject'].gvars()
2125
2126 - def lvars(self):
2127 lvars = self.__dict__['__subject'].lvars() 2128 lvars.update(self.__dict__['overrides']) 2129 return lvars
2130 2131 # Overridden public construction environment methods.
2132 - def Replace(self, **kw):
2133 kw = copy_non_reserved_keywords(kw) 2134 self.__dict__['overrides'].update(semi_deepcopy(kw))
2135 2136 # The entry point that will be used by the external world 2137 # to refer to a construction environment. This allows the wrapper 2138 # interface to extend a construction environment for its own purposes 2139 # by subclassing SCons.Environment.Base and then assigning the 2140 # class to SCons.Environment.Environment. 2141 2142 Environment = Base 2143 2144 # An entry point for returning a proxy subclass instance that overrides 2145 # the subst*() methods so they don't actually perform construction 2146 # variable substitution. This is specifically intended to be the shim 2147 # layer in between global function calls (which don't want construction 2148 # variable substitution) and the DefaultEnvironment() (which would 2149 # substitute variables if left to its own devices).""" 2150 # 2151 # We have to wrap this in a function that allows us to delay definition of 2152 # the class until it's necessary, so that when it subclasses Environment 2153 # it will pick up whatever Environment subclass the wrapper interface 2154 # might have assigned to SCons.Environment.Environment. 2155
2156 -def NoSubstitutionProxy(subject):
2157 class _NoSubstitutionProxy(Environment): 2158 def __init__(self, subject): 2159 self.__dict__['__subject'] = subject
2160 def __getattr__(self, name): 2161 return getattr(self.__dict__['__subject'], name) 2162 def __setattr__(self, name, value): 2163 return setattr(self.__dict__['__subject'], name, value) 2164 def raw_to_mode(self, dict): 2165 try: 2166 raw = dict['raw'] 2167 except KeyError: 2168 pass 2169 else: 2170 del dict['raw'] 2171 dict['mode'] = raw 2172 def subst(self, string, *args, **kwargs): 2173 return string 2174 def subst_kw(self, kw, *args, **kwargs): 2175 return kw 2176 def subst_list(self, string, *args, **kwargs): 2177 nargs = (string, self,) + args 2178 nkw = kwargs.copy() 2179 nkw['gvars'] = {} 2180 self.raw_to_mode(nkw) 2181 return apply(SCons.Subst.scons_subst_list, nargs, nkw) 2182 def subst_target_source(self, string, *args, **kwargs): 2183 nargs = (string, self,) + args 2184 nkw = kwargs.copy() 2185 nkw['gvars'] = {} 2186 self.raw_to_mode(nkw) 2187 return apply(SCons.Subst.scons_subst, nargs, nkw) 2188 return _NoSubstitutionProxy(subject) 2189