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