diff options
author | Steven Knight <knight@baldmt.com> | 2002-11-25 23:33:49 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2002-11-25 23:33:49 (GMT) |
commit | dd30a312b7c59abd25d41c3d332df57801abf66b (patch) | |
tree | 850fbde140c46672ee388dea2fe3fc0dd4ed525f /src | |
parent | 22c249b07f1831b86aca87ba1728a0cf19884b80 (diff) | |
download | SCons-dd30a312b7c59abd25d41c3d332df57801abf66b.zip SCons-dd30a312b7c59abd25d41c3d332df57801abf66b.tar.gz SCons-dd30a312b7c59abd25d41c3d332df57801abf66b.tar.bz2 |
Make the shell pickable via a construction variable. (Anthony Roach)
Diffstat (limited to 'src')
-rw-r--r-- | src/CHANGES.txt | 3 | ||||
-rw-r--r-- | src/RELEASE.txt | 41 | ||||
-rw-r--r-- | src/engine/SCons/Action.py | 182 | ||||
-rw-r--r-- | src/engine/SCons/ActionTests.py | 36 | ||||
-rw-r--r-- | src/engine/SCons/BuilderTests.py | 6 | ||||
-rw-r--r-- | src/engine/SCons/Platform/PlatformTests.py | 17 | ||||
-rw-r--r-- | src/engine/SCons/Platform/cygwin.py | 16 | ||||
-rw-r--r-- | src/engine/SCons/Platform/posix.py | 58 | ||||
-rw-r--r-- | src/engine/SCons/Platform/win32.py | 55 |
9 files changed, 185 insertions, 229 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 0c6325f..825d657 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -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 diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 0dab6ce..5f8c89a 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -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. diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 8347195..534b813 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -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 diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 1ef71f0..86b84b9 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -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 diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index 1917405..b087d7b 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -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): diff --git a/src/engine/SCons/Platform/PlatformTests.py b/src/engine/SCons/Platform/PlatformTests.py index b4b67bb..ffbee67 100644 --- a/src/engine/SCons/Platform/PlatformTests.py +++ b/src/engine/SCons/Platform/PlatformTests.py @@ -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 diff --git a/src/engine/SCons/Platform/cygwin.py b/src/engine/SCons/Platform/cygwin.py index 47b64d6..fe5a818 100644 --- a/src/engine/SCons/Platform/cygwin.py +++ b/src/engine/SCons/Platform/cygwin.py @@ -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' ] diff --git a/src/engine/SCons/Platform/posix.py b/src/engine/SCons/Platform/posix.py index 551265c..c7ec9d1 100644 --- a/src/engine/SCons/Platform/posix.py +++ b/src/engine/SCons/Platform/posix.py @@ -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 diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 5ad9ff1..b54aefe 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -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 |