OLD | NEW |
(Empty) | |
| 1 """SCons.Script.SConscript |
| 2 |
| 3 This module defines the Python API provided to SConscript and SConstruct |
| 4 files. |
| 5 |
| 6 """ |
| 7 |
| 8 # |
| 9 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The S
Cons Foundation |
| 10 # |
| 11 # Permission is hereby granted, free of charge, to any person obtaining |
| 12 # a copy of this software and associated documentation files (the |
| 13 # "Software"), to deal in the Software without restriction, including |
| 14 # without limitation the rights to use, copy, modify, merge, publish, |
| 15 # distribute, sublicense, and/or sell copies of the Software, and to |
| 16 # permit persons to whom the Software is furnished to do so, subject to |
| 17 # the following conditions: |
| 18 # |
| 19 # The above copyright notice and this permission notice shall be included |
| 20 # in all copies or substantial portions of the Software. |
| 21 # |
| 22 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY |
| 23 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE |
| 24 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 25 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE |
| 26 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION |
| 27 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 28 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 29 from __future__ import division |
| 30 |
| 31 __revision__ = "src/engine/SCons/Script/SConscript.py 5134 2010/08/16 23:02:40 b
deegan" |
| 32 |
| 33 import SCons |
| 34 import SCons.Action |
| 35 import SCons.Builder |
| 36 import SCons.Defaults |
| 37 import SCons.Environment |
| 38 import SCons.Errors |
| 39 import SCons.Node |
| 40 import SCons.Node.Alias |
| 41 import SCons.Node.FS |
| 42 import SCons.Platform |
| 43 import SCons.SConf |
| 44 import SCons.Script.Main |
| 45 import SCons.Tool |
| 46 import SCons.Util |
| 47 |
| 48 import collections |
| 49 import os |
| 50 import os.path |
| 51 import re |
| 52 import sys |
| 53 import traceback |
| 54 |
| 55 # The following variables used to live in this module. Some |
| 56 # SConscript files out there may have referred to them directly as |
| 57 # SCons.Script.SConscript.*. This is now supported by some special |
| 58 # handling towards the bottom of the SConscript.__init__.py module. |
| 59 #Arguments = {} |
| 60 #ArgList = [] |
| 61 #BuildTargets = TargetList() |
| 62 #CommandLineTargets = [] |
| 63 #DefaultTargets = [] |
| 64 |
| 65 class SConscriptReturn(Exception): |
| 66 pass |
| 67 |
| 68 launch_dir = os.path.abspath(os.curdir) |
| 69 |
| 70 GlobalDict = None |
| 71 |
| 72 # global exports set by Export(): |
| 73 global_exports = {} |
| 74 |
| 75 # chdir flag |
| 76 sconscript_chdir = 1 |
| 77 |
| 78 def get_calling_namespaces(): |
| 79 """Return the locals and globals for the function that called |
| 80 into this module in the current call stack.""" |
| 81 try: 1//0 |
| 82 except ZeroDivisionError: |
| 83 # Don't start iterating with the current stack-frame to |
| 84 # prevent creating reference cycles (f_back is safe). |
| 85 frame = sys.exc_info()[2].tb_frame.f_back |
| 86 |
| 87 # Find the first frame that *isn't* from this file. This means |
| 88 # that we expect all of the SCons frames that implement an Export() |
| 89 # or SConscript() call to be in this file, so that we can identify |
| 90 # the first non-Script.SConscript frame as the user's local calling |
| 91 # environment, and the locals and globals dictionaries from that |
| 92 # frame as the calling namespaces. See the comment below preceding |
| 93 # the DefaultEnvironmentCall block for even more explanation. |
| 94 while frame.f_globals.get("__name__") == __name__: |
| 95 frame = frame.f_back |
| 96 |
| 97 return frame.f_locals, frame.f_globals |
| 98 |
| 99 |
| 100 def compute_exports(exports): |
| 101 """Compute a dictionary of exports given one of the parameters |
| 102 to the Export() function or the exports argument to SConscript().""" |
| 103 |
| 104 loc, glob = get_calling_namespaces() |
| 105 |
| 106 retval = {} |
| 107 try: |
| 108 for export in exports: |
| 109 if SCons.Util.is_Dict(export): |
| 110 retval.update(export) |
| 111 else: |
| 112 try: |
| 113 retval[export] = loc[export] |
| 114 except KeyError: |
| 115 retval[export] = glob[export] |
| 116 except KeyError, x: |
| 117 raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x) |
| 118 |
| 119 return retval |
| 120 |
| 121 class Frame(object): |
| 122 """A frame on the SConstruct/SConscript call stack""" |
| 123 def __init__(self, fs, exports, sconscript): |
| 124 self.globals = BuildDefaultGlobals() |
| 125 self.retval = None |
| 126 self.prev_dir = fs.getcwd() |
| 127 self.exports = compute_exports(exports) # exports from the calling SCon
script |
| 128 # make sure the sconscript attr is a Node. |
| 129 if isinstance(sconscript, SCons.Node.Node): |
| 130 self.sconscript = sconscript |
| 131 elif sconscript == '-': |
| 132 self.sconscript = None |
| 133 else: |
| 134 self.sconscript = fs.File(str(sconscript)) |
| 135 |
| 136 # the SConstruct/SConscript call stack: |
| 137 call_stack = [] |
| 138 |
| 139 # For documentation on the methods in this file, see the scons man-page |
| 140 |
| 141 def Return(*vars, **kw): |
| 142 retval = [] |
| 143 try: |
| 144 fvars = SCons.Util.flatten(vars) |
| 145 for var in fvars: |
| 146 for v in var.split(): |
| 147 retval.append(call_stack[-1].globals[v]) |
| 148 except KeyError, x: |
| 149 raise SCons.Errors.UserError("Return of non-existent variable '%s'"%x) |
| 150 |
| 151 if len(retval) == 1: |
| 152 call_stack[-1].retval = retval[0] |
| 153 else: |
| 154 call_stack[-1].retval = tuple(retval) |
| 155 |
| 156 stop = kw.get('stop', True) |
| 157 |
| 158 if stop: |
| 159 raise SConscriptReturn |
| 160 |
| 161 |
| 162 stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :) |
| 163 |
| 164 def _SConscript(fs, *files, **kw): |
| 165 top = fs.Top |
| 166 sd = fs.SConstruct_dir.rdir() |
| 167 exports = kw.get('exports', []) |
| 168 |
| 169 # evaluate each SConscript file |
| 170 results = [] |
| 171 for fn in files: |
| 172 call_stack.append(Frame(fs, exports, fn)) |
| 173 old_sys_path = sys.path |
| 174 try: |
| 175 SCons.Script.sconscript_reading = SCons.Script.sconscript_reading +
1 |
| 176 if fn == "-": |
| 177 exec sys.stdin in call_stack[-1].globals |
| 178 else: |
| 179 if isinstance(fn, SCons.Node.Node): |
| 180 f = fn |
| 181 else: |
| 182 f = fs.File(str(fn)) |
| 183 _file_ = None |
| 184 |
| 185 # Change directory to the top of the source |
| 186 # tree to make sure the os's cwd and the cwd of |
| 187 # fs match so we can open the SConscript. |
| 188 fs.chdir(top, change_os_dir=1) |
| 189 if f.rexists(): |
| 190 actual = f.rfile() |
| 191 _file_ = open(actual.get_abspath(), "r") |
| 192 elif f.srcnode().rexists(): |
| 193 actual = f.srcnode().rfile() |
| 194 _file_ = open(actual.get_abspath(), "r") |
| 195 elif f.has_src_builder(): |
| 196 # The SConscript file apparently exists in a source |
| 197 # code management system. Build it, but then clear |
| 198 # the builder so that it doesn't get built *again* |
| 199 # during the actual build phase. |
| 200 f.build() |
| 201 f.built() |
| 202 f.builder_set(None) |
| 203 if f.exists(): |
| 204 _file_ = open(f.get_abspath(), "r") |
| 205 if _file_: |
| 206 # Chdir to the SConscript directory. Use a path |
| 207 # name relative to the SConstruct file so that if |
| 208 # we're using the -f option, we're essentially |
| 209 # creating a parallel SConscript directory structure |
| 210 # in our local directory tree. |
| 211 # |
| 212 # XXX This is broken for multiple-repository cases |
| 213 # where the SConstruct and SConscript files might be |
| 214 # in different Repositories. For now, cross that |
| 215 # bridge when someone comes to it. |
| 216 try: |
| 217 src_dir = kw['src_dir'] |
| 218 except KeyError: |
| 219 ldir = fs.Dir(f.dir.get_path(sd)) |
| 220 else: |
| 221 ldir = fs.Dir(src_dir) |
| 222 if not ldir.is_under(f.dir): |
| 223 # They specified a source directory, but |
| 224 # it's above the SConscript directory. |
| 225 # Do the sensible thing and just use the |
| 226 # SConcript directory. |
| 227 ldir = fs.Dir(f.dir.get_path(sd)) |
| 228 try: |
| 229 fs.chdir(ldir, change_os_dir=sconscript_chdir) |
| 230 except OSError: |
| 231 # There was no local directory, so we should be |
| 232 # able to chdir to the Repository directory. |
| 233 # Note that we do this directly, not through |
| 234 # fs.chdir(), because we still need to |
| 235 # interpret the stuff within the SConscript file |
| 236 # relative to where we are logically. |
| 237 fs.chdir(ldir, change_os_dir=0) |
| 238 os.chdir(actual.dir.get_abspath()) |
| 239 |
| 240 # Append the SConscript directory to the beginning |
| 241 # of sys.path so Python modules in the SConscript |
| 242 # directory can be easily imported. |
| 243 sys.path = [ f.dir.get_abspath() ] + sys.path |
| 244 |
| 245 # This is the magic line that actually reads up |
| 246 # and executes the stuff in the SConscript file. |
| 247 # The locals for this frame contain the special |
| 248 # bottom-of-the-stack marker so that any |
| 249 # exceptions that occur when processing this |
| 250 # SConscript can base the printed frames at this |
| 251 # level and not show SCons internals as well. |
| 252 call_stack[-1].globals.update({stack_bottom:1}) |
| 253 old_file = call_stack[-1].globals.get('__file__') |
| 254 try: |
| 255 del call_stack[-1].globals['__file__'] |
| 256 except KeyError: |
| 257 pass |
| 258 try: |
| 259 try: |
| 260 exec _file_ in call_stack[-1].globals |
| 261 except SConscriptReturn: |
| 262 pass |
| 263 finally: |
| 264 if old_file is not None: |
| 265 call_stack[-1].globals.update({__file__:old_file}) |
| 266 else: |
| 267 SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning, |
| 268 "Ignoring missing SConscript '%s'" % f.path) |
| 269 |
| 270 finally: |
| 271 SCons.Script.sconscript_reading = SCons.Script.sconscript_reading -
1 |
| 272 sys.path = old_sys_path |
| 273 frame = call_stack.pop() |
| 274 try: |
| 275 fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir) |
| 276 except OSError: |
| 277 # There was no local directory, so chdir to the |
| 278 # Repository directory. Like above, we do this |
| 279 # directly. |
| 280 fs.chdir(frame.prev_dir, change_os_dir=0) |
| 281 rdir = frame.prev_dir.rdir() |
| 282 rdir._create() # Make sure there's a directory there. |
| 283 try: |
| 284 os.chdir(rdir.get_abspath()) |
| 285 except OSError, e: |
| 286 # We still couldn't chdir there, so raise the error, |
| 287 # but only if actions are being executed. |
| 288 # |
| 289 # If the -n option was used, the directory would *not* |
| 290 # have been created and we should just carry on and |
| 291 # let things muddle through. This isn't guaranteed |
| 292 # to work if the SConscript files are reading things |
| 293 # from disk (for example), but it should work well |
| 294 # enough for most configurations. |
| 295 if SCons.Action.execute_actions: |
| 296 raise e |
| 297 |
| 298 results.append(frame.retval) |
| 299 |
| 300 # if we only have one script, don't return a tuple |
| 301 if len(results) == 1: |
| 302 return results[0] |
| 303 else: |
| 304 return tuple(results) |
| 305 |
| 306 def SConscript_exception(file=sys.stderr): |
| 307 """Print an exception stack trace just for the SConscript file(s). |
| 308 This will show users who have Python errors where the problem is, |
| 309 without cluttering the output with all of the internal calls leading |
| 310 up to where we exec the SConscript.""" |
| 311 exc_type, exc_value, exc_tb = sys.exc_info() |
| 312 tb = exc_tb |
| 313 while tb and stack_bottom not in tb.tb_frame.f_locals: |
| 314 tb = tb.tb_next |
| 315 if not tb: |
| 316 # We did not find our exec statement, so this was actually a bug |
| 317 # in SCons itself. Show the whole stack. |
| 318 tb = exc_tb |
| 319 stack = traceback.extract_tb(tb) |
| 320 try: |
| 321 type = exc_type.__name__ |
| 322 except AttributeError: |
| 323 type = str(exc_type) |
| 324 if type[:11] == "exceptions.": |
| 325 type = type[11:] |
| 326 file.write('%s: %s:\n' % (type, exc_value)) |
| 327 for fname, line, func, text in stack: |
| 328 file.write(' File "%s", line %d:\n' % (fname, line)) |
| 329 file.write(' %s\n' % text) |
| 330 |
| 331 def annotate(node): |
| 332 """Annotate a node with the stack frame describing the |
| 333 SConscript file and line number that created it.""" |
| 334 tb = sys.exc_info()[2] |
| 335 while tb and stack_bottom not in tb.tb_frame.f_locals: |
| 336 tb = tb.tb_next |
| 337 if not tb: |
| 338 # We did not find any exec of an SConscript file: what?! |
| 339 raise SCons.Errors.InternalError("could not find SConscript stack frame"
) |
| 340 node.creator = traceback.extract_stack(tb)[0] |
| 341 |
| 342 # The following line would cause each Node to be annotated using the |
| 343 # above function. Unfortunately, this is a *huge* performance hit, so |
| 344 # leave this disabled until we find a more efficient mechanism. |
| 345 #SCons.Node.Annotate = annotate |
| 346 |
| 347 class SConsEnvironment(SCons.Environment.Base): |
| 348 """An Environment subclass that contains all of the methods that |
| 349 are particular to the wrapper SCons interface and which aren't |
| 350 (or shouldn't be) part of the build engine itself. |
| 351 |
| 352 Note that not all of the methods of this class have corresponding |
| 353 global functions, there are some private methods. |
| 354 """ |
| 355 |
| 356 # |
| 357 # Private methods of an SConsEnvironment. |
| 358 # |
| 359 def _exceeds_version(self, major, minor, v_major, v_minor): |
| 360 """Return 1 if 'major' and 'minor' are greater than the version |
| 361 in 'v_major' and 'v_minor', and 0 otherwise.""" |
| 362 return (major > v_major or (major == v_major and minor > v_minor)) |
| 363 |
| 364 def _get_major_minor_revision(self, version_string): |
| 365 """Split a version string into major, minor and (optionally) |
| 366 revision parts. |
| 367 |
| 368 This is complicated by the fact that a version string can be |
| 369 something like 3.2b1.""" |
| 370 version = version_string.split(' ')[0].split('.') |
| 371 v_major = int(version[0]) |
| 372 v_minor = int(re.match('\d+', version[1]).group()) |
| 373 if len(version) >= 3: |
| 374 v_revision = int(re.match('\d+', version[2]).group()) |
| 375 else: |
| 376 v_revision = 0 |
| 377 return v_major, v_minor, v_revision |
| 378 |
| 379 def _get_SConscript_filenames(self, ls, kw): |
| 380 """ |
| 381 Convert the parameters passed to SConscript() calls into a list |
| 382 of files and export variables. If the parameters are invalid, |
| 383 throws SCons.Errors.UserError. Returns a tuple (l, e) where l |
| 384 is a list of SConscript filenames and e is a list of exports. |
| 385 """ |
| 386 exports = [] |
| 387 |
| 388 if len(ls) == 0: |
| 389 try: |
| 390 dirs = kw["dirs"] |
| 391 except KeyError: |
| 392 raise SCons.Errors.UserError("Invalid SConscript usage - no para
meters") |
| 393 |
| 394 if not SCons.Util.is_List(dirs): |
| 395 dirs = [ dirs ] |
| 396 dirs = list(map(str, dirs)) |
| 397 |
| 398 name = kw.get('name', 'SConscript') |
| 399 |
| 400 files = [os.path.join(n, name) for n in dirs] |
| 401 |
| 402 elif len(ls) == 1: |
| 403 |
| 404 files = ls[0] |
| 405 |
| 406 elif len(ls) == 2: |
| 407 |
| 408 files = ls[0] |
| 409 exports = self.Split(ls[1]) |
| 410 |
| 411 else: |
| 412 |
| 413 raise SCons.Errors.UserError("Invalid SConscript() usage - too many
arguments") |
| 414 |
| 415 if not SCons.Util.is_List(files): |
| 416 files = [ files ] |
| 417 |
| 418 if kw.get('exports'): |
| 419 exports.extend(self.Split(kw['exports'])) |
| 420 |
| 421 variant_dir = kw.get('variant_dir') or kw.get('build_dir') |
| 422 if variant_dir: |
| 423 if len(files) != 1: |
| 424 raise SCons.Errors.UserError("Invalid SConscript() usage - can o
nly specify one SConscript with a variant_dir") |
| 425 duplicate = kw.get('duplicate', 1) |
| 426 src_dir = kw.get('src_dir') |
| 427 if not src_dir: |
| 428 src_dir, fname = os.path.split(str(files[0])) |
| 429 files = [os.path.join(str(variant_dir), fname)] |
| 430 else: |
| 431 if not isinstance(src_dir, SCons.Node.Node): |
| 432 src_dir = self.fs.Dir(src_dir) |
| 433 fn = files[0] |
| 434 if not isinstance(fn, SCons.Node.Node): |
| 435 fn = self.fs.File(fn) |
| 436 if fn.is_under(src_dir): |
| 437 # Get path relative to the source directory. |
| 438 fname = fn.get_path(src_dir) |
| 439 files = [os.path.join(str(variant_dir), fname)] |
| 440 else: |
| 441 files = [fn.abspath] |
| 442 kw['src_dir'] = variant_dir |
| 443 self.fs.VariantDir(variant_dir, src_dir, duplicate) |
| 444 |
| 445 return (files, exports) |
| 446 |
| 447 # |
| 448 # Public methods of an SConsEnvironment. These get |
| 449 # entry points in the global name space so they can be called |
| 450 # as global functions. |
| 451 # |
| 452 |
| 453 def Configure(self, *args, **kw): |
| 454 if not SCons.Script.sconscript_reading: |
| 455 raise SCons.Errors.UserError("Calling Configure from Builders is not
supported.") |
| 456 kw['_depth'] = kw.get('_depth', 0) + 1 |
| 457 return SCons.Environment.Base.Configure(self, *args, **kw) |
| 458 |
| 459 def Default(self, *targets): |
| 460 SCons.Script._Set_Default_Targets(self, targets) |
| 461 |
| 462 def EnsureSConsVersion(self, major, minor, revision=0): |
| 463 """Exit abnormally if the SCons version is not late enough.""" |
| 464 scons_ver = self._get_major_minor_revision(SCons.__version__) |
| 465 if scons_ver < (major, minor, revision): |
| 466 if revision: |
| 467 scons_ver_string = '%d.%d.%d' % (major, minor, revision) |
| 468 else: |
| 469 scons_ver_string = '%d.%d' % (major, minor) |
| 470 print "SCons %s or greater required, but you have SCons %s" % \ |
| 471 (scons_ver_string, SCons.__version__) |
| 472 sys.exit(2) |
| 473 |
| 474 def EnsurePythonVersion(self, major, minor): |
| 475 """Exit abnormally if the Python version is not late enough.""" |
| 476 try: |
| 477 v_major, v_minor, v_micro, release, serial = sys.version_info |
| 478 python_ver = (v_major, v_minor) |
| 479 except AttributeError: |
| 480 python_ver = self._get_major_minor_revision(sys.version)[:2] |
| 481 if python_ver < (major, minor): |
| 482 v = sys.version.split(" ", 1)[0] |
| 483 print "Python %d.%d or greater required, but you have Python %s" %(m
ajor,minor,v) |
| 484 sys.exit(2) |
| 485 |
| 486 def Exit(self, value=0): |
| 487 sys.exit(value) |
| 488 |
| 489 def Export(self, *vars, **kw): |
| 490 for var in vars: |
| 491 global_exports.update(compute_exports(self.Split(var))) |
| 492 global_exports.update(kw) |
| 493 |
| 494 def GetLaunchDir(self): |
| 495 global launch_dir |
| 496 return launch_dir |
| 497 |
| 498 def GetOption(self, name): |
| 499 name = self.subst(name) |
| 500 return SCons.Script.Main.GetOption(name) |
| 501 |
| 502 def Help(self, text): |
| 503 text = self.subst(text, raw=1) |
| 504 SCons.Script.HelpFunction(text) |
| 505 |
| 506 def Import(self, *vars): |
| 507 try: |
| 508 frame = call_stack[-1] |
| 509 globals = frame.globals |
| 510 exports = frame.exports |
| 511 for var in vars: |
| 512 var = self.Split(var) |
| 513 for v in var: |
| 514 if v == '*': |
| 515 globals.update(global_exports) |
| 516 globals.update(exports) |
| 517 else: |
| 518 if v in exports: |
| 519 globals[v] = exports[v] |
| 520 else: |
| 521 globals[v] = global_exports[v] |
| 522 except KeyError,x: |
| 523 raise SCons.Errors.UserError("Import of non-existent variable '%s'"%
x) |
| 524 |
| 525 def SConscript(self, *ls, **kw): |
| 526 if 'build_dir' in kw: |
| 527 msg = """The build_dir keyword has been deprecated; use the variant_
dir keyword instead.""" |
| 528 SCons.Warnings.warn(SCons.Warnings.DeprecatedBuildDirWarning, msg) |
| 529 def subst_element(x, subst=self.subst): |
| 530 if SCons.Util.is_List(x): |
| 531 x = list(map(subst, x)) |
| 532 else: |
| 533 x = subst(x) |
| 534 return x |
| 535 ls = list(map(subst_element, ls)) |
| 536 subst_kw = {} |
| 537 for key, val in kw.items(): |
| 538 if SCons.Util.is_String(val): |
| 539 val = self.subst(val) |
| 540 elif SCons.Util.is_List(val): |
| 541 result = [] |
| 542 for v in val: |
| 543 if SCons.Util.is_String(v): |
| 544 v = self.subst(v) |
| 545 result.append(v) |
| 546 val = result |
| 547 subst_kw[key] = val |
| 548 |
| 549 files, exports = self._get_SConscript_filenames(ls, subst_kw) |
| 550 subst_kw['exports'] = exports |
| 551 return _SConscript(self.fs, *files, **subst_kw) |
| 552 |
| 553 def SConscriptChdir(self, flag): |
| 554 global sconscript_chdir |
| 555 sconscript_chdir = flag |
| 556 |
| 557 def SetOption(self, name, value): |
| 558 name = self.subst(name) |
| 559 SCons.Script.Main.SetOption(name, value) |
| 560 |
| 561 # |
| 562 # |
| 563 # |
| 564 SCons.Environment.Environment = SConsEnvironment |
| 565 |
| 566 def Configure(*args, **kw): |
| 567 if not SCons.Script.sconscript_reading: |
| 568 raise SCons.Errors.UserError("Calling Configure from Builders is not sup
ported.") |
| 569 kw['_depth'] = 1 |
| 570 return SCons.SConf.SConf(*args, **kw) |
| 571 |
| 572 # It's very important that the DefaultEnvironmentCall() class stay in this |
| 573 # file, with the get_calling_namespaces() function, the compute_exports() |
| 574 # function, the Frame class and the SConsEnvironment.Export() method. |
| 575 # These things make up the calling stack leading up to the actual global |
| 576 # Export() or SConscript() call that the user issued. We want to allow |
| 577 # users to export local variables that they define, like so: |
| 578 # |
| 579 # def func(): |
| 580 # x = 1 |
| 581 # Export('x') |
| 582 # |
| 583 # To support this, the get_calling_namespaces() function assumes that |
| 584 # the *first* stack frame that's not from this file is the local frame |
| 585 # for the Export() or SConscript() call. |
| 586 |
| 587 _DefaultEnvironmentProxy = None |
| 588 |
| 589 def get_DefaultEnvironmentProxy(): |
| 590 global _DefaultEnvironmentProxy |
| 591 if not _DefaultEnvironmentProxy: |
| 592 default_env = SCons.Defaults.DefaultEnvironment() |
| 593 _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default
_env) |
| 594 return _DefaultEnvironmentProxy |
| 595 |
| 596 class DefaultEnvironmentCall(object): |
| 597 """A class that implements "global function" calls of |
| 598 Environment methods by fetching the specified method from the |
| 599 DefaultEnvironment's class. Note that this uses an intermediate |
| 600 proxy class instead of calling the DefaultEnvironment method |
| 601 directly so that the proxy can override the subst() method and |
| 602 thereby prevent expansion of construction variables (since from |
| 603 the user's point of view this was called as a global function, |
| 604 with no associated construction environment).""" |
| 605 def __init__(self, method_name, subst=0): |
| 606 self.method_name = method_name |
| 607 if subst: |
| 608 self.factory = SCons.Defaults.DefaultEnvironment |
| 609 else: |
| 610 self.factory = get_DefaultEnvironmentProxy |
| 611 def __call__(self, *args, **kw): |
| 612 env = self.factory() |
| 613 method = getattr(env, self.method_name) |
| 614 return method(*args, **kw) |
| 615 |
| 616 |
| 617 def BuildDefaultGlobals(): |
| 618 """ |
| 619 Create a dictionary containing all the default globals for |
| 620 SConstruct and SConscript files. |
| 621 """ |
| 622 |
| 623 global GlobalDict |
| 624 if GlobalDict is None: |
| 625 GlobalDict = {} |
| 626 |
| 627 import SCons.Script |
| 628 d = SCons.Script.__dict__ |
| 629 def not_a_module(m, d=d, mtype=type(SCons.Script)): |
| 630 return not isinstance(d[m], mtype) |
| 631 for m in filter(not_a_module, dir(SCons.Script)): |
| 632 GlobalDict[m] = d[m] |
| 633 |
| 634 return GlobalDict.copy() |
| 635 |
| 636 # Local Variables: |
| 637 # tab-width:4 |
| 638 # indent-tabs-mode:nil |
| 639 # End: |
| 640 # vim: set expandtab tabstop=4 shiftwidth=4: |
OLD | NEW |