http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / Script / SConscript.py
index 92cd68dd8f55a5932914fb243395b798a20a1d4a..c55d22038b3ebb3c514c5b4d6ee5c0884406b9a5 100644 (file)
@@ -1,6 +1,6 @@
 """SCons.Script.SConscript
 
-This module defines the Python API provided to SConscript and SConstruct 
+This module defines the Python API provided to SConscript and SConstruct
 files.
 
 """
@@ -27,6 +27,7 @@ files.
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
+from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
@@ -39,51 +40,64 @@ import SCons.Errors
 import SCons.Node
 import SCons.Node.Alias
 import SCons.Node.FS
-import SCons.Options
 import SCons.Platform
 import SCons.SConf
-import SCons.Script
+import SCons.Script.Main
 import SCons.Tool
 import SCons.Util
 
 import os
 import os.path
 import re
-import string
 import sys
 import traceback
-import types
+import UserList
 
-def do_nothing(text): pass
-HelpFunction = do_nothing
+# The following variables used to live in this module.  Some
+# SConscript files out there may have referred to them directly as
+# SCons.Script.SConscript.*.  This is now supported by some special
+# handling towards the bottom of the SConscript.__init__.py module.
+#Arguments = {}
+#ArgList = []
+#BuildTargets = TargetList()
+#CommandLineTargets = []
+#DefaultTargets = []
+
+class SConscriptReturn(Exception):
+    pass
 
-arguments = {}
-GlobalDict = {}
 launch_dir = os.path.abspath(os.curdir)
 
+GlobalDict = None
+
 # global exports set by Export():
 global_exports = {}
 
 # chdir flag
 sconscript_chdir = 1
 
-def _scons_add_args(alist):
-    global arguments
-    for arg in alist:
-        a, b = string.split(arg, '=', 2)
-        arguments[a] = b
-
 def get_calling_namespaces():
     """Return the locals and globals for the function that called
-    into this module in the current callstack."""
+    into this module in the current call stack."""
     try: 1/0
-    except: frame = sys.exc_info()[2].tb_frame
-    
-    while frame.f_globals.get("__name__") == __name__: frame = frame.f_back
+    except ZeroDivisionError: 
+        # Don't start iterating with the current stack-frame to
+        # prevent creating reference cycles (f_back is safe).
+        frame = sys.exc_info()[2].tb_frame.f_back
+
+    # Find the first frame that *isn't* from this file.  This means
+    # that we expect all of the SCons frames that implement an Export()
+    # or SConscript() call to be in this file, so that we can identify
+    # the first non-Script.SConscript frame as the user's local calling
+    # environment, and the locals and globals dictionaries from that
+    # frame as the calling namespaces.  See the comment below preceding
+    # the DefaultEnvironmentCall block for even more explanation.
+    while frame.f_globals.get("__name__") == __name__:
+        frame = frame.f_back
 
     return frame.f_locals, frame.f_globals
 
-    
+
 def compute_exports(exports):
     """Compute a dictionary of exports given one of the parameters
     to the Export() function or the exports argument to SConscript()."""
@@ -101,42 +115,52 @@ def compute_exports(exports):
                 except KeyError:
                     retval[export] = glob[export]
     except KeyError, x:
-        raise SCons.Errors.UserError, "Export of non-existant variable '%s'"%x
+        raise SCons.Errors.UserError, "Export of non-existent variable '%s'"%x
 
     return retval
-    
 
 class Frame:
     """A frame on the SConstruct/SConscript call stack"""
-    def __init__(self, exports, sconscript):
+    def __init__(self, fs, exports, sconscript):
         self.globals = BuildDefaultGlobals()
-        self.retval = None 
-        self.prev_dir = SCons.Node.FS.default_fs.getcwd()
+        self.retval = None
+        self.prev_dir = fs.getcwd()
         self.exports = compute_exports(exports)  # exports from the calling SConscript
         # make sure the sconscript attr is a Node.
         if isinstance(sconscript, SCons.Node.Node):
             self.sconscript = sconscript
+        elif sconscript == '-':
+            self.sconscript = None
         else:
-            self.sconscript = SCons.Node.FS.default_fs.File(str(sconscript))
-        
+            self.sconscript = fs.File(str(sconscript))
+
 # the SConstruct/SConscript call stack:
-stack = []
+call_stack = []
 
 # For documentation on the methods in this file, see the scons man-page
 
-def Return(*vars):
+def Return(*vars, **kw):
     retval = []
     try:
-        for var in vars:
-            for v in string.split(var):
-                retval.append(stack[-1].globals[v])
+        fvars = SCons.Util.flatten(vars)
+        for var in fvars:
+            for v in var.split():
+                retval.append(call_stack[-1].globals[v])
     except KeyError, x:
-        raise SCons.Errors.UserError, "Return of non-existant variable '%s'"%x
-        
+        raise SCons.Errors.UserError, "Return of non-existent variable '%s'"%x
+
     if len(retval) == 1:
-        stack[-1].retval = retval[0]
+        call_stack[-1].retval = retval[0]
     else:
-        stack[-1].retval = tuple(retval)
+        call_stack[-1].retval = tuple(retval)
+
+    stop = kw.get('stop', True)
+
+    if stop:
+        raise SConscriptReturn
+
+
+stack_bottom = '% Stack boTTom %' # hard to define a variable w/this name :)
 
 def _SConscript(fs, *files, **kw):
     top = fs.Top
@@ -146,11 +170,12 @@ def _SConscript(fs, *files, **kw):
     # evaluate each SConscript file
     results = []
     for fn in files:
-        stack.append(Frame(exports,fn))
+        call_stack.append(Frame(fs, exports, fn))
         old_sys_path = sys.path
         try:
+            SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1
             if fn == "-":
-                exec sys.stdin in stack[-1].globals
+                exec sys.stdin in call_stack[-1].globals
             else:
                 if isinstance(fn, SCons.Node.Node):
                     f = fn
@@ -163,17 +188,21 @@ def _SConscript(fs, *files, **kw):
                 # fs match so we can open the SConscript.
                 fs.chdir(top, change_os_dir=1)
                 if f.rexists():
-                    _file_ = open(f.rstr(), "r")
+                    actual = f.rfile()
+                    _file_ = open(actual.get_abspath(), "r")
+                elif f.srcnode().rexists():
+                    actual = f.srcnode().rfile()
+                    _file_ = open(actual.get_abspath(), "r")
                 elif f.has_src_builder():
                     # The SConscript file apparently exists in a source
                     # code management system.  Build it, but then clear
                     # the builder so that it doesn't get built *again*
                     # during the actual build phase.
                     f.build()
+                    f.built()
                     f.builder_set(None)
-                    s = str(f)
-                    if os.path.exists(s):
-                        _file_ = open(s, "r")
+                    if f.exists():
+                        _file_ = open(f.get_abspath(), "r")
                 if _file_:
                     # Chdir to the SConscript directory.  Use a path
                     # name relative to the SConstruct file so that if
@@ -185,7 +214,18 @@ def _SConscript(fs, *files, **kw):
                     # where the SConstruct and SConscript files might be
                     # in different Repositories.  For now, cross that
                     # bridge when someone comes to it.
-                    ldir = fs.Dir(f.dir.get_path(sd))
+                    try:
+                        src_dir = kw['src_dir']
+                    except KeyError:
+                        ldir = fs.Dir(f.dir.get_path(sd))
+                    else:
+                        ldir = fs.Dir(src_dir)
+                        if not ldir.is_under(f.dir):
+                            # They specified a source directory, but
+                            # it's above the SConscript directory.
+                            # Do the sensible thing and just use the
+                            # SConcript directory.
+                            ldir = fs.Dir(f.dir.get_path(sd))
                     try:
                         fs.chdir(ldir, change_os_dir=sconscript_chdir)
                     except OSError:
@@ -196,27 +236,42 @@ def _SConscript(fs, *files, **kw):
                         # interpret the stuff within the SConscript file
                         # relative to where we are logically.
                         fs.chdir(ldir, change_os_dir=0)
-                        os.chdir(f.rfile().dir.get_abspath())
+                        os.chdir(actual.dir.get_abspath())
 
                     # Append the SConscript directory to the beginning
                     # of sys.path so Python modules in the SConscript
                     # directory can be easily imported.
                     sys.path = [ f.dir.get_abspath() ] + sys.path
 
-                    # This is the magic line that actually reads up and
-                    # executes the stuff in the SConscript file.  We
-                    # look for the "exec _file_ " from the beginning
-                    # of this line to find the right stack frame (the
-                    # next one) describing the SConscript file and line
-                    # number that creates a node.
-                    exec _file_ in stack[-1].globals
+                    # This is the magic line that actually reads up
+                    # and executes the stuff in the SConscript file.
+                    # The locals for this frame contain the special
+                    # bottom-of-the-stack marker so that any
+                    # exceptions that occur when processing this
+                    # SConscript can base the printed frames at this
+                    # level and not show SCons internals as well.
+                    call_stack[-1].globals.update({stack_bottom:1})
+                    old_file = call_stack[-1].globals.get('__file__')
+                    try:
+                        del call_stack[-1].globals['__file__']
+                    except KeyError:
+                        pass
+                    try:
+                        try:
+                            exec _file_ in call_stack[-1].globals
+                        except SConscriptReturn:
+                            pass
+                    finally:
+                        if old_file is not None:
+                            call_stack[-1].globals.update({__file__:old_file})
                 else:
-                    sys.stderr.write("Ignoring missing SConscript '%s'\n" %
-                                     f.path)
-                
+                    SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
+                             "Ignoring missing SConscript '%s'" % f.path)
+
         finally:
+            SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1
             sys.path = old_sys_path
-            frame = stack.pop()
+            frame = call_stack.pop()
             try:
                 fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir)
             except OSError:
@@ -224,7 +279,22 @@ def _SConscript(fs, *files, **kw):
                 # Repository directory.  Like above, we do this
                 # directly.
                 fs.chdir(frame.prev_dir, change_os_dir=0)
-                os.chdir(frame.prev_dir.rdir().get_abspath())
+                rdir = frame.prev_dir.rdir()
+                rdir._create()  # Make sure there's a directory there.
+                try:
+                    os.chdir(rdir.get_abspath())
+                except OSError, e:
+                    # We still couldn't chdir there, so raise the error,
+                    # but only if actions are being executed.
+                    #
+                    # If the -n option was used, the directory would *not*
+                    # have been created and we should just carry on and
+                    # let things muddle through.  This isn't guaranteed
+                    # to work if the SConscript files are reading things
+                    # from disk (for example), but it should work well
+                    # enough for most configurations.
+                    if SCons.Action.execute_actions:
+                        raise e
 
             results.append(frame.retval)
 
@@ -234,44 +304,41 @@ def _SConscript(fs, *files, **kw):
     else:
         return tuple(results)
 
-def is_our_exec_statement(line):
-    return not line is None and line[:12] == "exec _file_ "
-
 def SConscript_exception(file=sys.stderr):
     """Print an exception stack trace just for the SConscript file(s).
     This will show users who have Python errors where the problem is,
     without cluttering the output with all of the internal calls leading
     up to where we exec the SConscript."""
-    stack = traceback.extract_tb(sys.exc_traceback)
-    last_text = ""
-    i = 0
-    for frame in stack:
-        if is_our_exec_statement(last_text):
-            break
-        i = i + 1
-        last_text = frame[3]
-    type = str(sys.exc_type)
-    if type[:11] == "exceptions.":
-        type = type[11:]
-    file.write('%s: %s:\n' % (type, sys.exc_value))
-    for fname, line, func, text in stack[i:]:
+    exc_type, exc_value, exc_tb = sys.exc_info()
+    tb = exc_tb
+    while tb and stack_bottom not in tb.tb_frame.f_locals:
+        tb = tb.tb_next
+    if not tb:
+        # We did not find our exec statement, so this was actually a bug
+        # in SCons itself.  Show the whole stack.
+        tb = exc_tb
+    stack = traceback.extract_tb(tb)
+    try:
+        type = exc_type.__name__
+    except AttributeError:
+        type = str(exc_type)
+        if type[:11] == "exceptions.":
+            type = type[11:]
+    file.write('%s: %s:\n' % (type, exc_value))
+    for fname, line, func, text in stack:
         file.write('  File "%s", line %d:\n' % (fname, line))
         file.write('    %s\n' % text)
 
 def annotate(node):
     """Annotate a node with the stack frame describing the
     SConscript file and line number that created it."""
-    stack = traceback.extract_stack()
-    last_text = ""
-    for frame in stack:
-        # If the script text of the previous frame begins with the
-        # magic "exec _file_ " string, then this frame describes the
-        # SConscript file and line number that caused this node to be
-        # created.  Record the tuple and carry on.
-        if is_our_exec_statement(last_text):
-            node.creator = frame
-            return
-        last_text = frame[3]
+    tb = sys.exc_info()[2]
+    while tb and stack_bottom not in tb.tb_frame.f_locals:
+        tb = tb.tb_next
+    if not tb:
+        # We did not find any exec of an SConscript file: what?!
+        raise SCons.Errors.InternalError, "could not find SConscript stack frame"
+    node.creator = traceback.extract_stack(tb)[0]
 
 # The following line would cause each Node to be annotated using the
 # above function.  Unfortunately, this is a *huge* performance hit, so
@@ -290,20 +357,25 @@ class SConsEnvironment(SCons.Environment.Base):
     #
     # Private methods of an SConsEnvironment.
     #
-
-    def _check_version(self, major, minor, version_string):
-        """Return 0 if 'major' and 'minor' are greater than the version
-        in 'version_string', and 1 otherwise."""
-        try:
-            v_major, v_minor, v_micro, release, serial = sys.version_info
-        except AttributeError:
-            version = string.split(string.split(version_string, ' ')[0], '.')
-            v_major = int(version[0])
-            v_minor = int(re.match('\d+', version[1]).group())
-        if major > v_major or (major == v_major and minor > v_minor):
-            return 0
+    def _exceeds_version(self, major, minor, v_major, v_minor):
+        """Return 1 if 'major' and 'minor' are greater than the version
+        in 'v_major' and 'v_minor', and 0 otherwise."""
+        return (major > v_major or (major == v_major and minor > v_minor))
+
+    def _get_major_minor_revision(self, version_string):
+        """Split a version string into major, minor and (optionally)
+        revision parts.
+
+        This is complicated by the fact that a version string can be
+        something like 3.2b1."""
+        version = version_string.split(' ')[0].split('.')
+        v_major = int(version[0])
+        v_minor = int(re.match('\d+', version[1]).group())
+        if len(version) >= 3:
+            v_revision = int(re.match('\d+', version[2]).group())
         else:
-            return 1
+            v_revision = 0
+        return v_major, v_minor, v_revision
 
     def _get_SConscript_filenames(self, ls, kw):
         """
@@ -323,11 +395,11 @@ class SConsEnvironment(SCons.Environment.Base):
 
             if not SCons.Util.is_List(dirs):
                 dirs = [ dirs ]
-            dirs = map(str, dirs)
+            dirs = list(map(str, dirs))
 
             name = kw.get('name', 'SConscript')
 
-            files = map(lambda n, name = name: os.path.join(n, name), dirs)
+            files = [os.path.join(n, name) for n in dirs]
 
         elif len(ls) == 1:
 
@@ -349,15 +421,16 @@ class SConsEnvironment(SCons.Environment.Base):
         if kw.get('exports'):
             exports.extend(self.Split(kw['exports']))
 
-        build_dir = kw.get('build_dir')
-        if build_dir:
+        variant_dir = kw.get('variant_dir') or kw.get('build_dir')
+        if variant_dir:
             if len(files) != 1:
                 raise SCons.Errors.UserError, \
-                    "Invalid SConscript() usage - can only specify one SConscript with a build_dir"
+                    "Invalid SConscript() usage - can only specify one SConscript with a variant_dir"
             duplicate = kw.get('duplicate', 1)
             src_dir = kw.get('src_dir')
             if not src_dir:
                 src_dir, fname = os.path.split(str(files[0]))
+                files = [os.path.join(str(variant_dir), fname)]
             else:
                 if not isinstance(src_dir, SCons.Node.Node):
                     src_dir = self.fs.Dir(src_dir)
@@ -367,11 +440,11 @@ class SConsEnvironment(SCons.Environment.Base):
                 if fn.is_under(src_dir):
                     # Get path relative to the source directory.
                     fname = fn.get_path(src_dir)
+                    files = [os.path.join(str(variant_dir), fname)]
                 else:
-                    # Fast way to only get the terminal path component of a Node.
-                    fname = fn.get_path(fn.dir)
-            self.fs.BuildDir(build_dir, src_dir, duplicate)
-            files = [os.path.join(str(build_dir), fname)]
+                    files = [fn.abspath]
+                kw['src_dir'] = variant_dir
+            self.fs.VariantDir(variant_dir, src_dir, duplicate)
 
         return (files, exports)
 
@@ -381,25 +454,46 @@ class SConsEnvironment(SCons.Environment.Base):
     # as global functions.
     #
 
-    def EnsureSConsVersion(self, major, minor):
+    def Configure(self, *args, **kw):
+        if not SCons.Script.sconscript_reading:
+            raise SCons.Errors.UserError, "Calling Configure from Builders is not supported."
+        kw['_depth'] = kw.get('_depth', 0) + 1
+        return SCons.Environment.Base.Configure(self, *args, **kw)
+
+    def Default(self, *targets):
+        SCons.Script._Set_Default_Targets(self, targets)
+
+    def EnsureSConsVersion(self, major, minor, revision=0):
         """Exit abnormally if the SCons version is not late enough."""
-        if not self._check_version(major,minor,SCons.__version__):
-            print "SCons %d.%d or greater required, but you have SCons %s" %(major,minor,SCons.__version__)
+        scons_ver = self._get_major_minor_revision(SCons.__version__)
+        if scons_ver < (major, minor, revision):
+            if revision:
+                scons_ver_string = '%d.%d.%d' % (major, minor, revision)
+            else:
+                scons_ver_string = '%d.%d' % (major, minor)
+            print "SCons %s or greater required, but you have SCons %s" % \
+                  (scons_ver_string, SCons.__version__)
             sys.exit(2)
 
     def EnsurePythonVersion(self, major, minor):
         """Exit abnormally if the Python version is not late enough."""
-        if not self._check_version(major,minor,sys.version):
-            v = string.split(sys.version, " ", 1)[0]
+        try:
+            v_major, v_minor, v_micro, release, serial = sys.version_info
+            python_ver = (v_major, v_minor)
+        except AttributeError:
+            python_ver = self._get_major_minor_revision(sys.version)[:2]
+        if python_ver < (major, minor):
+            v = sys.version.split(" ", 1)[0]
             print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v)
             sys.exit(2)
 
     def Exit(self, value=0):
         sys.exit(value)
 
-    def Export(self, *vars):
+    def Export(self, *vars, **kw):
         for var in vars:
             global_exports.update(compute_exports(self.Split(var)))
+        global_exports.update(kw)
 
     def GetLaunchDir(self):
         global launch_dir
@@ -407,39 +501,55 @@ class SConsEnvironment(SCons.Environment.Base):
 
     def GetOption(self, name):
         name = self.subst(name)
-        return SCons.Script.ssoptions.get(name)
+        return SCons.Script.Main.GetOption(name)
 
     def Help(self, text):
         text = self.subst(text, raw=1)
-        HelpFunction(text)
+        SCons.Script.HelpFunction(text)
 
     def Import(self, *vars):
         try:
+            frame = call_stack[-1]
+            globals = frame.globals
+            exports = frame.exports
             for var in vars:
                 var = self.Split(var)
                 for v in var:
                     if v == '*':
-                        stack[-1].globals.update(global_exports)
-                        stack[-1].globals.update(stack[-1].exports)
+                        globals.update(global_exports)
+                        globals.update(exports)
                     else:
-                        if stack[-1].exports.has_key(v):
-                            stack[-1].globals[v] = stack[-1].exports[v]
+                        if v in exports:
+                            globals[v] = exports[v]
                         else:
-                            stack[-1].globals[v] = global_exports[v]
+                            globals[v] = global_exports[v]
         except KeyError,x:
-            raise SCons.Errors.UserError, "Import of non-existant variable '%s'"%x
+            raise SCons.Errors.UserError, "Import of non-existent variable '%s'"%x
 
     def SConscript(self, *ls, **kw):
-        ls = map(lambda l, self=self: self.subst(l), ls)
+        def subst_element(x, subst=self.subst):
+            if SCons.Util.is_List(x):
+                x = list(map(subst, x))
+            else:
+                x = subst(x)
+            return x
+        ls = list(map(subst_element, ls))
         subst_kw = {}
         for key, val in kw.items():
             if SCons.Util.is_String(val):
                 val = self.subst(val)
+            elif SCons.Util.is_List(val):
+                result = []
+                for v in val:
+                    if SCons.Util.is_String(v):
+                        v = self.subst(v)
+                    result.append(v)
+                val = result
             subst_kw[key] = val
 
         files, exports = self._get_SConscript_filenames(ls, subst_kw)
-
-        return apply(_SConscript, [self.fs,] + files, {'exports' : exports})
+        subst_kw['exports'] = exports
+        return _SConscript(self.fs, *files, **subst_kw)
 
     def SConscriptChdir(self, flag):
         global sconscript_chdir
@@ -447,75 +557,41 @@ class SConsEnvironment(SCons.Environment.Base):
 
     def SetOption(self, name, value):
         name = self.subst(name)
-        SCons.Script.ssoptions.set(name, value)
+        SCons.Script.Main.SetOption(name, value)
 
 #
 #
 #
 SCons.Environment.Environment = SConsEnvironment
 
-def Options(files=None, args=arguments):
-    return SCons.Options.Options(files, args)
-
-def SetBuildSignatureType(type):
-    SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
-                        "The SetBuildSignatureType() function has been deprecated;\n" +\
-                        "\tuse the TargetSignatures() function instead.")
-    SCons.Defaults.DefaultEnvironment().TargetSignatures(type)
-
-def SetContentSignatureType(type):
-    SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
-                        "The SetContentSignatureType() function has been deprecated;\n" +\
-                        "\tuse the SourceSignatures() function instead.")
-    SCons.Defaults.DefaultEnvironment().SourceSignatures(type)
-
-def GetJobs():
-    SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
-                        "The GetJobs() function has been deprecated;\n" +\
-                        "\tuse GetOption('num_jobs') instead.")
-
-    return GetOption('num_jobs')
-def SetJobs(num):
-    SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
-                        "The SetJobs() function has been deprecated;\n" +\
-                        "\tuse SetOption('num_jobs', num) instead.")
-    SetOption('num_jobs', num)
-def ParseConfig(env, command, function=None):
-    SCons.Warnings.warn(SCons.Warnings.DeprecatedWarning,
-                        "The ParseConfig() function has been deprecated;\n" +\
-                        "\tuse the env.ParseConfig() method instead.")
-    return env.ParseConfig(command, function)
-
+def Configure(*args, **kw):
+    if not SCons.Script.sconscript_reading:
+        raise SCons.Errors.UserError, "Calling Configure from Builders is not supported."
+    kw['_depth'] = 1
+    return SCons.SConf.SConf(*args, **kw)
+
+# It's very important that the DefaultEnvironmentCall() class stay in this
+# file, with the get_calling_namespaces() function, the compute_exports()
+# function, the Frame class and the SConsEnvironment.Export() method.
+# These things make up the calling stack leading up to the actual global
+# Export() or SConscript() call that the user issued.  We want to allow
+# users to export local variables that they define, like so:
 #
+#       def func():
+#           x = 1
+#           Export('x')
+#
+# To support this, the get_calling_namespaces() function assumes that
+# the *first* stack frame that's not from this file is the local frame
+# for the Export() or SConscript() call.
+
 _DefaultEnvironmentProxy = None
 
 def get_DefaultEnvironmentProxy():
     global _DefaultEnvironmentProxy
     if not _DefaultEnvironmentProxy:
-        class EnvironmentProxy(SCons.Environment.Environment):
-            """A proxy subclass for an environment instance that overrides
-            the subst() and subst_list() methods so they don't actually
-            actually perform construction variable substitution.  This is
-            specifically intended to be the shim layer in between global
-            function calls (which don't want want construction variable
-            substitution) and the DefaultEnvironment() (which would
-            substitute variables if left to its own devices)."""
-            def __init__(self, subject):
-                self.__dict__['__subject'] = subject
-            def __getattr__(self, name):
-                return getattr(self.__dict__['__subject'], name)
-            def __setattr__(self, name, value):
-                return setattr(self.__dict__['__subject'], name, value)
-            def subst(self, string, raw=0, target=None, source=None):
-                return string
-            def subst_kw(self, kw, raw=0, target=None, source=None):
-                return kw
-            def subst_list(self, string, raw=0, target=None, source=None):
-                return string
         default_env = SCons.Defaults.DefaultEnvironment()
-        _DefaultEnvironmentProxy = EnvironmentProxy(default_env)
+        _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env)
     return _DefaultEnvironmentProxy
 
 class DefaultEnvironmentCall:
@@ -527,121 +603,39 @@ class DefaultEnvironmentCall:
     thereby prevent expansion of construction variables (since from
     the user's point of view this was called as a global function,
     with no associated construction environment)."""
-    def __init__(self, method_name):
+    def __init__(self, method_name, subst=0):
         self.method_name = method_name
+        if subst:
+            self.factory = SCons.Defaults.DefaultEnvironment
+        else:
+            self.factory = get_DefaultEnvironmentProxy
     def __call__(self, *args, **kw):
-        proxy = get_DefaultEnvironmentProxy()
-        method = getattr(proxy, self.method_name)
-        return apply(method, args, kw)
-
-# The list of global functions to add to the SConscript name space
-# that end up calling corresponding methods or Builders in the
-# DefaultEnvironment().
-GlobalDefaultEnvironmentFunctions = [
-    # Methods from the SConsEnvironment class, above.
-    'EnsurePythonVersion',
-    'EnsureSConsVersion',
-    'Exit',
-    'Export',
-    'GetLaunchDir',
-    'GetOption',
-    'Help',
-    'Import',
-    'SConscript',
-    'SConscriptChdir',
-    'SetOption',
-
-    # Methods from the Environment.Base class.
-    'Action',
-    'AddPostAction',
-    'AddPreAction',
-    'Alias',
-    'AlwaysBuild',
-    'BuildDir',
-    'Builder',
-    'CacheDir',
-    'Clean',
-    'Command',
-    'Default',
-    'Depends',
-    'Dir',
-    'Environment',
-    'File',
-    'FindFile',
-    'GetBuildPath',
-    'Ignore',
-    'Install',
-    'InstallAs',
-    'Literal',
-    'Local',
-    'Precious',
-    'Repository',
-    'Scanner',
-    'SConsignFile',
-    'SideEffect',
-    'SourceCode',
-    'SourceSignatures',
-    'Split',
-    'TargetSignatures',
-    'Value',
-]
-
-GlobalDefaultBuilders = [
-    # Supported builders.
-    'CFile',
-    'CXXFile',
-    'DVI',
-    'Jar',
-    'Java',
-    'JavaH',
-    'Library',
-    'M4',
-    'MSVSProject',
-    'Object',
-    'PCH',
-    'PDF',
-    'PostScript',
-    'Program',
-    'RES',
-    'RMIC',
-    'SharedLibrary',
-    'SharedObject',
-    'StaticLibrary',
-    'StaticObject',
-    'Tar',
-    'TypeLibrary',
-    'Zip',
-]
-
-for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
-    GlobalDict[name] = DefaultEnvironmentCall(name)
+        env = self.factory()
+        method = getattr(env, self.method_name)
+        return method(*args, **kw)
+
 
 def BuildDefaultGlobals():
     """
-    Create a dictionary containing all the default globals for 
+    Create a dictionary containing all the default globals for
     SConstruct and SConscript files.
     """
 
-    globals = {}
-    globals['ARGUMENTS']         = arguments
-    globals['Configure']         = SCons.SConf.SConf
-    globals['Options']           = Options
-    globals['Platform']          = SCons.Platform.Platform
-    globals['Return']            = Return
-    globals['Tool']              = SCons.Tool.Tool
-    globals['WhereIs']           = SCons.Util.WhereIs
-
-    # Functions we're in the process of converting to Environment methods.
-    globals['CScan']             = SCons.Defaults.CScan
-    globals['DefaultEnvironment'] = SCons.Defaults.DefaultEnvironment
-
-    # Deprecated functions, leave these here for now.
-    globals['GetJobs']           = GetJobs
-    globals['ParseConfig']       = ParseConfig
-    globals['SetBuildSignatureType'] = SetBuildSignatureType
-    globals['SetContentSignatureType'] = SetContentSignatureType
-    globals['SetJobs']           = SetJobs
-
-    globals.update(GlobalDict)
-
-    return globals
+    global GlobalDict
+    if GlobalDict is None:
+        GlobalDict = {}
+
+        import SCons.Script
+        d = SCons.Script.__dict__
+        def not_a_module(m, d=d, mtype=type(SCons.Script)):
+             return not isinstance(d[m], mtype)
+        for m in filter(not_a_module, dir(SCons.Script)):
+             GlobalDict[m] = d[m]
+
+    return GlobalDict.copy()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: