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__"
28 # Define a null function for use as a builder action.
29 # Where this is defined in the file seems to affect its
30 # byte-code contents, so try to minimize changes by
31 # defining it here, before we even import anything.
46 import SCons.Environment
51 sys.stdout = io.StringIO()
53 # Initial setup of the common environment for all tests,
54 # a temporary working directory containing a
55 # script for writing arguments to an output file.
57 # We don't do this as a setUp() method because it's
58 # unnecessary to create a separate directory and script
59 # for each test, they can just use the one.
60 test = TestCmd.TestCmd(workdir = '')
62 outfile = test.workpath('outfile')
63 outfile2 = test.workpath('outfile2')
65 infile = test.workpath('infile')
66 test.write(infile, "infile\n")
70 scons_env = SCons.Environment.Environment()
72 env_arg2nodes_called = None
75 def __init__(self, **kw):
77 self.d['SHELL'] = scons_env['SHELL']
78 self.d['SPAWN'] = scons_env['SPAWN']
79 self.d['ESCAPE'] = scons_env['ESCAPE']
80 for k, v in kw.items():
82 global env_arg2nodes_called
83 env_arg2nodes_called = None
85 self.fs = SCons.Node.FS.FS()
87 if not SCons.Util.is_String(s):
89 def substitute(m, d=self.d):
90 return d.get(m.group(1), '')
91 return re.sub(r'\$(\w+)', substitute, s)
92 def subst_target_source(self, string, raw=0, target=None,
93 source=None, dict=None, conv=None):
94 return SCons.Subst.scons_subst(string, self, raw, target,
96 def subst_list(self, string, raw=0, target=None, source=None, conv=None):
97 return SCons.Subst.scons_subst_list(string, self, raw, target,
99 def arg2nodes(self, args, factory, **kw):
100 global env_arg2nodes_called
101 env_arg2nodes_called = 1
102 if not SCons.Util.is_List(args):
106 if SCons.Util.is_String(a):
107 a = factory(self.subst(a))
110 def get_factory(self, factory):
111 return factory or self.fs.File
112 def get_scanner(self, ext):
114 def Dictionary(self):
116 def autogenerate(self, dir=''):
118 def __setitem__(self, item, var):
120 def __getitem__(self, item):
122 def __contains__(self, item):
123 return self.d.__contains__(item)
124 def has_key(self, item):
125 return item in self.d
128 def get(self, key, value=None):
129 return self.d.get(key, value)
130 def Override(self, overrides):
131 env = Environment(**self.d)
132 env.d.update(overrides)
133 env.scanner = self.scanner
135 def _update(self, dict):
138 return self.d.items()
141 for k,v in self.items(): d[k] = v
142 d['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
143 d['TARGET'] = d['TARGETS'][0]
144 d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
145 d['SOURCE'] = d['SOURCES'][0]
147 def __cmp__(self, other):
148 return cmp(self.scanner, other.scanner) or cmp(self.d, other.d)
151 def __init__(self, action):
153 def __call__(self, *args, **kw):
155 def get_executor(self, env, overrides, tlist, slist, executor_kw):
156 return ['executor'] + [self.action]
158 class MyNode_without_target_from_source:
159 def __init__(self, name):
163 self.is_explicit = None
165 self.suffix = os.path.splitext(name)[1]
166 def disambiguate(self):
170 def builder_set(self, builder):
171 self.builder = builder
172 def has_builder(self):
173 return not self.builder is None
174 def set_explicit(self, is_explicit):
175 self.is_explicit = is_explicit
176 def has_explicit_builder(self):
177 return self.is_explicit
178 def env_set(self, env, safe=0):
180 def add_source(self, source):
181 self.sources.extend(source)
182 def scanner_key(self):
184 def is_derived(self):
185 return self.has_builder()
186 def generate_build_env(self, env):
188 def get_build_env(self):
189 return self.executor.get_build_env()
190 def set_executor(self, executor):
191 self.executor = executor
192 def get_executor(self, create=1):
195 class MyNode(MyNode_without_target_from_source):
196 def target_from_source(self, prefix, suffix, stripext):
197 return MyNode(prefix + stripext(str(self))[0] + suffix)
199 class BuilderTestCase(unittest.TestCase):
201 def test__init__(self):
202 """Test simple Builder creation
204 builder = SCons.Builder.Builder(action="foo")
205 assert not builder is None, builder
206 builder = SCons.Builder.Builder(action="foo", OVERRIDE='x')
207 x = builder.overrides['OVERRIDE']
210 def test__nonzero__(self):
211 """Test a builder raising an exception when __nonzero__ is called
213 builder = SCons.Builder.Builder(action="foo")
216 builder.__nonzero__()
217 except SCons.Errors.InternalError:
219 assert exc_caught, "did not catch expected InternalError exception"
230 except SCons.Errors.InternalError:
232 assert exc_caught, "did not catch expected InternalError exception"
234 def test__call__(self):
235 """Test calling a builder to establish source dependencies
238 builder = SCons.Builder.Builder(action="foo",
239 target_factory=MyNode,
240 source_factory=MyNode)
242 tgt = builder(env, source=[])
243 assert tgt == [], tgt
247 builder(env, target = n1, source = n2)
248 assert env_arg2nodes_called
249 assert n1.env == env, n1.env
250 assert n1.builder == builder, n1.builder
251 assert n1.sources == [n2], n1.sources
252 assert n1.executor, "no executor found"
253 assert not hasattr(n2, 'env')
256 ul = collections.UserList([2])
261 return str(list(map(str, l)))
265 nnn1 = MyNode("nnn1")
266 nnn2 = MyNode("nnn2")
267 tlist = builder(env, target = [nnn1, nnn2], source = [])
269 assert s == "['nnn1', 'nnn2']", s
270 l = list(map(str, tlist))
271 assert l == ['nnn1', 'nnn2'], l
273 tlist = builder(env, target = 'n3', source = 'n4')
275 assert s == "['n3']", s
277 l = list(map(str, tlist))
278 assert l == ['n3'], l
279 assert target.name == 'n3'
280 assert target.sources[0].name == 'n4'
282 tlist = builder(env, target = 'n4 n5', source = ['n6 n7'])
284 assert s == "['n4 n5']", s
285 l = list(map(str, tlist))
286 assert l == ['n4 n5'], l
288 assert target.name == 'n4 n5'
289 assert target.sources[0].name == 'n6 n7'
291 tlist = builder(env, target = ['n8 n9'], source = 'n10 n11')
293 assert s == "['n8 n9']", s
294 l = list(map(str, tlist))
295 assert l == ['n8 n9'], l
297 assert target.name == 'n8 n9'
298 assert target.sources[0].name == 'n10 n11'
300 # A test to be uncommented when we freeze the environment
301 # as part of calling the builder.
302 #env1 = Environment(VAR='foo')
303 #target = builder(env1, target = 'n12', source = 'n13')
305 #be = target.get_build_env()
306 #assert be['VAR'] == 'foo', be['VAR']
314 target = builder(env, target = uni('n12 n13'),
315 source = [uni('n14 n15')])[0]
316 assert target.name == uni('n12 n13')
317 assert target.sources[0].name == uni('n14 n15')
319 target = builder(env, target = [uni('n16 n17')],
320 source = uni('n18 n19'))[0]
321 assert target.name == uni('n16 n17')
322 assert target.sources[0].name == uni('n18 n19')
324 n20 = MyNode_without_target_from_source('n20')
327 target = builder(env, None, source=n20)
328 except SCons.Errors.UserError, e:
330 assert flag, "UserError should be thrown if a source node can't create a target."
332 builder = SCons.Builder.Builder(action="foo",
333 target_factory=MyNode,
334 source_factory=MyNode,
337 target = builder(env, None, source='n21')[0]
338 assert target.name == 'p-n21.s', target
340 builder = SCons.Builder.Builder(misspelled_action="foo",
343 builder(env, target = 'n22', source = 'n22')
344 except SCons.Errors.UserError, e:
347 raise "Did not catch expected UserError."
349 builder = SCons.Builder.Builder(action="foo")
350 target = builder(env, None, source='n22', srcdir='src_dir')[0]
351 p = target.sources[0].path
352 assert p == os.path.join('src_dir', 'n22'), p
354 def test_mistaken_variables(self):
355 """Test keyword arguments that are often mistakes
357 import SCons.Warnings
359 builder = SCons.Builder.Builder(action="foo")
361 save_warn = SCons.Warnings.warn
363 def my_warn(exception, warning, warned=warned):
364 warned.append(warning)
365 SCons.Warnings.warn = my_warn
368 target = builder(env, 'mistaken1', sources='mistaken1.c')
369 assert warned == ["Did you mean to use `source' instead of `sources'?"], warned
372 target = builder(env, 'mistaken2', targets='mistaken2.c')
373 assert warned == ["Did you mean to use `target' instead of `targets'?"], warned
376 target = builder(env, 'mistaken3', targets='mistaken3', sources='mistaken3.c')
377 assert "Did you mean to use `source' instead of `sources'?" in warned, warned
378 assert "Did you mean to use `target' instead of `targets'?" in warned, warned
381 SCons.Warnings.warn = save_warn
383 def test_action(self):
384 """Test Builder creation
386 Verify that we can retrieve the supplied action attribute.
388 builder = SCons.Builder.Builder(action="foo")
389 assert builder.action.cmd_list == "foo"
393 builder = SCons.Builder.Builder(action=func)
394 assert isinstance(builder.action, SCons.Action.FunctionAction)
395 # Preserve the following so that the baseline test will fail.
396 # Remove it in favor of the previous test at some convenient
397 # point in the future.
398 assert builder.action.execfunction == func
400 def test_generator(self):
401 """Test Builder creation given a generator function."""
406 builder = SCons.Builder.Builder(generator=generator)
407 assert builder.action.generator == generator
409 def test_get_name(self):
410 """Test the get_name() method
414 """Test simple comparisons of Builder objects
416 b1 = SCons.Builder.Builder(src_suffix = '.o')
417 b2 = SCons.Builder.Builder(src_suffix = '.o')
419 b3 = SCons.Builder.Builder(src_suffix = '.x')
423 def test_target_factory(self):
424 """Test a Builder that creates target nodes of a specified class
428 def FooFactory(target):
431 builder = SCons.Builder.Builder(target_factory = FooFactory)
432 assert builder.target_factory is FooFactory
433 assert not builder.source_factory is FooFactory
435 def test_source_factory(self):
436 """Test a Builder that creates source nodes of a specified class
440 def FooFactory(source):
443 builder = SCons.Builder.Builder(source_factory = FooFactory)
444 assert not builder.target_factory is FooFactory
445 assert builder.source_factory is FooFactory
447 def test_splitext(self):
448 """Test the splitext() method attached to a Builder."""
449 b = SCons.Builder.Builder()
450 assert b.splitext('foo') == ('foo','')
451 assert b.splitext('foo.bar') == ('foo','.bar')
452 assert b.splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
454 class MyBuilder(SCons.Builder.BuilderBase):
455 def splitext(self, path):
456 return "called splitext()"
459 ret = b.splitext('xyz.c')
460 assert ret == "called splitext()", ret
462 def test_adjust_suffix(self):
463 """Test how a Builder adjusts file suffixes
465 b = SCons.Builder.Builder()
466 assert b.adjust_suffix('.foo') == '.foo'
467 assert b.adjust_suffix('foo') == '.foo'
468 assert b.adjust_suffix('$foo') == '$foo'
470 class MyBuilder(SCons.Builder.BuilderBase):
471 def adjust_suffix(self, suff):
472 return "called adjust_suffix()"
475 ret = b.adjust_suffix('.foo')
476 assert ret == "called adjust_suffix()", ret
478 def test_prefix(self):
479 """Test Builder creation with a specified target prefix
481 Make sure that there is no '.' separator appended.
484 builder = SCons.Builder.Builder(prefix = 'lib.')
485 assert builder.get_prefix(env) == 'lib.'
486 builder = SCons.Builder.Builder(prefix = 'lib', action='')
487 assert builder.get_prefix(env) == 'lib'
488 tgt = builder(env, target = 'tgt1', source = 'src1')[0]
489 assert tgt.path == 'libtgt1', \
490 "Target has unexpected name: %s" % tgt.path
491 tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
492 assert tgt.path == 'libtgt2a tgt2b', \
493 "Target has unexpected name: %s" % tgt.path
494 tgt = builder(env, target = None, source = 'src3')[0]
495 assert tgt.path == 'libsrc3', \
496 "Target has unexpected name: %s" % tgt.path
497 tgt = builder(env, target = None, source = 'lib/src4')[0]
498 assert tgt.path == os.path.join('lib', 'libsrc4'), \
499 "Target has unexpected name: %s" % tgt.path
500 tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
501 assert tgt.path == os.path.join('lib', 'libtgt5'), \
502 "Target has unexpected name: %s" % tgt.path
504 def gen_prefix(env, sources):
505 return "gen_prefix() says " + env['FOO']
506 my_env = Environment(FOO = 'xyzzy')
507 builder = SCons.Builder.Builder(prefix = gen_prefix)
508 assert builder.get_prefix(my_env) == "gen_prefix() says xyzzy"
509 my_env['FOO'] = 'abracadabra'
510 assert builder.get_prefix(my_env) == "gen_prefix() says abracadabra"
512 def my_emit(env, sources):
513 return env.subst('$EMIT')
514 my_env = Environment(FOO = '.foo', EMIT = 'emit-')
515 builder = SCons.Builder.Builder(prefix = {None : 'default-',
521 tgt = builder(my_env, target = None, source = 'f1')[0]
522 assert tgt.path == 'default-f1', tgt.path
523 tgt = builder(my_env, target = None, source = 'f2.c')[0]
524 assert tgt.path == 'default-f2', tgt.path
525 tgt = builder(my_env, target = None, source = 'f3.in')[0]
526 assert tgt.path == 'out-f3', tgt.path
527 tgt = builder(my_env, target = None, source = 'f4.x')[0]
528 assert tgt.path == 'y-f4', tgt.path
529 tgt = builder(my_env, target = None, source = 'f5.foo')[0]
530 assert tgt.path == 'foo-f5', tgt.path
531 tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
532 assert tgt.path == 'emit-f6', tgt.path
534 def test_set_suffix(self):
535 """Test the set_suffix() method"""
536 b = SCons.Builder.Builder(action='')
537 env = Environment(XSUFFIX = '.x')
539 s = b.get_suffix(env)
543 s = b.get_suffix(env)
544 assert s == '.foo', s
546 b.set_suffix('$XSUFFIX')
547 s = b.get_suffix(env)
550 def test_src_suffix(self):
551 """Test Builder creation with a specified source file suffix
553 Make sure that the '.' separator is appended to the
554 beginning if it isn't already present.
556 env = Environment(XSUFFIX = '.x', YSUFFIX = '.y')
558 b1 = SCons.Builder.Builder(src_suffix = '.c', action='')
559 assert b1.src_suffixes(env) == ['.c'], b1.src_suffixes(env)
561 tgt = b1(env, target = 'tgt2', source = 'src2')[0]
562 assert tgt.sources[0].path == 'src2.c', \
563 "Source has unexpected name: %s" % tgt.sources[0].path
565 tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0]
566 assert len(tgt.sources) == 1
567 assert tgt.sources[0].path == 'src3a src3b.c', \
568 "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].path
570 b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1)
571 r = sorted(b2.src_suffixes(env))
572 assert r == ['.2', '.c'], r
574 b3 = SCons.Builder.Builder(action = {'.3a' : '', '.3b' : ''})
575 s = sorted(b3.src_suffixes(env))
576 assert s == ['.3a', '.3b'], s
578 b4 = SCons.Builder.Builder(src_suffix = '$XSUFFIX')
579 assert b4.src_suffixes(env) == ['.x'], b4.src_suffixes(env)
581 b5 = SCons.Builder.Builder(action = { '.y' : ''})
582 assert b5.src_suffixes(env) == ['.y'], b5.src_suffixes(env)
584 def test_srcsuffix_nonext(self):
585 "Test target generation from non-extension source suffixes"
587 b6 = SCons.Builder.Builder(action = '',
590 tgt = b6(env, target=None, source='foo_src.a')
591 assert str(tgt[0]) == 'foo.b', str(tgt[0])
593 b7 = SCons.Builder.Builder(action = '',
594 src_suffix='_source.a',
596 b8 = SCons.Builder.Builder(action = '',
599 tgt = b8(env, target=None, source='foo_source.a')
600 assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
601 src = env.fs.File('foo_source.a')
602 tgt = b8(env, target=None, source=src)
603 assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
605 b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'},
607 b9.add_action('_altsrc.b', 'altaction')
608 tgt = b9(env, target=None, source='foo_altsrc.b')
609 assert str(tgt[0]) == 'foo.c', str(tgt[0])
611 def test_src_suffix_expansion(self):
612 """Test handling source suffixes when an expansion is involved"""
613 env = Environment(OBJSUFFIX = '.obj')
615 b1 = SCons.Builder.Builder(action = '',
618 b2 = SCons.Builder.Builder(action = '',
622 tgt = b2(env, target=None, source=['foo$OBJSUFFIX'])
623 s = list(map(str, tgt[0].sources))
624 assert s == ['foo.obj'], s
626 def test_suffix(self):
627 """Test Builder creation with a specified target suffix
629 Make sure that the '.' separator is appended to the
630 beginning if it isn't already present.
633 builder = SCons.Builder.Builder(suffix = '.o')
634 assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
635 builder = SCons.Builder.Builder(suffix = 'o', action='')
636 assert builder.get_suffix(env) == '.o', builder.get_suffix(env)
637 tgt = builder(env, target = 'tgt3', source = 'src3')[0]
638 assert tgt.path == 'tgt3.o', \
639 "Target has unexpected name: %s" % tgt.path
640 tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
641 assert tgt.path == 'tgt4a tgt4b.o', \
642 "Target has unexpected name: %s" % tgt.path
643 tgt = builder(env, target = None, source = 'src5')[0]
644 assert tgt.path == 'src5.o', \
645 "Target has unexpected name: %s" % tgt.path
647 def gen_suffix(env, sources):
648 return "gen_suffix() says " + env['BAR']
649 my_env = Environment(BAR = 'hocus pocus')
650 builder = SCons.Builder.Builder(suffix = gen_suffix)
651 assert builder.get_suffix(my_env) == "gen_suffix() says hocus pocus", builder.get_suffix(my_env)
652 my_env['BAR'] = 'presto chango'
653 assert builder.get_suffix(my_env) == "gen_suffix() says presto chango"
655 def my_emit(env, sources):
656 return env.subst('$EMIT')
657 my_env = Environment(BAR = '.bar', EMIT = '.emit')
658 builder = SCons.Builder.Builder(suffix = {None : '.default',
664 tgt = builder(my_env, target = None, source = 'f1')[0]
665 assert tgt.path == 'f1.default', tgt.path
666 tgt = builder(my_env, target = None, source = 'f2.c')[0]
667 assert tgt.path == 'f2.default', tgt.path
668 tgt = builder(my_env, target = None, source = 'f3.in')[0]
669 assert tgt.path == 'f3.out', tgt.path
670 tgt = builder(my_env, target = None, source = 'f4.x')[0]
671 assert tgt.path == 'f4.y', tgt.path
672 tgt = builder(my_env, target = None, source = 'f5.bar')[0]
673 assert tgt.path == 'f5.new', tgt.path
674 tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
675 assert tgt.path == 'f6.emit', tgt.path
677 def test_single_source(self):
678 """Test Builder with single_source flag set"""
679 def func(target, source, env):
680 open(str(target[0]), "w")
681 if (len(source) == 1 and len(target) == 1):
682 env['CNT'][0] = env['CNT'][0] + 1
688 infiles.append(test.workpath('%d.in' % i))
689 outfiles.append(test.workpath('%d.out' % i))
690 test.write(infiles[-1], "\n")
691 builder = SCons.Builder.Builder(action=SCons.Action.Action(func,None),
692 single_source = 1, suffix='.out')
694 tgt = builder(env, target=outfiles[0], source=infiles[0])[0]
696 t = os.path.normcase(test.workpath('0.out'))
697 assert os.path.normcase(s) == t, s
700 assert env['CNT'][0] == 1, env['CNT'][0]
701 tgt = builder(env, outfiles[1], infiles[1])[0]
703 t = os.path.normcase(test.workpath('1.out'))
704 assert os.path.normcase(s) == t, s
707 assert env['CNT'][0] == 2
708 tgts = builder(env, None, infiles[2:4])
710 [].extend(collections.UserList())
712 # Old Python version (1.5.2) that can't handle extending
713 # a list with list-like objects. That means the return
714 # value from the builder call is a real list with Nodes,
715 # and doesn't have a __str__() method that stringifies
716 # the individual elements. Since we're gong to drop 1.5.2
717 # support anyway, don't bother trying to test for it.
720 s = list(map(str, tgts))
721 expect = [test.workpath('2.out'), test.workpath('3.out')]
722 expect = list(map(os.path.normcase, expect))
723 assert list(map(os.path.normcase, s)) == expect, s
724 for t in tgts: t.prepare()
727 assert env['CNT'][0] == 4
729 tgt = builder(env, outfiles[4], infiles[4:6])
730 except SCons.Errors.UserError:
735 # The builder may output more than one target per input file.
736 tgt = builder(env, outfiles[4:6], infiles[4:6])
737 except SCons.Errors.UserError:
743 def test_lists(self):
744 """Testing handling lists of targets and source"""
745 def function2(target, source, env, tlist = [outfile, outfile2], **kw):
747 open(str(t), 'w').write("function2\n")
749 if not t in list(map(str, target)):
750 open(t, 'w').write("function2\n")
754 builder = SCons.Builder.Builder(action = function2)
756 tgts = builder(env, source=[])
757 assert tgts == [], tgts
759 tgts = builder(env, target = [outfile, outfile2], source = infile)
764 except SCons.Errors.BuildError:
766 c = test.read(outfile, 'r')
767 assert c == "function2\n", c
768 c = test.read(outfile2, 'r')
769 assert c == "function2\n", c
771 sub1_out = test.workpath('sub1', 'out')
772 sub2_out = test.workpath('sub2', 'out')
774 def function3(target, source, env, tlist = [sub1_out, sub2_out]):
776 open(str(t), 'w').write("function3\n")
778 if not t in list(map(str, target)):
779 open(t, 'w').write("function3\n")
782 builder = SCons.Builder.Builder(action = function3)
783 tgts = builder(env, target = [sub1_out, sub2_out], source = infile)
788 except SCons.Errors.BuildError:
790 c = test.read(sub1_out, 'r')
791 assert c == "function3\n", c
792 c = test.read(sub2_out, 'r')
793 assert c == "function3\n", c
794 assert os.path.exists(test.workpath('sub1'))
795 assert os.path.exists(test.workpath('sub2'))
797 def test_src_builder(self):
798 """Testing Builders with src_builder"""
799 # These used to be MultiStepBuilder objects until we
800 # eliminated it as a separate class
802 builder1 = SCons.Builder.Builder(action='foo',
805 builder2 = SCons.Builder.Builder(action=MyAction('act'),
806 src_builder = builder1,
809 tgt = builder2(env, source=[])
810 assert tgt == [], tgt
812 sources = ['test.bar', 'test2.foo', 'test3.txt', 'test4']
813 tgt = builder2(env, target='baz', source=sources)[0]
816 s = list(map(str, tgt.sources))
817 assert s == ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s
818 s = list(map(str, tgt.sources[0].sources))
819 assert s == ['test.bar'], s
821 tgt = builder2(env, None, 'aaa.bar')[0]
824 s = list(map(str, tgt.sources))
825 assert s == ['aaa.foo'], s
826 s = list(map(str, tgt.sources[0].sources))
827 assert s == ['aaa.bar'], s
829 builder3 = SCons.Builder.Builder(action='bld3')
830 assert not builder3.src_builder is builder1.src_builder
832 builder4 = SCons.Builder.Builder(action='bld4',
835 builder5 = SCons.Builder.Builder(action=MyAction('act'),
836 src_builder=builder4,
839 builder6 = SCons.Builder.Builder(action=MyAction('act'),
840 src_builder=builder5,
843 tgt = builder6(env, 'test', 'test.i')[0]
845 assert s == 'test.exe', s
846 s = list(map(str, tgt.sources))
847 assert s == ['test_wrap.obj'], s
848 s = list(map(str, tgt.sources[0].sources))
849 assert s == ['test_wrap.c'], s
850 s = list(map(str, tgt.sources[0].sources[0].sources))
851 assert s == ['test.i'], s
853 def test_target_scanner(self):
854 """Testing ability to set target and source scanners through a builder."""
858 tscan = TestScanner()
859 sscan = TestScanner()
861 builder = SCons.Builder.Builder(target_scanner=tscan,
862 source_scanner=sscan,
864 tgt = builder(env, target='foo2', source='bar')[0]
865 assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
866 assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner
868 builder1 = SCons.Builder.Builder(action='foo',
871 builder2 = SCons.Builder.Builder(action='foo',
872 src_builder = builder1,
873 target_scanner = tscan,
874 source_scanner = tscan)
875 tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
876 assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
877 assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner
879 def test_actual_scanner(self):
880 """Test usage of actual Scanner objects."""
887 scanner = SCons.Scanner.Base(func, name='fooscan')
889 b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
890 b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
891 b3 = SCons.Builder.Builder(action='bld')
896 def test_src_scanner(slf):
897 """Testing ability to set a source file scanner through a builder."""
900 return 'TestScannerkey'
901 def instance(self, env):
903 def select(self, node):
909 scanner = TestScanner()
910 builder = SCons.Builder.Builder(action='action')
912 # With no scanner specified, source_scanner and
913 # backup_source_scanner are None.
914 bar_y = MyNode('bar.y')
916 tgt = builder(env1, target='foo1.x', source='bar.y')[0]
918 assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
919 assert tgt.builder.source_scanner is None, tgt.builder.source_scanner
920 assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y)
921 assert not src.has_builder(), src.has_builder()
922 s = src.get_source_scanner(bar_y)
923 assert isinstance(s, SCons.Util.Null), repr(s)
925 # An Environment that has suffix-specified SCANNERS should
926 # provide a source scanner to the target.
927 class EnvTestScanner:
930 def instance(self, env):
932 name = 'EnvTestScanner'
935 def select(self, node):
937 def path(self, env, dir=None):
939 def __call__(self, node, env, path):
941 env3 = Environment(SCANNERS = [EnvTestScanner()])
942 env3.scanner = EnvTestScanner() # test env's version of SCANNERS
943 tgt = builder(env3, target='foo2.x', source='bar.y')[0]
945 assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
946 assert not tgt.builder.source_scanner, tgt.builder.source_scanner
947 assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
948 assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
949 assert not src.has_builder(), src.has_builder()
950 s = src.get_source_scanner(bar_y)
951 assert isinstance(s, SCons.Util.Null), repr(s)
953 # Can't simply specify the scanner as a builder argument; it's
954 # global to all invocations of this builder.
955 tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0]
957 assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
958 assert not tgt.builder.source_scanner, tgt.builder.source_scanner
959 assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
960 assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
961 assert not src.has_builder(), src.has_builder()
962 s = src.get_source_scanner(bar_y)
963 assert isinstance(s, SCons.Util.Null), s
965 # Now use a builder that actually has scanners and ensure that
966 # the target is set accordingly (using the specified scanner
967 # instead of the Environment's scanner)
968 builder = SCons.Builder.Builder(action='action',
969 source_scanner=scanner,
970 target_scanner=scanner)
971 tgt = builder(env3, target='foo4.x', source='bar.y')[0]
973 assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner
974 assert tgt.builder.source_scanner, tgt.builder.source_scanner
975 assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner
976 assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner)
977 assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
978 assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y)
979 assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y)
980 assert not src.has_builder(), src.has_builder()
981 s = src.get_source_scanner(bar_y)
982 assert isinstance(s, SCons.Util.Null), s
986 def test_Builder_API(self):
987 """Test Builder interface.
989 Some of this is tested elsewhere in this file, but this is a
990 quick collection of common operations on builders with various
991 forms of component specifications."""
993 builder = SCons.Builder.Builder()
994 env = Environment(BUILDERS={'Bld':builder})
996 r = builder.get_name(env)
998 r = builder.get_prefix(env)
1000 r = builder.get_suffix(env)
1002 r = builder.get_src_suffix(env)
1004 r = builder.src_suffixes(env)
1007 # src_suffix can be a single string or a list of strings
1008 # src_suffixes() caches its return value, so we use a new
1009 # Builder each time we do any of these tests
1011 bld = SCons.Builder.Builder()
1012 env = Environment(BUILDERS={'Bld':bld})
1014 bld.set_src_suffix('.foo')
1015 r = bld.get_src_suffix(env)
1016 assert r == '.foo', r
1017 r = bld.src_suffixes(env)
1018 assert r == ['.foo'], r
1020 bld = SCons.Builder.Builder()
1021 env = Environment(BUILDERS={'Bld':bld})
1023 bld.set_src_suffix(['.foo', '.bar'])
1024 r = bld.get_src_suffix(env)
1025 assert r == '.foo', r
1026 r = bld.src_suffixes(env)
1027 assert r == ['.foo', '.bar'], r
1029 bld = SCons.Builder.Builder()
1030 env = Environment(BUILDERS={'Bld':bld})
1032 bld.set_src_suffix(['.bar', '.foo'])
1033 r = bld.get_src_suffix(env)
1034 assert r == '.bar', r
1035 r = sorted(bld.src_suffixes(env))
1036 assert r == ['.bar', '.foo'], r
1038 # adjust_suffix normalizes the suffix, adding a `.' if needed
1040 r = builder.adjust_suffix('.foo')
1041 assert r == '.foo', r
1042 r = builder.adjust_suffix('_foo')
1043 assert r == '_foo', r
1044 r = builder.adjust_suffix('$foo')
1045 assert r == '$foo', r
1046 r = builder.adjust_suffix('foo')
1047 assert r == '.foo', r
1048 r = builder.adjust_suffix('f._$oo')
1049 assert r == '.f._$oo', r
1051 # prefix and suffix can be one of:
1052 # 1. a string (adjusted and env variables substituted),
1053 # 2. a function (passed (env,sources), returns suffix string)
1054 # 3. a dict of src_suffix:suffix settings, key==None is
1055 # default suffix (special case of #2, so adjust_suffix
1058 builder = SCons.Builder.Builder(prefix='lib', suffix='foo')
1060 env = Environment(BUILDERS={'Bld':builder})
1061 r = builder.get_name(env)
1062 assert r == 'Bld', r
1063 r = builder.get_prefix(env)
1064 assert r == 'lib', r
1065 r = builder.get_suffix(env)
1066 assert r == '.foo', r
1068 mkpref = lambda env,sources: 'Lib'
1069 mksuff = lambda env,sources: '.Foo'
1070 builder = SCons.Builder.Builder(prefix=mkpref, suffix=mksuff)
1072 env = Environment(BUILDERS={'Bld':builder})
1073 r = builder.get_name(env)
1074 assert r == 'Bld', r
1075 r = builder.get_prefix(env)
1076 assert r == 'Lib', r
1077 r = builder.get_suffix(env)
1078 assert r == '.Foo', r
1080 builder = SCons.Builder.Builder(prefix='$PREF', suffix='$SUFF')
1082 env = Environment(BUILDERS={'Bld':builder},PREF="LIB",SUFF=".FOO")
1083 r = builder.get_name(env)
1084 assert r == 'Bld', r
1085 r = builder.get_prefix(env)
1086 assert r == 'LIB', r
1087 r = builder.get_suffix(env)
1088 assert r == '.FOO', r
1090 builder = SCons.Builder.Builder(prefix={None:'A_',
1095 env = Environment(BUILDERS={'Bld':builder})
1096 r = builder.get_name(env)
1097 assert r == 'Bld', r
1098 r = builder.get_prefix(env)
1100 r = builder.get_suffix(env)
1102 r = builder.get_prefix(env, [MyNode('X.C')])
1104 r = builder.get_suffix(env, [MyNode('X.C')])
1107 builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
1108 env = Environment(BUILDERS={'Bld':builder})
1110 r = builder.get_name(env)
1111 assert r == 'Bld', r
1112 r = builder.get_prefix(env)
1114 r = builder.get_suffix(env)
1116 r = builder.get_src_suffix(env)
1118 r = builder.src_suffixes(env)
1121 # Builder actions can be a string, a list, or a dictionary
1122 # whose keys are the source suffix. The add_action()
1123 # specifies a new source suffix/action binding.
1125 builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
1126 env = Environment(BUILDERS={'Bld':builder})
1127 builder.add_action('.src_sfx1', 'FOO')
1129 r = builder.get_name(env)
1130 assert r == 'Bld', r
1131 r = builder.get_prefix(env)
1133 r = builder.get_suffix(env)
1135 r = builder.get_suffix(env, [MyNode('X.src_sfx1')])
1137 r = builder.get_src_suffix(env)
1138 assert r == '.src_sfx1', r
1139 r = builder.src_suffixes(env)
1140 assert r == ['.src_sfx1'], r
1142 builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
1143 env = Environment(BUILDERS={'Bld':builder})
1144 builder.add_action('.src_sfx1', 'FOO')
1145 builder.add_action('.src_sfx2', 'BAR')
1147 r = builder.get_name(env)
1148 assert r == 'Bld', r
1149 r = builder.get_prefix(env)
1151 r = builder.get_suffix(env)
1153 r = builder.get_src_suffix(env)
1154 assert r == '.src_sfx1', r
1155 r = sorted(builder.src_suffixes(env))
1156 assert r == ['.src_sfx1', '.src_sfx2'], r
1159 def test_Builder_Args(self):
1160 """Testing passing extra args to a builder."""
1161 def buildFunc(target, source, env, s=self):
1164 assert env['CC'] == 'mycc'
1166 env=Environment(CC='cc')
1168 builder = SCons.Builder.Builder(action=buildFunc)
1169 tgt = builder(env, target='foo', source='bar', foo=1, bar=2, CC='mycc')[0]
1171 assert self.foo == 1, self.foo
1172 assert self.bar == 2, self.bar
1174 def test_emitter(self):
1175 """Test emitter functions."""
1176 def emit(target, source, env):
1177 foo = env.get('foo', 0)
1178 bar = env.get('bar', 0)
1180 assert isinstance(t, MyNode)
1181 assert t.has_builder()
1183 assert isinstance(s, MyNode)
1185 target.append("bar%d"%foo)
1187 source.append("baz")
1188 return ( target, source )
1191 builder = SCons.Builder.Builder(action='foo',
1193 target_factory=MyNode,
1194 source_factory=MyNode)
1195 tgt = builder(env, target='foo2', source='bar')[0]
1196 assert str(tgt) == 'foo2', str(tgt)
1197 assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
1199 tgt = builder(env, target='foo3', source='bar', foo=1)
1200 assert len(tgt) == 2, len(tgt)
1201 assert 'foo3' in list(map(str, tgt)), list(map(str, tgt))
1202 assert 'bar1' in list(map(str, tgt)), list(map(str, tgt))
1204 tgt = builder(env, target='foo4', source='bar', bar=1)[0]
1205 assert str(tgt) == 'foo4', str(tgt)
1206 assert len(tgt.sources) == 2, len(tgt.sources)
1207 assert 'baz' in list(map(str, tgt.sources)), list(map(str, tgt.sources))
1208 assert 'bar' in list(map(str, tgt.sources)), list(map(str, tgt.sources))
1210 env2=Environment(FOO=emit)
1211 builder2=SCons.Builder.Builder(action='foo',
1213 target_factory=MyNode,
1214 source_factory=MyNode)
1216 builder2a=SCons.Builder.Builder(action='foo',
1218 target_factory=MyNode,
1219 source_factory=MyNode)
1221 assert builder2 == builder2a, repr(builder2.__dict__) + "\n" + repr(builder2a.__dict__)
1223 tgt = builder2(env2, target='foo5', source='bar')[0]
1224 assert str(tgt) == 'foo5', str(tgt)
1225 assert str(tgt.sources[0]) == 'bar', str(tgt.sources[0])
1227 tgt = builder2(env2, target='foo6', source='bar', foo=2)
1228 assert len(tgt) == 2, len(tgt)
1229 assert 'foo6' in list(map(str, tgt)), list(map(str, tgt))
1230 assert 'bar2' in list(map(str, tgt)), list(map(str, tgt))
1232 tgt = builder2(env2, target='foo7', source='bar', bar=1)[0]
1233 assert str(tgt) == 'foo7', str(tgt)
1234 assert len(tgt.sources) == 2, len(tgt.sources)
1235 assert 'baz' in list(map(str, tgt.sources)), list(map(str, tgt.sources))
1236 assert 'bar' in list(map(str, tgt.sources)), list(map(str, tgt.sources))
1238 def test_emitter_preserve_builder(self):
1239 """Test an emitter not overwriting a newly-set builder"""
1242 new_builder = SCons.Builder.Builder(action='new')
1243 node = MyNode('foo8')
1244 new_node = MyNode('foo8.new')
1246 def emit(target, source, env, nb=new_builder, nn=new_node):
1251 builder=SCons.Builder.Builder(action='foo',
1253 target_factory=MyNode,
1254 source_factory=MyNode)
1255 tgt = builder(env, target=node, source='bar')[0]
1256 assert tgt is new_node, tgt
1257 assert tgt.builder is builder, tgt.builder
1258 assert node.builder is new_builder, node.builder
1260 def test_emitter_suffix_map(self):
1261 """Test mapping file suffixes to emitter functions"""
1264 def emit4a(target, source, env):
1265 source = list(map(str, source))
1266 target = ['emit4a-' + x[:-3] for x in source]
1267 return (target, source)
1268 def emit4b(target, source, env):
1269 source = list(map(str, source))
1270 target = ['emit4b-' + x[:-3] for x in source]
1271 return (target, source)
1273 builder = SCons.Builder.Builder(action='foo',
1274 emitter={'.4a':emit4a,
1276 target_factory=MyNode,
1277 source_factory=MyNode)
1278 tgt = builder(env, None, source='aaa.4a')[0]
1279 assert str(tgt) == 'emit4a-aaa', str(tgt)
1280 tgt = builder(env, None, source='bbb.4b')[0]
1281 assert str(tgt) == 'emit4b-bbb', str(tgt)
1282 tgt = builder(env, None, source='ccc.4c')[0]
1283 assert str(tgt) == 'ccc', str(tgt)
1285 def emit4c(target, source, env):
1286 source = list(map(str, source))
1287 target = ['emit4c-' + x[:-3] for x in source]
1288 return (target, source)
1290 builder.add_emitter('.4c', emit4c)
1291 tgt = builder(env, None, source='ccc.4c')[0]
1292 assert str(tgt) == 'emit4c-ccc', str(tgt)
1294 def test_emitter_function_list(self):
1295 """Test lists of emitter functions"""
1298 def emit1a(target, source, env):
1299 source = list(map(str, source))
1300 target = target + ['emit1a-' + x[:-2] for x in source]
1301 return (target, source)
1302 def emit1b(target, source, env):
1303 source = list(map(str, source))
1304 target = target + ['emit1b-' + x[:-2] for x in source]
1305 return (target, source)
1306 builder1 = SCons.Builder.Builder(action='foo',
1307 emitter=[emit1a, emit1b],
1308 node_factory=MyNode)
1310 tgts = builder1(env, target='target-1', source='aaa.1')
1311 tgts = list(map(str, tgts))
1312 assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts
1314 # Test a list of emitter functions through the environment.
1315 def emit2a(target, source, env):
1316 source = list(map(str, source))
1317 target = target + ['emit2a-' + x[:-2] for x in source]
1318 return (target, source)
1319 def emit2b(target, source, env):
1320 source = list(map(str, source))
1321 target = target + ['emit2b-' + x[:-2] for x in source]
1322 return (target, source)
1323 builder2 = SCons.Builder.Builder(action='foo',
1324 emitter='$EMITTERLIST',
1325 node_factory=MyNode)
1327 env = Environment(EMITTERLIST = [emit2a, emit2b])
1329 tgts = builder2(env, target='target-2', source='aaa.2')
1330 tgts = list(map(str, tgts))
1331 assert tgts == ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts
1333 def test_emitter_TARGET_SOURCE(self):
1334 """Test use of $TARGET and $SOURCE in emitter results"""
1336 env = SCons.Environment.Environment()
1338 def emit(target, source, env):
1339 return (target + ['${SOURCE}.s1', '${TARGET}.t1'],
1340 source + ['${TARGET}.t2', '${SOURCE}.s2'])
1342 builder = SCons.Builder.Builder(action='foo',
1344 node_factory = MyNode)
1346 targets = builder(env, target = 'TTT', source ='SSS')
1347 sources = targets[0].sources
1348 targets = list(map(str, targets))
1349 sources = list(map(str, sources))
1350 assert targets == ['TTT', 'SSS.s1', 'TTT.t1'], targets
1351 assert sources == ['SSS', 'TTT.t2', 'SSS.s2'], targets
1353 def test_no_target(self):
1354 """Test deducing the target from the source."""
1357 b = SCons.Builder.Builder(action='foo', suffix='.o')
1359 tgt = b(env, None, 'aaa')[0]
1360 assert str(tgt) == 'aaa.o', str(tgt)
1361 assert len(tgt.sources) == 1, list(map(str, tgt.sources))
1362 assert str(tgt.sources[0]) == 'aaa', list(map(str, tgt.sources))
1364 tgt = b(env, None, 'bbb.c')[0]
1365 assert str(tgt) == 'bbb.o', str(tgt)
1366 assert len(tgt.sources) == 1, list(map(str, tgt.sources))
1367 assert str(tgt.sources[0]) == 'bbb.c', list(map(str, tgt.sources))
1369 tgt = b(env, None, 'ccc.x.c')[0]
1370 assert str(tgt) == 'ccc.x.o', str(tgt)
1371 assert len(tgt.sources) == 1, list(map(str, tgt.sources))
1372 assert str(tgt.sources[0]) == 'ccc.x.c', list(map(str, tgt.sources))
1374 tgt = b(env, None, ['d0.c', 'd1.c'])[0]
1375 assert str(tgt) == 'd0.o', str(tgt)
1376 assert len(tgt.sources) == 2, list(map(str, tgt.sources))
1377 assert str(tgt.sources[0]) == 'd0.c', list(map(str, tgt.sources))
1378 assert str(tgt.sources[1]) == 'd1.c', list(map(str, tgt.sources))
1380 tgt = b(env, target = None, source='eee')[0]
1381 assert str(tgt) == 'eee.o', str(tgt)
1382 assert len(tgt.sources) == 1, list(map(str, tgt.sources))
1383 assert str(tgt.sources[0]) == 'eee', list(map(str, tgt.sources))
1385 tgt = b(env, target = None, source='fff.c')[0]
1386 assert str(tgt) == 'fff.o', str(tgt)
1387 assert len(tgt.sources) == 1, list(map(str, tgt.sources))
1388 assert str(tgt.sources[0]) == 'fff.c', list(map(str, tgt.sources))
1390 tgt = b(env, target = None, source='ggg.x.c')[0]
1391 assert str(tgt) == 'ggg.x.o', str(tgt)
1392 assert len(tgt.sources) == 1, list(map(str, tgt.sources))
1393 assert str(tgt.sources[0]) == 'ggg.x.c', list(map(str, tgt.sources))
1395 tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0]
1396 assert str(tgt) == 'h0.o', str(tgt)
1397 assert len(tgt.sources) == 2, list(map(str, tgt.sources))
1398 assert str(tgt.sources[0]) == 'h0.c', list(map(str, tgt.sources))
1399 assert str(tgt.sources[1]) == 'h1.c', list(map(str, tgt.sources))
1401 w = b(env, target='i0.w', source=['i0.x'])[0]
1402 y = b(env, target='i1.y', source=['i1.z'])[0]
1403 tgt = b(env, None, source=[w, y])[0]
1404 assert str(tgt) == 'i0.o', str(tgt)
1405 assert len(tgt.sources) == 2, list(map(str, tgt.sources))
1406 assert str(tgt.sources[0]) == 'i0.w', list(map(str, tgt.sources))
1407 assert str(tgt.sources[1]) == 'i1.y', list(map(str, tgt.sources))
1409 def test_get_name(self):
1410 """Test getting name of builder.
1412 Each type of builder should return its environment-specific
1413 name when queried appropriately. """
1415 b1 = SCons.Builder.Builder(action='foo', suffix='.o')
1416 b2 = SCons.Builder.Builder(action='foo', suffix='.c')
1417 b3 = SCons.Builder.Builder(action='bar', src_suffix = '.foo',
1419 b4 = SCons.Builder.Builder(action={})
1420 b5 = SCons.Builder.Builder(action='foo', name='builder5')
1421 b6 = SCons.Builder.Builder(action='foo')
1422 assert isinstance(b4, SCons.Builder.CompositeBuilder)
1423 assert isinstance(b4.action, SCons.Action.CommandGeneratorAction)
1425 env = Environment(BUILDERS={'bldr1': b1,
1429 env2 = Environment(BUILDERS={'B1': b1,
1433 # With no name, get_name will return the class. Allow
1436 'SCons.Builder.BuilderBase',
1437 "<class 'SCons.Builder.BuilderBase'>",
1438 'SCons.Memoize.BuilderBase',
1439 "<class 'SCons.Memoize.BuilderBase'>",
1442 assert b1.get_name(env) == 'bldr1', b1.get_name(env)
1443 assert b2.get_name(env) == 'bldr2', b2.get_name(env)
1444 assert b3.get_name(env) == 'bldr3', b3.get_name(env)
1445 assert b4.get_name(env) == 'bldr4', b4.get_name(env)
1446 assert b5.get_name(env) == 'builder5', b5.get_name(env)
1447 assert b6.get_name(env) in b6_names, b6.get_name(env)
1449 assert b1.get_name(env2) == 'B1', b1.get_name(env2)
1450 assert b2.get_name(env2) == 'B2', b2.get_name(env2)
1451 assert b3.get_name(env2) == 'B3', b3.get_name(env2)
1452 assert b4.get_name(env2) == 'B4', b4.get_name(env2)
1453 assert b5.get_name(env2) == 'builder5', b5.get_name(env2)
1454 assert b6.get_name(env2) in b6_names, b6.get_name(env2)
1456 assert b5.get_name(None) == 'builder5', b5.get_name(None)
1457 assert b6.get_name(None) in b6_names, b6.get_name(None)
1459 # This test worked before adding batch builders, but we must now
1460 # be able to disambiguate a CompositeAction into a more specific
1461 # action based on file suffix at call time. Leave this commented
1462 # out (for now) in case this reflects a real-world use case that
1463 # we must accomodate and we want to resurrect this test.
1464 #tgt = b4(env, target = 'moo', source='cow')
1465 #assert tgt[0].builder.get_name(env) == 'bldr4'
1467 class CompositeBuilderTestCase(unittest.TestCase):
1470 def func_action(target, source, env):
1473 builder = SCons.Builder.Builder(action={ '.foo' : func_action,
1474 '.bar' : func_action})
1476 self.func_action = func_action
1477 self.builder = builder
1479 def test___init__(self):
1480 """Test CompositeBuilder creation"""
1482 builder = SCons.Builder.Builder(action={})
1484 tgt = builder(env, source=[])
1485 assert tgt == [], tgt
1487 assert isinstance(builder, SCons.Builder.CompositeBuilder)
1488 assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
1490 def test_target_action(self):
1491 """Test CompositeBuilder setting of target builder actions"""
1493 builder = self.builder
1495 tgt = builder(env, target='test1', source='test1.foo')[0]
1496 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
1497 assert tgt.builder.action is builder.action
1499 tgt = builder(env, target='test2', source='test1.bar')[0]
1500 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
1501 assert tgt.builder.action is builder.action
1503 def test_multiple_suffix_error(self):
1504 """Test the CompositeBuilder multiple-source-suffix error"""
1506 builder = self.builder
1510 builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
1511 except SCons.Errors.UserError, e:
1513 assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
1514 expect = "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo"
1515 assert str(e) == expect, e
1517 def test_source_ext_match(self):
1518 """Test the CompositeBuilder source_ext_match argument"""
1520 func_action = self.func_action
1521 builder = SCons.Builder.Builder(action={ '.foo' : func_action,
1522 '.bar' : func_action},
1523 source_ext_match = None)
1525 tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
1528 def test_suffix_variable(self):
1529 """Test CompositeBuilder defining action suffixes through a variable"""
1530 env = Environment(BAR_SUFFIX = '.BAR2', FOO_SUFFIX = '.FOO2')
1531 func_action = self.func_action
1532 builder = SCons.Builder.Builder(action={ '.foo' : func_action,
1533 '.bar' : func_action,
1534 '$BAR_SUFFIX' : func_action,
1535 '$FOO_SUFFIX' : func_action })
1537 tgt = builder(env, target='test4', source=['test4.BAR2'])[0]
1538 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
1542 except SCons.Errors.UserError, e:
1545 assert flag, "It should be possible to define actions in composite builders using variables."
1546 env['FOO_SUFFIX'] = '.BAR2'
1547 builder.add_action('$NEW_SUFFIX', func_action)
1550 builder(env, target='test5', source=['test5.BAR2'])[0]
1551 except SCons.Errors.UserError:
1553 assert flag, "UserError should be thrown when we call a builder with ambigous suffixes."
1555 def test_src_builder(self):
1556 """Test CompositeBuilder's use of a src_builder"""
1559 foo_bld = SCons.Builder.Builder(action = 'a-foo',
1560 src_suffix = '.ina',
1562 assert isinstance(foo_bld, SCons.Builder.BuilderBase)
1563 builder = SCons.Builder.Builder(action = { '.foo' : 'foo',
1565 src_builder = foo_bld)
1566 assert isinstance(builder, SCons.Builder.CompositeBuilder)
1567 assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
1569 tgt = builder(env, target='t1', source='t1a.ina t1b.ina')[0]
1570 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
1572 tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0]
1573 assert isinstance(tgt.builder, SCons.Builder.BuilderBase), tgt.builder.__dict__
1575 bar_bld = SCons.Builder.Builder(action = 'a-bar',
1576 src_suffix = '.inb',
1578 assert isinstance(bar_bld, SCons.Builder.BuilderBase)
1579 builder = SCons.Builder.Builder(action = { '.foo' : 'foo'},
1580 src_builder = [foo_bld, bar_bld])
1581 assert isinstance(builder, SCons.Builder.CompositeBuilder)
1582 assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
1584 builder.add_action('.bar', 'bar')
1586 tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0]
1587 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
1589 tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0]
1590 assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
1594 builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0]
1595 except SCons.Errors.UserError, e:
1597 assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
1598 expect = "While building `['t5']' from `test5b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
1599 assert str(e) == expect, e
1603 builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])[0]
1604 except SCons.Errors.UserError, e:
1606 assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
1607 expect = "While building `['t6']' from `test6b.foo': Cannot build multiple sources with different extensions: .bar, .foo"
1608 assert str(e) == expect, e
1612 builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])[0]
1613 except SCons.Errors.UserError, e:
1615 assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
1616 expect = "While building `['t4']' from `test4b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
1617 assert str(e) == expect, e
1621 builder(env, target='t7', source=[env.fs.File('test7')])[0]
1622 except SCons.Errors.UserError, e:
1624 assert flag, "UserError should be thrown when we call a builder with files of different suffixes."
1625 expect = "While building `['t7']': Cannot deduce file extension from source files: ['test7']"
1626 assert str(e) == expect, e
1630 builder(env, target='t8', source=['test8.unknown'])[0]
1631 except SCons.Errors.UserError, e:
1633 assert flag, "UserError should be thrown when we call a builder target with an unknown suffix."
1634 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']."
1635 assert str(e) == expect, e
1637 if __name__ == "__main__":
1638 suite = unittest.TestSuite()
1641 CompositeBuilderTestCase
1643 for tclass in tclasses:
1644 names = unittest.getTestCaseNames(tclass, 'test_')
1645 suite.addTests(list(map(tclass, names)))
1646 if not unittest.TextTestRunner().run(suite).wasSuccessful():
1651 # indent-tabs-mode:nil
1653 # vim: set expandtab tabstop=4 shiftwidth=4: