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