# MIT License # # Copyright 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. import SCons.compat # 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 from collections import UserList import io import os.path import re import sys import unittest import TestCmd import SCons.Action import SCons.Builder import SCons.Environment import SCons.Errors import SCons.Subst import SCons.Util sys.stdout = io.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 __contains__(self, key): return key in self.d def keys(self): return list(self.d.keys()) def get(self, key, value=None): return self.d.get(key, value) def Override(self, overrides): env = 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 list(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 __eq__(self, other): return self.scanner == other.scanner or 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 def get_suffix(self): return os.path.splitext(self.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 self.builder is not 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 builder is not None, builder builder = SCons.Builder.Builder(action="foo", OVERRIDE='x') x = builder.overrides['OVERRIDE'] assert x == 'x', x def test__bool__(self): """Test a builder raising an exception when __bool__ is called. """ # basic test: explicitly call it builder = SCons.Builder.Builder(action="foo") exc_caught = None try: builder.__bool__() except SCons.Errors.InternalError: exc_caught = 1 assert exc_caught, "did not catch expected InternalError exception" class Node: pass # the interesting test: checking the attribute this way # should call back to Builder.__bool__ - so we can tell # people not to check that way (for performance). # TODO: workaround #3860: with just a "pass" in the check body, # py3.10a optimizes out the whole thing, so add a "real" stmt n = Node() n.builder = builder exc_caught = None try: if n.builder: #pass _ = True 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([2]) try: l.extend(ul) except TypeError: def mystr(l): return str(list(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 = list(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 = list(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 = list(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 = list(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'] n20 = MyNode_without_target_from_source('n20') flag = 0 try: target = builder(env, None, source=n20) except SCons.Errors.UserError as 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 as e: pass else: raise Exception("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].get_internal_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_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 builder.source_factory is not 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 builder.target_factory is not 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.get_internal_path() == 'libtgt1', \ "Target has unexpected name: %s" % tgt.get_internal_path() tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0] assert tgt.get_internal_path() == 'libtgt2a tgt2b', \ "Target has unexpected name: %s" % tgt.get_internal_path() tgt = builder(env, target = None, source = 'src3')[0] assert tgt.get_internal_path() == 'libsrc3', \ "Target has unexpected name: %s" % tgt.get_internal_path() tgt = builder(env, target = None, source = 'lib/src4')[0] assert tgt.get_internal_path() == os.path.join('lib', 'libsrc4'), \ "Target has unexpected name: %s" % tgt.get_internal_path() tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0] assert tgt.get_internal_path() == os.path.join('lib', 'libtgt5'), \ "Target has unexpected name: %s" % tgt.get_internal_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.get_internal_path() == 'default-f1', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f2.c')[0] assert tgt.get_internal_path() == 'default-f2', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f3.in')[0] assert tgt.get_internal_path() == 'out-f3', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f4.x')[0] assert tgt.get_internal_path() == 'y-f4', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f5.foo')[0] assert tgt.get_internal_path() == 'foo-f5', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f6.zzz')[0] assert tgt.get_internal_path() == 'emit-f6', tgt.get_internal_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].get_internal_path() == 'src2.c', \ "Source has unexpected name: %s" % tgt.sources[0].get_internal_path() tgt = b1(env, target = 'tgt3', source = 'src3a src3b')[0] assert len(tgt.sources) == 1 assert tgt.sources[0].get_internal_path() == 'src3a src3b.c', \ "Unexpected tgt.sources[0] name: %s" % tgt.sources[0].get_internal_path() b2 = SCons.Builder.Builder(src_suffix = '.2', src_builder = b1) r = sorted(b2.src_suffixes(env)) assert r == ['.2', '.c'], r b3 = SCons.Builder.Builder(action = {'.3a' : '', '.3b' : ''}) s = sorted(b3.src_suffixes(env)) 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 = list(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.get_internal_path() == 'tgt3.o', \ "Target has unexpected name: %s" % tgt.get_internal_path() tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0] assert tgt.get_internal_path() == 'tgt4a tgt4b.o', \ "Target has unexpected name: %s" % tgt.get_internal_path() tgt = builder(env, target = None, source = 'src5')[0] assert tgt.get_internal_path() == 'src5.o', \ "Target has unexpected name: %s" % tgt.get_internal_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.get_internal_path() == 'f1.default', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f2.c')[0] assert tgt.get_internal_path() == 'f2.default', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f3.in')[0] assert tgt.get_internal_path() == 'f3.out', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f4.x')[0] assert tgt.get_internal_path() == 'f4.y', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f5.bar')[0] assert tgt.get_internal_path() == 'f5.new', tgt.get_internal_path() tgt = builder(my_env, target = None, source = 'f6.zzz')[0] assert tgt.get_internal_path() == 'f6.emit', tgt.get_internal_path() def test_single_source(self): """Test Builder with single_source flag set""" def func(target, source, env): """create the file""" with open(str(target[0]), "w"): pass 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) t = os.path.normcase(test.workpath('0.out')) assert os.path.normcase(s) == t, s tgt.prepare() tgt.build() assert env['CNT'][0] == 1, env['CNT'][0] tgt = builder(env, outfiles[1], infiles[1])[0] s = str(tgt) t = os.path.normcase(test.workpath('1.out')) assert os.path.normcase(s) == t, s tgt.prepare() tgt.build() assert env['CNT'][0] == 2 tgts = builder(env, None, infiles[2:4]) s = list(map(str, tgts)) expect = [test.workpath('2.out'), test.workpath('3.out')] expect = list(map(os.path.normcase, expect)) assert list(map(os.path.normcase, 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: with open(str(t), 'w') as f: f.write("function2\n") for t in tlist: if t not in list(map(str, target)): with open(t, 'w') as f: f.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: with open(str(t), 'w') as f: f.write("function3\n") for t in tlist: if t not in list(map(str, target)): with open(t, 'w') as f: f.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 = list(map(str, tgt.sources)) assert s == ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s s = list(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 = list(map(str, tgt.sources)) assert s == ['aaa.foo'], s s = list(map(str, tgt.sources[0].sources)) assert s == ['aaa.bar'], s builder3 = SCons.Builder.Builder(action='bld3') assert builder3.src_builder is not 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 = list(map(str, tgt.sources)) assert s == ['test_wrap.obj'], s s = list(map(str, tgt.sources[0].sources)) assert s == ['test_wrap.c'], s s = list(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.ScannerBase(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(self): """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 = sorted(bld.src_suffixes(env)) 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 = sorted(builder.src_suffixes(env)) 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 list(map(str, tgt)), list(map(str, tgt)) assert 'bar1' in list(map(str, tgt)), list(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 list(map(str, tgt.sources)), list(map(str, tgt.sources)) assert 'bar' in list(map(str, tgt.sources)), list(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 list(map(str, tgt)), list(map(str, tgt)) assert 'bar2' in list(map(str, tgt)), list(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 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', "", '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 as e: flag = 1 err = e 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(err) == expect, err 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 as 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 as e: flag = 1 err = e 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(err) == expect, err flag = 0 try: builder(env, target='t6', source=['test6a.bar', 'test6b.ina'])[0] except SCons.Errors.UserError as e: flag = 1 err = e 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(err) == expect, err flag = 0 try: builder(env, target='t4', source=['test4a.ina', 'test4b.inb'])[0] except SCons.Errors.UserError as e: flag = 1 err = e 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(err) == expect, err flag = 0 try: builder(env, target='t7', source=[env.fs.File('test7')])[0] except SCons.Errors.UserError as e: flag = 1 err = e 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(err) == expect, err flag = 0 try: builder(env, target='t8', source=['test8.unknown'])[0] except SCons.Errors.UserError as e: flag = 1 err = e 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(err) == expect, err if __name__ == "__main__": unittest.main() # Local Variables: # tab-width:4 # indent-tabs-mode:nil # End: # vim: set expandtab tabstop=4 shiftwidth=4: