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