summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2002-04-15 18:43:38 (GMT)
committerSteven Knight <knight@baldmt.com>2002-04-15 18:43:38 (GMT)
commita8176f609ff3ecc090f51830408d3b4dc6338d7e (patch)
treebab059042f2f8cbc85dcf7a619dbebbbe23dc4fb /src/engine
parent05029e336146444501a66b53e4699c09d6e08977 (diff)
downloadSCons-a8176f609ff3ecc090f51830408d3b4dc6338d7e.zip
SCons-a8176f609ff3ecc090f51830408d3b4dc6338d7e.tar.gz
SCons-a8176f609ff3ecc090f51830408d3b4dc6338d7e.tar.bz2
Big change for shared libraries and a bunch of other flexibility. (Charles Crain)
Diffstat (limited to 'src/engine')
-rw-r--r--src/engine/SCons/Action.py125
-rw-r--r--src/engine/SCons/ActionTests.py63
-rw-r--r--src/engine/SCons/Builder.py302
-rw-r--r--src/engine/SCons/BuilderTests.py146
-rw-r--r--src/engine/SCons/Defaults.py231
-rw-r--r--src/engine/SCons/Environment.py13
-rw-r--r--src/engine/SCons/EnvironmentTests.py6
-rw-r--r--src/engine/SCons/Node/NodeTests.py10
-rw-r--r--src/engine/SCons/Node/__init__.py39
-rw-r--r--src/engine/SCons/Scanner/Prog.py19
-rw-r--r--src/engine/SCons/Scanner/ProgTests.py5
-rw-r--r--src/engine/SCons/Sig/MD5.py9
-rw-r--r--src/engine/SCons/Sig/MD5Tests.py3
-rw-r--r--src/engine/SCons/Util.py30
-rw-r--r--src/engine/SCons/UtilTests.py46
15 files changed, 725 insertions, 322 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index 3d52142..38b5413 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -38,6 +38,7 @@ import sys
import UserDict
import SCons.Util
+import SCons.Errors
print_actions = 1;
execute_actions = 1;
@@ -171,28 +172,53 @@ def GetCommandHandler():
class CommandGenerator:
"""
- Wrappes a command generator function so the Action() factory
+ Wraps a command generator function so the Action() factory
function can tell a generator function from a function action.
"""
def __init__(self, generator):
self.generator = generator
+def _do_create_action(act):
+ """This is the actual "implementation" for the
+ Action factory method, below. This handles the
+ fact that passing lists to Action() itself has
+ different semantics than passing lists as elements
+ of lists.
+
+ The former will create a ListAction, the latter
+ will create a CommandAction by converting the inner
+ list elements to strings."""
-def Action(act):
- """A factory for action objects."""
if isinstance(act, ActionBase):
return act
+ elif SCons.Util.is_List(act):
+ return CommandAction(act)
elif isinstance(act, CommandGenerator):
return CommandGeneratorAction(act.generator)
elif callable(act):
return FunctionAction(act)
elif SCons.Util.is_String(act):
- return CommandAction(act)
- elif SCons.Util.is_List(act):
- return ListAction(act)
+ listCmds = map(lambda x: CommandAction(string.split(x)),
+ string.split(act, '\n'))
+ if len(listCmds) == 1:
+ return listCmds[0]
+ else:
+ return ListAction(listCmds)
else:
return None
+def Action(act):
+ """A factory for action objects."""
+ if SCons.Util.is_List(act):
+ acts = filter(lambda x: not x is None,
+ map(_do_create_action, act))
+ if len(acts) == 1:
+ return acts[0]
+ else:
+ return ListAction(acts)
+ else:
+ return _do_create_action(act)
+
class ActionBase:
"""Base class for actions that create output objects."""
def __cmp__(self, other):
@@ -241,7 +267,7 @@ class ActionBase:
t = [t]
try:
cwd = t[0].cwd
- except AttributeError:
+ except (IndexError, AttributeError):
pass
dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, t)))
if dict['TARGETS']:
@@ -260,6 +286,16 @@ class ActionBase:
return dict
+def _string_from_cmd_list(cmd_list):
+ """Takes a list of command line arguments and returns a pretty
+ representation for printing."""
+ cl = []
+ for arg in cmd_list:
+ if ' ' in arg or '\t' in arg:
+ arg = '"' + arg + '"'
+ cl.append(arg)
+ return string.join(cl)
+
_rm = re.compile(r'\$[()]')
_remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
@@ -286,22 +322,19 @@ class EnvDictProxy(UserDict.UserDict):
class CommandAction(ActionBase):
"""Class for command-execution actions."""
- def __init__(self, string):
- self.command = string
+ def __init__(self, cmd):
+ import SCons.Util
+
+ self.cmd_list = map(SCons.Util.to_String, cmd)
def execute(self, **kw):
dict = apply(self.subst_dict, (), kw)
import SCons.Util
- cmd_list = SCons.Util.scons_subst_list(self.command, dict, {}, _rm)
+ cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
for cmd_line in cmd_list:
if len(cmd_line):
if print_actions:
- cl = []
- for arg in cmd_line:
- if ' ' in arg or '\t' in arg:
- arg = '"' + arg + '"'
- cl.append(arg)
- self.show(string.join(cl))
+ self.show(_string_from_cmd_list(cmd_line))
if execute_actions:
try:
ENV = kw['env']['ENV']
@@ -328,7 +361,8 @@ class CommandAction(ActionBase):
def get_raw_contents(self, **kw):
"""Return the complete contents of this action's command line.
"""
- return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {})
+ return SCons.Util.scons_subst(string.join(self.cmd_list),
+ self._sig_dict(kw), {})
def get_contents(self, **kw):
"""Return the signature contents of this action's command line.
@@ -336,19 +370,16 @@ class CommandAction(ActionBase):
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
- return SCons.Util.scons_subst(self.command, self._sig_dict(kw), {}, _remove)
+ return SCons.Util.scons_subst(string.join(self.cmd_list),
+ self._sig_dict(kw), {}, _remove)
class CommandGeneratorAction(ActionBase):
"""Class for command-generator actions."""
def __init__(self, generator):
self.generator = generator
- def execute(self, **kw):
- # ensure that target is a list, to make it easier to write
- # generator functions:
+ def __generate(self, kw):
import SCons.Util
- if kw.has_key("target") and not SCons.Util.is_List(kw["target"]):
- kw["target"] = [kw["target"]]
# Wrap the environment dictionary in an EnvDictProxy
# object to make variable interpolation easier for the
@@ -357,36 +388,19 @@ class CommandGeneratorAction(ActionBase):
if args.has_key("env") and not isinstance(args["env"], EnvDictProxy):
args["env"] = EnvDictProxy(args["env"])
- gen_list = apply(self.generator, (), args)
- gen_list = map(lambda x: map(str, x), gen_list)
+ # ensure that target is a list, to make it easier to write
+ # generator functions:
+ if args.has_key("target") and not SCons.Util.is_List(args["target"]):
+ args["target"] = [args["target"]]
- # Do environment variable substitution on returned command list
- dict = apply(self.subst_dict, (), kw)
- cmd_list = [ ]
- for gen_line in gen_list:
- cmd_list.append([])
- curr_line = cmd_list[-1]
- for gen_arg in gen_line:
- arg_list = SCons.Util.scons_subst_list(gen_arg, dict, {})
- curr_line.extend(arg_list[0])
- if(len(arg_list) > 1):
- cmd_list.extend(arg_list[1:])
- curr_line = cmd_list[-1]
-
- for cmd_line in filter(lambda x: x, cmd_list):
- if print_actions:
- self.show(cmd_line)
- if execute_actions:
- try:
- ENV = kw['env']['ENV']
- except:
- import SCons.Defaults
- ENV = SCons.Defaults.ConstructionEnvironment['ENV']
- ret = spawn(cmd_line[0], cmd_line, ENV)
- if ret:
- return ret
+ ret = apply(self.generator, (), args)
+ gen_cmd = Action(ret)
+ if not gen_cmd:
+ raise SCons.Errors.UserError("Object returned from command generator: %s cannot be used to create an Action." % repr(ret))
+ return gen_cmd
- return 0
+ def execute(self, **kw):
+ return apply(self.__generate(kw).execute, (), kw)
def get_contents(self, **kw):
"""Return the signature contents of this action's command line.
@@ -394,14 +408,7 @@ class CommandGeneratorAction(ActionBase):
This strips $(-$) and everything in between the string,
since those parts don't affect signatures.
"""
- kw['source'] = ["__s1__", "__s2__"]
- kw['target'] = ["__t1__", "__t2__"]
- cmd_list = apply(self.generator, (), kw)
- cmd_list = map(lambda x: map(str, x), cmd_list)
- cmd_list = map(lambda x: string.join(x, "\0"), cmd_list)
- cmd_list = map(lambda x: _remove.sub('', x), cmd_list)
- cmd_list = map(lambda x: filter(lambda y: y, string.split(x, "\0")), cmd_list)
- return cmd_list
+ return apply(self.__generate(kw).get_contents, (), kw)
class FunctionAction(ActionBase):
"""Class for Python function actions."""
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index 1a0596f..648b77b 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -54,14 +54,27 @@ class ActionTestCase(unittest.TestCase):
exec "a3 = SCons.Action.Action(u'string')"
exec "assert isinstance(a3, SCons.Action.CommandAction), a3"
- a4 = SCons.Action.Action(["x", a2, "y"])
+ a4 = SCons.Action.Action(["x", "y", "z", [ "a", "b", "c"]])
assert isinstance(a4, SCons.Action.ListAction), a4
+ assert isinstance(a4.list[0], SCons.Action.CommandAction), a4.list[0]
+ assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1]
+ assert isinstance(a4.list[2], SCons.Action.CommandAction), a4.list[2]
+ assert isinstance(a4.list[3], SCons.Action.CommandAction), a4.list[3]
+ assert a4.list[3].cmd_list == [ "a", "b", "c" ], a4.list[3].cmd_list
a5 = SCons.Action.Action(1)
assert a5 is None, a5
a6 = SCons.Action.Action(a1)
- assert a6 is a1
+ assert a6 is a1, a6
+
+ a7 = SCons.Action.Action([[ "explicit", "command", "line" ]])
+ assert isinstance(a7, SCons.Action.CommandAction), a7
+ assert a7.cmd_list == [ "explicit", "command", "line" ], a7.cmd_list
+
+ a8 = SCons.Action.Action(["a8"])
+ assert isinstance(a8, SCons.Action.CommandAction), a8
+ assert a8.cmd_list == [ "a8" ], a8.cmd_list
class ActionBaseTestCase(unittest.TestCase):
@@ -106,8 +119,8 @@ class CommandActionTestCase(unittest.TestCase):
def test_init(self):
"""Test creation of a command Action
"""
- a = SCons.Action.CommandAction("xyzzy")
- assert a.command == "xyzzy"
+ a = SCons.Action.CommandAction(["xyzzy"])
+ assert a.cmd_list == [ "xyzzy" ], a.cmd_list
def test_execute(self):
"""Test executing a command Action
@@ -127,23 +140,27 @@ class CommandActionTestCase(unittest.TestCase):
return 0
SCons.Action.SetCommandHandler(func)
assert SCons.Action.spawn is func
- a = SCons.Action.CommandAction("xyzzy")
+ a = SCons.Action.CommandAction(["xyzzy"])
a.execute()
assert t.executed == 1
def test_get_raw_contents(self):
"""Test fetching the contents of a command Action
"""
- a = SCons.Action.CommandAction("| $( $foo | $bar $) |")
- c = a.get_contents(foo = 'FFF', bar = 'BBB')
+ a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
+ "$)", "|"])
+ c = a.get_contents(target=[], source=[],
+ foo = 'FFF', bar = 'BBB')
assert c == "| $( FFF | BBB $) |"
def test_get_contents(self):
"""Test fetching the contents of a command Action
"""
- a = SCons.Action.CommandAction("| $foo $( | $) $bar |")
- c = a.get_contents(foo = 'FFF', bar = 'BBB')
- assert c == "| FFF BBB |"
+ a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
+ "$)", "|"])
+ c = a.get_contents(target=[], source=[],
+ foo = 'FFF', bar = 'BBB')
+ assert c == "| |", c
class CommandGeneratorActionTestCase(unittest.TestCase):
@@ -164,7 +181,14 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert env.subst('$FOO') == 'foo baz\nbar ack', env.subst('$FOO')
assert env.subst_list('$FOO') == [ [ 'foo', 'baz' ],
[ 'bar', 'ack' ] ], env.subst_list('$FOO')
- return [["$FOO"]]
+ return "$FOO"
+ def func_action(env, dummy, self=self):
+ assert env.subst('$foo') == 'bar', env.subst('$foo')
+ assert env.subst_list('$foo') == [ [ 'bar' ] ], env.subst_list('$foo')
+ assert env.subst_list([ '$foo', 'bar' ]) == [[ 'bar', 'bar' ]], env.subst_list([ [ '$foo', 'bar' ] ])
+ self.dummy=dummy
+ def f2(dummy, env, f=func_action):
+ return f
def ch(cmd, args, env, self=self):
self.cmd.append(cmd)
self.args.append(args)
@@ -182,6 +206,11 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
assert self.dummy == 1
assert self.cmd == [ 'foo', 'bar'], self.cmd
assert self.args == [ [ 'foo', 'baz' ], [ 'bar', 'ack' ] ], self.args
+
+ b=SCons.Action.CommandGeneratorAction(f2)
+ self.dummy = 0
+ b.execute(dummy=2, env={ 'foo' : 'bar' })
+ assert self.dummy==2, self.dummy
del self.dummy
def test_get_contents(self):
@@ -191,8 +220,9 @@ class CommandGeneratorActionTestCase(unittest.TestCase):
return [["guux", foo, "$(", "ignore", "$)", bar]]
a = SCons.Action.CommandGeneratorAction(f)
- c = a.get_contents(foo = 'FFF', bar = 'BBB')
- assert c == [["guux", 'FFF', 'BBB']], c
+ c = a.get_contents(target=[], source=[],
+ foo = 'FFF', bar = 'BBB')
+ assert c == "guux FFF BBB", c
class FunctionActionTestCase(unittest.TestCase):
@@ -227,7 +257,7 @@ class FunctionActionTestCase(unittest.TestCase):
"""Test fetching the contents of a function Action
"""
a = SCons.Action.FunctionAction(Func)
- c = a.get_contents()
+ c = a.get_contents(target=[], source=[])
assert c == "\177\036\000\177\037\000d\000\000S", repr(c)
class ListActionTestCase(unittest.TestCase):
@@ -241,8 +271,7 @@ class ListActionTestCase(unittest.TestCase):
assert isinstance(a.list[0], SCons.Action.CommandAction)
assert isinstance(a.list[1], SCons.Action.FunctionAction)
assert isinstance(a.list[2], SCons.Action.ListAction)
- assert isinstance(a.list[2].list[0], SCons.Action.CommandAction)
- assert isinstance(a.list[2].list[1], SCons.Action.CommandAction)
+ assert a.list[2].list[0].cmd_list == [ 'y' ]
def test_execute(self):
"""Test executing a list of subsidiary Actions
@@ -259,7 +288,7 @@ class ListActionTestCase(unittest.TestCase):
"""Test fetching the contents of a list of subsidiary Actions
"""
a = SCons.Action.ListAction(["x", "y", "z"])
- c = a.get_contents()
+ c = a.get_contents(target=[], source=[])
assert c == "xyz", c
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index 2ccba00..2a1b7d8 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -33,32 +33,61 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os.path
import string
-from Errors import UserError
+import copy
+from SCons.Errors import UserError
import SCons.Action
import SCons.Node
import SCons.Node.FS
import SCons.Util
+class DictCmdGenerator:
+ """This is a callable class that can be used as a
+ command generator function. It holds on to a dictionary
+ mapping file suffixes to Actions. It uses that dictionary
+ to return the proper action based on the file suffix of
+ the source file."""
+
+ def __init__(self, action_dict):
+ self.action_dict = action_dict
+
+ def src_suffixes(self):
+ return self.action_dict.keys()
+
+ def __call__(self, source, target, env, **kw):
+ ext = None
+ for src in map(str, source):
+ my_ext = os.path.splitext(src)[1]
+ if ext and my_ext != ext:
+ raise UserError("Cannot build multiple sources with different extensions.")
+ ext = my_ext
+
+ if ext is None:
+ raise UserError("Cannot deduce file extension from source files: %s" % repr(map(str, source)))
+ try:
+ # XXX Do we need to perform Environment substitution
+ # on the keys of action_dict before looking it up?
+ return self.action_dict[ext]
+ except KeyError:
+ raise UserError("Don't know how to build a file with suffix %s." % ext)
def Builder(**kw):
"""A factory for builder objects."""
-
if kw.has_key('generator'):
if kw.has_key('action'):
raise UserError, "You must not specify both an action and a generator."
kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
del kw['generator']
-
- if kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
- return apply(CompositeBuilder, (), kw)
- elif kw.has_key('src_builder'):
+ elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
+ action_dict = kw['action']
+ kw['action'] = SCons.Action.CommandGenerator(DictCmdGenerator(action_dict))
+ kw['src_suffix'] = action_dict.keys()
+
+ if kw.has_key('src_builder'):
return apply(MultiStepBuilder, (), kw)
else:
return apply(BuilderBase, (), kw)
-
-
def _init_nodes(builder, env, tlist, slist):
"""Initialize lists of target and source nodes with all of
the proper Builder information.
@@ -76,6 +105,29 @@ def _init_nodes(builder, env, tlist, slist):
t.add_source(slist)
if builder.scanner:
t.target_scanner = builder.scanner
+
+class _callable_adaptor:
+ """When crteating a Builder, you can pass a string OR
+ a callable in for prefix, suffix, or src_suffix.
+ src_suffix even takes a list!
+
+ If a string or list is passed, we use this class to
+ adapt it to a callable."""
+ def __init__(self, static):
+ self.static = static
+
+ def __call__(self, **kw):
+ return self.static
+
+ def __cmp__(self, other):
+ if isinstance(other, _callable_adaptor):
+ return cmp(self.static, other.static)
+ return -1
+
+def _adjust_suffix(suff):
+ if suff and not suff[0] in [ '.', '$' ]:
+ return '.' + suff
+ return suff
class BuilderBase:
"""Base class for Builders, objects that create output
@@ -90,27 +142,40 @@ class BuilderBase:
node_factory = SCons.Node.FS.default_fs.File,
target_factory = None,
source_factory = None,
- scanner = None):
+ scanner = None,
+ emitter = None):
if name is None:
raise UserError, "You must specify a name for the builder."
self.name = name
self.action = SCons.Action.Action(action)
- self.prefix = prefix
- self.suffix = suffix
- self.src_suffix = src_suffix
+ if callable(prefix):
+ self.prefix = prefix
+ else:
+ self.prefix = _callable_adaptor(str(prefix))
+
+ if callable(suffix):
+ self.suffix = suffix
+ else:
+ self.suffix = _callable_adaptor(str(suffix))
+
+ if callable(src_suffix):
+ self.src_suffix = src_suffix
+ elif SCons.Util.is_String(src_suffix):
+ self.src_suffix = _callable_adaptor([ str(src_suffix) ])
+ else:
+ self.src_suffix = _callable_adaptor(src_suffix)
+
self.target_factory = target_factory or node_factory
self.source_factory = source_factory or node_factory
self.scanner = scanner
- if self.suffix and self.suffix[0] not in '.$':
- self.suffix = '.' + self.suffix
- if self.src_suffix and self.src_suffix[0] not in '.$':
- self.src_suffix = '.' + self.src_suffix
+
+ self.emitter = emitter
def __cmp__(self, other):
return cmp(self.__dict__, other.__dict__)
- def _create_nodes(self, env, target = None, source = None):
+ def _create_nodes(self, env, args, target = None, source = None):
"""Create and return lists of target and source nodes.
"""
def adjustixes(files, pre, suf):
@@ -122,25 +187,46 @@ class BuilderBase:
if pre and f[:len(pre)] != pre:
path, fn = os.path.split(os.path.normpath(f))
f = os.path.join(path, pre + fn)
- if suf:
- if f[-len(suf):] != suf:
- f = f + suf
- ret.append(f)
- return ret
-
+ # Only append a suffix if the file does not have one.
+ if suf and not os.path.splitext(f)[1]:
+ if f[-len(suf):] != suf:
+ f = f + suf
+ ret.append(f)
+ return ret
+
+ pre = self.get_prefix(env, args)
+ suf = self.get_suffix(env, args)
tlist = SCons.Node.arg2nodes(adjustixes(target,
- env.subst(self.prefix),
- env.subst(self.suffix)),
+ pre, suf),
self.target_factory)
-
+ src_suf = self.get_src_suffix(env, args)
slist = SCons.Node.arg2nodes(adjustixes(source,
None,
- env.subst(self.src_suffix)),
+ src_suf),
self.source_factory)
+ if self.emitter:
+ emit_args = { 'target' : tlist,
+ 'source' : slist,
+ 'env' : env }
+ emit_args.update(args)
+ target, source = apply(self.emitter, (), emit_args)
+
+ # Have to run it through again in case the
+ # function returns non-Node targets/sources.
+ tlist = SCons.Node.arg2nodes(adjustixes(target,
+ pre, suf),
+ self.target_factory)
+ slist = SCons.Node.arg2nodes(adjustixes(source,
+ None,
+ src_suf),
+ self.source_factory)
+
+ for t in tlist:
+ t.build_args = args
return tlist, slist
- def __call__(self, env, target = None, source = None):
- tlist, slist = self._create_nodes(env, target, source)
+ def __call__(self, env, target = None, source = None, **kw):
+ tlist, slist = self._create_nodes(env, kw, target, source)
if len(tlist) == 1:
_init_nodes(self, env, tlist, slist)
@@ -167,10 +253,23 @@ class BuilderBase:
"""
return apply(self.action.get_contents, (), kw)
- def src_suffixes(self, env):
- if self.src_suffix != '':
- return [env.subst(self.src_suffix)]
- return []
+ def src_suffixes(self, env, args):
+ return map(lambda x, e=env: e.subst(_adjust_suffix(x)),
+ apply(self.src_suffix, (), args))
+
+ def get_src_suffix(self, env, args):
+ """Get the first src_suffix in the list of src_suffixes."""
+ ret = self.src_suffixes(env, args)
+ if not ret:
+ return ''
+ else:
+ return ret[0]
+
+ def get_suffix(self, env, args):
+ return env.subst(_adjust_suffix(apply(self.suffix, (), args)))
+
+ def get_prefix(self, env, args):
+ return env.subst(apply(self.prefix, (), args))
def targets(self, node):
"""Return the list of targets for this builder instance.
@@ -199,7 +298,7 @@ class ListBuilder:
# unlink all targets and make all directories
# before building anything
t.prepare()
- kw['target'] = self.tlist[0]
+ kw['target'] = self.tlist
self.status = apply(self.builder.execute, (), kw)
for t in self.tlist:
if not t is kw['target']:
@@ -212,8 +311,8 @@ class ListBuilder:
def get_contents(self, **kw):
return apply(self.builder.get_contents, (), kw)
- def src_suffixes(self, env):
- return self.builder.src_suffixes(env)
+ def src_suffixes(self, env, args):
+ return self.builder.src_suffixes(env, args)
def targets(self, node):
"""Return the list of targets for this builder instance.
@@ -240,112 +339,55 @@ class MultiStepBuilder(BuilderBase):
node_factory = SCons.Node.FS.default_fs.File,
target_factory = None,
source_factory = None,
- scanner=None):
+ scanner=None,
+ emitter=None):
BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
node_factory, target_factory, source_factory,
- scanner)
+ scanner, emitter)
+ if not SCons.Util.is_List(src_builder):
+ src_builder = [ src_builder ]
self.src_builder = src_builder
+ self.sdict = {}
- def __call__(self, env, target = None, source = None):
+ def __call__(self, env, target = None, source = None, **kw):
slist = SCons.Node.arg2nodes(source, self.source_factory)
final_sources = []
- src_suffix = env.subst(self.src_suffix)
- sdict = {}
- for suff in self.src_builder.src_suffixes(env):
- sdict[suff] = None
+
+ r=repr(env)
+ try:
+ sdict = self.sdict[r]
+ except KeyError:
+ sdict = {}
+ self.sdict[r] = sdict
+ for bld in self.src_builder:
+ for suf in bld.src_suffixes(env, kw):
+ sdict[suf] = bld
+
for snode in slist:
path, ext = os.path.splitext(snode.abspath)
if sdict.has_key(ext):
- tgt = self.src_builder(env, target = [ path ], source = snode)
+ src_bld = sdict[ext]
+
+ dictArgs = copy.copy(kw)
+ dictArgs['target'] = [path]
+ dictArgs['source'] = snode
+ dictArgs['env'] = env
+ tgt = apply(src_bld, (), dictArgs)
if not SCons.Util.is_List(tgt):
final_sources.append(tgt)
else:
final_sources.extend(tgt)
else:
final_sources.append(snode)
- return BuilderBase.__call__(self, env, target=target,
- source=final_sources)
-
- def src_suffixes(self, env):
- return BuilderBase.src_suffixes(self, env) + \
- self.src_builder.src_suffixes(env)
-
-class CompositeBuilder(BuilderBase):
- """This is a convenient Builder subclass that can build different
- files based on their suffixes. For each target, this builder
- will examine the target's sources. If they are all the same
- suffix, and that suffix is equal to one of the child builders'
- src_suffix, then that child builder will be used. Otherwise,
- UserError is thrown."""
- def __init__(self, name = None,
- prefix='',
- suffix='',
- action = {},
- src_builder = []):
- BuilderBase.__init__(self, name=name, prefix=prefix,
- suffix=suffix)
- if src_builder and not SCons.Util.is_List(src_builder):
- src_builder = [src_builder]
- self.src_builder = src_builder
- self.action_dict = action
- self.sdict = {}
- self.sbuild = {}
-
- def __call__(self, env, target = None, source = None):
- tlist, slist = BuilderBase._create_nodes(self, env,
- target=target, source=source)
-
- r = repr(env)
- if not self.sdict.has_key(r):
- self.sdict[r] = {}
- self.sbuild[r] = []
- for suff in self.src_suffixes(env):
- suff = env.subst(suff)
- self.sdict[r][suff] = suff
- self.sbuild[r].extend(filter(lambda x, e=env, s=suff:
- e.subst(x.suffix) == s,
- self.src_builder))
- for sb in self.sbuild[r]:
- suff = env.subst(sb.suffix)
- for s in sb.src_suffixes(env):
- self.sdict[r][env.subst(s)] = suff
-
- sufflist = map(lambda x, s=self.sdict[r]:
- s[os.path.splitext(x.path)[1]],
- slist)
- last_suffix = ''
- for suff in sufflist:
- if last_suffix and last_suffix != suff:
- raise UserError, "The builder for %s can only build source files of identical suffixes: %s." % \
- (tlist[0].path,
- str(map(lambda t: str(t.path), tlist[0].sources)))
- last_suffix = suff
-
- if last_suffix:
- kw = {
- 'name' : self.name,
- 'action' : self.action_dict[last_suffix],
- 'src_suffix' : last_suffix,
- }
- if self.sbuild[r]:
- sb = filter(lambda x, e=env, s=last_suffix:
- e.subst(x.suffix) == s,
- self.sbuild[r])
- if sb:
- kw['src_builder'] = sb[0]
- # XXX We should be able to cache this
- bld = apply(Builder, (), kw)
- for tnode in tlist:
- bld.__call__(env, target = tnode, source = slist)
-
- if len(tlist) == 1:
- tlist = tlist[0]
- return tlist
-
- def src_suffixes(self, env):
- suffixes = map(lambda k, e=env: e.subst(k), self.action_dict.keys()) + \
- reduce(lambda x, y: x + y,
- map(lambda b, e=env: b.src_suffixes(e),
- self.src_builder),
- [])
- return suffixes
+ dictKwArgs = kw
+ dictKwArgs['target'] = target
+ dictKwArgs['source'] = final_sources
+ return apply(BuilderBase.__call__,
+ (self, env), dictKwArgs)
+
+ def src_suffixes(self, env, args):
+ return BuilderBase.src_suffixes(self, env, args) + \
+ reduce(lambda x, y: x + y,
+ map(lambda b, e=env, args=args: b.src_suffixes(e, args),
+ self.src_builder),
+ [])
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 0527fe0..e30079c 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -82,6 +82,11 @@ class Environment:
return self.d.get(s, s)
def get_scanner(self, ext):
return env_scanner
+ def Dictionary(self):
+ return {}
+ def autogenerate(self, dir=''):
+ return {}
+
env = Environment()
class BuilderTestCase(unittest.TestCase):
@@ -129,20 +134,23 @@ class BuilderTestCase(unittest.TestCase):
assert target.sources[0].name == 'n10'
assert target.sources[1].name == 'n11'
- if hasattr(types, 'UnicodeType'):
- code = """if 1:
- targets = builder(env, target = u'n12 n13', source = [u'n14 n15'])
- assert targets[0].name == u'n12'
- assert targets[0].sources[0].name == u'n14 n15'
- assert targets[1].name == u'n13'
- assert targets[1].sources[0].name == u'n14 n15'
-
- target = builder(env, target = [u'n16 n17'], source = u'n18 n19')
- assert target.name == u'n16 n17'
- assert target.sources[0].name == u'n18'
- assert target.sources[1].name == u'n19'
- \n"""
- exec code
+ if not hasattr(types, 'UnicodeType'):
+ uni = str
+ else:
+ uni = unicode
+
+ targets = builder(env, target = uni('n12 n13'),
+ source = [uni('n14 n15')])
+ assert targets[0].name == uni('n12')
+ assert targets[0].sources[0].name == uni('n14 n15')
+ assert targets[1].name == uni('n13')
+ assert targets[1].sources[0].name == uni('n14 n15')
+
+ target = builder(env, target = [uni('n16 n17')],
+ source = uni('n18 n19'))
+ assert target.name == uni('n16 n17')
+ assert target.sources[0].name == uni('n18')
+ assert target.sources[1].name == uni('n19')
def test_noname(self):
"""Test error reporting for missing name
@@ -163,7 +171,7 @@ class BuilderTestCase(unittest.TestCase):
Verify that we can retrieve the supplied action attribute.
"""
builder = SCons.Builder.Builder(name="builder", action="foo")
- assert builder.action.command == "foo"
+ assert builder.action.cmd_list == ["foo"]
def test_generator(self):
"""Test Builder creation given a generator function."""
@@ -276,7 +284,8 @@ class BuilderTestCase(unittest.TestCase):
def my_show(string):
global show_string
show_string = show_string + string + "\n"
- builder.action.show = my_show
+ for action in builder.action.list:
+ action.show = my_show
r = builder.execute()
assert r == 0
@@ -341,7 +350,7 @@ class BuilderTestCase(unittest.TestCase):
def __init__(self, **kw):
open(kw['out'], 'a').write("class2b\n")
- builder = MyBuilder(action = [cmd2, function2, class2a(), class2b], name = "clist")
+ builder = MyBuilder(action = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]), name = "clist")
r = builder.execute(out = outfile)
assert r.__class__ == class2b
c = test.read(outfile, 'r')
@@ -384,7 +393,7 @@ class BuilderTestCase(unittest.TestCase):
contents = b2.get_contents()
assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
- b3 = SCons.Builder.Builder(name = "b3", action = ["foo", Func, "bar"])
+ b3 = SCons.Builder.Builder(name = "b3", action = SCons.Action.ListAction(["foo", Func, "bar"]))
contents = b3.get_contents()
assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
@@ -430,9 +439,9 @@ class BuilderTestCase(unittest.TestCase):
Make sure that there is no '.' separator appended.
"""
builder = SCons.Builder.Builder(name = "builder", prefix = 'lib.')
- assert builder.prefix == 'lib.'
+ assert builder.get_prefix(env,{}) == 'lib.'
builder = SCons.Builder.Builder(name = "builder", prefix = 'lib')
- assert builder.prefix == 'lib'
+ assert builder.get_prefix(env,{}) == 'lib'
tgt = builder(env, target = 'tgt1', source = 'src1')
assert tgt.path == 'libtgt1', \
"Target has unexpected name: %s" % tgt.path
@@ -451,7 +460,7 @@ class BuilderTestCase(unittest.TestCase):
env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
b1 = SCons.Builder.Builder(name = "builder", src_suffix = '.c')
- assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
+ assert b1.src_suffixes(env,{}) == ['.c'], b1.src_suffixes(env,{})
tgt = b1(env, target = 'tgt2', source = 'src2')
assert tgt.sources[0].path == 'src2.c', \
@@ -466,19 +475,19 @@ class BuilderTestCase(unittest.TestCase):
b2 = SCons.Builder.Builder(name = "b2",
src_suffix = '.2',
src_builder = b1)
- assert b2.src_suffixes(env) == ['.2', '.c'], b2.src_suffixes(env)
+ assert b2.src_suffixes(env,{}) == ['.2', '.c'], b2.src_suffixes(env,{})
b3 = SCons.Builder.Builder(name = "b3",
action = {'.3a' : '', '.3b' : ''})
- s = b3.src_suffixes(env)
+ s = b3.src_suffixes(env,{})
s.sort()
assert s == ['.3a', '.3b'], s
b4 = SCons.Builder.Builder(name = "b4", src_suffix = '$XSUFFIX')
- assert b4.src_suffixes(env) == ['.x'], b4.src_suffixes(env)
+ assert b4.src_suffixes(env,{}) == ['.x'], b4.src_suffixes(env,{})
b5 = SCons.Builder.Builder(name = "b5", action = {'$YSUFFIX' : ''})
- assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
+ assert b5.src_suffixes(env,{}) == ['.y'], b5.src_suffixes(env,{})
def test_suffix(self):
"""Test Builder creation with a specified target suffix
@@ -487,9 +496,9 @@ class BuilderTestCase(unittest.TestCase):
beginning if it isn't already present.
"""
builder = SCons.Builder.Builder(name = "builder", suffix = '.o')
- assert builder.suffix == '.o'
+ assert builder.get_suffix(env,{}) == '.o', builder.get_suffix(env,{})
builder = SCons.Builder.Builder(name = "builder", suffix = 'o')
- assert builder.suffix == '.o'
+ assert builder.get_suffix(env,{}) == '.o', builder.get_suffix(env,{})
tgt = builder(env, target = 'tgt3', source = 'src3')
assert tgt.path == 'tgt3.o', \
"Target has unexpected name: %s" % tgt[0].path
@@ -575,20 +584,24 @@ class BuilderTestCase(unittest.TestCase):
def test_CompositeBuilder(self):
"""Testing CompositeBuilder class."""
+ def func_action(target, source, env):
+ return 0
+
builder = SCons.Builder.Builder(name = "builder",
- action={ '.foo' : 'foo',
- '.bar' : 'bar' })
+ action={ '.foo' : func_action,
+ '.bar' : func_action })
- assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder, SCons.Builder.BuilderBase)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
tgt = builder(env, target='test1', source='test1.foo')
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
- assert tgt.builder.action.command == 'foo'
- tgt = builder(env, target='test2', source='test2.bar')
- assert tgt.builder.action.command == 'bar'
+ assert isinstance(tgt.builder.action.generator, SCons.Builder.DictCmdGenerator)
flag = 0
+ tgt = builder(env, target='test2', source='test2.bar test1.foo')
try:
- tgt = builder(env, target='test2', source='test2.bar test1.foo')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
@@ -601,7 +614,8 @@ class BuilderTestCase(unittest.TestCase):
action = { '.foo' : 'foo',
'.bar' : 'bar' },
src_builder = foo_bld)
- assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder, SCons.Builder.MultiStepBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
tgt = builder(env, target='t1', source='t1a.ina t1b.ina')
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
@@ -618,7 +632,8 @@ class BuilderTestCase(unittest.TestCase):
action = { '.foo' : 'foo',
'.bar' : 'bar' },
src_builder = [foo_bld, bar_bld])
- assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder, SCons.Builder.MultiStepBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')
assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
@@ -627,26 +642,33 @@ class BuilderTestCase(unittest.TestCase):
assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
flag = 0
+ tgt = builder(env, target='t5', source='test5a.foo test5b.inb')
try:
- tgt = builder(env, target='t5', source='test5a.foo test5b.inb')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
flag = 0
+ tgt = builder(env, target='t6', source='test6a.bar test6b.ina')
try:
- tgt = builder(env, target='t6', source='test6a.bar test6b.ina')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
flag = 0
+ tgt = builder(env, target='t4', source='test4a.ina test4b.inb')
try:
- tgt = builder(env, target='t4', source='test4a.ina test4b.inb')
- except SCons.Errors.UserError:
+ tgt.build()
+ except SCons.Errors.BuildError, e:
+ assert e.args[0] == SCons.Errors.UserError
flag = 1
assert flag, "UserError should be thrown when we build targets with files of different suffixes."
+
def test_build_scanner(self):
"""Testing ability to set a target scanner through a builder."""
global instanced
@@ -683,6 +705,44 @@ class BuilderTestCase(unittest.TestCase):
assert tgt.target_scanner != env_scanner, tgt.target_scanner
assert src.source_scanner == env_scanner
+ def test_Builder_Args(self):
+ """Testing passing extra agrs to a builder."""
+ def buildFunc(target, source, env, foo, bar, s=self):
+ s.foo=foo
+ s.bar=bar
+
+ builder = SCons.Builder.Builder(name="builder", action=buildFunc)
+ tgt = builder(env, target='foo', source='bar', foo=1, bar=2)
+ tgt.build()
+ assert self.foo == 1, self.foo
+ assert self.bar == 2, self.bar
+
+ def test_emitter(self):
+ """Test emitter functions."""
+ def emit(target, source, env, foo=0, bar=0):
+ if foo:
+ target.append("bar")
+ if bar:
+ source.append("foo")
+ return ( target, source )
+
+ builder = SCons.Builder.Builder(name="builder", action='foo',
+ emitter=emit)
+ tgt = builder(env, target='foo', source='bar')
+ assert str(tgt) == 'foo', str(tgt)
+ assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+ tgt = builder(env, target='foo', source='bar', foo=1)
+ assert len(tgt) == 2, len(tgt)
+ assert 'foo' in map(str, tgt), map(str, tgt)
+ assert 'bar' in map(str, tgt), map(str, tgt)
+
+ tgt = builder(env, target='foo', source='bar', bar=1)
+ assert str(tgt) == 'foo', str(tgt)
+ assert len(tgt.sources) == 2, len(tgt.sources)
+ assert 'foo' in map(str, tgt.sources), map(str, tgt.sources)
+ assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
if __name__ == "__main__":
suite = unittest.makeSuite(BuilderTestCase, 'test_')
if not unittest.TextTestRunner().run(suite).wasSuccessful():
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index 711130d..67317a3 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -40,6 +40,7 @@ import os
import stat
import string
import sys
+import os.path
import SCons.Action
import SCons.Builder
@@ -50,8 +51,33 @@ import SCons.Scanner.C
import SCons.Scanner.Prog
import SCons.Util
-
-
+class SharedCmdGenerator:
+ """A callable class that acts as a command generator.
+ It is designed to hold on to 2 actions, and return
+ one if the shared=1 keyword arg is supplied to the
+ Builder method, and the other if not.
+
+ Also, all target nodes will have the shared attribute
+ set to match the vaue of the shared keyword argument,
+ zero by default."""
+ def __init__(self, static, shared):
+ self.action_static = static
+ self.action_shared = shared
+
+ def __call__(self, target, source, env, shared=0):
+ for src in source:
+ try:
+ if src.attributes.shared != shared:
+ raise UserError("Source file: %s must be built with shared=%s in order to be compatible with the selected target." % (src, str(shared)))
+ except AttributeError:
+ pass
+ for t in target:
+ t.attributes.shared = shared
+ if shared:
+ return self.action_shared
+ else:
+ return self.action_static
+
CFile = SCons.Builder.Builder(name = 'CFile',
action = { '.l' : '$LEXCOM',
'.y' : '$YACCCOM',
@@ -64,29 +90,48 @@ CXXFile = SCons.Builder.Builder(name = 'CXXFile',
},
suffix = '$CXXFILESUFFIX')
-CPlusPlusAction = SCons.Action.Action('$CXXCOM')
-
-FortranAction = SCons.Action.Action('$F77COM')
-
-FortranPPAction = SCons.Action.Action('$F77PPCOM')
-
+CXXAction = SCons.Action.Action("$CXXCOM")
+ShCXXAction = SCons.Action.Action("$SHCXXCOM")
+F77Action = SCons.Action.Action("$F77COM")
+ShF77Action = SCons.Action.Action("$SHF77COM")
+F77PPAction = SCons.Action.Action("$F77PPCOM")
+ShF77PPAction = SCons.Action.Action("$SHF77PPCOM")
+
+shared_obj = SCons.Builder.DictCmdGenerator({ ".C" : ShCXXAction,
+ ".cc" : ShCXXAction,
+ ".cpp" : ShCXXAction,
+ ".cxx" : ShCXXAction,
+ ".c++" : ShCXXAction,
+ ".C++" : ShCXXAction,
+ ".c" : "$SHCCCOM",
+ ".f" : ShF77Action,
+ ".for" : ShF77Action,
+ ".FOR" : ShF77Action,
+ ".F" : ShF77PPAction,
+ ".fpp" : ShF77PPAction,
+ ".FPP" : ShF77PPAction })
+
+static_obj = SCons.Builder.DictCmdGenerator({ ".C" : CXXAction,
+ ".cc" : CXXAction,
+ ".cpp" : CXXAction,
+ ".cxx" : CXXAction,
+ ".c++" : CXXAction,
+ ".C++" : CXXAction,
+ ".c" : "$CCCOM",
+ ".f" : F77Action,
+ ".for" : F77Action,
+ ".F" : F77PPAction,
+ ".FOR" : F77Action,
+ ".fpp" : F77PPAction,
+ ".FPP" : F77PPAction })
+
Object = SCons.Builder.Builder(name = 'Object',
- action = { '.c' : '$CCCOM',
- '.C' : CPlusPlusAction,
- '.cc' : CPlusPlusAction,
- '.cpp' : CPlusPlusAction,
- '.cxx' : CPlusPlusAction,
- '.c++' : CPlusPlusAction,
- '.C++' : CPlusPlusAction,
- '.f' : FortranAction,
- '.for' : FortranAction,
- '.FOR' : FortranAction,
- '.F' : FortranPPAction,
- '.fpp' : FortranPPAction,
- '.FPP' : FortranPPAction,
- },
+ generator = \
+ SharedCmdGenerator(static=SCons.Action.CommandGeneratorAction(static_obj),
+ shared=SCons.Action.CommandGeneratorAction(shared_obj)),
prefix = '$OBJPREFIX',
suffix = '$OBJSUFFIX',
+ src_suffix = static_obj.src_suffixes(),
src_builder = [CFile, CXXFile])
Program = SCons.Builder.Builder(name = 'Program',
@@ -97,12 +142,91 @@ Program = SCons.Builder.Builder(name = 'Program',
src_builder = Object,
scanner = SCons.Scanner.Prog.ProgScan())
-Library = SCons.Builder.Builder(name = 'Library',
- action = '$ARCOM',
- prefix = '$LIBPREFIX',
- suffix = '$LIBSUFFIX',
- src_suffix = '$OBJSUFFIX',
- src_builder = Object)
+class LibAffixGenerator:
+ def __init__(self, static, shared):
+ self.static_affix = static
+ self.shared_affix = shared
+
+ def __call__(self, shared=0, win32=0):
+ if shared:
+ return self.shared_affix
+ return self.static_affix
+
+def win32LibGenerator(target, source, env, shared=1):
+ listCmd = [ "$SHLINK", "$SHLINKFLAGS" ]
+
+ for tgt in target:
+ ext = os.path.splitext(str(tgt))[1]
+ if ext == env.subst("$LIBSUFFIX"):
+ # Put it on the command line as an import library.
+ listCmd.append("${WIN32IMPLIBPREFIX}%s" % tgt)
+ else:
+ listCmd.append("${WIN32DLLPREFIX}%s" % tgt)
+
+ listCmd.extend([ '$_LIBDIRFLAGS', '$_LIBFLAGS' ])
+ for src in source:
+ ext = os.path.splitext(str(src))[1]
+ if ext == env.subst("$WIN32DEFSUFFIX"):
+ # Treat this source as a .def file.
+ listCmd.append("${WIN32DEFPREFIX}%s" % src)
+ else:
+ # Just treat it as a generic source file.
+ listCmd.append(str(src))
+ return listCmd
+
+def win32LibEmitter(target, source, env, shared=0):
+ if shared:
+ dll = None
+ for tgt in target:
+ ext = os.path.splitext(str(tgt))[1]
+ if ext == env.subst("$SHLIBSUFFIX"):
+ dll = tgt
+ break
+ if not dll:
+ raise UserError("A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX"))
+
+ if env.has_key("WIN32_INSERT_DEF") and \
+ env["WIN32_INSERT_DEF"] and \
+ not '.def' in map(lambda x: os.path.split(str(x))[1],
+ source):
+
+ # append a def file to the list of sources
+ source.append("%s%s" % (os.path.splitext(str(dll))[0],
+ env.subst("$WIN32DEFSUFFIX")))
+ if not env.subst("$LIBSUFFIX") in \
+ map(lambda x: os.path.split(str(x))[1], target):
+ # Append an import library to the list of targets.
+ target.append("%s%s%s" % (env.subst("$LIBPREFIX"),
+ os.path.splitext(str(dll))[0],
+ env.subst("$LIBSUFFIX")))
+ return (target, source)
+
+PosixLibrary = SCons.Builder.Builder(name = 'Library',
+ generator = \
+ SharedCmdGenerator(shared="$SHLINKCOM",
+ static="$ARCOM"),
+ prefix = \
+ LibAffixGenerator(static='$LIBPREFIX',
+ shared='$SHLIBPREFIX'),
+ suffix = \
+ LibAffixGenerator(static='$LIBSUFFIX',
+ shared='$SHLIBSUFFIX'),
+ src_suffix = '$OBJSUFFIX',
+ src_builder = Object)
+
+Win32Library = SCons.Builder.Builder(name = 'Library',
+ generator = \
+ SharedCmdGenerator(shared=SCons.Action.CommandGeneratorAction(win32LibGenerator),
+ static="$ARCOM"),
+ emitter = win32LibEmitter,
+ prefix = \
+ LibAffixGenerator(static='$LIBPREFIX',
+ shared='$SHLIBPREFIX'),
+ suffix = \
+ LibAffixGenerator(static='$LIBSUFFIX',
+ shared='$SHLIBSUFFIX'),
+ src_suffix = '$OBJSUFFIX',
+ src_builder = Object)
LaTeXAction = SCons.Action.Action('$LATEXCOM')
@@ -244,21 +368,36 @@ def make_win32_env_from_paths(include, lib, path):
'CC' : 'cl',
'CCFLAGS' : '/nologo',
'CCCOM' : '$CC $CCFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+ 'SHCC' : '$CC',
+ 'SHCCFLAGS' : '$CCFLAGS',
+ 'SHCCCOM' : '$SHCC $SHCCFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
'CFILESUFFIX' : '.c',
'CXX' : '$CC',
'CXXFLAGS' : '$CCFLAGS',
'CXXCOM' : '$CXX $CXXFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
+ 'SHCXX' : '$CXX',
+ 'SHCXXFLAGS' : '$CXXFLAGS',
+ 'SHCXXCOM' : '$SHCXX $SHCXXFLAGS $CPPFLAGS $_INCFLAGS /c $SOURCES /Fo$TARGET',
'CXXFILESUFFIX' : '.cc',
'F77' : 'g77',
'F77FLAGS' : '',
'F77COM' : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77' : '$F77',
+ 'SHF77FLAGS' : '$F77FLAGS',
+ 'SHF77COM' : '$SHF77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'LINK' : 'link',
'LINKFLAGS' : '/nologo',
'LINKCOM' : '$LINK $LINKFLAGS /OUT:$TARGET $_LIBDIRFLAGS $_LIBFLAGS $SOURCES',
+ 'SHLINK' : '$LINK',
+ 'SHLINKFLAGS': '$LINKFLAGS /dll',
+ 'SHLINKCOM' : '$SHLINK $SHLINKFLAGS /OUT:$TARGET $_LIBDIRFLAGS $_LIBFLAGS $SOURCES',
'AR' : 'lib',
'ARFLAGS' : '/nologo',
'ARCOM' : '$AR $ARFLAGS /OUT:$TARGET $SOURCES',
+ 'SHLIBPREFIX': '',
+ 'SHLIBSUFFIX': '.dll',
'LEX' : 'lex',
'LEXFLAGS' : '',
'LEXCOM' : '$LEX $LEXFLAGS -t $SOURCES > $TARGET',
@@ -281,7 +420,7 @@ def make_win32_env_from_paths(include, lib, path):
'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
'PSPREFIX' : '',
'PSSUFFIX' : '.ps',
- 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Library, Object,
+ 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Win32Library, Object,
PDF, PostScript, Program],
'SCANNERS' : [CScan],
'OBJPREFIX' : '',
@@ -289,13 +428,20 @@ def make_win32_env_from_paths(include, lib, path):
'PROGPREFIX' : '',
'PROGSUFFIX' : '.exe',
'LIBPREFIX' : '',
+ 'LIBPREFIXES': '$LIBPREFIX',
'LIBSUFFIX' : '.lib',
+ 'LIBSUFFIXES': '$LIBSUFFIX',
'LIBDIRPREFIX' : '/LIBPATH:',
'LIBDIRSUFFIX' : '',
'LIBLINKPREFIX' : '',
'LIBLINKSUFFIX' : '$LIBSUFFIX',
'INCPREFIX' : '/I',
'INCSUFFIX' : '',
+ 'WIN32DEFPREFIX' : '/def:',
+ 'WIN32DEFSUFFIX' : '.def',
+ 'WIN32DLLPREFIX' : '/out:',
+ 'WIN32IMPLIBPREFIX' : '/implib:',
+ 'WIN32_INSERT_DEF' : 1,
'ENV' : {
'INCLUDE' : include,
'LIB' : lib,
@@ -316,7 +462,8 @@ def make_win32_env(version):
if os.name == 'posix':
-
+ Library = PosixLibrary
+
arcom = '$AR $ARFLAGS $TARGET $SOURCES'
ranlib = 'ranlib'
if SCons.Util.WhereIs(ranlib):
@@ -326,23 +473,41 @@ if os.name == 'posix':
'CC' : 'cc',
'CCFLAGS' : '',
'CCCOM' : '$CC $CCFLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHCC' : '$CC',
+ 'SHCCFLAGS' : '$CCFLAGS -fPIC',
+ 'SHCCCOM' : '$SHCC $SHCCFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'CFILESUFFIX' : '.c',
'CXX' : 'c++',
'CXXFLAGS' : '$CCFLAGS',
'CXXCOM' : '$CXX $CXXFLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'CXXFILESUFFIX' : '.cc',
+ 'SHCXX' : '$CXX',
+ 'SHCXXFLAGS' : '$CXXFLAGS -fPIC',
+ 'SHCXXCOM' : '$SHCXX $SHCXXFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'F77' : 'g77',
'F77FLAGS' : '',
'F77COM' : '$F77 $F77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'F77PPCOM' : '$F77 $F77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77FLAGS' : '$F77FLAGS -fPIC',
+ 'SHF77COM' : '$F77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77PPCOM' : '$F77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77' : '$F77',
+ 'SHF77FLAGS' : '$F77FLAGS -fPIC',
+ 'SHF77COM' : '$SHF77 $SHF77FLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
+ 'SHF77PPCOM' : '$SHF77 $SHF77FLAGS $CPPFLAGS $_INCFLAGS -c -o $TARGET $SOURCES',
'LINK' : '$CXX',
'LINKFLAGS' : '',
'LINKCOM' : '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
+ 'SHLINK' : '$LINK',
+ 'SHLINKFLAGS': '$LINKFLAGS -shared',
+ 'SHLINKCOM' : '$SHLINK $SHLINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS',
'AR' : 'ar',
'ARFLAGS' : 'r',
'RANLIB' : ranlib,
'RANLIBFLAGS' : '',
'ARCOM' : arcom,
+ 'SHLIBPREFIX': '$LIBPREFIX',
+ 'SHLIBSUFFIX': '.so',
'LEX' : 'lex',
'LEXFLAGS' : '',
'LEXCOM' : '$LEX $LEXFLAGS -t $SOURCES > $TARGET',
@@ -363,7 +528,7 @@ if os.name == 'posix':
'PSCOM' : '$DVIPS $DVIPSFLAGS -o $TARGET $SOURCES',
'PSPREFIX' : '',
'PSSUFFIX' : '.ps',
- 'BUILDERS' : [Alias, CFile, CXXFile, DVI, Library, Object,
+ 'BUILDERS' : [Alias, CFile, CXXFile, DVI, PosixLibrary, Object,
PDF, PostScript, Program],
'SCANNERS' : [CScan],
'OBJPREFIX' : '',
@@ -371,7 +536,9 @@ if os.name == 'posix':
'PROGPREFIX' : '',
'PROGSUFFIX' : (sys.platform == 'cygwin') and '.exe' or '',
'LIBPREFIX' : 'lib',
+ 'LIBPREFIXES': '$LIBPREFIX',
'LIBSUFFIX' : '.a',
+ 'LIBSUFFIXES': [ '$LIBSUFFIX', '$SHLIBSUFFIX' ],
'LIBDIRPREFIX' : '-L',
'LIBDIRSUFFIX' : '',
'LIBLINKPREFIX' : '-l',
@@ -382,6 +549,8 @@ if os.name == 'posix':
}
elif os.name == 'nt':
+ Library = Win32Library
+
versions = None
try:
versions = get_devstudio_versions()
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 04bf26d..b0d1457 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -167,8 +167,9 @@ class Environment:
self.env = env
self.builder = builder
- def __call__(self, target = None, source = None):
- return self.builder(self.env, target, source)
+ def __call__(self, target = None, source = None, **kw):
+ return apply(self.builder, (self.env, target, source),
+ kw)
# This allows a Builder to be executed directly
# through the Environment to which it's attached.
@@ -238,6 +239,9 @@ class Environment:
def __delitem__(self, key):
del self._dict[key]
+ def has_key(self, key):
+ return self._dict.has_key(key)
+
def Command(self, target, source, action):
"""Builds the supplied target files from the supplied
source files using the supplied action. Action may
@@ -282,6 +286,11 @@ class Environment:
"""
return SCons.Util.scons_subst(string, self._dict, {})
+ def subst_list(self, string):
+ """Calls through to SCons.Util.scons_subst_list(). See
+ the documentation for that function."""
+ return SCons.Util.scons_subst_list(string, self._dict, {})
+
def get_scanner(self, skey):
"""Find the appropriate scanner given a key (usually a file suffix).
Does a linear search. Could be sped up by creating a dictionary if
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index 654d432..24db460 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -296,7 +296,7 @@ class EnvironmentTestCase(unittest.TestCase):
action='buildfoo $target $source')
assert t.builder
assert t.builder.action.__class__.__name__ == 'CommandAction'
- assert t.builder.action.command == 'buildfoo $target $source'
+ assert t.builder.action.cmd_list == ['buildfoo', '$target', '$source']
assert 'foo1.in' in map(lambda x: x.path, t.sources)
assert 'foo2.in' in map(lambda x: x.path, t.sources)
@@ -328,6 +328,10 @@ class EnvironmentTestCase(unittest.TestCase):
str = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
assert str == "c c", str
+ env = Environment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
+ lst = env.subst_list([ "$AAA", "B $CCC" ])
+ assert lst == [ [ "a", "b" ], [ "c", "B a", "b" ], [ "c" ] ], lst
+
def test_autogenerate(dict):
"""Test autogenerating variables in a dictionary."""
env = Environment(LIBS = [ 'foo', 'bar', 'baz' ],
diff --git a/src/engine/SCons/Node/NodeTests.py b/src/engine/SCons/Node/NodeTests.py
index 447ef93..fac20e3 100644
--- a/src/engine/SCons/Node/NodeTests.py
+++ b/src/engine/SCons/Node/NodeTests.py
@@ -40,12 +40,13 @@ cycle_detected = None
class Builder:
def execute(self, **kw):
- global built_it, built_target, built_source
- built_it = 1
+ global built_it, built_target, built_source, built_args
+ built_it = 1
built_target = kw['target']
built_source = kw['source']
+ built_args = kw
return 0
- def get_contents(self, env):
+ def get_contents(self, env, target, source):
return 7
class NoneBuilder(Builder):
@@ -160,11 +161,14 @@ class NodeTestCase(unittest.TestCase):
node.env_set(Environment())
node.path = "qqq"
node.sources = ["rrr", "sss"]
+ node.build_args = { "foo" : 1, "bar" : 2 }
node.build()
assert built_it
assert type(built_target) == type(MyNode()), type(built_target)
assert str(built_target) == "qqq", str(built_target)
assert built_source == ["rrr", "sss"], built_source
+ assert built_args["foo"] == 1, built_args
+ assert built_args["bar"] == 2, built_args
fff = MyNode()
ggg = MyNode()
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index c8d5b2c..db52b3e 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -62,6 +62,9 @@ class Node:
build, or use to build other Nodes.
"""
+ class Attrs:
+ pass
+
def __init__(self):
self.sources = [] # source files used to build node
self.depends = [] # explicit dependencies (from Depends)
@@ -80,6 +83,22 @@ class Node:
self.precious = None
self.found_includes = {}
self.includes = None
+ self.build_args = {}
+ self.attributes = self.Attrs() # Generic place to stick information about the Node.
+
+ def generate_build_args(self):
+ dict = copy.copy(self.env.Dictionary())
+ if hasattr(self, 'dir'):
+ auto = self.env.autogenerate(dir = self.dir)
+ else:
+ auto = self.env.autogenerate()
+ dict.update(auto)
+
+ dictArgs = { 'env' : dict,
+ 'target' : self,
+ 'source' : self.sources }
+ dictArgs.update(self.build_args)
+ return dictArgs
def build(self):
"""Actually build the node. Return the status from the build."""
@@ -93,15 +112,8 @@ class Node:
stat = self.builder.status
except AttributeError:
try:
- dict = copy.copy(self.env.Dictionary())
- if hasattr(self, 'dir'):
- auto = self.env.autogenerate(dir = self.dir)
- else:
- auto = self.env.autogenerate()
- dict.update(auto)
- stat = self.builder.execute(env = dict,
- target = self,
- source = self.sources)
+ stat = apply(self.builder.execute, (),
+ self.generate_build_args())
except:
raise BuildError(self, "Exception",
sys.exc_type,
@@ -158,13 +170,8 @@ class Node:
def __init__(self, node):
self.node = node
def get_contents(self):
- dict = self.node.env.Dictionary()
- dict.update(self.node.env.autogenerate())
- try:
- dir = self.node.getcwd()
- except AttributeError:
- dir = None
- return self.node.builder.get_contents(env = dict)
+ return apply(self.node.builder.get_contents, (),
+ self.node.generate_build_args())
return Adapter(self)
def get_implicit_deps(self, env, scanner, target):
diff --git a/src/engine/SCons/Scanner/Prog.py b/src/engine/SCons/Scanner/Prog.py
index 76567ab..818aa86 100644
--- a/src/engine/SCons/Scanner/Prog.py
+++ b/src/engine/SCons/Scanner/Prog.py
@@ -66,14 +66,21 @@ def scan(node, env, target, fs):
libs = string.split(libs)
try:
- prefix = env.Dictionary('LIBPREFIX')
+ prefix = env.Dictionary('LIBPREFIXES')
+ if not SCons.Util.is_List(prefix):
+ prefix = [ prefix ]
except KeyError:
- prefix = ''
+ prefix = [ '' ]
try:
- suffix = env.Dictionary('LIBSUFFIX')
+ suffix = env.Dictionary('LIBSUFFIXES')
+ if not SCons.Util.is_List(suffix):
+ suffix = [ suffix ]
except KeyError:
- suffix = ''
+ suffix = [ '' ]
- libs = map(lambda x, s=suffix, p=prefix: p + x + s, libs)
- return SCons.Node.FS.find_files(libs, libpath, fs.File)
+ ret = []
+ for suf in map(env.subst, suffix):
+ for pref in map(env.subst, prefix):
+ ret.extend(map(lambda x, s=suf, p=pref: p + x + s, libs))
+ return SCons.Node.FS.find_files(ret, libpath, fs.File)
diff --git a/src/engine/SCons/Scanner/ProgTests.py b/src/engine/SCons/Scanner/ProgTests.py
index ea3d00d..75ce697 100644
--- a/src/engine/SCons/Scanner/ProgTests.py
+++ b/src/engine/SCons/Scanner/ProgTests.py
@@ -49,7 +49,7 @@ class DummyTarget:
class DummyEnvironment:
def __init__(self, **kw):
self._dict = kw
- self._dict['LIBSUFFIX'] = '.lib'
+ self._dict['LIBSUFFIXES'] = '.lib'
def Dictionary(self, *args):
if not args:
@@ -67,6 +67,9 @@ class DummyEnvironment:
def __delitem__(self,key):
del self.Dictionary()[key]
+ def subst(self, s):
+ return s
+
def deps_match(deps, libs):
deps=map(str, deps)
deps.sort()
diff --git a/src/engine/SCons/Sig/MD5.py b/src/engine/SCons/Sig/MD5.py
index c2331bd..251c1eb 100644
--- a/src/engine/SCons/Sig/MD5.py
+++ b/src/engine/SCons/Sig/MD5.py
@@ -78,12 +78,9 @@ def collect(signatures):
def signature(obj):
"""Generate a signature for an object
"""
- try:
- contents = str(obj.get_contents())
- except AttributeError, e:
- raise AttributeError, \
- "unable to fetch contents of '%s': %s" % (str(obj), e)
- return hexdigest(md5.new(contents).digest())
+ if not hasattr(obj, 'get_contents'):
+ raise AttributeError, "unable to fetch contents of '%s'" % str(obj)
+ return hexdigest(md5.new(str(obj.get_contents())).digest())
def to_string(signature):
"""Convert a signature to a string"""
diff --git a/src/engine/SCons/Sig/MD5Tests.py b/src/engine/SCons/Sig/MD5Tests.py
index ecdb920..b5578b3 100644
--- a/src/engine/SCons/Sig/MD5Tests.py
+++ b/src/engine/SCons/Sig/MD5Tests.py
@@ -84,8 +84,7 @@ class MD5TestCase(unittest.TestCase):
try:
signature('string')
except AttributeError, e:
- # the error string should begin with "unable to fetch contents of 'string': "
- assert string.find(str(e), "unable to fetch contents of 'string':") == 0
+ assert string.find(str(e), "unable to fetch contents") == 0, str(e)
else:
raise AttributeError, "unexpected get_contents() attribute"
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index 3612b9a..1f27ff8 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -153,6 +153,13 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
the argv array that should be passed to a spawn or exec
function.
+ Also, this method can accept a list of strings as input
+ to strSubst, which explicitly denotes the command line
+ arguments. This is useful if you want to pass in
+ command line arguments with spaces or newlines in them.
+ Otheriwise, if you just passed in a string, they would
+ get split along the spaces and newlines.
+
One important thing this guy does is preserve environment
variables that are lists. For instance, if you have
an environment variable that is a Python list (or UserList-
@@ -170,16 +177,23 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
if e is None:
s = ''
elif is_List(e):
- s = string.join(map(str, e), '\0')
+ s = string.join(map(to_String, e), '\0')
else:
- s = _space_sep.sub('\0', str(e))
+ s = _space_sep.sub('\0', to_String(e))
except NameError:
s = ''
return s
n = 1
- # Tokenize the original string...
- strSubst = _space_sep.sub('\0', str(strSubst))
+ if is_List(strSubst):
+ # This looks like our input is a list of strings,
+ # as explained in the docstring above. Munge
+ # it into a tokenized string by concatenating
+ # the list with nulls.
+ strSubst = string.join(strSubst, '\0')
+ else:
+ # Tokenize the original string...
+ strSubst = _space_sep.sub('\0', to_String(strSubst))
# Now, do the substitution
while n != 0:
@@ -249,6 +263,14 @@ def is_Dict(e):
def is_List(e):
return type(e) is types.ListType or isinstance(e, UserList.UserList)
+def to_String(s):
+ """Better than str() because it will preserve a unicode
+ object without converting it to ASCII."""
+ if is_String(s):
+ return s
+ else:
+ return str(s)
+
def argmunge(arg):
"""This function converts a string or list into a list of strings or Nodes.
It follows the rules outlined in the SCons design document by accepting
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index 8be22f8..fdffe4d 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -93,7 +93,9 @@ class UtilTestCase(unittest.TestCase):
assert newcom == cvt("test %s/foo/bar.exe"%os.getcwd()), newcom
newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
- assert newcom == cvt("test %s/foo/blah.cpp /bar/ack.cpp %s/foo/ack.c"%(os.getcwd(),os.path.normpath(os.getcwd()+"/.."))), newcom
+ assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(os.getcwd(),
+ os.path.abspath(os.path.normpath("/bar/ack.cpp")),
+ os.path.normpath(os.getcwd()+"/.."))), newcom
newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
assert newcom == cvt("test %s/foo/blah.cpp"%os.getcwd()), newcom
@@ -160,12 +162,24 @@ class UtilTestCase(unittest.TestCase):
assert cmd_list[1][0] == 'after', cmd_list[1][0]
assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
+ # Test inputting a list to scons_subst_list()
+ cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
+ "This is a test" ],
+ loc, {})
+ assert len(cmd_list) == 2, len(cmd_list)
+ assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
+ assert cmd_list[1][0] == cvt("after"), cmd_list[1]
+ assert cmd_list[1][4] == "This is a test", cmd_list[1]
+
glob = { 'a' : 1, 'b' : 2 }
loc = {'a' : 3, 'c' : 4 }
cmd_list = scons_subst_list("test $a $b $c $d test", glob, loc)
assert len(cmd_list) == 1, cmd_list
assert cmd_list[0] == ['test', '3', '2', '4', 'test'], cmd_list
+
+
+
def test_render_tree(self):
class Node:
def __init__(self, name, children=[]):
@@ -236,6 +250,36 @@ class UtilTestCase(unittest.TestCase):
assert not is_String({})
assert not is_String([])
+ def test_to_String(self):
+ """Test the to_String() method."""
+ assert to_String(1) == "1", to_String(1)
+ assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
+ assert to_String("foo") == "foo", to_String("foo")
+
+ try:
+ import UserString
+
+ s1=UserString.UserString('blah')
+ assert to_String(s1) is s1, s1
+ assert to_String(s1) == 'blah', s1
+
+ class Derived(UserString.UserString):
+ pass
+ s2 = Derived('foo')
+ assert to_String(s2) is s2, s2
+ assert to_String(s2) == 'foo', s2
+
+ if hasattr(types, 'UnicodeType'):
+ s3=UserString.UserString(unicode('bar'))
+ assert to_String(s3) is s3, s3
+ assert to_String(s3) == unicode('bar'), s3
+ except ImportError:
+ pass
+
+ if hasattr(types, 'UnicodeType'):
+ s4 = unicode('baz')
+ assert to_String(s4) == unicode('baz'), to_String(s4)
+
def test_WhereIs(self):
test = TestCmd.TestCmd(workdir = '')