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.
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.
.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.
.\"XXX
.\"
.\".TP
-.\".RI GetCommandHandler( XXX )
-.\"XXX
-.\"
-.\".TP
.\".RI GetLaunchDir( XXX )
.\"XXX
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 )
- 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
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
- 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.
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.
import SCons.Util
import SCons.Errors
+
print_actions = 1;
execute_actions = 1;
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:
"""
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)
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
import SCons.Action
import TestCmd
+import SCons.Errors
import UserDict
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 + '**'
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):
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
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
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):
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
else:
raise
- env = {}
+ env = Environment()
SCons.Platform.Platform()(env)
assert env != {}, env
__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' ]
__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'
env['SHLIBSUFFIX'] = '.so'
env['LIBPREFIXES'] = '$LIBPREFIX'
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['SPAWN'] = spawn
+ env['SHELL'] = 'sh'
+ env['ESCAPE'] = escape
__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'
env['SHLIBSUFFIX'] = '.dll'
env['LIBPREFIXES'] = [ '$LIBPREFIX', '$SHLIBPREFIX' ]
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
+ env['SPAWN'] = spawn
+ env['SHELL'] = cmd_interp
+ env['ESCAPE'] = escape
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)
""")
test.write('SConscript', """
-env = {}
+env = Environment()
Platform('cygwin')(env)
print "'%s'" % env['LIBSUFFIX']
Platform('os2')(env)