Make the shell pickable via a construction variable. (Anthony Roach)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 25 Nov 2002 23:33:49 +0000 (23:33 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Mon, 25 Nov 2002 23:33:49 +0000 (23:33 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@506 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/RELEASE.txt
src/engine/SCons/Action.py
src/engine/SCons/ActionTests.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Platform/PlatformTests.py
src/engine/SCons/Platform/cygwin.py
src/engine/SCons/Platform/posix.py
src/engine/SCons/Platform/win32.py
test/Platform.py

index f75b58d6a3b415e18ce7cd29ecb931f008cce07f..e1f0ed10a4a84764fb869dc246d48b4a83149e90 100644 (file)
@@ -1656,6 +1656,11 @@ import os
 env = Environment(ENV = {'PATH' : os.environ['PATH']})
 .EE
 
+.IP ESCAPE
+A function that will be called to escape shell special characters in
+command lines. The function should take one argument: the command line
+string to escape; and should return the escaped command line.
+
 .IP F77
 The Fortran compiler.
 
@@ -2010,6 +2015,14 @@ are included on this command line.
 Options that are passed to the C++ compiler
 to generate shared-library objects.
 
+.IP SHELL
+A string naming the shell program that will be passed to the 
+.I SPAWN 
+function. 
+See the 
+.I SPAWN 
+construction variable for more information.
+
 .IP SHF77
 The Fortran compiler used for generating shared-library objects.
 
@@ -2046,6 +2059,27 @@ The prefix used for shared object file names.
 .IP SHOBJSUFFIX 
 The suffix used for shared object file names.
 
+.IP SPAWN
+A command interpreter function that will be called to execute command line
+strings. The function must expect 4 arguments:
+
+.ES
+def spawn(shell, escape, cmd, args, env):
+.EE
+.IP
+.I sh
+is a string naming the shell program to use.
+.I escape
+is a function that can be called to escape shell special characters in
+the command line. 
+.I cmd
+is the path to the command to be executed.
+.I args
+is that arguments to the command.
+.I env
+is a dictionary of the environment variables
+in which the command should be executed.
+
 .IP TAR
 The tar archiver.
 
@@ -2347,10 +2381,6 @@ foo = FindFile('foo', ['dir1', 'dir2'])
 .\"XXX
 .\"
 .\".TP
-.\".RI GetCommandHandler( XXX )
-.\"XXX
-.\"
-.\".TP
 .\".RI GetLaunchDir( XXX )
 .\"XXX
 
@@ -2559,27 +2589,6 @@ the derived files content signature as its signature. "build" signatures
 are usually faster to compute, but "content" signatures can prevent
 redundant rebuilds. The default is "build".
 
-.TP 
-.RI SetCommandHandler( function )
-
-This registers a user
-.I function
-as the handler
-for interpreting and executing command-line strings.
-The function must expect three arguments:
-
-.ES
-def commandhandler(cmd, args, env):
-.EE
-.IP
-.I cmd
-is the path to the command to be executed.
-.I args
-is that arguments to the command.
-.I env
-is a dictionary of the environment variables
-in which the command should be executed.
-
 .TP
 .RI SetContentSignatureType( type )
 
index 0c6325fed9d422e6a24d2f38f533eb0f5bb6af6d..825d65745276da4851f4ecc7154f9f766004bf5d 100644 (file)
@@ -122,6 +122,9 @@ RELEASE 0.09 -
   - Allow the File() and Dir() methods to take a path-name string as
     the starting directory, in addition to a Dir object.
 
+  - Allow the command handler to be selected via the SPAWN, SHELL
+    and ESCAPE construction variables.
+
   From sam th:
 
   - Dynamically check for the existence of utilities with which to
index 0dab6ce6f9f7d482d9ece2d9a20281a66dbdf64d..5f8c89a2dcfc3a80f24f3f3c5dc3bb0a5c50e5ed 100644 (file)
@@ -25,6 +25,13 @@ RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
   This is the eighth alpha release of SCons.  Please consult the
   CHANGES.txt file for a list of specific changes since last release.
 
+  Please note the following important changes since release 0.08:
+
+    - The SetCommandHandler() function has been superceded
+      by the SPAWN, SHELL and ESCAPE construction variables.
+
+      XXX
+
   Please note the following important changes since release 0.07:
 
     - Builder objects no longer automatically split target and source
@@ -66,35 +73,6 @@ RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
     - The prefix, suffix, and src_suffix keyword arguments to the
       Builder() function may no longer be callable functions.
 
-  Please note the following important changes since release 0.06:
-
-    - The functionality of the -U option has changed.  It now works
-      exactly like the -u option (searches up the directory tree for an
-      SConstruct file) but, when no targets are specified on the command
-      line, it will build all targets that are defined in any SConscript
-      files in the current directory.
-
-      The previous functionality of this option is now available in the
-      -D option:  when no targets are specified on the command line,
-      SCons will build *all* Default() targets, not just those at or
-      below the current directory,
-
-    - The default Fortran compilation command on Windows systems now
-      uses Windows conventions (/Fo) instead of UNIX conventions (-o).
-
-    - The $SOURCE construction variable is now a synonym for
-      ${SOURCES[0]}.  This will affect you if you previously set $SOURCE
-      explicitly in a construction environment.
-
-    - Scanner functions now take three or four arguments.  The target
-      Node is now passed in as the third argument; the fourth argument
-      is an optional SCons.Node.FS.FS object.  You will need to update
-      the interfaces of any local Scanner functions you have defined.
-
-    - Command generator functions now take a fourth argument,
-      for_signature.  You will need to add this argument to any
-      generator functions you have defined.
-
   Owing to an extensive test suite, the SCons team believes that this
   release is of sufficient quality that you can use it for real work,
   despite the "alpha" label.
@@ -160,11 +138,6 @@ RELEASE 0.08 - Mon, 15 Jul 2002 12:08:51 -0500
       site is currently out of date.  Take what you read there with a
       grain of salt.
 
-    - SCons does not yet support file names with quotes (" or ') in the
-      file name when an external command is used to create or process a
-      file.  Results will be unpredictable based on the interaction with
-      the shell used to execute the external command.
-
     - If a file is specified to be built in multiple ways, the last
       processed builder specification overwrites all other builders,
       without any warning.
index 8347195c35060933ca7e2479c6a8f6a7c1717ff4..534b813f7b226350e84cc21c62072453ffcb0b42 100644 (file)
@@ -40,6 +40,7 @@ import UserDict
 import SCons.Util
 import SCons.Errors
 
+
 print_actions = 1;
 execute_actions = 1;
 
@@ -56,168 +57,11 @@ def rfile(n):
     except AttributeError:
         return n
 
-if os.name == 'posix':
-
-    def defaultEscape(arg):
-        "escape shell special characters"
-        slash = '\\'
-        special = '"$'
-
-        arg = string.replace(arg, slash, slash+slash)
-        for c in special:
-            arg = string.replace(arg, c, slash+c)
-
-        return '"' + arg + '"'
-
-    # If the env command exists, then we can use os.system()
-    # to spawn commands, otherwise we fall back on os.fork()/os.exec().
-    # os.system() is prefered because it seems to work better with
-    # threads (i.e. -j) and is more efficient than forking Python.
-    if SCons.Util.WhereIs('env'):
-        def defaultSpawn(cmd, args, env):
-            if env:
-                s = 'env -i '
-                for key in env.keys():
-                    s = s + '%s=%s '%(key, defaultEscape(env[key]))
-                s = s + 'sh -c '
-                s = s + defaultEscape(string.join(args))
-            else:
-                s = string.join(args)
-
-            return os.system(s) >> 8
-    else:
-        def defaultSpawn(cmd, args, env):
-            pid = os.fork()
-            if not pid:
-                # Child process.
-                exitval = 127
-                args = ['sh', '-c', string.join(args)]
-                try:
-                    os.execvpe('sh', args, env)
-                except OSError, e:
-                    exitval = exitvalmap[e[0]]
-                    sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
-                os._exit(exitval)
-            else:
-                # Parent process.
-                pid, stat = os.waitpid(pid, 0)
-                ret = stat >> 8
-                return ret
-
-elif os.name == 'nt':
-
-    def pathsearch(cmd, env):
-        # In order to deal with the fact that 1.5.2 doesn't have
-        # os.spawnvpe(), roll our own PATH search.
-        if os.path.isabs(cmd):
-            if not os.path.exists(cmd):
-                exts = env['PATHEXT']
-                if not SCons.Util.is_List(exts):
-                    exts = string.split(exts, os.pathsep)
-                for e in exts:
-                    f = cmd + e
-                    if os.path.exists(f):
-                        return f
-            else:
-                return cmd
-        else:
-            path = env['PATH']
-            if not SCons.Util.is_List(path):
-                path = string.split(path, os.pathsep)
-            exts = env['PATHEXT']
-            if not SCons.Util.is_List(exts):
-                exts = string.split(exts, os.pathsep)
-            pairs = []
-            for dir in path:
-                for e in exts:
-                    pairs.append((dir, e))
-            for dir, ext in pairs:
-                f = os.path.join(dir, cmd)
-                if not ext is None:
-                    f = f + ext
-                if os.path.exists(f):
-                    return f
-        return None
-
-    # Attempt to find cmd.exe (for WinNT/2k/XP) or
-    # command.com for Win9x
-
-    cmd_interp = ''
-    # First see if we can look in the registry...
-    if SCons.Util.can_read_reg:
-        try:
-            # Look for Windows NT system root
-            k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
-                                          'Software\\Microsoft\\Windows NT\\CurrentVersion')
-            val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
-            cmd_interp = os.path.join(val, 'System32\\cmd.exe')
-        except SCons.Util.RegError:
-            try:
-                # Okay, try the Windows 9x system root
-                k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
-                                              'Software\\Microsoft\\Windows\\CurrentVersion')
-                val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
-                cmd_interp = os.path.join(val, 'command.com')
-            except:
-                pass
-    if not cmd_interp:
-        cmd_interp = pathsearch('cmd', os.environ)
-        if not cmd_interp:
-            cmd_interp = pathsearch('command', os.environ)
-
-    # The upshot of all this is that, if you are using Python 1.5.2,
-    # you had better have cmd or command.com in your PATH when you run
-    # scons.
-
-    def defaultSpawn(cmd, args, env):
-        if not cmd_interp:
-            sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
-            return 127
-        else:
-            try:
-                args = [cmd_interp, '/C', escape_cmd(string.join(args)) ]
-                ret = os.spawnve(os.P_WAIT, cmd_interp, args, env)
-            except OSError, e:
-                ret = exitvalmap[e[0]]
-                sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
-            return ret
-
-    # Windows does not allow special characters in file names
-    # anyway, so no need for an escape function, we will just quote
-    # the arg.
-    defaultEscape = lambda x: '"' + x + '"'
-else:
-    def defaultSpawn(cmd, args, env):
-        sys.stderr.write("scons: Unknown os '%s', cannot spawn command interpreter.\n" % os.name)
-        sys.stderr.write("scons: Set your command handler with SetCommandHandler().\n")
-        return 127
-
-spawn = defaultSpawn
-escape_cmd = defaultEscape
-
 def SetCommandHandler(func, escape = lambda x: x):
-    """Sets the command handler and escape function for the
-    system.  All command actions are passed through
-    the command handler, which should be a function that accepts
-    3 arguments: a string command, a list of arguments (the first
-    of which is the command itself), and a dictionary representing
-    the execution environment.  The function should then pass
-    the string to a suitable command interpreter.
-
-    The escape function should take a string and return the same
-    string with all special characters escaped such that the command
-    interpreter will interpret the string literally."""
-    global spawn, escape_cmd
-    spawn = func
-    escape_cmd = escape
-    
-def GetCommandHandler():
-    global spawn
-    return spawn
+    raise SCons.Errors.UserError("SetCommandHandler() is no longer supported, use the SPAWN and ESCAPE construction variables.")
 
-def GetEscapeHandler():
-    global escape_cmd
-    return escape_cmd
+def GetCommandHandler():
+    raise SCons.Errors.UserError("GetCommandHandler() is no longer supported, use the SPAWN construction variable.")
 
 class CommandGenerator:
     """
@@ -347,6 +191,20 @@ class CommandAction(ActionBase):
         self.cmd_list = cmd
 
     def execute(self, target, source, env):
+        escape = env.get('ESCAPE', lambda x: x)
+
+        import SCons.Errors
+        
+        if env.has_key('SHELL'):
+            shell = env['SHELL']
+        else:
+            raise SCons.Errors.UserError('Missing SHELL construction variable.')
+
+        if env.has_key('SPAWN'):
+            spawn = env['SPAWN']
+        else:
+            raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+
         dict = self.subst_dict(target, source, env)
         import SCons.Util
         cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
@@ -365,9 +223,9 @@ class CommandAction(ActionBase):
                         ENV = default_ENV
                     # Escape the command line for the command
                     # interpreter we are using
-                    map(lambda x: x.escape(escape_cmd), cmd_line)
+                    map(lambda x, e=escape: x.escape(e), cmd_line)
                     cmd_line = map(str, cmd_line)
-                    ret = spawn(cmd_line[0], cmd_line, ENV)
+                    ret = spawn(shell, escape, cmd_line[0], cmd_line, ENV)
                     if ret:
                         return ret
         return 0
index 1ef71f024995cc65fd20c90be6bfdec266f8795a..86b84b9dbe8a77b67c5ec57c0b2275f500d7ced4 100644 (file)
@@ -36,6 +36,7 @@ import unittest
 
 import SCons.Action
 import TestCmd
+import SCons.Errors
 
 import UserDict
 
@@ -157,8 +158,9 @@ class CommandActionTestCase(unittest.TestCase):
             def __init__(self):
                 self.executed = 0
         t=Test()
-        def func(cmd, args, env, test=t):
+        def func(sh, escape, cmd, args, env, test=t):
             test.executed = args
+            test.shell = sh
             return 0
         def escape_func(cmd):
             return '**' + cmd + '**'
@@ -170,17 +172,25 @@ class CommandActionTestCase(unittest.TestCase):
                 return self.data
             def is_literal(self):
                 return 1
+
+        try:
+            SCons.Action.SetCommandHandler(func)
+        except SCons.Errors.UserError:
+            pass
+        else:
+            assert 0, "should have gotten user error"
             
-        SCons.Action.SetCommandHandler(func)
-        assert SCons.Action.spawn is func
         a = SCons.Action.CommandAction(["xyzzy"])
-        a.execute([],[],Environment({}))
+        a.execute([],[],Environment({'SPAWN':func}))
         assert t.executed == [ 'xyzzy' ]
 
-        SCons.Action.SetCommandHandler(func,escape_func)
-        assert SCons.Action.GetEscapeHandler() == escape_func
+        a = SCons.Action.CommandAction(["xyzzy"])
+        a.execute([],[],Environment({'SPAWN':func, 'SHELL':'fake shell'}))
+        assert t.executed == [ 'xyzzy' ]
+        assert t.shell == 'fake shell'
+
         a = SCons.Action.CommandAction([ LiteralStr("xyzzy") ])
-        a.execute([],[],Environment({ }))
+        a.execute([],[],Environment({'SPAWN':func, 'ESCAPE':escape_func}))
         assert t.executed == [ '**xyzzy**' ], t.executed
 
     def test_get_raw_contents(self):
@@ -241,21 +251,17 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
             self.dummy=dummy
         def f2(target, source, env, for_signature, f=func_action):
             return f
-        def ch(cmd, args, env, self=self):
+        def ch(sh, escape, cmd, args, env, self=self):
             self.cmd.append(cmd)
             self.args.append(args)
 
         a = SCons.Action.CommandGeneratorAction(f)
         self.dummy = 0
-        old_hdl = SCons.Action.GetCommandHandler()
         self.cmd = []
         self.args = []
-        try:
-            SCons.Action.SetCommandHandler(ch)
-            a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack',
-                                              'dummy' : 1}))
-        finally:
-            SCons.Action.SetCommandHandler(old_hdl)
+        a.execute([],[],env=Environment({ 'FOO' : 'foo baz\nbar ack',
+                                          'dummy' : 1,
+                                          'SPAWN':ch}))
         assert self.dummy == 1, self.dummy
         assert self.cmd == ['foo', 'bar'], self.cmd
         assert self.args == [[ 'foo', 'baz' ], [ 'bar', 'ack' ]], self.args
index 191740517c7195988317767162e5cee01e058874..b087d7bc6b079091e3733e8d845b2ea99311bfaa 100644 (file)
@@ -40,6 +40,7 @@ import SCons.Builder
 import SCons.Errors
 import SCons.Node.FS
 import SCons.Warnings
+import SCons.Environment
 
 # Initial setup of the common environment for all tests,
 # a temporary working directory containing a
@@ -70,9 +71,14 @@ show_string = None
 env_scanner = None
 count = 0
 
+scons_env = SCons.Environment.Environment()
+
 class Environment:
     def __init__(self, **kw):
         self.d = {}
+        self.d['SHELL'] = scons_env['SHELL']
+        self.d['SPAWN'] = scons_env['SPAWN']
+        self.d['ESCAPE'] = scons_env['ESCAPE']
         for k, v in kw.items():
             self.d[k] = v
     def subst(self, s):
index b4b67bb7a2a01c614e64180796fc64f614c0737b..ffbee67a350d1c23e590b43bd7027f99ab42811f 100644 (file)
@@ -28,34 +28,41 @@ import unittest
 
 import SCons.Errors
 import SCons.Platform
+import UserDict
+
+class Environment(UserDict.UserDict):
+    def Detect(self, cmd):
+        return cmd
 
 class PlatformTestCase(unittest.TestCase):
     def test_Platform(self):
         """Test the Platform() function"""
         p = SCons.Platform.Platform('cygwin')
         assert str(p) == 'cygwin', p
-        env = {}
+        env = Environment()
         p(env)
         assert env['PROGSUFFIX'] == '.exe', env
         assert env['LIBSUFFIX'] == '.a', env
+        assert env['SHELL'] == 'sh', env
 
         p = SCons.Platform.Platform('os2')
         assert str(p) == 'os2', p
-        env = {}
+        env = Environment()
         p(env)
         assert env['PROGSUFFIX'] == '.exe', env
         assert env['LIBSUFFIX'] == '.lib', env
 
         p = SCons.Platform.Platform('posix')
         assert str(p) == 'posix', p
-        env = {}
+        env = Environment()
         p(env)
         assert env['PROGSUFFIX'] == '', env
         assert env['LIBSUFFIX'] == '.a', env
+        assert env['SHELL'] == 'sh', env
 
         p = SCons.Platform.Platform('win32')
         assert str(p) == 'win32', p
-        env = {}
+        env = Environment()
         p(env)
         assert env['PROGSUFFIX'] == '.exe', env
         assert env['LIBSUFFIX'] == '.lib', env
@@ -68,7 +75,7 @@ class PlatformTestCase(unittest.TestCase):
         else:
             raise
 
-        env = {}
+        env = Environment()
         SCons.Platform.Platform()(env)
         assert env != {}, env
 
index 47b64d661efb80e3cf39749ac5010f9b3b30f8dc..fe5a818636bf7821c59cf1966b7b2f013a2a5efb 100644 (file)
@@ -33,20 +33,10 @@ selection method.
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.Util
+import posix
 
 def generate(env):
-    if not env.has_key('ENV'):
-        env['ENV']        = {}
-    env['ENV']['PATH']    = '/usr/local/bin:/bin:/usr/bin'
-    env['OBJPREFIX']      = ''
-    env['OBJSUFFIX']      = '.o'
-    env['SHOBJPREFIX']    = '$OBJPREFIX'
-    env['SHOBJSUFFIX']    = '$OBJSUFFIX'
+    posix.generate(env)
+
     env['PROGPREFIX']     = ''
     env['PROGSUFFIX']     = '.exe'
-    env['LIBPREFIX']      = 'lib'
-    env['LIBSUFFIX']      = '.a'
-    env['SHLIBPREFIX']    = '$LIBPREFIX'
-    env['SHLIBSUFFIX']    = '.so'
-    env['LIBPREFIXES']    = '$LIBPREFIX'
-    env['LIBSUFFIXES']    = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
index 551265c7a31abc7ac32fe6ce14006ba53c30555b..c7ec9d1eee2faade71cc678a343c932794690096 100644 (file)
@@ -33,8 +33,63 @@ selection method.
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.Util
+import string
+import os
+import sys
+import os.path
 
+def escape(arg):
+    "escape shell special characters"
+    slash = '\\'
+    special = '"$'
+
+    arg = string.replace(arg, slash, slash+slash)
+    for c in special:
+        arg = string.replace(arg, c, slash+c)
+
+    return '"' + arg + '"'
+
+def env_spawn(sh, escape, cmd, args, env):
+    if env:
+        s = 'env -i '
+        for key in env.keys():
+            s = s + '%s=%s '%(key, escape(env[key]))
+        s = s + sh + ' -c '
+        s = s + escape(string.join(args))
+    else:
+        s = string.join(args)
+
+    return os.system(s) >> 8
+
+def fork_spawn(sh, escape, cmd, args, env):
+    pid = os.fork()
+    if not pid:
+        # Child process.
+        exitval = 127
+        args = [sh, '-c', string.join(args)]
+        try:
+            os.execvpe(sh, args, env)
+        except OSError, e:
+            exitval = exitvalmap[e[0]]
+            sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+        os._exit(exitval)
+    else:
+        # Parent process.
+        pid, stat = os.waitpid(pid, 0)
+        ret = stat >> 8
+        return ret
+            
 def generate(env):
+
+    # If the env command exists, then we can use os.system()
+    # to spawn commands, otherwise we fall back on os.fork()/os.exec().
+    # os.system() is prefered because it seems to work better with
+    # threads (i.e. -j) and is more efficient than forking Python.
+    if env.Detect('env'):
+        spawn = env_spawn
+    else:
+        spawn = fork_spawn
+
     if not env.has_key('ENV'):
         env['ENV']        = {}
     env['ENV']['PATH']    = '/usr/local/bin:/bin:/usr/bin'
@@ -50,3 +105,6 @@ def generate(env):
     env['SHLIBSUFFIX']    = '.so'
     env['LIBPREFIXES']    = '$LIBPREFIX'
     env['LIBSUFFIXES']    = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+    env['SPAWN']          = spawn
+    env['SHELL']          = 'sh'
+    env['ESCAPE']         = escape
index 5ad9ff17720882ded4e58d42b460ff6c8eb9717f..b54aefe6d1de7babf6331c4edfe16b83828c3063 100644 (file)
@@ -33,8 +33,60 @@ selection method.
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.Util
+import os
+import os.path
+import string
+import sys
+
+# The upshot of all this is that, if you are using Python 1.5.2,
+# you had better have cmd or command.com in your PATH when you run
+# scons.
+
+def spawn(sh, escape, cmd, args, env):
+    if not sh:
+        sys.stderr.write("scons: Could not find command interpreter, is it in your PATH?\n")
+        return 127
+    else:
+        try:
+            args = [sh, '/C', escape(string.join(args)) ]
+            ret = os.spawnve(os.P_WAIT, sh, args, env)
+        except OSError, e:
+            ret = exitvalmap[e[0]]
+            sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
+        return ret
+
+# Windows does not allow special characters in file names
+# anyway, so no need for an escape function, we will just quote
+# the arg.
+escape = lambda x: '"' + x + '"'
 
 def generate(env):
+
+    # Attempt to find cmd.exe (for WinNT/2k/XP) or
+    # command.com for Win9x
+    cmd_interp = ''
+    # First see if we can look in the registry...
+    if SCons.Util.can_read_reg:
+        try:
+            # Look for Windows NT system root
+            k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+                                          'Software\\Microsoft\\Windows NT\\CurrentVersion')
+            val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+            cmd_interp = os.path.join(val, 'System32\\cmd.exe')
+        except SCons.Util.RegError:
+            try:
+                # Okay, try the Windows 9x system root
+                k=SCons.Util.RegOpenKeyEx(SCons.Util.hkey_mod.HKEY_LOCAL_MACHINE,
+                                              'Software\\Microsoft\\Windows\\CurrentVersion')
+                val, tok = SCons.Util.RegQueryValueEx(k, 'SystemRoot')
+                cmd_interp = os.path.join(val, 'command.com')
+            except:
+                pass
+    if not cmd_interp:
+        cmd_interp = env.Detect('cmd')
+        if not cmd_interp:
+            cmd_interp = env.Detect('command')
+    
     if not env.has_key('ENV'):
         env['ENV']        = {}
     env['ENV']['PATHEXT'] = '.COM;.EXE;.BAT;.CMD'
@@ -50,3 +102,6 @@ def generate(env):
     env['SHLIBSUFFIX']    = '.dll'
     env['LIBPREFIXES']    = [ '$LIBPREFIX', '$SHLIBPREFIX' ]
     env['LIBSUFFIXES']    = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+    env['SPAWN']          = spawn
+    env['SHELL']          = cmd_interp
+    env['ESCAPE']         = escape
index 34f1b5250227fd8860a7d24a153abb7085bc99e2..7bbdf7a749ff6025ba7aa2475dc6465e762cb863 100644 (file)
@@ -29,9 +29,10 @@ import TestSCons
 test = TestSCons.TestSCons()
 
 test.write('SConstruct', """
-env = {}
+env = Environment()
 Platform('cygwin')(env)
 print "'%s'" % env['PROGSUFFIX']
+assert env['SHELL'] == 'sh'
 Platform('os2')(env)
 print "'%s'" % env['PROGSUFFIX']
 Platform('posix')(env)
@@ -42,7 +43,7 @@ SConscript('SConscript')
 """)
 
 test.write('SConscript', """
-env = {}
+env = Environment()
 Platform('cygwin')(env)
 print "'%s'" % env['LIBSUFFIX']
 Platform('os2')(env)