summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-03-13 14:18:35 (GMT)
committerSteven Knight <knight@baldmt.com>2003-03-13 14:18:35 (GMT)
commit71a49faea8b046709ef816aee7b7bb5f9e4a1ccc (patch)
tree5ebbb790d2e1f160925b8a749029572076e2613b /src/engine/SCons
parent739a3fa86075710448e66e6488135b65a9e3f9c4 (diff)
downloadSCons-71a49faea8b046709ef816aee7b7bb5f9e4a1ccc.zip
SCons-71a49faea8b046709ef816aee7b7bb5f9e4a1ccc.tar.gz
SCons-71a49faea8b046709ef816aee7b7bb5f9e4a1ccc.tar.bz2
Support using construction variables as re-usable, callable command generators. (Charles Crain)
Diffstat (limited to 'src/engine/SCons')
-rw-r--r--src/engine/SCons/Action.py85
-rw-r--r--src/engine/SCons/ActionTests.py95
-rw-r--r--src/engine/SCons/BuilderTests.py2
-rw-r--r--src/engine/SCons/Defaults.py40
-rw-r--r--src/engine/SCons/Environment.py43
-rw-r--r--src/engine/SCons/EnvironmentTests.py23
-rw-r--r--src/engine/SCons/Platform/win32.py50
-rw-r--r--src/engine/SCons/Tool/gnulink.py2
-rw-r--r--src/engine/SCons/Tool/linkloc.py15
-rw-r--r--src/engine/SCons/Tool/mslib.py11
-rw-r--r--src/engine/SCons/Tool/mslink.py77
-rw-r--r--src/engine/SCons/Tool/sgilink.py2
-rw-r--r--src/engine/SCons/Util.py161
-rw-r--r--src/engine/SCons/UtilTests.py298
14 files changed, 474 insertions, 430 deletions
diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py
index ecad9b0..45f4c98 100644
--- a/src/engine/SCons/Action.py
+++ b/src/engine/SCons/Action.py
@@ -126,8 +126,7 @@ def _do_create_action(act, strfunction=_null, varlist=[]):
# like a function or a CommandGenerator in that variable
# instead of a string.
return CommandGeneratorAction(LazyCmdGenerator(var))
- listCmds = map(lambda x: CommandAction(string.split(x)),
- string.split(act, '\n'))
+ listCmds = map(lambda x: CommandAction(x), string.split(act, '\n'))
if len(listCmds) == 1:
return listCmds[0]
else:
@@ -161,49 +160,6 @@ class ActionBase:
def get_actions(self):
return [self]
- def subst_dict(self, target, source, env):
- """Create a dictionary for substitution of construction
- variables.
-
- This translates the following special arguments:
-
- env - the construction environment itself,
- the values of which (CC, CCFLAGS, etc.)
- are copied straight into the dictionary
-
- target - the target (object or array of objects),
- used to generate the TARGET and TARGETS
- construction variables
-
- source - the source (object or array of objects),
- used to generate the SOURCES and SOURCE
- construction variables
- """
-
- dict = {}
-
- for k,v in env.items(): dict[k] = v
-
- if not SCons.Util.is_List(target):
- target = [target]
-
- dict['TARGETS'] = SCons.Util.PathList(map(os.path.normpath, map(str, target)))
- if dict['TARGETS']:
- dict['TARGET'] = dict['TARGETS'][0]
-
- def rstr(x):
- try:
- return x.rstr()
- except AttributeError:
- return str(x)
- if not SCons.Util.is_List(source):
- source = [source]
- dict['SOURCES'] = SCons.Util.PathList(map(os.path.normpath, map(rstr, source)))
- if dict['SOURCES']:
- dict['SOURCE'] = dict['SOURCES'][0]
-
- return dict
-
def __add__(self, other):
return _actionAppend(self, other)
@@ -226,11 +182,14 @@ _remove = re.compile(r'\$\(([^\$]|\$[^\(])*?\$\)')
class CommandAction(ActionBase):
"""Class for command-execution actions."""
def __init__(self, cmd):
+ # Cmd list can actually be a list or a single item...basically
+ # anything that we could pass in as the first arg to
+ # scons_subst_list().
self.cmd_list = cmd
def strfunction(self, target, source, env):
- dict = self.subst_dict(target, source, env)
- cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
+ cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env, _rm,
+ target, source)
return map(_string_from_cmd_list, cmd_list)
def __call__(self, target, source, env):
@@ -256,15 +215,15 @@ class CommandAction(ActionBase):
else:
raise SCons.Errors.UserError('Missing SPAWN construction variable.')
- dict = self.subst_dict(target, source, env)
- cmd_list = SCons.Util.scons_subst_list(self.cmd_list, dict, {}, _rm)
+ cmd_list = SCons.Util.scons_subst_list(self.cmd_list, env, _rm,
+ target, source)
for cmd_line in cmd_list:
if len(cmd_line):
if print_actions:
self.show(_string_from_cmd_list(cmd_line))
if execute_actions:
try:
- ENV = dict['ENV']
+ ENV = env['ENV']
except KeyError:
global default_ENV
if not default_ENV:
@@ -280,16 +239,6 @@ class CommandAction(ActionBase):
return ret
return 0
- def _sig_dict(self, target, source, env):
- """Supply a dictionary for use in computing signatures.
-
- For signature purposes, it doesn't matter what targets or
- sources we use, so long as we use the same ones every time
- so the signature stays the same. We supply an array of two
- of each to allow for distinction between TARGET and TARGETS.
- """
- return self.subst_dict(['__t1__', '__t2__'], ['__s1__', '__s2__'], env)
-
def get_raw_contents(self, target, source, env):
"""Return the complete contents of this action's command line.
"""
@@ -303,9 +252,11 @@ class CommandAction(ActionBase):
#return SCons.Util.scons_subst(string.join(self.cmd_list),
# self.subst_dict(target, source, env),
# {})
- return SCons.Util.scons_subst(string.join(self.cmd_list),
- env.sig_dict(),
- {})
+ cmd = self.cmd_list
+ if not SCons.Util.is_List(cmd):
+ cmd = [ cmd ]
+ return SCons.Util.scons_subst(string.join(map(str, cmd)),
+ env)
def get_contents(self, target, source, env):
"""Return the signature contents of this action's command line.
@@ -324,9 +275,11 @@ class CommandAction(ActionBase):
# self.subst_dict(target, source, env),
# {},
# _remove)
- return SCons.Util.scons_subst(string.join(map(str, self.cmd_list)),
- env.sig_dict(),
- {},
+ cmd = self.cmd_list
+ if not SCons.Util.is_List(cmd):
+ cmd = [ cmd ]
+ return SCons.Util.scons_subst(string.join(map(str, cmd)),
+ env,
_remove)
class CommandGeneratorAction(ActionBase):
diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py
index de157ae..eedc865 100644
--- a/src/engine/SCons/ActionTests.py
+++ b/src/engine/SCons/ActionTests.py
@@ -100,6 +100,8 @@ class Environment:
return self.d.get(key, value)
def items(self):
return self.d.items()
+ def Dictionary(self):
+ return self.d
def sig_dict(self):
d = {}
for k,v in self.items(): d[k] = v
@@ -129,7 +131,7 @@ class ActionTestCase(unittest.TestCase):
a2 = SCons.Action.Action("string")
assert isinstance(a2, SCons.Action.CommandAction), a2
- assert a2.cmd_list == ["string"], a2.cmd_list
+ assert a2.cmd_list == "string", a2.cmd_list
if hasattr(types, 'UnicodeType'):
exec "a3 = SCons.Action.Action(u'string')"
@@ -138,11 +140,11 @@ class ActionTestCase(unittest.TestCase):
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 a4.list[0].cmd_list == ["x"], a4.list[0].cmd_list
+ assert a4.list[0].cmd_list == "x", a4.list[0].cmd_list
assert isinstance(a4.list[1], SCons.Action.CommandAction), a4.list[1]
- assert a4.list[1].cmd_list == ["y"], a4.list[1].cmd_list
+ assert a4.list[1].cmd_list == "y", a4.list[1].cmd_list
assert isinstance(a4.list[2], SCons.Action.CommandAction), a4.list[2]
- assert a4.list[2].cmd_list == ["z"], a4.list[2].cmd_list
+ assert a4.list[2].cmd_list == "z", a4.list[2].cmd_list
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
@@ -158,25 +160,25 @@ class ActionTestCase(unittest.TestCase):
a8 = SCons.Action.Action(["a8"])
assert isinstance(a8, SCons.Action.CommandAction), a8
- assert a8.cmd_list == [ "a8" ], a8.cmd_list
+ assert a8.cmd_list == "a8", a8.cmd_list
a9 = SCons.Action.Action("x\ny\nz")
assert isinstance(a9, SCons.Action.ListAction), a9
assert isinstance(a9.list[0], SCons.Action.CommandAction), a9.list[0]
- assert a9.list[0].cmd_list == ["x"], a9.list[0].cmd_list
+ assert a9.list[0].cmd_list == "x", a9.list[0].cmd_list
assert isinstance(a9.list[1], SCons.Action.CommandAction), a9.list[1]
- assert a9.list[1].cmd_list == ["y"], a9.list[1].cmd_list
+ assert a9.list[1].cmd_list == "y", a9.list[1].cmd_list
assert isinstance(a9.list[2], SCons.Action.CommandAction), a9.list[2]
- assert a9.list[2].cmd_list == ["z"], a9.list[2].cmd_list
+ assert a9.list[2].cmd_list == "z", a9.list[2].cmd_list
a10 = SCons.Action.Action(["x", foo, "z"])
assert isinstance(a10, SCons.Action.ListAction), a10
assert isinstance(a10.list[0], SCons.Action.CommandAction), a10.list[0]
- assert a10.list[0].cmd_list == ["x"], a10.list[0].cmd_list
+ assert a10.list[0].cmd_list == "x", a10.list[0].cmd_list
assert isinstance(a10.list[1], SCons.Action.FunctionAction), a10.list[1]
assert a10.list[1].execfunction == foo, a10.list[1].execfunction
assert isinstance(a10.list[2], SCons.Action.CommandAction), a10.list[2]
- assert a10.list[2].cmd_list == ["z"], a10.list[2].cmd_list
+ assert a10.list[2].cmd_list == "z", a10.list[2].cmd_list
a11 = SCons.Action.Action(foo, strfunction=bar)
assert isinstance(a11, SCons.Action.FunctionAction), a11
@@ -228,51 +230,6 @@ class ActionBaseTestCase(unittest.TestCase):
l = a.get_actions()
assert l == [a], l
- def test_subst_dict(self):
- """Test substituting dictionary values in an Action
- """
- a = SCons.Action.Action("x")
-
- d = a.subst_dict([], [], Environment(a = 'A', b = 'B'))
- assert d['a'] == 'A', d
- assert d['b'] == 'B', d
-
- d = a.subst_dict(target = 't', source = 's', env = Environment())
- assert str(d['TARGETS']) == 't', d['TARGETS']
- assert str(d['TARGET']) == 't', d['TARGET']
- assert str(d['SOURCES']) == 's', d['SOURCES']
- assert str(d['SOURCE']) == 's', d['SOURCE']
-
- d = a.subst_dict(target = ['t1', 't2'],
- source = ['s1', 's2'],
- env = Environment())
- TARGETS = map(lambda x: str(x), d['TARGETS'])
- TARGETS.sort()
- assert TARGETS == ['t1', 't2'], d['TARGETS']
- assert str(d['TARGET']) == 't1', d['TARGET']
- SOURCES = map(lambda x: str(x), d['SOURCES'])
- SOURCES.sort()
- assert SOURCES == ['s1', 's2'], d['SOURCES']
- assert str(d['SOURCE']) == 's1', d['SOURCE']
-
- class N:
- def __init__(self, name):
- self.name = name
- def __str__(self):
- return self.name
- def rstr(self):
- return 'rstr-' + self.name
-
- d = a.subst_dict(target = [N('t3'), 't4'],
- source = ['s3', N('s4')],
- env = Environment())
- TARGETS = map(lambda x: str(x), d['TARGETS'])
- TARGETS.sort()
- assert TARGETS == ['t3', 't4'], d['TARGETS']
- SOURCES = map(lambda x: str(x), d['SOURCES'])
- SOURCES.sort()
- assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
-
def test_add(self):
"""Test adding Actions to stuff."""
# Adding actions to other Actions or to stuff that can
@@ -547,11 +504,18 @@ class CommandActionTestCase(unittest.TestCase):
def test_get_raw_contents(self):
"""Test fetching the contents of a command Action
"""
+ def CmdGen(target, source, env):
+ assert target is None, target
+ return "%s %s" % \
+ (env["foo"], env["bar"])
+
+ # The number 1 is there to make sure all args get converted to strings.
a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
- "$)", "|"])
+ "$)", "|", "$baz", 1])
c = a.get_raw_contents(target=[], source=[],
- env=Environment(foo = 'FFF', bar = 'BBB'))
- assert c == "| $( FFF | BBB $) |", c
+ env=Environment(foo = 'FFF', bar = 'BBB',
+ baz = CmdGen))
+ assert c == "| $( FFF | BBB $) | FFF BBB 1", c
# We've discusssed using the real target and source names in a
# CommandAction's signature contents. This would have have the
@@ -601,11 +565,18 @@ class CommandActionTestCase(unittest.TestCase):
def test_get_contents(self):
"""Test fetching the contents of a command Action
"""
+ def CmdGen(target, source, env):
+ assert target is None, target
+ return "%s %s" % \
+ (env["foo"], env["bar"])
+
+ # The number 1 is there to make sure all args get converted to strings.
a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar",
- "$)", "|"])
+ "$)", "|", "$baz", 1])
c = a.get_contents(target=[], source=[],
- env=Environment(foo = 'FFF', bar = 'BBB'))
- assert c == "| |", c
+ env=Environment(foo = 'FFF', bar = 'BBB',
+ baz = CmdGen))
+ assert c == "| | FFF BBB 1", c
# We've discusssed using the real target and source names in a
# CommandAction's signature contents. This would have have the
@@ -848,7 +819,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 a.list[2].list[0].cmd_list == [ 'y' ]
+ assert a.list[2].list[0].cmd_list == 'y'
def test_get_actions(self):
"""Test the get_actions() method for ListActions
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 433fadf..2f17227 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -199,7 +199,7 @@ class BuilderTestCase(unittest.TestCase):
Verify that we can retrieve the supplied action attribute.
"""
builder = SCons.Builder.Builder(action="foo")
- assert builder.action.cmd_list == ["foo"]
+ assert builder.action.cmd_list == "foo"
def func():
pass
diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py
index f0c2163..f4bd637 100644
--- a/src/engine/SCons/Defaults.py
+++ b/src/engine/SCons/Defaults.py
@@ -193,23 +193,23 @@ def copyFunc(dest, source, env):
os.chmod(dest, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
return 0
-def _concat(prefix, list, suffix, locals, globals, f=lambda x: x):
+def _concat(prefix, list, suffix, env, f=lambda x: x):
"""Creates a new list from 'list' by first interpolating each
element in the list using 'locals' and 'globals' and then calling f
on the list, and finally concatenating 'prefix' and 'suffix' onto
each element of the list. A trailing space on 'prefix' or leading
space on 'suffix' will cause them to be put into seperate list
elements rather than being concatenated."""
-
+
if not list:
return list
if not SCons.Util.is_List(list):
list = [list]
- def subst(x, locals=locals, globals=globals):
+ def subst(x, env = env):
if SCons.Util.is_String(x):
- return SCons.Util.scons_subst(x, locals, globals)
+ return SCons.Util.scons_subst(x, env)
else:
return x
@@ -239,11 +239,12 @@ def _concat(prefix, list, suffix, locals, globals, f=lambda x: x):
return ret
-def _stripixes(prefix, list, suffix, locals, globals, stripprefix, stripsuffix, c=_concat):
+def _stripixes(prefix, list, suffix, stripprefix, stripsuffix, env, c=_concat):
"""This is a wrapper around _concat() that checks for the existence
of prefixes or suffixes on list elements and strips them where it
finds them. This is used by tools (like the GNU linker) that need
to turn something like 'libfoo.a' into '-lfoo'."""
+
def f(list, sp=stripprefix, ss=stripsuffix):
ret = []
for l in list:
@@ -253,7 +254,25 @@ def _stripixes(prefix, list, suffix, locals, globals, stripprefix, stripsuffix,
l = l[:-len(ss)]
ret.append(l)
return ret
- return c(prefix, list, suffix, locals, globals, f)
+ return c(prefix, list, suffix, env, f)
+
+class NullCmdGenerator:
+ """This is a callable class that can be used in place of other
+ command generators if you don't want them to do anything.
+
+ The __call__ method for this class simply returns the thing
+ you instantiated it with.
+
+ Example usage:
+ env["DO_NOTHING"] = NullCmdGenerator
+ env["LINKCOM"] = "${DO_NOTHING('$LINK $SOURCES $TARGET')}"
+ """
+
+ def __init__(self, cmd):
+ self.cmd = cmd
+
+ def __call__(self, target, source, env):
+ return self.cmd
ConstructionEnvironment = {
'BUILDERS' : { 'SharedLibrary' : SharedLibrary,
@@ -270,8 +289,9 @@ ConstructionEnvironment = {
'INSTALL' : copyFunc,
'_concat' : _concat,
'_stripixes' : _stripixes,
- '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, locals(), globals())}',
- '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, locals(), globals(), RDirs)} $)',
- '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, locals(), globals(), RDirs)} $)',
- '_F77INCFLAGS' : '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, locals(), globals(), RDirs)} $)'
+ '_LIBFLAGS' : '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
+ '_LIBDIRFLAGS' : '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs)} $)',
+ '_CPPINCFLAGS' : '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)',
+ '_F77INCFLAGS' : '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs)} $)',
+ 'TEMPFILE' : NullCmdGenerator
}
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 8336f74..37182c4 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -143,6 +143,32 @@ class BuilderDict(UserDict):
_rm = re.compile(r'\$[()]')
+class _lister:
+ """This class is used to implement dummy targets and sources
+ for signature calculation."""
+ def __init__(self, fmt):
+ self.fmt = fmt
+ def _format(self, index):
+ # For some reason, I originally made the fake names of
+ # the targets and sources 1-based (['__t1__, '__t2__']),
+ # not 0-based. We preserve this behavior by adding one
+ # to the returned item names, so everyone's targets
+ # won't get recompiled if they were using an old
+ # version.
+ return self.fmt % (index + 1)
+ def __getitem__(self, index):
+ return SCons.Util.PathList([self._format(index)])[0]
+ def __getslice__(self, i, j):
+ slice = []
+ for x in range(i, j):
+ slice.append(self._format(x))
+ return SCons.Util.PathList(slice)
+ def __getattr__(self, name):
+ # If we don't find an attribute in this class, let's
+ # look in PathList. self[0:2] returns a PathList instance
+ # via __getslice__
+ return getattr(self[0:2], name)
+
class Environment:
"""Base class for construction Environments. These are
the primary objects used to communicate dependency and
@@ -404,7 +430,7 @@ class Environment:
else:
return side_effects
- def subst(self, string, raw=0):
+ def subst(self, string, raw=0, target=None, source=None):
"""Recursively interpolates construction variables from the
Environment into the specified string, returning the expanded
result. Construction variables are specified by a $ prefix
@@ -418,16 +444,18 @@ class Environment:
regex_remove = None
else:
regex_remove = _rm
- return SCons.Util.scons_subst(string, self._dict, {}, regex_remove)
+ return SCons.Util.scons_subst(string, self, regex_remove,
+ target, source)
- def subst_list(self, string, raw=0):
+ def subst_list(self, string, raw=0, target=None, source=None):
"""Calls through to SCons.Util.scons_subst_list(). See
the documentation for that function."""
if raw:
regex_remove = None
else:
regex_remove = _rm
- return SCons.Util.scons_subst_list(string, self._dict, {}, regex_remove)
+ return SCons.Util.scons_subst_list(string, self, regex_remove,
+ target, source)
def get_scanner(self, skey):
"""Find the appropriate scanner given a key (usually a file suffix).
@@ -549,10 +577,9 @@ class Environment:
This fills in static TARGET, TARGETS, SOURCE and SOURCES
variables so that signatures stay the same every time.
"""
- dict = {}
- for k,v in self.items(): dict[k] = v
- dict['TARGETS'] = SCons.Util.Lister('__t%d__')
+ dict = self._dict.copy()
+ dict['TARGETS'] = _lister('__t%d__')
dict['TARGET'] = dict['TARGETS'][0]
- dict['SOURCES'] = SCons.Util.Lister('__s%d__')
+ dict['SOURCES'] = _lister('__s%d__')
dict['SOURCE'] = dict['SOURCES'][0]
return dict
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index e590d7d..9a6eb1c 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -477,7 +477,7 @@ class EnvironmentTestCase(unittest.TestCase):
action='buildfoo $target $source')
assert not t.builder is None
assert t.builder.action.__class__.__name__ == 'CommandAction'
- assert t.builder.action.cmd_list == ['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)
@@ -540,6 +540,21 @@ class EnvironmentTestCase(unittest.TestCase):
lst = env.subst_list([ "$AAA", "B $CCC" ])
assert lst == [ [ "a", "b" ], [ "c", "B a", "b" ], [ "c" ] ], lst
+ # Test callables in the Environment
+ def foo(target, source, env):
+ assert target == 1, target
+ assert source == 2, source
+ return env["FOO"]
+
+ env = Environment(BAR=foo, FOO='baz')
+
+ subst = env.subst('test $BAR', target=1, source=2)
+ assert subst == 'test baz', subst
+
+ lst = env.subst_list('test $BAR', target=1, source=2)
+ assert lst[0][0] == 'test', lst[0][0]
+ assert lst[0][1] == 'baz', lst[0][1]
+
def test_autogenerate(dict):
"""Test autogenerating variables in a dictionary."""
@@ -816,6 +831,9 @@ class EnvironmentTestCase(unittest.TestCase):
assert s == '', s
s = map(str, d['TARGETS'][3:5])
assert s == ['__t4__', '__t5__'], s
+ s = map(lambda x: os.path.normcase(str(x)), d['TARGETS'].abspath)
+ assert s == map(os.path.normcase, [ os.path.join(os.getcwd(), '__t1__'),
+ os.path.join(os.getcwd(), '__t2__') ])
s = str(d['SOURCE'])
assert s == '__s1__', s
@@ -833,6 +851,9 @@ class EnvironmentTestCase(unittest.TestCase):
assert s == '', s
s = map(str, d['SOURCES'][3:5])
assert s == ['__s4__', '__s5__'], s
+ s = map(lambda x: os.path.normcase(str(x)), d['SOURCES'].abspath)
+ assert s == map(os.path.normcase, [ os.path.join(os.getcwd(), '__s1__'),
+ os.path.join(os.getcwd(), '__s2__') ])
if __name__ == "__main__":
diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py
index 6a14a7a..870dd99 100644
--- a/src/engine/SCons/Platform/win32.py
+++ b/src/engine/SCons/Platform/win32.py
@@ -38,29 +38,34 @@ import os.path
import string
import sys
-#
+class TempFileMunge:
+ """A callable class. You can set an Environment variable to this,
+ then call it with a string argument, then it will perform temporary
+ file substitution on it. This is used to circumvent the win32 long command
+ line limitation.
-def TempFileMunge(env, cmd_list, for_signature):
- """Given a list of command line arguments, see if it is too
- long to pass to the win32 command line interpreter. If so,
- create a temp file, then pass "@tempfile" as the sole argument
- to the supplied command (which is the first element of cmd_list).
- Otherwise, just return [cmd_list]."""
- cmd = env.subst_list(cmd_list)[0]
- if for_signature or \
- (reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)) <= 2048:
- return [cmd_list]
- else:
- import tempfile
- # We do a normpath because mktemp() has what appears to be
- # a bug in Win32 that will use a forward slash as a path
- # delimiter. Win32's link mistakes that for a command line
- # switch and barfs.
- tmp = os.path.normpath(tempfile.mktemp())
- args = map(SCons.Util.quote_spaces, cmd[1:])
- open(tmp, 'w').write(string.join(args, " ") + "\n")
- return [ [cmd[0], '@' + tmp],
- ['del', tmp] ]
+ Example usage:
+ env["TEMPFILE"] = TempFileMunge
+ env["LINKCOM"] = "${TEMPFILE('$LINK $TARGET $SOURCES')}"
+ """
+ def __init__(self, cmd):
+ self.cmd = cmd
+
+ def __call__(self, target, source, env):
+ cmd = env.subst_list(self.cmd, 0, target, source)[0]
+ if target is None or \
+ (reduce(lambda x, y: x + len(y), cmd, 0) + len(cmd)) <= 2048:
+ return self.cmd
+ else:
+ import tempfile
+ # We do a normpath because mktemp() has what appears to be
+ # a bug in Win32 that will use a forward slash as a path
+ # delimiter. Win32's link mistakes that for a command line
+ # switch and barfs.
+ tmp = os.path.normpath(tempfile.mktemp())
+ args = map(SCons.Util.quote_spaces, cmd[1:])
+ open(tmp, 'w').write(string.join(args, " ") + "\n")
+ return [ cmd[0], '@' + tmp + '\ndel', tmp ]
# 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
@@ -128,4 +133,5 @@ def generate(env):
env['LIBSUFFIXES'] = [ '$LIBSUFFIX', '$SHLIBSUFFIX' ]
env['SPAWN'] = spawn
env['SHELL'] = cmd_interp
+ env['TEMPFILE'] = TempFileMunge
env['ESCAPE'] = escape
diff --git a/src/engine/SCons/Tool/gnulink.py b/src/engine/SCons/Tool/gnulink.py
index db7ff4b..9f782c1 100644
--- a/src/engine/SCons/Tool/gnulink.py
+++ b/src/engine/SCons/Tool/gnulink.py
@@ -52,7 +52,7 @@ def generate(env, platform):
env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
env['LIBDIRPREFIX']='-L'
env['LIBDIRSUFFIX']=''
- env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, locals(), globals(), LIBPREFIX, LIBSUFFIX)}'
+ env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIX, LIBSUFFIX, __env__)}'
env['LIBLINKPREFIX']='-l'
env['LIBLINKSUFFIX']=''
diff --git a/src/engine/SCons/Tool/linkloc.py b/src/engine/SCons/Tool/linkloc.py
index 51ce60c..d55a4ee 100644
--- a/src/engine/SCons/Tool/linkloc.py
+++ b/src/engine/SCons/Tool/linkloc.py
@@ -43,7 +43,6 @@ import SCons.Defaults
import SCons.Errors
import SCons.Util
-from SCons.Platform.win32 import TempFileMunge
from SCons.Tool.msvc import get_msdev_paths
from SCons.Tool.PharLapCommon import addPharLapPaths
@@ -66,8 +65,8 @@ class LinklocGenerator:
def __init__(self, cmdline):
self.cmdline = cmdline
- def __call__(self, env, target, source, for_signature):
- if for_signature:
+ def __call__(self, env, target, source):
+ if target is None:
# Expand the contents of any linker command files recursively
subs = 1
strsub = env.subst(self.cmdline)
@@ -75,23 +74,21 @@ class LinklocGenerator:
strsub, subs = _re_linker_command.subn(repl_linker_command, strsub)
return strsub
else:
- return TempFileMunge(env, string.split(self.cmdline), 0)
-
-_linklocLinkAction = SCons.Action.Action(SCons.Action.CommandGenerator(LinklocGenerator("$LINK $LINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -exe $TARGET $SOURCES")))
-_linklocShLinkAction = SCons.Action.Action(SCons.Action.CommandGenerator(LinklocGenerator("$SHLINK $SHLINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -dll $TARGET $SOURCES")))
+ return "${TEMPFILE('" + self.cmdline + "')}"
def generate(env, platform):
"""Add Builders and construction variables for ar to an Environment."""
env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
env['BUILDERS']['Program'] = SCons.Defaults.Program
+ env['SUBST_CMD_FILE'] = LinklocGenerator
env['SHLINK'] = '$LINK'
env['SHLINKFLAGS'] = '$LINKFLAGS'
- env['SHLINKCOM'] = _linklocShLinkAction
+ env['SHLINKCOM'] = '${SUBST_CMD_FILE("$SHLINK $SHLINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -dll $TARGET $SOURCES")}'
env['SHLIBEMITTER']= None
env['LINK'] = "linkloc"
env['LINKFLAGS'] = ''
- env['LINKCOM'] = _linklocLinkAction
+ env['LINKCOM'] = '${SUBST_CMD_FILE("$LINK $LINKFLAGS $( $_LIBDIRFLAGS $) $_LIBFLAGS -exe $TARGET $SOURCES")}'
env['LIBDIRPREFIX']='-libpath '
env['LIBDIRSUFFIX']=''
env['LIBLINKPREFIX']='-lib '
diff --git a/src/engine/SCons/Tool/mslib.py b/src/engine/SCons/Tool/mslib.py
index e8667ee..958218f 100644
--- a/src/engine/SCons/Tool/mslib.py
+++ b/src/engine/SCons/Tool/mslib.py
@@ -35,15 +35,6 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Defaults
-from SCons.Platform.win32 import TempFileMunge
-
-def win32ArGenerator(env, target, source, for_signature, **kw):
- args = [ '$AR', '$ARFLAGS', '/OUT:%s' % target[0]]
- args.extend(map(SCons.Util.to_String, source))
- return TempFileMunge(env, args, for_signature)
-
-ArAction = SCons.Action.CommandGenerator(win32ArGenerator)
-
def generate(env, platform):
"""Add Builders and construction variables for lib to an Environment."""
env['BUILDERS']['Library'] = SCons.Defaults.StaticLibrary
@@ -51,7 +42,7 @@ def generate(env, platform):
env['AR'] = 'lib'
env['ARFLAGS'] = '/nologo'
- env['ARCOM'] = ArAction
+ env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}"
def exists(env):
return env.Detect('lib')
diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py
index 6ef1542..fa174e4 100644
--- a/src/engine/SCons/Tool/mslink.py
+++ b/src/engine/SCons/Tool/mslink.py
@@ -42,44 +42,42 @@ import SCons.Errors
import SCons.Util
import msvc
-from SCons.Platform.win32 import TempFileMunge
from SCons.Tool.msvc import get_msdev_paths
-
-def win32LinkGenerator(env, target, source, for_signature):
- args = [ '$LINK', '$LINKFLAGS', '/OUT:%s' % target[0],
- '$(', '$_LIBDIRFLAGS', '$)', '$_LIBFLAGS' ]
-
- if env.has_key('PDB') and env['PDB']:
- args.extend(['/PDB:%s'%target[0].File(env['PDB']), '/DEBUG'])
- args.extend(map(SCons.Util.to_String, source))
- return TempFileMunge(env, args, for_signature)
+def pdbGenerator(env, target, source):
+ if target and env.has_key('PDB') and env['PDB']:
+ return ['/PDB:%s'%target[0].File(env['PDB']), '/DEBUG']
-def win32LibGenerator(target, source, env, for_signature):
- listCmd = [ "$SHLINK", "$SHLINKFLAGS" ]
+def win32ShlinkTargets(target, source, env):
+ if target:
+ listCmd = []
+ dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
+ if dll: listCmd.append("/out:%s"%dll)
- if env.has_key('PDB') and env['PDB']:
- listCmd.extend(['/PDB:%s'%target[0].File(env['PDB']), '/DEBUG'])
-
- dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
- if dll: listCmd.append("/out:%s"%dll)
-
- implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
- if implib: listCmd.append("/implib:%s"%implib)
-
- listCmd.extend([ '$_LIBDIRFLAGS', '$_LIBFLAGS' ])
+ implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
+ if implib: listCmd.append("/implib:%s"%implib)
- deffile = env.FindIxes(source, "WIN32DEFPREFIX", "WIN32DEFSUFFIX")
-
- for src in source:
- if src == deffile:
- # Treat this source as a .def file.
- listCmd.append("/def:%s" % src)
- else:
- # Just treat it as a generic source file.
- listCmd.append(str(src))
-
- return TempFileMunge(env, listCmd, for_signature)
+ return listCmd
+ else:
+ # For signature calculation
+ return '/out:$TARGET'
+
+def win32ShlinkSources(target, source, env):
+ if target:
+ listCmd = []
+
+ deffile = env.FindIxes(source, "WIN32DEFPREFIX", "WIN32DEFSUFFIX")
+ for src in source:
+ if src == deffile:
+ # Treat this source as a .def file.
+ listCmd.append("/def:%s" % src)
+ else:
+ # Just treat it as a generic source file.
+ listCmd.append(str(src))
+ return listCmd
+ else:
+ # For signature calculation
+ return "$SOURCES"
def win32LibEmitter(target, source, env):
msvc.validate_vars(env)
@@ -120,9 +118,6 @@ def prog_emitter(target, source, env):
return (target,source)
-ShLibAction = SCons.Action.CommandGenerator(win32LibGenerator)
-LinkAction = SCons.Action.CommandGenerator(win32LinkGenerator)
-
def generate(env, platform):
"""Add Builders and construction variables for ar to an Environment."""
env['BUILDERS']['SharedLibrary'] = SCons.Defaults.SharedLibrary
@@ -130,14 +125,14 @@ def generate(env, platform):
env['SHLINK'] = '$LINK'
env['SHLINKFLAGS'] = '$LINKFLAGS /dll'
- env['SHLINKCOM'] = ShLibAction
+ env['_SHLINK_TARGETS'] = win32ShlinkTargets
+ env['_SHLINK_SOURCES'] = win32ShlinkSources
+ env['SHLINKCOM'] = '${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}'
env['SHLIBEMITTER']= win32LibEmitter
env['LINK'] = 'link'
env['LINKFLAGS'] = '/nologo'
- if str(platform) == 'cygwin':
- env['LINKCOM'] = '$LINK $LINKFLAGS /OUT:$TARGET $( $_LIBDIRFLAGS $) $_LIBFLAGS $SOURCES'
- else:
- env['LINKCOM'] = LinkAction
+ env['_PDB'] = pdbGenerator
+ env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $SOURCES")}'
env['PROGEMITTER'] = prog_emitter
env['LIBDIRPREFIX']='/LIBPATH:'
env['LIBDIRSUFFIX']=''
diff --git a/src/engine/SCons/Tool/sgilink.py b/src/engine/SCons/Tool/sgilink.py
index 6d04ec2..8304bb6 100644
--- a/src/engine/SCons/Tool/sgilink.py
+++ b/src/engine/SCons/Tool/sgilink.py
@@ -52,7 +52,7 @@ def generate(env, platform):
env['LINKCOM'] = '$LINK $LINKFLAGS -o $TARGET $SOURCES $_LIBDIRFLAGS $_LIBFLAGS'
env['LIBDIRPREFIX']='-L'
env['LIBDIRSUFFIX']=''
- env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, locals(), globals(), LIBPREFIX, LIBSUFFIX)}'
+ env['_LIBFLAGS']='${_stripixes(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, LIBPREFIX, LIBSUFFIX, __env__)}'
env['LIBLINKPREFIX']='-l'
env['LIBLINKSUFFIX']=''
diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py
index b03ff0f..16ff388 100644
--- a/src/engine/SCons/Util.py
+++ b/src/engine/SCons/Util.py
@@ -194,50 +194,7 @@ class PathList(UserList.UserList):
# suffix and basepath.
return self.__class__([ UserList.UserList.__getitem__(self, item), ])
-class Lister(PathList):
- """A special breed of fake list that not only supports the inherited
- "path dissection" attributes of PathList, but also uses a supplied
- format string to generate arbitrary (slices of) individually-named
- elements on the fly.
- """
- def __init__(self, fmt):
- self.fmt = fmt
- PathList.__init__(self, [ self._element(0), self._element(1) ])
- def __getitem__(self, index):
- return PathList([self._element(index)])
- def _element(self, index):
- """Generate the index'th element in this list."""
- # For some reason, I originally made the fake names of
- # the targets and sources 1-based (['__t1__, '__t2__']),
- # not 0-based. We preserve this behavior by adding one
- # to the returned item names, so everyone's targets
- # won't get recompiled if they were using an old version.
- return self.fmt % (index + 1)
- def __iter__(self):
- """Return an iterator object for Python 2.2."""
- class Lister_iter:
- def __init__(self, data):
- self.data = data
- self.index = 0
- def __iter__(self):
- return self
- def next(self):
- try:
- element = self.data[self.index]
- except IndexError:
- raise StopIteration
- self.index = self.index + 1
- return element
- return Lister_iter(self.data)
- def __getslice__(self, i, j):
- slice = []
- if j == sys.maxint:
- j = i + 2
- for x in range(i, j):
- slice.append(self._element(x))
- return PathList(slice)
-
-_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[^}]*})$')
+_env_var = re.compile(r'^\$([_a-zA-Z]\w*|{[_a-zA-Z]\w*})$')
def get_environment_var(varstr):
"""Given a string, first determine if it looks like a reference
@@ -272,7 +229,7 @@ def quote_spaces(arg):
# a command line.
#
# \0\2 signifies a division between multiple distinct
-# commands
+# commands, i.e., a newline
#
# \0\3 This string should be interpreted literally.
# This code occurring anywhere in the string means
@@ -282,7 +239,7 @@ def quote_spaces(arg):
# \0\4 A literal dollar sign '$'
#
# \0\5 Placed before and after interpolated variables
-# so that we do not accidentally smush to variables
+# so that we do not accidentally smush two variables
# together during the recursive interpolation process.
_cv = re.compile(r'\$([_a-zA-Z]\w*|{[^}]*})')
@@ -397,7 +354,49 @@ class DisplayEngine:
self.__call__ = self.dont_print
-def scons_subst_list(strSubst, globals, locals, remove=None):
+def subst_dict(target, source, env):
+ """Create a dictionary for substitution of construction
+ variables.
+
+ This translates the following special arguments:
+
+ env - the construction environment itself,
+ the values of which (CC, CCFLAGS, etc.)
+ are copied straight into the dictionary
+
+ target - the target (object or array of objects),
+ used to generate the TARGET and TARGETS
+ construction variables
+
+ source - the source (object or array of objects),
+ used to generate the SOURCES and SOURCE
+ construction variables
+ """
+
+ dict = env.Dictionary().copy()
+
+ if not is_List(target):
+ target = [target]
+
+ dict['TARGETS'] = PathList(map(os.path.normpath, map(str, target)))
+ if dict['TARGETS']:
+ dict['TARGET'] = dict['TARGETS'][0]
+
+ def rstr(x):
+ try:
+ return x.rstr()
+ except AttributeError:
+ return str(x)
+ if not is_List(source):
+ source = [source]
+ dict['SOURCES'] = PathList(map(os.path.normpath, map(rstr, source)))
+ if dict['SOURCES']:
+ dict['SOURCE'] = dict['SOURCES'][0]
+
+ return dict
+
+def scons_subst_list(strSubst, env, remove=None, target=None,
+ source=None):
"""
This function serves the same purpose as scons_subst(), except
this function returns the interpolated list as a list of lines, where
@@ -409,7 +408,7 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
function.
There are a few simple rules this function follows in order to
- determine how to parse strSubst and consruction variables into lines
+ determine how to parse strSubst and construction variables into lines
and arguments:
1) A string is interpreted as a space delimited list of arguments.
@@ -422,33 +421,34 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
(e.g. file names) to contain embedded newline characters.
"""
- def convert(x):
- """This function is used to convert construction variable
- values or the value of strSubst to a string for interpolation.
- This function follows the rules outlined in the documentaion
- for scons_subst_list()"""
- if x is None:
- return ''
- elif is_String(x):
- return _space_sep.sub('\0', x)
- elif is_List(x):
- try:
- return x.to_String()
- except AttributeError:
- return string.join(map(to_String, x), '\0')
- else:
- return to_String(x)
-
- def repl(m, globals=globals, locals=locals):
+ if target != None:
+ dict = subst_dict(target, source, env)
+ else:
+ dict = env.sig_dict()
+
+ def repl(m,
+ target=target,
+ source=source,
+ env=env,
+ local_vars = dict,
+ global_vars = { "__env__" : env }):
key = m.group(1)
if key[0] == '{':
key = key[1:-1]
try:
- e = eval(key, globals, locals)
- # The \0\5 escape code keeps us from smushing two or more
- # variables together during recusrive substitution, i.e.
- # foo=$bar bar=baz barbaz=blat => $foo$bar->blat (bad)
- return "\0\5" + _convert(e) + "\0\5"
+ e = eval(key, global_vars, local_vars)
+ if callable(e):
+ # We wait to evaluate callables until the end of everything
+ # else. For now, we instert a special escape sequence
+ # that we will look for later.
+ return '\0\5' + _convert(e(target=target,
+ source=source,
+ env=env)) + '\0\5'
+ else:
+ # The \0\5 escape code keeps us from smushing two or more
+ # variables together during recusrive substitution, i.e.
+ # foo=$bar bar=baz barbaz=blat => $foo$bar->blat (bad)
+ return "\0\5" + _convert(e) + "\0\5"
except NameError:
return '\0\5'
@@ -474,7 +474,8 @@ def scons_subst_list(strSubst, globals, locals, remove=None):
return map(lambda x: map(CmdStringHolder, filter(lambda y:y, string.split(x, '\0\1'))),
listLines)
-def scons_subst(strSubst, globals, locals, remove=None):
+def scons_subst(strSubst, env, remove=None, target=None,
+ source=None):
"""Recursively interpolates dictionary variables into
the specified string, returning the expanded result.
Variables are specified by a $ prefix in the string and
@@ -487,12 +488,24 @@ def scons_subst(strSubst, globals, locals, remove=None):
# This function needs to be fast, so don't call scons_subst_list
- def repl(m, globals=globals, locals=locals):
+ if target != None:
+ dict = subst_dict(target, source, env)
+ else:
+ dict = env.sig_dict()
+
+ def repl(m,
+ target=target,
+ source=source,
+ env=env,
+ local_vars = dict,
+ global_vars = { '__env__' : env }):
key = m.group(1)
if key[0] == '{':
key = key[1:-1]
try:
- e = eval(key, globals, locals)
+ e = eval(key, global_vars, local_vars)
+ if callable(e):
+ e = e(target=target, source=source, env=env)
if e is None:
s = ''
elif is_List(e):
diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py
index f77265a..603631c 100644
--- a/src/engine/SCons/UtilTests.py
+++ b/src/engine/SCons/UtilTests.py
@@ -43,22 +43,57 @@ class OutBuffer:
def write(self, str):
self.buffer = self.buffer + str
+class DummyEnv:
+ def __init__(self, dict={}):
+ self.dict = dict
+
+ def Dictionary(self, key = None):
+ if not key:
+ return self.dict
+ return self.dict[key]
+
+ def sig_dict(self):
+ dict = self.dict.copy()
+ dict["TARGETS"] = 'tsig'
+ dict["SOURCES"] = 'ssig'
+ return dict
+
+def CmdGen1(target, source, env):
+ # Nifty trick...since Environment references are interpolated,
+ # instantiate an instance of a callable class with this one,
+ # which will then get evaluated.
+ assert target == 't', target
+ assert source == 's', source
+ return "${CMDGEN2('foo')}"
+
+class CmdGen2:
+ def __init__(self, mystr):
+ self.mystr = mystr
+
+ def __call__(self, target, source, env):
+ assert target == 't', target
+ assert source == 's', source
+ return [ self.mystr, env.Dictionary('BAR') ]
class UtilTestCase(unittest.TestCase):
- def test_subst_PathList(self):
- """Test the subst function with PathLists"""
+ def test_subst(self):
+ """Test the subst function"""
loc = {}
- loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
- "/bar/baz.obj",
- "../foo/baz.obj" ]))
- loc['TARGET'] = loc['TARGETS'][0]
- loc['SOURCES'] = PathList(map(os.path.normpath, [ "./foo/blah.cpp",
- "/bar/ack.cpp",
- "../foo/ack.c" ]))
- loc['SOURCE'] = loc['SOURCES'][0]
+ target = [ "./foo/bar.exe",
+ "/bar/baz.obj",
+ "../foo/baz.obj" ]
+ source = [ "./foo/blah.cpp",
+ "/bar/ack.cpp",
+ "../foo/ack.c" ]
loc['xxx'] = None
loc['zero'] = 0
loc['one'] = 1
+ loc['BAR'] = 'baz'
+
+ loc['CMDGEN1'] = CmdGen1
+ loc['CMDGEN2'] = CmdGen2
+
+ env = DummyEnv(loc)
if os.sep == '/':
def cvt(str):
@@ -67,151 +102,102 @@ class UtilTestCase(unittest.TestCase):
def cvt(str):
return string.replace(str, '/', os.sep)
- newcom = scons_subst("test $TARGETS $SOURCES", loc, {})
+ newcom = scons_subst("test $TARGETS $SOURCES", env,
+ target=target, source=source)
assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c")
- newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", loc, {})
+ newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", env,
+ target=target, source=source)
assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp")
- newcom = scons_subst("test ${TARGETS[1:]}v", loc, {})
+ newcom = scons_subst("test ${TARGETS[1:]}v", env,
+ target=target, source=source)
assert newcom == cvt("test /bar/baz.obj ../foo/baz.objv")
- newcom = scons_subst("test $TARGET", loc, {})
+ newcom = scons_subst("test $TARGET", env,
+ target=target, source=source)
assert newcom == cvt("test foo/bar.exe")
- newcom = scons_subst("test $TARGET$FOO[0]", loc, {})
+ newcom = scons_subst("test $TARGET$FOO[0]", env,
+ target=target, source=source)
assert newcom == cvt("test foo/bar.exe[0]")
- newcom = scons_subst("test ${TARGET.file}", loc, {})
+ newcom = scons_subst("test ${TARGET.file}", env,
+ target=target, source=source)
assert newcom == cvt("test bar.exe")
- newcom = scons_subst("test ${TARGET.filebase}", loc, {})
+ newcom = scons_subst("test ${TARGET.filebase}", env,
+ target=target, source=source)
assert newcom == cvt("test bar")
- newcom = scons_subst("test ${TARGET.suffix}", loc, {})
+ newcom = scons_subst("test ${TARGET.suffix}", env,
+ target=target, source=source)
assert newcom == cvt("test .exe")
- newcom = scons_subst("test ${TARGET.base}", loc, {})
+ newcom = scons_subst("test ${TARGET.base}", env,
+ target=target, source=source)
assert newcom == cvt("test foo/bar")
- newcom = scons_subst("test ${TARGET.dir}", loc, {})
+ newcom = scons_subst("test ${TARGET.dir}", env,
+ target=target, source=source)
assert newcom == cvt("test foo")
- cwd = SCons.Util.updrive(os.getcwd())
-
- newcom = scons_subst("test ${TARGET.abspath}", loc, {})
- assert newcom == cvt("test %s/foo/bar.exe" % (cwd)), newcom
+ newcom = scons_subst("test ${TARGET.abspath}", env,
+ target=target, source=source)
+ assert newcom == cvt("test %s/foo/bar.exe"%SCons.Util.updrive(os.getcwd())), newcom
- newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
- assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(cwd,
+ newcom = scons_subst("test ${SOURCES.abspath}", env,
+ target=target, source=source)
+ assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(SCons.Util.updrive(os.getcwd()),
SCons.Util.updrive(os.path.abspath(os.path.normpath("/bar/ack.cpp"))),
SCons.Util.updrive(os.path.normpath(os.getcwd()+"/..")))), newcom
- newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
- assert newcom == cvt("test %s/foo/blah.cpp" % (cwd)), newcom
+ newcom = scons_subst("test ${SOURCE.abspath}", env,
+ target=target, source=source)
+ assert newcom == cvt("test %s/foo/blah.cpp"%SCons.Util.updrive(os.getcwd())), newcom
- newcom = scons_subst("test $xxx", loc, {})
+ newcom = scons_subst("test $xxx", env)
assert newcom == cvt("test"), newcom
- newcom = scons_subst("test $($xxx$)", loc, {})
+ newcom = scons_subst("test $($xxx$)", env)
assert newcom == cvt("test $($)"), newcom
- newcom = scons_subst("test $( $xxx $)", loc, {})
+ newcom = scons_subst("test $( $xxx $)", env)
assert newcom == cvt("test $( $)"), newcom
- newcom = scons_subst("test $($xxx$)", loc, {}, re.compile('\$[()]'))
+ newcom = scons_subst("test $($xxx$)", env, re.compile('\$[()]'))
assert newcom == cvt("test"), newcom
- newcom = scons_subst("test $( $xxx $)", loc, {}, re.compile('\$[()]'))
+ newcom = scons_subst("test $( $xxx $)", env, re.compile('\$[()]'))
assert newcom == cvt("test"), newcom
- newcom = scons_subst("test $zero", loc, {})
+ newcom = scons_subst("test $zero", env)
assert newcom == cvt("test 0"), newcom
- newcom = scons_subst("test $one", loc, {})
+ newcom = scons_subst("test $one", env)
assert newcom == cvt("test 1"), newcom
- newcom = scons_subst("test aXbXcXd", loc, {}, re.compile('X'))
+ newcom = scons_subst("test aXbXcXd", env, re.compile('X'))
assert newcom == cvt("test abcd"), newcom
- glob = { 'a' : 1, 'b' : 2 }
- loc = {'a' : 3, 'c' : 4 }
- newcom = scons_subst("test $a $b $c $d test", glob, loc)
- assert newcom == "test 3 2 4 test", newcom
+ newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
+ env, target='t', source='s')
+ assert newcom == cvt("test foo baz s t"), newcom
# Test against a former bug in scons_subst_list()
glob = { "FOO" : "$BAR",
"BAR" : "BAZ",
"BLAT" : "XYX",
"BARXYX" : "BADNEWS" }
- newcom = scons_subst("$FOO$BLAT", glob, {})
+ newcom = scons_subst("$FOO$BLAT", DummyEnv(glob))
assert newcom == "BAZXYX", newcom
# Test for double-dollar-sign behavior
glob = { "FOO" : "BAR",
"BAZ" : "BLAT" }
- newcom = scons_subst("$$FOO$BAZ", glob, {})
+ newcom = scons_subst("$$FOO$BAZ", DummyEnv(glob))
assert newcom == "$FOOBLAT", newcom
- def test_subst_Lister(self):
- """Test the subst function with Listers"""
- loc = {}
- loc['TARGETS'] = Lister('t%d')
- loc['TARGET'] = loc['TARGETS'][0]
- loc['SOURCES'] = Lister('s%d')
- loc['SOURCE'] = loc['SOURCES'][0]
- loc['xxx'] = None
- loc['zero'] = 0
- loc['one'] = 1
-
- if os.sep == '/':
- def cvt(str):
- return str
- else:
- def cvt(str):
- return string.replace(str, '/', os.sep)
-
- newcom = scons_subst("test $TARGETS $SOURCES", loc, {})
- assert newcom == cvt("test t1 t2 s1 s2"), newcom
-
- newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", loc, {})
- assert newcom == cvt("test t1 t2 s1"), newcom
-
- newcom = scons_subst("test ${TARGETS[1:]}v", loc, {})
- assert newcom == cvt("test t2 t3v"), newcom
-
- newcom = scons_subst("test $TARGET", loc, {})
- assert newcom == cvt("test t1"), newcom
-
- newcom = scons_subst("test $TARGET$FOO[0]", loc, {})
- assert newcom == cvt("test t1[0]"), newcom
-
- newcom = scons_subst("test ${TARGET.file}", loc, {})
- assert newcom == cvt("test t1"), newcom
-
- newcom = scons_subst("test ${TARGET.filebase}", loc, {})
- assert newcom == cvt("test t1"), newcom
-
- newcom = scons_subst("test ${TARGET.suffix}", loc, {})
- assert newcom == cvt("test"), newcom
-
- newcom = scons_subst("test ${TARGET.base}", loc, {})
- assert newcom == cvt("test t1"), newcom
-
- newcom = scons_subst("test ${TARGET.dir}", loc, {})
- assert newcom == cvt("test"), newcom
-
- cwd = SCons.Util.updrive(os.getcwd())
-
- newcom = scons_subst("test ${TARGET.abspath}", loc, {})
- assert newcom == cvt("test %s/t1" % (cwd)), newcom
-
- newcom = scons_subst("test ${SOURCES.abspath}", loc, {})
- assert newcom == cvt("test %s/s1 %s/s2" % (cwd, cwd)), newcom
-
- newcom = scons_subst("test ${SOURCE.abspath}", loc, {})
- assert newcom == cvt("test %s/s1" % cwd), newcom
-
def test_splitext(self):
assert splitext('foo') == ('foo','')
assert splitext('foo.bar') == ('foo','.bar')
@@ -229,13 +215,12 @@ class UtilTestCase(unittest.TestCase):
return 1
loc = {}
- loc['TARGETS'] = PathList(map(os.path.normpath, [ "./foo/bar.exe",
- "/bar/baz with spaces.obj",
- "../foo/baz.obj" ]))
- loc['TARGET'] = loc['TARGETS'][0]
- loc['SOURCES'] = PathList(map(os.path.normpath, [ "./foo/blah with spaces.cpp",
- "/bar/ack.cpp",
- "../foo/ack.c" ]))
+ target = [ "./foo/bar.exe",
+ "/bar/baz with spaces.obj",
+ "../foo/baz.obj" ]
+ source = [ "./foo/blah with spaces.cpp",
+ "/bar/ack.cpp",
+ "../foo/ack.c" ]
loc['xxx'] = None
loc['NEWLINE'] = 'before\nafter'
@@ -244,6 +229,11 @@ class UtilTestCase(unittest.TestCase):
loc['BAR'] = Node('bar with spaces.out')
loc['CRAZY'] = Node('crazy\nfile.in')
+ loc['CMDGEN1'] = CmdGen1
+ loc['CMDGEN2'] = CmdGen2
+
+ env = DummyEnv(loc)
+
if os.sep == '/':
def cvt(str):
return str
@@ -251,20 +241,26 @@ class UtilTestCase(unittest.TestCase):
def cvt(str):
return string.replace(str, '/', os.sep)
- cmd_list = scons_subst_list("$TARGETS", loc, {})
+ cmd_list = scons_subst_list("$TARGETS", env,
+ target=target,
+ source=source)
assert cmd_list[0][1] == cvt("/bar/baz with spaces.obj"), cmd_list[0][1]
- cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", loc, {})
+ cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", env,
+ target=target,
+ source=source)
assert len(cmd_list) == 2, cmd_list
assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
assert cmd_list[1][2] == cvt("/bar/baz with spaces.obj"), cmd_list[1]
- cmd_list = scons_subst_list("$SOURCES$NEWLINE", loc, {})
+ cmd_list = scons_subst_list("$SOURCES$NEWLINE", env,
+ target=target,
+ source=source)
assert len(cmd_list) == 2, cmd_list
assert cmd_list[1][0] == 'after', cmd_list[1][0]
assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
- cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", loc, {})
+ cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", env)
assert len(cmd_list) == 1, cmd_list
assert len(cmd_list[0]) == 3, cmd_list
assert cmd_list[0][0] == 'do something', cmd_list[0][0]
@@ -272,7 +268,7 @@ class UtilTestCase(unittest.TestCase):
assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
# This test is now fixed, and works like it should.
- cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", loc, {})
+ cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", env)
assert len(cmd_list) == 1, map(str, cmd_list[0])
assert len(cmd_list[0]) == 3, cmd_list
assert cmd_list[0][0] == 'do something', cmd_list[0][0]
@@ -282,30 +278,37 @@ class UtilTestCase(unittest.TestCase):
# Test inputting a list to scons_subst_list()
cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
"This is a test" ],
- loc, {})
+ env,
+ target=target,
+ source=source)
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 map(str, cmd_list[0]) == ['test', '3', '2', '4', 'test'], map(str, cmd_list[0])
+ # Test interpolating a callable.
+ cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES", env,
+ target='t', source='s')
+ assert len(cmd_list) == 1, len(cmd_list)
+ assert cmd_list[0][0] == 'testing', cmd_list[0][0]
+ assert cmd_list[0][1] == 'foo', cmd_list[0][1]
+ assert cmd_list[0][2] == 'bar with spaces.out', cmd_list[0][2]
+ assert cmd_list[0][3] == 't', cmd_list[0][3]
+ assert cmd_list[0][4] == 's', cmd_list[0][4]
+
# Test against a former bug in scons_subst_list()
glob = { "FOO" : "$BAR",
"BAR" : "BAZ",
"BLAT" : "XYX",
"BARXYX" : "BADNEWS" }
- cmd_list = scons_subst_list("$FOO$BLAT", glob, {})
+ cmd_list = scons_subst_list("$FOO$BLAT", DummyEnv(glob))
assert cmd_list[0][0] == "BAZXYX", cmd_list[0][0]
# Test for double-dollar-sign behavior
glob = { "FOO" : "BAR",
"BAZ" : "BLAT" }
- cmd_list = scons_subst_list("$$FOO$BAZ", glob, {})
+ cmd_list = scons_subst_list("$$FOO$BAZ", DummyEnv(glob))
assert cmd_list[0][0] == "$FOOBLAT", cmd_list[0][0]
# Now test escape functionality
@@ -315,7 +318,7 @@ class UtilTestCase(unittest.TestCase):
return foo
glob = { "FOO" : PathList([ 'foo\nwith\nnewlines',
'bar\nwith\nnewlines' ]) }
- cmd_list = scons_subst_list("$FOO", glob, {})
+ cmd_list = scons_subst_list("$FOO", DummyEnv(glob))
assert cmd_list[0][0] == 'foo\nwith\nnewlines', cmd_list[0][0]
cmd_list[0][0].escape(escape_func)
assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
@@ -519,9 +522,13 @@ class UtilTestCase(unittest.TestCase):
"""Testing get_environment_var()."""
assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
+ assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
+ assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
+ assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
+ assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
def test_Proxy(self):
"""Test generic Proxy class."""
@@ -553,8 +560,8 @@ class UtilTestCase(unittest.TestCase):
"""Test the Literal() function."""
cmd_list = [ '$FOO', Literal('$BAR') ]
cmd_list = scons_subst_list(cmd_list,
- { 'FOO' : 'BAZ',
- 'BAR' : 'BLAT' }, {})
+ DummyEnv({ 'FOO' : 'BAZ',
+ 'BAR' : 'BLAT' }))
def escape_func(cmd):
return '**' + cmd + '**'
@@ -624,6 +631,49 @@ class UtilTestCase(unittest.TestCase):
test._dirlist = None
sys.stdout = old_stdout
+ def test_subst_dict(self):
+ """Test substituting dictionary values in an Action
+ """
+ d = subst_dict([], [], DummyEnv({'a' : 'A', 'b' : 'B'}))
+ assert d['a'] == 'A', d
+ assert d['b'] == 'B', d
+
+ d = subst_dict(target = 't', source = 's', env=DummyEnv())
+ assert str(d['TARGETS']) == 't', d['TARGETS']
+ assert str(d['TARGET']) == 't', d['TARGET']
+ assert str(d['SOURCES']) == 's', d['SOURCES']
+ assert str(d['SOURCE']) == 's', d['SOURCE']
+
+ d = subst_dict(target = ['t1', 't2'],
+ source = ['s1', 's2'],
+ env = DummyEnv())
+ TARGETS = map(lambda x: str(x), d['TARGETS'])
+ TARGETS.sort()
+ assert TARGETS == ['t1', 't2'], d['TARGETS']
+ assert str(d['TARGET']) == 't1', d['TARGET']
+ SOURCES = map(lambda x: str(x), d['SOURCES'])
+ SOURCES.sort()
+ assert SOURCES == ['s1', 's2'], d['SOURCES']
+ assert str(d['SOURCE']) == 's1', d['SOURCE']
+
+ class N:
+ def __init__(self, name):
+ self.name = name
+ def __str__(self):
+ return self.name
+ def rstr(self):
+ return 'rstr-' + self.name
+
+ d = subst_dict(target = [N('t3'), 't4'],
+ source = ['s3', N('s4')],
+ env = DummyEnv())
+ TARGETS = map(lambda x: str(x), d['TARGETS'])
+ TARGETS.sort()
+ assert TARGETS == ['t3', 't4'], d['TARGETS']
+ SOURCES = map(lambda x: str(x), d['SOURCES'])
+ SOURCES.sort()
+ assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
+
if __name__ == "__main__":
suite = unittest.makeSuite(UtilTestCase, 'test_')