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 72ae09dc35ac2626f8ff711d8c4b30b6138e08e3 2019-08-08 14:50:06 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 '-fmerge-all-constants', 787 '-fopenmp']: 788 dict['CCFLAGS'].append(arg) 789 dict['LINKFLAGS'].append(arg) 790 elif arg == '-mwindows': 791 dict['LINKFLAGS'].append(arg) 792 elif arg[:5] == '-std=': 793 if arg[5:].find('++')!=-1: 794 key='CXXFLAGS' 795 else: 796 key='CFLAGS' 797 dict[key].append(arg) 798 elif arg[0] == '+': 799 dict['CCFLAGS'].append(arg) 800 dict['LINKFLAGS'].append(arg) 801 elif arg in ['-include', '-isysroot', '-isystem', '-iquote', '-idirafter', '-arch']: 802 append_next_arg_to = arg 803 else: 804 dict['CCFLAGS'].append(arg) 805 806 for arg in flags: 807 do_parse(arg) 808 return dict 809
810 - def MergeFlags(self, args, unique=1, dict=None):
811 """ 812 Merge the dict in args into the construction variables of this 813 env, or the passed-in dict. If args is not a dict, it is 814 converted into a dict using ParseFlags. If unique is not set, 815 the flags are appended rather than merged. 816 """ 817 818 if dict is None: 819 dict = self 820 if not SCons.Util.is_Dict(args): 821 args = self.ParseFlags(args) 822 if not unique: 823 self.Append(**args) 824 return self 825 for key, value in args.items(): 826 if not value: 827 continue 828 try: 829 orig = self[key] 830 except KeyError: 831 orig = value 832 else: 833 if not orig: 834 orig = value 835 elif value: 836 # Add orig and value. The logic here was lifted from 837 # part of env.Append() (see there for a lot of comments 838 # about the order in which things are tried) and is 839 # used mainly to handle coercion of strings to CLVar to 840 # "do the right thing" given (e.g.) an original CCFLAGS 841 # string variable like '-pipe -Wall'. 842 try: 843 orig = orig + value 844 except (KeyError, TypeError): 845 try: 846 add_to_orig = orig.append 847 except AttributeError: 848 value.insert(0, orig) 849 orig = value 850 else: 851 add_to_orig(value) 852 t = [] 853 if key[-4:] == 'PATH': 854 ### keep left-most occurence 855 for v in orig: 856 if v not in t: 857 t.append(v) 858 else: 859 ### keep right-most occurence 860 orig.reverse() 861 for v in orig: 862 if v not in t: 863 t.insert(0, v) 864 self[key] = t 865 return self
866
867 868 -def default_decide_source(dependency, target, prev_ni, repo_node=None):
869 f = SCons.Defaults.DefaultEnvironment().decide_source 870 return f(dependency, target, prev_ni, repo_node)
871
872 873 -def default_decide_target(dependency, target, prev_ni, repo_node=None):
874 f = SCons.Defaults.DefaultEnvironment().decide_target 875 return f(dependency, target, prev_ni, repo_node)
876
877 878 -def default_copy_from_cache(src, dst):
879 f = SCons.Defaults.DefaultEnvironment().copy_from_cache 880 return f(src, dst)
881
882 883 -class Base(SubstitutionEnvironment):
884 """Base class for "real" construction Environments. These are the 885 primary objects used to communicate dependency and construction 886 information to the build engine. 887 888 Keyword arguments supplied when the construction Environment 889 is created are construction variables used to initialize the 890 Environment. 891 """ 892 893 ####################################################################### 894 # This is THE class for interacting with the SCons build engine, 895 # and it contains a lot of stuff, so we're going to try to keep this 896 # a little organized by grouping the methods. 897 ####################################################################### 898 899 ####################################################################### 900 # Methods that make an Environment act like a dictionary. These have 901 # the expected standard names for Python mapping objects. Note that 902 # we don't actually make an Environment a subclass of UserDict for 903 # performance reasons. Note also that we only supply methods for 904 # dictionary functionality that we actually need and use. 905 ####################################################################### 906
907 - def __init__(self, 908 platform=None, 909 tools=None, 910 toolpath=None, 911 variables=None, 912 parse_flags = None, 913 **kw):
914 """ 915 Initialization of a basic SCons construction environment, 916 including setting up special construction variables like BUILDER, 917 PLATFORM, etc., and searching for and applying available Tools. 918 919 Note that we do *not* call the underlying base class 920 (SubsitutionEnvironment) initialization, because we need to 921 initialize things in a very specific order that doesn't work 922 with the much simpler base class initialization. 923 """ 924 if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.Base') 925 self._memo = {} 926 self.fs = SCons.Node.FS.get_default_fs() 927 self.ans = SCons.Node.Alias.default_ans 928 self.lookup_list = SCons.Node.arg2nodes_lookups 929 self._dict = semi_deepcopy(SCons.Defaults.ConstructionEnvironment) 930 self._init_special() 931 self.added_methods = [] 932 933 # We don't use AddMethod, or define these as methods in this 934 # class, because we *don't* want these functions to be bound 935 # methods. They need to operate independently so that the 936 # settings will work properly regardless of whether a given 937 # target ends up being built with a Base environment or an 938 # OverrideEnvironment or what have you. 939 self.decide_target = default_decide_target 940 self.decide_source = default_decide_source 941 942 self.copy_from_cache = default_copy_from_cache 943 944 self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) 945 946 if platform is None: 947 platform = self._dict.get('PLATFORM', None) 948 if platform is None: 949 platform = SCons.Platform.Platform() 950 if SCons.Util.is_String(platform): 951 platform = SCons.Platform.Platform(platform) 952 self._dict['PLATFORM'] = str(platform) 953 platform(self) 954 955 self._dict['HOST_OS'] = self._dict.get('HOST_OS',None) 956 self._dict['HOST_ARCH'] = self._dict.get('HOST_ARCH',None) 957 958 # Now set defaults for TARGET_{OS|ARCH} 959 self._dict['TARGET_OS'] = self._dict.get('TARGET_OS',None) 960 self._dict['TARGET_ARCH'] = self._dict.get('TARGET_ARCH',None) 961 962 963 # Apply the passed-in and customizable variables to the 964 # environment before calling the tools, because they may use 965 # some of them during initialization. 966 if 'options' in kw: 967 # Backwards compatibility: they may stll be using the 968 # old "options" keyword. 969 variables = kw['options'] 970 del kw['options'] 971 self.Replace(**kw) 972 keys = list(kw.keys()) 973 if variables: 974 keys = keys + list(variables.keys()) 975 variables.Update(self) 976 977 save = {} 978 for k in keys: 979 try: 980 save[k] = self._dict[k] 981 except KeyError: 982 # No value may have been set if they tried to pass in a 983 # reserved variable name like TARGETS. 984 pass 985 986 SCons.Tool.Initializers(self) 987 988 if tools is None: 989 tools = self._dict.get('TOOLS', None) 990 if tools is None: 991 tools = ['default'] 992 apply_tools(self, tools, toolpath) 993 994 # Now restore the passed-in and customized variables 995 # to the environment, since the values the user set explicitly 996 # should override any values set by the tools. 997 for key, val in save.items(): 998 self._dict[key] = val 999 1000 # Finally, apply any flags to be merged in 1001 if parse_flags: self.MergeFlags(parse_flags)
1002 1003 ####################################################################### 1004 # Utility methods that are primarily for internal use by SCons. 1005 # These begin with lower-case letters. 1006 ####################################################################### 1007
1008 - def get_builder(self, name):
1009 """Fetch the builder with the specified name from the environment. 1010 """ 1011 try: 1012 return self._dict['BUILDERS'][name] 1013 except KeyError: 1014 return None
1015
1016 - def get_CacheDir(self):
1017 try: 1018 path = self._CacheDir_path 1019 except AttributeError: 1020 path = SCons.Defaults.DefaultEnvironment()._CacheDir_path 1021 try: 1022 if path == self._last_CacheDir_path: 1023 return self._last_CacheDir 1024 except AttributeError: 1025 pass 1026 cd = SCons.CacheDir.CacheDir(path) 1027 self._last_CacheDir_path = path 1028 self._last_CacheDir = cd 1029 return cd
1030
1031 - def get_factory(self, factory, default='File'):
1032 """Return a factory function for creating Nodes for this 1033 construction environment. 1034 """ 1035 name = default 1036 try: 1037 is_node = issubclass(factory, SCons.Node.FS.Base) 1038 except TypeError: 1039 # The specified factory isn't a Node itself--it's 1040 # most likely None, or possibly a callable. 1041 pass 1042 else: 1043 if is_node: 1044 # The specified factory is a Node (sub)class. Try to 1045 # return the FS method that corresponds to the Node's 1046 # name--that is, we return self.fs.Dir if they want a Dir, 1047 # self.fs.File for a File, etc. 1048 try: name = factory.__name__ 1049 except AttributeError: pass 1050 else: factory = None 1051 if not factory: 1052 # They passed us None, or we picked up a name from a specified 1053 # class, so return the FS method. (Note that we *don't* 1054 # use our own self.{Dir,File} methods because that would 1055 # cause env.subst() to be called twice on the file name, 1056 # interfering with files that have $$ in them.) 1057 factory = getattr(self.fs, name) 1058 return factory
1059 1060 @SCons.Memoize.CountMethodCall
1061 - def _gsm(self):
1062 try: 1063 return self._memo['_gsm'] 1064 except KeyError: 1065 pass 1066 1067 result = {} 1068 1069 try: 1070 scanners = self._dict['SCANNERS'] 1071 except KeyError: 1072 pass 1073 else: 1074 # Reverse the scanner list so that, if multiple scanners 1075 # claim they can scan the same suffix, earlier scanners 1076 # in the list will overwrite later scanners, so that 1077 # the result looks like a "first match" to the user. 1078 if not SCons.Util.is_List(scanners): 1079 scanners = [scanners] 1080 else: 1081 scanners = scanners[:] # copy so reverse() doesn't mod original 1082 scanners.reverse() 1083 for scanner in scanners: 1084 for k in scanner.get_skeys(self): 1085 if k and self['PLATFORM'] == 'win32': 1086 k = k.lower() 1087 result[k] = scanner 1088 1089 self._memo['_gsm'] = result 1090 1091 return result
1092
1093 - def get_scanner(self, skey):
1094 """Find the appropriate scanner given a key (usually a file suffix). 1095 """ 1096 if skey and self['PLATFORM'] == 'win32': 1097 skey = skey.lower() 1098 return self._gsm().get(skey)
1099
1100 - def scanner_map_delete(self, kw=None):
1101 """Delete the cached scanner map (if we need to). 1102 """ 1103 try: 1104 del self._memo['_gsm'] 1105 except KeyError: 1106 pass
1107
1108 - def _update(self, dict):
1109 """Update an environment's values directly, bypassing the normal 1110 checks that occur when users try to set items. 1111 """ 1112 self._dict.update(dict)
1113
1114 - def get_src_sig_type(self):
1115 try: 1116 return self.src_sig_type 1117 except AttributeError: 1118 t = SCons.Defaults.DefaultEnvironment().src_sig_type 1119 self.src_sig_type = t 1120 return t
1121
1122 - def get_tgt_sig_type(self):
1123 try: 1124 return self.tgt_sig_type 1125 except AttributeError: 1126 t = SCons.Defaults.DefaultEnvironment().tgt_sig_type 1127 self.tgt_sig_type = t 1128 return t
1129 1130 ####################################################################### 1131 # Public methods for manipulating an Environment. These begin with 1132 # upper-case letters. The essential characteristic of methods in 1133 # this section is that they do *not* have corresponding same-named 1134 # global functions. For example, a stand-alone Append() function 1135 # makes no sense, because Append() is all about appending values to 1136 # an Environment's construction variables. 1137 ####################################################################### 1138
1139 - def Append(self, **kw):
1140 """Append values to existing construction variables 1141 in an Environment. 1142 """ 1143 kw = copy_non_reserved_keywords(kw) 1144 for key, val in kw.items(): 1145 # It would be easier on the eyes to write this using 1146 # "continue" statements whenever we finish processing an item, 1147 # but Python 1.5.2 apparently doesn't let you use "continue" 1148 # within try:-except: blocks, so we have to nest our code. 1149 try: 1150 if key == 'CPPDEFINES' and SCons.Util.is_String(self._dict[key]): 1151 self._dict[key] = [self._dict[key]] 1152 orig = self._dict[key] 1153 except KeyError: 1154 # No existing variable in the environment, so just set 1155 # it to the new value. 1156 if key == 'CPPDEFINES' and SCons.Util.is_String(val): 1157 self._dict[key] = [val] 1158 else: 1159 self._dict[key] = val 1160 else: 1161 try: 1162 # Check if the original looks like a dictionary. 1163 # If it is, we can't just try adding the value because 1164 # dictionaries don't have __add__() methods, and 1165 # things like UserList will incorrectly coerce the 1166 # original dict to a list (which we don't want). 1167 update_dict = orig.update 1168 except AttributeError: 1169 try: 1170 # Most straightforward: just try to add them 1171 # together. This will work in most cases, when the 1172 # original and new values are of compatible types. 1173 self._dict[key] = orig + val 1174 except (KeyError, TypeError): 1175 try: 1176 # Check if the original is a list. 1177 add_to_orig = orig.append 1178 except AttributeError: 1179 # The original isn't a list, but the new 1180 # value is (by process of elimination), 1181 # so insert the original in the new value 1182 # (if there's one to insert) and replace 1183 # the variable with it. 1184 if orig: 1185 val.insert(0, orig) 1186 self._dict[key] = val 1187 else: 1188 # The original is a list, so append the new 1189 # value to it (if there's a value to append). 1190 if val: 1191 add_to_orig(val) 1192 else: 1193 # The original looks like a dictionary, so update it 1194 # based on what we think the value looks like. 1195 if SCons.Util.is_List(val): 1196 if key == 'CPPDEFINES': 1197 tmp = [] 1198 for (k, v) in orig.items(): 1199 if v is not None: 1200 tmp.append((k, v)) 1201 else: 1202 tmp.append((k,)) 1203 orig = tmp 1204 orig += val 1205 self._dict[key] = orig 1206 else: 1207 for v in val: 1208 orig[v] = None 1209 else: 1210 try: 1211 update_dict(val) 1212 except (AttributeError, TypeError, ValueError): 1213 if SCons.Util.is_Dict(val): 1214 for k, v in val.items(): 1215 orig[k] = v 1216 else: 1217 orig[val] = None 1218 self.scanner_map_delete(kw)
1219 1220 # allow Dirs and strings beginning with # for top-relative 1221 # Note this uses the current env's fs (in self).
1222 - def _canonicalize(self, path):
1223 if not SCons.Util.is_String(path): # typically a Dir 1224 path = str(path) 1225 if path and path[0] == '#': 1226 path = str(self.fs.Dir(path)) 1227 return path
1228
1229 - def AppendENVPath(self, name, newpath, envname = 'ENV', 1230 sep = os.pathsep, delete_existing=0):
1231 """Append path elements to the path 'name' in the 'ENV' 1232 dictionary for this environment. Will only add any particular 1233 path once, and will normpath and normcase all paths to help 1234 assure this. This can also handle the case where the env 1235 variable is a list instead of a string. 1236 1237 If delete_existing is 0, a newpath which is already in the path 1238 will not be moved to the end (it will be left where it is). 1239 """ 1240 1241 orig = '' 1242 if envname in self._dict and name in self._dict[envname]: 1243 orig = self._dict[envname][name] 1244 1245 nv = SCons.Util.AppendPath(orig, newpath, sep, delete_existing, 1246 canonicalize=self._canonicalize) 1247 1248 if envname not in self._dict: 1249 self._dict[envname] = {} 1250 1251 self._dict[envname][name] = nv
1252
1253 - def AppendUnique(self, delete_existing=0, **kw):
1254 """Append values to existing construction variables 1255 in an Environment, if they're not already there. 1256 If delete_existing is 1, removes existing values first, so 1257 values move to end. 1258 """ 1259 kw = copy_non_reserved_keywords(kw) 1260 for key, val in kw.items(): 1261 if SCons.Util.is_List(val): 1262 val = _delete_duplicates(val, delete_existing) 1263 if key not in self._dict or self._dict[key] in ('', None): 1264 self._dict[key] = val 1265 elif SCons.Util.is_Dict(self._dict[key]) and \ 1266 SCons.Util.is_Dict(val): 1267 self._dict[key].update(val) 1268 elif SCons.Util.is_List(val): 1269 dk = self._dict[key] 1270 if key == 'CPPDEFINES': 1271 tmp = [] 1272 for i in val: 1273 if SCons.Util.is_List(i): 1274 if len(i) >= 2: 1275 tmp.append((i[0], i[1])) 1276 else: 1277 tmp.append((i[0],)) 1278 elif SCons.Util.is_Tuple(i): 1279 tmp.append(i) 1280 else: 1281 tmp.append((i,)) 1282 val = tmp 1283 # Construct a list of (key, value) tuples. 1284 if SCons.Util.is_Dict(dk): 1285 tmp = [] 1286 for (k, v) in dk.items(): 1287 if v is not None: 1288 tmp.append((k, v)) 1289 else: 1290 tmp.append((k,)) 1291 dk = tmp 1292 elif SCons.Util.is_String(dk): 1293 dk = [(dk,)] 1294 else: 1295 tmp = [] 1296 for i in dk: 1297 if SCons.Util.is_List(i): 1298 if len(i) >= 2: 1299 tmp.append((i[0], i[1])) 1300 else: 1301 tmp.append((i[0],)) 1302 elif SCons.Util.is_Tuple(i): 1303 tmp.append(i) 1304 else: 1305 tmp.append((i,)) 1306 dk = tmp 1307 else: 1308 if not SCons.Util.is_List(dk): 1309 dk = [dk] 1310 if delete_existing: 1311 dk = [x for x in dk if x not in val] 1312 else: 1313 val = [x for x in val if x not in dk] 1314 self._dict[key] = dk + val 1315 else: 1316 dk = self._dict[key] 1317 if SCons.Util.is_List(dk): 1318 if key == 'CPPDEFINES': 1319 tmp = [] 1320 for i in dk: 1321 if SCons.Util.is_List(i): 1322 if len(i) >= 2: 1323 tmp.append((i[0], i[1])) 1324 else: 1325 tmp.append((i[0],)) 1326 elif SCons.Util.is_Tuple(i): 1327 tmp.append(i) 1328 else: 1329 tmp.append((i,)) 1330 dk = tmp 1331 # Construct a list of (key, value) tuples. 1332 if SCons.Util.is_Dict(val): 1333 tmp = [] 1334 for (k, v) in val.items(): 1335 if v is not None: 1336 tmp.append((k, v)) 1337 else: 1338 tmp.append((k,)) 1339 val = tmp 1340 elif SCons.Util.is_String(val): 1341 val = [(val,)] 1342 if delete_existing: 1343 dk = list(filter(lambda x, val=val: x not in val, dk)) 1344 self._dict[key] = dk + val 1345 else: 1346 dk = [x for x in dk if x not in val] 1347 self._dict[key] = dk + val 1348 else: 1349 # By elimination, val is not a list. Since dk is a 1350 # list, wrap val in a list first. 1351 if delete_existing: 1352 dk = list(filter(lambda x, val=val: x not in val, dk)) 1353 self._dict[key] = dk + [val] 1354 else: 1355 if val not in dk: 1356 self._dict[key] = dk + [val] 1357 else: 1358 if key == 'CPPDEFINES': 1359 if SCons.Util.is_String(dk): 1360 dk = [dk] 1361 elif SCons.Util.is_Dict(dk): 1362 tmp = [] 1363 for (k, v) in dk.items(): 1364 if v is not None: 1365 tmp.append((k, v)) 1366 else: 1367 tmp.append((k,)) 1368 dk = tmp 1369 if SCons.Util.is_String(val): 1370 if val in dk: 1371 val = [] 1372 else: 1373 val = [val] 1374 elif SCons.Util.is_Dict(val): 1375 tmp = [] 1376 for i,j in val.items(): 1377 if j is not None: 1378 tmp.append((i,j)) 1379 else: 1380 tmp.append(i) 1381 val = tmp 1382 if delete_existing: 1383 dk = [x for x in dk if x not in val] 1384 self._dict[key] = dk + val 1385 self.scanner_map_delete(kw)
1386
1387 - def Clone(self, tools=[], toolpath=None, parse_flags = None, **kw):
1388 """Return a copy of a construction Environment. The 1389 copy is like a Python "deep copy"--that is, independent 1390 copies are made recursively of each objects--except that 1391 a reference is copied when an object is not deep-copyable 1392 (like a function). There are no references to any mutable 1393 objects in the original Environment. 1394 """ 1395 1396 builders = self._dict.get('BUILDERS', {}) 1397 1398 clone = copy.copy(self) 1399 # BUILDERS is not safe to do a simple copy 1400 clone._dict = semi_deepcopy_dict(self._dict, ['BUILDERS']) 1401 clone._dict['BUILDERS'] = BuilderDict(builders, clone) 1402 1403 # Check the methods added via AddMethod() and re-bind them to 1404 # the cloned environment. Only do this if the attribute hasn't 1405 # been overwritten by the user explicitly and still points to 1406 # the added method. 1407 clone.added_methods = [] 1408 for mw in self.added_methods: 1409 if mw == getattr(self, mw.name): 1410 clone.added_methods.append(mw.clone(clone)) 1411 1412 clone._memo = {} 1413 1414 # Apply passed-in variables before the tools 1415 # so the tools can use the new variables 1416 kw = copy_non_reserved_keywords(kw) 1417 new = {} 1418 for key, value in kw.items(): 1419 new[key] = SCons.Subst.scons_subst_once(value, self, key) 1420 clone.Replace(**new) 1421 1422 apply_tools(clone, tools, toolpath) 1423 1424 # apply them again in case the tools overwrote them 1425 clone.Replace(**new) 1426 1427 # Finally, apply any flags to be merged in 1428 if parse_flags: clone.MergeFlags(parse_flags) 1429 1430 if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.EnvironmentClone') 1431 return clone
1432
1433 - def Copy(self, *args, **kw):
1434 global _warn_copy_deprecated 1435 if _warn_copy_deprecated: 1436 msg = "The env.Copy() method is deprecated; use the env.Clone() method instead." 1437 SCons.Warnings.warn(SCons.Warnings.DeprecatedCopyWarning, msg) 1438 _warn_copy_deprecated = False 1439 return self.Clone(*args, **kw)
1440
1441 - def _changed_build(self, dependency, target, prev_ni, repo_node=None):
1442 if dependency.changed_state(target, prev_ni, repo_node): 1443 return 1 1444 return self.decide_source(dependency, target, prev_ni, repo_node)
1445
1446 - def _changed_content(self, dependency, target, prev_ni, repo_node=None):
1447 return dependency.changed_content(target, prev_ni, repo_node)
1448
1449 - def _changed_source(self, dependency, target, prev_ni, repo_node=None):
1450 target_env = dependency.get_build_env() 1451 type = target_env.get_tgt_sig_type() 1452 if type == 'source': 1453 return target_env.decide_source(dependency, target, prev_ni, repo_node) 1454 else: 1455 return target_env.decide_target(dependency, target, prev_ni, repo_node)
1456
1457 - def _changed_timestamp_then_content(self, dependency, target, prev_ni, repo_node=None):
1458 return dependency.changed_timestamp_then_content(target, prev_ni, repo_node)
1459
1460 - def _changed_timestamp_newer(self, dependency, target, prev_ni, repo_node=None):
1461 return dependency.changed_timestamp_newer(target, prev_ni, repo_node)
1462
1463 - def _changed_timestamp_match(self, dependency, target, prev_ni, repo_node=None):
1464 return dependency.changed_timestamp_match(target, prev_ni, repo_node)
1465
1466 - def _copy_from_cache(self, src, dst):
1467 return self.fs.copy(src, dst)
1468
1469 - def _copy2_from_cache(self, src, dst):
1470 return self.fs.copy2(src, dst)
1471
1472 - def Decider(self, function):
1473 copy_function = self._copy2_from_cache 1474 if function in ('MD5', 'content'): 1475 if not SCons.Util.md5: 1476 raise UserError("MD5 signatures are not available in this version of Python.") 1477 function = self._changed_content 1478 elif function == 'MD5-timestamp': 1479 function = self._changed_timestamp_then_content 1480 elif function in ('timestamp-newer', 'make'): 1481 function = self._changed_timestamp_newer 1482 copy_function = self._copy_from_cache 1483 elif function == 'timestamp-match': 1484 function = self._changed_timestamp_match 1485 elif not callable(function): 1486 raise UserError("Unknown Decider value %s" % repr(function)) 1487 1488 # We don't use AddMethod because we don't want to turn the 1489 # function, which only expects three arguments, into a bound 1490 # method, which would add self as an initial, fourth argument. 1491 self.decide_target = function 1492 self.decide_source = function 1493 1494 self.copy_from_cache = copy_function
1495
1496 - def Detect(self, progs):
1497 """Return the first available program in progs. 1498 """ 1499 if not SCons.Util.is_List(progs): 1500 progs = [ progs ] 1501 for prog in progs: 1502 path = self.WhereIs(prog) 1503 if path: return prog 1504 return None
1505
1506 - def Dictionary(self, *args):
1507 if not args: 1508 return self._dict 1509 dlist = [self._dict[x] for x in args] 1510 if len(dlist) == 1: 1511 dlist = dlist[0] 1512 return dlist
1513
1514 - def Dump(self, key = None):
1515 """ 1516 Using the standard Python pretty printer, return the contents of the 1517 scons build environment as a string. 1518 1519 If the key passed in is anything other than None, then that will 1520 be used as an index into the build environment dictionary and 1521 whatever is found there will be fed into the pretty printer. Note 1522 that this key is case sensitive. 1523 """ 1524 import pprint 1525 pp = pprint.PrettyPrinter(indent=2) 1526 if key: 1527 dict = self.Dictionary(key) 1528 else: 1529 dict = self.Dictionary() 1530 return pp.pformat(dict)
1531
1532 - def FindIxes(self, paths, prefix, suffix):
1533 """ 1534 Search a list of paths for something that matches the prefix and suffix. 1535 1536 paths - the list of paths or nodes. 1537 prefix - construction variable for the prefix. 1538 suffix - construction variable for the suffix. 1539 """ 1540 1541 suffix = self.subst('$'+suffix) 1542 prefix = self.subst('$'+prefix) 1543 1544 for path in paths: 1545 dir,name = os.path.split(str(path)) 1546 if name[:len(prefix)] == prefix and name[-len(suffix):] == suffix: 1547 return path
1548
1549 - def ParseConfig(self, command, function=None, unique=1):
1550 """ 1551 Use the specified function to parse the output of the command 1552 in order to modify the current environment. The 'command' can 1553 be a string or a list of strings representing a command and 1554 its arguments. 'Function' is an optional argument that takes 1555 the environment, the output of the command, and the unique flag. 1556 If no function is specified, MergeFlags, which treats the output 1557 as the result of a typical 'X-config' command (i.e. gtk-config), 1558 will merge the output into the appropriate variables. 1559 """ 1560 if function is None: 1561 def parse_conf(env, cmd, unique=unique): 1562 return env.MergeFlags(cmd, unique)
1563 function = parse_conf 1564 if SCons.Util.is_List(command): 1565 command = ' '.join(command) 1566 command = self.subst(command) 1567 return function(self, self.backtick(command))
1568
1569 - def ParseDepends(self, filename, must_exist=None, only_one=0):
1570 """ 1571 Parse a mkdep-style file for explicit dependencies. This is 1572 completely abusable, and should be unnecessary in the "normal" 1573 case of proper SCons configuration, but it may help make 1574 the transition from a Make hierarchy easier for some people 1575 to swallow. It can also be genuinely useful when using a tool 1576 that can write a .d file, but for which writing a scanner would 1577 be too complicated. 1578 """ 1579 filename = self.subst(filename) 1580 try: 1581 with open(filename, 'r') as fp: 1582 lines = SCons.Util.LogicalLines(fp).readlines() 1583 except IOError: 1584 if must_exist: 1585 raise 1586 return 1587 lines = [l for l in lines if l[0] != '#'] 1588 tdlist = [] 1589 for line in lines: 1590 try: 1591 target, depends = line.split(':', 1) 1592 except (AttributeError, ValueError): 1593 # Throws AttributeError if line isn't a string. Can throw 1594 # ValueError if line doesn't split into two or more elements. 1595 pass 1596 else: 1597 tdlist.append((target.split(), depends.split())) 1598 if only_one: 1599 targets = [] 1600 for td in tdlist: 1601 targets.extend(td[0]) 1602 if len(targets) > 1: 1603 raise SCons.Errors.UserError( 1604 "More than one dependency target found in `%s': %s" 1605 % (filename, targets)) 1606 for target, depends in tdlist: 1607 self.Depends(target, depends)
1608
1609 - def Platform(self, platform):
1610 platform = self.subst(platform) 1611 return SCons.Platform.Platform(platform)(self)
1612
1613 - def Prepend(self, **kw):
1614 """Prepend values to existing construction variables 1615 in an Environment. 1616 """ 1617 kw = copy_non_reserved_keywords(kw) 1618 for key, val in kw.items(): 1619 # It would be easier on the eyes to write this using 1620 # "continue" statements whenever we finish processing an item, 1621 # but Python 1.5.2 apparently doesn't let you use "continue" 1622 # within try:-except: blocks, so we have to nest our code. 1623 try: 1624 orig = self._dict[key] 1625 except KeyError: 1626 # No existing variable in the environment, so just set 1627 # it to the new value. 1628 self._dict[key] = val 1629 else: 1630 try: 1631 # Check if the original looks like a dictionary. 1632 # If it is, we can't just try adding the value because 1633 # dictionaries don't have __add__() methods, and 1634 # things like UserList will incorrectly coerce the 1635 # original dict to a list (which we don't want). 1636 update_dict = orig.update 1637 except AttributeError: 1638 try: 1639 # Most straightforward: just try to add them 1640 # together. This will work in most cases, when the 1641 # original and new values are of compatible types. 1642 self._dict[key] = val + orig 1643 except (KeyError, TypeError): 1644 try: 1645 # Check if the added value is a list. 1646 add_to_val = val.append 1647 except AttributeError: 1648 # The added value isn't a list, but the 1649 # original is (by process of elimination), 1650 # so insert the the new value in the original 1651 # (if there's one to insert). 1652 if val: 1653 orig.insert(0, val) 1654 else: 1655 # The added value is a list, so append 1656 # the original to it (if there's a value 1657 # to append). 1658 if orig: 1659 add_to_val(orig) 1660 self._dict[key] = val 1661 else: 1662 # The original looks like a dictionary, so update it 1663 # based on what we think the value looks like. 1664 if SCons.Util.is_List(val): 1665 for v in val: 1666 orig[v] = None 1667 else: 1668 try: 1669 update_dict(val) 1670 except (AttributeError, TypeError, ValueError): 1671 if SCons.Util.is_Dict(val): 1672 for k, v in val.items(): 1673 orig[k] = v 1674 else: 1675 orig[val] = None 1676 self.scanner_map_delete(kw)
1677
1678 - def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep, 1679 delete_existing=1):
1680 """Prepend path elements to the path 'name' in the 'ENV' 1681 dictionary for this environment. Will only add any particular 1682 path once, and will normpath and normcase all paths to help 1683 assure this. This can also handle the case where the env 1684 variable is a list instead of a string. 1685 1686 If delete_existing is 0, a newpath which is already in the path 1687 will not be moved to the front (it will be left where it is). 1688 """ 1689 1690 orig = '' 1691 if envname in self._dict and name in self._dict[envname]: 1692 orig = self._dict[envname][name] 1693 1694 nv = SCons.Util.PrependPath(orig, newpath, sep, delete_existing, 1695 canonicalize=self._canonicalize) 1696 1697 if envname not in self._dict: 1698 self._dict[envname] = {} 1699 1700 self._dict[envname][name] = nv
1701
1702 - def PrependUnique(self, delete_existing=0, **kw):
1703 """Prepend values to existing construction variables 1704 in an Environment, if they're not already there. 1705 If delete_existing is 1, removes existing values first, so 1706 values move to front. 1707 """ 1708 kw = copy_non_reserved_keywords(kw) 1709 for key, val in kw.items(): 1710 if SCons.Util.is_List(val): 1711 val = _delete_duplicates(val, not delete_existing) 1712 if key not in self._dict or self._dict[key] in ('', None): 1713 self._dict[key] = val 1714 elif SCons.Util.is_Dict(self._dict[key]) and \ 1715 SCons.Util.is_Dict(val): 1716 self._dict[key].update(val) 1717 elif SCons.Util.is_List(val): 1718 dk = self._dict[key] 1719 if not SCons.Util.is_List(dk): 1720 dk = [dk] 1721 if delete_existing: 1722 dk = [x for x in dk if x not in val] 1723 else: 1724 val = [x for x in val if x not in dk] 1725 self._dict[key] = val + dk 1726 else: 1727 dk = self._dict[key] 1728 if SCons.Util.is_List(dk): 1729 # By elimination, val is not a list. Since dk is a 1730 # list, wrap val in a list first. 1731 if delete_existing: 1732 dk = [x for x in dk if x not in val] 1733 self._dict[key] = [val] + dk 1734 else: 1735 if val not in dk: 1736 self._dict[key] = [val] + dk 1737 else: 1738 if delete_existing: 1739 dk = [x for x in dk if x not in val] 1740 self._dict[key] = val + dk 1741 self.scanner_map_delete(kw)
1742
1743 - def Replace(self, **kw):
1744 """Replace existing construction variables in an Environment 1745 with new construction variables and/or values. 1746 """ 1747 try: 1748 kwbd = kw['BUILDERS'] 1749 except KeyError: 1750 pass 1751 else: 1752 kwbd = BuilderDict(kwbd,self) 1753 del kw['BUILDERS'] 1754 self.__setitem__('BUILDERS', kwbd) 1755 kw = copy_non_reserved_keywords(kw) 1756 self._update(semi_deepcopy(kw)) 1757 self.scanner_map_delete(kw)
1758
1759 - def ReplaceIxes(self, path, old_prefix, old_suffix, new_prefix, new_suffix):
1760 """ 1761 Replace old_prefix with new_prefix and old_suffix with new_suffix. 1762 1763 env - Environment used to interpolate variables. 1764 path - the path that will be modified. 1765 old_prefix - construction variable for the old prefix. 1766 old_suffix - construction variable for the old suffix. 1767 new_prefix - construction variable for the new prefix. 1768 new_suffix - construction variable for the new suffix. 1769 """ 1770 old_prefix = self.subst('$'+old_prefix) 1771 old_suffix = self.subst('$'+old_suffix) 1772 1773 new_prefix = self.subst('$'+new_prefix) 1774 new_suffix = self.subst('$'+new_suffix) 1775 1776 dir,name = os.path.split(str(path)) 1777 if name[:len(old_prefix)] == old_prefix: 1778 name = name[len(old_prefix):] 1779 if name[-len(old_suffix):] == old_suffix: 1780 name = name[:-len(old_suffix)] 1781 return os.path.join(dir, new_prefix+name+new_suffix)
1782
1783 - def SetDefault(self, **kw):
1784 for k in list(kw.keys()): 1785 if k in self._dict: 1786 del kw[k] 1787 self.Replace(**kw)
1788
1789 - def _find_toolpath_dir(self, tp):
1790 return self.fs.Dir(self.subst(tp)).srcnode().get_abspath()
1791
1792 - def Tool(self, tool, toolpath=None, **kw):
1793 if SCons.Util.is_String(tool): 1794 tool = self.subst(tool) 1795 if toolpath is None: 1796 toolpath = self.get('toolpath', []) 1797 toolpath = list(map(self._find_toolpath_dir, toolpath)) 1798 tool = SCons.Tool.Tool(tool, toolpath, **kw) 1799 tool(self)
1800
1801 - def WhereIs(self, prog, path=None, pathext=None, reject=[]):
1802 """Find prog in the path. 1803 """ 1804 if path is None: 1805 try: 1806 path = self['ENV']['PATH'] 1807 except KeyError: 1808 pass 1809 elif SCons.Util.is_String(path): 1810 path = self.subst(path) 1811 if pathext is None: 1812 try: 1813 pathext = self['ENV']['PATHEXT'] 1814 except KeyError: 1815 pass 1816 elif SCons.Util.is_String(pathext): 1817 pathext = self.subst(pathext) 1818 prog = SCons.Util.CLVar(self.subst(prog)) # support "program --with-args" 1819 path = SCons.Util.WhereIs(prog[0], path, pathext, reject) 1820 if path: return path 1821 return None
1822 1823 ####################################################################### 1824 # Public methods for doing real "SCons stuff" (manipulating 1825 # dependencies, setting attributes on targets, etc.). These begin 1826 # with upper-case letters. The essential characteristic of methods 1827 # in this section is that they all *should* have corresponding 1828 # same-named global functions. 1829 ####################################################################### 1830
1831 - def Action(self, *args, **kw):
1832 def subst_string(a, self=self): 1833 if SCons.Util.is_String(a): 1834 a = self.subst(a) 1835 return a
1836 nargs = list(map(subst_string, args)) 1837 nkw = self.subst_kw(kw) 1838 return SCons.Action.Action(*nargs, **nkw) 1839
1840 - def AddPreAction(self, files, action):
1841 nodes = self.arg2nodes(files, self.fs.Entry) 1842 action = SCons.Action.Action(action) 1843 uniq = {} 1844 for executor in [n.get_executor() for n in nodes]: 1845 uniq[executor] = 1 1846 for executor in list(uniq.keys()): 1847 executor.add_pre_action(action) 1848 return nodes
1849
1850 - def AddPostAction(self, files, action):
1851 nodes = self.arg2nodes(files, self.fs.Entry) 1852 action = SCons.Action.Action(action) 1853 uniq = {} 1854 for executor in [n.get_executor() for n in nodes]: 1855 uniq[executor] = 1 1856 for executor in list(uniq.keys()): 1857 executor.add_post_action(action) 1858 return nodes
1859
1860 - def Alias(self, target, source=[], action=None, **kw):
1861 tlist = self.arg2nodes(target, self.ans.Alias) 1862 if not SCons.Util.is_List(source): 1863 source = [source] 1864 source = [_f for _f in source if _f] 1865 1866 if not action: 1867 if not source: 1868 # There are no source files and no action, so just 1869 # return a target list of classic Alias Nodes, without 1870 # any builder. The externally visible effect is that 1871 # this will make the wrapping Script.BuildTask class 1872 # say that there's "Nothing to be done" for this Alias, 1873 # instead of that it's "up to date." 1874 return tlist 1875 1876 # No action, but there are sources. Re-call all the target 1877 # builders to add the sources to each target. 1878 result = [] 1879 for t in tlist: 1880 bld = t.get_builder(AliasBuilder) 1881 result.extend(bld(self, t, source)) 1882 return result 1883 1884 nkw = self.subst_kw(kw) 1885 nkw.update({ 1886 'action' : SCons.Action.Action(action), 1887 'source_factory' : self.fs.Entry, 1888 'multi' : 1, 1889 'is_explicit' : None, 1890 }) 1891 bld = SCons.Builder.Builder(**nkw) 1892 1893 # Apply the Builder separately to each target so that the Aliases 1894 # stay separate. If we did one "normal" Builder call with the 1895 # whole target list, then all of the target Aliases would be 1896 # associated under a single Executor. 1897 result = [] 1898 for t in tlist: 1899 # Calling the convert() method will cause a new Executor to be 1900 # created from scratch, so we have to explicitly initialize 1901 # it with the target's existing sources, plus our new ones, 1902 # so nothing gets lost. 1903 b = t.get_builder() 1904 if b is None or b is AliasBuilder: 1905 b = bld 1906 else: 1907 nkw['action'] = b.action + action 1908 b = SCons.Builder.Builder(**nkw) 1909 t.convert() 1910 result.extend(b(self, t, t.sources + source)) 1911 return result
1912
1913 - def AlwaysBuild(self, *targets):
1914 tlist = [] 1915 for t in targets: 1916 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 1917 for t in tlist: 1918 t.set_always_build() 1919 return tlist
1920
1921 - def BuildDir(self, *args, **kw):
1922 msg = """BuildDir() and the build_dir keyword have been deprecated;\n\tuse VariantDir() and the variant_dir keyword instead.""" 1923 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) 1924 if 'build_dir' in kw: 1925 kw['variant_dir'] = kw['build_dir'] 1926 del kw['build_dir'] 1927 return self.VariantDir(*args, **kw)
1928
1929 - def Builder(self, **kw):
1930 nkw = self.subst_kw(kw) 1931 return SCons.Builder.Builder(**nkw)
1932
1933 - def CacheDir(self, path):
1934 import SCons.CacheDir 1935 if path is not None: 1936 path = self.subst(path) 1937 self._CacheDir_path = path
1938
1939 - def Clean(self, targets, files):
1940 global CleanTargets 1941 tlist = self.arg2nodes(targets, self.fs.Entry) 1942 flist = self.arg2nodes(files, self.fs.Entry) 1943 for t in tlist: 1944 try: 1945 CleanTargets[t].extend(flist) 1946 except KeyError: 1947 CleanTargets[t] = flist
1948
1949 - def Configure(self, *args, **kw):
1950 nargs = [self] 1951 if args: 1952 nargs = nargs + self.subst_list(args)[0] 1953 nkw = self.subst_kw(kw) 1954 nkw['_depth'] = kw.get('_depth', 0) + 1 1955 try: 1956 nkw['custom_tests'] = self.subst_kw(nkw['custom_tests']) 1957 except KeyError: 1958 pass 1959 return SCons.SConf.SConf(*nargs, **nkw)
1960
1961 - def Command(self, target, source, action, **kw):
1962 """Builds the supplied target files from the supplied 1963 source files using the supplied action. Action may 1964 be any type that the Builder constructor will accept 1965 for an action.""" 1966 bkw = { 1967 'action' : action, 1968 'target_factory' : self.fs.Entry, 1969 'source_factory' : self.fs.Entry, 1970 } 1971 try: bkw['source_scanner'] = kw['source_scanner'] 1972 except KeyError: pass 1973 else: del kw['source_scanner'] 1974 bld = SCons.Builder.Builder(**bkw) 1975 return bld(self, target, source, **kw)
1976
1977 - def Depends(self, target, dependency):
1978 """Explicity specify that 'target's depend on 'dependency'.""" 1979 tlist = self.arg2nodes(target, self.fs.Entry) 1980 dlist = self.arg2nodes(dependency, self.fs.Entry) 1981 for t in tlist: 1982 t.add_dependency(dlist) 1983 return tlist
1984
1985 - def Dir(self, name, *args, **kw):
1986 """ 1987 """ 1988 s = self.subst(name) 1989 if SCons.Util.is_Sequence(s): 1990 result=[] 1991 for e in s: 1992 result.append(self.fs.Dir(e, *args, **kw)) 1993 return result 1994 return self.fs.Dir(s, *args, **kw)
1995
1996 - def PyPackageDir(self, modulename):
1997 s = self.subst(modulename) 1998 if SCons.Util.is_Sequence(s): 1999 result=[] 2000 for e in s: 2001 result.append(self.fs.PyPackageDir(e)) 2002 return result 2003 return self.fs.PyPackageDir(s)
2004
2005 - def NoClean(self, *targets):
2006 """Tags a target so that it will not be cleaned by -c""" 2007 tlist = [] 2008 for t in targets: 2009 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 2010 for t in tlist: 2011 t.set_noclean() 2012 return tlist
2013
2014 - def NoCache(self, *targets):
2015 """Tags a target so that it will not be cached""" 2016 tlist = [] 2017 for t in targets: 2018 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 2019 for t in tlist: 2020 t.set_nocache() 2021 return tlist
2022
2023 - def Entry(self, name, *args, **kw):
2024 """ 2025 """ 2026 s = self.subst(name) 2027 if SCons.Util.is_Sequence(s): 2028 result=[] 2029 for e in s: 2030 result.append(self.fs.Entry(e, *args, **kw)) 2031 return result 2032 return self.fs.Entry(s, *args, **kw)
2033
2034 - def Environment(self, **kw):
2035 return SCons.Environment.Environment(**self.subst_kw(kw))
2036
2037 - def Execute(self, action, *args, **kw):
2038 """Directly execute an action through an Environment 2039 """ 2040 action = self.Action(action, *args, **kw) 2041 result = action([], [], self) 2042 if isinstance(result, SCons.Errors.BuildError): 2043 errstr = result.errstr 2044 if result.filename: 2045 errstr = result.filename + ': ' + errstr 2046 sys.stderr.write("scons: *** %s\n" % errstr) 2047 return result.status 2048 else: 2049 return result
2050
2051 - def File(self, name, *args, **kw):
2052 """ 2053 """ 2054 s = self.subst(name) 2055 if SCons.Util.is_Sequence(s): 2056 result=[] 2057 for e in s: 2058 result.append(self.fs.File(e, *args, **kw)) 2059 return result 2060 return self.fs.File(s, *args, **kw)
2061
2062 - def FindFile(self, file, dirs):
2063 file = self.subst(file) 2064 nodes = self.arg2nodes(dirs, self.fs.Dir) 2065 return SCons.Node.FS.find_file(file, tuple(nodes))
2066
2067 - def Flatten(self, sequence):
2068 return SCons.Util.flatten(sequence)
2069
2070 - def GetBuildPath(self, files):
2071 result = list(map(str, self.arg2nodes(files, self.fs.Entry))) 2072 if SCons.Util.is_List(files): 2073 return result 2074 else: 2075 return result[0]
2076
2077 - def Glob(self, pattern, ondisk=True, source=False, strings=False, exclude=None):
2078 return self.fs.Glob(self.subst(pattern), ondisk, source, strings, exclude)
2079
2080 - def Ignore(self, target, dependency):
2081 """Ignore a dependency.""" 2082 tlist = self.arg2nodes(target, self.fs.Entry) 2083 dlist = self.arg2nodes(dependency, self.fs.Entry) 2084 for t in tlist: 2085 t.add_ignore(dlist) 2086 return tlist
2087
2088 - def Literal(self, string):
2089 return SCons.Subst.Literal(string)
2090
2091 - def Local(self, *targets):
2092 ret = [] 2093 for targ in targets: 2094 if isinstance(targ, SCons.Node.Node): 2095 targ.set_local() 2096 ret.append(targ) 2097 else: 2098 for t in self.arg2nodes(targ, self.fs.Entry): 2099 t.set_local() 2100 ret.append(t) 2101 return ret
2102
2103 - def Precious(self, *targets):
2104 tlist = [] 2105 for t in targets: 2106 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 2107 for t in tlist: 2108 t.set_precious() 2109 return tlist
2110
2111 - def Pseudo(self, *targets):
2112 tlist = [] 2113 for t in targets: 2114 tlist.extend(self.arg2nodes(t, self.fs.Entry)) 2115 for t in tlist: 2116 t.set_pseudo() 2117 return tlist
2118
2119 - def Repository(self, *dirs, **kw):
2120 dirs = self.arg2nodes(list(dirs), self.fs.Dir) 2121 self.fs.Repository(*dirs, **kw)
2122
2123 - def Requires(self, target, prerequisite):
2124 """Specify that 'prerequisite' must be built before 'target', 2125 (but 'target' does not actually depend on 'prerequisite' 2126 and need not be rebuilt if it changes).""" 2127 tlist = self.arg2nodes(target, self.fs.Entry) 2128 plist = self.arg2nodes(prerequisite, self.fs.Entry) 2129 for t in tlist: 2130 t.add_prerequisite(plist) 2131 return tlist
2132
2133 - def Scanner(self, *args, **kw):
2134 nargs = [] 2135 for arg in args: 2136 if SCons.Util.is_String(arg): 2137 arg = self.subst(arg) 2138 nargs.append(arg) 2139 nkw = self.subst_kw(kw) 2140 return SCons.Scanner.Base(*nargs, **nkw)
2141
2142 - def SConsignFile(self, name=".sconsign", dbm_module=None):
2143 if name is not None: 2144 name = self.subst(name) 2145 if not os.path.isabs(name): 2146 name = os.path.join(str(self.fs.SConstruct_dir), name) 2147 if name: 2148 name = os.path.normpath(name) 2149 sconsign_dir = os.path.dirname(name) 2150 if sconsign_dir and not os.path.exists(sconsign_dir): 2151 self.Execute(SCons.Defaults.Mkdir(sconsign_dir)) 2152 SCons.SConsign.File(name, dbm_module)
2153
2154 - def SideEffect(self, side_effect, target):
2155 """Tell scons that side_effects are built as side 2156 effects of building targets.""" 2157 side_effects = self.arg2nodes(side_effect, self.fs.Entry) 2158 targets = self.arg2nodes(target, self.fs.Entry) 2159 2160 for side_effect in side_effects: 2161 if side_effect.multiple_side_effect_has_builder(): 2162 raise SCons.Errors.UserError("Multiple ways to build the same target were specified for: %s" % str(side_effect)) 2163 side_effect.add_source(targets) 2164 side_effect.side_effect = 1 2165 self.Precious(side_effect) 2166 for target in targets: 2167 target.side_effects.append(side_effect) 2168 return side_effects
2169
2170 - def SourceCode(self, entry, builder):
2171 """Arrange for a source code builder for (part of) a tree.""" 2172 msg = """SourceCode() has been deprecated and there is no replacement. 2173 \tIf you need this function, please contact scons-dev@scons.org""" 2174 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceCodeWarning, msg) 2175 entries = self.arg2nodes(entry, self.fs.Entry) 2176 for entry in entries: 2177 entry.set_src_builder(builder) 2178 return entries
2179
2180 - def SourceSignatures(self, type):
2181 global _warn_source_signatures_deprecated 2182 if _warn_source_signatures_deprecated: 2183 msg = "The env.SourceSignatures() method is deprecated;\n" + \ 2184 "\tconvert your build to use the env.Decider() method instead." 2185 SCons.Warnings.warn(SCons.Warnings.DeprecatedSourceSignaturesWarning, msg) 2186 _warn_source_signatures_deprecated = False 2187 type = self.subst(type) 2188 self.src_sig_type = type 2189 if type == 'MD5': 2190 if not SCons.Util.md5: 2191 raise UserError("MD5 signatures are not available in this version of Python.") 2192 self.decide_source = self._changed_content 2193 elif type == 'timestamp': 2194 self.decide_source = self._changed_timestamp_match 2195 else: 2196 raise UserError("Unknown source signature type '%s'" % type)
2197
2198 - def Split(self, arg):
2199 """This function converts a string or list into a list of strings 2200 or Nodes. This makes things easier for users by allowing files to 2201 be specified as a white-space separated list to be split. 2202 2203 The input rules are: 2204 - A single string containing names separated by spaces. These will be 2205 split apart at the spaces. 2206 - A single Node instance 2207 - A list containing either strings or Node instances. Any strings 2208 in the list are not split at spaces. 2209 2210 In all cases, the function returns a list of Nodes and strings.""" 2211 2212 if SCons.Util.is_List(arg): 2213 return list(map(self.subst, arg)) 2214 elif SCons.Util.is_String(arg): 2215 return self.subst(arg).split() 2216 else: 2217 return [self.subst(arg)]
2218
2219 - def TargetSignatures(self, type):
2220 global _warn_target_signatures_deprecated 2221 if _warn_target_signatures_deprecated: 2222 msg = "The env.TargetSignatures() method is deprecated;\n" + \ 2223 "\tconvert your build to use the env.Decider() method instead." 2224 SCons.Warnings.warn(SCons.Warnings.DeprecatedTargetSignaturesWarning, msg) 2225 _warn_target_signatures_deprecated = False 2226 type = self.subst(type) 2227 self.tgt_sig_type = type 2228 if type in ('MD5', 'content'): 2229 if not SCons.Util.md5: 2230 raise UserError("MD5 signatures are not available in this version of Python.") 2231 self.decide_target = self._changed_content 2232 elif type == 'timestamp': 2233 self.decide_target = self._changed_timestamp_match 2234 elif type == 'build': 2235 self.decide_target = self._changed_build 2236 elif type == 'source': 2237 self.decide_target = self._changed_source 2238 else: 2239 raise UserError("Unknown target signature type '%s'"%type)
2240
2241 - def Value(self, value, built_value=None):
2242 """ 2243 """ 2244 return SCons.Node.Python.Value(value, built_value)
2245
2246 - def VariantDir(self, variant_dir, src_dir, duplicate=1):
2247 variant_dir = self.arg2nodes(variant_dir, self.fs.Dir)[0] 2248 src_dir = self.arg2nodes(src_dir, self.fs.Dir)[0] 2249 self.fs.VariantDir(variant_dir, src_dir, duplicate)
2250
2251 - def FindSourceFiles(self, node='.'):
2252 """ returns a list of all source files. 2253 """ 2254 node = self.arg2nodes(node, self.fs.Entry)[0] 2255 2256 sources = [] 2257 def build_source(ss): 2258 for s in ss: 2259 if isinstance(s, SCons.Node.FS.Dir): 2260 build_source(s.all_children()) 2261 elif s.has_builder(): 2262 build_source(s.sources) 2263 elif isinstance(s.disambiguate(), SCons.Node.FS.File): 2264 sources.append(s)
2265 build_source(node.all_children()) 2266 2267 def final_source(node): 2268 while (node != node.srcnode()): 2269 node = node.srcnode() 2270 return node 2271 sources = list(map(final_source, sources)) 2272 # remove duplicates 2273 return list(set(sources)) 2274
2275 - def FindInstalledFiles(self):
2276 """ returns the list of all targets of the Install and InstallAs Builder. 2277 """ 2278 from SCons.Tool import install 2279 if install._UNIQUE_INSTALLED_FILES is None: 2280 install._UNIQUE_INSTALLED_FILES = SCons.Util.uniquer_hashables(install._INSTALLED_FILES) 2281 return install._UNIQUE_INSTALLED_FILES
2282
2283 2284 -class OverrideEnvironment(Base):
2285 """A proxy that overrides variables in a wrapped construction 2286 environment by returning values from an overrides dictionary in 2287 preference to values from the underlying subject environment. 2288 2289 This is a lightweight (I hope) proxy that passes through most use of 2290 attributes to the underlying Environment.Base class, but has just 2291 enough additional methods defined to act like a real construction 2292 environment with overridden values. It can wrap either a Base 2293 construction environment, or another OverrideEnvironment, which 2294 can in turn nest arbitrary OverrideEnvironments... 2295 2296 Note that we do *not* call the underlying base class 2297 (SubsitutionEnvironment) initialization, because we get most of those 2298 from proxying the attributes of the subject construction environment. 2299 But because we subclass SubstitutionEnvironment, this class also 2300 has inherited arg2nodes() and subst*() methods; those methods can't 2301 be proxied because they need *this* object's methods to fetch the 2302 values from the overrides dictionary. 2303 """ 2304
2305 - def __init__(self, subject, overrides={}):
2306 if SCons.Debug.track_instances: logInstanceCreation(self, 'Environment.OverrideEnvironment') 2307 self.__dict__['__subject'] = subject 2308 self.__dict__['overrides'] = overrides
2309 2310 # Methods that make this class act like a proxy.
2311 - def __getattr__(self, name):
2312 attr = getattr(self.__dict__['__subject'], name) 2313 # Here we check if attr is one of the Wrapper classes. For 2314 # example when a pseudo-builder is being called from an 2315 # OverrideEnvironment. 2316 # 2317 # These wrappers when they're constructed capture the 2318 # Environment they are being constructed with and so will not 2319 # have access to overrided values. So we rebuild them with the 2320 # OverrideEnvironment so they have access to overrided values. 2321 if isinstance(attr, (MethodWrapper, BuilderWrapper)): 2322 return attr.clone(self) 2323 else: 2324 return attr
2325
2326 - def __setattr__(self, name, value):
2327 setattr(self.__dict__['__subject'], name, value)
2328 2329 # Methods that make this class act like a dictionary.
2330 - def __getitem__(self, key):
2331 try: 2332 return self.__dict__['overrides'][key] 2333 except KeyError: 2334 return self.__dict__['__subject'].__getitem__(key)
2335 - def __setitem__(self, key, value):
2336 if not is_valid_construction_var(key): 2337 raise SCons.Errors.UserError("Illegal construction variable `%s'" % key) 2338 self.__dict__['overrides'][key] = value
2339 - def __delitem__(self, key):
2340 try: 2341 del self.__dict__['overrides'][key] 2342 except KeyError: 2343 deleted = 0 2344 else: 2345 deleted = 1 2346 try: 2347 result = self.__dict__['__subject'].__delitem__(key) 2348 except KeyError: 2349 if not deleted: 2350 raise 2351 result = None 2352 return result
2353 - def get(self, key, default=None):
2354 """Emulates the get() method of dictionaries.""" 2355 try: 2356 return self.__dict__['overrides'][key] 2357 except KeyError: 2358 return self.__dict__['__subject'].get(key, default)
2359 - def has_key(self, key):
2360 try: 2361 self.__dict__['overrides'][key] 2362 return 1 2363 except KeyError: 2364 return key in self.__dict__['__subject']
2365 - def __contains__(self, key):
2366 if self.__dict__['overrides'].__contains__(key): 2367 return 1 2368 return self.__dict__['__subject'].__contains__(key)
2369 - def Dictionary(self):
2370 """Emulates the items() method of dictionaries.""" 2371 d = self.__dict__['__subject'].Dictionary().copy() 2372 d.update(self.__dict__['overrides']) 2373 return d
2374 - def items(self):
2375 """Emulates the items() method of dictionaries.""" 2376 return list(self.Dictionary().items())
2377 2378 # Overridden private construction environment methods.
2379 - def _update(self, dict):
2380 """Update an environment's values directly, bypassing the normal 2381 checks that occur when users try to set items. 2382 """ 2383 self.__dict__['overrides'].update(dict)
2384
2385 - def gvars(self):
2386 return self.__dict__['__subject'].gvars()
2387
2388 - def lvars(self):
2389 lvars = self.__dict__['__subject'].lvars() 2390 lvars.update(self.__dict__['overrides']) 2391 return lvars
2392 2393 # Overridden public construction environment methods.
2394 - def Replace(self, **kw):
2395 kw = copy_non_reserved_keywords(kw) 2396 self.__dict__['overrides'].update(semi_deepcopy(kw))
2397 2398 2399 # The entry point that will be used by the external world 2400 # to refer to a construction environment. This allows the wrapper 2401 # interface to extend a construction environment for its own purposes 2402 # by subclassing SCons.Environment.Base and then assigning the 2403 # class to SCons.Environment.Environment. 2404 2405 Environment = Base
2406 2407 2408 -def NoSubstitutionProxy(subject):
2409 """ 2410 An entry point for returning a proxy subclass instance that overrides 2411 the subst*() methods so they don't actually perform construction 2412 variable substitution. This is specifically intended to be the shim 2413 layer in between global function calls (which don't want construction 2414 variable substitution) and the DefaultEnvironment() (which would 2415 substitute variables if left to its own devices). 2416 2417 We have to wrap this in a function that allows us to delay definition of 2418 the class until it's necessary, so that when it subclasses Environment 2419 it will pick up whatever Environment subclass the wrapper interface 2420 might have assigned to SCons.Environment.Environment. 2421 """ 2422 class _NoSubstitutionProxy(Environment): 2423 def __init__(self, subject): 2424 self.__dict__['__subject'] = subject
2425 def __getattr__(self, name): 2426 return getattr(self.__dict__['__subject'], name) 2427 def __setattr__(self, name, value): 2428 return setattr(self.__dict__['__subject'], name, value) 2429 def executor_to_lvars(self, kwdict): 2430 if 'executor' in kwdict: 2431 kwdict['lvars'] = kwdict['executor'].get_lvars() 2432 del kwdict['executor'] 2433 else: 2434 kwdict['lvars'] = {} 2435 def raw_to_mode(self, dict): 2436 try: 2437 raw = dict['raw'] 2438 except KeyError: 2439 pass 2440 else: 2441 del dict['raw'] 2442 dict['mode'] = raw 2443 def subst(self, string, *args, **kwargs): 2444 return string 2445 def subst_kw(self, kw, *args, **kwargs): 2446 return kw 2447 def subst_list(self, string, *args, **kwargs): 2448 nargs = (string, self,) + args 2449 nkw = kwargs.copy() 2450 nkw['gvars'] = {} 2451 self.executor_to_lvars(nkw) 2452 self.raw_to_mode(nkw) 2453 return SCons.Subst.scons_subst_list(*nargs, **nkw) 2454 def subst_target_source(self, string, *args, **kwargs): 2455 nargs = (string, self,) + args 2456 nkw = kwargs.copy() 2457 nkw['gvars'] = {} 2458 self.executor_to_lvars(nkw) 2459 self.raw_to_mode(nkw) 2460 return SCons.Subst.scons_subst(*nargs, **nkw) 2461 return _NoSubstitutionProxy(subject) 2462 2463 # Local Variables: 2464 # tab-width:4 2465 # indent-tabs-mode:nil 2466 # End: 2467 # vim: set expandtab tabstop=4 shiftwidth=4: 2468