pandemonium_engine/SCSCons/BuilderTests.py

1630 lines
64 KiB
Python

# 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',
"<class 'SCons.Builder.BuilderBase'>",
'SCons.Memoize.BuilderBase',
"<class 'SCons.Memoize.BuilderBase'>",
]
assert b1.get_name(env) == 'bldr1', b1.get_name(env)
assert b2.get_name(env) == 'bldr2', b2.get_name(env)
assert b3.get_name(env) == 'bldr3', b3.get_name(env)
assert b4.get_name(env) == 'bldr4', b4.get_name(env)
assert b5.get_name(env) == 'builder5', b5.get_name(env)
assert b6.get_name(env) in b6_names, b6.get_name(env)
assert b1.get_name(env2) == 'B1', b1.get_name(env2)
assert b2.get_name(env2) == 'B2', b2.get_name(env2)
assert b3.get_name(env2) == 'B3', b3.get_name(env2)
assert b4.get_name(env2) == 'B4', b4.get_name(env2)
assert b5.get_name(env2) == 'builder5', b5.get_name(env2)
assert b6.get_name(env2) in b6_names, b6.get_name(env2)
assert b5.get_name(None) == 'builder5', b5.get_name(None)
assert b6.get_name(None) in b6_names, b6.get_name(None)
# This test worked before adding batch builders, but we must now
# be able to disambiguate a CompositeAction into a more specific
# action based on file suffix at call time. Leave this commented
# out (for now) in case this reflects a real-world use case that
# we must accomodate and we want to resurrect this test.
#tgt = b4(env, target = 'moo', source='cow')
#assert tgt[0].builder.get_name(env) == 'bldr4'
class CompositeBuilderTestCase(unittest.TestCase):
def setUp(self):
def func_action(target, source, env):
return 0
builder = SCons.Builder.Builder(action={ '.foo' : func_action,
'.bar' : func_action})
self.func_action = func_action
self.builder = builder
def test___init__(self):
"""Test CompositeBuilder creation"""
env = Environment()
builder = SCons.Builder.Builder(action={})
tgt = builder(env, source=[])
assert tgt == [], tgt
assert isinstance(builder, SCons.Builder.CompositeBuilder)
assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
def test_target_action(self):
"""Test CompositeBuilder setting of target builder actions"""
env = Environment()
builder = self.builder
tgt = builder(env, target='test1', source='test1.foo')[0]
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
assert tgt.builder.action is builder.action
tgt = builder(env, target='test2', source='test1.bar')[0]
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
assert tgt.builder.action is builder.action
def test_multiple_suffix_error(self):
"""Test the CompositeBuilder multiple-source-suffix error"""
env = Environment()
builder = self.builder
flag = 0
try:
builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
except SCons.Errors.UserError 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: