From: stevenknight Date: Fri, 5 Mar 2004 09:49:04 +0000 (+0000) Subject: Fix use of things like ${TARGET.dir} in *PATH expansions. X-Git-Url: http://git.tremily.us/?a=commitdiff_plain;h=3fd03a1a0a3331982584bdf0bad4d6601d1d3466;p=scons.git Fix use of things like ${TARGET.dir} in *PATH expansions. git-svn-id: http://scons.tigris.org/svn/scons/trunk@915 fdb21ef1-2011-0410-befe-b5e4ea1792b1 --- diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 76f08b52..10609395 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -192,6 +192,8 @@ RELEASE 0.95 - XXX - Initialize all *FLAGS variables with objects do the right thing with appending flags as strings or lists. + - Make things like ${TARGET.dir} work in *PATH construction variables. + From Vincent Risi: - Add support for the bcc32, ilink32 and tlib Borland tools. diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 2235ace4..da455bcf 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -366,17 +366,17 @@ class Base: return scanner return None - def subst(self, string, raw=0, target=None, source=None, dict=None): - """Recursively interpolates construction variables from the - Environment into the specified string, returning the expanded - result. Construction variables are specified by a $ prefix - in the string and begin with an initial underscore or - alphabetic character followed by any number of underscores - or alphanumeric characters. The construction variable names - may be surrounded by curly braces to separate the name from - trailing characters. - """ - return SCons.Util.scons_subst(string, self, raw, target, source, dict) + def subst(self, string, raw=0, target=None, source=None, dict=None, conv=None): + """Recursively interpolates construction variables from the + Environment into the specified string, returning the expanded + result. Construction variables are specified by a $ prefix + in the string and begin with an initial underscore or + alphabetic character followed by any number of underscores + or alphanumeric characters. The construction variable names + may be surrounded by curly braces to separate the name from + trailing characters. + """ + return SCons.Util.scons_subst(string, self, raw, target, source, dict, conv) def subst_kw(self, kw, raw=0, target=None, source=None, dict=None): nkw = {} @@ -387,21 +387,42 @@ class Base: nkw[k] = v return nkw - def subst_list(self, string, raw=0, target=None, source=None, dict=None): + def subst_list(self, string, raw=0, target=None, source=None, dict=None, conv=None): """Calls through to SCons.Util.scons_subst_list(). See the documentation for that function.""" - return SCons.Util.scons_subst_list(string, self, raw, target, source, dict) + return SCons.Util.scons_subst_list(string, self, raw, target, source, dict, conv) + def subst_path(self, path): - """Substitute a path list.""" + """Substitute a path list, turning EntryProxies into Nodes + and leaving Nodes (and other objects) as-is.""" if not SCons.Util.is_List(path): path = [path] + def s(obj): + """This is the "string conversion" routine that we have our + substitutions use to return Nodes, not strings. This relies + on the fact that an EntryProxy object has a get() method that + returns the underlying Node that it wraps, which is a bit of + architectural dependence that we might need to break or modify + in the future in response to additional requirements.""" + try: + get = obj.get + except AttributeError: + pass + else: + obj = get() + return obj + r = [] for p in path: if SCons.Util.is_String(p): - p = self.subst(p) + p = self.subst(p, conv=s) + if SCons.Util.is_List(p): + p = p[0] + else: + p = s(p) r.append(p) return r diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 45f5fdbc..d450075e 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -419,7 +419,16 @@ class EnvironmentTestCase(unittest.TestCase): def test_subst_path(self): """Test substituting a path list """ - env = Environment(FOO='foo', BAR='bar') + class MyProxy: + def __init__(self, val): + self.val = val + def get(self): + return self.val + '-proxy' + + class MyObj: + pass + + env = Environment(FOO='foo', BAR='bar', PROXY=MyProxy('my1')) r = env.subst_path('$FOO') assert r == ['foo'], r @@ -427,6 +436,11 @@ class EnvironmentTestCase(unittest.TestCase): r = env.subst_path(['$FOO', 'xxx', '$BAR']) assert r == ['foo', 'xxx', 'bar'], r + n = MyObj() + + r = env.subst_path(['$PROXY', MyProxy('my2'), n]) + assert r == ['my1-proxy', 'my2-proxy', n], r + def test_Builder_calls(self): """Test Builder calls through different environments """ diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 97ba8b5e..1722f3a4 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -501,7 +501,7 @@ _separate_args = re.compile(r'(\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}|\s+|[^\s\ # space characters in the string result from the scons_subst() function. _space_sep = re.compile(r'[\t ]+(?![^{]*})') -def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None): +def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None): """Expand a string containing construction variable substitutions. This is the work-horse function for substitutions in file names @@ -516,14 +516,14 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No source with two methods (substitute() and expand()) that handle the expansion. """ - def __init__(self, env, mode, target, source): + def __init__(self, env, mode, target, source, conv): self.env = env self.mode = mode self.target = target self.source = source self.gvars = env.Dictionary() - self.str = _strconv[mode] + self.conv = conv def expand(self, s, lvars): """Expand a single "token" as necessary, returning an @@ -570,7 +570,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No elif is_List(s): r = [] for l in s: - r.append(self.str(self.substitute(l, lvars))) + r.append(self.conv(self.substitute(l, lvars))) return string.join(r) elif callable(s): s = s(target=self.target, @@ -593,15 +593,21 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No args = _separate_args.findall(args) result = [] for a in args: - result.append(self.str(self.expand(a, lvars))) - return string.join(result, '') + result.append(self.conv(self.expand(a, lvars))) + try: + result = string.join(result, '') + except TypeError: + pass + return result else: return self.expand(args, lvars) if dict is None: dict = subst_dict(target, source) + if conv is None: + conv = _strconv[mode] - ss = StringSubber(env, mode, target, source) + ss = StringSubber(env, mode, target, source, conv) result = ss.substitute(strSubst, dict) if is_String(result): @@ -617,7 +623,7 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=No return result -def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None): +def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, dict=None, conv=None): """Substitute construction variables in a string (or list or other object) and separate the arguments into a command list. @@ -641,7 +647,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di and the rest of the object takes care of doing the right thing internally. """ - def __init__(self, env, mode, target, source): + def __init__(self, env, mode, target, source, conv): UserList.UserList.__init__(self, []) self.env = env self.mode = mode @@ -654,7 +660,7 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di self.add_strip = lambda x, s=self: s.append(x) else: self.add_strip = lambda x, s=self: None - self.str = _strconv[mode] + self.conv = conv self.in_strip = None self.next_line() @@ -768,7 +774,10 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di literal = None else: literal = l() - self[-1].append(CmdStringHolder(self.str(x), literal)) + x = self.conv(x) + if is_String(x): + x = CmdStringHolder(x, literal) + self[-1].append(x) self.append = self.add_to_current_word def open_strip(self, x): @@ -782,8 +791,10 @@ def scons_subst_list(strSubst, env, mode=SUBST_RAW, target=None, source=None, di if dict is None: dict = subst_dict(target, source) + if conv is None: + conv = _strconv[mode] - ls = ListSubber(env, mode, target, source) + ls = ListSubber(env, mode, target, source, conv) ls.substitute(strSubst, dict, 0) return ls.data diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index c96f1249..38e8c30f 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -429,6 +429,19 @@ class UtilTestCase(unittest.TestCase): else: raise AssertionError, "did not catch expected UserError" + # Test we handle overriding the internal conversion routines. + def s(obj): + return obj + + n1 = MyNode('n1') + env = DummyEnv({'NODE' : n1}) + node = scons_subst("$NODE", env, mode=SUBST_RAW, conv=s) + assert node == [n1], node + node = scons_subst("$NODE", env, mode=SUBST_CMD, conv=s) + assert node == [n1], node + node = scons_subst("$NODE", env, mode=SUBST_SIG, conv=s) + assert node == [n1], node + # Test returning a function. #env = DummyEnv({'FUNCTION' : foo}) #func = scons_subst("$FUNCTION", env, mode=SUBST_RAW, call=None) @@ -771,6 +784,19 @@ class UtilTestCase(unittest.TestCase): else: raise AssertionError, "did not catch expected SyntaxError" + # Test we handle overriding the internal conversion routines. + def s(obj): + return obj + + n1 = MyNode('n1') + env = DummyEnv({'NODE' : n1}) + node = scons_subst_list("$NODE", env, mode=SUBST_RAW, conv=s) + assert node == [[n1]], node + node = scons_subst_list("$NODE", env, mode=SUBST_CMD, conv=s) + assert node == [[n1]], node + node = scons_subst_list("$NODE", env, mode=SUBST_SIG, conv=s) + assert node == [[n1]], node + def test_subst_once(self): """Testing the scons_subst_once() method""" diff --git a/test/TARGET-dir.py b/test/TARGET-dir.py new file mode 100644 index 00000000..c599a160 --- /dev/null +++ b/test/TARGET-dir.py @@ -0,0 +1,87 @@ +#!/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 use of ${TARGET.dir} to specify a CPPPATH directory in +combination BuildDirs and a generated .h file. +""" + +import TestSCons + +_exe = TestSCons._exe + +test = TestSCons.TestSCons() + +build1_foo = test.workpath('build1', 'foo' + _exe) +build2_foo = test.workpath('build2', 'foo' + _exe) + +test.subdir('src', 'build1', 'build2') + +test.write('SConstruct', """ +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() +env = Environment(CPPPATH='${TARGET.dir}') +env.Append(BUILDERS = {'Cat' : Builder(action=cat)}) +Export('env') +BuildDir('build1', 'src') +SConscript('build1/SConscript') +BuildDir('build2', 'src') +SConscript('build2/SConscript', duplicate=0) +""") + +test.write(['src', 'SConscript'], """ +Import('env') +env.Cat('foo.h', 'foo.h.in') +env.Program('foo', ['foo.c']) +""") + +test.write(['src', 'foo.h.in'], """\ +#define STRING "foo.h.in\\n" +""") + +test.write(['src', 'foo.c'], """\ +#include + +int +main(int argc, char *argv[]) +{ + printf(STRING); + printf("foo.c\\n"); + exit (0); +} +""") + +test.run(arguments = '.') + +test.run(program = build1_foo, stdout = "foo.h.in\nfoo.c\n") +test.run(program = build2_foo, stdout = "foo.h.in\nfoo.c\n") + +test.pass_test()