Updates to PR 4374 from mwichmann to correct config file hash changes
[scons.git] / SCons / EnvironmentTests.py
blobe7cb99f3468ec9c683a2870ca5539344384c4ed1
1 # MIT License
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.
24 import SCons.compat
26 import copy
27 import io
28 import os
29 import sys
30 import unittest
31 from collections import UserDict as UD, UserList as UL, deque
33 import TestCmd
35 import SCons.Warnings
36 from SCons.Environment import (
37 Environment,
38 NoSubstitutionProxy,
39 OverrideEnvironment,
40 SubstitutionEnvironment,
41 is_valid_construction_var,
43 from SCons.Util import CLVar
44 from SCons.SConsign import current_sconsign_filename
47 def diff_env(env1, env2):
48 s1 = "env1 = {\n"
49 s2 = "env2 = {\n"
50 d = {}
51 for k in list(env1._dict.keys()) + list(env2._dict.keys()):
52 d[k] = None
53 for k in sorted(d.keys()):
54 if k in env1:
55 if k in env2:
56 if env1[k] != env2[k]:
57 s1 = s1 + " " + repr(k) + " : " + repr(env1[k]) + "\n"
58 s2 = s2 + " " + repr(k) + " : " + repr(env2[k]) + "\n"
59 else:
60 s1 = s1 + " " + repr(k) + " : " + repr(env1[k]) + "\n"
61 elif k in env2:
62 s2 = s2 + " " + repr(k) + " : " + repr(env2[k]) + "\n"
63 s1 = s1 + "}\n"
64 s2 = s2 + "}\n"
65 return s1 + s2
67 def diff_dict(d1, d2):
68 s1 = "d1 = {\n"
69 s2 = "d2 = {\n"
70 d = {}
71 for k in list(d1.keys()) + list(d2.keys()):
72 d[k] = None
73 for k in sorted(d.keys()):
74 if k in d1:
75 if k in d2:
76 if d1[k] != d2[k]:
77 s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n"
78 s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n"
79 else:
80 s1 = s1 + " " + repr(k) + " : " + repr(d1[k]) + "\n"
81 elif k in d2:
82 s2 = s2 + " " + repr(k) + " : " + repr(d2[k]) + "\n"
83 s1 = s1 + "}\n"
84 s2 = s2 + "}\n"
85 return s1 + s2
87 called_it = {}
88 built_it = {}
90 class Builder(SCons.Builder.BuilderBase):
91 """A dummy Builder class for testing purposes. "Building"
92 a target is simply setting a value in the dictionary.
93 """
94 def __init__(self, name = None) -> None:
95 self.name = name
97 def __call__(self, env, target=None, source=None, **kw) -> None:
98 global called_it
99 called_it['target'] = target
100 called_it['source'] = source
101 called_it.update(kw)
103 def execute(self, target = None, **kw) -> None:
104 global built_it
105 built_it[target] = 1
109 scanned_it = {}
111 class Scanner:
112 """A dummy Scanner class for testing purposes. "Scanning"
113 a target is simply setting a value in the dictionary.
115 def __init__(self, name, skeys=[]) -> None:
116 self.name = name
117 self.skeys = skeys
119 def __call__(self, filename) -> None:
120 global scanned_it
121 scanned_it[filename] = 1
123 def __eq__(self, other):
124 try:
125 return self.__dict__ == other.__dict__
126 except AttributeError:
127 return False
129 def get_skeys(self, env):
130 return self.skeys
132 def __str__(self) -> str:
133 return self.name
136 class DummyNode:
137 def __init__(self, name) -> None:
138 self.name = name
139 def __str__(self) -> str:
140 return self.name
141 def rfile(self):
142 return self
143 def get_subst_proxy(self):
144 return self
146 def test_tool( env ) -> None:
147 env['_F77INCFLAGS'] = '${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}'
149 class TestEnvironmentFixture:
150 def TestEnvironment(self, *args, **kw):
151 if not kw or 'tools' not in kw:
152 kw['tools'] = [test_tool]
153 default_keys = { 'CC' : 'cc',
154 'CCFLAGS' : '-DNDEBUG',
155 'ENV' : { 'TMP' : '/tmp' } }
156 for key, value in default_keys.items():
157 if key not in kw:
158 kw[key] = value
159 if 'BUILDERS' not in kw:
160 static_obj = SCons.Builder.Builder(action = {},
161 emitter = {},
162 suffix = '.o',
163 single_source = 1)
164 kw['BUILDERS'] = {'Object' : static_obj}
165 static_obj.add_action('.cpp', 'fake action')
167 env = Environment(*args, **kw)
168 return env
170 class SubstitutionTestCase(unittest.TestCase):
172 def test___init__(self) -> None:
173 """Test initializing a SubstitutionEnvironment."""
174 env = SubstitutionEnvironment()
175 assert '__env__' not in env
177 def test___cmp__(self) -> None:
178 """Test comparing SubstitutionEnvironments."""
179 env1 = SubstitutionEnvironment(XXX = 'x')
180 env2 = SubstitutionEnvironment(XXX = 'x')
181 env3 = SubstitutionEnvironment(XXX = 'xxx')
182 env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x')
184 assert env1 == env2
185 assert env1 != env3
186 assert env1 != env4
188 def test___delitem__(self) -> None:
189 """Test deleting a variable from a SubstitutionEnvironment."""
190 env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
191 env2 = SubstitutionEnvironment(XXX = 'x')
192 del env1['YYY']
193 assert env1 == env2
195 def test___getitem__(self) -> None:
196 """Test fetching a variable from a SubstitutionEnvironment."""
197 env = SubstitutionEnvironment(XXX = 'x')
198 assert env['XXX'] == 'x', env['XXX']
200 def test___setitem__(self) -> None:
201 """Test setting a variable in a SubstitutionEnvironment."""
202 env1 = SubstitutionEnvironment(XXX = 'x')
203 env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
204 env1['YYY'] = 'y'
205 assert env1 == env2
207 def test_get(self) -> None:
208 """Test the SubstitutionEnvironment get() method."""
209 env = SubstitutionEnvironment(XXX = 'x')
210 assert env.get('XXX') == 'x', env.get('XXX')
211 assert env.get('YYY') is None, env.get('YYY')
213 def test_contains(self) -> None:
214 """Test the SubstitutionEnvironment __contains__() method."""
215 env = SubstitutionEnvironment(XXX = 'x')
216 assert 'XXX' in env
217 assert 'YYY' not in env
219 def test_keys(self) -> None:
220 """Test the SubstitutionEnvironment keys() method."""
221 testdata = {'XXX': 'x', 'YYY': 'y'}
222 env = SubstitutionEnvironment(**testdata)
223 keys = list(env.keys())
224 assert len(keys) == 2, keys
225 for k in testdata.keys():
226 assert k in keys, keys
228 def test_values(self) -> None:
229 """Test the SubstitutionEnvironment values() method."""
230 testdata = {'XXX': 'x', 'YYY': 'y'}
231 env = SubstitutionEnvironment(**testdata)
232 values = list(env.values())
233 assert len(values) == 2, values
234 for v in testdata.values():
235 assert v in values, values
237 def test_items(self) -> None:
238 """Test the SubstitutionEnvironment items() method."""
239 testdata = {'XXX': 'x', 'YYY': 'y'}
240 env = SubstitutionEnvironment(**testdata)
241 items = list(env.items())
242 assert len(items) == 2, items
243 for k, v in testdata.items():
244 assert (k, v) in items, items
246 def test_setdefault(self) -> None:
247 """Test the SubstitutionEnvironment setdefault() method."""
248 env = SubstitutionEnvironment(XXX = 'x')
249 assert env.setdefault('XXX', 'z') == 'x', env['XXX']
250 assert env.setdefault('YYY', 'y') == 'y', env['YYY']
251 assert 'YYY' in env
253 def test_arg2nodes(self) -> None:
254 """Test the arg2nodes method."""
255 env = SubstitutionEnvironment()
256 dict = {}
257 class X(SCons.Node.Node):
258 pass
259 def Factory(name, directory = None, create: int = 1, dict=dict, X=X):
260 if name not in dict:
261 dict[name] = X()
262 dict[name].name = name
263 return dict[name]
265 nodes = env.arg2nodes("Util.py UtilTests.py", Factory)
266 assert len(nodes) == 1, nodes
267 assert isinstance(nodes[0], X)
268 assert nodes[0].name == "Util.py UtilTests.py", nodes[0].name
270 nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory)
271 assert len(nodes) == 2, nodes
272 assert isinstance(nodes[0], X)
273 assert isinstance(nodes[1], X)
274 assert nodes[0].name == "Util.py", nodes[0].name
275 assert nodes[1].name == "UtilTests.py", nodes[1].name
277 n1 = Factory("Util.py")
278 nodes = env.arg2nodes([n1, "UtilTests.py"], Factory)
279 assert len(nodes) == 2, nodes
280 assert isinstance(nodes[0], X)
281 assert isinstance(nodes[1], X)
282 assert nodes[0].name == "Util.py", nodes[0].name
283 assert nodes[1].name == "UtilTests.py", nodes[1].name
285 class SConsNode(SCons.Node.Node):
286 pass
287 nodes = env.arg2nodes(SConsNode())
288 assert len(nodes) == 1, nodes
289 assert isinstance(nodes[0], SConsNode), nodes[0]
291 class OtherNode:
292 pass
293 nodes = env.arg2nodes(OtherNode())
294 assert len(nodes) == 1, nodes
295 assert isinstance(nodes[0], OtherNode), nodes[0]
297 def lookup_a(str, F=Factory):
298 if str[0] == 'a':
299 n = F(str)
300 n.a = 1
301 return n
302 else:
303 return None
305 def lookup_b(str, F=Factory):
306 if str[0] == 'b':
307 n = F(str)
308 n.b = 1
309 return n
310 else:
311 return None
313 env_ll = SubstitutionEnvironment()
314 env_ll.lookup_list = [lookup_a, lookup_b]
316 nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
317 assert len(nodes) == 3, nodes
319 assert nodes[0].name == 'aaa', nodes[0]
320 assert nodes[0].a == 1, nodes[0]
321 assert not hasattr(nodes[0], 'b'), nodes[0]
323 assert nodes[1].name == 'bbb'
324 assert not hasattr(nodes[1], 'a'), nodes[1]
325 assert nodes[1].b == 1, nodes[1]
327 assert nodes[2].name == 'ccc'
328 assert not hasattr(nodes[2], 'a'), nodes[1]
329 assert not hasattr(nodes[2], 'b'), nodes[1]
331 def lookup_bbbb(str, F=Factory):
332 if str == 'bbbb':
333 n = F(str)
334 n.bbbb = 1
335 return n
336 else:
337 return None
339 def lookup_c(str, F=Factory):
340 if str[0] == 'c':
341 n = F(str)
342 n.c = 1
343 return n
344 else:
345 return None
347 nodes = env.arg2nodes(['bbbb', 'ccc'], Factory,
348 [lookup_c, lookup_bbbb, lookup_b])
349 assert len(nodes) == 2, nodes
351 assert nodes[0].name == 'bbbb'
352 assert not hasattr(nodes[0], 'a'), nodes[1]
353 assert not hasattr(nodes[0], 'b'), nodes[1]
354 assert nodes[0].bbbb == 1, nodes[1]
355 assert not hasattr(nodes[0], 'c'), nodes[0]
357 assert nodes[1].name == 'ccc'
358 assert not hasattr(nodes[1], 'a'), nodes[1]
359 assert not hasattr(nodes[1], 'b'), nodes[1]
360 assert not hasattr(nodes[1], 'bbbb'), nodes[0]
361 assert nodes[1].c == 1, nodes[1]
363 def test_arg2nodes_target_source(self) -> None:
364 """Test the arg2nodes method with target= and source= keywords
366 targets = [DummyNode('t1'), DummyNode('t2')]
367 sources = [DummyNode('s1'), DummyNode('s2')]
368 env = SubstitutionEnvironment()
369 nodes = env.arg2nodes(['${TARGET}-a',
370 '${SOURCE}-b',
371 '${TARGETS[1]}-c',
372 '${SOURCES[1]}-d'],
373 DummyNode,
374 target=targets,
375 source=sources)
376 names = [n.name for n in nodes]
377 assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names
379 def test_gvars(self) -> None:
380 """Test the base class gvars() method"""
381 env = SubstitutionEnvironment()
382 gvars = env.gvars()
383 assert gvars == {}, gvars
385 def test_lvars(self) -> None:
386 """Test the base class lvars() method"""
387 env = SubstitutionEnvironment()
388 lvars = env.lvars()
389 assert lvars == {}, lvars
391 def test_subst(self) -> None:
392 """Test substituting construction variables within strings
394 Check various combinations, including recursive expansion
395 of variables into other variables.
397 env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
398 mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
399 assert mystr == "a aA b", mystr
401 # Changed the tests below to reflect a bug fix in
402 # subst()
403 env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
404 mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
405 assert mystr == "b bA bB b", mystr
407 env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
408 mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
409 assert mystr == "c cA cB c", mystr
411 # Lists:
412 env = SubstitutionEnvironment(AAA = ['a', 'aa', 'aaa'])
413 mystr = env.subst("$AAA")
414 assert mystr == "a aa aaa", mystr
416 # Tuples:
417 env = SubstitutionEnvironment(AAA = ('a', 'aa', 'aaa'))
418 mystr = env.subst("$AAA")
419 assert mystr == "a aa aaa", mystr
421 t1 = DummyNode('t1')
422 t2 = DummyNode('t2')
423 s1 = DummyNode('s1')
424 s2 = DummyNode('s2')
426 env = SubstitutionEnvironment(AAA = 'aaa')
427 s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
428 assert s == "aaa t1 s1 s2", s
429 s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
430 assert s == "aaa t1 t2 s1", s
432 # Test callables in the SubstitutionEnvironment
433 def foo(target, source, env, for_signature):
434 assert str(target) == 't', target
435 assert str(source) == 's', source
436 return env["FOO"]
438 env = SubstitutionEnvironment(BAR=foo, FOO='baz')
439 t = DummyNode('t')
440 s = DummyNode('s')
442 subst = env.subst('test $BAR', target=t, source=s)
443 assert subst == 'test baz', subst
445 # Test not calling callables in the SubstitutionEnvironment
446 if 0:
447 # This will take some serious surgery to subst() and
448 # subst_list(), so just leave these tests out until we can
449 # do that.
450 def bar(arg) -> None:
451 pass
453 env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
455 subst = env.subst('$BAR', call=None)
456 assert subst is bar, subst
458 subst = env.subst('$FOO', call=None)
459 assert subst is bar, subst
461 def test_subst_kw(self) -> None:
462 """Test substituting construction variables within dictionaries"""
463 env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
464 kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
465 assert len(kw) == 2, kw
466 assert kw['a'] == 'aaa', kw['a']
467 assert kw['bbb'] == 'b', kw['bbb']
469 def test_subst_list(self) -> None:
470 """Test substituting construction variables in command lists
472 env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
473 l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
474 assert l == [["a", "aA", "b"]], l
476 # Changed the tests below to reflect a bug fix in
477 # subst()
478 env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
479 l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
480 assert l == [["b", "bA", "bB", "b"]], l
482 env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
483 l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
484 assert l == [["c", "cA", "cB", "c"]], l
486 env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
487 lst = env.subst_list([ "$AAA", "B $CCC" ])
488 assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
490 t1 = DummyNode('t1')
491 t2 = DummyNode('t2')
492 s1 = DummyNode('s1')
493 s2 = DummyNode('s2')
495 env = SubstitutionEnvironment(AAA = 'aaa')
496 s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
497 assert s == [["aaa", "t1", "s1", "s2"]], s
498 s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
499 assert s == [["aaa", "t1", "t2", "s1"]], s
501 # Test callables in the SubstitutionEnvironment
502 def foo(target, source, env, for_signature):
503 assert str(target) == 't', target
504 assert str(source) == 's', source
505 return env["FOO"]
507 env = SubstitutionEnvironment(BAR=foo, FOO='baz')
508 t = DummyNode('t')
509 s = DummyNode('s')
511 lst = env.subst_list('test $BAR', target=t, source=s)
512 assert lst == [['test', 'baz']], lst
514 # Test not calling callables in the SubstitutionEnvironment
515 if 0:
516 # This will take some serious surgery to subst() and
517 # subst_list(), so just leave these tests out until we can
518 # do that.
519 def bar(arg) -> None:
520 pass
522 env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
524 subst = env.subst_list('$BAR', call=None)
525 assert subst is bar, subst
527 subst = env.subst_list('$FOO', call=None)
528 assert subst is bar, subst
530 def test_subst_path(self) -> None:
531 """Test substituting a path list
533 class MyProxy:
534 def __init__(self, val) -> None:
535 self.val = val
536 def get(self):
537 return self.val + '-proxy'
539 class MyNode:
540 def __init__(self, val) -> None:
541 self.val = val
542 def get_subst_proxy(self):
543 return self
544 def __str__(self) -> str:
545 return self.val
547 class MyObj:
548 def get(self):
549 return self
551 env = SubstitutionEnvironment(FOO='foo',
552 BAR='bar',
553 LIST=['one', 'two'],
554 PROXY=MyProxy('my1'))
556 r = env.subst_path('$FOO')
557 assert r == ['foo'], r
559 r = env.subst_path(['$FOO', 'xxx', '$BAR'])
560 assert r == ['foo', 'xxx', 'bar'], r
562 r = env.subst_path(['$FOO', '$LIST', '$BAR'])
563 assert list(map(str, r)) == ['foo', 'one two', 'bar'], r
565 r = env.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR'])
566 assert r == ['foo', '', '', 'bar'], r
568 r = env.subst_path(['$FOO', '$TARGET', '$BAR'], target=MyNode('ttt'))
569 assert list(map(str, r)) == ['foo', 'ttt', 'bar'], r
571 r = env.subst_path(['$FOO', '$SOURCE', '$BAR'], source=MyNode('sss'))
572 assert list(map(str, r)) == ['foo', 'sss', 'bar'], r
574 n = MyObj()
576 r = env.subst_path(['$PROXY', MyProxy('my2'), n])
577 assert r == ['my1-proxy', 'my2-proxy', n], r
579 class StringableObj:
580 def __init__(self, s) -> None:
581 self.s = s
582 def __str__(self) -> str:
583 return self.s
585 env = SubstitutionEnvironment(FOO=StringableObj("foo"),
586 BAR=StringableObj("bar"))
588 r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
589 assert r == [ "foo/bar", "bar/baz" ], r
591 r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
592 assert r == [ "bar/foo", "baz/bar" ], r
594 r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
595 assert r == [ "bar/foo/bar", "baz/bar/baz" ], r
597 def test_subst_target_source(self) -> None:
598 """Test the base environment subst_target_source() method"""
599 env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
600 mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
601 assert mystr == "a aA b", mystr
603 def test_backtick(self) -> None:
604 """Test the backtick() method for capturing command output"""
605 env = SubstitutionEnvironment()
607 test = TestCmd.TestCmd(workdir = '')
608 test.write('stdout.py', """\
609 import sys
610 sys.stdout.write('this came from stdout.py\\n')
611 sys.exit(0)
612 """)
613 test.write('stderr.py', """\
614 import sys
615 sys.stderr.write('this came from stderr.py\\n')
616 sys.exit(0)
617 """)
618 test.write('fail.py', """\
619 import sys
620 sys.exit(1)
621 """)
622 test.write('echo.py', """\
623 import os, sys
624 sys.stdout.write(os.environ['ECHO'] + '\\n')
625 sys.exit(0)
626 """)
628 save_stderr = sys.stderr
630 python = '"' + sys.executable + '"'
632 try:
633 sys.stderr = io.StringIO()
634 cmd = '%s %s' % (python, test.workpath('stdout.py'))
635 output = env.backtick(cmd)
636 errout = sys.stderr.getvalue()
637 assert output == 'this came from stdout.py\n', output
638 assert errout == '', errout
640 sys.stderr = io.StringIO()
641 cmd = '%s %s' % (python, test.workpath('stderr.py'))
642 output = env.backtick(cmd)
643 errout = sys.stderr.getvalue()
644 assert output == '', output
645 assert errout == 'this came from stderr.py\n', errout
647 sys.stderr = io.StringIO()
648 cmd = '%s %s' % (python, test.workpath('fail.py'))
649 try:
650 env.backtick(cmd)
651 except OSError as e:
652 assert str(e) == "'%s' exited 1" % cmd, str(e)
653 else:
654 self.fail("did not catch expected OSError")
656 sys.stderr = io.StringIO()
657 cmd = '%s %s' % (python, test.workpath('echo.py'))
658 env['ENV'] = os.environ.copy()
659 env['ENV']['ECHO'] = 'this came from ECHO'
660 output = env.backtick(cmd)
661 errout = sys.stderr.getvalue()
662 assert output == 'this came from ECHO\n', output
663 assert errout == '', errout
665 finally:
666 sys.stderr = save_stderr
668 def test_AddMethod(self) -> None:
669 """Test the AddMethod() method"""
670 env = SubstitutionEnvironment(FOO = 'foo')
672 def func(self):
673 return 'func-' + self['FOO']
675 assert not hasattr(env, 'func')
676 env.AddMethod(func)
677 r = env.func()
678 assert r == 'func-foo', r
680 assert not hasattr(env, 'bar')
681 env.AddMethod(func, 'bar')
682 r = env.bar()
683 assert r == 'func-foo', r
685 def func2(self, arg: str=''):
686 return 'func2-' + self['FOO'] + arg
688 env.AddMethod(func2)
689 r = env.func2()
690 assert r == 'func2-foo', r
691 r = env.func2('-xxx')
692 assert r == 'func2-foo-xxx', r
694 env.AddMethod(func2, 'func')
695 r = env.func()
696 assert r == 'func2-foo', r
697 r = env.func('-yyy')
698 assert r == 'func2-foo-yyy', r
700 # Test that clones of clones correctly re-bind added methods.
701 env1 = Environment(FOO = '1')
702 env1.AddMethod(func2)
703 env2 = env1.Clone(FOO = '2')
704 env3 = env2.Clone(FOO = '3')
705 env4 = env3.Clone(FOO = '4')
706 r = env1.func2()
707 assert r == 'func2-1', r
708 r = env2.func2()
709 assert r == 'func2-2', r
710 r = env3.func2()
711 assert r == 'func2-3', r
712 r = env4.func2()
713 assert r == 'func2-4', r
715 # Test that clones don't re-bind an attribute that the user set.
716 env1 = Environment(FOO = '1')
717 env1.AddMethod(func2)
718 def replace_func2() -> str:
719 return 'replace_func2'
720 env1.func2 = replace_func2
721 env2 = env1.Clone(FOO = '2')
722 r = env2.func2()
723 assert r == 'replace_func2', r
725 # Test clone rebinding if using global AddMethod.
726 env1 = Environment(FOO='1')
727 SCons.Util.AddMethod(env1, func2)
728 r = env1.func2()
729 assert r == 'func2-1', r
730 r = env1.func2('-xxx')
731 assert r == 'func2-1-xxx', r
732 env2 = env1.Clone(FOO='2')
733 r = env2.func2()
734 assert r == 'func2-2', r
737 def test_Override(self) -> None:
738 """Test overriding construction variables"""
739 env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4)
740 assert env['ONE'] == 1, env['ONE']
741 assert env['TWO'] == 2, env['TWO']
742 assert env['THREE'] == 3, env['THREE']
743 assert env['FOUR'] == 4, env['FOUR']
745 env2 = env.Override({'TWO' : '10',
746 'THREE' :'x $THREE y',
747 'FOUR' : ['x', '$FOUR', 'y']})
748 assert env2['ONE'] == 1, env2['ONE']
749 assert env2['TWO'] == '10', env2['TWO']
750 assert env2['THREE'] == 'x 3 y', env2['THREE']
751 assert env2['FOUR'] == ['x', 4, 'y'], env2['FOUR']
753 assert env['ONE'] == 1, env['ONE']
754 assert env['TWO'] == 2, env['TWO']
755 assert env['THREE'] == 3, env['THREE']
756 assert env['FOUR'] == 4, env['FOUR']
758 env2.Replace(ONE = "won")
759 assert env2['ONE'] == "won", env2['ONE']
760 assert env['ONE'] == 1, env['ONE']
762 def test_ParseFlags(self) -> None:
763 """Test the ParseFlags() method
765 env = SubstitutionEnvironment()
767 empty = {
768 'ASFLAGS' : [],
769 'CFLAGS' : [],
770 'CCFLAGS' : [],
771 'CXXFLAGS' : [],
772 'CPPDEFINES' : [],
773 'CPPFLAGS' : [],
774 'CPPPATH' : [],
775 'FRAMEWORKPATH' : [],
776 'FRAMEWORKS' : [],
777 'LIBPATH' : [],
778 'LIBS' : [],
779 'LINKFLAGS' : [],
780 'RPATH' : [],
783 d = env.ParseFlags(None)
784 assert d == empty, d
786 d = env.ParseFlags('')
787 assert d == empty, d
789 d = env.ParseFlags([])
790 assert d == empty, d
792 s = (
793 "-I/usr/include/fum -I bar -X "
794 '-I"C:\\Program Files\\ASCEND\\include" '
795 "-L/usr/fax -L foo -lxxx -l yyy "
796 '-L"C:\\Program Files\\ASCEND" -lascend '
797 "-Wa,-as -Wl,-link "
798 "-Wl,-rpath=rpath1 "
799 "-Wl,-R,rpath2 "
800 "-Wl,-Rrpath3 "
801 "-Wp,-cpp "
802 "-std=c99 "
803 "-std=c++0x "
804 "-framework Carbon "
805 "-frameworkdir=fwd1 "
806 "-Ffwd2 "
807 "-F fwd3 "
808 "-dylib_file foo-dylib "
809 "-pthread "
810 "-fmerge-all-constants "
811 "-fopenmp "
812 "-mno-cygwin -mwindows "
813 "-arch i386 "
814 "-isysroot /tmp "
815 "-iquote /usr/include/foo1 "
816 "-isystem /usr/include/foo2 "
817 "-idirafter /usr/include/foo3 "
818 "-imacros /usr/include/foo4 "
819 "-include /usr/include/foo5 "
820 "--param l1-cache-size=32 --param l2-cache-size=6144 "
821 "+DD64 "
822 "-DFOO -DBAR=value -D BAZ "
823 "-fsanitize=memory "
824 "-fsanitize-address-use-after-return "
827 d = env.ParseFlags(s)
829 assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
830 assert d['CFLAGS'] == ['-std=c99']
831 assert d['CCFLAGS'] == ['-X', '-Wa,-as',
832 '-pthread', '-fmerge-all-constants',
833 '-fopenmp', '-mno-cygwin',
834 ('-arch', 'i386'), ('-isysroot', '/tmp'),
835 ('-iquote', '/usr/include/foo1'),
836 ('-isystem', '/usr/include/foo2'),
837 ('-idirafter', '/usr/include/foo3'),
838 ('-imacros', env.fs.File('/usr/include/foo4')),
839 ('-include', env.fs.File('/usr/include/foo5')),
840 ('--param', 'l1-cache-size=32'), ('--param', 'l2-cache-size=6144'),
841 '+DD64',
842 '-fsanitize=memory',
843 '-fsanitize-address-use-after-return'], repr(d['CCFLAGS'])
844 assert d['CXXFLAGS'] == ['-std=c++0x'], repr(d['CXXFLAGS'])
845 assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
846 assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
847 assert d['CPPPATH'] == ['/usr/include/fum',
848 'bar',
849 'C:\\Program Files\\ASCEND\\include'], d['CPPPATH']
850 assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
851 assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS']
852 assert d['LIBPATH'] == ['/usr/fax',
853 'foo',
854 'C:\\Program Files\\ASCEND'], d['LIBPATH']
855 LIBS = list(map(str, d['LIBS']))
856 assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS)
857 assert d['LINKFLAGS'] == ['-Wl,-link',
858 '-dylib_file', 'foo-dylib',
859 '-pthread', '-fmerge-all-constants', '-fopenmp',
860 '-mno-cygwin', '-mwindows',
861 ('-arch', 'i386'),
862 ('-isysroot', '/tmp'),
863 '+DD64',
864 '-fsanitize=memory',
865 '-fsanitize-address-use-after-return'], repr(d['LINKFLAGS'])
866 assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH']
869 def test_MergeFlags(self) -> None:
870 """Test the MergeFlags() method."""
872 env = SubstitutionEnvironment()
873 # does not set flag if value empty
874 env.MergeFlags('')
875 assert 'CCFLAGS' not in env, env['CCFLAGS']
876 # merges value if flag did not exist
877 env.MergeFlags('-X')
878 assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
880 # avoid SubstitutionEnvironment for these, has no .Append method,
881 # which is needed for unique=False test
882 env = Environment(CCFLAGS="")
883 # merge with existing but empty flag
884 env.MergeFlags('-X')
885 assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
886 # default Unique=True enforces no dupes
887 env.MergeFlags('-X')
888 assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
889 # Unique=False allows dupes
890 env.MergeFlags('-X', unique=False)
891 assert env['CCFLAGS'] == ['-X', '-X'], env['CCFLAGS']
893 # merge from a dict with list values
894 env = SubstitutionEnvironment(B='b')
895 env.MergeFlags({'A': ['aaa'], 'B': ['bb', 'bbb']})
896 assert env['A'] == ['aaa'], env['A']
897 assert env['B'] == ['b', 'bb', 'bbb'], env['B']
899 # issue #2961: merge from a dict with string values
900 env = SubstitutionEnvironment(B='b')
901 env.MergeFlags({'A': 'aaa', 'B': 'bb bbb'})
902 assert env['A'] == ['aaa'], env['A']
903 assert env['B'] == ['b', 'bb', 'bbb'], env['B']
905 # issue #4231: CPPDEFINES can be a deque, tripped up merge logic
906 env = Environment(CPPDEFINES=deque(['aaa', 'bbb']))
907 env.MergeFlags({'CPPDEFINES': 'ccc'})
908 self.assertEqual(env['CPPDEFINES'], deque(['aaa', 'bbb', 'ccc']))
910 # issue #3665: if merging dict which is a compound object
911 # (i.e. value can be lists, etc.), the value object should not
912 # be modified. per the issue, this happened if key not in env.
913 env = SubstitutionEnvironment()
914 try:
915 del env['CFLAGS'] # just to be sure
916 except KeyError:
917 pass
918 flags = {'CFLAGS': ['-pipe', '-pthread', '-g']}
919 import copy
921 saveflags = copy.deepcopy(flags)
922 env.MergeFlags(flags)
923 self.assertEqual(flags, saveflags)
926 class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
928 reserved_variables = [
929 'CHANGED_SOURCES',
930 'CHANGED_TARGETS',
931 'SOURCE',
932 'SOURCES',
933 'TARGET',
934 'TARGETS',
935 'UNCHANGED_SOURCES',
936 'UNCHANGED_TARGETS',
939 def test___init__(self) -> None:
940 """Test construction Environment creation
942 Create two with identical arguments and check that
943 they compare the same.
945 env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
946 env2 = self.TestEnvironment(XXX = 'x', YYY = 'y')
947 assert env1 == env2, diff_env(env1, env2)
949 assert '__env__' not in env1
950 assert '__env__' not in env2
952 def test_variables(self) -> None:
953 """Test that variables only get applied once."""
954 class FakeOptions:
955 def __init__(self, key, val) -> None:
956 self.calls = 0
957 self.key = key
958 self.val = val
959 def keys(self):
960 return [self.key]
961 def Update(self, env) -> None:
962 env[self.key] = self.val
963 self.calls = self.calls + 1
965 o = FakeOptions('AAA', 'fake_opt')
966 env = Environment(variables=o, AAA='keyword_arg')
967 assert o.calls == 1, o.calls
968 assert env['AAA'] == 'fake_opt', env['AAA']
970 def test_get(self) -> None:
971 """Test the get() method."""
972 env = self.TestEnvironment(aaa = 'AAA')
974 x = env.get('aaa')
975 assert x == 'AAA', x
976 x = env.get('aaa', 'XXX')
977 assert x == 'AAA', x
978 x = env.get('bbb')
979 assert x is None, x
980 x = env.get('bbb', 'XXX')
981 assert x == 'XXX', x
983 def test_Builder_calls(self) -> None:
984 """Test Builder calls through different environments
986 global called_it
988 b1 = Builder()
989 b2 = Builder()
991 env = Environment()
992 env.Replace(BUILDERS = { 'builder1' : b1,
993 'builder2' : b2 })
994 called_it = {}
995 env.builder1('in1')
996 assert called_it['target'] is None, called_it
997 assert called_it['source'] == ['in1'], called_it
999 called_it = {}
1000 env.builder2(source = 'in2', xyzzy = 1)
1001 assert called_it['target'] is None, called_it
1002 assert called_it['source'] == ['in2'], called_it
1003 assert called_it['xyzzy'] == 1, called_it
1005 called_it = {}
1006 env.builder1(foo = 'bar')
1007 assert called_it['foo'] == 'bar', called_it
1008 assert called_it['target'] is None, called_it
1009 assert called_it['source'] is None, called_it
1011 def test_BuilderWrapper_attributes(self) -> None:
1012 """Test getting and setting of BuilderWrapper attributes."""
1013 b1 = Builder()
1014 b2 = Builder()
1015 e1 = Environment()
1016 e2 = Environment()
1018 e1.Replace(BUILDERS={'b': b1})
1019 bw = e1.b
1021 assert bw.env is e1
1022 bw.env = e2
1023 assert bw.env is e2
1025 assert bw.builder is b1
1026 bw.builder = b2
1027 assert bw.builder is b2
1029 self.assertRaises(AttributeError, getattr, bw, 'foobar')
1030 bw.foobar = 42
1031 assert bw.foobar == 42
1033 # This unit test is currently disabled because we don't think the
1034 # underlying method it tests (Environment.BuilderWrapper.execute())
1035 # is necessary, but we're leaving the code here for now in case
1036 # that's mistaken.
1037 def _DO_NOT_test_Builder_execs(self) -> None:
1038 """Test Builder execution through different environments
1040 One environment is initialized with a single
1041 Builder object, one with a list of a single Builder
1042 object, and one with a list of two Builder objects.
1044 global built_it
1046 b1 = Builder()
1047 b2 = Builder()
1049 built_it = {}
1050 env3 = Environment()
1051 env3.Replace(BUILDERS = { 'builder1' : b1,
1052 'builder2' : b2 })
1053 env3.builder1.execute(target = 'out1')
1054 env3.builder2.execute(target = 'out2')
1055 env3.builder1.execute(target = 'out3')
1056 assert built_it['out1']
1057 assert built_it['out2']
1058 assert built_it['out3']
1060 env4 = env3.Clone()
1061 assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (
1062 env4.builder1.env, env3)
1063 assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (
1064 env4.builder1.env, env3)
1066 # Now test BUILDERS as a dictionary.
1067 built_it = {}
1068 env5 = self.TestEnvironment(BUILDERS={ 'foo' : b1 })
1069 env5['BUILDERS']['bar'] = b2
1070 env5.foo.execute(target='out1')
1071 env5.bar.execute(target='out2')
1072 assert built_it['out1']
1073 assert built_it['out2']
1075 built_it = {}
1076 env6 = Environment()
1077 env6['BUILDERS'] = { 'foo' : b1,
1078 'bar' : b2 }
1079 env6.foo.execute(target='out1')
1080 env6.bar.execute(target='out2')
1081 assert built_it['out1']
1082 assert built_it['out2']
1086 def test_Scanners(self) -> None:
1087 """Test setting SCANNERS in various ways
1089 One environment is initialized with a single
1090 Scanner object, one with a list of a single Scanner
1091 object, and one with a list of two Scanner objects.
1093 global scanned_it
1095 s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
1096 s2 = Scanner(name = 'scanner2', skeys = [".m4"])
1097 s3 = Scanner(name = 'scanner3', skeys = [".m4", ".m5"])
1098 s4 = Scanner(name = 'scanner4', skeys = [None])
1100 # XXX Tests for scanner execution through different environments,
1101 # XXX if we ever want to do that some day
1102 # scanned_it = {}
1103 # env1 = self.TestEnvironment(SCANNERS = s1)
1104 # env1.scanner1(filename = 'out1')
1105 # assert scanned_it['out1']
1107 # scanned_it = {}
1108 # env2 = self.TestEnvironment(SCANNERS = [s1])
1109 # env1.scanner1(filename = 'out1')
1110 # assert scanned_it['out1']
1112 # scanned_it = {}
1113 # env3 = Environment()
1114 # env3.Replace(SCANNERS = [s1])
1115 # env3.scanner1(filename = 'out1')
1116 # env3.scanner2(filename = 'out2')
1117 # env3.scanner1(filename = 'out3')
1118 # assert scanned_it['out1']
1119 # assert scanned_it['out2']
1120 # assert scanned_it['out3']
1122 suffixes = [".c", ".cc", ".cxx", ".m4", ".m5"]
1124 env = Environment()
1125 try: del env['SCANNERS']
1126 except KeyError: pass
1127 s = list(map(env.get_scanner, suffixes))
1128 assert s == [None, None, None, None, None], s
1130 env = self.TestEnvironment(SCANNERS = [])
1131 s = list(map(env.get_scanner, suffixes))
1132 assert s == [None, None, None, None, None], s
1134 env.Replace(SCANNERS = [s1])
1135 s = list(map(env.get_scanner, suffixes))
1136 assert s == [s1, s1, None, None, None], s
1138 env.Append(SCANNERS = [s2])
1139 s = list(map(env.get_scanner, suffixes))
1140 assert s == [s1, s1, None, s2, None], s
1142 env.AppendUnique(SCANNERS = [s3])
1143 s = list(map(env.get_scanner, suffixes))
1144 assert s == [s1, s1, None, s2, s3], s
1146 env = env.Clone(SCANNERS = [s2])
1147 s = list(map(env.get_scanner, suffixes))
1148 assert s == [None, None, None, s2, None], s
1150 env['SCANNERS'] = [s1]
1151 s = list(map(env.get_scanner, suffixes))
1152 assert s == [s1, s1, None, None, None], s
1154 env.PrependUnique(SCANNERS = [s2, s1])
1155 s = list(map(env.get_scanner, suffixes))
1156 assert s == [s1, s1, None, s2, None], s
1158 env.Prepend(SCANNERS = [s3])
1159 s = list(map(env.get_scanner, suffixes))
1160 assert s == [s1, s1, None, s3, s3], s
1162 # Verify behavior of case-insensitive suffix matches on Windows.
1163 uc_suffixes = [_.upper() for _ in suffixes]
1165 env = Environment(SCANNERS = [s1, s2, s3],
1166 PLATFORM = 'linux')
1168 s = list(map(env.get_scanner, suffixes))
1169 assert s == [s1, s1, None, s2, s3], s
1171 s = list(map(env.get_scanner, uc_suffixes))
1172 assert s == [None, None, None, None, None], s
1174 env['PLATFORM'] = 'win32'
1176 s = list(map(env.get_scanner, uc_suffixes))
1177 assert s == [s1, s1, None, s2, s3], s
1179 # Verify behavior for a scanner returning None (on Windows
1180 # where we might try to perform case manipulation on None).
1181 env.Replace(SCANNERS = [s4])
1182 s = list(map(env.get_scanner, suffixes))
1183 assert s == [None, None, None, None, None], s
1185 def test_ENV(self) -> None:
1186 """Test setting the external ENV in Environments
1188 env = Environment()
1189 assert 'ENV' in env.Dictionary()
1191 env = self.TestEnvironment(ENV = { 'PATH' : '/foo:/bar' })
1192 assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
1194 def test_ReservedVariables(self) -> None:
1195 """Test warning generation when reserved variable names are set"""
1197 reserved_variables = [
1198 'CHANGED_SOURCES',
1199 'CHANGED_TARGETS',
1200 'SOURCE',
1201 'SOURCES',
1202 'TARGET',
1203 'TARGETS',
1204 'UNCHANGED_SOURCES',
1205 'UNCHANGED_TARGETS',
1208 warning = SCons.Warnings.ReservedVariableWarning
1209 SCons.Warnings.enableWarningClass(warning)
1210 old = SCons.Warnings.warningAsException(1)
1212 try:
1213 env4 = Environment()
1214 for kw in self.reserved_variables:
1215 exc_caught = None
1216 try:
1217 env4[kw] = 'xyzzy'
1218 except warning:
1219 exc_caught = 1
1220 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
1221 assert kw not in env4, "`%s' variable was incorrectly set" % kw
1222 finally:
1223 SCons.Warnings.warningAsException(old)
1225 def test_FutureReservedVariables(self) -> None:
1226 """Test warning generation when future reserved variable names are set"""
1228 future_reserved_variables = []
1230 warning = SCons.Warnings.FutureReservedVariableWarning
1231 SCons.Warnings.enableWarningClass(warning)
1232 old = SCons.Warnings.warningAsException(1)
1234 try:
1235 env4 = Environment()
1236 for kw in future_reserved_variables:
1237 exc_caught = None
1238 try:
1239 env4[kw] = 'xyzzy'
1240 except warning:
1241 exc_caught = 1
1242 assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw
1243 assert kw in env4, "`%s' variable was not set" % kw
1244 finally:
1245 SCons.Warnings.warningAsException(old)
1247 def test_IllegalVariables(self) -> None:
1248 """Test that use of illegal variables raises an exception"""
1249 env = Environment()
1250 def test_it(var, env=env) -> None:
1251 exc_caught = None
1252 try:
1253 env[var] = 1
1254 except SCons.Errors.UserError:
1255 exc_caught = 1
1256 assert exc_caught, "did not catch UserError for '%s'" % var
1257 env['aaa'] = 1
1258 assert env['aaa'] == 1, env['aaa']
1259 test_it('foo/bar')
1260 test_it('foo.bar')
1261 test_it('foo-bar')
1263 def test_autogenerate(self) -> None:
1264 """Test autogenerating variables in a dictionary."""
1266 drive, p = os.path.splitdrive(os.getcwd())
1267 def normalize_path(path, drive=drive):
1268 if path[0] in '\\/':
1269 path = drive + path
1270 path = os.path.normpath(path)
1271 drive, path = os.path.splitdrive(path)
1272 return drive.lower() + path
1274 env = self.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ],
1275 LIBLINKPREFIX = 'foo',
1276 LIBLINKSUFFIX = 'bar')
1278 def RDirs(pathlist, fs=env.fs):
1279 return fs.Dir('xx').Rfindalldirs(pathlist)
1281 env['RDirs'] = RDirs
1282 flags = env.subst_list('$_LIBFLAGS', 1)[0]
1283 assert flags == ['foobar', 'foobar', 'foobazbar'], flags
1285 blat = env.fs.Dir('blat')
1287 env.Replace(CPPPATH = [ 'foo', '$FOO/bar', blat ],
1288 INCPREFIX = 'foo ',
1289 INCSUFFIX = 'bar',
1290 FOO = 'baz')
1291 flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
1292 expect = [ '$(',
1293 normalize_path('foo'),
1294 normalize_path('xx/foobar'),
1295 normalize_path('foo'),
1296 normalize_path('xx/baz/bar'),
1297 normalize_path('foo'),
1298 normalize_path('blatbar'),
1299 '$)',
1301 assert flags == expect, flags
1303 env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ],
1304 INCPREFIX = 'foo ',
1305 INCSUFFIX = 'bar',
1306 FOO = 'baz')
1307 flags = env.subst_list('$_F77INCFLAGS', 1)[0]
1308 expect = [ '$(',
1309 normalize_path('foo'),
1310 normalize_path('xx/foobar'),
1311 normalize_path('foo'),
1312 normalize_path('xx/baz/bar'),
1313 normalize_path('foo'),
1314 normalize_path('blatbar'),
1315 '$)',
1317 assert flags == expect, flags
1319 env.Replace(CPPPATH = '', F77PATH = '', LIBPATH = '')
1320 l = env.subst_list('$_CPPINCFLAGS')
1321 assert l == [[]], l
1322 l = env.subst_list('$_F77INCFLAGS')
1323 assert l == [[]], l
1324 l = env.subst_list('$_LIBDIRFLAGS')
1325 assert l == [[]], l
1327 env.fs.Repository('/rep1')
1328 env.fs.Repository('/rep2')
1329 env.Replace(CPPPATH = [ 'foo', '/__a__/b', '$FOO/bar', blat],
1330 INCPREFIX = '-I ',
1331 INCSUFFIX = 'XXX',
1332 FOO = 'baz')
1333 flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
1334 expect = [ '$(',
1335 '-I', normalize_path('xx/fooXXX'),
1336 '-I', normalize_path('/rep1/xx/fooXXX'),
1337 '-I', normalize_path('/rep2/xx/fooXXX'),
1338 '-I', normalize_path('/__a__/bXXX'),
1339 '-I', normalize_path('xx/baz/barXXX'),
1340 '-I', normalize_path('/rep1/xx/baz/barXXX'),
1341 '-I', normalize_path('/rep2/xx/baz/barXXX'),
1342 '-I', normalize_path('blatXXX'),
1343 '$)'
1345 def normalize_if_path(arg, np=normalize_path):
1346 if arg not in ('$(','$)','-I'):
1347 return np(str(arg))
1348 return arg
1349 flags = list(map(normalize_if_path, flags))
1350 assert flags == expect, flags
1352 def test_platform(self) -> None:
1353 """Test specifying a platform callable when instantiating."""
1354 class platform:
1355 def __str__(self) -> str: return "TestPlatform"
1356 def __call__(self, env) -> None: env['XYZZY'] = 777
1358 def tool(env) -> None:
1359 env['SET_TOOL'] = 'initialized'
1360 assert env['PLATFORM'] == "TestPlatform"
1362 env = self.TestEnvironment(platform = platform(), tools = [tool])
1363 assert env['XYZZY'] == 777, env
1364 assert env['PLATFORM'] == "TestPlatform"
1365 assert env['SET_TOOL'] == "initialized"
1367 def test_Default_PLATFORM(self) -> None:
1368 """Test overriding the default PLATFORM variable"""
1369 class platform:
1370 def __str__(self) -> str: return "DefaultTestPlatform"
1371 def __call__(self, env) -> None: env['XYZZY'] = 888
1373 def tool(env) -> None:
1374 env['SET_TOOL'] = 'abcde'
1375 assert env['PLATFORM'] == "DefaultTestPlatform"
1377 import SCons.Defaults
1378 save = SCons.Defaults.ConstructionEnvironment.copy()
1379 try:
1380 import SCons.Defaults
1381 SCons.Defaults.ConstructionEnvironment.update({
1382 'PLATFORM' : platform(),
1384 env = self.TestEnvironment(tools = [tool])
1385 assert env['XYZZY'] == 888, env
1386 assert env['PLATFORM'] == "DefaultTestPlatform"
1387 assert env['SET_TOOL'] == "abcde"
1388 finally:
1389 SCons.Defaults.ConstructionEnvironment = save
1391 def test_tools(self) -> None:
1392 """Test specifying a tool callable when instantiating."""
1393 def t1(env) -> None:
1394 env['TOOL1'] = 111
1395 def t2(env) -> None:
1396 env['TOOL2'] = 222
1397 def t3(env) -> None:
1398 env['AAA'] = env['XYZ']
1399 def t4(env) -> None:
1400 env['TOOL4'] = 444
1401 env = self.TestEnvironment(tools = [t1, t2, t3], XYZ = 'aaa')
1402 assert env['TOOL1'] == 111, env['TOOL1']
1403 assert env['TOOL2'] == 222, env
1404 assert env['AAA'] == 'aaa', env
1405 t4(env)
1406 assert env['TOOL4'] == 444, env
1408 test = TestCmd.TestCmd(workdir = '')
1409 test.write('faketool.py', """\
1410 def generate(env, **kw):
1411 for k, v in kw.items():
1412 env[k] = v
1414 def exists(env):
1415 return True
1416 """)
1418 env = self.TestEnvironment(tools = [('faketool', {'a':1, 'b':2, 'c':3})],
1419 toolpath = [test.workpath('')])
1420 assert env['a'] == 1, env['a']
1421 assert env['b'] == 2, env['b']
1422 assert env['c'] == 3, env['c']
1424 def test_Default_TOOLS(self) -> None:
1425 """Test overriding the default TOOLS variable"""
1426 def t5(env) -> None:
1427 env['TOOL5'] = 555
1428 def t6(env) -> None:
1429 env['TOOL6'] = 666
1430 def t7(env) -> None:
1431 env['BBB'] = env['XYZ']
1432 def t8(env) -> None:
1433 env['TOOL8'] = 888
1435 import SCons.Defaults
1436 save = SCons.Defaults.ConstructionEnvironment.copy()
1437 try:
1438 SCons.Defaults.ConstructionEnvironment.update({
1439 'TOOLS' : [t5, t6, t7],
1441 env = Environment(XYZ = 'bbb')
1442 assert env['TOOL5'] == 555, env['TOOL5']
1443 assert env['TOOL6'] == 666, env
1444 assert env['BBB'] == 'bbb', env
1445 t8(env)
1446 assert env['TOOL8'] == 888, env
1447 finally:
1448 SCons.Defaults.ConstructionEnvironment = save
1450 def test_null_tools(self) -> None:
1451 """Test specifying a tool of None is OK."""
1452 def t1(env) -> None:
1453 env['TOOL1'] = 111
1454 def t2(env) -> None:
1455 env['TOOL2'] = 222
1456 env = self.TestEnvironment(tools = [t1, None, t2], XYZ = 'aaa')
1457 assert env['TOOL1'] == 111, env['TOOL1']
1458 assert env['TOOL2'] == 222, env
1459 assert env['XYZ'] == 'aaa', env
1460 env = self.TestEnvironment(tools = [None], XYZ = 'xyz')
1461 assert env['XYZ'] == 'xyz', env
1462 env = self.TestEnvironment(tools = [t1, '', t2], XYZ = 'ddd')
1463 assert env['TOOL1'] == 111, env['TOOL1']
1464 assert env['TOOL2'] == 222, env
1465 assert env['XYZ'] == 'ddd', env
1467 def test_default_copy_cache(self) -> None:
1468 copied = False
1470 def copy2(self, src, dst) -> None:
1471 nonlocal copied
1472 copied = True
1474 save_copy_from_cache = SCons.CacheDir.CacheDir.copy_from_cache
1475 SCons.CacheDir.CacheDir.copy_from_cache = copy2
1477 save_copy_to_cache = SCons.CacheDir.CacheDir.copy_to_cache
1478 SCons.CacheDir.CacheDir.copy_to_cache = copy2
1480 env = self.TestEnvironment()
1482 SCons.Environment.default_copy_from_cache(env, 'test.in', 'test.out')
1483 assert copied
1485 copied = False
1486 SCons.Environment.default_copy_to_cache(env, 'test.in', 'test.out')
1487 assert copied
1489 SCons.CacheDir.CacheDir.copy_from_cache = save_copy_from_cache
1490 SCons.CacheDir.CacheDir.copy_to_cache = save_copy_to_cache
1492 def test_concat(self) -> None:
1493 """Test _concat()"""
1494 e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
1495 s = e1.subst
1496 x = s("${_concat('', '', '', __env__)}")
1497 assert x == '', x
1498 x = s("${_concat('', [], '', __env__)}")
1499 assert x == '', x
1500 x = s("${_concat(PRE, '', SUF, __env__)}")
1501 assert x == '', x
1502 x = s("${_concat(PRE, STR, SUF, __env__)}")
1503 assert x == 'prea bsuf', x
1504 x = s("${_concat(PRE, LIST, SUF, __env__)}")
1505 assert x == 'preasuf prebsuf', x
1506 x = s("${_concat(PRE, LIST, SUF, __env__,affect_signature=False)}", raw=True)
1507 assert x == '$( preasuf prebsuf $)', x
1510 def test_concat_nested(self) -> None:
1511 """Test _concat() on a nested substitution strings."""
1512 e = self.TestEnvironment(PRE='pre', SUF='suf',
1513 L1=['a', 'b'],
1514 L2=['c', 'd'],
1515 L3=['$L2'])
1516 x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)')
1517 assert x == 'preasuf prebsuf', x
1518 e.AppendUnique(L1 = ['$L2'])
1519 x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)')
1520 assert x == 'preasuf prebsuf precsuf predsuf', x
1521 e.AppendUnique(L1 = ['$L3'])
1522 x = e.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)')
1523 assert x == 'preasuf prebsuf precsuf predsuf precsuf predsuf', x
1525 def test_gvars(self) -> None:
1526 """Test the Environment gvars() method"""
1527 env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
1528 gvars = env.gvars()
1529 assert gvars['XXX'] == 'x', gvars['XXX']
1530 assert gvars['YYY'] == 'y', gvars['YYY']
1531 assert gvars['ZZZ'] == 'z', gvars['ZZZ']
1533 def test__update(self) -> None:
1534 """Test the _update() method"""
1535 env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z')
1536 assert env['X'] == 'x', env['X']
1537 assert env['Y'] == 'y', env['Y']
1538 assert env['Z'] == 'z', env['Z']
1539 env._update({'X' : 'xxx',
1540 'TARGET' : 't',
1541 'TARGETS' : 'ttt',
1542 'SOURCE' : 's',
1543 'SOURCES' : 'sss',
1544 'Z' : 'zzz'})
1545 assert env['X'] == 'xxx', env['X']
1546 assert env['Y'] == 'y', env['Y']
1547 assert env['Z'] == 'zzz', env['Z']
1548 assert env['TARGET'] == 't', env['TARGET']
1549 assert env['TARGETS'] == 'ttt', env['TARGETS']
1550 assert env['SOURCE'] == 's', env['SOURCE']
1551 assert env['SOURCES'] == 'sss', env['SOURCES']
1553 def test_Append(self) -> None:
1554 """Test appending to construction variables in an Environment
1557 b1 = Environment()['BUILDERS']
1558 b2 = Environment()['BUILDERS']
1559 assert b1 == b2, diff_dict(b1, b2)
1561 cases = [
1562 'a1', 'A1', 'a1A1',
1563 'a2', ['A2'], ['a2', 'A2'],
1564 'a3', UL(['A3']), UL(['a', '3', 'A3']),
1565 'a4', '', 'a4',
1566 'a5', [], ['a5'],
1567 'a6', UL([]), UL(['a', '6']),
1568 'a7', [''], ['a7', ''],
1569 'a8', UL(['']), UL(['a', '8', '']),
1571 ['e1'], 'E1', ['e1', 'E1'],
1572 ['e2'], ['E2'], ['e2', 'E2'],
1573 ['e3'], UL(['E3']), UL(['e3', 'E3']),
1574 ['e4'], '', ['e4'],
1575 ['e5'], [], ['e5'],
1576 ['e6'], UL([]), UL(['e6']),
1577 ['e7'], [''], ['e7', ''],
1578 ['e8'], UL(['']), UL(['e8', '']),
1580 UL(['i1']), 'I1', UL(['i1', 'I', '1']),
1581 UL(['i2']), ['I2'], UL(['i2', 'I2']),
1582 UL(['i3']), UL(['I3']), UL(['i3', 'I3']),
1583 UL(['i4']), '', UL(['i4']),
1584 UL(['i5']), [], UL(['i5']),
1585 UL(['i6']), UL([]), UL(['i6']),
1586 UL(['i7']), [''], UL(['i7', '']),
1587 UL(['i8']), UL(['']), UL(['i8', '']),
1589 {'d1':1}, 'D1', {'d1':1, 'D1':None},
1590 {'d2':1}, ['D2'], {'d2':1, 'D2':None},
1591 {'d3':1}, UL(['D3']), {'d3':1, 'D3':None},
1592 {'d4':1}, {'D4':1}, {'d4':1, 'D4':1},
1593 {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}),
1595 UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}),
1596 UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}),
1597 UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}),
1598 UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}),
1599 UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
1601 '', 'M1', 'M1',
1602 '', ['M2'], ['M2'],
1603 '', UL(['M3']), UL(['M3']),
1604 '', '', '',
1605 '', [], [],
1606 '', UL([]), UL([]),
1607 '', [''], [''],
1608 '', UL(['']), UL(['']),
1610 [], 'N1', ['N1'],
1611 [], ['N2'], ['N2'],
1612 [], UL(['N3']), UL(['N3']),
1613 [], '', [],
1614 [], [], [],
1615 [], UL([]), UL([]),
1616 [], [''], [''],
1617 [], UL(['']), UL(['']),
1619 UL([]), 'O1', ['O', '1'],
1620 UL([]), ['O2'], ['O2'],
1621 UL([]), UL(['O3']), UL(['O3']),
1622 UL([]), '', UL([]),
1623 UL([]), [], UL([]),
1624 UL([]), UL([]), UL([]),
1625 UL([]), [''], UL(['']),
1626 UL([]), UL(['']), UL(['']),
1628 [''], 'P1', ['', 'P1'],
1629 [''], ['P2'], ['', 'P2'],
1630 [''], UL(['P3']), UL(['', 'P3']),
1631 [''], '', [''],
1632 [''], [], [''],
1633 [''], UL([]), UL(['']),
1634 [''], [''], ['', ''],
1635 [''], UL(['']), UL(['', '']),
1637 UL(['']), 'Q1', ['', 'Q', '1'],
1638 UL(['']), ['Q2'], ['', 'Q2'],
1639 UL(['']), UL(['Q3']), UL(['', 'Q3']),
1640 UL(['']), '', UL(['']),
1641 UL(['']), [], UL(['']),
1642 UL(['']), UL([]), UL(['']),
1643 UL(['']), [''], UL(['', '']),
1644 UL(['']), UL(['']), UL(['', '']),
1647 env = Environment()
1648 failed = 0
1649 while cases:
1650 input, append, expect = cases[:3]
1651 env['XXX'] = copy.copy(input)
1652 try:
1653 env.Append(XXX = append)
1654 except Exception as e:
1655 if failed == 0: print()
1656 print(" %s Append %s exception: %s" % \
1657 (repr(input), repr(append), e))
1658 failed = failed + 1
1659 else:
1660 result = env['XXX']
1661 if result != expect:
1662 if failed == 0: print()
1663 print(" %s Append %s => %s did not match %s" % \
1664 (repr(input), repr(append), repr(result), repr(expect)))
1665 failed = failed + 1
1666 del cases[:3]
1667 assert failed == 0, "%d Append() cases failed" % failed
1669 env['UL'] = UL(['foo'])
1670 env.Append(UL = 'bar')
1671 result = env['UL']
1672 assert isinstance(result, UL), repr(result)
1673 assert result == ['foo', 'b', 'a', 'r'], result
1675 env['CLVar'] = CLVar(['foo'])
1676 env.Append(CLVar = 'bar')
1677 result = env['CLVar']
1678 assert isinstance(result, CLVar), repr(result)
1679 assert result == ['foo', 'bar'], result
1681 class C:
1682 def __init__(self, name) -> None:
1683 self.name = name
1684 def __str__(self) -> str:
1685 return self.name
1686 def __eq__(self, other):
1687 raise Exception("should not compare")
1689 ccc = C('ccc')
1691 env2 = self.TestEnvironment(CCC1 = ['c1'], CCC2 = ccc)
1692 env2.Append(CCC1 = ccc, CCC2 = ['c2'])
1693 assert env2['CCC1'][0] == 'c1', env2['CCC1']
1694 assert env2['CCC1'][1] is ccc, env2['CCC1']
1695 assert env2['CCC2'][0] is ccc, env2['CCC2']
1696 assert env2['CCC2'][1] == 'c2', env2['CCC2']
1698 env3 = self.TestEnvironment(X = {'x1' : 7})
1699 env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
1700 assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
1701 assert env3['Y'] == {'y1': 10}, env3['Y']
1703 z1 = Builder()
1704 z2 = Builder()
1705 env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
1706 env4.Append(BUILDERS = {'z2' : z2})
1707 assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
1708 assert hasattr(env4, 'z1')
1709 assert hasattr(env4, 'z2')
1711 def test_AppendENVPath(self) -> None:
1712 """Test appending to an ENV path."""
1713 env1 = self.TestEnvironment(
1714 ENV={'PATH': r'C:\dir\num\one;C:\dir\num\two'},
1715 MYENV={'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'},
1717 # have to include the pathsep here so that the test will work on UNIX too.
1718 env1.AppendENVPath('PATH', r'C:\dir\num\two', sep=';')
1719 env1.AppendENVPath('PATH', r'C:\dir\num\three', sep=';')
1720 env1.AppendENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';')
1721 assert (
1722 env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three'
1723 ), env1['ENV']['PATH']
1725 env1.AppendENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';')
1726 env1.AppendENVPath(
1727 'MYPATH', r'C:\mydir\num\one', 'MYENV', sep=';', delete_existing=1
1729 # this should do nothing since delete_existing is 0
1730 assert (
1731 env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one'
1732 ), env1['MYENV']['MYPATH']
1734 test = TestCmd.TestCmd(workdir='')
1735 test.subdir('sub1', 'sub2')
1736 p = env1['ENV']['PATH']
1737 env1.AppendENVPath('PATH', '#sub1', sep=';')
1738 env1.AppendENVPath('PATH', env1.fs.Dir('sub2'), sep=';')
1739 assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH']
1741 def test_AppendUnique(self) -> None:
1742 """Test appending to unique values to construction variables
1744 This strips values that are already present when lists are
1745 involved."""
1746 env = self.TestEnvironment(AAA1 = 'a1',
1747 AAA2 = 'a2',
1748 AAA3 = 'a3',
1749 AAA4 = 'a4',
1750 AAA5 = 'a5',
1751 BBB1 = ['b1'],
1752 BBB2 = ['b2'],
1753 BBB3 = ['b3'],
1754 BBB4 = ['b4'],
1755 BBB5 = ['b5'],
1756 CCC1 = '',
1757 CCC2 = '',
1758 DDD1 = ['a', 'b', 'c'])
1759 env['LL1'] = [env.Literal('a literal'), env.Literal('b literal')]
1760 env['LL2'] = [env.Literal('c literal'), env.Literal('b literal')]
1761 env.AppendUnique(AAA1 = 'a1',
1762 AAA2 = ['a2'],
1763 AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
1764 AAA4 = 'a4.new',
1765 AAA5 = ['a5.new'],
1766 BBB1 = 'b1',
1767 BBB2 = ['b2'],
1768 BBB3 = ['b3', 'c', 'd', 'c', 'b3'],
1769 BBB4 = 'b4.new',
1770 BBB5 = ['b5.new'],
1771 CCC1 = 'c1',
1772 CCC2 = ['c2'],
1773 DDD1 = 'b',
1774 LL1 = env.Literal('a literal'),
1775 LL2 = env.Literal('a literal'))
1777 assert env['AAA1'] == 'a1a1', env['AAA1']
1778 assert env['AAA2'] == ['a2'], env['AAA2']
1779 assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3']
1780 assert env['AAA4'] == 'a4a4.new', env['AAA4']
1781 assert env['AAA5'] == ['a5', 'a5.new'], env['AAA5']
1782 assert env['BBB1'] == ['b1'], env['BBB1']
1783 assert env['BBB2'] == ['b2'], env['BBB2']
1784 assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3']
1785 assert env['BBB4'] == ['b4', 'b4.new'], env['BBB4']
1786 assert env['BBB5'] == ['b5', 'b5.new'], env['BBB5']
1787 assert env['CCC1'] == 'c1', env['CCC1']
1788 assert env['CCC2'] == ['c2'], env['CCC2']
1789 assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
1790 assert env['LL1'] == [env.Literal('a literal'), env.Literal('b literal')], env['LL1']
1791 assert env['LL2'] == [env.Literal('c literal'), env.Literal('b literal'), env.Literal('a literal')], [str(x) for x in env['LL2']]
1793 env.AppendUnique(DDD1 = 'b', delete_existing=1)
1794 assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end
1795 env.AppendUnique(DDD1 = ['a','b'], delete_existing=1)
1796 assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
1797 env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
1798 assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
1800 env['CLVar'] = CLVar([])
1801 env.AppendUnique(CLVar = 'bar')
1802 result = env['CLVar']
1803 assert isinstance(result, CLVar), repr(result)
1804 assert result == ['bar'], result
1806 env['CLVar'] = CLVar(['abc'])
1807 env.AppendUnique(CLVar = 'bar')
1808 result = env['CLVar']
1809 assert isinstance(result, CLVar), repr(result)
1810 assert result == ['abc', 'bar'], result
1812 env['CLVar'] = CLVar(['bar'])
1813 env.AppendUnique(CLVar = 'bar')
1814 result = env['CLVar']
1815 assert isinstance(result, CLVar), repr(result)
1816 assert result == ['bar'], result
1818 def test_Clone(self) -> None:
1819 """Test construction environment cloning.
1821 The clone should compare equal if there are no overrides.
1822 Update the clone independently afterwards and check that
1823 the original remains intact (that is, no dangling
1824 references point to objects in the copied environment).
1825 Clone the original with some construction variable
1826 updates and check that the original remains intact
1827 and the copy has the updated values.
1829 with self.subTest():
1830 env1 = self.TestEnvironment(XXX='x', YYY='y')
1831 env2 = env1.Clone()
1832 env1copy = env1.Clone()
1833 self.assertEqual(env1copy, env1)
1834 self.assertEqual(env2, env1)
1835 env2.Replace(YYY = 'yyy')
1836 self.assertNotEqual(env1, env2)
1837 self.assertEqual(env1, env1copy)
1839 env3 = env1.Clone(XXX='x3', ZZZ='z3')
1840 self.assertNotEqual(env3, env1)
1841 self.assertEqual(env3.Dictionary('XXX'), 'x3')
1842 self.assertEqual(env1.Dictionary('XXX'), 'x')
1843 self.assertEqual(env3.Dictionary('YYY'), 'y')
1844 self.assertEqual(env3.Dictionary('ZZZ'), 'z3')
1845 self.assertRaises(KeyError, env1.Dictionary, 'ZZZ') # leak test
1846 self.assertEqual(env1, env1copy)
1848 # Ensure that lists and dictionaries are deep copied, but not instances
1849 with self.subTest():
1851 class TestA:
1852 pass
1854 env1 = self.TestEnvironment(
1855 XXX=TestA(),
1856 YYY=[1, 2, 3],
1857 ZZZ={1: 2, 3: 4}
1859 env2 = env1.Clone()
1860 env2.Dictionary('YYY').append(4)
1861 env2.Dictionary('ZZZ')[5] = 6
1862 self.assertIs(env1.Dictionary('XXX'), env2.Dictionary('XXX'))
1863 self.assertIn(4, env2.Dictionary('YYY'))
1864 self.assertNotIn(4, env1.Dictionary('YYY'))
1865 self.assertIn(5, env2.Dictionary('ZZZ'))
1866 self.assertNotIn(5, env1.Dictionary('ZZZ'))
1868 # We also need to look at the special cases in semi_deepcopy()
1869 # used when cloning - these should not leak to the original either
1870 with self.subTest():
1871 env1 = self.TestEnvironment(
1872 XXX=deque([1, 2, 3]),
1873 YYY=UL([1, 2, 3]),
1874 ZZZ=UD({1: 2, 3: 4}),
1876 env2 = env1.Clone()
1877 env2['XXX'].append(4)
1878 env2['YYY'].append(4)
1879 env2['ZZZ'][5] = 6
1880 self.assertIn(4, env2['XXX'])
1881 self.assertNotIn(4, env1['XXX'])
1882 self.assertIn(4, env2['YYY'])
1883 self.assertNotIn(4, env1['YYY'])
1884 self.assertIn(5, env2['ZZZ'])
1885 self.assertNotIn(5, env1['ZZZ'])
1887 # BUILDERS is special...
1888 with self.subTest():
1889 env1 = self.TestEnvironment(BUILDERS={'b1': Builder()})
1890 assert hasattr(env1, 'b1'), "env1.b1 was not set"
1891 assert env1.b1.object == env1, "b1.object doesn't point to env1"
1892 env2 = env1.Clone(BUILDERS = {'b2' : Builder()})
1893 assert env2 != env1
1894 assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
1895 assert env1.b1.object == env1, "b1.object was changed"
1896 assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
1897 assert hasattr(env2, 'b2'), "env2.b2 was not set"
1898 assert env2.b2.object == env2, "b2.object doesn't point to env2"
1900 # Ensure that specifying new tools in a copied environment works.
1901 with self.subTest():
1903 def foo(env) -> None:
1904 env['FOO'] = 1
1906 def bar(env) -> None:
1907 env['BAR'] = 2
1909 def baz(env) -> None:
1910 env['BAZ'] = 3
1912 env1 = self.TestEnvironment(tools=[foo])
1913 env2 = env1.Clone()
1914 env3 = env1.Clone(tools=[bar, baz])
1916 assert env1.get('FOO') == 1
1917 assert env1.get('BAR') is None
1918 assert env1.get('BAZ') is None
1919 assert env2.get('FOO') == 1
1920 assert env2.get('BAR') is None
1921 assert env2.get('BAZ') is None
1922 assert env3.get('FOO') == 1
1923 assert env3.get('BAR') == 2
1924 assert env3.get('BAZ') == 3
1926 # Ensure that recursive variable substitution when copying
1927 # environments works properly.
1928 with self.subTest():
1929 env1 = self.TestEnvironment(CCFLAGS='-DFOO', XYZ='-DXYZ')
1930 env2 = env1.Clone(
1931 CCFLAGS='$CCFLAGS -DBAR', XYZ=['-DABC', 'x $XYZ y', '-DDEF']
1933 x = env2.get('CCFLAGS')
1934 assert x == '-DFOO -DBAR', x
1935 x = env2.get('XYZ')
1936 assert x == ['-DABC', 'x -DXYZ y', '-DDEF'], x
1938 # Ensure that special properties of a class don't get
1939 # lost on copying.
1940 with self.subTest():
1941 env1 = self.TestEnvironment(FLAGS=CLVar('flag1 flag2'))
1942 x = env1.get('FLAGS')
1943 assert x == ['flag1', 'flag2'], x
1944 env2 = env1.Clone()
1945 env2.Append(FLAGS='flag3 flag4')
1946 x = env2.get('FLAGS')
1947 assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
1948 x = env1.get('FLAGS')
1949 assert x == ['flag1', 'flag2'], x
1951 # Ensure that appending directly to a copied CLVar
1952 # doesn't modify the original.
1953 with self.subTest():
1954 env1 = self.TestEnvironment(FLAGS=CLVar('flag1 flag2'))
1955 x = env1.get('FLAGS')
1956 assert x == ['flag1', 'flag2'], x
1957 env2 = env1.Clone()
1958 env2['FLAGS'] += ['flag3', 'flag4']
1959 x = env2.get('FLAGS')
1960 assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
1961 x = env1.get('FLAGS')
1962 assert x == ['flag1', 'flag2'], x
1964 # Test that the environment stores the toolpath and
1965 # re-uses it for copies.
1966 with self.subTest():
1967 test = TestCmd.TestCmd(workdir='')
1969 test.write('xxx.py', """\
1970 def exists(env):
1971 return True
1972 def generate(env):
1973 env['XXX'] = 'one'
1974 """)
1976 test.write('yyy.py', """\
1977 def exists(env):
1978 return True
1979 def generate(env):
1980 env['YYY'] = 'two'
1981 """)
1983 env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
1984 assert env['XXX'] == 'one', env['XXX']
1985 env = env.Clone(tools=['yyy'])
1986 assert env['YYY'] == 'two', env['YYY']
1988 # Test that
1989 with self.subTest():
1990 real_value = [4]
1992 def my_tool(env, rv=real_value) -> None:
1993 assert env['KEY_THAT_I_WANT'] == rv[0]
1994 env['KEY_THAT_I_WANT'] = rv[0] + 1
1996 env = self.TestEnvironment()
1998 real_value[0] = 5
1999 env = env.Clone(KEY_THAT_I_WANT=5, tools=[my_tool])
2000 assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
2002 real_value[0] = 6
2003 env = env.Clone(KEY_THAT_I_WANT=6, tools=[my_tool])
2004 assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
2006 # test for pull request #150
2007 with self.subTest():
2008 env = self.TestEnvironment()
2009 env._dict.pop('BUILDERS')
2010 assert ('BUILDERS' in env) is False
2011 env2 = env.Clone()
2013 def test_Detect(self) -> None:
2014 """Test Detect()ing tools"""
2015 test = TestCmd.TestCmd(workdir = '')
2016 test.subdir('sub1', 'sub2')
2017 sub1 = test.workpath('sub1')
2018 sub2 = test.workpath('sub2')
2020 if sys.platform == 'win32':
2021 test.write(['sub1', 'xxx'], "sub1/xxx\n")
2022 test.write(['sub2', 'xxx'], "sub2/xxx\n")
2024 env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
2026 x = env.Detect('xxx.exe')
2027 assert x is None, x
2029 test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
2031 env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
2033 x = env.Detect('xxx.exe')
2034 assert x == 'xxx.exe', x
2036 test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
2038 x = env.Detect('xxx.exe')
2039 assert x == 'xxx.exe', x
2041 else:
2042 test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
2043 test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
2045 env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
2047 x = env.Detect('xxx.exe')
2048 assert x is None, x
2050 sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
2051 os.chmod(sub2_xxx_exe, 0o755)
2053 env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
2055 x = env.Detect('xxx.exe')
2056 assert x == 'xxx.exe', x
2058 sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
2059 os.chmod(sub1_xxx_exe, 0o755)
2061 x = env.Detect('xxx.exe')
2062 assert x == 'xxx.exe', x
2064 env = self.TestEnvironment(ENV = { 'PATH' : [] })
2065 x = env.Detect('xxx.exe')
2066 assert x is None, x
2068 def test_Dictionary(self) -> None:
2069 """Test retrieval of known construction variables
2071 Fetch them from the Dictionary and check for well-known
2072 defaults that get inserted.
2074 env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
2075 assert env.Dictionary('XXX') == 'x'
2076 assert env.Dictionary('YYY') == 'y'
2077 assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
2078 xxx, zzz = env.Dictionary('XXX', 'ZZZ')
2079 assert xxx == 'x'
2080 assert zzz == 'z'
2081 assert 'BUILDERS' in env.Dictionary()
2082 assert 'CC' in env.Dictionary()
2083 assert 'CCFLAGS' in env.Dictionary()
2084 assert 'ENV' in env.Dictionary()
2086 assert env['XXX'] == 'x'
2087 env['XXX'] = 'foo'
2088 assert env.Dictionary('XXX') == 'foo'
2089 del env['XXX']
2090 assert 'XXX' not in env.Dictionary()
2092 def test_FindIxes(self) -> None:
2093 """Test FindIxes()"""
2094 env = self.TestEnvironment(LIBPREFIX='lib',
2095 LIBSUFFIX='.a',
2096 SHLIBPREFIX='lib',
2097 SHLIBSUFFIX='.so',
2098 PREFIX='pre',
2099 SUFFIX='post')
2101 paths = [os.path.join('dir', 'libfoo.a'),
2102 os.path.join('dir', 'libfoo.so')]
2104 assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
2105 assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
2106 assert None is env.FindIxes(paths, 'PREFIX', 'POST')
2108 paths = ['libfoo.a', 'prefoopost']
2110 assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
2111 assert None is env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
2112 assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
2114 def test_ParseConfig(self) -> None:
2115 """Test the ParseConfig() method"""
2116 env = self.TestEnvironment(COMMAND='command',
2117 ASFLAGS='assembler',
2118 CCFLAGS=[''],
2119 CPPDEFINES=[],
2120 CPPFLAGS=[''],
2121 CPPPATH='string',
2122 FRAMEWORKPATH=[],
2123 FRAMEWORKS=[],
2124 LIBPATH=['list'],
2125 LIBS='',
2126 LINKFLAGS=[''],
2127 RPATH=[])
2129 orig_backtick = env.backtick
2130 class my_backtick:
2131 """mocked backtick routine so command is not actually issued.
2133 Just returns the string it was given.
2135 def __init__(self, save_command, output) -> None:
2136 self.save_command = save_command
2137 self.output = output
2138 def __call__(self, command):
2139 self.save_command.append(command)
2140 return self.output
2142 try:
2143 save_command = []
2144 env.backtick = my_backtick(save_command,
2145 "-I/usr/include/fum -I bar -X\n" + \
2146 "-L/usr/fax -L foo -lxxx -l yyy " + \
2147 "-Wa,-as -Wl,-link " + \
2148 "-Wl,-rpath=rpath1 " + \
2149 "-Wl,-R,rpath2 " + \
2150 "-Wl,-Rrpath3 " + \
2151 "-Wp,-cpp abc " + \
2152 "-framework Carbon " + \
2153 "-frameworkdir=fwd1 " + \
2154 "-Ffwd2 " + \
2155 "-F fwd3 " + \
2156 "-pthread " + \
2157 "-fmerge-all-constants " + \
2158 "-mno-cygwin -mwindows " + \
2159 "-arch i386 -isysroot /tmp " + \
2160 "-iquote /usr/include/foo1 " + \
2161 "-isystem /usr/include/foo2 " + \
2162 "-idirafter /usr/include/foo3 " + \
2163 "+DD64 " + \
2164 "-DFOO -DBAR=value")
2165 env.ParseConfig("fake $COMMAND")
2166 assert save_command == ['fake command'], save_command
2167 assert env['ASFLAGS'] == ['assembler', '-as'], env['ASFLAGS']
2168 assert env['CCFLAGS'] == ['', '-X', '-Wa,-as',
2169 '-pthread', '-fmerge-all-constants', '-mno-cygwin',
2170 ('-arch', 'i386'), ('-isysroot', '/tmp'),
2171 ('-iquote', '/usr/include/foo1'),
2172 ('-isystem', '/usr/include/foo2'),
2173 ('-idirafter', '/usr/include/foo3'),
2174 '+DD64'], env['CCFLAGS']
2175 self.assertEqual(list(env['CPPDEFINES']), ['FOO', ['BAR', 'value']])
2176 assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS']
2177 assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
2178 assert env['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env['FRAMEWORKPATH']
2179 assert env['FRAMEWORKS'] == ['Carbon'], env['FRAMEWORKS']
2180 assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH']
2181 assert env['LIBS'] == ['xxx', 'yyy', env.File('abc')], env['LIBS']
2182 assert env['LINKFLAGS'] == ['', '-Wl,-link', '-pthread',
2183 '-fmerge-all-constants',
2184 '-mno-cygwin', '-mwindows',
2185 ('-arch', 'i386'),
2186 ('-isysroot', '/tmp'),
2187 '+DD64'], env['LINKFLAGS']
2188 assert env['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env['RPATH']
2190 env.backtick = my_backtick([], "-Ibar")
2191 env.ParseConfig("fake2")
2192 assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
2193 env.ParseConfig("fake2", unique=0)
2194 assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env['CPPPATH']
2195 finally:
2196 env.backtick = orig_backtick
2198 # check that we can pass our own function,
2199 # and that it works for both values of unique
2201 def my_function(myenv, flags, unique: bool=True) -> None:
2202 import json
2204 args = json.loads(flags)
2205 if unique:
2206 myenv.AppendUnique(**args)
2207 else:
2208 myenv.Append(**args)
2210 json_str = '{"LIBS": ["yyy", "xxx", "yyy"]}'
2212 env = Environment(LIBS=['xxx'])
2213 env2 = env.Clone()
2214 env.backtick = my_backtick([], json_str)
2215 env2.backtick = my_backtick([], json_str)
2217 env.ParseConfig("foo", my_function)
2218 assert env['LIBS'] == ['xxx', 'yyy'], env['LIBS']
2220 env2.ParseConfig("foo2", my_function, unique=False)
2221 assert env2['LIBS'] == ['xxx', 'yyy', 'xxx', 'yyy'], env2['LIBS']
2224 def test_ParseDepends(self) -> None:
2225 """Test the ParseDepends() method"""
2226 test = TestCmd.TestCmd(workdir = '')
2228 test.write('single', """
2229 #file: dependency
2231 f0: \
2232 d1 \
2233 d2 \
2234 d3 \
2236 """)
2238 test.write('multiple', """
2239 f1: foo
2240 f2 f3: bar
2241 f4: abc def
2242 #file: dependency
2243 f5: \
2244 ghi \
2245 jkl \
2246 mno \
2247 """)
2249 env = self.TestEnvironment(SINGLE = test.workpath('single'))
2251 tlist = []
2252 dlist = []
2253 def my_depends(target, dependency, tlist=tlist, dlist=dlist) -> None:
2254 tlist.extend(target)
2255 dlist.extend(dependency)
2257 env.Depends = my_depends
2259 env.ParseDepends(test.workpath('does_not_exist'))
2261 exc_caught = None
2262 try:
2263 env.ParseDepends(test.workpath('does_not_exist'), must_exist=1)
2264 except IOError:
2265 exc_caught = 1
2266 assert exc_caught, "did not catch expected IOError"
2268 del tlist[:]
2269 del dlist[:]
2271 env.ParseDepends('$SINGLE', only_one=1)
2272 t = list(map(str, tlist))
2273 d = list(map(str, dlist))
2274 assert t == ['f0'], t
2275 assert d == ['d1', 'd2', 'd3'], d
2277 del tlist[:]
2278 del dlist[:]
2280 env.ParseDepends(test.workpath('multiple'))
2281 t = list(map(str, tlist))
2282 d = list(map(str, dlist))
2283 assert t == ['f1', 'f2', 'f3', 'f4', 'f5'], t
2284 assert d == ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d
2286 exc_caught = None
2287 try:
2288 env.ParseDepends(test.workpath('multiple'), only_one=1)
2289 except SCons.Errors.UserError:
2290 exc_caught = 1
2291 assert exc_caught, "did not catch expected UserError"
2293 def test_Platform(self) -> None:
2294 """Test the Platform() method"""
2295 env = self.TestEnvironment(WIN32='win32', NONE='no-such-platform')
2297 exc_caught = None
2298 try:
2299 env.Platform('does_not_exist')
2300 except SCons.Errors.UserError:
2301 exc_caught = 1
2302 assert exc_caught, "did not catch expected UserError"
2304 exc_caught = None
2305 try:
2306 env.Platform('$NONE')
2307 except SCons.Errors.UserError:
2308 exc_caught = 1
2309 assert exc_caught, "did not catch expected UserError"
2311 env.Platform('posix')
2312 assert env['OBJSUFFIX'] == '.o', env['OBJSUFFIX']
2314 env.Platform('$WIN32')
2315 assert env['OBJSUFFIX'] == '.obj', env['OBJSUFFIX']
2317 def test_Prepend(self) -> None:
2318 """Test prepending to construction variables in an Environment
2320 cases = [
2321 'a1', 'A1', 'A1a1',
2322 'a2', ['A2'], ['A2', 'a2'],
2323 'a3', UL(['A3']), UL(['A3', 'a', '3']),
2324 'a4', '', 'a4',
2325 'a5', [], ['a5'],
2326 'a6', UL([]), UL(['a', '6']),
2327 'a7', [''], ['', 'a7'],
2328 'a8', UL(['']), UL(['', 'a', '8']),
2330 ['e1'], 'E1', ['E1', 'e1'],
2331 ['e2'], ['E2'], ['E2', 'e2'],
2332 ['e3'], UL(['E3']), UL(['E3', 'e3']),
2333 ['e4'], '', ['e4'],
2334 ['e5'], [], ['e5'],
2335 ['e6'], UL([]), UL(['e6']),
2336 ['e7'], [''], ['', 'e7'],
2337 ['e8'], UL(['']), UL(['', 'e8']),
2339 UL(['i1']), 'I1', UL(['I', '1', 'i1']),
2340 UL(['i2']), ['I2'], UL(['I2', 'i2']),
2341 UL(['i3']), UL(['I3']), UL(['I3', 'i3']),
2342 UL(['i4']), '', UL(['i4']),
2343 UL(['i5']), [], UL(['i5']),
2344 UL(['i6']), UL([]), UL(['i6']),
2345 UL(['i7']), [''], UL(['', 'i7']),
2346 UL(['i8']), UL(['']), UL(['', 'i8']),
2348 {'d1':1}, 'D1', {'d1':1, 'D1':None},
2349 {'d2':1}, ['D2'], {'d2':1, 'D2':None},
2350 {'d3':1}, UL(['D3']), {'d3':1, 'D3':None},
2351 {'d4':1}, {'D4':1}, {'d4':1, 'D4':1},
2352 {'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1}),
2354 UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None}),
2355 UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None}),
2356 UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None}),
2357 UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1}),
2358 UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
2360 '', 'M1', 'M1',
2361 '', ['M2'], ['M2'],
2362 '', UL(['M3']), UL(['M3']),
2363 '', '', '',
2364 '', [], [],
2365 '', UL([]), UL([]),
2366 '', [''], [''],
2367 '', UL(['']), UL(['']),
2369 [], 'N1', ['N1'],
2370 [], ['N2'], ['N2'],
2371 [], UL(['N3']), UL(['N3']),
2372 [], '', [],
2373 [], [], [],
2374 [], UL([]), UL([]),
2375 [], [''], [''],
2376 [], UL(['']), UL(['']),
2378 UL([]), 'O1', UL(['O', '1']),
2379 UL([]), ['O2'], UL(['O2']),
2380 UL([]), UL(['O3']), UL(['O3']),
2381 UL([]), '', UL([]),
2382 UL([]), [], UL([]),
2383 UL([]), UL([]), UL([]),
2384 UL([]), [''], UL(['']),
2385 UL([]), UL(['']), UL(['']),
2387 [''], 'P1', ['P1', ''],
2388 [''], ['P2'], ['P2', ''],
2389 [''], UL(['P3']), UL(['P3', '']),
2390 [''], '', [''],
2391 [''], [], [''],
2392 [''], UL([]), UL(['']),
2393 [''], [''], ['', ''],
2394 [''], UL(['']), UL(['', '']),
2396 UL(['']), 'Q1', UL(['Q', '1', '']),
2397 UL(['']), ['Q2'], UL(['Q2', '']),
2398 UL(['']), UL(['Q3']), UL(['Q3', '']),
2399 UL(['']), '', UL(['']),
2400 UL(['']), [], UL(['']),
2401 UL(['']), UL([]), UL(['']),
2402 UL(['']), [''], UL(['', '']),
2403 UL(['']), UL(['']), UL(['', '']),
2406 env = Environment()
2407 failed = 0
2408 while cases:
2409 input, prepend, expect = cases[:3]
2410 env['XXX'] = copy.copy(input)
2411 try:
2412 env.Prepend(XXX = prepend)
2413 except Exception as e:
2414 if failed == 0: print()
2415 print(" %s Prepend %s exception: %s" % \
2416 (repr(input), repr(prepend), e))
2417 failed = failed + 1
2418 else:
2419 result = env['XXX']
2420 if result != expect:
2421 if failed == 0: print()
2422 print(" %s Prepend %s => %s did not match %s" % \
2423 (repr(input), repr(prepend), repr(result), repr(expect)))
2424 failed = failed + 1
2425 del cases[:3]
2426 assert failed == 0, "%d Prepend() cases failed" % failed
2428 env['UL'] = UL(['foo'])
2429 env.Prepend(UL = 'bar')
2430 result = env['UL']
2431 assert isinstance(result, UL), repr(result)
2432 assert result == ['b', 'a', 'r', 'foo'], result
2434 env['CLVar'] = CLVar(['foo'])
2435 env.Prepend(CLVar = 'bar')
2436 result = env['CLVar']
2437 assert isinstance(result, CLVar), repr(result)
2438 assert result == ['bar', 'foo'], result
2440 env3 = self.TestEnvironment(X = {'x1' : 7})
2441 env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
2442 assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
2443 assert env3['Y'] == {'y1': 10}, env3['Y']
2445 z1 = Builder()
2446 z2 = Builder()
2447 env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
2448 env4.Prepend(BUILDERS = {'z2' : z2})
2449 assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
2450 assert hasattr(env4, 'z1')
2451 assert hasattr(env4, 'z2')
2453 def test_PrependENVPath(self) -> None:
2454 """Test prepending to an ENV path."""
2455 env1 = self.TestEnvironment(
2456 ENV={'PATH': r'C:\dir\num\one;C:\dir\num\two'},
2457 MYENV={'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'},
2459 # have to include the pathsep here so that the test will work on UNIX too.
2460 env1.PrependENVPath('PATH', r'C:\dir\num\two', sep=';')
2461 env1.PrependENVPath('PATH', r'C:\dir\num\three', sep=';')
2462 assert (
2463 env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one'
2464 ), env1['ENV']['PATH']
2466 env1.PrependENVPath('MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';')
2467 env1.PrependENVPath('MYPATH', r'C:\mydir\num\one', 'MYENV', sep=';')
2468 # this should do nothing since delete_existing is 0
2469 env1.PrependENVPath(
2470 'MYPATH', r'C:\mydir\num\three', 'MYENV', sep=';', delete_existing=0
2472 assert (
2473 env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two'
2474 ), env1['MYENV']['MYPATH']
2476 test = TestCmd.TestCmd(workdir='')
2477 test.subdir('sub1', 'sub2')
2478 p = env1['ENV']['PATH']
2479 env1.PrependENVPath('PATH', '#sub1', sep=';')
2480 env1.PrependENVPath('PATH', env1.fs.Dir('sub2'), sep=';')
2481 assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH']
2483 def test_PrependUnique(self) -> None:
2484 """Test prepending unique values to construction variables
2486 This strips values that are already present when lists are
2487 involved."""
2488 env = self.TestEnvironment(AAA1 = 'a1',
2489 AAA2 = 'a2',
2490 AAA3 = 'a3',
2491 AAA4 = 'a4',
2492 AAA5 = 'a5',
2493 BBB1 = ['b1'],
2494 BBB2 = ['b2'],
2495 BBB3 = ['b3'],
2496 BBB4 = ['b4'],
2497 BBB5 = ['b5'],
2498 CCC1 = '',
2499 CCC2 = '',
2500 DDD1 = ['a', 'b', 'c'])
2501 env.PrependUnique(AAA1 = 'a1',
2502 AAA2 = ['a2'],
2503 AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
2504 AAA4 = 'a4.new',
2505 AAA5 = ['a5.new'],
2506 BBB1 = 'b1',
2507 BBB2 = ['b2'],
2508 BBB3 = ['b3', 'b', 'c', 'b3'],
2509 BBB4 = 'b4.new',
2510 BBB5 = ['b5.new'],
2511 CCC1 = 'c1',
2512 CCC2 = ['c2'],
2513 DDD1 = 'b')
2514 assert env['AAA1'] == 'a1a1', env['AAA1']
2515 assert env['AAA2'] == ['a2'], env['AAA2']
2516 assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3']
2517 assert env['AAA4'] == 'a4.newa4', env['AAA4']
2518 assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5']
2519 assert env['BBB1'] == ['b1'], env['BBB1']
2520 assert env['BBB2'] == ['b2'], env['BBB2']
2521 assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3']
2522 assert env['BBB4'] == ['b4.new', 'b4'], env['BBB4']
2523 assert env['BBB5'] == ['b5.new', 'b5'], env['BBB5']
2524 assert env['CCC1'] == 'c1', env['CCC1']
2525 assert env['CCC2'] == ['c2'], env['CCC2']
2526 assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
2528 env.PrependUnique(DDD1 = 'b', delete_existing=1)
2529 assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front
2530 env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
2531 assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
2532 env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
2533 assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1']
2536 env['CLVar'] = CLVar([])
2537 env.PrependUnique(CLVar = 'bar')
2538 result = env['CLVar']
2539 assert isinstance(result, CLVar), repr(result)
2540 assert result == ['bar'], result
2542 env['CLVar'] = CLVar(['abc'])
2543 env.PrependUnique(CLVar = 'bar')
2544 result = env['CLVar']
2545 assert isinstance(result, CLVar), repr(result)
2546 assert result == ['bar', 'abc'], result
2548 env['CLVar'] = CLVar(['bar'])
2549 env.PrependUnique(CLVar = 'bar')
2550 result = env['CLVar']
2551 assert isinstance(result, CLVar), repr(result)
2552 assert result == ['bar'], result
2554 def test_Replace(self) -> None:
2555 """Test replacing construction variables in an Environment
2557 After creation of the Environment, of course.
2559 env1 = self.TestEnvironment(AAA = 'a', BBB = 'b')
2560 env1.Replace(BBB = 'bbb', CCC = 'ccc')
2562 env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
2563 assert env1 == env2, diff_env(env1, env2)
2565 b1 = Builder()
2566 b2 = Builder()
2567 env3 = self.TestEnvironment(BUILDERS = {'b1' : b1})
2568 assert hasattr(env3, 'b1'), "b1 was not set"
2569 env3.Replace(BUILDERS = {'b2' : b2})
2570 assert not hasattr(env3, 'b1'), "b1 was not cleared"
2571 assert hasattr(env3, 'b2'), "b2 was not set"
2573 def test_ReplaceIxes(self) -> None:
2574 """Test ReplaceIxes()"""
2575 env = self.TestEnvironment(LIBPREFIX='lib',
2576 LIBSUFFIX='.a',
2577 SHLIBPREFIX='lib',
2578 SHLIBSUFFIX='.so',
2579 PREFIX='pre',
2580 SUFFIX='post')
2582 assert 'libfoo.a' == env.ReplaceIxes('libfoo.so',
2583 'SHLIBPREFIX', 'SHLIBSUFFIX',
2584 'LIBPREFIX', 'LIBSUFFIX')
2586 assert os.path.join('dir', 'libfoo.a') == env.ReplaceIxes(os.path.join('dir', 'libfoo.so'),
2587 'SHLIBPREFIX', 'SHLIBSUFFIX',
2588 'LIBPREFIX', 'LIBSUFFIX')
2590 assert 'libfoo.a' == env.ReplaceIxes('prefoopost',
2591 'PREFIX', 'SUFFIX',
2592 'LIBPREFIX', 'LIBSUFFIX')
2594 def test_SetDefault(self) -> None:
2595 """Test the SetDefault method"""
2596 env = self.TestEnvironment(tools = [])
2597 env.SetDefault(V1 = 1)
2598 env.SetDefault(V1 = 2)
2599 assert env['V1'] == 1
2600 env['V2'] = 2
2601 env.SetDefault(V2 = 1)
2602 assert env['V2'] == 2
2604 def test_Tool(self) -> None:
2605 """Test the Tool() method"""
2606 env = self.TestEnvironment(LINK='link', NONE='no-such-tool')
2608 exc_caught = None
2609 try:
2610 tool = env.Tool('does_not_exist')
2611 except SCons.Errors.UserError:
2612 exc_caught = 1
2613 else:
2614 assert isinstance(tool, SCons.Tool.Tool)
2615 assert exc_caught, "did not catch expected UserError"
2617 exc_caught = None
2618 try:
2619 env.Tool('$NONE')
2620 except SCons.Errors.UserError:
2621 exc_caught = 1
2622 assert exc_caught, "did not catch expected UserError"
2624 # Use a non-existent toolpath directory just to make sure we
2625 # can call Tool() with the keyword argument.
2626 env.Tool('cc', toolpath=['/no/such/directory'])
2627 assert env['CC'] == 'cc', env['CC']
2629 env.Tool('$LINK')
2630 assert env['LINK'] == '$SMARTLINK', env['LINK']
2632 # Test that the environment stores the toolpath and
2633 # re-uses it for later calls.
2634 test = TestCmd.TestCmd(workdir = '')
2636 test.write('xxx.py', """\
2637 def exists(env):
2638 return True
2639 def generate(env):
2640 env['XXX'] = 'one'
2641 """)
2643 test.write('yyy.py', """\
2644 def exists(env):
2645 return True
2646 def generate(env):
2647 env['YYY'] = 'two'
2648 """)
2650 env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
2651 assert env['XXX'] == 'one', env['XXX']
2652 env.Tool('yyy')
2653 assert env['YYY'] == 'two', env['YYY']
2655 def test_WhereIs(self) -> None:
2656 """Test the WhereIs() method"""
2657 test = TestCmd.TestCmd(workdir = '')
2659 sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
2660 sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
2661 sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
2662 sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
2664 test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
2666 if sys.platform != 'win32':
2667 test.write(sub1_xxx_exe, "\n")
2669 os.mkdir(sub2_xxx_exe)
2671 test.write(sub3_xxx_exe, "\n")
2672 os.chmod(sub3_xxx_exe, 0o777)
2674 test.write(sub4_xxx_exe, "\n")
2675 os.chmod(sub4_xxx_exe, 0o777)
2677 env_path = os.environ['PATH']
2679 pathdirs_1234 = [ test.workpath('sub1'),
2680 test.workpath('sub2'),
2681 test.workpath('sub3'),
2682 test.workpath('sub4'),
2683 ] + env_path.split(os.pathsep)
2685 pathdirs_1243 = [ test.workpath('sub1'),
2686 test.workpath('sub2'),
2687 test.workpath('sub4'),
2688 test.workpath('sub3'),
2689 ] + env_path.split(os.pathsep)
2691 path = os.pathsep.join(pathdirs_1234)
2692 env = self.TestEnvironment(ENV = {'PATH' : path})
2693 wi = env.WhereIs('')
2694 assert wi is None
2695 wi = env.WhereIs('xxx.exe')
2696 assert wi == test.workpath(sub3_xxx_exe), wi
2697 wi = env.WhereIs('xxx.exe', pathdirs_1243)
2698 assert wi == test.workpath(sub4_xxx_exe), wi
2699 wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1243))
2700 assert wi == test.workpath(sub4_xxx_exe), wi
2702 wi = env.WhereIs('xxx.exe', reject = sub3_xxx_exe)
2703 assert wi == test.workpath(sub4_xxx_exe), wi
2704 wi = env.WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
2705 assert wi == test.workpath(sub4_xxx_exe), wi
2707 path = os.pathsep.join(pathdirs_1243)
2708 env = self.TestEnvironment(ENV = {'PATH' : path})
2709 wi = env.WhereIs('xxx.exe')
2710 assert wi == test.workpath(sub4_xxx_exe), wi
2711 wi = env.WhereIs('xxx.exe', pathdirs_1234)
2712 assert wi == test.workpath(sub3_xxx_exe), wi
2713 wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1234))
2714 assert wi == test.workpath(sub3_xxx_exe), wi
2716 if sys.platform == 'win32':
2717 wi = env.WhereIs('xxx', pathext = '')
2718 assert wi is None, wi
2720 wi = env.WhereIs('xxx', pathext = '.exe')
2721 assert wi == test.workpath(sub4_xxx_exe), wi
2723 wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
2724 assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
2726 # Test that we return a normalized path even when
2727 # the path contains forward slashes.
2728 forward_slash = test.workpath('') + '/sub3'
2729 wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE')
2730 assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
2734 def test_Action(self) -> None:
2735 """Test the Action() method"""
2736 import SCons.Action
2738 env = self.TestEnvironment(FOO = 'xyzzy')
2740 a = env.Action('foo')
2741 assert a, a
2742 assert a.__class__ is SCons.Action.CommandAction, a.__class__
2744 a = env.Action('$FOO')
2745 assert a, a
2746 assert a.__class__ is SCons.Action.CommandAction, a.__class__
2748 a = env.Action('$$FOO')
2749 assert a, a
2750 assert a.__class__ is SCons.Action.LazyAction, a.__class__
2752 a = env.Action(['$FOO', 'foo'])
2753 assert a, a
2754 assert a.__class__ is SCons.Action.ListAction, a.__class__
2756 def func(arg) -> None:
2757 pass
2758 a = env.Action(func)
2759 assert a, a
2760 assert a.__class__ is SCons.Action.FunctionAction, a.__class__
2762 def test_AddPostAction(self) -> None:
2763 """Test the AddPostAction() method"""
2764 env = self.TestEnvironment(FOO='fff', BAR='bbb')
2766 n = env.AddPostAction('$FOO', lambda x: x)
2767 assert str(n[0]) == 'fff', n[0]
2769 n = env.AddPostAction(['ggg', '$BAR'], lambda x: x)
2770 assert str(n[0]) == 'ggg', n[0]
2771 assert str(n[1]) == 'bbb', n[1]
2773 def test_AddPreAction(self) -> None:
2774 """Test the AddPreAction() method"""
2775 env = self.TestEnvironment(FOO='fff', BAR='bbb')
2777 n = env.AddPreAction('$FOO', lambda x: x)
2778 assert str(n[0]) == 'fff', n[0]
2780 n = env.AddPreAction(['ggg', '$BAR'], lambda x: x)
2781 assert str(n[0]) == 'ggg', n[0]
2782 assert str(n[1]) == 'bbb', n[1]
2784 def test_Alias(self) -> None:
2785 """Test the Alias() method"""
2786 env = self.TestEnvironment(FOO='kkk', BAR='lll', EA='export_alias')
2788 tgt = env.Alias('new_alias')[0]
2789 assert str(tgt) == 'new_alias', tgt
2790 assert tgt.sources == [], tgt.sources
2791 assert not hasattr(tgt, 'builder'), tgt.builder
2793 tgt = env.Alias('None_alias', None)[0]
2794 assert str(tgt) == 'None_alias', tgt
2795 assert tgt.sources == [], tgt.sources
2797 tgt = env.Alias('empty_list', [])[0]
2798 assert str(tgt) == 'empty_list', tgt
2799 assert tgt.sources == [], tgt.sources
2801 tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
2802 assert str(tgt) == 'export_alias', tgt
2803 assert len(tgt.sources) == 2, list(map(str, tgt.sources))
2804 assert str(tgt.sources[0]) == 'asrc1', list(map(str, tgt.sources))
2805 assert str(tgt.sources[1]) == 'kkk', list(map(str, tgt.sources))
2807 n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0]
2808 assert n is tgt, n
2809 assert len(tgt.sources) == 4, list(map(str, tgt.sources))
2810 assert str(tgt.sources[2]) == 'lll', list(map(str, tgt.sources))
2811 assert str(tgt.sources[3]) == 'asrc4', list(map(str, tgt.sources))
2813 n = env.Alias('$EA', 'asrc5')[0]
2814 assert n is tgt, n
2815 assert len(tgt.sources) == 5, list(map(str, tgt.sources))
2816 assert str(tgt.sources[4]) == 'asrc5', list(map(str, tgt.sources))
2818 t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
2819 assert str(t1) == 't1', t1
2820 assert str(t2) == 't2', t2
2821 assert len(t1.sources) == 2, list(map(str, t1.sources))
2822 assert str(t1.sources[0]) == 'asrc6', list(map(str, t1.sources))
2823 assert str(t1.sources[1]) == 'asrc7', list(map(str, t1.sources))
2824 assert len(t2.sources) == 2, list(map(str, t2.sources))
2825 assert str(t2.sources[0]) == 'asrc6', list(map(str, t2.sources))
2826 assert str(t2.sources[1]) == 'asrc7', list(map(str, t2.sources))
2828 tgt = env.Alias('add', 's1')
2829 tgt = env.Alias('add', 's2')[0]
2830 s = list(map(str, tgt.sources))
2831 assert s == ['s1', 's2'], s
2832 tgt = env.Alias(tgt, 's3')[0]
2833 s = list(map(str, tgt.sources))
2834 assert s == ['s1', 's2', 's3'], s
2836 tgt = env.Alias('act', None, "action1")[0]
2837 s = str(tgt.builder.action)
2838 assert s == "action1", s
2839 tgt = env.Alias('act', None, "action2")[0]
2840 s = str(tgt.builder.action)
2841 assert s == "action1\naction2", s
2842 tgt = env.Alias(tgt, None, "action3")[0]
2843 s = str(tgt.builder.action)
2844 assert s == "action1\naction2\naction3", s
2846 def test_AlwaysBuild(self) -> None:
2847 """Test the AlwaysBuild() method"""
2848 env = self.TestEnvironment(FOO='fff', BAR='bbb')
2849 t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
2850 env.fs.Dir('dir'), env.fs.File('file'))
2851 assert t[0].__class__.__name__ == 'Entry'
2852 assert t[0].get_internal_path() == 'a'
2853 assert t[0].always_build
2854 assert t[1].__class__.__name__ == 'Entry'
2855 assert t[1].get_internal_path() == 'bfff'
2856 assert t[1].always_build
2857 assert t[2].__class__.__name__ == 'Entry'
2858 assert t[2].get_internal_path() == 'c'
2859 assert t[2].always_build
2860 assert t[3].__class__.__name__ == 'Entry'
2861 assert t[3].get_internal_path() == 'd'
2862 assert t[3].always_build
2863 assert t[4].__class__.__name__ == 'Entry'
2864 assert t[4].get_internal_path() == 'bbb'
2865 assert t[4].always_build
2866 assert t[5].__class__.__name__ == 'Dir'
2867 assert t[5].get_internal_path() == 'dir'
2868 assert t[5].always_build
2869 assert t[6].__class__.__name__ == 'File'
2870 assert t[6].get_internal_path() == 'file'
2871 assert t[6].always_build
2873 def test_VariantDir(self) -> None:
2874 """Test the VariantDir() method"""
2875 class MyFS:
2876 def Dir(self, name):
2877 return name
2878 def VariantDir(self, variant_dir, src_dir, duplicate) -> None:
2879 self.variant_dir = variant_dir
2880 self.src_dir = src_dir
2881 self.duplicate = duplicate
2883 env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
2884 env.fs = MyFS()
2886 env.VariantDir('build', 'src')
2887 assert env.fs.variant_dir == 'build', env.fs.variant_dir
2888 assert env.fs.src_dir == 'src', env.fs.src_dir
2889 assert env.fs.duplicate == 1, env.fs.duplicate
2891 env.VariantDir('build${FOO}', '${BAR}src', 0)
2892 assert env.fs.variant_dir == 'buildfff', env.fs.variant_dir
2893 assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
2894 assert env.fs.duplicate == 0, env.fs.duplicate
2896 def test_Builder(self) -> None:
2897 """Test the Builder() method"""
2898 env = self.TestEnvironment(FOO = 'xyzzy')
2900 b = env.Builder(action = 'foo')
2901 assert b is not None, b
2903 b = env.Builder(action = '$FOO')
2904 assert b is not None, b
2906 b = env.Builder(action = ['$FOO', 'foo'])
2907 assert b is not None, b
2909 def func(arg) -> None:
2910 pass
2911 b = env.Builder(action = func)
2912 assert b is not None, b
2913 b = env.Builder(generator = func)
2914 assert b is not None, b
2916 def test_CacheDir(self) -> None:
2917 """Test the CacheDir() method"""
2919 test = TestCmd.TestCmd(workdir = '')
2921 test_cachedir = os.path.join(test.workpath(),'CacheDir')
2922 test_cachedir_config = os.path.join(test_cachedir, 'config')
2923 test_foo = os.path.join(test.workpath(), 'foo-cachedir')
2924 test_foo_config = os.path.join(test_foo,'config')
2925 test_foo1 = os.path.join(test.workpath(), 'foo1-cachedir')
2926 test_foo1_config = os.path.join(test_foo1, 'config')
2928 env = self.TestEnvironment(CD = test_cachedir)
2930 env.CacheDir(test_foo)
2931 assert env._CacheDir_path == test_foo, env._CacheDir_path
2932 assert os.path.isfile(test_foo_config), "No file %s"%test_foo_config
2934 env.CacheDir('$CD')
2935 assert env._CacheDir_path == test_cachedir, env._CacheDir_path
2936 assert os.path.isfile(test_cachedir_config), "No file %s"%test_cachedir_config
2938 # Now verify that -n/-no_exec wil prevent the CacheDir/config from being created
2939 import SCons.Action
2940 SCons.Action.execute_actions = False
2941 env.CacheDir(test_foo1)
2942 assert env._CacheDir_path == test_foo1, env._CacheDir_path
2943 assert not os.path.isfile(test_foo1_config), "No file %s"%test_foo1_config
2946 def test_Clean(self) -> None:
2947 """Test the Clean() method"""
2948 env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
2950 CT = SCons.Environment.CleanTargets
2952 foo = env.arg2nodes('foo')[0]
2953 fff = env.arg2nodes('fff')[0]
2955 t = env.Clean('foo', 'aaa')
2956 l = list(map(str, CT[foo]))
2957 assert l == ['aaa'], l
2959 t = env.Clean(foo, ['$BAR', 'ccc'])
2960 l = list(map(str, CT[foo]))
2961 assert l == ['aaa', 'bbb', 'ccc'], l
2963 eee = env.arg2nodes('eee')[0]
2965 t = env.Clean('$FOO', 'ddd')
2966 l = list(map(str, CT[fff]))
2967 assert l == ['ddd'], l
2968 t = env.Clean(fff, [eee, 'fff'])
2969 l = list(map(str, CT[fff]))
2970 assert l == ['ddd', 'eee', 'fff'], l
2972 def test_Command(self) -> None:
2973 """Test the Command() method."""
2974 env = Environment()
2975 t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
2976 action='buildfoo $target $source')[0]
2977 assert t.builder is not None
2978 assert t.builder.action.__class__.__name__ == 'CommandAction'
2979 assert t.builder.action.cmd_list == 'buildfoo $target $source'
2980 assert 'foo1.in' in [x.get_internal_path() for x in t.sources]
2981 assert 'foo2.in' in [x.get_internal_path() for x in t.sources]
2983 sub = env.fs.Dir('sub')
2984 t = env.Command(target='bar.out', source='sub',
2985 action='buildbar $target $source')[0]
2986 assert 'sub' in [x.get_internal_path() for x in t.sources]
2988 def testFunc(env, target, source) -> int:
2989 assert str(target[0]) == 'foo.out'
2990 srcs = list(map(str, source))
2991 assert 'foo1.in' in srcs and 'foo2.in' in srcs, srcs
2992 return 0
2994 # avoid spurious output from action
2995 act = env.Action(testFunc, cmdstr=None)
2996 t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
2997 action=act)[0]
2998 assert t.builder is not None
2999 assert t.builder.action.__class__.__name__ == 'FunctionAction'
3000 t.build()
3001 assert 'foo1.in' in [x.get_internal_path() for x in t.sources]
3002 assert 'foo2.in' in [x.get_internal_path() for x in t.sources]
3004 x = []
3005 def test2(baz, x=x) -> None:
3006 x.append(baz)
3007 env = self.TestEnvironment(TEST2 = test2)
3008 t = env.Command(target='baz.out', source='baz.in',
3009 action='${TEST2(XYZ)}',
3010 XYZ='magic word')[0]
3011 assert t.builder is not None
3012 t.build()
3013 assert x[0] == 'magic word', x
3015 t = env.Command(target='${X}.out', source='${X}.in',
3016 action = 'foo',
3017 X = 'xxx')[0]
3018 assert str(t) == 'xxx.out', str(t)
3019 assert 'xxx.in' in [x.get_internal_path() for x in t.sources]
3021 env = self.TestEnvironment(source_scanner = 'should_not_find_this')
3022 t = env.Command(target='file.out', source='file.in',
3023 action = 'foo',
3024 source_scanner = 'fake')[0]
3025 assert t.builder.source_scanner == 'fake', t.builder.source_scanner
3027 def test_Configure(self) -> None:
3028 """Test the Configure() method"""
3029 # Configure() will write to a local temporary file.
3030 test = TestCmd.TestCmd(workdir = '')
3031 save = os.getcwd()
3033 try:
3034 os.chdir(test.workpath())
3036 env = self.TestEnvironment(FOO = 'xyzzy')
3038 def func(arg) -> None:
3039 pass
3041 c = env.Configure()
3042 assert c is not None, c
3043 c.Finish()
3045 c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func})
3046 assert c is not None, c
3047 assert hasattr(c, 'foo')
3048 assert hasattr(c, 'xyzzy')
3049 c.Finish()
3050 finally:
3051 os.chdir(save)
3053 def test_Depends(self) -> None:
3054 """Test the explicit Depends method."""
3055 env = self.TestEnvironment(FOO = 'xxx', BAR='yyy')
3056 env.Dir('dir1')
3057 env.Dir('dir2')
3058 env.File('xxx.py')
3059 env.File('yyy.py')
3060 t = env.Depends(target='EnvironmentTest.py',
3061 dependency='Environment.py')[0]
3062 assert t.__class__.__name__ == 'Entry', t.__class__.__name__
3063 assert t.get_internal_path() == 'EnvironmentTest.py'
3064 assert len(t.depends) == 1
3065 d = t.depends[0]
3066 assert d.__class__.__name__ == 'Entry', d.__class__.__name__
3067 assert d.get_internal_path() == 'Environment.py'
3069 t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
3070 assert t.__class__.__name__ == 'File', t.__class__.__name__
3071 assert t.get_internal_path() == 'xxx.py'
3072 assert len(t.depends) == 1
3073 d = t.depends[0]
3074 assert d.__class__.__name__ == 'File', d.__class__.__name__
3075 assert d.get_internal_path() == 'yyy.py'
3077 t = env.Depends(target='dir1', dependency='dir2')[0]
3078 assert t.__class__.__name__ == 'Dir', t.__class__.__name__
3079 assert t.get_internal_path() == 'dir1'
3080 assert len(t.depends) == 1
3081 d = t.depends[0]
3082 assert d.__class__.__name__ == 'Dir', d.__class__.__name__
3083 assert d.get_internal_path() == 'dir2'
3085 def test_Dir(self) -> None:
3086 """Test the Dir() method"""
3087 class MyFS:
3088 def Dir(self, name) -> str:
3089 return 'Dir(%s)' % name
3091 env = self.TestEnvironment(FOO = 'foodir', BAR = 'bardir')
3092 env.fs = MyFS()
3094 d = env.Dir('d')
3095 assert d == 'Dir(d)', d
3097 d = env.Dir('$FOO')
3098 assert d == 'Dir(foodir)', d
3100 d = env.Dir('${BAR}_$BAR')
3101 assert d == 'Dir(bardir_bardir)', d
3103 d = env.Dir(['dir1'])
3104 assert d == ['Dir(dir1)'], d
3106 d = env.Dir(['dir1', 'dir2'])
3107 assert d == ['Dir(dir1)', 'Dir(dir2)'], d
3109 def test_NoClean(self) -> None:
3110 """Test the NoClean() method"""
3111 env = self.TestEnvironment(FOO='ggg', BAR='hhh')
3112 env.Dir('p_hhhb')
3113 env.File('p_d')
3114 t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3116 assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
3117 assert t[0].get_internal_path() == 'p_a'
3118 assert t[0].noclean
3119 assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
3120 assert t[1].get_internal_path() == 'p_hhhb'
3121 assert t[1].noclean
3122 assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
3123 assert t[2].get_internal_path() == 'p_c'
3124 assert t[2].noclean
3125 assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
3126 assert t[3].get_internal_path() == 'p_d'
3127 assert t[3].noclean
3128 assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
3129 assert t[4].get_internal_path() == 'p_ggg'
3130 assert t[4].noclean
3132 def test_Dump(self) -> None:
3133 """Test the Dump() method"""
3135 env = self.TestEnvironment(FOO = 'foo')
3136 assert env.Dump('FOO') == "'foo'", env.Dump('FOO')
3137 assert len(env.Dump()) > 200, env.Dump() # no args version
3139 assert env.Dump('FOO', 'json') == '"foo"' # JSON key version
3140 import json
3141 env_dict = json.loads(env.Dump(format = 'json'))
3142 assert env_dict['FOO'] == 'foo' # full JSON version
3144 try:
3145 env.Dump(format = 'markdown')
3146 except ValueError as e:
3147 assert str(e) == "Unsupported serialization format: markdown."
3148 else:
3149 self.fail("Did not catch expected ValueError.")
3151 def test_Environment(self) -> None:
3152 """Test the Environment() method"""
3153 env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy')
3155 e2 = env.Environment(X = '$FOO', Y = '$BAR')
3156 assert e2['X'] == 'xxx', e2['X']
3157 assert e2['Y'] == 'yyy', e2['Y']
3159 def test_Execute(self) -> None:
3160 """Test the Execute() method"""
3162 class MyAction:
3163 def __init__(self, *args, **kw) -> None:
3164 self.args = args
3165 def __call__(self, target, source, env) -> str:
3166 return "%s executed" % self.args
3168 env = Environment()
3169 env.Action = MyAction
3171 result = env.Execute("foo")
3172 assert result == "foo executed", result
3174 def test_Entry(self) -> None:
3175 """Test the Entry() method"""
3176 class MyFS:
3177 def Entry(self, name) -> str:
3178 return 'Entry(%s)' % name
3180 env = self.TestEnvironment(FOO = 'fooentry', BAR = 'barentry')
3181 env.fs = MyFS()
3183 e = env.Entry('e')
3184 assert e == 'Entry(e)', e
3186 e = env.Entry('$FOO')
3187 assert e == 'Entry(fooentry)', e
3189 e = env.Entry('${BAR}_$BAR')
3190 assert e == 'Entry(barentry_barentry)', e
3192 e = env.Entry(['entry1'])
3193 assert e == ['Entry(entry1)'], e
3195 e = env.Entry(['entry1', 'entry2'])
3196 assert e == ['Entry(entry1)', 'Entry(entry2)'], e
3198 def test_File(self) -> None:
3199 """Test the File() method"""
3200 class MyFS:
3201 def File(self, name) -> str:
3202 return 'File(%s)' % name
3204 env = self.TestEnvironment(FOO = 'foofile', BAR = 'barfile')
3205 env.fs = MyFS()
3207 f = env.File('f')
3208 assert f == 'File(f)', f
3210 f = env.File('$FOO')
3211 assert f == 'File(foofile)', f
3213 f = env.File('${BAR}_$BAR')
3214 assert f == 'File(barfile_barfile)', f
3216 f = env.File(['file1'])
3217 assert f == ['File(file1)'], f
3219 f = env.File(['file1', 'file2'])
3220 assert f == ['File(file1)', 'File(file2)'], f
3222 def test_FindFile(self) -> None:
3223 """Test the FindFile() method"""
3224 env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
3226 r = env.FindFile('foo', ['no_such_directory'])
3227 assert r is None, r
3229 # XXX
3231 def test_Flatten(self) -> None:
3232 """Test the Flatten() method"""
3233 env = Environment()
3234 l = env.Flatten([1])
3235 assert l == [1]
3236 l = env.Flatten([1, [2, [3, [4]]]])
3237 assert l == [1, 2, 3, 4], l
3239 def test_GetBuildPath(self) -> None:
3240 """Test the GetBuildPath() method."""
3241 env = self.TestEnvironment(MAGIC = 'xyzzy')
3243 p = env.GetBuildPath('foo')
3244 assert p == 'foo', p
3246 p = env.GetBuildPath('$MAGIC')
3247 assert p == 'xyzzy', p
3249 def test_Ignore(self) -> None:
3250 """Test the explicit Ignore method."""
3251 env = self.TestEnvironment(FOO='yyy', BAR='zzz')
3252 env.Dir('dir1')
3253 env.Dir('dir2')
3254 env.File('yyyzzz')
3255 env.File('zzzyyy')
3257 t = env.Ignore(target='targ.py', dependency='dep.py')[0]
3258 assert t.__class__.__name__ == 'Entry', t.__class__.__name__
3259 assert t.get_internal_path() == 'targ.py'
3260 assert len(t.ignore) == 1
3261 i = t.ignore[0]
3262 assert i.__class__.__name__ == 'Entry', i.__class__.__name__
3263 assert i.get_internal_path() == 'dep.py'
3265 t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
3266 assert t.__class__.__name__ == 'File', t.__class__.__name__
3267 assert t.get_internal_path() == 'yyyzzz'
3268 assert len(t.ignore) == 1
3269 i = t.ignore[0]
3270 assert i.__class__.__name__ == 'File', i.__class__.__name__
3271 assert i.get_internal_path() == 'zzzyyy'
3273 t = env.Ignore(target='dir1', dependency='dir2')[0]
3274 assert t.__class__.__name__ == 'Dir', t.__class__.__name__
3275 assert t.get_internal_path() == 'dir1'
3276 assert len(t.ignore) == 1
3277 i = t.ignore[0]
3278 assert i.__class__.__name__ == 'Dir', i.__class__.__name__
3279 assert i.get_internal_path() == 'dir2'
3281 def test_Literal(self) -> None:
3282 """Test the Literal() method"""
3283 env = self.TestEnvironment(FOO='fff', BAR='bbb')
3284 list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0]
3285 assert list == ['$FOO', 'bbb'], list
3286 list = env.subst_list(['$FOO', env.Literal('$BAR')])[0]
3287 assert list == ['fff', '$BAR'], list
3289 def test_Local(self) -> None:
3290 """Test the Local() method."""
3291 env = self.TestEnvironment(FOO='lll')
3293 l = env.Local(env.fs.File('fff'))
3294 assert str(l[0]) == 'fff', l[0]
3296 l = env.Local('ggg', '$FOO')
3297 assert str(l[0]) == 'ggg', l[0]
3298 assert str(l[1]) == 'lll', l[1]
3300 def test_Precious(self) -> None:
3301 """Test the Precious() method"""
3302 env = self.TestEnvironment(FOO='ggg', BAR='hhh')
3303 env.Dir('p_hhhb')
3304 env.File('p_d')
3305 t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3307 assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
3308 assert t[0].get_internal_path() == 'p_a'
3309 assert t[0].precious
3310 assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
3311 assert t[1].get_internal_path() == 'p_hhhb'
3312 assert t[1].precious
3313 assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
3314 assert t[2].get_internal_path() == 'p_c'
3315 assert t[2].precious
3316 assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
3317 assert t[3].get_internal_path() == 'p_d'
3318 assert t[3].precious
3319 assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
3320 assert t[4].get_internal_path() == 'p_ggg'
3321 assert t[4].precious
3323 def test_Pseudo(self) -> None:
3324 """Test the Pseudo() method"""
3325 env = self.TestEnvironment(FOO='ggg', BAR='hhh')
3326 env.Dir('p_hhhb')
3327 env.File('p_d')
3328 t = env.Pseudo('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3330 assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
3331 assert t[0].get_internal_path() == 'p_a'
3332 assert t[0].pseudo
3333 assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
3334 assert t[1].get_internal_path() == 'p_hhhb'
3335 assert t[1].pseudo
3336 assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
3337 assert t[2].get_internal_path() == 'p_c'
3338 assert t[2].pseudo
3339 assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
3340 assert t[3].get_internal_path() == 'p_d'
3341 assert t[3].pseudo
3342 assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
3343 assert t[4].get_internal_path() == 'p_ggg'
3344 assert t[4].pseudo
3346 def test_Repository(self) -> None:
3347 """Test the Repository() method."""
3348 class MyFS:
3349 def __init__(self) -> None:
3350 self.list = []
3351 def Repository(self, *dirs) -> None:
3352 self.list.extend(list(dirs))
3353 def Dir(self, name):
3354 return name
3355 env = self.TestEnvironment(FOO='rrr', BAR='sss')
3356 env.fs = MyFS()
3357 env.Repository('/tmp/foo')
3358 env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
3359 expect = ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo']
3360 assert env.fs.list == expect, env.fs.list
3362 def test_Scanner(self) -> None:
3363 """Test the Scanner() method"""
3364 def scan(node, env, target, arg) -> None:
3365 pass
3367 env = self.TestEnvironment(FOO = scan)
3369 s = env.Scanner('foo')
3370 assert s is not None, s
3372 s = env.Scanner(function = 'foo')
3373 assert s is not None, s
3375 if 0:
3376 s = env.Scanner('$FOO')
3377 assert s is not None, s
3379 s = env.Scanner(function = '$FOO')
3380 assert s is not None, s
3382 def test_SConsignFile(self) -> None:
3383 """Test the SConsignFile() method"""
3384 import SCons.SConsign
3386 class MyFS:
3387 SConstruct_dir = os.sep + 'dir'
3389 env = self.TestEnvironment(FOO = 'SConsign',
3390 BAR = os.path.join(os.sep, 'File'))
3391 env.fs = MyFS()
3392 env.Execute = lambda action: None
3394 try:
3395 fnames = []
3396 dbms = []
3397 def capture(name, dbm_module, fnames=fnames, dbms=dbms) -> None:
3398 fnames.append(name)
3399 dbms.append(dbm_module)
3401 save_SConsign_File = SCons.SConsign.File
3402 SCons.SConsign.File = capture
3404 env.SConsignFile('foo')
3405 assert fnames[-1] == os.path.join(os.sep, 'dir', 'foo'), fnames
3406 assert dbms[-1] is None, dbms
3408 env.SConsignFile('$FOO')
3409 assert fnames[-1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames
3410 assert dbms[-1] is None, dbms
3412 env.SConsignFile('/$FOO')
3413 assert fnames[-1] == os.sep + 'SConsign', fnames
3414 assert dbms[-1] is None, dbms
3416 env.SConsignFile(os.sep + '$FOO')
3417 assert fnames[-1] == os.sep + 'SConsign', fnames
3418 assert dbms[-1] is None, dbms
3420 env.SConsignFile('$BAR', 'x')
3421 assert fnames[-1] == os.path.join(os.sep, 'File'), fnames
3422 assert dbms[-1] == 'x', dbms
3424 env.SConsignFile('__$BAR', 7)
3425 assert fnames[-1] == os.path.join(os.sep, 'dir', '__', 'File'), fnames
3426 assert dbms[-1] == 7, dbms
3428 env.SConsignFile()
3429 assert fnames[-1] == os.path.join(os.sep, 'dir', current_sconsign_filename()), fnames
3430 assert dbms[-1] is None, dbms
3432 env.SConsignFile(None)
3433 assert fnames[-1] is None, fnames
3434 assert dbms[-1] is None, dbms
3435 finally:
3436 SCons.SConsign.File = save_SConsign_File
3438 def test_SideEffect(self) -> None:
3439 """Test the SideEffect() method"""
3440 env = self.TestEnvironment(LIB='lll', FOO='fff', BAR='bbb')
3441 env.File('mylll.pdb')
3442 env.Dir('mymmm.pdb')
3444 foo = env.Object('foo.obj', 'foo.cpp')[0]
3445 bar = env.Object('bar.obj', 'bar.cpp')[0]
3446 s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])
3447 assert len(s) == 1, len(s)
3448 s = s[0]
3449 assert s.__class__.__name__ == 'Entry', s.__class__.__name__
3450 assert s.get_internal_path() == 'mylib.pdb'
3451 assert s.side_effect
3452 assert foo.side_effects == [s]
3453 assert bar.side_effects == [s]
3455 fff = env.Object('fff.obj', 'fff.cpp')[0]
3456 bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
3457 s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])
3458 assert len(s) == 1, len(s)
3459 s = s[0]
3460 assert s.__class__.__name__ == 'File', s.__class__.__name__
3461 assert s.get_internal_path() == 'mylll.pdb'
3462 assert s.side_effect
3463 assert fff.side_effects == [s], fff.side_effects
3464 assert bbb.side_effects == [s], bbb.side_effects
3466 ggg = env.Object('ggg.obj', 'ggg.cpp')[0]
3467 ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
3468 s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])
3469 assert len(s) == 1, len(s)
3470 s = s[0]
3471 assert s.__class__.__name__ == 'Dir', s.__class__.__name__
3472 assert s.get_internal_path() == 'mymmm.pdb'
3473 assert s.side_effect
3474 assert ggg.side_effects == [s], ggg.side_effects
3475 assert ccc.side_effects == [s], ccc.side_effects
3477 # Verify that duplicate side effects are not allowed.
3478 before = len(ggg.side_effects)
3479 s = env.SideEffect('mymmm.pdb', ggg)
3480 assert len(s) == 0, len(s)
3481 assert len(ggg.side_effects) == before, len(ggg.side_effects)
3483 def test_Split(self) -> None:
3484 """Test the Split() method"""
3485 env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
3486 s = env.Split("foo bar")
3487 assert s == ["foo", "bar"], s
3488 s = env.Split("$FOO bar")
3489 assert s == ["fff", "bar"], s
3490 s = env.Split(["foo", "bar"])
3491 assert s == ["foo", "bar"], s
3492 s = env.Split(["foo", "${BAR}-bbb"])
3493 assert s == ["foo", "bbb-bbb"], s
3494 s = env.Split("foo")
3495 assert s == ["foo"], s
3496 s = env.Split("$FOO$BAR")
3497 assert s == ["fffbbb"], s
3500 def test_Value(self) -> None:
3501 """Test creating a Value() object
3503 env = Environment()
3504 v1 = env.Value('a')
3505 assert v1.value == 'a', v1.value
3507 value2 = 'a'
3508 v2 = env.Value(value2)
3509 assert v2.value == value2, v2.value
3510 assert v2.value is value2, v2.value
3512 assert v1 is v2
3514 v3 = env.Value('c', 'build-c')
3515 assert v3.value == 'c', v3.value
3517 v4 = env.Value(b'\x00\x0F', name='name')
3518 assert v4.value == b'\x00\x0F', v4.value
3519 assert v4.name == 'name', v4.name
3522 def test_Environment_global_variable(self) -> None:
3523 """Test setting Environment variable to an Environment.Base subclass"""
3524 class MyEnv(SCons.Environment.Base):
3525 def xxx(self, string):
3526 return self.subst(string)
3528 SCons.Environment.Environment = MyEnv
3530 env = SCons.Environment.Environment(FOO = 'foo')
3532 f = env.subst('$FOO')
3533 assert f == 'foo', f
3535 f = env.xxx('$FOO')
3536 assert f == 'foo', f
3538 def test_bad_keywords(self) -> None:
3539 """Test trying to use reserved keywords in an Environment"""
3540 added = []
3542 env = self.TestEnvironment(TARGETS = 'targets',
3543 SOURCES = 'sources',
3544 SOURCE = 'source',
3545 TARGET = 'target',
3546 CHANGED_SOURCES = 'changed_sources',
3547 CHANGED_TARGETS = 'changed_targets',
3548 UNCHANGED_SOURCES = 'unchanged_sources',
3549 UNCHANGED_TARGETS = 'unchanged_targets',
3550 INIT = 'init')
3551 bad_msg = '%s is not reserved, but got omitted; see Environment.construction_var_name_ok'
3552 added.append('INIT')
3553 for x in self.reserved_variables:
3554 assert x not in env, env[x]
3555 for x in added:
3556 assert x in env, bad_msg % x
3558 env.Append(TARGETS = 'targets',
3559 SOURCES = 'sources',
3560 SOURCE = 'source',
3561 TARGET = 'target',
3562 CHANGED_SOURCES = 'changed_sources',
3563 CHANGED_TARGETS = 'changed_targets',
3564 UNCHANGED_SOURCES = 'unchanged_sources',
3565 UNCHANGED_TARGETS = 'unchanged_targets',
3566 APPEND = 'append')
3567 added.append('APPEND')
3568 for x in self.reserved_variables:
3569 assert x not in env, env[x]
3570 for x in added:
3571 assert x in env, bad_msg % x
3573 env.AppendUnique(TARGETS = 'targets',
3574 SOURCES = 'sources',
3575 SOURCE = 'source',
3576 TARGET = 'target',
3577 CHANGED_SOURCES = 'changed_sources',
3578 CHANGED_TARGETS = 'changed_targets',
3579 UNCHANGED_SOURCES = 'unchanged_sources',
3580 UNCHANGED_TARGETS = 'unchanged_targets',
3581 APPENDUNIQUE = 'appendunique')
3582 added.append('APPENDUNIQUE')
3583 for x in self.reserved_variables:
3584 assert x not in env, env[x]
3585 for x in added:
3586 assert x in env, bad_msg % x
3588 env.Prepend(TARGETS = 'targets',
3589 SOURCES = 'sources',
3590 SOURCE = 'source',
3591 TARGET = 'target',
3592 CHANGED_SOURCES = 'changed_sources',
3593 CHANGED_TARGETS = 'changed_targets',
3594 UNCHANGED_SOURCES = 'unchanged_sources',
3595 UNCHANGED_TARGETS = 'unchanged_targets',
3596 PREPEND = 'prepend')
3597 added.append('PREPEND')
3598 for x in self.reserved_variables:
3599 assert x not in env, env[x]
3600 for x in added:
3601 assert x in env, bad_msg % x
3603 env.Prepend(TARGETS = 'targets',
3604 SOURCES = 'sources',
3605 SOURCE = 'source',
3606 TARGET = 'target',
3607 CHANGED_SOURCES = 'changed_sources',
3608 CHANGED_TARGETS = 'changed_targets',
3609 UNCHANGED_SOURCES = 'unchanged_sources',
3610 UNCHANGED_TARGETS = 'unchanged_targets',
3611 PREPENDUNIQUE = 'prependunique')
3612 added.append('PREPENDUNIQUE')
3613 for x in self.reserved_variables:
3614 assert x not in env, env[x]
3615 for x in added:
3616 assert x in env, bad_msg % x
3618 env.Replace(TARGETS = 'targets',
3619 SOURCES = 'sources',
3620 SOURCE = 'source',
3621 TARGET = 'target',
3622 CHANGED_SOURCES = 'changed_sources',
3623 CHANGED_TARGETS = 'changed_targets',
3624 UNCHANGED_SOURCES = 'unchanged_sources',
3625 UNCHANGED_TARGETS = 'unchanged_targets',
3626 REPLACE = 'replace')
3627 added.append('REPLACE')
3628 for x in self.reserved_variables:
3629 assert x not in env, env[x]
3630 for x in added:
3631 assert x in env, bad_msg % x
3633 copy = env.Clone(TARGETS = 'targets',
3634 SOURCES = 'sources',
3635 SOURCE = 'source',
3636 TARGET = 'target',
3637 CHANGED_SOURCES = 'changed_sources',
3638 CHANGED_TARGETS = 'changed_targets',
3639 UNCHANGED_SOURCES = 'unchanged_sources',
3640 UNCHANGED_TARGETS = 'unchanged_targets',
3641 COPY = 'copy')
3642 for x in self.reserved_variables:
3643 assert x not in copy, env[x]
3644 for x in added + ['COPY']:
3645 assert x in copy, bad_msg % x
3647 over = env.Override({'TARGETS' : 'targets',
3648 'SOURCES' : 'sources',
3649 'SOURCE' : 'source',
3650 'TARGET' : 'target',
3651 'CHANGED_SOURCES' : 'changed_sources',
3652 'CHANGED_TARGETS' : 'changed_targets',
3653 'UNCHANGED_SOURCES' : 'unchanged_sources',
3654 'UNCHANGED_TARGETS' : 'unchanged_targets',
3655 'OVERRIDE' : 'override'})
3656 for x in self.reserved_variables:
3657 assert x not in over, over[x]
3658 for x in added + ['OVERRIDE']:
3659 assert x in over, bad_msg % x
3661 def test_parse_flags(self) -> None:
3662 """Test the Base class parse_flags argument"""
3663 # all we have to show is that it gets to MergeFlags internally
3664 env = Environment(tools=[], parse_flags = '-X')
3665 assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
3667 env = Environment(tools=[], CCFLAGS=None, parse_flags = '-Y')
3668 assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
3670 env = Environment(tools=[], CPPDEFINES='FOO', parse_flags='-std=c99 -X -DBAR')
3671 assert env['CFLAGS'] == ['-std=c99'], env['CFLAGS']
3672 assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
3673 self.assertEqual(list(env['CPPDEFINES']), ['FOO', 'BAR'])
3675 def test_clone_parse_flags(self) -> None:
3676 """Test the env.Clone() parse_flags argument"""
3677 # all we have to show is that it gets to MergeFlags internally
3678 env = Environment(tools = [])
3679 env2 = env.Clone(parse_flags = '-X')
3680 assert 'CCFLAGS' not in env
3681 assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3683 env = Environment(tools = [], CCFLAGS=None)
3684 env2 = env.Clone(parse_flags = '-Y')
3685 assert env['CCFLAGS'] is None, env['CCFLAGS']
3686 assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
3688 env = Environment(tools = [], CPPDEFINES = 'FOO')
3689 env2 = env.Clone(parse_flags = '-std=c99 -X -DBAR')
3690 assert 'CFLAGS' not in env
3691 assert env2['CFLAGS'] == ['-std=c99'], env2['CFLAGS']
3692 assert 'CCFLAGS' not in env
3693 assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3694 assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
3695 self.assertEqual(list(env2['CPPDEFINES']), ['FOO','BAR'])
3698 class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
3700 def setUp(self) -> None:
3701 env = Environment()
3702 env._dict = {'XXX' : 'x', 'YYY' : 'y'}
3703 def verify_value(env, key, value, *args, **kwargs) -> None:
3704 """Verifies that key is value on the env this is called with."""
3705 assert env[key] == value
3706 env.AddMethod(verify_value)
3707 env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
3708 env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
3709 self.envs = [ env, env2, env3 ]
3711 def checkpath(self, node, expect):
3712 return str(node) == os.path.normpath(expect)
3714 def test___init__(self) -> None:
3715 """Test OverrideEnvironment initialization"""
3716 env, env2, env3 = self.envs
3717 assert env['XXX'] == 'x', env['XXX']
3718 assert env2['XXX'] == 'x2', env2['XXX']
3719 assert env3['XXX'] == 'x3', env3['XXX']
3720 assert env['YYY'] == 'y', env['YYY']
3721 assert env2['YYY'] == 'y', env2['YYY']
3722 assert env3['YYY'] == 'y3', env3['YYY']
3724 def test___delitem__(self) -> None:
3725 """Test deleting variables from an OverrideEnvironment"""
3726 env, env2, env3 = self.envs
3728 del env3['XXX']
3729 assert 'XXX' not in env, "env has XXX?"
3730 assert 'XXX' not in env2, "env2 has XXX?"
3731 assert 'XXX' not in env3, "env3 has XXX?"
3733 del env3['YYY']
3734 assert 'YYY' not in env, "env has YYY?"
3735 assert 'YYY' not in env2, "env2 has YYY?"
3736 assert 'YYY' not in env3, "env3 has YYY?"
3738 del env3['ZZZ']
3739 assert 'ZZZ' not in env, "env has ZZZ?"
3740 assert 'ZZZ' not in env2, "env2 has ZZZ?"
3741 assert 'ZZZ' not in env3, "env3 has ZZZ?"
3743 def test_get(self) -> None:
3744 """Test the OverrideEnvironment get() method"""
3745 env, env2, env3 = self.envs
3746 assert env.get('XXX') == 'x', env.get('XXX')
3747 assert env2.get('XXX') == 'x2', env2.get('XXX')
3748 assert env3.get('XXX') == 'x3', env3.get('XXX')
3749 assert env.get('YYY') == 'y', env.get('YYY')
3750 assert env2.get('YYY') == 'y', env2.get('YYY')
3751 assert env3.get('YYY') == 'y3', env3.get('YYY')
3752 assert env.get('ZZZ') is None, env.get('ZZZ')
3753 assert env2.get('ZZZ') is None, env2.get('ZZZ')
3754 assert env3.get('ZZZ') == 'z3', env3.get('ZZZ')
3756 def test_contains(self) -> None:
3757 """Test the OverrideEnvironment __contains__() method"""
3758 env, env2, env3 = self.envs
3759 assert 'XXX' in env, 'XXX' in env
3760 assert 'XXX' in env2, 'XXX' in env2
3761 assert 'XXX' in env3, 'XXX' in env3
3762 assert 'YYY' in env, 'YYY' in env
3763 assert 'YYY' in env2, 'YYY' in env2
3764 assert 'YYY' in env3, 'YYY' in env3
3765 assert 'ZZZ' not in env, 'ZZZ' in env
3766 assert 'ZZZ' not in env2, 'ZZZ' in env2
3767 assert 'ZZZ' in env3, 'ZZZ' in env3
3769 def test_Dictionary(self) -> None:
3770 """Test the OverrideEnvironment Dictionary() method"""
3771 env, env2, env3 = self.envs
3772 # nothing overrriden
3773 items = env.Dictionary()
3774 assert items == {'XXX' : 'x', 'YYY' : 'y'}, items
3775 # env2 overrides XXX, YYY unchanged
3776 items = env2.Dictionary()
3777 assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items
3778 # env3 overrides XXX, YYY, adds ZZZ
3779 items = env3.Dictionary()
3780 assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
3781 # test one-arg and multi-arg Dictionary
3782 assert env3.Dictionary('XXX') == 'x3', env3.Dictionary('XXX')
3783 xxx, yyy = env2.Dictionary('XXX', 'YYY')
3784 assert xxx == 'x2', xxx
3785 assert yyy == 'y', yyy
3786 del env3['XXX']
3787 assert 'XXX' not in env3.Dictionary()
3788 assert 'XXX' not in env2.Dictionary()
3789 assert 'XXX' not in env.Dictionary()
3791 def test_items(self) -> None:
3792 """Test the OverrideEnvironment items() method"""
3793 env, env2, env3 = self.envs
3794 items = sorted(env.items())
3795 assert items == [('XXX', 'x'), ('YYY', 'y')], items
3796 items = sorted(env2.items())
3797 assert items == [('XXX', 'x2'), ('YYY', 'y')], items
3798 items = sorted(env3.items())
3799 assert items == [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items
3801 def test_keys(self) -> None:
3802 """Test the OverrideEnvironment keys() method"""
3803 env, env2, env3 = self.envs
3804 keys = sorted(env.keys())
3805 assert keys == ['XXX', 'YYY'], keys
3806 keys = sorted(env2.keys())
3807 assert keys == ['XXX', 'YYY'], keys
3808 keys = sorted(env3.keys())
3809 assert keys == ['XXX', 'YYY', 'ZZZ'], keys
3811 def test_values(self) -> None:
3812 """Test the OverrideEnvironment values() method"""
3813 env, env2, env3 = self.envs
3814 values = sorted(env.values())
3815 assert values == ['x', 'y'], values
3816 values = sorted(env2.values())
3817 assert values == ['x2', 'y'], values
3818 values = sorted(env3.values())
3819 assert values == ['x3', 'y3', 'z3'], values
3821 def test_setdefault(self) -> None:
3822 """Test the OverrideEnvironment setdefault() method."""
3823 env, env2, env3 = self.envs
3824 # does not set for existing key
3825 assert env2.setdefault('XXX', 'z') == 'x2', env2['XXX']
3826 # set/return using default for non-existing key
3827 assert env2.setdefault('ZZZ', 'z2') == 'z2', env2['ZZZ']
3828 # set did not leak through to base env
3829 assert 'ZZZ' not in env
3831 def test_gvars(self) -> None:
3832 """Test the OverrideEnvironment gvars() method"""
3833 env, env2, env3 = self.envs
3834 gvars = env.gvars()
3835 assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3836 gvars = env2.gvars()
3837 assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3838 gvars = env3.gvars()
3839 assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3841 def test_lvars(self) -> None:
3842 """Test the OverrideEnvironment lvars() method"""
3843 env, env2, env3 = self.envs
3844 lvars = env.lvars()
3845 assert lvars == {}, lvars
3846 lvars = env2.lvars()
3847 assert lvars == {'XXX' : 'x2'}, lvars
3848 lvars = env3.lvars()
3849 assert lvars == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars
3851 def test_Replace(self) -> None:
3852 """Test the OverrideEnvironment Replace() method"""
3853 env, env2, env3 = self.envs
3854 assert env['XXX'] == 'x', env['XXX']
3855 assert env2['XXX'] == 'x2', env2['XXX']
3856 assert env3['XXX'] == 'x3', env3['XXX']
3857 assert env['YYY'] == 'y', env['YYY']
3858 assert env2['YYY'] == 'y', env2['YYY']
3859 assert env3['YYY'] == 'y3', env3['YYY']
3861 env.Replace(YYY = 'y4')
3863 assert env['XXX'] == 'x', env['XXX']
3864 assert env2['XXX'] == 'x2', env2['XXX']
3865 assert env3['XXX'] == 'x3', env3['XXX']
3866 assert env['YYY'] == 'y4', env['YYY']
3867 assert env2['YYY'] == 'y4', env2['YYY']
3868 assert env3['YYY'] == 'y3', env3['YYY']
3870 # Tests a number of Base methods through an OverrideEnvironment to
3871 # make sure they handle overridden constructionv variables properly.
3873 # The following Base methods also call self.subst(), and so could
3874 # theoretically be subject to problems with evaluating overridden
3875 # variables, but they're never really called that way in the rest
3876 # of our code, so we won't worry about them (at least for now):
3878 # ParseConfig()
3879 # ParseDepends()
3880 # Platform()
3881 # Tool()
3883 # Action()
3884 # Alias()
3885 # Builder()
3886 # CacheDir()
3887 # Configure()
3888 # Environment()
3889 # FindFile()
3890 # Scanner()
3892 # It's unlikely Clone() will ever be called this way, so let the
3893 # other methods test that handling overridden values works.
3894 #def test_Clone(self):
3895 # """Test the OverrideEnvironment Clone() method"""
3896 # pass
3898 def test_FindIxes(self) -> None:
3899 """Test the OverrideEnvironment FindIxes() method"""
3900 env, env2, env3 = self.envs
3901 x = env.FindIxes(['xaaay'], 'XXX', 'YYY')
3902 assert x == 'xaaay', x
3903 x = env2.FindIxes(['x2aaay'], 'XXX', 'YYY')
3904 assert x == 'x2aaay', x
3905 x = env3.FindIxes(['x3aaay3'], 'XXX', 'YYY')
3906 assert x == 'x3aaay3', x
3908 def test_ReplaceIxes(self) -> None:
3909 """Test the OverrideEnvironment ReplaceIxes() method"""
3910 env, env2, env3 = self.envs
3911 x = env.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX')
3912 assert x == 'yaaax', x
3913 x = env2.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX')
3914 assert x == 'yaaax2', x
3915 x = env3.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX')
3916 assert x == 'y3aaax3', x
3918 # It's unlikely WhereIs() will ever be called this way, so let the
3919 # other methods test that handling overridden values works.
3920 #def test_WhereIs(self):
3921 # """Test the OverrideEnvironment WhereIs() method"""
3922 # pass
3924 def test_PseudoBuilderInherits(self) -> None:
3925 """Test that pseudo-builders inherit the overrided values."""
3926 env, env2, env3 = self.envs
3927 env.verify_value('XXX', 'x')
3928 env2.verify_value('XXX', 'x2')
3929 env3.verify_value('XXX', 'x3')
3931 def test_Dir(self) -> None:
3932 """Test the OverrideEnvironment Dir() method"""
3933 env, env2, env3 = self.envs
3934 x = env.Dir('ddir/$XXX')
3935 assert self.checkpath(x, 'ddir/x'), str(x)
3936 x = env2.Dir('ddir/$XXX')
3937 assert self.checkpath(x, 'ddir/x2'), str(x)
3938 x = env3.Dir('ddir/$XXX')
3939 assert self.checkpath(x, 'ddir/x3'), str(x)
3941 def test_Entry(self) -> None:
3942 """Test the OverrideEnvironment Entry() method"""
3943 env, env2, env3 = self.envs
3944 x = env.Entry('edir/$XXX')
3945 assert self.checkpath(x, 'edir/x'), str(x)
3946 x = env2.Entry('edir/$XXX')
3947 assert self.checkpath(x, 'edir/x2'), str(x)
3948 x = env3.Entry('edir/$XXX')
3949 assert self.checkpath(x, 'edir/x3'), str(x)
3951 def test_File(self) -> None:
3952 """Test the OverrideEnvironment File() method"""
3953 env, env2, env3 = self.envs
3954 x = env.File('fdir/$XXX')
3955 assert self.checkpath(x, 'fdir/x'), str(x)
3956 x = env2.File('fdir/$XXX')
3957 assert self.checkpath(x, 'fdir/x2'), str(x)
3958 x = env3.File('fdir/$XXX')
3959 assert self.checkpath(x, 'fdir/x3'), str(x)
3961 def test_Split(self) -> None:
3962 """Test the OverrideEnvironment Split() method"""
3963 env, env2, env3 = self.envs
3964 env['AAA'] = '$XXX $YYY $ZZZ'
3965 x = env.Split('$AAA')
3966 assert x == ['x', 'y'], x
3967 x = env2.Split('$AAA')
3968 assert x == ['x2', 'y'], x
3969 x = env3.Split('$AAA')
3970 assert x == ['x3', 'y3', 'z3'], x
3972 def test_parse_flags(self) -> None:
3973 """Test the OverrideEnvironment parse_flags argument"""
3974 # all we have to show is that it gets to MergeFlags internally
3975 env = SubstitutionEnvironment()
3976 env2 = env.Override({'parse_flags' : '-X'})
3977 assert 'CCFLAGS' not in env
3978 assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3980 env = SubstitutionEnvironment(CCFLAGS=None)
3981 env2 = env.Override({'parse_flags' : '-Y'})
3982 assert env['CCFLAGS'] is None, env['CCFLAGS']
3983 assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
3985 env = SubstitutionEnvironment(CPPDEFINES='FOO')
3986 env2 = env.Override({'parse_flags': '-std=c99 -X -DBAR'})
3987 assert 'CFLAGS' not in env
3988 assert env2['CFLAGS'] == ['-std=c99'], env2['CFLAGS']
3989 assert 'CCFLAGS' not in env
3990 assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3991 # make sure they are independent
3992 self.assertIsNot(env['CPPDEFINES'], env2['CPPDEFINES'])
3993 assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
3994 self.assertEqual(list(env2['CPPDEFINES']), ['FOO','BAR'])
3997 class NoSubstitutionProxyTestCase(unittest.TestCase,TestEnvironmentFixture):
3999 def test___init__(self) -> None:
4000 """Test NoSubstitutionProxy initialization"""
4001 env = self.TestEnvironment(XXX = 'x', YYY = 'y')
4002 assert env['XXX'] == 'x', env['XXX']
4003 assert env['YYY'] == 'y', env['YYY']
4005 proxy = NoSubstitutionProxy(env)
4006 assert proxy['XXX'] == 'x', proxy['XXX']
4007 assert proxy['YYY'] == 'y', proxy['YYY']
4009 def test_attributes(self) -> None:
4010 """Test getting and setting NoSubstitutionProxy attributes"""
4011 env = Environment()
4012 setattr(env, 'env_attr', 'value1')
4014 proxy = NoSubstitutionProxy(env)
4015 setattr(proxy, 'proxy_attr', 'value2')
4017 x = getattr(env, 'env_attr')
4018 assert x == 'value1', x
4019 x = getattr(proxy, 'env_attr')
4020 assert x == 'value1', x
4022 x = getattr(env, 'proxy_attr')
4023 assert x == 'value2', x
4024 x = getattr(proxy, 'proxy_attr')
4025 assert x == 'value2', x
4027 def test_subst(self) -> None:
4028 """Test the NoSubstitutionProxy.subst() method"""
4029 env = self.TestEnvironment(XXX = 'x', YYY = 'y')
4030 assert env['XXX'] == 'x', env['XXX']
4031 assert env['YYY'] == 'y', env['YYY']
4033 proxy = NoSubstitutionProxy(env)
4034 assert proxy['XXX'] == 'x', proxy['XXX']
4035 assert proxy['YYY'] == 'y', proxy['YYY']
4037 x = env.subst('$XXX')
4038 assert x == 'x', x
4039 x = proxy.subst('$XXX')
4040 assert x == '$XXX', x
4042 x = proxy.subst('$YYY', raw=7, target=None, source=None,
4043 conv=None,
4044 extra_meaningless_keyword_argument=None)
4045 assert x == '$YYY', x
4047 def test_subst_kw(self) -> None:
4048 """Test the NoSubstitutionProxy.subst_kw() method"""
4049 env = self.TestEnvironment(XXX = 'x', YYY = 'y')
4050 assert env['XXX'] == 'x', env['XXX']
4051 assert env['YYY'] == 'y', env['YYY']
4053 proxy = NoSubstitutionProxy(env)
4054 assert proxy['XXX'] == 'x', proxy['XXX']
4055 assert proxy['YYY'] == 'y', proxy['YYY']
4057 x = env.subst_kw({'$XXX':'$YYY'})
4058 assert x == {'x':'y'}, x
4059 x = proxy.subst_kw({'$XXX':'$YYY'})
4060 assert x == {'$XXX':'$YYY'}, x
4062 def test_subst_list(self) -> None:
4063 """Test the NoSubstitutionProxy.subst_list() method"""
4064 env = self.TestEnvironment(XXX = 'x', YYY = 'y')
4065 assert env['XXX'] == 'x', env['XXX']
4066 assert env['YYY'] == 'y', env['YYY']
4068 proxy = NoSubstitutionProxy(env)
4069 assert proxy['XXX'] == 'x', proxy['XXX']
4070 assert proxy['YYY'] == 'y', proxy['YYY']
4072 x = env.subst_list('$XXX')
4073 assert x == [['x']], x
4074 x = proxy.subst_list('$XXX')
4075 assert x == [[]], x
4077 x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None)
4078 assert x == [[]], x
4080 def test_subst_target_source(self) -> None:
4081 """Test the NoSubstitutionProxy.subst_target_source() method"""
4082 env = self.TestEnvironment(XXX = 'x', YYY = 'y')
4083 assert env['XXX'] == 'x', env['XXX']
4084 assert env['YYY'] == 'y', env['YYY']
4086 proxy = NoSubstitutionProxy(env)
4087 assert proxy['XXX'] == 'x', proxy['XXX']
4088 assert proxy['YYY'] == 'y', proxy['YYY']
4090 args = ('$XXX $TARGET $SOURCE $YYY',)
4091 kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
4092 x = env.subst_target_source(*args, **kw)
4093 assert x == 'x ttt sss y', x
4094 x = proxy.subst_target_source(*args, **kw)
4095 assert x == ' ttt sss ', x
4097 class EnvironmentVariableTestCase(unittest.TestCase):
4099 def test_is_valid_construction_var(self) -> None:
4100 """Testing is_valid_construction_var()"""
4101 r = is_valid_construction_var("_a")
4102 assert r is not None, r
4103 r = is_valid_construction_var("z_")
4104 assert r is not None, r
4105 r = is_valid_construction_var("X_")
4106 assert r is not None, r
4107 r = is_valid_construction_var("2a")
4108 assert r is None, r
4109 r = is_valid_construction_var("a2_")
4110 assert r is not None, r
4111 r = is_valid_construction_var("/")
4112 assert r is None, r
4113 r = is_valid_construction_var("_/")
4114 assert r is None, r
4115 r = is_valid_construction_var("a/")
4116 assert r is None, r
4117 r = is_valid_construction_var(".b")
4118 assert r is None, r
4119 r = is_valid_construction_var("_.b")
4120 assert r is None, r
4121 r = is_valid_construction_var("b1._")
4122 assert r is None, r
4123 r = is_valid_construction_var("-b")
4124 assert r is None, r
4125 r = is_valid_construction_var("_-b")
4126 assert r is None, r
4127 r = is_valid_construction_var("b1-_")
4128 assert r is None, r
4132 if __name__ == "__main__":
4133 unittest.main()
4135 # Local Variables:
4136 # tab-width:4
4137 # indent-tabs-mode:nil
4138 # End:
4139 # vim: set expandtab tabstop=4 shiftwidth=4: