From bf221d4e593f803116af76ec3bc16514b666c9f1 Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Fri, 5 Sep 2003 19:26:05 +0000 Subject: Support construction variable expansion anywhere in a file or path name. --- src/CHANGES.txt | 3 + src/RELEASE.txt | 10 ++ src/engine/SCons/Builder.py | 11 +- src/engine/SCons/BuilderTests.py | 30 +++++- src/engine/SCons/Environment.py | 80 ++++++++++---- src/engine/SCons/EnvironmentTests.py | 201 ++++++++++++++++++++++++++++++++--- src/engine/SCons/Util.py | 4 +- src/engine/SCons/UtilTests.py | 14 ++- test/AlwaysBuild.py | 17 ++- test/Depends.py | 24 +++-- test/Ignore.py | 25 ++++- test/Install.py | 11 +- test/InstallAs.py | 68 ++++++++++++ test/Precious.py | 47 ++++---- test/SideEffect.py | 29 +++-- test/SourceCode.py | 95 +++++++++++++++++ test/expansion.py | 108 +++++++++++++++++++ test/special-filenames.py | 8 +- 18 files changed, 688 insertions(+), 97 deletions(-) create mode 100644 test/InstallAs.py create mode 100644 test/SourceCode.py create mode 100644 test/expansion.py diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 1eeb7a9..8f1f754 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -36,6 +36,9 @@ RELEASE X.XX - XXX - Accomodate alphanumeric version strings in EnsurePythonVersion(). + - Support arbitrary expansion of construction variables within + file and directory arguments to Builder calls and Environment methods. + From Bram Moolenaar: - Split the non-SCons-specific functionality from SConf.py to a new, diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 36b9f00..ae69387 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -27,6 +27,16 @@ RELEASE X.XX - XXX Please note the following important changes since release 0.92: + - Construction variables are now expanded anywhere within a + target or source name, as well as in the arguments to the following + Environment methods: AlwaysBuild(), Depends(), Ignore(), Install(), + InstallAs(), Precious(), SideEffect() and SourceCode(). + + If you have any files or directories that actually contain one or + more dollar signs ($), you must now precede the dollar sign with + another dollar sign ($$) when referring to the file or directory + as part of calling a Builder, or any of the above methods. + Please note the following important changes since release 0.91: - The Debian package available from the SCons web site now diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py index af03e82..fe017ae 100644 --- a/src/engine/SCons/Builder.py +++ b/src/engine/SCons/Builder.py @@ -49,7 +49,6 @@ import UserDict import SCons.Action from SCons.Errors import InternalError, UserError import SCons.Executor -import SCons.Node import SCons.Node.FS import SCons.Util import SCons.Warnings @@ -329,7 +328,7 @@ class BuilderBase: src_suf = self.get_src_suffix(env) source = adjustixes(source, None, src_suf) - slist = SCons.Node.arg2nodes(source, self.source_factory) + slist = env.arg2nodes(source, self.source_factory) pre = self.get_prefix(env, slist) suf = self.get_suffix(env, slist) @@ -342,7 +341,7 @@ class BuilderBase: tlist = [ t_from_s(pre, suf, self.splitext) ] else: target = adjustixes(target, pre, suf) - tlist = SCons.Node.arg2nodes(target, self.target_factory) + tlist = env.arg2nodes(target, self.target_factory) if self.emitter: # The emitter is going to do str(node), but because we're @@ -369,8 +368,8 @@ class BuilderBase: # Have to call arg2nodes yet again, since it is legal for # emitters to spit out strings as well as Node instances. - slist = SCons.Node.arg2nodes(source, self.source_factory) - tlist = SCons.Node.arg2nodes(target, self.target_factory) + slist = env.arg2nodes(source, self.source_factory) + tlist = env.arg2nodes(target, self.target_factory) return tlist, slist @@ -503,7 +502,7 @@ class MultiStepBuilder(BuilderBase): source = target target = None - slist = SCons.Node.arg2nodes(source, self.source_factory) + slist = env.arg2nodes(source, self.source_factory) final_sources = [] try: diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py index e0ba2fe..b51eb9f 100644 --- a/src/engine/SCons/BuilderTests.py +++ b/src/engine/SCons/BuilderTests.py @@ -59,6 +59,8 @@ env_scanner = None scons_env = SCons.Environment.Environment() +env_arg2nodes_called = None + class Environment: def __init__(self, **kw): self.d = {} @@ -67,6 +69,8 @@ class Environment: self.d['ESCAPE'] = scons_env['ESCAPE'] for k, v in kw.items(): self.d[k] = v + global env_arg2nodes_called + env_arg2nodes_called = None def subst(self, s): if not SCons.Util.is_String(s): return s @@ -78,6 +82,17 @@ class Environment: except IndexError: pass return self.d.get(s, s) + def arg2nodes(self, args, factory): + global env_arg2nodes_called + env_arg2nodes_called = 1 + if not SCons.Util.is_List(args): + args = [args] + list = [] + for a in args: + if SCons.Util.is_String(a): + a = factory(a) + list.append(a) + return list def get_scanner(self, ext): return env_scanner def Dictionary(self): @@ -108,8 +123,6 @@ class Environment: d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__'] d['SOURCE'] = d['SOURCES'][0] return d - -env = Environment() class MyNode_without_target_from_source: def __init__(self, name): @@ -173,11 +186,13 @@ class BuilderTestCase(unittest.TestCase): def test__call__(self): """Test calling a builder to establish source dependencies """ + env = Environment() builder = SCons.Builder.Builder(action="foo", node_factory=MyNode) n1 = MyNode("n1"); n2 = MyNode("n2"); builder(env, target = n1, source = n2) + assert env_arg2nodes_called assert n1.env == env, n1.env assert n1.builder == builder, n1.builder assert n1.sources == [n2], n1.sources @@ -342,6 +357,7 @@ class BuilderTestCase(unittest.TestCase): Make sure that there is no '.' separator appended. """ + env = Environment() builder = SCons.Builder.Builder(prefix = 'lib.') assert builder.get_prefix(env) == 'lib.' builder = SCons.Builder.Builder(prefix = 'lib') @@ -431,6 +447,7 @@ class BuilderTestCase(unittest.TestCase): Make sure that the '.' separator is appended to the beginning if it isn't already present. """ + env = Environment() builder = SCons.Builder.Builder(suffix = '.o') assert builder.get_suffix(env) == '.o', builder.get_suffix(env) builder = SCons.Builder.Builder(suffix = 'o') @@ -484,6 +501,7 @@ class BuilderTestCase(unittest.TestCase): open(t, 'w').write("function2\n") return 1 + env = Environment() builder = SCons.Builder.Builder(action = function2) tgts = builder(env, target = [outfile, outfile2], source = 'foo') for t in tgts: @@ -525,6 +543,7 @@ class BuilderTestCase(unittest.TestCase): def test_MultiStepBuilder(self): """Testing MultiStepBuilder class.""" + env = Environment() builder1 = SCons.Builder.Builder(action='foo', src_suffix='.bar', suffix='.foo') @@ -574,8 +593,7 @@ class BuilderTestCase(unittest.TestCase): def func_action(target, source, env): return 0 - env['BAR_SUFFIX'] = '.BAR2' - env['FOO_SUFFIX'] = '.FOO2' + env = Environment(BAR_SUFFIX = '.BAR2', FOO_SUFFIX = '.FOO2') builder = SCons.Builder.Builder(action={ '.foo' : func_action, '.bar' : func_action, '$BAR_SUFFIX' : func_action, @@ -709,6 +727,7 @@ class BuilderTestCase(unittest.TestCase): class TestScanner: pass scn = TestScanner() + env = Environment() builder = SCons.Builder.Builder(scanner=scn) tgt = builder(env, target='foo2', source='bar') assert tgt.target_scanner == scn, tgt.target_scanner @@ -731,6 +750,7 @@ class BuilderTestCase(unittest.TestCase): def instance(self, env): return self env_scanner = TestScanner() + env = Environment() builder = SCons.Builder.Builder(action='action') tgt = builder(env, target='foo.x', source='bar') src = tgt.sources[0] @@ -768,6 +788,7 @@ class BuilderTestCase(unittest.TestCase): source.append("baz") return ( target, source ) + env = Environment() builder = SCons.Builder.Builder(action='foo', emitter=emit, node_factory=MyNode) @@ -862,6 +883,7 @@ class BuilderTestCase(unittest.TestCase): def test_no_target(self): """Test deducing the target from the source.""" + env = Environment() b = SCons.Builder.Builder(action='foo', suffix='.o') tgt = b(env, 'aaa') diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index fae86e4..f72bc95 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -54,6 +54,11 @@ import SCons.Tool import SCons.Util import SCons.Warnings +class _Null: + pass + +_null = _Null + def installFunc(target, source, env): """Install a source file into a target using the function specified as the INSTALL construction variable.""" @@ -170,6 +175,7 @@ class Environment: options=None, **kw): self.fs = SCons.Node.FS.default_fs + self.lookup_list = SCons.Node.arg2nodes_lookups self._dict = our_deepcopy(SCons.Defaults.ConstructionEnvironment) self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) @@ -211,6 +217,40 @@ class Environment: def __cmp__(self, other): return cmp(self._dict, other._dict) + def arg2nodes(self, args, node_factory=_null, lookup_list=_null): + if node_factory is _null: + node_factory = self.fs.File + if lookup_list is _null: + lookup_list = self.lookup_list + + if not args: + return [] + + if not SCons.Util.is_List(args): + args = [args] + + nodes = [] + for v in args: + if SCons.Util.is_String(v): + n = None + for l in lookup_list: + n = l(v) + if not n is None: + break + if not n is None: + if SCons.Util.is_String(n): + n = self.subst(n, raw=1) + if node_factory: + n = node_factory(n) + nodes.append(n) + elif node_factory: + v = self.subst(v, raw=1) + nodes.append(node_factory(v)) + else: + nodes.append(v) + + return nodes + def Builders(self): pass # XXX @@ -330,21 +370,21 @@ class Environment: self._dict[envname][name] = nv - def Depends(self, target, dependency): - """Explicity specify that 'target's depend on 'dependency'.""" - tlist = SCons.Node.arg2nodes(target, self.fs.File) - dlist = SCons.Node.arg2nodes(dependency, self.fs.File) - for t in tlist: - t.add_dependency(dlist) + def Depends(self, target, dependency): + """Explicity specify that 'target's depend on 'dependency'.""" + tlist = self.arg2nodes(target, self.fs.File) + dlist = self.arg2nodes(dependency, self.fs.File) + for t in tlist: + t.add_dependency(dlist) - if len(tlist) == 1: - tlist = tlist[0] - return tlist + if len(tlist) == 1: + tlist = tlist[0] + return tlist def Ignore(self, target, dependency): """Ignore a dependency.""" - tlist = SCons.Node.arg2nodes(target, self.fs.File) - dlist = SCons.Node.arg2nodes(dependency, self.fs.File) + tlist = self.arg2nodes(target, self.fs.File) + dlist = self.arg2nodes(dependency, self.fs.File) for t in tlist: t.add_ignore(dlist) @@ -355,7 +395,7 @@ class Environment: def AlwaysBuild(self, *targets): tlist = [] for t in targets: - tlist.extend(SCons.Node.arg2nodes(t, self.fs.File)) + tlist.extend(self.arg2nodes(t, self.fs.File)) for t in tlist: t.set_always_build() @@ -367,7 +407,7 @@ class Environment: def Precious(self, *targets): tlist = [] for t in targets: - tlist.extend(SCons.Node.arg2nodes(t, self.fs.File)) + tlist.extend(self.arg2nodes(t, self.fs.File)) for t in tlist: t.set_precious() @@ -422,11 +462,11 @@ class Environment: def Install(self, dir, source): """Install specified files in the given directory.""" try: - dnodes = SCons.Node.arg2nodes(dir, self.fs.Dir) + dnodes = self.arg2nodes(dir, self.fs.Dir) except TypeError: raise SCons.Errors.UserError, "Target `%s' of Install() is a file, but should be a directory. Perhaps you have the Install() arguments backwards?" % str(dir) try: - sources = SCons.Node.arg2nodes(source, self.fs.File) + sources = self.arg2nodes(source, self.fs.File) except TypeError: if SCons.Util.is_List(source): raise SCons.Errors.UserError, "Source `%s' of Install() contains one or more non-files. Install() source must be one or more files." % repr(map(str, source)) @@ -443,8 +483,8 @@ class Environment: def InstallAs(self, target, source): """Install sources as targets.""" - sources = SCons.Node.arg2nodes(source, self.fs.File) - targets = SCons.Node.arg2nodes(target, self.fs.File) + sources = self.arg2nodes(source, self.fs.File) + targets = self.arg2nodes(target, self.fs.File) ret = [] for src, tgt in map(lambda x, y: (x, y), sources, targets): ret.append(InstallBuilder(self, tgt, src)) @@ -454,7 +494,7 @@ class Environment: def SourceCode(self, entry, builder): """Arrange for a source code builder for (part of) a tree.""" - entries = SCons.Node.arg2nodes(entry, self.fs.Entry) + entries = self.arg2nodes(entry, self.fs.Entry) for entry in entries: entry.set_src_builder(builder) if len(entries) == 1: @@ -464,8 +504,8 @@ class Environment: def SideEffect(self, side_effect, target): """Tell scons that side_effects are built as side effects of building targets.""" - side_effects = SCons.Node.arg2nodes(side_effect, self.fs.File) - targets = SCons.Node.arg2nodes(target, self.fs.File) + side_effects = self.arg2nodes(side_effect, self.fs.File) + targets = self.arg2nodes(target, self.fs.File) for side_effect in side_effects: # A builder of 1 means the node is supposed to appear diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index e79ce7c..12766ec 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -128,7 +128,128 @@ class EnvironmentTestCase(unittest.TestCase): env2.Replace(ONE = "won") assert env2['ONE'] == "won" assert env['ONE'] == 1 - + + def test_arg2nodes(self): + """Test the arg2nodes method + """ + env = Environment() + dict = {} + class X(SCons.Node.Node): + pass + def Factory(name, directory = None, create = 1, dict=dict, X=X): + if not dict.has_key(name): + dict[name] = X() + dict[name].name = name + return dict[name] + + nodes = env.arg2nodes("Util.py UtilTests.py", Factory) + assert len(nodes) == 1, nodes + assert isinstance(nodes[0], X) + assert nodes[0].name == "Util.py UtilTests.py" + + import types + if hasattr(types, 'UnicodeType'): + code = """if 1: + nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory) + assert len(nodes) == 1, nodes + assert isinstance(nodes[0], X) + assert nodes[0].name == u"Util.py UtilTests.py" + \n""" + exec code in globals(), locals() + + nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory) + assert len(nodes) == 2, nodes + assert isinstance(nodes[0], X) + assert isinstance(nodes[1], X) + assert nodes[0].name == "Util.py" + assert nodes[1].name == "UtilTests.py" + + n1 = Factory("Util.py") + nodes = env.arg2nodes([n1, "UtilTests.py"], Factory) + assert len(nodes) == 2, nodes + assert isinstance(nodes[0], X) + assert isinstance(nodes[1], X) + assert nodes[0].name == "Util.py" + assert nodes[1].name == "UtilTests.py" + + class SConsNode(SCons.Node.Node): + pass + nodes = env.arg2nodes(SConsNode()) + assert len(nodes) == 1, nodes + assert isinstance(nodes[0], SConsNode), node + + class OtherNode: + pass + nodes = env.arg2nodes(OtherNode()) + assert len(nodes) == 1, nodes + assert isinstance(nodes[0], OtherNode), node + + def lookup_a(str, F=Factory): + if str[0] == 'a': + n = F(str) + n.a = 1 + return n + else: + return None + + def lookup_b(str, F=Factory): + if str[0] == 'b': + n = F(str) + n.b = 1 + return n + else: + return None + + env_ll = env.Copy() + env_ll.lookup_list = [lookup_a, lookup_b] + + nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory) + assert len(nodes) == 3, nodes + + assert nodes[0].name == 'aaa', nodes[0] + assert nodes[0].a == 1, nodes[0] + assert not hasattr(nodes[0], 'b'), nodes[0] + + assert nodes[1].name == 'bbb' + assert not hasattr(nodes[1], 'a'), nodes[1] + assert nodes[1].b == 1, nodes[1] + + assert nodes[2].name == 'ccc' + assert not hasattr(nodes[2], 'a'), nodes[1] + assert not hasattr(nodes[2], 'b'), nodes[1] + + def lookup_bbbb(str, F=Factory): + if str == 'bbbb': + n = F(str) + n.bbbb = 1 + return n + else: + return None + + def lookup_c(str, F=Factory): + if str[0] == 'c': + n = F(str) + n.c = 1 + return n + else: + return None + + nodes = env.arg2nodes(['bbbb', 'ccc'], Factory, + [lookup_c, lookup_bbbb, lookup_b]) + assert len(nodes) == 2, nodes + + assert nodes[0].name == 'bbbb' + assert not hasattr(nodes[0], 'a'), nodes[1] + assert not hasattr(nodes[0], 'b'), nodes[1] + assert nodes[0].bbbb == 1, nodes[1] + assert not hasattr(nodes[0], 'c'), nodes[0] + + assert nodes[1].name == 'ccc' + assert not hasattr(nodes[1], 'a'), nodes[1] + assert not hasattr(nodes[1], 'b'), nodes[1] + assert not hasattr(nodes[1], 'bbbb'), nodes[0] + assert nodes[1].c == 1, nodes[1] + def test_Builder_calls(self): """Test Builder calls through different environments """ @@ -354,7 +475,8 @@ class EnvironmentTestCase(unittest.TestCase): def test_Install(self): """Test Install and InstallAs methods""" - env=Environment() + env = Environment(FOO='iii', BAR='jjj') + tgt = env.Install('export', [ 'build/foo1', 'build/foo2' ]) paths = map(str, tgt) paths.sort() @@ -363,6 +485,14 @@ class EnvironmentTestCase(unittest.TestCase): for tnode in tgt: assert tnode.builder == InstallBuilder + tgt = env.Install('$FOO', [ 'build/${BAR}1', 'build/${BAR}2' ]) + paths = map(str, tgt) + paths.sort() + expect = map(os.path.normpath, [ 'iii/jjj1', 'iii/jjj2' ]) + assert paths == expect, paths + for tnode in tgt: + assert tnode.builder == InstallBuilder + exc_caught = None try: tgt = env.Install('export', 'export') @@ -400,6 +530,11 @@ class EnvironmentTestCase(unittest.TestCase): for tnode in tgt: assert tnode.builder == InstallBuilder + tgt = env.InstallAs(target='${FOO}.t', source='${BAR}.s') + assert tgt.path == 'iii.t' + assert tgt.sources[0].path == 'jjj.s' + assert tgt.builder == InstallBuilder + def test_ReservedVariables(self): """Test generation of warnings when reserved variable names are set in an environment.""" @@ -570,7 +705,7 @@ class EnvironmentTestCase(unittest.TestCase): def test_Depends(self): """Test the explicit Depends method.""" - env = Environment() + env = Environment(FOO = 'xxx', BAR='yyy') t = env.Depends(target='EnvironmentTest.py', dependency='Environment.py') assert t.__class__.__name__ == 'File' assert t.path == 'EnvironmentTest.py' @@ -579,9 +714,17 @@ class EnvironmentTestCase(unittest.TestCase): assert d.__class__.__name__ == 'File' assert d.path == 'Environment.py' + t = env.Depends(target='${FOO}.py', dependency='${BAR}.py') + assert t.__class__.__name__ == 'File' + assert t.path == 'xxx.py' + assert len(t.depends) == 1 + d = t.depends[0] + assert d.__class__.__name__ == 'File' + assert d.path == 'yyy.py' + def test_Ignore(self): """Test the explicit Ignore method.""" - env = Environment() + env = Environment(FOO='yyy', BAR='zzz') t = env.Ignore(target='targ.py', dependency='dep.py') assert t.__class__.__name__ == 'File' assert t.path == 'targ.py' @@ -589,16 +732,23 @@ class EnvironmentTestCase(unittest.TestCase): i = t.ignore[0] assert i.__class__.__name__ == 'File' assert i.path == 'dep.py' + t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO') + assert t.__class__.__name__ == 'File' + assert t.path == 'yyyzzz' + assert len(t.ignore) == 1 + i = t.ignore[0] + assert i.__class__.__name__ == 'File' + assert i.path == 'zzzyyy' def test_AlwaysBuild(self): """Test the AlwaysBuild() method""" - env = Environment() - t = env.AlwaysBuild('a', 'b', ['c', 'd']) + env = Environment(FOO='fff', BAR='bbb') + t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR') assert t[0].__class__.__name__ == 'File' assert t[0].path == 'a' assert t[0].always_build assert t[1].__class__.__name__ == 'File' - assert t[1].path == 'b' + assert t[1].path == 'bfff' assert t[1].always_build assert t[2].__class__.__name__ == 'File' assert t[2].path == 'c' @@ -606,16 +756,19 @@ class EnvironmentTestCase(unittest.TestCase): assert t[3].__class__.__name__ == 'File' assert t[3].path == 'd' assert t[3].always_build + assert t[4].__class__.__name__ == 'File' + assert t[4].path == 'bbb' + assert t[4].always_build def test_Precious(self): """Test the Precious() method.""" - env = Environment() - t = env.Precious('a', 'b', ['c', 'd']) + env = Environment(FOO='ggg', BAR='hhh') + t = env.Precious('a', '${BAR}b', ['c', 'd'], '$FOO') assert t[0].__class__.__name__ == 'File' assert t[0].path == 'a' assert t[0].precious assert t[1].__class__.__name__ == 'File' - assert t[1].path == 'b' + assert t[1].path == 'hhhb' assert t[1].precious assert t[2].__class__.__name__ == 'File' assert t[2].path == 'c' @@ -623,6 +776,9 @@ class EnvironmentTestCase(unittest.TestCase): assert t[3].__class__.__name__ == 'File' assert t[3].path == 'd' assert t[3].precious + assert t[4].__class__.__name__ == 'File' + assert t[4].path == 'ggg' + assert t[4].precious def test_Command(self): """Test the Command() method.""" @@ -654,28 +810,47 @@ class EnvironmentTestCase(unittest.TestCase): def test_SourceCode(self): """Test the SourceCode() method.""" - env = Environment() + env = Environment(FOO='mmm', BAR='nnn') e = env.SourceCode('foo', None) + assert e.path == 'foo' s = e.src_builder() assert s is None, s b = Builder() - env.SourceCode(e, b) + e = env.SourceCode(e, b) + assert e.path == 'foo' s = e.src_builder() assert s is b, s + e = env.SourceCode('$BAR$FOO', None) + assert e.path == 'nnnmmm' + s = e.src_builder() + assert s is None, s + def test_SideEffect(self): """Test the SideEffect() method""" - env = Environment() + env = Environment(LIB='lll', FOO='fff', BAR='bbb') + foo = env.Object('foo.obj', 'foo.cpp') bar = env.Object('bar.obj', 'bar.cpp') s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj']) + assert s.path == 'mylib.pdb' assert s.side_effect assert foo.side_effects == [s] assert bar.side_effects == [s] assert s.depends_on([bar]) assert s.depends_on([foo]) + fff = env.Object('fff.obj', 'fff.cpp') + bbb = env.Object('bbb.obj', 'bbb.cpp') + s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj']) + assert s.path == 'mylll.pdb' + assert s.side_effect + assert fff.side_effects == [s], fff.side_effects + assert bbb.side_effects == [s], bbb.side_effects + assert s.depends_on([bbb]) + assert s.depends_on([fff]) + def test_subst(self): """Test substituting construction variables within strings diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 3d3f563..38b25af 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -575,7 +575,9 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None): strSubst = string.replace(string.replace(strSubst, '\0\4', '$'), '\0\5', '') # strip out redundant white-space - return string.strip(_space_sep.sub(' ', strSubst)) + if mode != SUBST_RAW: + strSubst = string.strip(_space_sep.sub(' ', strSubst)) + return strSubst def render_tree(root, child_func, prune=0, margin=[0], visited={}): """ diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index 23120e6..09b96d9 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -172,17 +172,23 @@ class UtilTestCase(unittest.TestCase): assert newcom == cvt("test foo/blah.cppblah /bar/ack.cppblah"), newcom newcom = scons_subst("test $xxx", env) + assert newcom == cvt("test "), newcom + newcom = scons_subst("test $xxx", env, mode=SUBST_CMD) + assert newcom == cvt("test"), newcom + newcom = scons_subst("test $xxx", env, mode=SUBST_SIG) assert newcom == cvt("test"), newcom newcom = scons_subst("test $($xxx$)", env) assert newcom == cvt("test $($)"), newcom - - newcom = scons_subst("test $( $xxx $)", env) - assert newcom == cvt("test $( $)"), newcom - + newcom = scons_subst("test $($xxx$)", env, mode=SUBST_CMD) + assert newcom == cvt("test"), newcom newcom = scons_subst("test $($xxx$)", env, mode=SUBST_SIG) assert newcom == cvt("test"), newcom + newcom = scons_subst("test $( $xxx $)", env) + assert newcom == cvt("test $( $)"), newcom + newcom = scons_subst("test $( $xxx $)", env, mode=SUBST_CMD) + assert newcom == cvt("test"), newcom newcom = scons_subst("test $( $xxx $)", env, mode=SUBST_SIG) assert newcom == cvt("test"), newcom diff --git a/test/AlwaysBuild.py b/test/AlwaysBuild.py index f4126c1..832120a 100644 --- a/test/AlwaysBuild.py +++ b/test/AlwaysBuild.py @@ -31,25 +31,36 @@ import TestSCons test = TestSCons.TestSCons() -test.write('SConstruct', """ +test.subdir('sub') + +test.write('SConstruct', """\ def bfunc(target, source, env): import shutil shutil.copyfile('f2.in', str(target[0])) B = Builder(action=bfunc) -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(BUILDERS = { 'B' : B }, SUBDIR='sub') env.B('f1.out', source='f1.in') env.AlwaysBuild('f1.out') -""") + +env.B(r'%s', source='f3.in') +env.AlwaysBuild(r'%s') +""" % (os.path.join('sub', 'f3.out'), + os.path.join('$SUBDIR', 'f3.out') + )) test.write('f1.in', "f1.in\n") test.write('f2.in', "1") +test.write('f3.in', "f3.in\n") test.run(arguments = ".") test.fail_test(test.read('f1.out') != '1') +test.fail_test(test.read(['sub', 'f3.out']) != '1') test.write('f2.in', "2") + test.run(arguments = ".") test.fail_test(test.read('f1.out') != '2') +test.fail_test(test.read(['sub', 'f3.out']) != '2') test.pass_test() diff --git a/test/Depends.py b/test/Depends.py index dd3c2b5..d65f2b6 100644 --- a/test/Depends.py +++ b/test/Depends.py @@ -24,7 +24,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import sys +import os.path + import TestSCons python = TestSCons.python @@ -44,14 +45,17 @@ file.close() test.write('SConstruct', """ Foo = Builder(action = r"%s build.py $TARGET $SOURCES subdir/foo.dep") Bar = Builder(action = r"%s build.py $TARGET $SOURCES subdir/bar.dep") -env = Environment(BUILDERS = { 'Foo' : Foo, 'Bar' : Bar }) -env.Depends(target = ['f1.out', 'f2.out'], dependency = 'subdir/foo.dep') -env.Depends(target = 'f3.out', dependency = 'subdir/bar.dep') +env = Environment(BUILDERS = { 'Foo' : Foo, 'Bar' : Bar }, SUBDIR='subdir') +env.Depends(target = ['f1.out', 'f2.out'], dependency = r'%s') +env.Depends(target = r'%s', dependency = 'subdir/bar.dep') env.Foo(target = 'f1.out', source = 'f1.in') env.Foo(target = 'f2.out', source = 'f2.in') -env.Bar(target = 'f3.out', source = 'f3.in') +env.Bar(target = 'subdir/f3.out', source = 'f3.in') SConscript('subdir/SConscript', "env") -""" % (python, python)) +""" % (python, + python, + os.path.join('$SUBDIR', 'foo.dep'), + os.path.join('$SUBDIR', 'f3.out'))) test.write(['subdir', 'SConscript'], """ Import("env") @@ -71,11 +75,11 @@ test.write(['subdir', 'foo.dep'], "subdir/foo.dep 1\n") test.write(['subdir', 'bar.dep'], "subdir/bar.dep 1\n") -test.run(arguments = '.') +test.run(arguments = '--debug=dtree .') test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 1\n") test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 1\n") -test.fail_test(test.read('f3.out') != "f3.in\nsubdir/bar.dep 1\n") +test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 1\n") test.fail_test(test.read(['subdir', 'f4.out']) != "subdir/f4.in\nsubdir/bar.dep 1\n") @@ -87,7 +91,7 @@ test.run(arguments = '.') test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 2\n") test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 2\n") -test.fail_test(test.read('f3.out') != "f3.in\nsubdir/bar.dep 2\n") +test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 2\n") test.fail_test(test.read(['subdir', 'f4.out']) != "subdir/f4.in\nsubdir/bar.dep 2\n") @@ -97,7 +101,7 @@ test.run(arguments = '.') test.fail_test(test.read('f1.out') != "f1.in\nsubdir/foo.dep 2\n") test.fail_test(test.read('f2.out') != "f2.in\nsubdir/foo.dep 2\n") -test.fail_test(test.read('f3.out') != "f3.in\nsubdir/bar.dep 3\n") +test.fail_test(test.read(['subdir', 'f3.out']) != "f3.in\nsubdir/bar.dep 3\n") test.fail_test(test.read(['subdir', 'f4.out']) != "subdir/f4.in\nsubdir/bar.dep 3\n") diff --git a/test/Ignore.py b/test/Ignore.py index 5d87bba..6809434 100644 --- a/test/Ignore.py +++ b/test/Ignore.py @@ -24,7 +24,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import sys +import os.path + import TestSCons python = TestSCons.python @@ -42,14 +43,19 @@ for arg in sys.argv[2:]: file.close() """) -test.write('SConstruct', """ +test.write('SConstruct', """\ Foo = Builder(action = r"%s build.py $TARGET $SOURCES") Bar = Builder(action = r"%s build.py $TARGET $SOURCES") -env = Environment(BUILDERS = { 'Foo' : Foo, 'Bar' : Bar }) +env = Environment(BUILDERS = { 'Foo' : Foo, 'Bar' : Bar }, SUBDIR='subdir') env.Foo(target = 'f1.out', source = ['f1a.in', 'f1b.in']) env.Ignore(target = 'f1.out', dependency = 'f1b.in') SConscript('subdir/SConscript', "env") -""" % (python, python)) +env.Foo(target = 'subdir/f3.out', source = ['subdir/f3a.in', 'subdir/f3b.in']) +env.Ignore(target = r'%s', dependency = r'%s') +""" % (python, + python, + os.path.join('$SUBDIR', 'f3.out'), + os.path.join('$SUBDIR', 'f3b.in'))) test.write(['subdir', 'SConscript'], """ Import("env") @@ -63,31 +69,42 @@ test.write('f1b.in', "f1b.in\n") test.write(['subdir', 'f2a.in'], "subdir/f2a.in\n") test.write(['subdir', 'f2b.in'], "subdir/f2b.in\n") +test.write(['subdir', 'f3a.in'], "subdir/f3a.in\n") +test.write(['subdir', 'f3b.in'], "subdir/f3b.in\n") + test.run(arguments = '.') test.fail_test(test.read('f1.out') != "f1a.in\nf1b.in\n") test.fail_test(test.read(['subdir', 'f2.out']) != "subdir/f2a.in\nsubdir/f2b.in\n") +test.fail_test(test.read(['subdir', 'f3.out']) != + "subdir/f3a.in\nsubdir/f3b.in\n") test.up_to_date(arguments = '.') test.write('f1b.in', "f1b.in 2\n") test.write(['subdir', 'f2a.in'], "subdir/f2a.in 2\n") +test.write(['subdir', 'f3b.in'], "subdir/f3b.in 2\n") test.up_to_date(arguments = '.') test.fail_test(test.read('f1.out') != "f1a.in\nf1b.in\n") test.fail_test(test.read(['subdir', 'f2.out']) != "subdir/f2a.in\nsubdir/f2b.in\n") +test.fail_test(test.read(['subdir', 'f3.out']) != + "subdir/f3a.in\nsubdir/f3b.in\n") test.write('f1a.in', "f1a.in 2\n") test.write(['subdir', 'f2b.in'], "subdir/f2b.in 2\n") +test.write(['subdir', 'f3a.in'], "subdir/f3a.in 2\n") test.run(arguments = '.') test.fail_test(test.read('f1.out') != "f1a.in 2\nf1b.in 2\n") test.fail_test(test.read(['subdir', 'f2.out']) != "subdir/f2a.in 2\nsubdir/f2b.in 2\n") +test.fail_test(test.read(['subdir', 'f3.out']) != + "subdir/f3a.in 2\nsubdir/f3b.in 2\n") test.up_to_date(arguments = '.') diff --git a/test/Install.py b/test/Install.py index f69c954..7637662 100644 --- a/test/Install.py +++ b/test/Install.py @@ -35,9 +35,12 @@ import TestSCons test = TestSCons.TestSCons() +test.subdir('sub') + f1_out = test.workpath('export', 'f1.out') f2_out = test.workpath('export', 'f2.out') f3_out = test.workpath('export', 'f3.out') +f4_out = test.workpath('export', 'f4.out') test.write('SConstruct', """\ def cat(env, source, target): @@ -64,17 +67,23 @@ env1.Install(dir='export', source=t) t = env3.Cat(target='f3.out', source='f3.in') env3.Install(dir='export', source=t) -""") + +env4 = env1.Copy(EXPORT='export', SUBDIR='sub') +t = env4.Cat(target='sub/f4.out', source='sub/f4.in') +env4.Install(dir='$EXPORT', source=r'%s') +""" % (os.path.join('$SUBDIR', 'f4.out'))) test.write('f1.in', "f1.in\n") test.write('f2.in', "f2.in\n") test.write('f3.in', "f3.in\n") +test.write(['sub', 'f4.in'], "sub/f4.in\n") test.run(arguments = '.') test.fail_test(test.read(f1_out) != "f1.in\n") test.fail_test(test.read(f2_out) != "f2.in\n") test.fail_test(test.read(f3_out) != "f3.in\n") +test.fail_test(test.read(f4_out) != "sub/f4.in\n") test.fail_test(test.read('my_install.out') != os.path.join('export', 'f3.out')) diff --git a/test/InstallAs.py b/test/InstallAs.py new file mode 100644 index 0000000..643ac85 --- /dev/null +++ b/test/InstallAs.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the InstallAs() Environment method. +""" + +import os.path +import sys +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('install', 'subdir') + +install = test.workpath('install') +install_file1_out = test.workpath('install', 'file1.out') +install_file2_out = test.workpath('install', 'file2.out') +install_file3_out = test.workpath('install', 'file3.out') + +# +test.write('SConstruct', r""" +env = Environment(INSTALLDIR=r'%s', SUBDIR='subdir') +env.InstallAs(r'%s', 'file1.in') +env.InstallAs([r'%s', r'%s'], ['file2.in', r'%s']) +""" % (install, + install_file1_out, + os.path.join('$INSTALLDIR', 'file2.out'), + install_file3_out, + os.path.join('$SUBDIR', 'file3.in'))) + +test.write('file1.in', "file1.in\n") +test.write('file2.in', "file2.in\n") +test.write(['subdir', 'file3.in'], "subdir/file3.in\n") + +test.run(arguments = '.') + +test.fail_test(test.read(install_file1_out) != "file1.in\n") +test.fail_test(test.read(install_file2_out) != "file2.in\n") +test.fail_test(test.read(install_file3_out) != "subdir/file3.in\n") + +test.up_to_date(arguments = '.') + +# +test.pass_test() diff --git a/test/Precious.py b/test/Precious.py index 8b2c7ff..6aee9b0 100644 --- a/test/Precious.py +++ b/test/Precious.py @@ -24,8 +24,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import os -import sys +import os.path + import TestSCons python = TestSCons.python @@ -39,74 +39,81 @@ import sys sys.exit(0) """) -test.write('SConstruct', """ +test.write('SConstruct', """\ B = Builder(action = r"%s build.py $TARGET $SOURCES") -env = Environment(BUILDERS = { 'B' : B }) +env = Environment(BUILDERS = { 'B' : B }, SUBDIR = 'subdir') f1 = env.B(target = 'f1.out', source = 'f1.in') env.B(target = 'f2.out', source = 'f2.in') env.B(target = 'f3.out', source = 'f3.in') -env.Precious(f1, 'f2.out') +env.B(target = 'subdir/f4.out', source = 'f4.in') +env.Precious(f1, 'f2.out', r'%s') SConscript('subdir/SConscript', "env") -""" % python) +""" % (python, + os.path.join('$SUBDIR', 'f4.out'))) test.write(['subdir', 'SConscript'], """ Import("env") -env.B(target = 'f4.out', source = 'f4.in') -f5 = env.B(target = 'f5.out', source = 'f5.in') -env.B(target = 'f6.out', source = 'f6.in') -env.Precious(['f4.out', f5]) +env.B(target = 'f5.out', source = 'f5.in') +f6 = env.B(target = 'f6.out', source = 'f6.in') +env.B(target = 'f7.out', source = 'f7.in') +env.Precious(['f5.out', f6]) """) test.write('f1.in', "f1.in\n") test.write('f2.in', "f2.in\n") test.write('f3.in', "f3.in\n") +test.write('f4.in', "f4.in\n") -test.write(['subdir', 'f4.in'], "subdir/f4.in\n") test.write(['subdir', 'f5.in'], "subdir/f5.in\n") test.write(['subdir', 'f6.in'], "subdir/f6.in\n") +test.write(['subdir', 'f7.in'], "subdir/f7.in\n") test.write('f1.out', "SHOULD NOT BE REMOVED\n") test.write('f2.out', "SHOULD NOT BE REMOVED\n") test.write('f3.out', "SHOULD BE REMOVED\n") - test.write(['subdir', 'f4.out'], "SHOULD NOT BE REMOVED\n") + test.write(['subdir', 'f5.out'], "SHOULD NOT BE REMOVED\n") -test.write(['subdir', 'f6.out'], "SHOULD BE REMOVED\n") +test.write(['subdir', 'f6.out'], "SHOULD NOT BE REMOVED\n") +test.write(['subdir', 'f7.out'], "SHOULD BE REMOVED\n") test.run(arguments = '.') test.fail_test(not os.path.exists(test.workpath('f1.out'))) test.fail_test(not os.path.exists(test.workpath('f2.out'))) test.fail_test(os.path.exists(test.workpath('f3.out'))) - test.fail_test(not os.path.exists(test.workpath('subdir', 'f4.out'))) + test.fail_test(not os.path.exists(test.workpath('subdir', 'f5.out'))) -test.fail_test(os.path.exists(test.workpath('subdir', 'f6.out'))) +test.fail_test(not os.path.exists(test.workpath('subdir', 'f6.out'))) +test.fail_test(os.path.exists(test.workpath('subdir', 'f7.out'))) test.write('f3.out', "SHOULD BE REMOVED\n") -test.write(['subdir', 'f6.out'], "SHOULD BE REMOVED\n") +test.write(['subdir', 'f7.out'], "SHOULD BE REMOVED\n") test.run(arguments = '.') test.fail_test(not os.path.exists(test.workpath('f1.out'))) test.fail_test(not os.path.exists(test.workpath('f2.out'))) test.fail_test(not os.path.exists(test.workpath('f3.out'))) - test.fail_test(not os.path.exists(test.workpath('subdir', 'f4.out'))) + test.fail_test(not os.path.exists(test.workpath('subdir', 'f5.out'))) test.fail_test(not os.path.exists(test.workpath('subdir', 'f6.out'))) +test.fail_test(not os.path.exists(test.workpath('subdir', 'f7.out'))) test.write('f3.in', "f3.in 2\n") -test.write(['subdir', 'f6.in'], "subdir/f6.in 2\n") +test.write(['subdir', 'f7.in'], "subdir/f7.in 2\n") test.run(arguments = '.') test.fail_test(not os.path.exists(test.workpath('f1.out'))) test.fail_test(not os.path.exists(test.workpath('f2.out'))) test.fail_test(os.path.exists(test.workpath('f3.out'))) - test.fail_test(not os.path.exists(test.workpath('subdir', 'f4.out'))) + test.fail_test(not os.path.exists(test.workpath('subdir', 'f5.out'))) -test.fail_test(os.path.exists(test.workpath('subdir', 'f6.out'))) +test.fail_test(not os.path.exists(test.workpath('subdir', 'f6.out'))) +test.fail_test(os.path.exists(test.workpath('subdir', 'f7.out'))) test.pass_test() diff --git a/test/SideEffect.py b/test/SideEffect.py index ca2dc4f..61b0d58 100644 --- a/test/SideEffect.py +++ b/test/SideEffect.py @@ -24,13 +24,13 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -import TestSCons import os.path +import TestSCons + test = TestSCons.TestSCons() -test.write('SConstruct', -""" +test.write('SConstruct', """\ def copy(source, target): open(target, "wb").write(open(source, "rb").read()) @@ -38,20 +38,25 @@ def build(env, source, target): copy(str(source[0]), str(target[0])) if target[0].side_effects: side_effect = open(str(target[0].side_effects[0]), "ab") - side_effect.write('%s -> %s\\n'%(str(source[0]), str(target[0]))) + side_effect.write('%%s -> %%s\\n'%%(str(source[0]), str(target[0]))) Build = Builder(action=build) -env = Environment(BUILDERS={'Build':Build}) +env = Environment(BUILDERS={'Build':Build}, SUBDIR='subdir') env.Build('foo.out', 'foo.in') env.Build('bar.out', 'bar.in') env.Build('blat.out', 'blat.in') env.SideEffect('log.txt', ['foo.out', 'bar.out', 'blat.out']) env.Build('log.out', 'log.txt') -""") +env.Build('subdir/baz.out', 'baz.in') +env.SideEffect(r'%s', ['blat.out', r'%s']) +env.Build('subdir/out.out', 'subdir/out.txt') +""" % (os.path.join('$SUBDIR', 'out.txt'), + os.path.join('$SUBDIR', 'baz.out'))) test.write('foo.in', 'foo.in\n') test.write('bar.in', 'bar.in\n') test.write('blat.in', 'blat.in\n') +test.write('baz.in', 'baz.in\n') test.run(arguments = 'foo.out bar.out', stdout=test.wrap_stdout("""\ build("foo.out", "foo.in") @@ -84,7 +89,11 @@ test.write('foo.in', 'foo.in 2 \n') test.run(arguments = ".", stdout=test.wrap_stdout("""\ build("foo.out", "foo.in") build("log.out", "log.txt") -""")) +build("%s", "baz.in") +build("%s", "%s") +""" % (os.path.join('subdir', 'baz.out'), + os.path.join('subdir', 'out.out'), + os.path.join('subdir', 'out.txt')))) expect = """\ foo.in -> foo.out @@ -107,7 +116,11 @@ build("bar.out", "bar.in") build("blat.out", "blat.in") build("foo.out", "foo.in") build("log.out", "log.txt") -""")) +build("%s", "baz.in") +build("%s", "%s") +""" % (os.path.join('subdir', 'baz.out'), + os.path.join('subdir', 'out.out'), + os.path.join('subdir', 'out.txt')))) expect = """\ bar.in -> bar.out diff --git a/test/SourceCode.py b/test/SourceCode.py new file mode 100644 index 0000000..cd8baf4 --- /dev/null +++ b/test/SourceCode.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test fetching source files using the SourceCode() method. +""" + +import os +import stat + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('sub') + +test.write('SConstruct', """\ +import os.path + +def cat(env, source, target): + target = str(target[0]) + source = map(str, source) + f = open(target, "wb") + for src in source: + f.write(open(src, "rb").read()) + f.close() + +def sc_cat(env, source, target): + source = [] + for t in target: + head, tail = os.path.split(str(t)) + source.append(os.path.join(head, 'sc-' + tail)) + cat(env, source, target) + +env = Environment(BUILDERS={'Cat':Builder(action=cat)}, SUBDIR='sub') +env.Cat('aaa.out', 'sub/aaa.in') +env.Cat('bbb.out', 'sub/bbb.in') +env.Cat('ccc.out', 'sub/ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +env.SourceCode('$SUBDIR', Builder(action=sc_cat, env=env)) +SConscript('sub/SConscript', "env") +""") + +test.write(['sub', 'sc-aaa.in'], "sub/sc-aaa.in\n") +test.write(['sub', 'sc-bbb.in'], "sub/sc-bbb.in\n") +test.write(['sub', 'sc-ccc.in'], "sub/sc-ccc.in\n") + +test.write(['sub', 'sc-SConscript'], "'sub/sc-SConscript'\n") + +test.run(arguments = '.', + stdout = test.wrap_stdout(read_str = """\ +sc_cat("%s", []) +""" % (os.path.join('sub', 'SConscript')), + build_str = """\ +sc_cat("%s", []) +cat("aaa.out", "%s") +sc_cat("%s", []) +cat("bbb.out", "%s") +sc_cat("%s", []) +cat("ccc.out", "%s") +cat("all", ["aaa.out", "bbb.out", "ccc.out"]) +""" % (os.path.join('sub', 'aaa.in'), + os.path.join('sub', 'aaa.in'), + os.path.join('sub', 'bbb.in'), + os.path.join('sub', 'bbb.in'), + os.path.join('sub', 'ccc.in'), + os.path.join('sub', 'ccc.in')))) + +test.fail_test(test.read(['sub', 'SConscript']) != "'sub/sc-SConscript'\n") +test.fail_test(test.read('all') != "sub/sc-aaa.in\nsub/sc-bbb.in\nsub/sc-ccc.in\n") + +test.pass_test() diff --git a/test/expansion.py b/test/expansion.py new file mode 100644 index 0000000..7b8676b --- /dev/null +++ b/test/expansion.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test construction variable expansion in Builder paths. +""" + +import os.path +import sys +import time +import TestSCons + +_exe = TestSCons._exe +_obj = TestSCons._obj + +test = TestSCons.TestSCons() + +test.subdir('sub') + +test.write('SConstruct', """\ +env = Environment(SUBDIR = 'sub') +env.Program(target = 'foo1', source = env.Object(source = r'%s')) +env.Program(source = env.Object(target = r'%s', source = 'f2.c')) +env.Program('foo3', r'%s') +env.Program(r'%s') +""" % (os.path.join('$SUBDIR', 'f1.c'), + os.path.join('$SUBDIR', 'foo2'), + os.path.join('$SUBDIR', 'f3.c'), + os.path.join('$SUBDIR', 'foo4.c'))) + +test.write(['sub', 'f1.c'], r""" +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("sub/f1.c\n"); + exit (0); +} +""") + +test.write('f2.c', r""" +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("f2.c\n"); + exit (0); +} +""") + +test.write(['sub', 'f3.c'], r""" +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("sub/f3.c\n"); + exit (0); +} +""") + +test.write(['sub', 'foo4.c'], r""" +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("sub/foo4.c\n"); + exit (0); +} +""") + +test.run(arguments = '.') + +test.run(program = test.workpath('foo1' + _exe), stdout = "sub/f1.c\n") +test.run(program = test.workpath('sub', 'foo2' + _exe), stdout = "f2.c\n") +test.run(program = test.workpath('foo3' + _exe), stdout = "sub/f3.c\n") +test.run(program = test.workpath('sub','foo4' + _exe), stdout = "sub/foo4.c\n") + +test.fail_test(not os.path.exists(test.workpath('sub', 'f1' + _obj))) +test.fail_test(not os.path.exists(test.workpath('sub', 'foo2' + _obj))) +test.fail_test(not os.path.exists(test.workpath('sub', 'f3' + _obj))) +test.fail_test(not os.path.exists(test.workpath('sub', 'foo4' + _obj))) + +test.up_to_date(arguments = '.') + +test.pass_test() diff --git a/test/special-filenames.py b/test/special-filenames.py index 2002b37..98096b7 100644 --- a/test/special-filenames.py +++ b/test/special-filenames.py @@ -45,7 +45,7 @@ attempt_file_names = [ "File&with&ersand", "File?with?question", "File\twith\ttab", - "File$with$dollar", + "File$$with$$dollar", "Combination '\"\n\\;<>?|*\t&" ] @@ -57,7 +57,8 @@ open(sys.argv[1], 'wb').write(open(sys.argv[2], 'rb').read()) file_names = [] for fn in attempt_file_names: try: - test.write(fn + '.in', fn + '\n') + in_name = string.replace(fn, '$$', '$') + '.in' + test.write(in_name, fn + '\n') file_names.append(fn) except IOError: # if the Python interpreter can't handle it, don't bother @@ -76,6 +77,7 @@ env=Environment(BUILDERS = {'Build' : Builder(action = '%s cat.py $TARGET $SOURC test.run(arguments='.') for fn in file_names: - test.fail_test(test.read(fn + '.out') != fn + '\n') + out_name = string.replace(fn, '$$', '$') + '.out' + test.fail_test(test.read(out_name) != fn + '\n') test.pass_test() -- cgit v0.12