http://scons.tigris.org/issues/show_bug.cgi?id=2329
[scons.git] / src / engine / SCons / Script / SConscript.py
index 36a147dba69696441635328c610331eba8667986..c55d22038b3ebb3c514c5b4d6ee5c0884406b9a5 100644 (file)
@@ -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,7 +40,6 @@ 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.Main
@@ -49,10 +49,8 @@ import SCons.Util
 import os
 import os.path
 import re
-import string
 import sys
 import traceback
-import types
 import UserList
 
 # The following variables used to live in this module.  Some
@@ -82,7 +80,10 @@ def get_calling_namespaces():
     """Return the locals and globals for the function that called
     into this module in the current call stack."""
     try: 1/0
-    except ZeroDivisionError: frame = sys.exc_info()[2].tb_frame
+    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()
@@ -141,8 +142,9 @@ call_stack = []
 def Return(*vars, **kw):
     retval = []
     try:
-        for var in vars:
-            for v in string.split(var):
+        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-existent variable '%s'"%x
@@ -186,7 +188,11 @@ 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.rfile().get_abspath(), "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
@@ -230,8 +236,7 @@ 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)
-                        # TODO Not sure how to handle src_dir here
-                        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
@@ -276,7 +281,20 @@ def _SConscript(fs, *files, **kw):
                 fs.chdir(frame.prev_dir, change_os_dir=0)
                 rdir = frame.prev_dir.rdir()
                 rdir._create()  # Make sure there's a directory there.
-                os.chdir(rdir.get_abspath())
+                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)
 
@@ -293,7 +311,7 @@ def SConscript_exception(file=sys.stderr):
     up to where we exec the SConscript."""
     exc_type, exc_value, exc_tb = sys.exc_info()
     tb = exc_tb
-    while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
+    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
@@ -315,7 +333,7 @@ def annotate(node):
     """Annotate a node with the stack frame describing the
     SConscript file and line number that created it."""
     tb = sys.exc_info()[2]
-    while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
+    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?!
@@ -350,7 +368,7 @@ class SConsEnvironment(SCons.Environment.Base):
 
         This is complicated by the fact that a version string can be
         something like 3.2b1."""
-        version = string.split(string.split(version_string, ' ')[0], '.')
+        version = version_string.split(' ')[0].split('.')
         v_major = int(version[0])
         v_minor = int(re.match('\d+', version[1]).group())
         if len(version) >= 3:
@@ -377,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:
 
@@ -440,7 +458,7 @@ class SConsEnvironment(SCons.Environment.Base):
         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 apply(SCons.Environment.Base.Configure, (self,)+args, kw)
+        return SCons.Environment.Base.Configure(self, *args, **kw)
 
     def Default(self, *targets):
         SCons.Script._Set_Default_Targets(self, targets)
@@ -465,16 +483,17 @@ class SConsEnvironment(SCons.Environment.Base):
         except AttributeError:
             python_ver = self._get_major_minor_revision(sys.version)[:2]
         if python_ver < (major, minor):
-            v = string.split(sys.version, " ", 1)[0]
+            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
@@ -500,7 +519,7 @@ class SConsEnvironment(SCons.Environment.Base):
                         globals.update(global_exports)
                         globals.update(exports)
                     else:
-                        if exports.has_key(v):
+                        if v in exports:
                             globals[v] = exports[v]
                         else:
                             globals[v] = global_exports[v]
@@ -510,11 +529,11 @@ class SConsEnvironment(SCons.Environment.Base):
     def SConscript(self, *ls, **kw):
         def subst_element(x, subst=self.subst):
             if SCons.Util.is_List(x):
-                x = map(subst, x)
+                x = list(map(subst, x))
             else:
                 x = subst(x)
             return x
-        ls = map(subst_element, ls)
+        ls = list(map(subst_element, ls))
         subst_kw = {}
         for key, val in kw.items():
             if SCons.Util.is_String(val):
@@ -530,7 +549,7 @@ class SConsEnvironment(SCons.Environment.Base):
 
         files, exports = self._get_SConscript_filenames(ls, subst_kw)
         subst_kw['exports'] = exports
-        return apply(_SConscript, [self.fs,] + files, subst_kw)
+        return _SConscript(self.fs, *files, **subst_kw)
 
     def SConscriptChdir(self, flag):
         global sconscript_chdir
@@ -549,7 +568,7 @@ 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 apply(SCons.SConf.SConf, args, kw)
+    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()
@@ -593,7 +612,7 @@ class DefaultEnvironmentCall:
     def __call__(self, *args, **kw):
         env = self.factory()
         method = getattr(env, self.method_name)
-        return apply(method, args, kw)
+        return method(*args, **kw)
 
 
 def BuildDefaultGlobals():
@@ -609,8 +628,14 @@ def BuildDefaultGlobals():
         import SCons.Script
         d = SCons.Script.__dict__
         def not_a_module(m, d=d, mtype=type(SCons.Script)):
-             return type(d[m]) != mtype
+             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: