summaryrefslogtreecommitdiff
path: root/src/engine/SCons/BuilderTests.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/BuilderTests.py')
-rw-r--r--src/engine/SCons/BuilderTests.py1650
1 files changed, 1650 insertions, 0 deletions
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
new file mode 100644
index 0000000..2faaa2d
--- /dev/null
+++ b/src/engine/SCons/BuilderTests.py
@@ -0,0 +1,1650 @@
+#
+# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation
+#
+# 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__ = "src/engine/SCons/BuilderTests.py 4577 2009/12/27 19:44:43 scons"
+
+# Define a null function for use as a builder action.
+# Where this is defined in the file seems to affect its
+# byte-code contents, so try to minimize changes by
+# defining it here, before we even import anything.
+def Func():
+ pass
+
+import os.path
+import re
+import sys
+import types
+import StringIO
+import unittest
+import UserList
+
+import TestCmd
+
+import SCons.Action
+import SCons.Builder
+import SCons.Environment
+import SCons.Errors
+import SCons.Subst
+import SCons.Util
+
+sys.stdout = StringIO.StringIO()
+
+# Initial setup of the common environment for all tests,
+# a temporary working directory containing a
+# script for writing arguments to an output file.
+#
+# We don't do this as a setUp() method because it's
+# unnecessary to create a separate directory and script
+# for each test, they can just use the one.
+test = TestCmd.TestCmd(workdir = '')
+
+outfile = test.workpath('outfile')
+outfile2 = test.workpath('outfile2')
+
+infile = test.workpath('infile')
+test.write(infile, "infile\n")
+
+show_string = None
+
+scons_env = SCons.Environment.Environment()
+
+env_arg2nodes_called = None
+
+class Environment:
+ def __init__(self, **kw):
+ self.d = {}
+ self.d['SHELL'] = scons_env['SHELL']
+ self.d['SPAWN'] = scons_env['SPAWN']
+ self.d['ESCAPE'] = scons_env['ESCAPE']
+ for k, v in kw.items():
+ self.d[k] = v
+ global env_arg2nodes_called
+ env_arg2nodes_called = None
+ self.scanner = None
+ self.fs = SCons.Node.FS.FS()
+ def subst(self, s):
+ if not SCons.Util.is_String(s):
+ return s
+ def substitute(m, d=self.d):
+ return d.get(m.group(1), '')
+ return re.sub(r'\$(\w+)', substitute, s)
+ def subst_target_source(self, string, raw=0, target=None,
+ source=None, dict=None, conv=None):
+ return SCons.Subst.scons_subst(string, self, raw, target,
+ source, dict, conv)
+ def subst_list(self, string, raw=0, target=None, source=None, conv=None):
+ return SCons.Subst.scons_subst_list(string, self, raw, target,
+ source, {}, {}, conv)
+ def arg2nodes(self, args, factory, **kw):
+ 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(self.subst(a))
+ list.append(a)
+ return list
+ def get_factory(self, factory):
+ return factory or self.fs.File
+ def get_scanner(self, ext):
+ return self.scanner
+ def Dictionary(self):
+ return {}
+ def autogenerate(self, dir=''):
+ return {}
+ def __setitem__(self, item, var):
+ self.d[item] = var
+ def __getitem__(self, item):
+ return self.d[item]
+ def has_key(self, item):
+ return self.d.has_key(item)
+ def keys(self):
+ return self.d.keys()
+ def get(self, key, value=None):
+ return self.d.get(key, value)
+ def Override(self, overrides):
+ env = apply(Environment, (), self.d)
+ env.d.update(overrides)
+ env.scanner = self.scanner
+ return env
+ def _update(self, dict):
+ self.d.update(dict)
+ def items(self):
+ return self.d.items()
+ def sig_dict(self):
+ d = {}
+ for k,v in self.items(): d[k] = v
+ d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
+ d['TARGET'] = d['TARGETS'][0]
+ d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
+ d['SOURCE'] = d['SOURCES'][0]
+ return d
+ def __cmp__(self, other):
+ return cmp(self.scanner, other.scanner) or cmp(self.d, other.d)
+
+class MyAction:
+ def __init__(self, action):
+ self.action = action
+ def __call__(self, *args, **kw):
+ pass
+ def get_executor(self, env, overrides, tlist, slist, executor_kw):
+ return ['executor'] + [self.action]
+
+class MyNode_without_target_from_source:
+ def __init__(self, name):
+ self.name = name
+ self.sources = []
+ self.builder = None
+ self.is_explicit = None
+ self.side_effect = 0
+ self.suffix = os.path.splitext(name)[1]
+ def disambiguate(self):
+ return self
+ def __str__(self):
+ return self.name
+ def builder_set(self, builder):
+ self.builder = builder
+ def has_builder(self):
+ return not self.builder is None
+ def set_explicit(self, is_explicit):
+ self.is_explicit = is_explicit
+ def has_explicit_builder(self):
+ return self.is_explicit
+ def env_set(self, env, safe=0):
+ self.env = env
+ def add_source(self, source):
+ self.sources.extend(source)
+ def scanner_key(self):
+ return self.name
+ def is_derived(self):
+ return self.has_builder()
+ def generate_build_env(self, env):
+ return env
+ def get_build_env(self):
+ return self.executor.get_build_env()
+ def set_executor(self, executor):
+ self.executor = executor
+ def get_executor(self, create=1):
+ return self.executor
+
+class MyNode(MyNode_without_target_from_source):
+ def target_from_source(self, prefix, suffix, stripext):
+ return MyNode(prefix + stripext(str(self))[0] + suffix)
+
+class BuilderTestCase(unittest.TestCase):
+
+ def test__init__(self):
+ """Test simple Builder creation
+ """
+ builder = SCons.Builder.Builder(action="foo")
+ assert not builder is None, builder
+ builder = SCons.Builder.Builder(action="foo", OVERRIDE='x')
+ x = builder.overrides['OVERRIDE']
+ assert x == 'x', x
+
+ def test__nonzero__(self):
+ """Test a builder raising an exception when __nonzero__ is called
+ """
+ builder = SCons.Builder.Builder(action="foo")
+ exc_caught = None
+ try:
+ builder.__nonzero__()
+ except SCons.Errors.InternalError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected InternalError exception"
+
+ class Node:
+ pass
+
+ n = Node()
+ n.builder = builder
+ exc_caught = None
+ try:
+ if n.builder:
+ pass
+ except SCons.Errors.InternalError:
+ exc_caught = 1
+ assert exc_caught, "did not catch expected InternalError exception"
+
+ def test__call__(self):
+ """Test calling a builder to establish source dependencies
+ """
+ env = Environment()
+ builder = SCons.Builder.Builder(action="foo",
+ target_factory=MyNode,
+ source_factory=MyNode)
+
+ tgt = builder(env, source=[])
+ assert tgt == [], tgt
+
+ 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
+ assert n1.executor, "no executor found"
+ assert not hasattr(n2, 'env')
+
+ l = [1]
+ ul = UserList.UserList([2])
+ try:
+ l.extend(ul)
+ except TypeError:
+ def mystr(l):
+ return str(map(str, l))
+ else:
+ mystr = str
+
+ nnn1 = MyNode("nnn1")
+ nnn2 = MyNode("nnn2")
+ tlist = builder(env, target = [nnn1, nnn2], source = [])
+ s = mystr(tlist)
+ assert s == "['nnn1', 'nnn2']", s
+ l = map(str, tlist)
+ assert l == ['nnn1', 'nnn2'], l
+
+ tlist = builder(env, target = 'n3', source = 'n4')
+ s = mystr(tlist)
+ assert s == "['n3']", s
+ target = tlist[0]
+ l = map(str, tlist)
+ assert l == ['n3'], l
+ assert target.name == 'n3'
+ assert target.sources[0].name == 'n4'
+
+ tlist = builder(env, target = 'n4 n5', source = ['n6 n7'])
+ s = mystr(tlist)
+ assert s == "['n4 n5']", s
+ l = map(str, tlist)
+ assert l == ['n4 n5'], l
+ target = tlist[0]
+ assert target.name == 'n4 n5'
+ assert target.sources[0].name == 'n6 n7'
+
+ tlist = builder(env, target = ['n8 n9'], source = 'n10 n11')
+ s = mystr(tlist)
+ assert s == "['n8 n9']", s
+ l = map(str, tlist)
+ assert l == ['n8 n9'], l
+ target = tlist[0]
+ assert target.name == 'n8 n9'
+ assert target.sources[0].name == 'n10 n11'
+
+ # A test to be uncommented when we freeze the environment
+ # as part of calling the builder.
+ #env1 = Environment(VAR='foo')
+ #target = builder(env1, target = 'n12', source = 'n13')
+ #env1['VAR'] = 'bar'
+ #be = target.get_build_env()
+ #assert be['VAR'] == 'foo', be['VAR']
+
+ if not hasattr(types, 'UnicodeType'):
+ uni = str
+ else:
+ uni = unicode
+
+ target = builder(env, target = uni('n12 n13'),
+ source = [uni('n14 n15')])[0]
+ assert target.name == uni('n12 n13')
+ assert target.sources[0].name == uni('n14 n15')
+
+ target = builder(env, target = [uni('n16 n17')],
+ source = uni('n18 n19'))[0]
+ assert target.name == uni('n16 n17')
+ assert target.sources[0].name == uni('n18 n19')
+
+ n20 = MyNode_without_target_from_source('n20')
+ flag = 0
+ try:
+ target = builder(env, None, source=n20)
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown if a source node can't create a target."
+
+ builder = SCons.Builder.Builder(action="foo",
+ target_factory=MyNode,
+ source_factory=MyNode,
+ prefix='p-',
+ suffix='.s')
+ target = builder(env, None, source='n21')[0]
+ assert target.name == 'p-n21.s', target
+
+ builder = SCons.Builder.Builder(misspelled_action="foo",
+ suffix = '.s')
+ try:
+ builder(env, target = 'n22', source = 'n22')
+ except SCons.Errors.UserError, e:
+ pass
+ else:
+ raise "Did not catch expected UserError."
+
+ builder = SCons.Builder.Builder(action="foo")
+ target = builder(env, None, source='n22', srcdir='src_dir')[0]
+ p = target.sources[0].path
+ assert p == os.path.join('src_dir', 'n22'), p
+
+ def test_mistaken_variables(self):
+ """Test keyword arguments that are often mistakes
+ """
+ import SCons.Warnings
+ env = Environment()
+ builder = SCons.Builder.Builder(action="foo")
+
+ save_warn = SCons.Warnings.warn
+ warned = []
+ def my_warn(exception, warning, warned=warned):
+ warned.append(warning)
+ SCons.Warnings.warn = my_warn
+
+ try:
+ target = builder(env, 'mistaken1', sources='mistaken1.c')
+ assert warned == ["Did you mean to use `source' instead of `sources'?"], warned
+ del warned[:]
+
+ target = builder(env, 'mistaken2', targets='mistaken2.c')
+ assert warned == ["Did you mean to use `target' instead of `targets'?"], warned
+ del warned[:]
+
+ target = builder(env, 'mistaken3', targets='mistaken3', sources='mistaken3.c')
+ assert "Did you mean to use `source' instead of `sources'?" in warned, warned
+ assert "Did you mean to use `target' instead of `targets'?" in warned, warned
+ del warned[:]
+ finally:
+ SCons.Warnings.warn = save_warn
+
+ def test_action(self):
+ """Test Builder creation
+
+ Verify that we can retrieve the supplied action attribute.
+ """
+ builder = SCons.Builder.Builder(action="foo")
+ assert builder.action.cmd_list == "foo"
+
+ def func():
+ pass
+ builder = SCons.Builder.Builder(action=func)
+ assert isinstance(builder.action, SCons.Action.FunctionAction)
+ # Preserve the following so that the baseline test will fail.
+ # Remove it in favor of the previous test at some convenient
+ # point in the future.
+ assert builder.action.execfunction == func
+
+ def test_generator(self):
+ """Test Builder creation given a generator function."""
+
+ def generator():
+ pass
+
+ builder = SCons.Builder.Builder(generator=generator)
+ assert builder.action.generator == generator
+
+ def test_get_name(self):
+ """Test the get_name() method
+ """
+
+ def test_cmp(self):
+ """Test simple comparisons of Builder objects
+ """
+ b1 = SCons.Builder.Builder(src_suffix = '.o')
+ b2 = SCons.Builder.Builder(src_suffix = '.o')
+ assert b1 == b2
+ b3 = SCons.Builder.Builder(src_suffix = '.x')
+ assert b1 != b3
+ assert b2 != b3
+
+ def test_target_factory(self):
+ """Test a Builder that creates target nodes of a specified class
+ """
+ class Foo:
+ pass
+ def FooFactory(target):
+ global Foo
+ return Foo(target)
+ builder = SCons.Builder.Builder(target_factory = FooFactory)
+ assert builder.target_factory is FooFactory
+ assert not builder.source_factory is FooFactory
+
+ def test_source_factory(self):
+ """Test a Builder that creates source nodes of a specified class
+ """
+ class Foo:
+ pass
+ def FooFactory(source):
+ global Foo
+ return Foo(source)
+ builder = SCons.Builder.Builder(source_factory = FooFactory)
+ assert not builder.target_factory is FooFactory
+ assert builder.source_factory is FooFactory
+
+ def test_splitext(self):
+ """Test the splitext() method attached to a Builder."""
+ b = SCons.Builder.Builder()
+ assert b.splitext('foo') == ('foo','')
+ assert b.splitext('foo.bar') == ('foo','.bar')
+ assert b.splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
+
+ class MyBuilder(SCons.Builder.BuilderBase):
+ def splitext(self, path):
+ return "called splitext()"
+
+ b = MyBuilder()
+ ret = b.splitext('xyz.c')
+ assert ret == "called splitext()", ret
+
+ def test_adjust_suffix(self):
+ """Test how a Builder adjusts file suffixes
+ """
+ b = SCons.Builder.Builder()
+ assert b.adjust_suffix('.foo') == '.foo'
+ assert b.adjust_suffix('foo') == '.foo'
+ assert b.adjust_suffix('$foo') == '$foo'
+
+ class MyBuilder(SCons.Builder.BuilderBase):
+ def adjust_suffix(self, suff):
+ return "called adjust_suffix()"
+
+ b = MyBuilder()
+ ret = b.adjust_suffix('.foo')
+ assert ret == "called adjust_suffix()", ret
+
+ def test_prefix(self):
+ """Test Builder creation with a specified target prefix
+
+ 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', action='')
+ assert builder.get_prefix(env) == 'lib'
+ tgt = builder(env, target = 'tgt1', source = 'src1')[0]
+ assert tgt.path == 'libtgt1', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
+ assert tgt.path == 'libtgt2a tgt2b', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = None, source = 'src3')[0]
+ assert tgt.path == 'libsrc3', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = None, source = 'lib/src4')[0]
+ assert tgt.path == os.path.join('lib', 'libsrc4'), \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
+ assert tgt.path == os.path.join('lib', 'libtgt5'), \
+ "Target has unexpected name: %s" % tgt.path
+
+ def gen_prefix(env, sources):
+ return "gen_prefix() says " + env['FOO']
+ my_env = Environment(FOO = 'xyzzy')
+ builder = SCons.Builder.Builder(prefix = gen_prefix)
+ assert builder.get_prefix(my_env) == "gen_prefix() says xyzzy"
+ my_env['FOO'] = 'abracadabra'
+ assert builder.get_prefix(my_env) == "gen_prefix() says abracadabra"
+
+ def my_emit(env, sources):
+ return env.subst('$EMIT')
+ my_env = Environment(FOO = '.foo', EMIT = 'emit-')
+ builder = SCons.Builder.Builder(prefix = {None : 'default-',
+ '.in' : 'out-',
+ '.x' : 'y-',
+ '$FOO' : 'foo-',
+ '.zzz' : my_emit},
+ action = '')
+ tgt = builder(my_env, target = None, source = 'f1')[0]
+ assert tgt.path == 'default-f1', tgt.path
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
+ assert tgt.path == 'default-f2', tgt.path
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
+ assert tgt.path == 'out-f3', tgt.path
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
+ assert tgt.path == 'y-f4', tgt.path
+ tgt = builder(my_env, target = None, source = 'f5.foo')[0]
+ assert tgt.path == 'foo-f5', tgt.path
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
+ assert tgt.path == 'emit-f6', tgt.path
+
+ def test_set_suffix(self):
+ """Test the set_suffix() method"""
+ b = SCons.Builder.Builder(action='')
+ env = Environment(XSUFFIX = '.x')
+
+ s = b.get_suffix(env)
+ assert s == '', s
+
+ b.set_suffix('.foo')
+ s = b.get_suffix(env)
+ assert s == '.foo', s
+
+ b.set_suffix('$XSUFFIX')
+ s = b.get_suffix(env)
+ assert s == '.x', s
+
+ def test_src_suffix(self):
+ """Test Builder creation with a specified source file suffix
+
+ Make sure that the '.' separator is appended to the
+ beginning if it isn't already present.
+ """
+ env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
+
+ b1 = SCons.Builder.Builder(src_suffix = '.c', action='')
+ assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
+
+ tgt = b1(env, target = 'tgt2', source = 'src2')[0]
+ assert tgt.sources[0].path == 'src2.c', \
+ "Source has unexpected name: %s" % tgt.sources[0].path
+
+ tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0]
+ assert len(tgt.sources) == 1
+ assert tgt.sources[0].path == 'src3a src3b.c', \
+ "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
+
+ b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1)
+ r = b2.src_suffixes(env)
+ r.sort()
+ assert r == ['.2', '.c'], r
+
+ b3 = SCons.Builder.Builder(action = {'.3a' : '', '.3b' : ''})
+ s = b3.src_suffixes(env)
+ s.sort()
+ assert s == ['.3a', '.3b'], s
+
+ b4 = SCons.Builder.Builder(src_suffix = '$XSUFFIX')
+ assert b4.src_suffixes(env) == ['.x'], b4.src_suffixes(env)
+
+ b5 = SCons.Builder.Builder(action = { '.y' : ''})
+ assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
+
+ def test_srcsuffix_nonext(self):
+ "Test target generation from non-extension source suffixes"
+ env = Environment()
+ b6 = SCons.Builder.Builder(action = '',
+ src_suffix='_src.a',
+ suffix='.b')
+ tgt = b6(env, target=None, source='foo_src.a')
+ assert str(tgt[0]) == 'foo.b', str(tgt[0])
+
+ b7 = SCons.Builder.Builder(action = '',
+ src_suffix='_source.a',
+ suffix='_obj.b')
+ b8 = SCons.Builder.Builder(action = '',
+ src_builder=b7,
+ suffix='.c')
+ tgt = b8(env, target=None, source='foo_source.a')
+ assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
+ src = env.fs.File('foo_source.a')
+ tgt = b8(env, target=None, source=src)
+ assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
+
+ b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'},
+ suffix='.c')
+ b9.add_action('_altsrc.b', 'altaction')
+ tgt = b9(env, target=None, source='foo_altsrc.b')
+ assert str(tgt[0]) == 'foo.c', str(tgt[0])
+
+ def test_src_suffix_expansion(self):
+ """Test handling source suffixes when an expansion is involved"""
+ env = Environment(OBJSUFFIX = '.obj')
+
+ b1 = SCons.Builder.Builder(action = '',
+ src_suffix='.c',
+ suffix='.obj')
+ b2 = SCons.Builder.Builder(action = '',
+ src_builder=b1,
+ src_suffix='.obj',
+ suffix='.exe')
+ tgt = b2(env, target=None, source=['foo$OBJSUFFIX'])
+ s = map(str, tgt[0].sources)
+ assert s == ['foo.obj'], s
+
+ def test_suffix(self):
+ """Test Builder creation with a specified target suffix
+
+ 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', action='')
+ assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
+ tgt = builder(env, target = 'tgt3', source = 'src3')[0]
+ assert tgt.path == 'tgt3.o', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
+ assert tgt.path == 'tgt4a tgt4b.o', \
+ "Target has unexpected name: %s" % tgt.path
+ tgt = builder(env, target = None, source = 'src5')[0]
+ assert tgt.path == 'src5.o', \
+ "Target has unexpected name: %s" % tgt.path
+
+ def gen_suffix(env, sources):
+ return "gen_suffix() says " + env['BAR']
+ my_env = Environment(BAR = 'hocus pocus')
+ builder = SCons.Builder.Builder(suffix = gen_suffix)
+ assert builder.get_suffix(my_env) == "gen_suffix() says hocus pocus", builder.get_suffix(my_env)
+ my_env['BAR'] = 'presto chango'
+ assert builder.get_suffix(my_env) == "gen_suffix() says presto chango"
+
+ def my_emit(env, sources):
+ return env.subst('$EMIT')
+ my_env = Environment(BAR = '.bar', EMIT = '.emit')
+ builder = SCons.Builder.Builder(suffix = {None : '.default',
+ '.in' : '.out',
+ '.x' : '.y',
+ '$BAR' : '.new',
+ '.zzz' : my_emit},
+ action='')
+ tgt = builder(my_env, target = None, source = 'f1')[0]
+ assert tgt.path == 'f1.default', tgt.path
+ tgt = builder(my_env, target = None, source = 'f2.c')[0]
+ assert tgt.path == 'f2.default', tgt.path
+ tgt = builder(my_env, target = None, source = 'f3.in')[0]
+ assert tgt.path == 'f3.out', tgt.path
+ tgt = builder(my_env, target = None, source = 'f4.x')[0]
+ assert tgt.path == 'f4.y', tgt.path
+ tgt = builder(my_env, target = None, source = 'f5.bar')[0]
+ assert tgt.path == 'f5.new', tgt.path
+ tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
+ assert tgt.path == 'f6.emit', tgt.path
+
+ def test_single_source(self):
+ """Test Builder with single_source flag set"""
+ def func(target, source, env):
+ open(str(target[0]), "w")
+ if (len(source) == 1 and len(target) == 1):
+ env['CNT'][0] = env['CNT'][0] + 1
+
+ env = Environment()
+ infiles = []
+ outfiles = []
+ for i in range(10):
+ infiles.append(test.workpath('%d.in' % i))
+ outfiles.append(test.workpath('%d.out' % i))
+ test.write(infiles[-1], "\n")
+ builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None),
+ single_source = 1, suffix='.out')
+ env['CNT'] = [0]
+ tgt = builder(env, target=outfiles[0], source=infiles[0])[0]
+ s = str(tgt)
+ assert s == test.workpath('0.out'), s
+ tgt.prepare()
+ tgt.build()
+ assert env['CNT'][0] == 1, env['CNT'][0]
+ tgt = builder(env, outfiles[1], infiles[1])[0]
+ s = str(tgt)
+ assert s == test.workpath('1.out'), s
+ tgt.prepare()
+ tgt.build()
+ assert env['CNT'][0] == 2
+ tgts = builder(env, None, infiles[2:4])
+ try:
+ [].extend(UserList.UserList())
+ except TypeError:
+ # Old Python version (1.5.2) that can't handle extending
+ # a list with list-like objects. That means the return
+ # value from the builder call is a real list with Nodes,
+ # and doesn't have a __str__() method that stringifies
+ # the individual elements. Since we're gong to drop 1.5.2
+ # support anyway, don't bother trying to test for it.
+ pass
+ else:
+ s = str(tgts)
+ expect = str([test.workpath('2.out'), test.workpath('3.out')])
+ assert s == expect, s
+ for t in tgts: t.prepare()
+ tgts[0].build()
+ tgts[1].build()
+ assert env['CNT'][0] == 4
+ try:
+ tgt = builder(env, outfiles[4], infiles[4:6])
+ except SCons.Errors.UserError:
+ pass
+ else:
+ assert 0
+ try:
+ # The builder may output more than one target per input file.
+ tgt = builder(env, outfiles[4:6], infiles[4:6])
+ except SCons.Errors.UserError:
+ pass
+ else:
+ assert 0
+
+
+ def test_lists(self):
+ """Testing handling lists of targets and source"""
+ def function2(target, source, env, tlist = [outfile, outfile2], **kw):
+ for t in target:
+ open(str(t), 'w').write("function2\n")
+ for t in tlist:
+ if not t in map(str, target):
+ open(t, 'w').write("function2\n")
+ return 1
+
+ env = Environment()
+ builder = SCons.Builder.Builder(action = function2)
+
+ tgts = builder(env, source=[])
+ assert tgts == [], tgts
+
+ tgts = builder(env, target = [outfile, outfile2], source = infile)
+ for t in tgts:
+ t.prepare()
+ try:
+ tgts[0].build()
+ except SCons.Errors.BuildError:
+ pass
+ c = test.read(outfile, 'r')
+ assert c == "function2\n", c
+ c = test.read(outfile2, 'r')
+ assert c == "function2\n", c
+
+ sub1_out = test.workpath('sub1', 'out')
+ sub2_out = test.workpath('sub2', 'out')
+
+ def function3(target, source, env, tlist = [sub1_out, sub2_out]):
+ for t in target:
+ open(str(t), 'w').write("function3\n")
+ for t in tlist:
+ if not t in map(str, target):
+ open(t, 'w').write("function3\n")
+ return 1
+
+ builder = SCons.Builder.Builder(action = function3)
+ tgts = builder(env, target = [sub1_out, sub2_out], source = infile)
+ for t in tgts:
+ t.prepare()
+ try:
+ tgts[0].build()
+ except SCons.Errors.BuildError:
+ pass
+ c = test.read(sub1_out, 'r')
+ assert c == "function3\n", c
+ c = test.read(sub2_out, 'r')
+ assert c == "function3\n", c
+ assert os.path.exists(test.workpath('sub1'))
+ assert os.path.exists(test.workpath('sub2'))
+
+ def test_src_builder(self):
+ """Testing Builders with src_builder"""
+ # These used to be MultiStepBuilder objects until we
+ # eliminated it as a separate class
+ env = Environment()
+ builder1 = SCons.Builder.Builder(action='foo',
+ src_suffix='.bar',
+ suffix='.foo')
+ builder2 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder = builder1,
+ src_suffix = '.foo')
+
+ tgt = builder2(env, source=[])
+ assert tgt == [], tgt
+
+ sources = ['test.bar', 'test2.foo', 'test3.txt', 'test4']
+ tgt = builder2(env, target='baz', source=sources)[0]
+ s = str(tgt)
+ assert s == 'baz', s
+ s = map(str, tgt.sources)
+ assert s == ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['test.bar'], s
+
+ tgt = builder2(env, None, 'aaa.bar')[0]
+ s = str(tgt)
+ assert s == 'aaa', s
+ s = map(str, tgt.sources)
+ assert s == ['aaa.foo'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['aaa.bar'], s
+
+ builder3 = SCons.Builder.Builder(action='bld3')
+ assert not builder3.src_builder is builder1.src_builder
+
+ builder4 = SCons.Builder.Builder(action='bld4',
+ src_suffix='.i',
+ suffix='_wrap.c')
+ builder5 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder=builder4,
+ suffix='.obj',
+ src_suffix='.c')
+ builder6 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder=builder5,
+ suffix='.exe',
+ src_suffix='.obj')
+ tgt = builder6(env, 'test', 'test.i')[0]
+ s = str(tgt)
+ assert s == 'test.exe', s
+ s = map(str, tgt.sources)
+ assert s == ['test_wrap.obj'], s
+ s = map(str, tgt.sources[0].sources)
+ assert s == ['test_wrap.c'], s
+ s = map(str, tgt.sources[0].sources[0].sources)
+ assert s == ['test.i'], s
+
+ def test_target_scanner(self):
+ """Testing ability to set target and source scanners through a builder."""
+ global instanced
+ class TestScanner:
+ pass
+ tscan = TestScanner()
+ sscan = TestScanner()
+ env = Environment()
+ builder = SCons.Builder.Builder(target_scanner=tscan,
+ source_scanner=sscan,
+ action='')
+ tgt = builder(env, target='foo2', source='bar')[0]
+ assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner
+
+ builder1 = SCons.Builder.Builder(action='foo',
+ src_suffix='.bar',
+ suffix='.foo')
+ builder2 = SCons.Builder.Builder(action='foo',
+ src_builder = builder1,
+ target_scanner = tscan,
+ source_scanner = tscan)
+ tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
+ assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner
+
+ def test_actual_scanner(self):
+ """Test usage of actual Scanner objects."""
+
+ import SCons.Scanner
+
+ def func(self):
+ pass
+
+ scanner = SCons.Scanner.Base(func, name='fooscan')
+
+ b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
+ b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
+ b3 = SCons.Builder.Builder(action='bld')
+
+ assert b1 == b2
+ assert b1 != b3
+
+ def test_src_scanner(slf):
+ """Testing ability to set a source file scanner through a builder."""
+ class TestScanner:
+ def key(self, env):
+ return 'TestScannerkey'
+ def instance(self, env):
+ return self
+ def select(self, node):
+ return self
+ name = 'TestScanner'
+ def __str__(self):
+ return self.name
+
+ scanner = TestScanner()
+ builder = SCons.Builder.Builder(action='action')
+
+ # With no scanner specified, source_scanner and
+ # backup_source_scanner are None.
+ bar_y = MyNode('bar.y')
+ env1 = Environment()
+ tgt = builder(env1, target='foo1.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner is None, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), repr(s)
+
+ # An Environment that has suffix-specified SCANNERS should
+ # provide a source scanner to the target.
+ class EnvTestScanner:
+ def key(self, env):
+ return '.y'
+ def instance(self, env):
+ return self
+ name = 'EnvTestScanner'
+ def __str__(self):
+ return self.name
+ def select(self, node):
+ return self
+ def path(self, env, dir=None):
+ return ()
+ def __call__(self, node, env, path):
+ return []
+ env3 = Environment(SCANNERS = [EnvTestScanner()])
+ env3.scanner = EnvTestScanner() # test env's version of SCANNERS
+ tgt = builder(env3, target='foo2.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), repr(s)
+
+ # Can't simply specify the scanner as a builder argument; it's
+ # global to all invocations of this builder.
+ tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
+ assert not tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), s
+
+ # Now use a builder that actually has scanners and ensure that
+ # the target is set accordingly (using the specified scanner
+ # instead of the Environment's scanner)
+ builder = SCons.Builder.Builder(action='action',
+ source_scanner=scanner,
+ target_scanner=scanner)
+ tgt = builder(env3, target='foo4.x', source='bar.y')[0]
+ src = tgt.sources[0]
+ assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner
+ assert tgt.builder.source_scanner, tgt.builder.source_scanner
+ assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner
+ assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner)
+ assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
+ assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y)
+ assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y)
+ assert not src.has_builder(), src.has_builder()
+ s = src.get_source_scanner(bar_y)
+ assert isinstance(s, SCons.Util.Null), s
+
+
+
+ def test_Builder_API(self):
+ """Test Builder interface.
+
+ Some of this is tested elsewhere in this file, but this is a
+ quick collection of common operations on builders with various
+ forms of component specifications."""
+
+ builder = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':builder})
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == '', r
+ r = builder.get_suffix(env)
+ assert r == '', r
+ r = builder.get_src_suffix(env)
+ assert r == '', r
+ r = builder.src_suffixes(env)
+ assert r == [], r
+
+ # src_suffix can be a single string or a list of strings
+ # src_suffixes() caches its return value, so we use a new
+ # Builder each time we do any of these tests
+
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix('.foo')
+ r = bld.get_src_suffix(env)
+ assert r == '.foo', r
+ r = bld.src_suffixes(env)
+ assert r == ['.foo'], r
+
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix(['.foo', '.bar'])
+ r = bld.get_src_suffix(env)
+ assert r == '.foo', r
+ r = bld.src_suffixes(env)
+ assert r == ['.foo', '.bar'], r
+
+ bld = SCons.Builder.Builder()
+ env = Environment(BUILDERS={'Bld':bld})
+
+ bld.set_src_suffix(['.bar', '.foo'])
+ r = bld.get_src_suffix(env)
+ assert r == '.bar', r
+ r = bld.src_suffixes(env)
+ r.sort()
+ assert r == ['.bar', '.foo'], r
+
+ # adjust_suffix normalizes the suffix, adding a `.' if needed
+
+ r = builder.adjust_suffix('.foo')
+ assert r == '.foo', r
+ r = builder.adjust_suffix('_foo')
+ assert r == '_foo', r
+ r = builder.adjust_suffix('$foo')
+ assert r == '$foo', r
+ r = builder.adjust_suffix('foo')
+ assert r == '.foo', r
+ r = builder.adjust_suffix('f._$oo')
+ assert r == '.f._$oo', r
+
+ # prefix and suffix can be one of:
+ # 1. a string (adjusted and env variables substituted),
+ # 2. a function (passed (env,sources), returns suffix string)
+ # 3. a dict of src_suffix:suffix settings, key==None is
+ # default suffix (special case of #2, so adjust_suffix
+ # not applied)
+
+ builder = SCons.Builder.Builder(prefix='lib', suffix='foo')
+
+ env = Environment(BUILDERS={'Bld':builder})
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'lib', r
+ r = builder.get_suffix(env)
+ assert r == '.foo', r
+
+ mkpref = lambda env,sources: 'Lib'
+ mksuff = lambda env,sources: '.Foo'
+ builder = SCons.Builder.Builder(prefix=mkpref, suffix=mksuff)
+
+ env = Environment(BUILDERS={'Bld':builder})
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'Lib', r
+ r = builder.get_suffix(env)
+ assert r == '.Foo', r
+
+ builder = SCons.Builder.Builder(prefix='$PREF', suffix='$SUFF')
+
+ env = Environment(BUILDERS={'Bld':builder},PREF="LIB",SUFF=".FOO")
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'LIB', r
+ r = builder.get_suffix(env)
+ assert r == '.FOO', r
+
+ builder = SCons.Builder.Builder(prefix={None:'A_',
+ '.C':'E_'},
+ suffix={None:'.B',
+ '.C':'.D'})
+
+ env = Environment(BUILDERS={'Bld':builder})
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r == '.B', r
+ r = builder.get_prefix(env, [MyNode('X.C')])
+ assert r == 'E_', r
+ r = builder.get_suffix(env, [MyNode('X.C')])
+ assert r == '.D', r
+
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r is None, r
+ r = builder.get_src_suffix(env)
+ assert r == '', r
+ r = builder.src_suffixes(env)
+ assert r == [], r
+
+ # Builder actions can be a string, a list, or a dictionary
+ # whose keys are the source suffix. The add_action()
+ # specifies a new source suffix/action binding.
+
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+ builder.add_action('.src_sfx1', 'FOO')
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r is None, r
+ r = builder.get_suffix(env, [MyNode('X.src_sfx1')])
+ assert r is None, r
+ r = builder.get_src_suffix(env)
+ assert r == '.src_sfx1', r
+ r = builder.src_suffixes(env)
+ assert r == ['.src_sfx1'], r
+
+ builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
+ env = Environment(BUILDERS={'Bld':builder})
+ builder.add_action('.src_sfx1', 'FOO')
+ builder.add_action('.src_sfx2', 'BAR')
+
+ r = builder.get_name(env)
+ assert r == 'Bld', r
+ r = builder.get_prefix(env)
+ assert r == 'A_', r
+ r = builder.get_suffix(env)
+ assert r is None, r
+ r = builder.get_src_suffix(env)
+ assert r == '.src_sfx1', r
+ r = builder.src_suffixes(env)
+ r.sort()
+ assert r == ['.src_sfx1', '.src_sfx2'], r
+
+
+ def test_Builder_Args(self):
+ """Testing passing extra args to a builder."""
+ def buildFunc(target, source, env, s=self):
+ s.foo=env['foo']
+ s.bar=env['bar']
+ assert env['CC'] == 'mycc'
+
+ env=Environment(CC='cc')
+
+ builder = SCons.Builder.Builder(action=buildFunc)
+ tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')[0]
+ tgt.build()
+ assert self.foo == 1, self.foo
+ assert self.bar == 2, self.bar
+
+ def test_emitter(self):
+ """Test emitter functions."""
+ def emit(target, source, env):
+ foo = env.get('foo', 0)
+ bar = env.get('bar', 0)
+ for t in target:
+ assert isinstance(t, MyNode)
+ assert t.has_builder()
+ for s in source:
+ assert isinstance(s, MyNode)
+ if foo:
+ target.append("bar%d"%foo)
+ if bar:
+ source.append("baz")
+ return ( target, source )
+
+ env = Environment()
+ builder = SCons.Builder.Builder(action='foo',
+ emitter=emit,
+ target_factory=MyNode,
+ source_factory=MyNode)
+ tgt = builder(env, target='foo2', source='bar')[0]
+ assert str(tgt) == 'foo2', str(tgt)
+ assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+ tgt = builder(env, target='foo3', source='bar', foo=1)
+ assert len(tgt) == 2, len(tgt)
+ assert 'foo3' in map(str, tgt), map(str, tgt)
+ assert 'bar1' in map(str, tgt), map(str, tgt)
+
+ tgt = builder(env, target='foo4', source='bar', bar=1)[0]
+ assert str(tgt) == 'foo4', str(tgt)
+ assert len(tgt.sources) == 2, len(tgt.sources)
+ assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
+ assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
+ env2=Environment(FOO=emit)
+ builder2=SCons.Builder.Builder(action='foo',
+ emitter="$FOO",
+ target_factory=MyNode,
+ source_factory=MyNode)
+
+ builder2a=SCons.Builder.Builder(action='foo',
+ emitter="$FOO",
+ target_factory=MyNode,
+ source_factory=MyNode)
+
+ assert builder2 == builder2a, repr(builder2.__dict__) + "\n" + repr(builder2a.__dict__)
+
+ tgt = builder2(env2, target='foo5', source='bar')[0]
+ assert str(tgt) == 'foo5', str(tgt)
+ assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
+
+ tgt = builder2(env2, target='foo6', source='bar', foo=2)
+ assert len(tgt) == 2, len(tgt)
+ assert 'foo6' in map(str, tgt), map(str, tgt)
+ assert 'bar2' in map(str, tgt), map(str, tgt)
+
+ tgt = builder2(env2, target='foo7', source='bar', bar=1)[0]
+ assert str(tgt) == 'foo7', str(tgt)
+ assert len(tgt.sources) == 2, len(tgt.sources)
+ assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
+ assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
+
+ def test_emitter_preserve_builder(self):
+ """Test an emitter not overwriting a newly-set builder"""
+ env = Environment()
+
+ new_builder = SCons.Builder.Builder(action='new')
+ node = MyNode('foo8')
+ new_node = MyNode('foo8.new')
+
+ def emit(target, source, env, nb=new_builder, nn=new_node):
+ for t in target:
+ t.builder = nb
+ return [nn], source
+
+ builder=SCons.Builder.Builder(action='foo',
+ emitter=emit,
+ target_factory=MyNode,
+ source_factory=MyNode)
+ tgt = builder(env, target=node, source='bar')[0]
+ assert tgt is new_node, tgt
+ assert tgt.builder is builder, tgt.builder
+ assert node.builder is new_builder, node.builder
+
+ def test_emitter_suffix_map(self):
+ """Test mapping file suffixes to emitter functions"""
+ env = Environment()
+
+ def emit4a(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4a-' + x[:-3], source)
+ return (target, source)
+ def emit4b(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4b-' + x[:-3], source)
+ return (target, source)
+
+ builder = SCons.Builder.Builder(action='foo',
+ emitter={'.4a':emit4a,
+ '.4b':emit4b},
+ target_factory=MyNode,
+ source_factory=MyNode)
+ tgt = builder(env, None, source='aaa.4a')[0]
+ assert str(tgt) == 'emit4a-aaa', str(tgt)
+ tgt = builder(env, None, source='bbb.4b')[0]
+ assert str(tgt) == 'emit4b-bbb', str(tgt)
+ tgt = builder(env, None, source='ccc.4c')[0]
+ assert str(tgt) == 'ccc', str(tgt)
+
+ def emit4c(target, source, env):
+ source = map(str, source)
+ target = map(lambda x: 'emit4c-' + x[:-3], source)
+ return (target, source)
+
+ builder.add_emitter('.4c', emit4c)
+ tgt = builder(env, None, source='ccc.4c')[0]
+ assert str(tgt) == 'emit4c-ccc', str(tgt)
+
+ def test_emitter_function_list(self):
+ """Test lists of emitter functions"""
+ env = Environment()
+
+ def emit1a(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit1a-' + x[:-2], source)
+ return (target, source)
+ def emit1b(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit1b-' + x[:-2], source)
+ return (target, source)
+ builder1 = SCons.Builder.Builder(action='foo',
+ emitter=[emit1a, emit1b],
+ node_factory=MyNode)
+
+ tgts = builder1(env, target='target-1', source='aaa.1')
+ tgts = map(str, tgts)
+ assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts
+
+ # Test a list of emitter functions through the environment.
+ def emit2a(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit2a-' + x[:-2], source)
+ return (target, source)
+ def emit2b(target, source, env):
+ source = map(str, source)
+ target = target + map(lambda x: 'emit2b-' + x[:-2], source)
+ return (target, source)
+ builder2 = SCons.Builder.Builder(action='foo',
+ emitter='$EMITTERLIST',
+ node_factory=MyNode)
+
+ env = Environment(EMITTERLIST = [emit2a, emit2b])
+
+ tgts = builder2(env, target='target-2', source='aaa.2')
+ tgts = map(str, tgts)
+ assert tgts == ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts
+
+ def test_emitter_TARGET_SOURCE(self):
+ """Test use of $TARGET and $SOURCE in emitter results"""
+
+ env = SCons.Environment.Environment()
+
+ def emit(target, source, env):
+ return (target + ['${SOURCE}.s1', '${TARGET}.t1'],
+ source + ['${TARGET}.t2', '${SOURCE}.s2'])
+
+ builder = SCons.Builder.Builder(action='foo',
+ emitter = emit,
+ node_factory = MyNode)
+
+ targets = builder(env, target = 'TTT', source ='SSS')
+ sources = targets[0].sources
+ targets = map(str, targets)
+ sources = map(str, sources)
+ assert targets == ['TTT', 'SSS.s1', 'TTT.t1'], targets
+ assert sources == ['SSS', 'TTT.t2', 'SSS.s2'], targets
+
+ 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, None, 'aaa')[0]
+ assert str(tgt) == 'aaa.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'aaa', map(str, tgt.sources)
+
+ tgt = b(env, None, 'bbb.c')[0]
+ assert str(tgt) == 'bbb.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'bbb.c', map(str, tgt.sources)
+
+ tgt = b(env, None, 'ccc.x.c')[0]
+ assert str(tgt) == 'ccc.x.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'ccc.x.c', map(str, tgt.sources)
+
+ tgt = b(env, None, ['d0.c', 'd1.c'])[0]
+ assert str(tgt) == 'd0.o', str(tgt)
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'd0.c', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'd1.c', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source='eee')[0]
+ assert str(tgt) == 'eee.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'eee', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source='fff.c')[0]
+ assert str(tgt) == 'fff.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'fff.c', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source='ggg.x.c')[0]
+ assert str(tgt) == 'ggg.x.o', str(tgt)
+ assert len(tgt.sources) == 1, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'ggg.x.c', map(str, tgt.sources)
+
+ tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0]
+ assert str(tgt) == 'h0.o', str(tgt)
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'h0.c', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'h1.c', map(str, tgt.sources)
+
+ w = b(env, target='i0.w', source=['i0.x'])[0]
+ y = b(env, target='i1.y', source=['i1.z'])[0]
+ tgt = b(env, None, source=[w, y])[0]
+ assert str(tgt) == 'i0.o', str(tgt)
+ assert len(tgt.sources) == 2, map(str, tgt.sources)
+ assert str(tgt.sources[0]) == 'i0.w', map(str, tgt.sources)
+ assert str(tgt.sources[1]) == 'i1.y', map(str, tgt.sources)
+
+ def test_get_name(self):
+ """Test getting name of builder.
+
+ Each type of builder should return its environment-specific
+ name when queried appropriately. """
+
+ b1 = SCons.Builder.Builder(action='foo', suffix='.o')
+ b2 = SCons.Builder.Builder(action='foo', suffix='.c')
+ b3 = SCons.Builder.Builder(action='bar', src_suffix = '.foo',
+ src_builder = b1)
+ b4 = SCons.Builder.Builder(action={})
+ b5 = SCons.Builder.Builder(action='foo', name='builder5')
+ b6 = SCons.Builder.Builder(action='foo')
+ assert isinstance(b4, SCons.Builder.CompositeBuilder)
+ assert isinstance(b4.action, SCons.Action.CommandGeneratorAction)
+
+ env = Environment(BUILDERS={'bldr1': b1,
+ 'bldr2': b2,
+ 'bldr3': b3,
+ 'bldr4': b4})
+ env2 = Environment(BUILDERS={'B1': b1,
+ 'B2': b2,
+ 'B3': b3,
+ 'B4': b4})
+ # With no name, get_name will return the class. Allow
+ # for caching...
+ b6_names = [
+ 'SCons.Builder.BuilderBase',
+ "<class 'SCons.Builder.BuilderBase'>",
+ 'SCons.Memoize.BuilderBase',
+ "<class 'SCons.Memoize.BuilderBase'>",
+ ]
+
+ assert b1.get_name(env) == 'bldr1', b1.get_name(env)
+ assert b2.get_name(env) == 'bldr2', b2.get_name(env)
+ assert b3.get_name(env) == 'bldr3', b3.get_name(env)
+ assert b4.get_name(env) == 'bldr4', b4.get_name(env)
+ assert b5.get_name(env) == 'builder5', b5.get_name(env)
+ assert b6.get_name(env) in b6_names, b6.get_name(env)
+
+ assert b1.get_name(env2) == 'B1', b1.get_name(env2)
+ assert b2.get_name(env2) == 'B2', b2.get_name(env2)
+ assert b3.get_name(env2) == 'B3', b3.get_name(env2)
+ assert b4.get_name(env2) == 'B4', b4.get_name(env2)
+ assert b5.get_name(env2) == 'builder5', b5.get_name(env2)
+ assert b6.get_name(env2) in b6_names, b6.get_name(env2)
+
+ assert b5.get_name(None) == 'builder5', b5.get_name(None)
+ assert b6.get_name(None) in b6_names, b6.get_name(None)
+
+ # This test worked before adding batch builders, but we must now
+ # be able to disambiguate a CompositeAction into a more specific
+ # action based on file suffix at call time. Leave this commented
+ # out (for now) in case this reflects a real-world use case that
+ # we must accomodate and we want to resurrect this test.
+ #tgt = b4(env, target = 'moo', source='cow')
+ #assert tgt[0].builder.get_name(env) == 'bldr4'
+
+class CompositeBuilderTestCase(unittest.TestCase):
+
+ def setUp(self):
+ def func_action(target, source, env):
+ return 0
+
+ builder = SCons.Builder.Builder(action={ '.foo' : func_action,
+ '.bar' : func_action})
+
+ self.func_action = func_action
+ self.builder = builder
+
+ def test___init__(self):
+ """Test CompositeBuilder creation"""
+ env = Environment()
+ builder = SCons.Builder.Builder(action={})
+
+ tgt = builder(env, source=[])
+ assert tgt == [], tgt
+
+ assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
+ def test_target_action(self):
+ """Test CompositeBuilder setting of target builder actions"""
+ env = Environment()
+ builder = self.builder
+
+ tgt = builder(env, target='test1', source='test1.foo')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ assert tgt.builder.action is builder.action
+
+ tgt = builder(env, target='test2', source='test1.bar')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ assert tgt.builder.action is builder.action
+
+ def test_multiple_suffix_error(self):
+ """Test the CompositeBuilder multiple-source-suffix error"""
+ env = Environment()
+ builder = self.builder
+
+ flag = 0
+ try:
+ builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo"
+ assert str(e) == expect, e
+
+ def test_source_ext_match(self):
+ """Test the CompositeBuilder source_ext_match argument"""
+ env = Environment()
+ func_action = self.func_action
+ builder = SCons.Builder.Builder(action={ '.foo' : func_action,
+ '.bar' : func_action},
+ source_ext_match = None)
+
+ tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
+ tgt.build()
+
+ def test_suffix_variable(self):
+ """Test CompositeBuilder defining action suffixes through a variable"""
+ env = Environment(BAR_SUFFIX = '.BAR2', FOO_SUFFIX = '.FOO2')
+ func_action = self.func_action
+ builder = SCons.Builder.Builder(action={ '.foo' : func_action,
+ '.bar' : func_action,
+ '$BAR_SUFFIX' : func_action,
+ '$FOO_SUFFIX' : func_action })
+
+ tgt = builder(env, target='test4', source=['test4.BAR2'])[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+ try:
+ tgt.build()
+ flag = 1
+ except SCons.Errors.UserError, e:
+ print e
+ flag = 0
+ assert flag, "It should be possible to define actions in composite builders using variables."
+ env['FOO_SUFFIX'] = '.BAR2'
+ builder.add_action('$NEW_SUFFIX', func_action)
+ flag = 0
+ try:
+ builder(env, target='test5', source=['test5.BAR2'])[0]
+ except SCons.Errors.UserError:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with ambigous suffixes."
+
+ def test_src_builder(self):
+ """Test CompositeBuilder's use of a src_builder"""
+ env = Environment()
+
+ foo_bld = SCons.Builder.Builder(action = 'a-foo',
+ src_suffix = '.ina',
+ suffix = '.foo')
+ assert isinstance(foo_bld, SCons.Builder.BuilderBase)
+ builder = SCons.Builder.Builder(action = { '.foo' : 'foo',
+ '.bar' : 'bar' },
+ src_builder = foo_bld)
+ assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
+ tgt = builder(env, target='t1', source='t1a.ina t1b.ina')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+ tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase), tgt.builder.__dict__
+
+ bar_bld = SCons.Builder.Builder(action = 'a-bar',
+ src_suffix = '.inb',
+ suffix = '.bar')
+ assert isinstance(bar_bld, SCons.Builder.BuilderBase)
+ builder = SCons.Builder.Builder(action = { '.foo' : 'foo'},
+ src_builder = [foo_bld, bar_bld])
+ assert isinstance(builder, SCons.Builder.CompositeBuilder)
+ assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
+
+ builder.add_action('.bar', 'bar')
+
+ tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+ tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0]
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
+
+ flag = 0
+ try:
+ builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t5']' from `test5b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t6']' from `test6b.foo': Cannot build multiple sources with different extensions: .bar, .foo"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t4']' from `test4b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t7', source=[env.fs.File('test7')])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
+ expect = "While building `['t7']': Cannot deduce file extension from source files: ['test7']"
+ assert str(e) == expect, e
+
+ flag = 0
+ try:
+ builder(env, target='t8', source=['test8.unknown'])[0]
+ except SCons.Errors.UserError, e:
+ flag = 1
+ assert flag, "UserError should be thrown when we call a builder target with an unknown suffix."
+ expect = "While building `['t8']' from `['test8.unknown']': Don't know how to build from a source file with suffix `.unknown'. Expected a suffix in this list: ['.foo', '.bar']."
+ assert str(e) == expect, e
+
+if __name__ == "__main__":
+ suite = unittest.TestSuite()
+ tclasses = [
+ BuilderTestCase,
+ CompositeBuilderTestCase
+ ]
+ for tclass in tclasses:
+ names = unittest.getTestCaseNames(tclass, 'test_')
+ suite.addTests(map(tclass, names))
+ if not unittest.TextTestRunner().run(suite).wasSuccessful():
+ sys.exit(1)
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: