+ assert 'baz' in list(map(str, tgt.sources)), list(map(str, tgt.sources))
+ assert 'bar' in list(map(str, tgt.sources)), list(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 = list(map(str, source))
+ target = ['emit4a-' + x[:-3] for x in source]
+ return (target, source)
+ def emit4b(target, source, env):
+ source = list(map(str, source))
+ target = ['emit4b-' + x[:-3] for x in 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 = list(map(str, source))
+ target = ['emit4c-' + x[:-3] for x in 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 = list(map(str, source))
+ target = target + ['emit1a-' + x[:-2] for x in source]
+ return (target, source)
+ def emit1b(target, source, env):
+ source = list(map(str, source))
+ target = target + ['emit1b-' + x[:-2] for x in 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 = list(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 = list(map(str, source))
+ target = target + ['emit2a-' + x[:-2] for x in source]
+ return (target, source)
+ def emit2b(target, source, env):
+ source = list(map(str, source))
+ target = target + ['emit2b-' + x[:-2] for x in 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 = list(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 = list(map(str, targets))
+ sources = list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'aaa', list(map(str, tgt.sources))
+
+ tgt = b(env, None, 'bbb.c')[0]
+ assert str(tgt) == 'bbb.o', str(tgt)
+ assert len(tgt.sources) == 1, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'bbb.c', list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'ccc.x.c', list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'd0.c', list(map(str, tgt.sources))
+ assert str(tgt.sources[1]) == 'd1.c', list(map(str, tgt.sources))
+
+ tgt = b(env, target = None, source='eee')[0]
+ assert str(tgt) == 'eee.o', str(tgt)
+ assert len(tgt.sources) == 1, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'eee', list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'fff.c', list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'ggg.x.c', list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'h0.c', list(map(str, tgt.sources))
+ assert str(tgt.sources[1]) == 'h1.c', list(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, list(map(str, tgt.sources))
+ assert str(tgt.sources[0]) == 'i0.w', list(map(str, tgt.sources))
+ assert str(tgt.sources[1]) == 'i1.y', list(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