import os
import SCons.Node.FS
-import SCons.Util
+from SCons.Util import PathList, scons_str2nodes, scons_varrepl
import string
import types
return cmp(self.__dict__, other.__dict__)
def __call__(self, env, target = None, source = None):
- tlist = SCons.Util.scons_str2nodes(target, self.node_factory)
- slist = SCons.Util.scons_str2nodes(source, self.node_factory)
+ tlist = scons_str2nodes(target, self.node_factory)
+ slist = scons_str2nodes(source, self.node_factory)
for t in tlist:
t.builder_set(self)
t.env_set(env)
self.suffix=suffix
def __call__(self, env, target = None, source = None):
- tlist = SCons.Util.scons_str2nodes(target,
- self.subject.node_factory)
+ tlist = scons_str2nodes(target, self.subject.node_factory)
tlist_decorated = []
for tnode in tlist:
path, fn = os.path.split(tnode.path)
self.builder_dict[bld.insuffix] = bld
def __call__(self, env, target = None, source = None):
- slist = SCons.Util.scons_str2nodes(source, self.node_factory)
+ slist = scons_str2nodes(source, self.node_factory)
final_sources = []
for snode in slist:
path, ext = os.path.splitext(snode.path)
self.command = string
def execute(self, **kw):
- cmd = self.command % kw
+ try:
+ t = kw['target']
+ if type(t) is types.StringType:
+ t = [t]
+ tgt = PathList(map(os.path.normpath, t))
+ except:
+ tgt = PathList()
+ try:
+ s = kw['source']
+ if type(s) is types.StringType:
+ s = [s]
+ src = PathList(map(os.path.normpath, s))
+ except:
+ src = PathList()
+ cmd = scons_varrepl(self.command, tgt, src)
if print_actions:
self.show(cmd)
ret = 0
def test__call__(self):
"""Test calling a builder to establish source dependencies
"""
+ env = Environment()
class Node:
def __init__(self, name):
self.name = name
builder = SCons.Builder.Builder(action = cmd1)
r = builder.execute()
assert r == 0
- assert test.read(outfile, 'r') == "act.py: xyzzy\n"
+ c = test.read(outfile, 'r')
+ assert c == "act.py: xyzzy\n", c
+
+ cmd2 = "python %s %s $target" % (act_py, outfile)
+
+ builder = SCons.Builder.Builder(action = cmd2)
+ r = builder.execute(target = 'foo')
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: foo\n", c
+
+ cmd3 = "python %s %s ${targets}" % (act_py, outfile)
+
+ builder = SCons.Builder.Builder(action = cmd3)
+ r = builder.execute(target = ['aaa', 'bbb'])
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: aaa bbb\n", c
+
+ cmd4 = "python %s %s $sources" % (act_py, outfile)
+
+ builder = SCons.Builder.Builder(action = cmd4)
+ r = builder.execute(source = ['one', 'two'])
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: one two\n", c
+
+ cmd4 = "python %s %s ${sources[:2]}" % (act_py, outfile)
+
+ builder = SCons.Builder.Builder(action = cmd4)
+ r = builder.execute(source = ['three', 'four', 'five'])
+ assert r == 0
+ c = test.read(outfile, 'r')
+ assert c == "act.py: three four\n", c
def function1(kw):
open(kw['out'], 'w').write("function1\n")
builder = SCons.Builder.Builder(action = function1)
r = builder.execute(out = outfile)
assert r == 1
- assert test.read(outfile, 'r') == "function1\n"
+ c = test.read(outfile, 'r')
+ assert c == "function1\n", c
class class1a:
def __init__(self, kw):
builder = SCons.Builder.Builder(action = class1a)
r = builder.execute(out = outfile)
assert r.__class__ == class1a
- assert test.read(outfile, 'r') == "class1a\n"
+ c = test.read(outfile, 'r')
+ assert c == "class1a\n", c
class class1b:
def __call__(self, kw):
builder = SCons.Builder.Builder(action = class1b())
r = builder.execute(out = outfile)
assert r == 2
- assert test.read(outfile, 'r') == "class1b\n"
+ c = test.read(outfile, 'r')
+ assert c == "class1b\n", c
cmd2 = "python %s %s syzygy" % (act_py, outfile)
builder = SCons.Builder.Builder(action = [cmd2, function2, class2a(), class2b])
r = builder.execute(out = outfile)
assert r.__class__ == class2b
- assert test.read(outfile, 'r') == "act.py: syzygy\nfunction2\nclass2a\nclass2b\n"
+ c = test.read(outfile, 'r')
+ assert c == "act.py: syzygy\nfunction2\nclass2a\nclass2b\n", c
def test_insuffix(self):
"""Test Builder creation with a specified input suffix
Object = SCons.Builder.Builder(name = 'Object',
- action = 'cc -c -o %(target)s %(source)s',
+ action = 'cc -c -o $target $sources',
input_suffix='.c',
output_suffix='.o')
Program = SCons.Builder.MultiStepBuilder(name = 'Program',
- action = 'cc -o %(target)s %(source)s',
+ action = 'cc -o $target $sources',
builders = [ Object ])
Library = SCons.Builder.MultiStepBuilder(name = 'Library',
- action = 'ar r %(target)s %(source)s\nranlib %(target)s',
+ action = 'ar r $target $sources\nranlib $target',
builders = [ Object ])
-
+
Library = SCons.Builder.TargetNamingBuilder(builder = Library,
prefix='lib',
suffix='.a')
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import os.path
import types
import string
+import re
+from UserList import UserList
import SCons.Node.FS
def scons_str2nodes(arg, node_factory=SCons.Node.FS.default_fs.File):
nodes.append(v)
return nodes
+
+
+class PathList(UserList):
+ """This class emulates the behavior of a list, but also implements
+ the special "path dissection" attributes we can use to find
+ suffixes, base names, etc. of the paths in the list.
+
+ One other special attribute of this class is that, by
+ overriding the __str__ and __repr__ methods, this class
+ represents itself as a space-concatenated string of
+ the list elements, as in:
+
+ >>> pl=PathList(["/foo/bar.txt", "/baz/foo.txt"])
+ >>> pl
+ '/foo/bar.txt /baz/foo.txt'
+ >>> pl.base
+ 'bar foo'
+ """
+ def __init__(self, seq = []):
+ UserList.__init__(self, seq)
+
+ def __getattr__(self, name):
+ # This is how we implement the "special" attributes
+ # such as base, suffix, basepath, etc.
+ try:
+ return self.dictSpecialAttrs[name](self)
+ except KeyError:
+ raise AttributeError, 'PathList has no attribute: %s' % name
+
+ def __splitPath(self, split_func=os.path.split):
+ """This method calls the supplied split_func on each element
+ in the contained list. We expect split_func to return a
+ 2-tuple, usually representing two elements of a split file path,
+ such as those returned by os.path.split().
+
+ We return a 2-tuple of lists, each equal in length to the contained
+ list. The first list represents all the elements from the
+ first part of the split operation, the second represents
+ all elements from the second part."""
+ list1 = []
+ list2 = []
+ for strPath in self.data:
+ first_part, second_part = split_func(strPath)
+ list1.append(first_part)
+ list2.append(second_part)
+ return (self.__class__(list1),
+ self.__class__(list2))
+
+ def __getBasePath(self):
+ """Return the file's directory and file name, with the
+ suffix stripped."""
+ return self.__splitPath(os.path.splitext)[0]
+
+ def __getSuffix(self):
+ """Return the file's suffix."""
+ return self.__splitPath(os.path.splitext)[1]
+
+ def __getFileName(self):
+ """Return the file's name without the path."""
+ return self.__splitPath()[1]
+
+ def __getDir(self):
+ """Return the file's path."""
+ return self.__splitPath()[0]
+
+ def __getBase(self):
+ """Return the file name with path and suffix stripped."""
+ return self.__getFileName().__splitPath(os.path.splitext)[0]
+
+ dictSpecialAttrs = { "file" : __getFileName,
+ "base" : __getBasePath,
+ "filebase" : __getBase,
+ "dir" : __getDir,
+ "suffix" : __getSuffix }
+
+ def __str__(self):
+ return string.join(self.data)
+
+ def __repr__(self):
+ return repr(string.join(self.data))
+
+ def __getitem__(self, item):
+ # We must do this to ensure that single items returned
+ # by index access have the special attributes such as
+ # suffix and basepath.
+ return self.__class__([ UserList.__getitem__(self, item), ])
+
+
+__tcv = re.compile(r'\$(\{?targets?(\[[0-9:]+\])?(\.[a-z]+)?\}?)')
+__scv = re.compile(r'\$(\{?sources(\[[0-9:]+\])?(\.[a-z]+)?\}?)')
+def scons_varrepl(command, targets, sources):
+ """This routine handles variable interpolation for the $targets and
+ $sources variables in the 'command' argument. The targets and sources
+ given in the other arguements must be lists containing 'Node's."""
+
+ def repl(m, targets=targets, sources=sources):
+ globals = {}
+ key = m.group(1)
+ if key[0] == '{':
+ if key[-1] == '}':
+ key = key[1:-1]
+ else:
+ raise SyntaxError, "Bad regular expression"
+
+ if key[:6] == 'target':
+ globals['targets'] = targets
+ globals['target'] = targets[0]
+ if key[:7] == 'sources':
+ globals['sources'] = sources
+ if globals:
+ return str(eval(key, globals ))
+
+ command = __tcv.sub(repl, command)
+ command = __scv.sub(repl, command)
+ return command
+
+
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import sys
+import os.path
import unittest
import SCons.Node
import SCons.Node.FS
-from SCons.Util import scons_str2nodes
+from SCons.Util import scons_str2nodes, scons_varrepl, PathList
+
class UtilTestCase(unittest.TestCase):
def test_str2nodes(self):
pass
node = scons_str2nodes(OtherNode())
+
+ def test_varrepl(self):
+ """Test the varrepl function."""
+ targets = PathList(map(os.path.normpath, [ "./foo/bar.exe",
+ "/bar/baz.obj",
+ "../foo/baz.obj" ]))
+ sources = PathList(map(os.path.normpath, [ "./foo/blah.cpp",
+ "/bar/ack.cpp",
+ "../foo/ack.c" ]))
+
+ newcom = scons_varrepl("test $targets $sources", targets, sources)
+ assert newcom == "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c"
+
+ newcom = scons_varrepl("test $targets[:] $sources[0]", targets, sources)
+ assert newcom == "test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp"
+
+ newcom = scons_varrepl("test ${targets[1:]}v", targets, sources)
+ assert newcom == "test /bar/baz.obj ../foo/baz.objv"
+
+ newcom = scons_varrepl("test $target", targets, sources)
+ assert newcom == "test foo/bar.exe"
+
+ newcom = scons_varrepl("test $target$source[0]", targets, sources)
+ assert newcom == "test foo/bar.exe$source[0]"
+
+ newcom = scons_varrepl("test ${target.file}", targets, sources)
+ assert newcom == "test bar.exe"
+
+ newcom = scons_varrepl("test ${target.filebase}", targets, sources)
+ assert newcom == "test bar"
+
+ newcom = scons_varrepl("test ${target.suffix}", targets, sources)
+ assert newcom == "test .exe"
+
+ newcom = scons_varrepl("test ${target.base}", targets, sources)
+ assert newcom == "test foo/bar"
+
+ newcom = scons_varrepl("test ${target.dir}", targets, sources)
+ assert newcom == "test foo"
+
+
+
if __name__ == "__main__":
suite = unittest.makeSuite(UtilTestCase, 'test_')
if not unittest.TextTestRunner().run(suite).wasSuccessful():
test.write('SConstruct', """
env = Environment()
env.Command(target = 'f1.out', source = 'f1.in',
- action = "python build.py %(target)s %(source)s")
+ action = "python build.py $target $sources")
env.Command(target = 'f2.out', source = 'f2.in',
- action = "python build.py temp2 %(source)s\\npython build.py %(target)s temp2")
+ action = "python build.py temp2 $sources\\npython build.py $target temp2")
env.Command(target = 'f3.out', source = 'f3.in',
- action = ["python build.py temp3 %(source)s",
- "python build.py %(target)s temp3"])
+ action = ["python build.py temp3 $sources",
+ "python build.py $target temp3"])
# Eventually, add ability to do execute Python code.
""")
""")
test.write(['one', 'SConstruct'], """
-B = Builder(name = 'B', action = "python ../build.py %(target)s %(source)s")
+B = Builder(name = 'B', action = "python ../build.py $target $sources")
env = Environment(BUILDERS = [B])
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
""")
test.write(['two', 'SConstruct'], """
-B = Builder(name = 'B', action = "python ../build.py %(target)s %(source)s")
+B = Builder(name = 'B', action = "python ../build.py $target $sources")
env = Environment(BUILDERS = [B])
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
""")
test.write(['three', 'SConstruct'], """
-B = Builder(name = 'B', action = "python ../build.py %(target)s %(source)s")
+B = Builder(name = 'B', action = "python ../build.py $target $sources")
env = Environment(BUILDERS = [B])
env.B(target = 'foo.out', source = 'foo.in')
env.B(target = 'bar.out', source = 'bar.in')
test.write('SConstruct', """
Foo = Builder(name = "Foo",
- action = "python build.py %(target)s %(source)s subdir/foo.dep")
+ action = "python build.py $target $sources subdir/foo.dep")
Bar = Builder(name = "Bar",
- action = "python build.py %(target)s %(source)s subdir/bar.dep")
+ action = "python build.py $target $sources subdir/bar.dep")
env = Environment(BUILDERS = [Foo, Bar])
env.Depends(target = ['f1.out', 'f2.out'], dependency = 'subdir/foo.dep')
env.Depends(target = 'f3.out', dependency = 'subdir/bar.dep')
import os
bin1_path = r'%s' + os.pathsep + os.environ['PATH']
bin2_path = r'%s' + os.pathsep + os.environ['PATH']
-Bld = Builder(name = 'Bld', action = "build.py %%(target)s %%(source)s")
+Bld = Builder(name = 'Bld', action = "build.py $target $sources")
bin1 = Environment(ENV = {'PATH' : bin1_path}, BUILDERS = [Bld])
bin2 = Environment(ENV = {'PATH' : bin2_path}, BUILDERS = [Bld])
bin1.Bld(target = 'bin1.out', source = 'input')
#XXX Need to switch TestBld to Program() when LIBS variable is working.
test.write('SConstruct', """
TestBld = Builder(name='TestBld',
- action='cc -o %(target)s %(source)s -L./ -lfoo1 -lfoo2 -lfoo3')
+ action='cc -o $target $sources -L./ -lfoo1 -lfoo2 -lfoo3')
env = Environment(BUILDERS=[ TestBld, Library ])
env.Library(target = 'foo1', source = 'f1.c')
env.Library(target = 'foo2', source = 'f2a.c f2b.c f2c.c')
""")
test.write(['one', 'SConstruct'], """
-B0 = Builder(name = 'B0', action = "python ../build.py 0 %(target)s %(source)s")
-B1 = Builder(name = 'B1', action = "python ../build.py 1 %(target)s %(source)s")
+B0 = Builder(name = 'B0', action = "python ../build.py 0 $target $sources")
+B1 = Builder(name = 'B1', action = "python ../build.py 1 $target $sources")
env = Environment(BUILDERS = [B0, B1])
env.B1(target = 'f1.out', source = 'f1.in')
env.B0(target = 'f2.out', source = 'f2.in')
test.fail_test(os.path.exists(test.workpath('f3.out')))
test.write(['two', 'SConstruct'], """
-B0 = Builder(name = 'B0', action = "python ../build.py 0 %(target)s %(source)s")
-B1 = Builder(name = 'B1', action = "python ../build.py 1 %(target)s %(source)s")
+B0 = Builder(name = 'B0', action = "python ../build.py 0 $target $sources")
+B1 = Builder(name = 'B1', action = "python ../build.py 1 $target $sources")
env = Environment(BUILDERS = [B0, B1])
env.B0(target = 'f1.out', source = 'f1.in')
env.B1(target = 'f2.out', source = 'f2.in')
test.fail_test(os.path.exists(test.workpath('f3.out')))
test.write(['three', 'SConstruct'], """
-B0 = Builder(name = 'B0', action = "python ../build.py 0 %(target)s %(source)s")
-B1 = Builder(name = 'B1', action = "python ../build.py 1 %(target)s %(source)s")
+B0 = Builder(name = 'B0', action = "python ../build.py 0 $target $sources")
+B1 = Builder(name = 'B1', action = "python ../build.py 1 $target $sources")
env = Environment(BUILDERS = [B0, B1])
env.B0(target = 'f1.out', source = 'f1.in')
env.B0(target = 'f2.out', source = 'f2.in')
""")
test.write('SConstruct', """
-B1 = Builder(name = 'B1', action = ["python build.py .temp %(source)s",
- "python build.py %(target)s .temp"])
-B2 = Builder(name = 'B2', action = "python build.py .temp %(source)s\\npython build.py %(target)s .temp")
+B1 = Builder(name = 'B1', action = ["python build.py .temp $sources",
+ "python build.py $targets .temp"])
+B2 = Builder(name = 'B2', action = "python build.py .temp $sources\\npython build.py $targets .temp")
env = Environment(BUILDERS = [B1, B2])
env.B1(target = 'foo1.out', source = 'foo1.in')
env.B2(target = 'foo2.out', source = 'foo2.in')
test.write('SConstruct', """
MyBuild = Builder(name = "MyBuild",
- action = "python build.py %(target)s")
+ action = "python build.py $targets")
env = Environment(BUILDERS = [MyBuild])
env.MyBuild(target = '-f1.out', source = 'f1.in')
env.MyBuild(target = '-f2.out', source = 'f2.in')
""")
test.write('SConstruct', """
-B = Builder(name = 'B', action = "python build.py %(target)s %(source)s")
+B = Builder(name = 'B', action = "python build.py $targets $sources")
env = Environment(BUILDERS = [B])
env.B(target = 'foo1.out', source = 'foo1.in')
env.B(target = 'foo2.out', source = 'foo2.in')
""")
test.write('SConstruct', """
-Succeed = Builder(name = "Succeed", action = "python succeed.py %(target)s")
-Fail = Builder(name = "Fail", action = "python fail.py %(target)s")
+Succeed = Builder(name = "Succeed", action = "python succeed.py $targets")
+Fail = Builder(name = "Fail", action = "python fail.py $targets")
env = Environment(BUILDERS = [Succeed, Fail])
env.Fail(target = 'aaa.1', source = 'aaa.in')
env.Succeed(target = 'aaa.out', source = 'aaa.1')
test.write('SConstruct', """
MyBuild = Builder(name = "MyBuild",
- action = "python build.py %(target)s")
+ action = "python build.py $targets")
env = Environment(BUILDERS = [MyBuild])
env.MyBuild(target = 'f1', source = 'f1.in')
env.MyBuild(target = 'f2', source = 'f2.in')
""")
test.write('SConstruct', """
-Succeed = Builder(name = "Succeed", action = "python succeed.py %(target)s")
-Fail = Builder(name = "Fail", action = "python fail.py %(target)s")
+Succeed = Builder(name = "Succeed", action = "python succeed.py $targets")
+Fail = Builder(name = "Fail", action = "python fail.py $targets")
env = Environment(BUILDERS = [Succeed, Fail])
env.Fail(target = 'aaa.1', source = 'aaa.in')
env.Succeed(target = 'aaa.out', source = 'aaa.1')
test.write('SConstruct', """
MyBuild = Builder(name = "MyBuild",
- action = "python build.py %(target)s")
+ action = "python build.py $targets")
env = Environment(BUILDERS = [MyBuild])
env.MyBuild(target = 'f1.out', source = 'f1.in')
env.MyBuild(target = 'f2.out', source = 'f2.in')
test.write('SConstruct', """
MyBuild = Builder(name = "MyBuild",
- action = "python build.py %(target)s")
+ action = "python build.py $target")
env = Environment(BUILDERS = [MyBuild])
env.MyBuild(target = 'f1.out', source = 'f1.in')
env.MyBuild(target = 'f2.out', source = 'f2.in')
""")
test.write('SConstruct', """
-B = Builder(name = "B", action = "python build.py %(target)s %(source)s")
+B = Builder(name = "B", action = "python build.py $targets $sources")
env = Environment(BUILDERS = [B])
env.B(target = 'f1.out', source = 'f1.in')
env.B(target = 'f2.out', source = 'f2.in')