3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 # Define a null function for use as a builder action.
27 # Where this is defined in the file seems to affect its
28 # byte-code contents, so try to minimize changes by
29 # defining it here, before we even import anything.
33 from collections
import UserList
44 import SCons
.Environment
49 sys
.stdout
= io
.StringIO()
51 # Initial setup of the common environment for all tests,
52 # a temporary working directory containing a
53 # script for writing arguments to an output file.
55 # We don't do this as a setUp() method because it's
56 # unnecessary to create a separate directory and script
57 # for each test, they can just use the one.
58 test
= TestCmd
.TestCmd(workdir
= '')
60 outfile
= test
.workpath('outfile')
61 outfile2
= test
.workpath('outfile2')
63 infile
= test
.workpath('infile')
64 test
.write(infile
, "infile\n")
68 scons_env
= SCons
.Environment
.Environment()
70 env_arg2nodes_called
= None
73 def __init__(self
, **kw
) -> None:
75 self
.d
['SHELL'] = scons_env
['SHELL']
76 self
.d
['SPAWN'] = scons_env
['SPAWN']
77 self
.d
['ESCAPE'] = scons_env
['ESCAPE']
78 for k
, v
in kw
.items():
80 global env_arg2nodes_called
81 env_arg2nodes_called
= None
83 self
.fs
= SCons
.Node
.FS
.FS()
85 if not SCons
.Util
.is_String(s
):
87 def substitute(m
, d
=self
.d
):
88 return d
.get(m
.group(1), '')
89 return re
.sub(r
'\$(\w+)', substitute
, s
)
90 def subst_target_source(self
, string
, raw
: int=0, target
=None,
91 source
=None, dict=None, conv
=None):
92 return SCons
.Subst
.scons_subst(string
, self
, raw
, target
,
94 def subst_list(self
, string
, raw
: int=0, target
=None, source
=None, conv
=None):
95 return SCons
.Subst
.scons_subst_list(string
, self
, raw
, target
,
97 def arg2nodes(self
, args
, factory
, **kw
):
98 global env_arg2nodes_called
99 env_arg2nodes_called
= 1
100 if not SCons
.Util
.is_List(args
):
104 if SCons
.Util
.is_String(a
):
105 a
= factory(self
.subst(a
))
108 def get_factory(self
, factory
):
109 return factory
or self
.fs
.File
110 def get_scanner(self
, ext
):
112 def Dictionary(self
):
114 def autogenerate(self
, dir: str=''):
116 def __setitem__(self
, item
, var
) -> None:
118 def __getitem__(self
, item
):
120 def __contains__(self
, key
) -> bool:
123 return list(self
.d
.keys())
124 def get(self
, key
, value
=None):
125 return self
.d
.get(key
, value
)
126 def Override(self
, overrides
):
127 env
= Environment(**self
.d
)
128 env
.d
.update(overrides
)
129 env
.scanner
= self
.scanner
131 def _update(self
, dict) -> None:
134 return list(self
.d
.items())
137 for k
,v
in self
.items(): d
[k
] = v
138 d
['TARGETS'] = ['__t1__', '__t2__', '__t3__', '__t4__', '__t5__', '__t6__']
139 d
['TARGET'] = d
['TARGETS'][0]
140 d
['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
141 d
['SOURCE'] = d
['SOURCES'][0]
143 def __eq__(self
, other
):
144 return self
.scanner
== other
.scanner
or self
.d
== other
.d
147 def __init__(self
, action
) -> None:
149 def __call__(self
, *args
, **kw
) -> None:
151 def get_executor(self
, env
, overrides
, tlist
, slist
, executor_kw
):
152 return ['executor'] + [self
.action
]
154 class MyNode_without_target_from_source
:
155 def __init__(self
, name
) -> None:
159 self
.is_explicit
= None
161 def get_suffix(self
):
162 return os
.path
.splitext(self
.name
)[1]
163 def disambiguate(self
):
165 def __str__(self
) -> str:
167 def builder_set(self
, builder
) -> None:
168 self
.builder
= builder
169 def has_builder(self
) -> bool:
170 return self
.builder
is not None
171 def set_explicit(self
, is_explicit
) -> None:
172 self
.is_explicit
= is_explicit
173 def has_explicit_builder(self
) -> bool:
174 return self
.is_explicit
175 def env_set(self
, env
, safe
: bool=False) -> None:
177 def add_source(self
, source
) -> None:
178 self
.sources
.extend(source
)
179 def scanner_key(self
):
181 def is_derived(self
) -> bool:
182 return self
.has_builder()
183 def generate_build_env(self
, env
):
185 def get_build_env(self
):
186 return self
.executor
.get_build_env()
187 def set_executor(self
, executor
) -> None:
188 self
.executor
= executor
189 def get_executor(self
, create
: int=1):
192 class MyNode(MyNode_without_target_from_source
):
193 def target_from_source(self
, prefix
, suffix
, stripext
):
194 return MyNode(prefix
+ stripext(str(self
))[0] + suffix
)
196 class BuilderTestCase(unittest
.TestCase
):
198 def test__init__(self
) -> None:
199 """Test simple Builder creation
201 builder
= SCons
.Builder
.Builder(action
="foo")
202 assert builder
is not None, builder
203 builder
= SCons
.Builder
.Builder(action
="foo", OVERRIDE
='x')
204 x
= builder
.overrides
['OVERRIDE']
207 def test__bool__(self
) -> None:
208 """Test a builder raising an exception when __bool__ is called. """
210 # basic test: explicitly call it
211 builder
= SCons
.Builder
.Builder(action
="foo")
215 except SCons
.Errors
.InternalError
:
217 assert exc_caught
, "did not catch expected InternalError exception"
222 # the interesting test: checking the attribute this way
223 # should call back to Builder.__bool__ - so we can tell
224 # people not to check that way (for performance).
225 # TODO: workaround #3860: with just a "pass" in the check body,
226 # py3.10a optimizes out the whole thing, so add a "real" stmt
234 except SCons
.Errors
.InternalError
:
236 assert exc_caught
, "did not catch expected InternalError exception"
238 def test__call__(self
):
239 """Test calling a builder to establish source dependencies
242 builder
= SCons
.Builder
.Builder(action
="foo",
243 target_factory
=MyNode
,
244 source_factory
=MyNode
)
246 tgt
= builder(env
, source
=[])
247 assert tgt
== [], tgt
251 builder(env
, target
= n1
, source
= n2
)
252 assert env_arg2nodes_called
253 assert n1
.env
== env
, n1
.env
254 assert n1
.builder
== builder
, n1
.builder
255 assert n1
.sources
== [n2
], n1
.sources
256 assert n1
.executor
, "no executor found"
257 assert not hasattr(n2
, 'env')
265 return str(list(map(str, l
)))
269 nnn1
= MyNode("nnn1")
270 nnn2
= MyNode("nnn2")
271 tlist
= builder(env
, target
= [nnn1
, nnn2
], source
= [])
273 assert s
== "['nnn1', 'nnn2']", s
274 l
= list(map(str, tlist
))
275 assert l
== ['nnn1', 'nnn2'], l
277 tlist
= builder(env
, target
= 'n3', source
= 'n4')
279 assert s
== "['n3']", s
281 l
= list(map(str, tlist
))
282 assert l
== ['n3'], l
283 assert target
.name
== 'n3'
284 assert target
.sources
[0].name
== 'n4'
286 tlist
= builder(env
, target
= 'n4 n5', source
= ['n6 n7'])
288 assert s
== "['n4 n5']", s
289 l
= list(map(str, tlist
))
290 assert l
== ['n4 n5'], l
292 assert target
.name
== 'n4 n5'
293 assert target
.sources
[0].name
== 'n6 n7'
295 tlist
= builder(env
, target
= ['n8 n9'], source
= 'n10 n11')
297 assert s
== "['n8 n9']", s
298 l
= list(map(str, tlist
))
299 assert l
== ['n8 n9'], l
301 assert target
.name
== 'n8 n9'
302 assert target
.sources
[0].name
== 'n10 n11'
304 # A test to be uncommented when we freeze the environment
305 # as part of calling the builder.
306 #env1 = Environment(VAR='foo')
307 #target = builder(env1, target = 'n12', source = 'n13')
309 #be = target.get_build_env()
310 #assert be['VAR'] == 'foo', be['VAR']
312 n20
= MyNode_without_target_from_source('n20')
315 target
= builder(env
, None, source
=n20
)
316 except SCons
.Errors
.UserError
as e
:
318 assert flag
, "UserError should be thrown if a source node can't create a target."
320 builder
= SCons
.Builder
.Builder(action
="foo",
321 target_factory
=MyNode
,
322 source_factory
=MyNode
,
325 target
= builder(env
, None, source
='n21')[0]
326 assert target
.name
== 'p-n21.s', target
328 builder
= SCons
.Builder
.Builder(misspelled_action
="foo",
331 builder(env
, target
= 'n22', source
= 'n22')
332 except SCons
.Errors
.UserError
as e
:
335 raise Exception("Did not catch expected UserError.")
337 builder
= SCons
.Builder
.Builder(action
="foo")
338 target
= builder(env
, None, source
='n22', srcdir
='src_dir')[0]
339 p
= target
.sources
[0].get_internal_path()
340 assert p
== os
.path
.join('src_dir', 'n22'), p
342 def test_mistaken_variables(self
) -> None:
343 """Test keyword arguments that are often mistakes
345 import SCons
.Warnings
347 builder
= SCons
.Builder
.Builder(action
="foo")
349 save_warn
= SCons
.Warnings
.warn
351 def my_warn(exception
, warning
, warned
=warned
) -> None:
352 warned
.append(warning
)
353 SCons
.Warnings
.warn
= my_warn
356 target
= builder(env
, 'mistaken1', sources
='mistaken1.c')
357 assert warned
== ["Did you mean to use `source' instead of `sources'?"], warned
360 target
= builder(env
, 'mistaken2', targets
='mistaken2.c')
361 assert warned
== ["Did you mean to use `target' instead of `targets'?"], warned
364 target
= builder(env
, 'mistaken3', targets
='mistaken3', sources
='mistaken3.c')
365 assert "Did you mean to use `source' instead of `sources'?" in warned
, warned
366 assert "Did you mean to use `target' instead of `targets'?" in warned
, warned
369 SCons
.Warnings
.warn
= save_warn
371 def test_action(self
) -> None:
372 """Test Builder creation
374 Verify that we can retrieve the supplied action attribute.
376 builder
= SCons
.Builder
.Builder(action
="foo")
377 assert builder
.action
.cmd_list
== "foo"
381 builder
= SCons
.Builder
.Builder(action
=func
)
382 assert isinstance(builder
.action
, SCons
.Action
.FunctionAction
)
383 # Preserve the following so that the baseline test will fail.
384 # Remove it in favor of the previous test at some convenient
385 # point in the future.
386 assert builder
.action
.execfunction
== func
388 def test_generator(self
) -> None:
389 """Test Builder creation given a generator function."""
391 def generator() -> None:
394 builder
= SCons
.Builder
.Builder(generator
=generator
)
395 assert builder
.action
.generator
== generator
397 def test_cmp(self
) -> None:
398 """Test simple comparisons of Builder objects
400 b1
= SCons
.Builder
.Builder(src_suffix
= '.o')
401 b2
= SCons
.Builder
.Builder(src_suffix
= '.o')
403 b3
= SCons
.Builder
.Builder(src_suffix
= '.x')
407 def test_target_factory(self
) -> None:
408 """Test a Builder that creates target nodes of a specified class
412 def FooFactory(target
):
415 builder
= SCons
.Builder
.Builder(target_factory
= FooFactory
)
416 assert builder
.target_factory
is FooFactory
417 assert builder
.source_factory
is not FooFactory
419 def test_source_factory(self
) -> None:
420 """Test a Builder that creates source nodes of a specified class
424 def FooFactory(source
):
427 builder
= SCons
.Builder
.Builder(source_factory
= FooFactory
)
428 assert builder
.target_factory
is not FooFactory
429 assert builder
.source_factory
is FooFactory
431 def test_splitext(self
) -> None:
432 """Test the splitext() method attached to a Builder."""
433 b
= SCons
.Builder
.Builder()
434 assert b
.splitext('foo') == ('foo','')
435 assert b
.splitext('foo.bar') == ('foo','.bar')
436 assert b
.splitext(os
.path
.join('foo.bar', 'blat')) == (os
.path
.join('foo.bar', 'blat'),'')
438 class MyBuilder(SCons
.Builder
.BuilderBase
):
439 def splitext(self
, path
) -> str:
440 return "called splitext()"
443 ret
= b
.splitext('xyz.c')
444 assert ret
== "called splitext()", ret
446 def test_adjust_suffix(self
) -> None:
447 """Test how a Builder adjusts file suffixes
449 b
= SCons
.Builder
.Builder()
450 assert b
.adjust_suffix('.foo') == '.foo'
451 assert b
.adjust_suffix('foo') == '.foo'
452 assert b
.adjust_suffix('$foo') == '$foo'
454 class MyBuilder(SCons
.Builder
.BuilderBase
):
455 def adjust_suffix(self
, suff
) -> str:
456 return "called adjust_suffix()"
459 ret
= b
.adjust_suffix('.foo')
460 assert ret
== "called adjust_suffix()", ret
462 def test_prefix(self
) -> None:
463 """Test Builder creation with a specified target prefix
465 Make sure that there is no '.' separator appended.
468 builder
= SCons
.Builder
.Builder(prefix
= 'lib.')
469 assert builder
.get_prefix(env
) == 'lib.'
470 builder
= SCons
.Builder
.Builder(prefix
= 'lib', action
='')
471 assert builder
.get_prefix(env
) == 'lib'
472 tgt
= builder(env
, target
= 'tgt1', source
= 'src1')[0]
473 assert tgt
.get_internal_path() == 'libtgt1', \
474 "Target has unexpected name: %s" % tgt
.get_internal_path()
475 tgt
= builder(env
, target
= 'tgt2a tgt2b', source
= 'src2')[0]
476 assert tgt
.get_internal_path() == 'libtgt2a tgt2b', \
477 "Target has unexpected name: %s" % tgt
.get_internal_path()
478 tgt
= builder(env
, target
= None, source
= 'src3')[0]
479 assert tgt
.get_internal_path() == 'libsrc3', \
480 "Target has unexpected name: %s" % tgt
.get_internal_path()
481 tgt
= builder(env
, target
= None, source
= 'lib/src4')[0]
482 assert tgt
.get_internal_path() == os
.path
.join('lib', 'libsrc4'), \
483 "Target has unexpected name: %s" % tgt
.get_internal_path()
484 tgt
= builder(env
, target
= 'lib/tgt5', source
= 'lib/src5')[0]
485 assert tgt
.get_internal_path() == os
.path
.join('lib', 'libtgt5'), \
486 "Target has unexpected name: %s" % tgt
.get_internal_path()
488 def gen_prefix(env
, sources
):
489 return "gen_prefix() says " + env
['FOO']
490 my_env
= Environment(FOO
= 'xyzzy')
491 builder
= SCons
.Builder
.Builder(prefix
= gen_prefix
)
492 assert builder
.get_prefix(my_env
) == "gen_prefix() says xyzzy"
493 my_env
['FOO'] = 'abracadabra'
494 assert builder
.get_prefix(my_env
) == "gen_prefix() says abracadabra"
496 def my_emit(env
, sources
):
497 return env
.subst('$EMIT')
498 my_env
= Environment(FOO
= '.foo', EMIT
= 'emit-')
499 builder
= SCons
.Builder
.Builder(prefix
= {None : 'default-',
505 tgt
= builder(my_env
, target
= None, source
= 'f1')[0]
506 assert tgt
.get_internal_path() == 'default-f1', tgt
.get_internal_path()
507 tgt
= builder(my_env
, target
= None, source
= 'f2.c')[0]
508 assert tgt
.get_internal_path() == 'default-f2', tgt
.get_internal_path()
509 tgt
= builder(my_env
, target
= None, source
= 'f3.in')[0]
510 assert tgt
.get_internal_path() == 'out-f3', tgt
.get_internal_path()
511 tgt
= builder(my_env
, target
= None, source
= 'f4.x')[0]
512 assert tgt
.get_internal_path() == 'y-f4', tgt
.get_internal_path()
513 tgt
= builder(my_env
, target
= None, source
= 'f5.foo')[0]
514 assert tgt
.get_internal_path() == 'foo-f5', tgt
.get_internal_path()
515 tgt
= builder(my_env
, target
= None, source
= 'f6.zzz')[0]
516 assert tgt
.get_internal_path() == 'emit-f6', tgt
.get_internal_path()
518 def test_set_suffix(self
) -> None:
519 """Test the set_suffix() method"""
520 b
= SCons
.Builder
.Builder(action
='')
521 env
= Environment(XSUFFIX
= '.x')
523 s
= b
.get_suffix(env
)
527 s
= b
.get_suffix(env
)
528 assert s
== '.foo', s
530 b
.set_suffix('$XSUFFIX')
531 s
= b
.get_suffix(env
)
534 def test_src_suffix(self
) -> None:
535 """Test Builder creation with a specified source file suffix
537 Make sure that the '.' separator is appended to the
538 beginning if it isn't already present.
540 env
= Environment(XSUFFIX
= '.x', YSUFFIX
= '.y')
542 b1
= SCons
.Builder
.Builder(src_suffix
= '.c', action
='')
543 assert b1
.src_suffixes(env
) == ['.c'], b1
.src_suffixes(env
)
545 tgt
= b1(env
, target
= 'tgt2', source
= 'src2')[0]
546 assert tgt
.sources
[0].get_internal_path() == 'src2.c', \
547 "Source has unexpected name: %s" % tgt
.sources
[0].get_internal_path()
549 tgt
= b1(env
, target
= 'tgt3', source
= 'src3a src3b')[0]
550 assert len(tgt
.sources
) == 1
551 assert tgt
.sources
[0].get_internal_path() == 'src3a src3b.c', \
552 "Unexpected tgt.sources[0] name: %s" % tgt
.sources
[0].get_internal_path()
554 b2
= SCons
.Builder
.Builder(src_suffix
= '.2', src_builder
= b1
)
555 r
= sorted(b2
.src_suffixes(env
))
556 assert r
== ['.2', '.c'], r
558 b3
= SCons
.Builder
.Builder(action
= {'.3a' : '', '.3b' : ''})
559 s
= sorted(b3
.src_suffixes(env
))
560 assert s
== ['.3a', '.3b'], s
562 b4
= SCons
.Builder
.Builder(src_suffix
= '$XSUFFIX')
563 assert b4
.src_suffixes(env
) == ['.x'], b4
.src_suffixes(env
)
565 b5
= SCons
.Builder
.Builder(action
= { '.y' : ''})
566 assert b5
.src_suffixes(env
) == ['.y'], b5
.src_suffixes(env
)
568 def test_srcsuffix_nonext(self
) -> None:
569 """Test target generation from non-extension source suffixes"""
571 b6
= SCons
.Builder
.Builder(action
= '',
574 tgt
= b6(env
, target
=None, source
='foo_src.a')
575 assert str(tgt
[0]) == 'foo.b', str(tgt
[0])
577 b7
= SCons
.Builder
.Builder(action
= '',
578 src_suffix
='_source.a',
580 b8
= SCons
.Builder
.Builder(action
= '',
583 tgt
= b8(env
, target
=None, source
='foo_source.a')
584 assert str(tgt
[0]) == 'foo_obj.c', str(tgt
[0])
585 src
= env
.fs
.File('foo_source.a')
586 tgt
= b8(env
, target
=None, source
=src
)
587 assert str(tgt
[0]) == 'foo_obj.c', str(tgt
[0])
589 b9
= SCons
.Builder
.Builder(action
={'_src.a' : 'srcaction'},
591 b9
.add_action('_altsrc.b', 'altaction')
592 tgt
= b9(env
, target
=None, source
='foo_altsrc.b')
593 assert str(tgt
[0]) == 'foo.c', str(tgt
[0])
595 def test_src_suffix_expansion(self
) -> None:
596 """Test handling source suffixes when an expansion is involved"""
597 env
= Environment(OBJSUFFIX
= '.obj')
599 b1
= SCons
.Builder
.Builder(action
= '',
602 b2
= SCons
.Builder
.Builder(action
= '',
606 tgt
= b2(env
, target
=None, source
=['foo$OBJSUFFIX'])
607 s
= list(map(str, tgt
[0].sources
))
608 assert s
== ['foo.obj'], s
610 def test_suffix(self
) -> None:
611 """Test Builder creation with a specified target suffix
613 Make sure that the '.' separator is appended to the
614 beginning if it isn't already present.
617 builder
= SCons
.Builder
.Builder(suffix
= '.o')
618 assert builder
.get_suffix(env
) == '.o', builder
.get_suffix(env
)
619 builder
= SCons
.Builder
.Builder(suffix
= 'o', action
='')
620 assert builder
.get_suffix(env
) == '.o', builder
.get_suffix(env
)
621 tgt
= builder(env
, target
= 'tgt3', source
= 'src3')[0]
622 assert tgt
.get_internal_path() == 'tgt3.o', \
623 "Target has unexpected name: %s" % tgt
.get_internal_path()
624 tgt
= builder(env
, target
= 'tgt4a tgt4b', source
= 'src4')[0]
625 assert tgt
.get_internal_path() == 'tgt4a tgt4b.o', \
626 "Target has unexpected name: %s" % tgt
.get_internal_path()
627 tgt
= builder(env
, target
= None, source
= 'src5')[0]
628 assert tgt
.get_internal_path() == 'src5.o', \
629 "Target has unexpected name: %s" % tgt
.get_internal_path()
631 def gen_suffix(env
, sources
):
632 return "gen_suffix() says " + env
['BAR']
633 my_env
= Environment(BAR
= 'hocus pocus')
634 builder
= SCons
.Builder
.Builder(suffix
= gen_suffix
)
635 assert builder
.get_suffix(my_env
) == "gen_suffix() says hocus pocus", builder
.get_suffix(my_env
)
636 my_env
['BAR'] = 'presto chango'
637 assert builder
.get_suffix(my_env
) == "gen_suffix() says presto chango"
639 def my_emit(env
, sources
):
640 return env
.subst('$EMIT')
641 my_env
= Environment(BAR
= '.bar', EMIT
= '.emit')
642 builder
= SCons
.Builder
.Builder(suffix
= {None : '.default',
648 tgt
= builder(my_env
, target
= None, source
= 'f1')[0]
649 assert tgt
.get_internal_path() == 'f1.default', tgt
.get_internal_path()
650 tgt
= builder(my_env
, target
= None, source
= 'f2.c')[0]
651 assert tgt
.get_internal_path() == 'f2.default', tgt
.get_internal_path()
652 tgt
= builder(my_env
, target
= None, source
= 'f3.in')[0]
653 assert tgt
.get_internal_path() == 'f3.out', tgt
.get_internal_path()
654 tgt
= builder(my_env
, target
= None, source
= 'f4.x')[0]
655 assert tgt
.get_internal_path() == 'f4.y', tgt
.get_internal_path()
656 tgt
= builder(my_env
, target
= None, source
= 'f5.bar')[0]
657 assert tgt
.get_internal_path() == 'f5.new', tgt
.get_internal_path()
658 tgt
= builder(my_env
, target
= None, source
= 'f6.zzz')[0]
659 assert tgt
.get_internal_path() == 'f6.emit', tgt
.get_internal_path()
661 def test_single_source(self
) -> None:
662 """Test Builder with single_source flag set"""
663 def func(target
, source
, env
) -> None:
664 """create the file"""
665 with
open(str(target
[0]), "w"):
667 if len(source
) == 1 and len(target
) == 1:
668 env
['CNT'][0] = env
['CNT'][0] + 1
674 infiles
.append(test
.workpath('%d.in' % i
))
675 outfiles
.append(test
.workpath('%d.out' % i
))
676 test
.write(infiles
[-1], "\n")
677 builder
= SCons
.Builder
.Builder(action
=SCons
.Action
.Action(func
,None),
678 single_source
= 1, suffix
='.out')
680 tgt
= builder(env
, target
=outfiles
[0], source
=infiles
[0])[0]
682 t
= os
.path
.normcase(test
.workpath('0.out'))
683 assert os
.path
.normcase(s
) == t
, s
686 assert env
['CNT'][0] == 1, env
['CNT'][0]
687 tgt
= builder(env
, outfiles
[1], infiles
[1])[0]
689 t
= os
.path
.normcase(test
.workpath('1.out'))
690 assert os
.path
.normcase(s
) == t
, s
693 assert env
['CNT'][0] == 2
694 tgts
= builder(env
, None, infiles
[2:4])
695 s
= list(map(str, tgts
))
696 expect
= [test
.workpath('2.out'), test
.workpath('3.out')]
697 expect
= list(map(os
.path
.normcase
, expect
))
698 assert list(map(os
.path
.normcase
, s
)) == expect
, s
699 for t
in tgts
: t
.prepare()
702 assert env
['CNT'][0] == 4
704 tgt
= builder(env
, outfiles
[4], infiles
[4:6])
705 except SCons
.Errors
.UserError
:
710 # The builder may output more than one target per input file.
711 tgt
= builder(env
, outfiles
[4:6], infiles
[4:6])
712 except SCons
.Errors
.UserError
:
718 def test_lists(self
) -> None:
719 """Testing handling lists of targets and source"""
720 def function2(target
, source
, env
, tlist
= [outfile
, outfile2
], **kw
) -> int:
722 with
open(str(t
), 'w') as f
:
723 f
.write("function2\n")
725 if t
not in list(map(str, target
)):
726 with
open(t
, 'w') as f
:
727 f
.write("function2\n")
731 builder
= SCons
.Builder
.Builder(action
= function2
)
733 tgts
= builder(env
, source
=[])
734 assert tgts
== [], tgts
736 tgts
= builder(env
, target
= [outfile
, outfile2
], source
= infile
)
741 except SCons
.Errors
.BuildError
:
743 c
= test
.read(outfile
, 'r')
744 assert c
== "function2\n", c
745 c
= test
.read(outfile2
, 'r')
746 assert c
== "function2\n", c
748 sub1_out
= test
.workpath('sub1', 'out')
749 sub2_out
= test
.workpath('sub2', 'out')
751 def function3(target
, source
, env
, tlist
= [sub1_out
, sub2_out
]) -> int:
753 with
open(str(t
), 'w') as f
:
754 f
.write("function3\n")
756 if t
not in list(map(str, target
)):
757 with
open(t
, 'w') as f
:
758 f
.write("function3\n")
761 builder
= SCons
.Builder
.Builder(action
= function3
)
762 tgts
= builder(env
, target
= [sub1_out
, sub2_out
], source
= infile
)
767 except SCons
.Errors
.BuildError
:
769 c
= test
.read(sub1_out
, 'r')
770 assert c
== "function3\n", c
771 c
= test
.read(sub2_out
, 'r')
772 assert c
== "function3\n", c
773 assert os
.path
.exists(test
.workpath('sub1'))
774 assert os
.path
.exists(test
.workpath('sub2'))
776 def test_src_builder(self
) -> None:
777 """Testing Builders with src_builder"""
778 # These used to be MultiStepBuilder objects until we
779 # eliminated it as a separate class
781 builder1
= SCons
.Builder
.Builder(action
='foo',
784 builder2
= SCons
.Builder
.Builder(action
=MyAction('act'),
785 src_builder
= builder1
,
788 tgt
= builder2(env
, source
=[])
789 assert tgt
== [], tgt
791 sources
= ['test.bar', 'test2.foo', 'test3.txt', 'test4']
792 tgt
= builder2(env
, target
='baz', source
=sources
)[0]
795 s
= list(map(str, tgt
.sources
))
796 assert s
== ['test.foo', 'test2.foo', 'test3.txt', 'test4.foo'], s
797 s
= list(map(str, tgt
.sources
[0].sources
))
798 assert s
== ['test.bar'], s
800 tgt
= builder2(env
, None, 'aaa.bar')[0]
803 s
= list(map(str, tgt
.sources
))
804 assert s
== ['aaa.foo'], s
805 s
= list(map(str, tgt
.sources
[0].sources
))
806 assert s
== ['aaa.bar'], s
808 builder3
= SCons
.Builder
.Builder(action
='bld3')
809 assert builder3
.src_builder
is not builder1
.src_builder
811 builder4
= SCons
.Builder
.Builder(action
='bld4',
814 builder5
= SCons
.Builder
.Builder(action
=MyAction('act'),
815 src_builder
=builder4
,
818 builder6
= SCons
.Builder
.Builder(action
=MyAction('act'),
819 src_builder
=builder5
,
822 tgt
= builder6(env
, 'test', 'test.i')[0]
824 assert s
== 'test.exe', s
825 s
= list(map(str, tgt
.sources
))
826 assert s
== ['test_wrap.obj'], s
827 s
= list(map(str, tgt
.sources
[0].sources
))
828 assert s
== ['test_wrap.c'], s
829 s
= list(map(str, tgt
.sources
[0].sources
[0].sources
))
830 assert s
== ['test.i'], s
832 def test_target_scanner(self
) -> None:
833 """Testing ability to set target and source scanners through a builder."""
837 tscan
= TestScanner()
838 sscan
= TestScanner()
840 builder
= SCons
.Builder
.Builder(target_scanner
=tscan
,
841 source_scanner
=sscan
,
843 tgt
= builder(env
, target
='foo2', source
='bar')[0]
844 assert tgt
.builder
.target_scanner
== tscan
, tgt
.builder
.target_scanner
845 assert tgt
.builder
.source_scanner
== sscan
, tgt
.builder
.source_scanner
847 builder1
= SCons
.Builder
.Builder(action
='foo',
850 builder2
= SCons
.Builder
.Builder(action
='foo',
851 src_builder
= builder1
,
852 target_scanner
= tscan
,
853 source_scanner
= tscan
)
854 tgt
= builder2(env
, target
='baz2', source
='test.bar test2.foo test3.txt')[0]
855 assert tgt
.builder
.target_scanner
== tscan
, tgt
.builder
.target_scanner
856 assert tgt
.builder
.source_scanner
== tscan
, tgt
.builder
.source_scanner
858 def test_actual_scanner(self
) -> None:
859 """Test usage of actual Scanner objects."""
863 def func(self
) -> None:
866 scanner
= SCons
.Scanner
.ScannerBase(func
, name
='fooscan')
868 b1
= SCons
.Builder
.Builder(action
='bld', target_scanner
=scanner
)
869 b2
= SCons
.Builder
.Builder(action
='bld', target_scanner
=scanner
)
870 b3
= SCons
.Builder
.Builder(action
='bld')
875 def test_src_scanner(self
) -> None:
876 """Testing ability to set a source file scanner through a builder."""
878 def key(self
, env
) -> str:
879 return 'TestScannerkey'
880 def instance(self
, env
):
882 def select(self
, node
):
885 def __str__(self
) -> str:
888 scanner
= TestScanner()
889 builder
= SCons
.Builder
.Builder(action
='action')
891 # With no scanner specified, source_scanner and
892 # backup_source_scanner are None.
893 bar_y
= MyNode('bar.y')
895 tgt
= builder(env1
, target
='foo1.x', source
='bar.y')[0]
897 assert tgt
.builder
.target_scanner
!= scanner
, tgt
.builder
.target_scanner
898 assert tgt
.builder
.source_scanner
is None, tgt
.builder
.source_scanner
899 assert tgt
.get_source_scanner(bar_y
) is None, tgt
.get_source_scanner(bar_y
)
900 assert not src
.has_builder(), src
.has_builder()
901 s
= src
.get_source_scanner(bar_y
)
902 assert isinstance(s
, SCons
.Util
.Null
), repr(s
)
904 # An Environment that has suffix-specified SCANNERS should
905 # provide a source scanner to the target.
906 class EnvTestScanner
:
907 def key(self
, env
) -> str:
909 def instance(self
, env
):
911 name
= 'EnvTestScanner'
912 def __str__(self
) -> str:
914 def select(self
, node
):
916 def path(self
, env
, dir=None):
918 def __call__(self
, node
, env
, path
):
920 env3
= Environment(SCANNERS
= [EnvTestScanner()])
921 env3
.scanner
= EnvTestScanner() # test env's version of SCANNERS
922 tgt
= builder(env3
, target
='foo2.x', source
='bar.y')[0]
924 assert tgt
.builder
.target_scanner
!= scanner
, tgt
.builder
.target_scanner
925 assert not tgt
.builder
.source_scanner
, tgt
.builder
.source_scanner
926 assert tgt
.get_source_scanner(bar_y
), tgt
.get_source_scanner(bar_y
)
927 assert str(tgt
.get_source_scanner(bar_y
)) == 'EnvTestScanner', tgt
.get_source_scanner(bar_y
)
928 assert not src
.has_builder(), src
.has_builder()
929 s
= src
.get_source_scanner(bar_y
)
930 assert isinstance(s
, SCons
.Util
.Null
), repr(s
)
932 # Can't simply specify the scanner as a builder argument; it's
933 # global to all invocations of this builder.
934 tgt
= builder(env3
, target
='foo3.x', source
='bar.y', source_scanner
= scanner
)[0]
936 assert tgt
.builder
.target_scanner
!= scanner
, tgt
.builder
.target_scanner
937 assert not tgt
.builder
.source_scanner
, tgt
.builder
.source_scanner
938 assert tgt
.get_source_scanner(bar_y
), tgt
.get_source_scanner(bar_y
)
939 assert str(tgt
.get_source_scanner(bar_y
)) == 'EnvTestScanner', tgt
.get_source_scanner(bar_y
)
940 assert not src
.has_builder(), src
.has_builder()
941 s
= src
.get_source_scanner(bar_y
)
942 assert isinstance(s
, SCons
.Util
.Null
), s
944 # Now use a builder that actually has scanners and ensure that
945 # the target is set accordingly (using the specified scanner
946 # instead of the Environment's scanner)
947 builder
= SCons
.Builder
.Builder(action
='action',
948 source_scanner
=scanner
,
949 target_scanner
=scanner
)
950 tgt
= builder(env3
, target
='foo4.x', source
='bar.y')[0]
952 assert tgt
.builder
.target_scanner
== scanner
, tgt
.builder
.target_scanner
953 assert tgt
.builder
.source_scanner
, tgt
.builder
.source_scanner
954 assert tgt
.builder
.source_scanner
== scanner
, tgt
.builder
.source_scanner
955 assert str(tgt
.builder
.source_scanner
) == 'TestScanner', str(tgt
.builder
.source_scanner
)
956 assert tgt
.get_source_scanner(bar_y
), tgt
.get_source_scanner(bar_y
)
957 assert tgt
.get_source_scanner(bar_y
) == scanner
, tgt
.get_source_scanner(bar_y
)
958 assert str(tgt
.get_source_scanner(bar_y
)) == 'TestScanner', tgt
.get_source_scanner(bar_y
)
959 assert not src
.has_builder(), src
.has_builder()
960 s
= src
.get_source_scanner(bar_y
)
961 assert isinstance(s
, SCons
.Util
.Null
), s
965 def test_Builder_API(self
) -> None:
966 """Test Builder interface.
968 Some of this is tested elsewhere in this file, but this is a
969 quick collection of common operations on builders with various
970 forms of component specifications."""
972 builder
= SCons
.Builder
.Builder()
973 env
= Environment(BUILDERS
={'Bld':builder
})
975 r
= builder
.get_name(env
)
977 r
= builder
.get_prefix(env
)
979 r
= builder
.get_suffix(env
)
981 r
= builder
.get_src_suffix(env
)
983 r
= builder
.src_suffixes(env
)
986 # src_suffix can be a single string or a list of strings
987 # src_suffixes() caches its return value, so we use a new
988 # Builder each time we do any of these tests
990 bld
= SCons
.Builder
.Builder()
991 env
= Environment(BUILDERS
={'Bld':bld
})
993 bld
.set_src_suffix('.foo')
994 r
= bld
.get_src_suffix(env
)
995 assert r
== '.foo', r
996 r
= bld
.src_suffixes(env
)
997 assert r
== ['.foo'], r
999 bld
= SCons
.Builder
.Builder()
1000 env
= Environment(BUILDERS
={'Bld':bld
})
1002 bld
.set_src_suffix(['.foo', '.bar'])
1003 r
= bld
.get_src_suffix(env
)
1004 assert r
== '.foo', r
1005 r
= bld
.src_suffixes(env
)
1006 assert r
== ['.foo', '.bar'], r
1008 bld
= SCons
.Builder
.Builder()
1009 env
= Environment(BUILDERS
={'Bld':bld
})
1011 bld
.set_src_suffix(['.bar', '.foo'])
1012 r
= bld
.get_src_suffix(env
)
1013 assert r
== '.bar', r
1014 r
= sorted(bld
.src_suffixes(env
))
1015 assert r
== ['.bar', '.foo'], r
1017 # adjust_suffix normalizes the suffix, adding a `.' if needed
1019 r
= builder
.adjust_suffix('.foo')
1020 assert r
== '.foo', r
1021 r
= builder
.adjust_suffix('_foo')
1022 assert r
== '_foo', r
1023 r
= builder
.adjust_suffix('$foo')
1024 assert r
== '$foo', r
1025 r
= builder
.adjust_suffix('foo')
1026 assert r
== '.foo', r
1027 r
= builder
.adjust_suffix('f._$oo')
1028 assert r
== '.f._$oo', r
1030 # prefix and suffix can be one of:
1031 # 1. a string (adjusted and env variables substituted),
1032 # 2. a function (passed (env,sources), returns suffix string)
1033 # 3. a dict of src_suffix:suffix settings, key==None is
1034 # default suffix (special case of #2, so adjust_suffix
1037 builder
= SCons
.Builder
.Builder(prefix
='lib', suffix
='foo')
1039 env
= Environment(BUILDERS
={'Bld':builder
})
1040 r
= builder
.get_name(env
)
1041 assert r
== 'Bld', r
1042 r
= builder
.get_prefix(env
)
1043 assert r
== 'lib', r
1044 r
= builder
.get_suffix(env
)
1045 assert r
== '.foo', r
1047 mkpref
= lambda env
,sources
: 'Lib'
1048 mksuff
= lambda env
,sources
: '.Foo'
1049 builder
= SCons
.Builder
.Builder(prefix
=mkpref
, suffix
=mksuff
)
1051 env
= Environment(BUILDERS
={'Bld':builder
})
1052 r
= builder
.get_name(env
)
1053 assert r
== 'Bld', r
1054 r
= builder
.get_prefix(env
)
1055 assert r
== 'Lib', r
1056 r
= builder
.get_suffix(env
)
1057 assert r
== '.Foo', r
1059 builder
= SCons
.Builder
.Builder(prefix
='$PREF', suffix
='$SUFF')
1061 env
= Environment(BUILDERS
={'Bld':builder
},PREF
="LIB",SUFF
=".FOO")
1062 r
= builder
.get_name(env
)
1063 assert r
== 'Bld', r
1064 r
= builder
.get_prefix(env
)
1065 assert r
== 'LIB', r
1066 r
= builder
.get_suffix(env
)
1067 assert r
== '.FOO', r
1069 builder
= SCons
.Builder
.Builder(prefix
={None:'A_',
1074 env
= Environment(BUILDERS
={'Bld':builder
})
1075 r
= builder
.get_name(env
)
1076 assert r
== 'Bld', r
1077 r
= builder
.get_prefix(env
)
1079 r
= builder
.get_suffix(env
)
1081 r
= builder
.get_prefix(env
, [MyNode('X.C')])
1083 r
= builder
.get_suffix(env
, [MyNode('X.C')])
1086 builder
= SCons
.Builder
.Builder(prefix
='A_', suffix
={}, action
={})
1087 env
= Environment(BUILDERS
={'Bld':builder
})
1089 r
= builder
.get_name(env
)
1090 assert r
== 'Bld', r
1091 r
= builder
.get_prefix(env
)
1093 r
= builder
.get_suffix(env
)
1095 r
= builder
.get_src_suffix(env
)
1097 r
= builder
.src_suffixes(env
)
1100 # Builder actions can be a string, a list, or a dictionary
1101 # whose keys are the source suffix. The add_action()
1102 # specifies a new source suffix/action binding.
1104 builder
= SCons
.Builder
.Builder(prefix
='A_', suffix
={}, action
={})
1105 env
= Environment(BUILDERS
={'Bld':builder
})
1106 builder
.add_action('.src_sfx1', 'FOO')
1108 r
= builder
.get_name(env
)
1109 assert r
== 'Bld', r
1110 r
= builder
.get_prefix(env
)
1112 r
= builder
.get_suffix(env
)
1114 r
= builder
.get_suffix(env
, [MyNode('X.src_sfx1')])
1116 r
= builder
.get_src_suffix(env
)
1117 assert r
== '.src_sfx1', r
1118 r
= builder
.src_suffixes(env
)
1119 assert r
== ['.src_sfx1'], r
1121 builder
= SCons
.Builder
.Builder(prefix
='A_', suffix
={}, action
={})
1122 env
= Environment(BUILDERS
={'Bld':builder
})
1123 builder
.add_action('.src_sfx1', 'FOO')
1124 builder
.add_action('.src_sfx2', 'BAR')
1126 r
= builder
.get_name(env
)
1127 assert r
== 'Bld', r
1128 r
= builder
.get_prefix(env
)
1130 r
= builder
.get_suffix(env
)
1132 r
= builder
.get_src_suffix(env
)
1133 assert r
== '.src_sfx1', r
1134 r
= sorted(builder
.src_suffixes(env
))
1135 assert r
== ['.src_sfx1', '.src_sfx2'], r
1138 def test_Builder_Args(self
) -> None:
1139 """Testing passing extra args to a builder."""
1140 def buildFunc(target
, source
, env
, s
=self
) -> None:
1143 assert env
['CC'] == 'mycc'
1145 env
=Environment(CC
='cc')
1147 builder
= SCons
.Builder
.Builder(action
=buildFunc
)
1148 tgt
= builder(env
, target
='foo', source
='bar', foo
=1, bar
=2, CC
='mycc')[0]
1150 assert self
.foo
== 1, self
.foo
1151 assert self
.bar
== 2, self
.bar
1153 def test_emitter(self
) -> None:
1154 """Test emitter functions."""
1155 def emit(target
, source
, env
):
1156 foo
= env
.get('foo', 0)
1157 bar
= env
.get('bar', 0)
1159 assert isinstance(t
, MyNode
)
1160 assert t
.has_builder()
1162 assert isinstance(s
, MyNode
)
1164 target
.append("bar%d"%foo)
1166 source
.append("baz")
1167 return ( target
, source
)
1170 builder
= SCons
.Builder
.Builder(action
='foo',
1172 target_factory
=MyNode
,
1173 source_factory
=MyNode
)
1174 tgt
= builder(env
, target
='foo2', source
='bar')[0]
1175 assert str(tgt
) == 'foo2', str(tgt
)
1176 assert str(tgt
.sources
[0]) == 'bar', str(tgt
.sources
[0])
1178 tgt
= builder(env
, target
='foo3', source
='bar', foo
=1)
1179 assert len(tgt
) == 2, len(tgt
)
1180 assert 'foo3' in list(map(str, tgt
)), list(map(str, tgt
))
1181 assert 'bar1' in list(map(str, tgt
)), list(map(str, tgt
))
1183 tgt
= builder(env
, target
='foo4', source
='bar', bar
=1)[0]
1184 assert str(tgt
) == 'foo4', str(tgt
)
1185 assert len(tgt
.sources
) == 2, len(tgt
.sources
)
1186 assert 'baz' in list(map(str, tgt
.sources
)), list(map(str, tgt
.sources
))
1187 assert 'bar' in list(map(str, tgt
.sources
)), list(map(str, tgt
.sources
))
1189 env2
=Environment(FOO
=emit
)
1190 builder2
=SCons
.Builder
.Builder(action
='foo',
1192 target_factory
=MyNode
,
1193 source_factory
=MyNode
)
1195 builder2a
=SCons
.Builder
.Builder(action
='foo',
1197 target_factory
=MyNode
,
1198 source_factory
=MyNode
)
1200 assert builder2
== builder2a
, repr(builder2
.__dict__
) + "\n" + repr(builder2a
.__dict
__)
1202 tgt
= builder2(env2
, target
='foo5', source
='bar')[0]
1203 assert str(tgt
) == 'foo5', str(tgt
)
1204 assert str(tgt
.sources
[0]) == 'bar', str(tgt
.sources
[0])
1206 tgt
= builder2(env2
, target
='foo6', source
='bar', foo
=2)
1207 assert len(tgt
) == 2, len(tgt
)
1208 assert 'foo6' in list(map(str, tgt
)), list(map(str, tgt
))
1209 assert 'bar2' in list(map(str, tgt
)), list(map(str, tgt
))
1211 tgt
= builder2(env2
, target
='foo7', source
='bar', bar
=1)[0]
1212 assert str(tgt
) == 'foo7', str(tgt
)
1213 assert len(tgt
.sources
) == 2, len(tgt
.sources
)
1214 assert 'baz' in list(map(str, tgt
.sources
)), list(map(str, tgt
.sources
))
1215 assert 'bar' in list(map(str, tgt
.sources
)), list(map(str, tgt
.sources
))
1217 def test_emitter_preserve_builder(self
) -> None:
1218 """Test an emitter not overwriting a newly-set builder"""
1221 new_builder
= SCons
.Builder
.Builder(action
='new')
1222 node
= MyNode('foo8')
1223 new_node
= MyNode('foo8.new')
1225 def emit(target
, source
, env
, nb
=new_builder
, nn
=new_node
):
1230 builder
=SCons
.Builder
.Builder(action
='foo',
1232 target_factory
=MyNode
,
1233 source_factory
=MyNode
)
1234 tgt
= builder(env
, target
=node
, source
='bar')[0]
1235 assert tgt
is new_node
, tgt
1236 assert tgt
.builder
is builder
, tgt
.builder
1237 assert node
.builder
is new_builder
, node
.builder
1239 def test_emitter_suffix_map(self
) -> None:
1240 """Test mapping file suffixes to emitter functions"""
1243 def emit4a(target
, source
, env
):
1244 source
= list(map(str, source
))
1245 target
= ['emit4a-' + x
[:-3] for x
in source
]
1246 return (target
, source
)
1247 def emit4b(target
, source
, env
):
1248 source
= list(map(str, source
))
1249 target
= ['emit4b-' + x
[:-3] for x
in source
]
1250 return (target
, source
)
1252 builder
= SCons
.Builder
.Builder(action
='foo',
1253 emitter
={'.4a':emit4a
,
1255 target_factory
=MyNode
,
1256 source_factory
=MyNode
)
1257 tgt
= builder(env
, None, source
='aaa.4a')[0]
1258 assert str(tgt
) == 'emit4a-aaa', str(tgt
)
1259 tgt
= builder(env
, None, source
='bbb.4b')[0]
1260 assert str(tgt
) == 'emit4b-bbb', str(tgt
)
1261 tgt
= builder(env
, None, source
='ccc.4c')[0]
1262 assert str(tgt
) == 'ccc', str(tgt
)
1264 def emit4c(target
, source
, env
):
1265 source
= list(map(str, source
))
1266 target
= ['emit4c-' + x
[:-3] for x
in source
]
1267 return (target
, source
)
1269 builder
.add_emitter('.4c', emit4c
)
1270 tgt
= builder(env
, None, source
='ccc.4c')[0]
1271 assert str(tgt
) == 'emit4c-ccc', str(tgt
)
1273 def test_emitter_function_list(self
) -> None:
1274 """Test lists of emitter functions"""
1277 def emit1a(target
, source
, env
):
1278 source
= list(map(str, source
))
1279 target
= target
+ ['emit1a-' + x
[:-2] for x
in source
]
1280 return (target
, source
)
1281 def emit1b(target
, source
, env
):
1282 source
= list(map(str, source
))
1283 target
= target
+ ['emit1b-' + x
[:-2] for x
in source
]
1284 return (target
, source
)
1285 builder1
= SCons
.Builder
.Builder(action
='foo',
1286 emitter
=[emit1a
, emit1b
],
1287 node_factory
=MyNode
)
1289 tgts
= builder1(env
, target
='target-1', source
='aaa.1')
1290 tgts
= list(map(str, tgts
))
1291 assert tgts
== ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts
1293 # Test a list of emitter functions through the environment.
1294 def emit2a(target
, source
, env
):
1295 source
= list(map(str, source
))
1296 target
= target
+ ['emit2a-' + x
[:-2] for x
in source
]
1297 return (target
, source
)
1298 def emit2b(target
, source
, env
):
1299 source
= list(map(str, source
))
1300 target
= target
+ ['emit2b-' + x
[:-2] for x
in source
]
1301 return (target
, source
)
1302 builder2
= SCons
.Builder
.Builder(action
='foo',
1303 emitter
='$EMITTERLIST',
1304 node_factory
=MyNode
)
1306 env
= Environment(EMITTERLIST
= [emit2a
, emit2b
])
1308 tgts
= builder2(env
, target
='target-2', source
='aaa.2')
1309 tgts
= list(map(str, tgts
))
1310 assert tgts
== ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts
1312 def test_emitter_TARGET_SOURCE(self
) -> None:
1313 """Test use of $TARGET and $SOURCE in emitter results"""
1315 env
= SCons
.Environment
.Environment()
1317 def emit(target
, source
, env
):
1318 return (target
+ ['${SOURCE}.s1', '${TARGET}.t1'],
1319 source
+ ['${TARGET}.t2', '${SOURCE}.s2'])
1321 builder
= SCons
.Builder
.Builder(action
='foo',
1323 node_factory
= MyNode
)
1325 targets
= builder(env
, target
= 'TTT', source
='SSS')
1326 sources
= targets
[0].sources
1327 targets
= list(map(str, targets
))
1328 sources
= list(map(str, sources
))
1329 assert targets
== ['TTT', 'SSS.s1', 'TTT.t1'], targets
1330 assert sources
== ['SSS', 'TTT.t2', 'SSS.s2'], targets
1332 def test_no_target(self
) -> None:
1333 """Test deducing the target from the source."""
1336 b
= SCons
.Builder
.Builder(action
='foo', suffix
='.o')
1338 tgt
= b(env
, None, 'aaa')[0]
1339 assert str(tgt
) == 'aaa.o', str(tgt
)
1340 assert len(tgt
.sources
) == 1, list(map(str, tgt
.sources
))
1341 assert str(tgt
.sources
[0]) == 'aaa', list(map(str, tgt
.sources
))
1343 tgt
= b(env
, None, 'bbb.c')[0]
1344 assert str(tgt
) == 'bbb.o', str(tgt
)
1345 assert len(tgt
.sources
) == 1, list(map(str, tgt
.sources
))
1346 assert str(tgt
.sources
[0]) == 'bbb.c', list(map(str, tgt
.sources
))
1348 tgt
= b(env
, None, 'ccc.x.c')[0]
1349 assert str(tgt
) == 'ccc.x.o', str(tgt
)
1350 assert len(tgt
.sources
) == 1, list(map(str, tgt
.sources
))
1351 assert str(tgt
.sources
[0]) == 'ccc.x.c', list(map(str, tgt
.sources
))
1353 tgt
= b(env
, None, ['d0.c', 'd1.c'])[0]
1354 assert str(tgt
) == 'd0.o', str(tgt
)
1355 assert len(tgt
.sources
) == 2, list(map(str, tgt
.sources
))
1356 assert str(tgt
.sources
[0]) == 'd0.c', list(map(str, tgt
.sources
))
1357 assert str(tgt
.sources
[1]) == 'd1.c', list(map(str, tgt
.sources
))
1359 tgt
= b(env
, target
= None, source
='eee')[0]
1360 assert str(tgt
) == 'eee.o', str(tgt
)
1361 assert len(tgt
.sources
) == 1, list(map(str, tgt
.sources
))
1362 assert str(tgt
.sources
[0]) == 'eee', list(map(str, tgt
.sources
))
1364 tgt
= b(env
, target
= None, source
='fff.c')[0]
1365 assert str(tgt
) == 'fff.o', str(tgt
)
1366 assert len(tgt
.sources
) == 1, list(map(str, tgt
.sources
))
1367 assert str(tgt
.sources
[0]) == 'fff.c', list(map(str, tgt
.sources
))
1369 tgt
= b(env
, target
= None, source
='ggg.x.c')[0]
1370 assert str(tgt
) == 'ggg.x.o', str(tgt
)
1371 assert len(tgt
.sources
) == 1, list(map(str, tgt
.sources
))
1372 assert str(tgt
.sources
[0]) == 'ggg.x.c', list(map(str, tgt
.sources
))
1374 tgt
= b(env
, target
= None, source
=['h0.c', 'h1.c'])[0]
1375 assert str(tgt
) == 'h0.o', str(tgt
)
1376 assert len(tgt
.sources
) == 2, list(map(str, tgt
.sources
))
1377 assert str(tgt
.sources
[0]) == 'h0.c', list(map(str, tgt
.sources
))
1378 assert str(tgt
.sources
[1]) == 'h1.c', list(map(str, tgt
.sources
))
1380 w
= b(env
, target
='i0.w', source
=['i0.x'])[0]
1381 y
= b(env
, target
='i1.y', source
=['i1.z'])[0]
1382 tgt
= b(env
, None, source
=[w
, y
])[0]
1383 assert str(tgt
) == 'i0.o', str(tgt
)
1384 assert len(tgt
.sources
) == 2, list(map(str, tgt
.sources
))
1385 assert str(tgt
.sources
[0]) == 'i0.w', list(map(str, tgt
.sources
))
1386 assert str(tgt
.sources
[1]) == 'i1.y', list(map(str, tgt
.sources
))
1388 def test_get_name(self
) -> None:
1389 """Test getting name of builder.
1391 Each type of builder should return its environment-specific
1392 name when queried appropriately. """
1394 b1
= SCons
.Builder
.Builder(action
='foo', suffix
='.o')
1395 b2
= SCons
.Builder
.Builder(action
='foo', suffix
='.c')
1396 b3
= SCons
.Builder
.Builder(action
='bar', src_suffix
= '.foo',
1398 b4
= SCons
.Builder
.Builder(action
={})
1399 b5
= SCons
.Builder
.Builder(action
='foo', name
='builder5')
1400 b6
= SCons
.Builder
.Builder(action
='foo')
1401 assert isinstance(b4
, SCons
.Builder
.CompositeBuilder
)
1402 assert isinstance(b4
.action
, SCons
.Action
.CommandGeneratorAction
)
1404 env
= Environment(BUILDERS
={'bldr1': b1
,
1408 env2
= Environment(BUILDERS
={'B1': b1
,
1412 # With no name, get_name will return the class. Allow
1415 'SCons.Builder.BuilderBase',
1416 "<class 'SCons.Builder.BuilderBase'>",
1417 'SCons.Memoize.BuilderBase',
1418 "<class 'SCons.Memoize.BuilderBase'>",
1421 assert b1
.get_name(env
) == 'bldr1', b1
.get_name(env
)
1422 assert b2
.get_name(env
) == 'bldr2', b2
.get_name(env
)
1423 assert b3
.get_name(env
) == 'bldr3', b3
.get_name(env
)
1424 assert b4
.get_name(env
) == 'bldr4', b4
.get_name(env
)
1425 assert b5
.get_name(env
) == 'builder5', b5
.get_name(env
)
1426 assert b6
.get_name(env
) in b6_names
, b6
.get_name(env
)
1428 assert b1
.get_name(env2
) == 'B1', b1
.get_name(env2
)
1429 assert b2
.get_name(env2
) == 'B2', b2
.get_name(env2
)
1430 assert b3
.get_name(env2
) == 'B3', b3
.get_name(env2
)
1431 assert b4
.get_name(env2
) == 'B4', b4
.get_name(env2
)
1432 assert b5
.get_name(env2
) == 'builder5', b5
.get_name(env2
)
1433 assert b6
.get_name(env2
) in b6_names
, b6
.get_name(env2
)
1435 assert b5
.get_name(None) == 'builder5', b5
.get_name(None)
1436 assert b6
.get_name(None) in b6_names
, b6
.get_name(None)
1438 # This test worked before adding batch builders, but we must now
1439 # be able to disambiguate a CompositeAction into a more specific
1440 # action based on file suffix at call time. Leave this commented
1441 # out (for now) in case this reflects a real-world use case that
1442 # we must accomodate and we want to resurrect this test.
1443 #tgt = b4(env, target = 'moo', source='cow')
1444 #assert tgt[0].builder.get_name(env) == 'bldr4'
1446 class CompositeBuilderTestCase(unittest
.TestCase
):
1448 def setUp(self
) -> None:
1449 def func_action(target
, source
, env
) -> int:
1452 builder
= SCons
.Builder
.Builder(action
={ '.foo' : func_action
,
1453 '.bar' : func_action
})
1455 self
.func_action
= func_action
1456 self
.builder
= builder
1458 def test___init__(self
) -> None:
1459 """Test CompositeBuilder creation"""
1461 builder
= SCons
.Builder
.Builder(action
={})
1463 tgt
= builder(env
, source
=[])
1464 assert tgt
== [], tgt
1466 assert isinstance(builder
, SCons
.Builder
.CompositeBuilder
)
1467 assert isinstance(builder
.action
, SCons
.Action
.CommandGeneratorAction
)
1469 def test_target_action(self
) -> None:
1470 """Test CompositeBuilder setting of target builder actions"""
1472 builder
= self
.builder
1474 tgt
= builder(env
, target
='test1', source
='test1.foo')[0]
1475 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
)
1476 assert tgt
.builder
.action
is builder
.action
1478 tgt
= builder(env
, target
='test2', source
='test1.bar')[0]
1479 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
)
1480 assert tgt
.builder
.action
is builder
.action
1482 def test_multiple_suffix_error(self
) -> None:
1483 """Test the CompositeBuilder multiple-source-suffix error"""
1485 builder
= self
.builder
1489 builder(env
, target
='test3', source
=['test2.bar', 'test1.foo'])[0]
1490 except SCons
.Errors
.UserError
as e
:
1493 assert flag
, "UserError should be thrown when we call a builder with files of different suffixes."
1494 expect
= "While building `['test3']' from `test1.foo': Cannot build multiple sources with different extensions: .bar, .foo"
1495 assert str(err
) == expect
, err
1497 def test_source_ext_match(self
) -> None:
1498 """Test the CompositeBuilder source_ext_match argument"""
1500 func_action
= self
.func_action
1501 builder
= SCons
.Builder
.Builder(action
={ '.foo' : func_action
,
1502 '.bar' : func_action
},
1503 source_ext_match
= None)
1505 tgt
= builder(env
, target
='test3', source
=['test2.bar', 'test1.foo'])[0]
1508 def test_suffix_variable(self
) -> None:
1509 """Test CompositeBuilder defining action suffixes through a variable"""
1510 env
= Environment(BAR_SUFFIX
= '.BAR2', FOO_SUFFIX
= '.FOO2')
1511 func_action
= self
.func_action
1512 builder
= SCons
.Builder
.Builder(action
={ '.foo' : func_action
,
1513 '.bar' : func_action
,
1514 '$BAR_SUFFIX' : func_action
,
1515 '$FOO_SUFFIX' : func_action
})
1517 tgt
= builder(env
, target
='test4', source
=['test4.BAR2'])[0]
1518 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
)
1522 except SCons
.Errors
.UserError
as e
:
1525 assert flag
, "It should be possible to define actions in composite builders using variables."
1526 env
['FOO_SUFFIX'] = '.BAR2'
1527 builder
.add_action('$NEW_SUFFIX', func_action
)
1530 builder(env
, target
='test5', source
=['test5.BAR2'])[0]
1531 except SCons
.Errors
.UserError
:
1533 assert flag
, "UserError should be thrown when we call a builder with ambigous suffixes."
1535 def test_src_builder(self
) -> None:
1536 """Test CompositeBuilder's use of a src_builder"""
1539 foo_bld
= SCons
.Builder
.Builder(action
= 'a-foo',
1540 src_suffix
= '.ina',
1542 assert isinstance(foo_bld
, SCons
.Builder
.BuilderBase
)
1543 builder
= SCons
.Builder
.Builder(action
= { '.foo' : 'foo',
1545 src_builder
= foo_bld
)
1546 assert isinstance(builder
, SCons
.Builder
.CompositeBuilder
)
1547 assert isinstance(builder
.action
, SCons
.Action
.CommandGeneratorAction
)
1549 tgt
= builder(env
, target
='t1', source
='t1a.ina t1b.ina')[0]
1550 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
)
1552 tgt
= builder(env
, target
='t2', source
='t2a.foo t2b.ina')[0]
1553 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
), tgt
.builder
.__dict
__
1555 bar_bld
= SCons
.Builder
.Builder(action
= 'a-bar',
1556 src_suffix
= '.inb',
1558 assert isinstance(bar_bld
, SCons
.Builder
.BuilderBase
)
1559 builder
= SCons
.Builder
.Builder(action
= { '.foo' : 'foo'},
1560 src_builder
= [foo_bld
, bar_bld
])
1561 assert isinstance(builder
, SCons
.Builder
.CompositeBuilder
)
1562 assert isinstance(builder
.action
, SCons
.Action
.CommandGeneratorAction
)
1564 builder
.add_action('.bar', 'bar')
1566 tgt
= builder(env
, target
='t3-foo', source
='t3a.foo t3b.ina')[0]
1567 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
)
1569 tgt
= builder(env
, target
='t3-bar', source
='t3a.bar t3b.inb')[0]
1570 assert isinstance(tgt
.builder
, SCons
.Builder
.BuilderBase
)
1574 builder(env
, target
='t5', source
=['test5a.foo', 'test5b.inb'])[0]
1575 except SCons
.Errors
.UserError
as e
:
1578 assert flag
, "UserError should be thrown when we call a builder with files of different suffixes."
1579 expect
= "While building `['t5']' from `test5b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
1580 assert str(err
) == expect
, err
1584 builder(env
, target
='t6', source
=['test6a.bar', 'test6b.ina'])[0]
1585 except SCons
.Errors
.UserError
as e
:
1588 assert flag
, "UserError should be thrown when we call a builder with files of different suffixes."
1589 expect
= "While building `['t6']' from `test6b.foo': Cannot build multiple sources with different extensions: .bar, .foo"
1590 assert str(err
) == expect
, err
1594 builder(env
, target
='t4', source
=['test4a.ina', 'test4b.inb'])[0]
1595 except SCons
.Errors
.UserError
as e
:
1598 assert flag
, "UserError should be thrown when we call a builder with files of different suffixes."
1599 expect
= "While building `['t4']' from `test4b.bar': Cannot build multiple sources with different extensions: .foo, .bar"
1600 assert str(err
) == expect
, err
1604 builder(env
, target
='t7', source
=[env
.fs
.File('test7')])[0]
1605 except SCons
.Errors
.UserError
as e
:
1608 assert flag
, "UserError should be thrown when we call a builder with files of different suffixes."
1609 expect
= "While building `['t7']': Cannot deduce file extension from source files: ['test7']"
1610 assert str(err
) == expect
, err
1614 builder(env
, target
='t8', source
=['test8.unknown'])[0]
1615 except SCons
.Errors
.UserError
as e
:
1618 assert flag
, "UserError should be thrown when we call a builder target with an unknown suffix."
1619 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']."
1620 assert str(err
) == expect
, err
1622 if __name__
== "__main__":
1627 # indent-tabs-mode:nil
1629 # vim: set expandtab tabstop=4 shiftwidth=4: