2 # Copyright (c) 2001, 2002 Steven Knight
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
26 # Define a null function for use as a builder action.
27 # Where this is defined in the file seems to affect its
28 # byte-code contents, so try to minimize changes by
29 # defining it here, before we even import anything.
43 # Initial setup of the common environment for all tests,
44 # a temporary working directory containing a
45 # script for writing arguments to an output file.
47 # We don't do this as a setUp() method because it's
48 # unnecessary to create a separate directory and script
49 # for each test, they can just use the one.
50 test = TestCmd.TestCmd(workdir = '')
52 test.write('act.py', """import os, string, sys
53 f = open(sys.argv[1], 'w')
54 f.write("act.py: '" + string.join(sys.argv[2:], "' '") + "'\\n")
57 f.write("act.py: '" + os.environ[sys.argv[3]] + "'\\n")
64 act_py = test.workpath('act.py')
65 outfile = test.workpath('outfile')
66 outfile2 = test.workpath('outfile2')
73 def __init__(self, **kw):
75 for k, v in kw.items():
80 return self.d.get(s[1:], '')
83 return self.d.get(s, s)
84 def get_scanner(self, ext):
88 def autogenerate(self, dir=''):
90 def __getitem__(self, item):
92 def has_key(self, item):
93 return self.d.has_key(item)
97 class BuilderTestCase(unittest.TestCase):
99 def test__call__(self):
100 """Test calling a builder to establish source dependencies
103 def __init__(self, name):
109 def builder_set(self, builder):
110 self.builder = builder
111 def env_set(self, env, safe=0):
113 def add_source(self, source):
114 self.sources.extend(source)
115 def scanner_key(self):
117 builder = SCons.Builder.Builder(name="builder", action="foo", node_factory=Node)
121 builder(env, target = n1, source = n2)
123 assert n1.builder == builder
124 assert n1.sources == [n2]
125 assert not hasattr(n2, 'env')
127 target = builder(env, target = 'n3', source = 'n4')
128 assert target.name == 'n3'
129 assert target.sources[0].name == 'n4'
131 targets = builder(env, target = 'n4 n5', source = ['n6 n7'])
132 assert targets[0].name == 'n4'
133 assert targets[0].sources[0].name == 'n6 n7'
134 assert targets[1].name == 'n5'
135 assert targets[1].sources[0].name == 'n6 n7'
137 target = builder(env, target = ['n8 n9'], source = 'n10 n11')
138 assert target.name == 'n8 n9'
139 assert target.sources[0].name == 'n10'
140 assert target.sources[1].name == 'n11'
142 if not hasattr(types, 'UnicodeType'):
147 targets = builder(env, target = uni('n12 n13'),
148 source = [uni('n14 n15')])
149 assert targets[0].name == uni('n12')
150 assert targets[0].sources[0].name == uni('n14 n15')
151 assert targets[1].name == uni('n13')
152 assert targets[1].sources[0].name == uni('n14 n15')
154 target = builder(env, target = [uni('n16 n17')],
155 source = uni('n18 n19'))
156 assert target.name == uni('n16 n17')
157 assert target.sources[0].name == uni('n18')
158 assert target.sources[1].name == uni('n19')
160 def test_noname(self):
161 """Test error reporting for missing name
163 Verify that the Builder constructor gives an error message if the
167 b = SCons.Builder.Builder()
168 except SCons.Errors.UserError:
173 def test_action(self):
174 """Test Builder creation
176 Verify that we can retrieve the supplied action attribute.
178 builder = SCons.Builder.Builder(name="builder", action="foo")
179 assert builder.action.cmd_list == ["foo"]
181 def test_generator(self):
182 """Test Builder creation given a generator function."""
187 builder = SCons.Builder.Builder(name="builder", generator=generator)
188 assert builder.action.generator == generator
191 """Test simple comparisons of Builder objects
193 b1 = SCons.Builder.Builder(name="b1", src_suffix = '.o')
194 b2 = SCons.Builder.Builder(name="b1", src_suffix = '.o')
196 b3 = SCons.Builder.Builder(name="b3", src_suffix = '.x')
200 def test_execute(self):
201 """Test execution of simple Builder objects
203 One Builder is a string that executes an external command,
204 one is an internal Python function, one is a list
205 containing one of each.
209 builder = apply(SCons.Builder.Builder, (), kw)
212 builder.action.show = no_show
215 python = sys.executable
217 cmd1 = r'%s %s %s xyzzy' % (python, act_py, outfile)
219 builder = MyBuilder(action = cmd1, name = "cmd1")
220 r = builder.execute()
222 c = test.read(outfile, 'r')
223 assert c == "act.py: 'xyzzy'\n", c
225 cmd2 = r'%s %s %s $TARGET' % (python, act_py, outfile)
227 builder = MyBuilder(action = cmd2, name = "cmd2")
228 r = builder.execute(target = 'foo')
230 c = test.read(outfile, 'r')
231 assert c == "act.py: 'foo'\n", c
233 cmd3 = r'%s %s %s ${TARGETS}' % (python, act_py, outfile)
235 builder = MyBuilder(action = cmd3, name = "cmd3")
236 r = builder.execute(target = ['aaa', 'bbb'])
238 c = test.read(outfile, 'r')
239 assert c == "act.py: 'aaa' 'bbb'\n", c
241 cmd4 = r'%s %s %s $SOURCES' % (python, act_py, outfile)
243 builder = MyBuilder(action = cmd4, name = "cmd4")
244 r = builder.execute(source = ['one', 'two'])
246 c = test.read(outfile, 'r')
247 assert c == "act.py: 'one' 'two'\n", c
249 cmd4 = r'%s %s %s ${SOURCES[:2]}' % (python, act_py, outfile)
251 builder = MyBuilder(action = cmd4, name = "cmd4")
252 r = builder.execute(source = ['three', 'four', 'five'])
254 c = test.read(outfile, 'r')
255 assert c == "act.py: 'three' 'four'\n", c
257 cmd5 = r'%s %s %s $TARGET XYZZY' % (python, act_py, outfile)
259 builder = MyBuilder(action = cmd5, name = "cmd5")
260 r = builder.execute(target = 'out5', env = {'ENV' : {'XYZZY' : 'xyzzy'}})
262 c = test.read(outfile, 'r')
263 assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy'\n", c
266 def __init__(self, str):
271 cmd6 = r'%s %s %s ${TARGETS[1]} $TARGET ${SOURCES[:2]}' % (python, act_py, outfile)
273 builder = MyBuilder(action = cmd6, name = "cmd6")
274 r = builder.execute(target = [Obj('111'), Obj('222')],
275 source = [Obj('333'), Obj('444'), Obj('555')])
277 c = test.read(outfile, 'r')
278 assert c == "act.py: '222' '111' '333' '444'\n", c
280 cmd7 = '%s %s %s one\n\n%s %s %s two' % (python, act_py, outfile,
281 python, act_py, outfile)
282 expect7 = '%s %s %s one\n%s %s %s two\n' % (python, act_py, outfile,
283 python, act_py, outfile)
285 builder = MyBuilder(action = cmd7, name = "cmd7")
291 show_string = show_string + string + "\n"
292 for action in builder.action.list:
293 action.show = my_show
295 r = builder.execute()
297 assert show_string == expect7, show_string
304 if not type(kw['target']) is type([]):
305 kw['target'] = [ kw['target'] ]
306 for t in kw['target']:
307 open(t, 'w').write("function1\n")
310 builder = MyBuilder(action = function1, name = "function1")
312 r = builder.execute(target = [outfile, outfile2])
313 except SCons.Errors.BuildError:
317 c = test.read(outfile, 'r')
318 assert c == "function1\n", c
319 c = test.read(outfile2, 'r')
320 assert c == "function1\n", c
323 def __init__(self, **kw):
324 open(kw['out'], 'w').write("class1a\n")
326 builder = MyBuilder(action = class1a, name = "class1a")
327 r = builder.execute(out = outfile)
328 assert r.__class__ == class1a
329 c = test.read(outfile, 'r')
330 assert c == "class1a\n", c
333 def __call__(self, **kw):
334 open(kw['out'], 'w').write("class1b\n")
337 builder = MyBuilder(action = class1b(), name = "class1b")
338 r = builder.execute(out = outfile)
340 c = test.read(outfile, 'r')
341 assert c == "class1b\n", c
343 cmd2 = r'%s %s %s syzygy' % (python, act_py, outfile)
346 open(kw['out'], 'a').write("function2\n")
350 def __call__(self, **kw):
351 open(kw['out'], 'a').write("class2a\n")
355 def __init__(self, **kw):
356 open(kw['out'], 'a').write("class2b\n")
358 builder = MyBuilder(action = SCons.Action.ListAction([cmd2, function2, class2a(), class2b]), name = "clist")
359 r = builder.execute(out = outfile)
360 assert r.__class__ == class2b
361 c = test.read(outfile, 'r')
362 assert c == "act.py: 'syzygy'\nfunction2\nclass2a\nclass2b\n", c
365 # NT treats execs of directories and non-executable files
366 # as "file not found" errors
367 expect_nonexistent = 1
368 expect_nonexecutable = 1
370 expect_nonexistent = 127
371 expect_nonexecutable = 126
373 # Test that a nonexistent command returns 127
374 builder = MyBuilder(action = python + "_XyZzY_", name="badcmd")
375 r = builder.execute(out = outfile)
376 assert r == expect_nonexistent, "r == %d" % r
378 # Test that trying to execute a directory returns 126
379 dir, tail = os.path.split(python)
380 builder = MyBuilder(action = dir, name = "dir")
381 r = builder.execute(out = outfile)
382 assert r == expect_nonexecutable, "r == %d" % r
384 # Test that trying to execute a non-executable file returns 126
385 builder = MyBuilder(action = outfile, name = "badfile")
386 r = builder.execute(out = outfile)
387 assert r == expect_nonexecutable, "r == %d" % r
389 def test_get_contents(self):
390 """Test returning the signature contents of a Builder
393 b1 = SCons.Builder.Builder(name = "b1", action = "foo")
394 contents = b1.get_contents()
395 assert contents == "foo", contents
397 b2 = SCons.Builder.Builder(name = "b2", action = Func)
398 contents = b2.get_contents()
399 assert contents == "\177\036\000\177\037\000d\000\000S", repr(contents)
401 b3 = SCons.Builder.Builder(name = "b3", action = SCons.Action.ListAction(["foo", Func, "bar"]))
402 contents = b3.get_contents()
403 assert contents == "foo\177\036\000\177\037\000d\000\000Sbar", repr(contents)
405 def test_node_factory(self):
406 """Test a Builder that creates nodes of a specified class
410 def FooFactory(target):
413 builder = SCons.Builder.Builder(name = "builder", node_factory = FooFactory)
414 assert builder.target_factory is FooFactory
415 assert builder.source_factory is FooFactory
417 def test_target_factory(self):
418 """Test a Builder that creates target nodes of a specified class
422 def FooFactory(target):
425 builder = SCons.Builder.Builder(name = "builder", target_factory = FooFactory)
426 assert builder.target_factory is FooFactory
427 assert not builder.source_factory is FooFactory
429 def test_source_factory(self):
430 """Test a Builder that creates source nodes of a specified class
434 def FooFactory(source):
437 builder = SCons.Builder.Builder(name = "builder", source_factory = FooFactory)
438 assert not builder.target_factory is FooFactory
439 assert builder.source_factory is FooFactory
441 def test_prefix(self):
442 """Test Builder creation with a specified target prefix
444 Make sure that there is no '.' separator appended.
446 builder = SCons.Builder.Builder(name = "builder", prefix = 'lib.')
447 assert builder.get_prefix(env,{}) == 'lib.'
448 builder = SCons.Builder.Builder(name = "builder", prefix = 'lib')
449 assert builder.get_prefix(env,{}) == 'lib'
450 tgt = builder(env, target = 'tgt1', source = 'src1')
451 assert tgt.path == 'libtgt1', \
452 "Target has unexpected name: %s" % tgt.path
453 tgts = builder(env, target = 'tgt2a tgt2b', source = 'src2')
454 assert tgts[0].path == 'libtgt2a', \
455 "Target has unexpected name: %s" % tgts[0].path
456 assert tgts[1].path == 'libtgt2b', \
457 "Target has unexpected name: %s" % tgts[1].path
459 def test_src_suffix(self):
460 """Test Builder creation with a specified source file suffix
462 Make sure that the '.' separator is appended to the
463 beginning if it isn't already present.
465 env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
467 b1 = SCons.Builder.Builder(name = "builder", src_suffix = '.c')
468 assert b1.src_suffixes(env,{}) == ['.c'], b1.src_suffixes(env,{})
470 tgt = b1(env, target = 'tgt2', source = 'src2')
471 assert tgt.sources[0].path == 'src2.c', \
472 "Source has unexpected name: %s" % tgt.sources[0].path
474 tgt = b1(env, target = 'tgt3', source = 'src3a src3b')
475 assert tgt.sources[0].path == 'src3a.c', \
476 "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
477 assert tgt.sources[1].path == 'src3b.c', \
478 "Unexpected tgt.sources[1] name: %s" % tgt.sources[1].path
480 b2 = SCons.Builder.Builder(name = "b2",
483 assert b2.src_suffixes(env,{}) == ['.2', '.c'], b2.src_suffixes(env,{})
485 b3 = SCons.Builder.Builder(name = "b3",
486 action = {'.3a' : '', '.3b' : ''})
487 s = b3.src_suffixes(env,{})
489 assert s == ['.3a', '.3b'], s
491 b4 = SCons.Builder.Builder(name = "b4", src_suffix = '$XSUFFIX')
492 assert b4.src_suffixes(env,{}) == ['.x'], b4.src_suffixes(env,{})
494 b5 = SCons.Builder.Builder(name = "b5", action = {'$YSUFFIX' : ''})
495 assert b5.src_suffixes(env,{}) == ['.y'], b5.src_suffixes(env,{})
497 def test_suffix(self):
498 """Test Builder creation with a specified target suffix
500 Make sure that the '.' separator is appended to the
501 beginning if it isn't already present.
503 builder = SCons.Builder.Builder(name = "builder", suffix = '.o')
504 assert builder.get_suffix(env,{}) == '.o', builder.get_suffix(env,{})
505 builder = SCons.Builder.Builder(name = "builder", suffix = 'o')
506 assert builder.get_suffix(env,{}) == '.o', builder.get_suffix(env,{})
507 tgt = builder(env, target = 'tgt3', source = 'src3')
508 assert tgt.path == 'tgt3.o', \
509 "Target has unexpected name: %s" % tgt[0].path
510 tgts = builder(env, target = 'tgt4a tgt4b', source = 'src4')
511 assert tgts[0].path == 'tgt4a.o', \
512 "Target has unexpected name: %s" % tgts[0].path
513 tgts = builder(env, target = 'tgt4a tgt4b', source = 'src4')
514 assert tgts[1].path == 'tgt4b.o', \
515 "Target has unexpected name: %s" % tgts[1].path
517 def test_ListBuilder(self):
518 """Testing ListBuilder class."""
521 def function2(tlist = [outfile, outfile2], **kw):
524 for t in kw['target']:
525 open(str(t), 'w').write("function2\n")
527 if not t in map(str, kw['target']):
528 open(t, 'w').write("function2\n")
531 builder = SCons.Builder.Builder(action = function2, name = "function2")
532 tgts = builder(env, target = [outfile, outfile2], source = 'foo')
534 r = tgts[0].builder.execute(target = tgts)
535 except SCons.Errors.BuildError:
537 c = test.read(outfile, 'r')
538 assert c == "function2\n", c
539 c = test.read(outfile2, 'r')
540 assert c == "function2\n", c
541 r = tgts[1].builder.execute(target = tgts[1])
543 assert count == 1, count
545 sub1_out = test.workpath('sub1', 'out')
546 sub2_out = test.workpath('sub2', 'out')
549 def function3(tlist = [sub1_out, sub2_out], **kw):
552 for t in kw['target']:
553 open(str(t), 'w').write("function3\n")
555 if not t in map(str, kw['target']):
556 open(t, 'w').write("function3\n")
559 builder = SCons.Builder.Builder(action = function3, name = "function3")
560 tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo')
562 r = tgts[0].builder.execute(target = tgts)
566 c = test.read(sub1_out, 'r')
567 assert c == "function3\n", c
568 c = test.read(sub2_out, 'r')
569 assert c == "function3\n", c
570 assert os.path.exists(test.workpath('sub1'))
571 assert os.path.exists(test.workpath('sub2'))
573 def test_MultiStepBuilder(self):
574 """Testing MultiStepBuilder class."""
575 builder1 = SCons.Builder.Builder(name = "builder1",
579 builder2 = SCons.Builder.MultiStepBuilder(name = "builder2",
581 src_builder = builder1,
583 tgt = builder2(env, target='baz', source='test.bar test2.foo test3.txt')
584 assert str(tgt.sources[0]) == 'test.foo', str(tgt.sources[0])
585 assert str(tgt.sources[0].sources[0]) == 'test.bar', \
586 str(tgt.sources[0].sources[0])
587 assert str(tgt.sources[1]) == 'test2.foo', str(tgt.sources[1])
588 assert str(tgt.sources[2]) == 'test3.txt', str(tgt.sources[2])
590 def test_CompositeBuilder(self):
591 """Testing CompositeBuilder class."""
592 def func_action(target, source, env):
595 builder = SCons.Builder.Builder(name = "builder",
596 action={ '.foo' : func_action,
597 '.bar' : func_action })
599 assert isinstance(builder, SCons.Builder.BuilderBase)
600 assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
601 tgt = builder(env, target='test1', source='test1.foo')
602 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
603 assert isinstance(tgt.builder.action.generator, SCons.Builder.DictCmdGenerator)
605 tgt = builder(env, target='test2', source='test2.bar test1.foo')
608 except SCons.Errors.BuildError, e:
609 assert e.args[0] == SCons.Errors.UserError
611 assert flag, "UserError should be thrown when we build targets with files of different suffixes."
613 foo_bld = SCons.Builder.Builder(name = "foo_bld",
617 assert isinstance(foo_bld, SCons.Builder.BuilderBase)
618 builder = SCons.Builder.Builder(name = "builder",
619 action = { '.foo' : 'foo',
621 src_builder = foo_bld)
622 assert isinstance(builder, SCons.Builder.MultiStepBuilder)
623 assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
625 tgt = builder(env, target='t1', source='t1a.ina t1b.ina')
626 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
628 tgt = builder(env, target='t2', source='t2a.foo t2b.ina')
629 assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder), tgt.builder.__dict__
631 bar_bld = SCons.Builder.Builder(name = "bar_bld",
635 assert isinstance(bar_bld, SCons.Builder.BuilderBase)
636 builder = SCons.Builder.Builder(name = "builder",
637 action = { '.foo' : 'foo',
639 src_builder = [foo_bld, bar_bld])
640 assert isinstance(builder, SCons.Builder.MultiStepBuilder)
641 assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
643 tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')
644 assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
646 tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')
647 assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
650 tgt = builder(env, target='t5', source='test5a.foo test5b.inb')
653 except SCons.Errors.BuildError, e:
654 assert e.args[0] == SCons.Errors.UserError
656 assert flag, "UserError should be thrown when we build targets with files of different suffixes."
659 tgt = builder(env, target='t6', source='test6a.bar test6b.ina')
662 except SCons.Errors.BuildError, e:
663 assert e.args[0] == SCons.Errors.UserError
665 assert flag, "UserError should be thrown when we build targets with files of different suffixes."
668 tgt = builder(env, target='t4', source='test4a.ina test4b.inb')
671 except SCons.Errors.BuildError, e:
672 assert e.args[0] == SCons.Errors.UserError
674 assert flag, "UserError should be thrown when we build targets with files of different suffixes."
677 def test_build_scanner(self):
678 """Testing ability to set a target scanner through a builder."""
683 builder = SCons.Builder.Builder(name = "builder", scanner=scn)
684 tgt = builder(env, target='foo2', source='bar')
685 assert tgt.target_scanner == scn, tgt.target_scanner
687 builder1 = SCons.Builder.Builder(name = "builder1",
691 builder2 = SCons.Builder.Builder(name = "builder2",
693 src_builder = builder1,
695 tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')
696 assert tgt.target_scanner == scn, tgt.target_scanner
698 def test_src_scanner(slf):
699 """Testing ability to set a source file scanner through a builder."""
703 return 'TestScannerkey'
704 def instance(self, env):
706 env_scanner = TestScanner()
707 builder = SCons.Builder.Builder(name = "builder", action='action')
708 tgt = builder(env, target='foo.x', source='bar')
710 assert tgt.target_scanner != env_scanner, tgt.target_scanner
711 assert src.source_scanner == env_scanner
713 def test_Builder_Args(self):
714 """Testing passing extra agrs to a builder."""
715 def buildFunc(target, source, env, foo, bar, s=self):
719 builder = SCons.Builder.Builder(name="builder", action=buildFunc)
720 tgt = builder(env, target='foo', source='bar', foo=1, bar=2)
722 assert self.foo == 1, self.foo
723 assert self.bar == 2, self.bar
725 def test_emitter(self):
726 """Test emitter functions."""
727 def emit(target, source, env, foo=0, bar=0):
729 target.append("bar%d"%foo)
732 return ( target, source )
734 builder = SCons.Builder.Builder(name="builder", action='foo',
736 tgt = builder(env, target='foo2', source='bar')
737 assert str(tgt) == 'foo2', str(tgt)
738 assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
740 tgt = builder(env, target='foo3', source='bar', foo=1)
741 assert len(tgt) == 2, len(tgt)
742 assert 'foo3' in map(str, tgt), map(str, tgt)
743 assert 'bar1' in map(str, tgt), map(str, tgt)
745 tgt = builder(env, target='foo4', source='bar', bar=1)
746 assert str(tgt) == 'foo4', str(tgt)
747 assert len(tgt.sources) == 2, len(tgt.sources)
748 assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
749 assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
751 env2=Environment(FOO=emit)
752 builder2=SCons.Builder.Builder(name="builder2", action='foo',
755 tgt = builder2(env2, target='foo5', source='bar')
756 assert str(tgt) == 'foo5', str(tgt)
757 assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
759 tgt = builder2(env2, target='foo6', source='bar', foo=2)
760 assert len(tgt) == 2, len(tgt)
761 assert 'foo6' in map(str, tgt), map(str, tgt)
762 assert 'bar2' in map(str, tgt), map(str, tgt)
764 tgt = builder2(env2, target='foo7', source='bar', bar=1)
765 assert str(tgt) == 'foo7', str(tgt)
766 assert len(tgt.sources) == 2, len(tgt.sources)
767 assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
768 assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
770 if __name__ == "__main__":
771 suite = unittest.makeSuite(BuilderTestCase, 'test_')
772 if not unittest.TextTestRunner().run(suite).wasSuccessful():