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