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 3363 2008/09/06 07:34:10 scons" 
  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 prog = self.subst(prog) 1603 path = SCons.Util.WhereIs(prog, path, pathext, reject) 1604 if path: return path 1605 return None
1606 1607 ####################################################################### 1608 # Public methods for doing real "SCons stuff" (manipulating 1609 # dependencies, setting attributes on targets, etc.). These begin 1610 # with upper-case letters. The essential characteristic of methods 1611 # in this section is that they all *should* have corresponding 1612 # same-named global functions. 1613 ####################################################################### 1614
1615 - def Action(self, *args, **kw):
1616 def subst_string(a, self=self): 1617 if SCons.Util.is_String(a): 1618 a = self.subst(a) 1619 return a
1620 nargs = map(subst_string, args) 1621 nkw = self.subst_kw(kw) 1622 return apply(SCons.Action.Action, nargs, nkw) 1623
1624 - def AddPreAction(self, files, action):
1625 nodes = self.arg2nodes(files, self.fs.Entry) 1626 action = SCons.Action.Action(action) 1627 uniq = {} 1628 for executor in map(lambda n: n.get_executor(), nodes): 1629 uniq[executor] = 1 1630 for executor in uniq.keys(): 1631 executor.add_pre_action(action) 1632 return nodes
1633
1634 - def AddPostAction(self, files, action):
1635 nodes = self.arg2nodes(files, self.fs.Entry) 1636 action = SCons.Action.Action(action) 1637 uniq = {} 1638 for executor in map(lambda n: n.get_executor(), nodes): 1639 uniq[executor] = 1 1640 for executor in uniq.keys(): 1641 executor.add_post_action(action) 1642 return nodes
1643
1644 - def Alias(self, target, source=[], action=None, **kw):
1645 tlist = self.arg2nodes(target, self.ans.Alias) 1646 if not SCons.Util.is_List(source): 1647 source = [source] 1648 source = filter(None, source) 1649 1650 if not action: 1651 if not source: 1652 # There are no source files and no action, so just 1653 # return a target list of classic Alias Nodes, without 1654 # any builder. The externally visible effect is that 1655 # this will make the wrapping Script.BuildTask class 1656 # say that there's "Nothing to be done" for this Alias, 1657 # instead of that it's "up to date." 1658 return tlist 1659 1660 # No action, but there are sources. Re-call all the target 1661 # builders to add the sources to each target. 1662 result = [] 1663 for t in tlist: 1664 bld = t.get_builder(AliasBuilder) 1665 result.extend(bld(self, t, source)) 1666 return result 1667 1668 nkw = self.subst_kw(kw) 1669 nkw.update({ 1670 'action' : SCons.Action.Action(action), 1671 'source_factory' : self.fs.Entry, 1672 'multi' : 1, 1673 'is_explicit' : None, 1674 }) 1675 bld = apply(SCons.Builder.Builder, (), nkw) 1676 1677 # Apply the Builder separately to each target so that the Aliases 1678 # stay separate. If we did one "normal" Builder call with the 1679 # whole target list, then all of the target Aliases would be 1680 # associated under a single Executor. 1681 result = [] 1682 for t in tlist: 1683 # Calling the convert() method will cause a new Executor to be 1684 # created from scratch, so we have to explicitly initialize 1685 # it with the target's existing sources, plus our new ones, 1686 # so nothing gets lost. 1687 b = t.get_builder() 1688 if b is None or b is AliasBuilder: 1689 b = bld 1690 else: 1691 nkw['action'] = b.action + action 1692 b = apply(SCons.Builder.Builder, (), nkw) 1693 t.convert() 1694 result.extend(b(self, t, t.sources + source)) 1695 return result
1696
1697 - def AlwaysBuild(self, *targets):
1698 tlist = [] 1699 for t in targets: 1700 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1701 for t in tlist: 1702 t.set_always_build() 1703 return tlist
1704
1705 - def BuildDir(self, *args, **kw):
1706 if kw.has_key('build_dir'): 1707 kw['variant_dir'] = kw['build_dir'] 1708 del kw['build_dir'] 1709 return apply(self.VariantDir, args, kw)
1710
1711 - def Builder(self, **kw):
1712 nkw = self.subst_kw(kw) 1713 return apply(SCons.Builder.Builder, [], nkw)
1714
1715 - def CacheDir(self, path):
1716 import SCons.CacheDir 1717 if not path is None: 1718 path = self.subst(path) 1719 self._CacheDir_path = path
1720
1721 - def Clean(self, targets, files):
1722 global CleanTargets 1723 tlist = self.arg2nodes(targets, self.fs.Entry) 1724 flist = self.arg2nodes(files, self.fs.Entry) 1725 for t in tlist: 1726 try: 1727 CleanTargets[t].extend(flist) 1728 except KeyError: 1729 CleanTargets[t] = flist
1730
1731 - def Configure(self, *args, **kw):
1732 nargs = [self] 1733 if args: 1734 nargs = nargs + self.subst_list(args)[0] 1735 nkw = self.subst_kw(kw) 1736 nkw['_depth'] = kw.get('_depth', 0) + 1 1737 try: 1738 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) 1739 except KeyError: 1740 pass 1741 return apply(SCons.SConf.SConf, nargs, nkw)
1742
1743 - def Command(self, target, source, action, **kw):
1744 """Builds the supplied target files from the supplied 1745 source files using the supplied action. Action may 1746 be any type that the Builder constructor will accept 1747 for an action.""" 1748 bkw = { 1749 'action' : action, 1750 'target_factory' : self.fs.Entry, 1751 'source_factory' : self.fs.Entry, 1752 } 1753 try: bkw['source_scanner'] = kw['source_scanner'] 1754 except KeyError: pass 1755 else: del kw['source_scanner'] 1756 bld = apply(SCons.Builder.Builder, (), bkw) 1757 return apply(bld, (self, target, source), kw)
1758
1759 - def Depends(self, target, dependency):
1760 """Explicity specify that 'target's depend on 'dependency'.""" 1761 tlist = self.arg2nodes(target, self.fs.Entry) 1762 dlist = self.arg2nodes(dependency, self.fs.Entry) 1763 for t in tlist: 1764 t.add_dependency(dlist) 1765 return tlist
1766
1767 - def Dir(self, name, *args, **kw):
1768 """ 1769 """ 1770 s = self.subst(name) 1771 if SCons.Util.is_Sequence(s): 1772 result=[] 1773 for e in s: 1774 result.append(apply(self.fs.Dir, (e,) + args, kw)) 1775 return result 1776 return apply(self.fs.Dir, (s,) + args, kw)
1777
1778 - def NoClean(self, *targets):
1779 """Tags a target so that it will not be cleaned by -c""" 1780 tlist = [] 1781 for t in targets: 1782 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1783 for t in tlist: 1784 t.set_noclean() 1785 return tlist
1786
1787 - def NoCache(self, *targets):
1788 """Tags a target so that it will not be cached""" 1789 tlist = [] 1790 for t in targets: 1791 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1792 for t in tlist: 1793 t.set_nocache() 1794 return tlist
1795
1796 - def Entry(self, name, *args, **kw):
1797 """ 1798 """ 1799 s = self.subst(name) 1800 if SCons.Util.is_Sequence(s): 1801 result=[] 1802 for e in s: 1803 result.append(apply(self.fs.Entry, (e,) + args, kw)) 1804 return result 1805 return apply(self.fs.Entry, (s,) + args, kw)
1806
1807 - def Environment(self, **kw):
1808 return apply(SCons.Environment.Environment, [], self.subst_kw(kw))
1809
1810 - def Execute(self, action, *args, **kw):
1811 """Directly execute an action through an Environment 1812 """ 1813 action = apply(self.Action, (action,) + args, kw) 1814 result = action([], [], self) 1815 if isinstance(result, SCons.Errors.BuildError): 1816 errstr = result.errstr 1817 if result.filename: 1818 errstr = result.filename + ': ' + errstr 1819 import sys 1820 sys.stderr.write("scons: *** %s\n" % errstr) 1821 return result.status 1822 else: 1823 return result
1824
1825 - def File(self, name, *args, **kw):
1826 """ 1827 """ 1828 s = self.subst(name) 1829 if SCons.Util.is_Sequence(s): 1830 result=[] 1831 for e in s: 1832 result.append(apply(self.fs.File, (e,) + args, kw)) 1833 return result 1834 return apply(self.fs.File, (s,) + args, kw)
1835
1836 - def FindFile(self, file, dirs):
1837 file = self.subst(file) 1838 nodes = self.arg2nodes(dirs, self.fs.Dir) 1839 return SCons.Node.FS.find_file(file, tuple(nodes))
1840
1841 - def Flatten(self, sequence):
1842 return SCons.Util.flatten(sequence)
1843
1844 - def GetBuildPath(self, files):
1845 result = map(str, self.arg2nodes(files, self.fs.Entry)) 1846 if SCons.Util.is_List(files): 1847 return result 1848 else: 1849 return result[0]
1850
1851 - def Glob(self, pattern, ondisk=True, source=False, strings=False):
1852 return self.fs.Glob(self.subst(pattern), ondisk, source, strings)
1853
1854 - def Ignore(self, target, dependency):
1855 """Ignore a dependency.""" 1856 tlist = self.arg2nodes(target, self.fs.Entry) 1857 dlist = self.arg2nodes(dependency, self.fs.Entry) 1858 for t in tlist: 1859 t.add_ignore(dlist) 1860 return tlist
1861
1862 - def Literal(self, string):
1863 return SCons.Subst.Literal(string)
1864
1865 - def Local(self, *targets):
1866 ret = [] 1867 for targ in targets: 1868 if isinstance(targ, SCons.Node.Node): 1869 targ.set_local() 1870 ret.append(targ) 1871 else: 1872 for t in self.arg2nodes(targ, self.fs.Entry): 1873 t.set_local() 1874 ret.append(t) 1875 return ret
1876
1877 - def Precious(self, *targets):
1878 tlist = [] 1879 for t in targets: 1880 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1881 for t in tlist: 1882 t.set_precious() 1883 return tlist
1884
1885 - def Repository(self, *dirs, **kw):
1886 dirs = self.arg2nodes(list(dirs), self.fs.Dir) 1887 apply(self.fs.Repository, dirs, kw)
1888
1889 - def Requires(self, target, prerequisite):
1890 """Specify that 'prerequisite' must be built before 'target', 1891 (but 'target' does not actually depend on 'prerequisite' 1892 and need not be rebuilt if it changes).""" 1893 tlist = self.arg2nodes(target, self.fs.Entry) 1894 plist = self.arg2nodes(prerequisite, self.fs.Entry) 1895 for t in tlist: 1896 t.add_prerequisite(plist) 1897 return tlist
1898
1899 - def Scanner(self, *args, **kw):
1900 nargs = [] 1901 for arg in args: 1902 if SCons.Util.is_String(arg): 1903 arg = self.subst(arg) 1904 nargs.append(arg) 1905 nkw = self.subst_kw(kw) 1906 return apply(SCons.Scanner.Base, nargs, nkw)
1907
1908 - def SConsignFile(self, name=".sconsign", dbm_module=None):
1909 if not name is None: 1910 name = self.subst(name) 1911 if not os.path.isabs(name): 1912 name = os.path.join(str(self.fs.SConstruct_dir), name) 1913 SCons.SConsign.File(name, dbm_module)
1914
1915 - def SideEffect(self, side_effect, target):
1916 """Tell scons that side_effects are built as side 1917 effects of building targets.""" 1918 side_effects = self.arg2nodes(side_effect, self.fs.Entry) 1919 targets = self.arg2nodes(target, self.fs.Entry) 1920 1921 for side_effect in side_effects: 1922 if side_effect.multiple_side_effect_has_builder(): 1923 raise SCons.Errors.UserError, "Multiple ways to build the same target were specified for: %s" % str(side_effect) 1924 side_effect.add_source(targets) 1925 side_effect.side_effect = 1 1926 self.Precious(side_effect) 1927 for target in targets: 1928 target.side_effects.append(side_effect) 1929 return side_effects
1930
1931 - def SourceCode(self, entry, builder):
1932 """Arrange for a source code builder for (part of) a tree.""" 1933 entries = self.arg2nodes(entry, self.fs.Entry) 1934 for entry in entries: 1935 entry.set_src_builder(builder) 1936 return entries
1937
1938 - def SourceSignatures(self, type):
1939 global _warn_source_signatures_deprecated 1940 if _warn_source_signatures_deprecated: 1941 msg = "The env.SourceSignatures() method is deprecated;\n" + \ 1942 "\tconvert your build to use the env.Decider() method instead." 1943 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) 1944 _warn_source_signatures_deprecated = False 1945 type = self.subst(type) 1946 self.src_sig_type = type 1947 if type == 'MD5': 1948 if not SCons.Util.md5: 1949 raise UserError, "MD5 signatures are not available in this version of Python." 1950 self.decide_source = self._changed_content 1951 elif type == 'timestamp': 1952 self.decide_source = self._changed_timestamp_match 1953 else: 1954 raise UserError, "Unknown source signature type '%s'" % type
1955
1956 - def Split(self, arg):
1957 """This function converts a string or list into a list of strings 1958 or Nodes. This makes things easier for users by allowing files to 1959 be specified as a white-space separated list to be split. 1960 The input rules are: 1961 - A single string containing names separated by spaces. These will be 1962 split apart at the spaces. 1963 - A single Node instance 1964 - A list containing either strings or Node instances. Any strings 1965 in the list are not split at spaces. 1966 In all cases, the function returns a list of Nodes and strings.""" 1967 if SCons.Util.is_List(arg): 1968 return map(self.subst, arg) 1969 elif SCons.Util.is_String(arg): 1970 return string.split(self.subst(arg)) 1971 else: 1972 return [self.subst(arg)]
1973
1974 - def TargetSignatures(self, type):
1975 global _warn_target_signatures_deprecated 1976 if _warn_target_signatures_deprecated: 1977 msg = "The env.TargetSignatures() method is deprecated;\n" + \ 1978 "\tconvert your build to use the env.Decider() method instead." 1979 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) 1980 _warn_target_signatures_deprecated = False 1981 type = self.subst(type) 1982 self.tgt_sig_type = type 1983 if type in ('MD5', 'content'): 1984 if not SCons.Util.md5: 1985 raise UserError, "MD5 signatures are not available in this version of Python." 1986 self.decide_target = self._changed_content 1987 elif type == 'timestamp': 1988 self.decide_target = self._changed_timestamp_match 1989 elif type == 'build': 1990 self.decide_target = self._changed_build 1991 elif type == 'source': 1992 self.decide_target = self._changed_source 1993 else: 1994 raise UserError, "Unknown target signature type '%s'"%type
1995
1996 - def Value(self, value, built_value=None):
1997 """ 1998 """ 1999 return SCons.Node.Python.Value(value, built_value)
2000
2001 - def VariantDir(self, variant_dir, src_dir, duplicate=1):
2002 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] 2003 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] 2004 self.fs.VariantDir(variant_dir, src_dir, duplicate)
2005
2006 - def FindSourceFiles(self, node='.'):
2007 """ returns a list of all source files. 2008 """ 2009 node = self.arg2nodes(node, self.fs.Entry)[0] 2010 2011 sources = [] 2012 # Uncomment this and get rid of the global definition when we 2013 # drop support for pre-2.2 Python versions. 2014 #def build_source(ss, result): 2015 # for s in ss: 2016 # if isinstance(s, SCons.Node.FS.Dir): 2017 # build_source(s.all_children(), result) 2018 # elif s.has_builder(): 2019 # build_source(s.sources, result) 2020 # elif isinstance(s.disambiguate(), SCons.Node.FS.File): 2021 # result.append(s) 2022 build_source(node.all_children(), sources) 2023 2024 # now strip the build_node from the sources by calling the srcnode 2025 # function 2026 def get_final_srcnode(file): 2027 srcnode = file.srcnode() 2028 while srcnode != file.srcnode(): 2029 srcnode = file.srcnode() 2030 return srcnode
2031 2032 # get the final srcnode for all nodes, this means stripping any 2033 # attached build node. 2034 map( get_final_srcnode, sources ) 2035 2036 # remove duplicates 2037 return list(set(sources)) 2038
2039 - def FindInstalledFiles(self):
2040 """ returns the list of all targets of the Install and InstallAs Builder. 2041 """ 2042 from SCons.Tool import install 2043 if install._UNIQUE_INSTALLED_FILES is None: 2044 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) 2045 return install._UNIQUE_INSTALLED_FILES
2046
2047 -class OverrideEnvironment(Base):
2048 """A proxy that overrides variables in a wrapped construction 2049 environment by returning values from an overrides dictionary in 2050 preference to values from the underlying subject environment. 2051 2052 This is a lightweight (I hope) proxy that passes through most use of 2053 attributes to the underlying Environment.Base class, but has just 2054 enough additional methods defined to act like a real construction 2055 environment with overridden values. It can wrap either a Base 2056 construction environment, or another OverrideEnvironment, which 2057 can in turn nest arbitrary OverrideEnvironments... 2058 2059 Note that we do *not* call the underlying base class 2060 (SubsitutionEnvironment) initialization, because we get most of those 2061 from proxying the attributes of the subject construction environment. 2062 But because we subclass SubstitutionEnvironment, this class also 2063 has inherited arg2nodes() and subst*() methods; those methods can't 2064 be proxied because they need *this* object's methods to fetch the 2065 values from the overrides dictionary. 2066 """ 2067 2068 if SCons.Memoize.use_memoizer: 2069 __metaclass__ = SCons.Memoize.Memoized_Metaclass 2070
2071 - def __init__(self, subject, overrides={}):
2072 if __debug__: logInstanceCreation(self, 'Environment.OverrideEnvironment') 2073 self.__dict__['__subject'] = subject 2074 self.__dict__['overrides'] = overrides
2075 2076 # Methods that make this class act like a proxy.
2077 - def __getattr__(self, name):
2078 return getattr(self.__dict__['__subject'], name)
2079 - def __setattr__(self, name, value):
2080 setattr(self.__dict__['__subject'], name, value)
2081 2082 # Methods that make this class act like a dictionary.
2083 - def __getitem__(self, key):
2084 try: 2085 return self.__dict__['overrides'][key] 2086 except KeyError: 2087 return self.__dict__['__subject'].__getitem__(key)
2088 - def __setitem__(self, key, value):
2089 if not is_valid_construction_var(key): 2090 raise SCons.Errors.UserError, "Illegal construction variable `%s'" % key 2091 self.__dict__['overrides'][key] = value
2092 - def __delitem__(self, key):
2093 try: 2094 del self.__dict__['overrides'][key] 2095 except KeyError: 2096 deleted = 0 2097 else: 2098 deleted = 1 2099 try: 2100 result = self.__dict__['__subject'].__delitem__(key) 2101 except KeyError: 2102 if not deleted: 2103 raise 2104 result = None 2105 return result
2106 - def get(self, key, default=None):
2107 """Emulates the get() method of dictionaries.""" 2108 try: 2109 return self.__dict__['overrides'][key] 2110 except KeyError: 2111 return self.__dict__['__subject'].get(key, default)
2112 - def has_key(self, key):
2113 try: 2114 self.__dict__['overrides'][key] 2115 return 1 2116 except KeyError: 2117 return self.__dict__['__subject'].has_key(key)
2118 - def Dictionary(self):
2119 """Emulates the items() method of dictionaries.""" 2120 d = self.__dict__['__subject'].Dictionary().copy() 2121 d.update(self.__dict__['overrides']) 2122 return d
2123 - def items(self):
2124 """Emulates the items() method of dictionaries.""" 2125 return self.Dictionary().items()
2126 2127 # Overridden private construction environment methods.
2128 - def _update(self, dict):
2129 """Update an environment's values directly, bypassing the normal 2130 checks that occur when users try to set items. 2131 """ 2132 self.__dict__['overrides'].update(dict)
2133
2134 - def gvars(self):
2135 return self.__dict__['__subject'].gvars()
2136
2137 - def lvars(self):
2138 lvars = self.__dict__['__subject'].lvars() 2139 lvars.update(self.__dict__['overrides']) 2140 return lvars
2141 2142 # Overridden public construction environment methods.
2143 - def Replace(self, **kw):
2144 kw = copy_non_reserved_keywords(kw) 2145 self.__dict__['overrides'].update(semi_deepcopy(kw))
2146 2147 # The entry point that will be used by the external world 2148 # to refer to a construction environment. This allows the wrapper 2149 # interface to extend a construction environment for its own purposes 2150 # by subclassing SCons.Environment.Base and then assigning the 2151 # class to SCons.Environment.Environment. 2152 2153 Environment = Base 2154 2155 # An entry point for returning a proxy subclass instance that overrides 2156 # the subst*() methods so they don't actually perform construction 2157 # variable substitution. This is specifically intended to be the shim 2158 # layer in between global function calls (which don't want construction 2159 # variable substitution) and the DefaultEnvironment() (which would 2160 # substitute variables if left to its own devices).""" 2161 # 2162 # We have to wrap this in a function that allows us to delay definition of 2163 # the class until it's necessary, so that when it subclasses Environment 2164 # it will pick up whatever Environment subclass the wrapper interface 2165 # might have assigned to SCons.Environment.Environment. 2166
2167 -def NoSubstitutionProxy(subject):
2168 class _NoSubstitutionProxy(Environment): 2169 def __init__(self, subject): 2170 self.__dict__['__subject'] = subject
2171 def __getattr__(self, name): 2172 return getattr(self.__dict__['__subject'], name) 2173 def __setattr__(self, name, value): 2174 return setattr(self.__dict__['__subject'], name, value) 2175 def raw_to_mode(self, dict): 2176 try: 2177 raw = dict['raw'] 2178 except KeyError: 2179 pass 2180 else: 2181 del dict['raw'] 2182 dict['mode'] = raw 2183 def subst(self, string, *args, **kwargs): 2184 return string 2185 def subst_kw(self, kw, *args, **kwargs): 2186 return kw 2187 def subst_list(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_list, nargs, nkw) 2193 def subst_target_source(self, string, *args, **kwargs): 2194 nargs = (string, self,) + args 2195 nkw = kwargs.copy() 2196 nkw['gvars'] = {} 2197 self.raw_to_mode(nkw) 2198 return apply(SCons.Subst.scons_subst, nargs, nkw) 2199 return _NoSubstitutionProxy(subject) 2200