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