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

Source Code for Module SCons.Environment

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