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.
31 from collections
import UserDict
as UD
, UserList
as UL
, deque
36 from SCons
.Environment
import (
40 SubstitutionEnvironment
,
42 from SCons
.Util
import CLVar
43 from SCons
.SConsign
import current_sconsign_filename
46 def diff_env(env1
, env2
):
50 for k
in list(env1
._dict
.keys()) + list(env2
._dict
.keys()):
52 for k
in sorted(d
.keys()):
55 if env1
[k
] != env2
[k
]:
56 s1
= s1
+ " " + repr(k
) + " : " + repr(env1
[k
]) + "\n"
57 s2
= s2
+ " " + repr(k
) + " : " + repr(env2
[k
]) + "\n"
59 s1
= s1
+ " " + repr(k
) + " : " + repr(env1
[k
]) + "\n"
61 s2
= s2
+ " " + repr(k
) + " : " + repr(env2
[k
]) + "\n"
66 def diff_dict(d1
, d2
):
70 for k
in list(d1
.keys()) + list(d2
.keys()):
72 for k
in sorted(d
.keys()):
76 s1
= s1
+ " " + repr(k
) + " : " + repr(d1
[k
]) + "\n"
77 s2
= s2
+ " " + repr(k
) + " : " + repr(d2
[k
]) + "\n"
79 s1
= s1
+ " " + repr(k
) + " : " + repr(d1
[k
]) + "\n"
81 s2
= s2
+ " " + repr(k
) + " : " + repr(d2
[k
]) + "\n"
89 class Builder(SCons
.Builder
.BuilderBase
):
90 """A dummy Builder class for testing purposes. "Building"
91 a target is simply setting a value in the dictionary.
93 def __init__(self
, name
= None) -> None:
96 def __call__(self
, env
, target
=None, source
=None, **kw
) -> None:
98 called_it
['target'] = target
99 called_it
['source'] = source
102 def execute(self
, target
= None, **kw
) -> None:
111 """A dummy Scanner class for testing purposes. "Scanning"
112 a target is simply setting a value in the dictionary.
114 def __init__(self
, name
, skeys
=[]) -> None:
118 def __call__(self
, filename
) -> None:
120 scanned_it
[filename
] = 1
122 def __eq__(self
, other
):
124 return self
.__dict
__ == other
.__dict
__
125 except AttributeError:
128 def get_skeys(self
, env
):
131 def __str__(self
) -> str:
136 def __init__(self
, name
) -> None:
138 def __str__(self
) -> str:
142 def get_subst_proxy(self
):
145 def test_tool( env
) -> None:
146 env
['_F77INCFLAGS'] = '${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE, affect_signature=False)}'
148 class TestEnvironmentFixture
:
149 def TestEnvironment(self
, *args
, **kw
):
150 if not kw
or 'tools' not in kw
:
151 kw
['tools'] = [test_tool
]
152 default_keys
= { 'CC' : 'cc',
153 'CCFLAGS' : '-DNDEBUG',
154 'ENV' : { 'TMP' : '/tmp' } }
155 for key
, value
in default_keys
.items():
158 if 'BUILDERS' not in kw
:
159 static_obj
= SCons
.Builder
.Builder(action
= {},
163 kw
['BUILDERS'] = {'Object' : static_obj
}
164 static_obj
.add_action('.cpp', 'fake action')
166 env
= Environment(*args
, **kw
)
169 class SubstitutionTestCase(unittest
.TestCase
):
171 def test___init__(self
) -> None:
172 """Test initializing a SubstitutionEnvironment."""
173 env
= SubstitutionEnvironment()
174 assert '__env__' not in env
176 def test___cmp__(self
) -> None:
177 """Test comparing SubstitutionEnvironments."""
178 env1
= SubstitutionEnvironment(XXX
= 'x')
179 env2
= SubstitutionEnvironment(XXX
= 'x')
180 env3
= SubstitutionEnvironment(XXX
= 'xxx')
181 env4
= SubstitutionEnvironment(XXX
= 'x', YYY
= 'x')
184 self
.assertEqual(env1
, env2
)
186 self
.assertNotEqual(env1
, env3
)
188 self
.assertNotEqual(env1
, env4
)
190 def test___delitem__(self
) -> None:
191 """Test deleting a variable from a SubstitutionEnvironment."""
192 env1
= SubstitutionEnvironment(XXX
= 'x', YYY
= 'y')
193 env2
= SubstitutionEnvironment(XXX
= 'x')
195 self
.assertEqual(env1
, env2
)
197 def test___getitem__(self
) -> None:
198 """Test fetching a variable from a SubstitutionEnvironment."""
199 env
= SubstitutionEnvironment(XXX
= 'x')
200 assert env
['XXX'] == 'x', env
['XXX']
202 def test___setitem__(self
) -> None:
203 """Test setting a variable in a SubstitutionEnvironment."""
204 env1
= SubstitutionEnvironment(XXX
= 'x')
205 env2
= SubstitutionEnvironment(XXX
= 'x', YYY
= 'y')
207 self
.assertEqual(env1
, env2
)
209 def test_get(self
) -> None:
210 """Test the SubstitutionEnvironment get() method."""
211 env
= SubstitutionEnvironment(XXX
= 'x')
212 assert env
.get('XXX') == 'x', env
.get('XXX')
213 assert env
.get('YYY') is None, env
.get('YYY')
215 def test_contains(self
) -> None:
216 """Test the SubstitutionEnvironment __contains__() method."""
217 env
= SubstitutionEnvironment(XXX
= 'x')
219 assert 'YYY' not in env
221 def test_keys(self
) -> None:
222 """Test the SubstitutionEnvironment keys() method."""
223 testdata
= {'XXX': 'x', 'YYY': 'y'}
224 env
= SubstitutionEnvironment(**testdata
)
225 keys
= list(env
.keys())
226 assert len(keys
) == 2, keys
227 for k
in testdata
.keys():
228 assert k
in keys
, keys
230 def test_values(self
) -> None:
231 """Test the SubstitutionEnvironment values() method."""
232 testdata
= {'XXX': 'x', 'YYY': 'y'}
233 env
= SubstitutionEnvironment(**testdata
)
234 values
= list(env
.values())
235 assert len(values
) == 2, values
236 for v
in testdata
.values():
237 assert v
in values
, values
239 def test_items(self
) -> None:
240 """Test the SubstitutionEnvironment items() method."""
241 testdata
= {'XXX': 'x', 'YYY': 'y'}
242 env
= SubstitutionEnvironment(**testdata
)
243 items
= list(env
.items())
244 assert len(items
) == 2, items
245 for k
, v
in testdata
.items():
246 assert (k
, v
) in items
, items
248 def test_setdefault(self
) -> None:
249 """Test the SubstitutionEnvironment setdefault() method."""
250 env
= SubstitutionEnvironment(XXX
= 'x')
251 assert env
.setdefault('XXX', 'z') == 'x', env
['XXX']
252 assert env
.setdefault('YYY', 'y') == 'y', env
['YYY']
255 def test_arg2nodes(self
) -> None:
256 """Test the arg2nodes method."""
257 env
= SubstitutionEnvironment()
259 class X(SCons
.Node
.Node
):
261 def Factory(name
, directory
= None, create
: int = 1, dict=dict, X
=X
):
264 dict[name
].name
= name
267 nodes
= env
.arg2nodes("Util.py UtilTests.py", Factory
)
268 assert len(nodes
) == 1, nodes
269 assert isinstance(nodes
[0], X
)
270 assert nodes
[0].name
== "Util.py UtilTests.py", nodes
[0].name
272 nodes
= env
.arg2nodes(["Util.py", "UtilTests.py"], Factory
)
273 assert len(nodes
) == 2, nodes
274 assert isinstance(nodes
[0], X
)
275 assert isinstance(nodes
[1], X
)
276 assert nodes
[0].name
== "Util.py", nodes
[0].name
277 assert nodes
[1].name
== "UtilTests.py", nodes
[1].name
279 n1
= Factory("Util.py")
280 nodes
= env
.arg2nodes([n1
, "UtilTests.py"], Factory
)
281 assert len(nodes
) == 2, nodes
282 assert isinstance(nodes
[0], X
)
283 assert isinstance(nodes
[1], X
)
284 assert nodes
[0].name
== "Util.py", nodes
[0].name
285 assert nodes
[1].name
== "UtilTests.py", nodes
[1].name
287 class SConsNode(SCons
.Node
.Node
):
289 nodes
= env
.arg2nodes(SConsNode())
290 assert len(nodes
) == 1, nodes
291 assert isinstance(nodes
[0], SConsNode
), nodes
[0]
295 nodes
= env
.arg2nodes(OtherNode())
296 assert len(nodes
) == 1, nodes
297 assert isinstance(nodes
[0], OtherNode
), nodes
[0]
299 def lookup_a(str, F
=Factory
):
307 def lookup_b(str, F
=Factory
):
315 env_ll
= SubstitutionEnvironment()
316 env_ll
.lookup_list
= [lookup_a
, lookup_b
]
318 nodes
= env_ll
.arg2nodes(['aaa', 'bbb', 'ccc'], Factory
)
319 assert len(nodes
) == 3, nodes
321 assert nodes
[0].name
== 'aaa', nodes
[0]
322 assert nodes
[0].a
== 1, nodes
[0]
323 assert not hasattr(nodes
[0], 'b'), nodes
[0]
325 assert nodes
[1].name
== 'bbb'
326 assert not hasattr(nodes
[1], 'a'), nodes
[1]
327 assert nodes
[1].b
== 1, nodes
[1]
329 assert nodes
[2].name
== 'ccc'
330 assert not hasattr(nodes
[2], 'a'), nodes
[1]
331 assert not hasattr(nodes
[2], 'b'), nodes
[1]
333 def lookup_bbbb(str, F
=Factory
):
341 def lookup_c(str, F
=Factory
):
349 nodes
= env
.arg2nodes(['bbbb', 'ccc'], Factory
,
350 [lookup_c
, lookup_bbbb
, lookup_b
])
351 assert len(nodes
) == 2, nodes
353 assert nodes
[0].name
== 'bbbb'
354 assert not hasattr(nodes
[0], 'a'), nodes
[1]
355 assert not hasattr(nodes
[0], 'b'), nodes
[1]
356 assert nodes
[0].bbbb
== 1, nodes
[1]
357 assert not hasattr(nodes
[0], 'c'), nodes
[0]
359 assert nodes
[1].name
== 'ccc'
360 assert not hasattr(nodes
[1], 'a'), nodes
[1]
361 assert not hasattr(nodes
[1], 'b'), nodes
[1]
362 assert not hasattr(nodes
[1], 'bbbb'), nodes
[0]
363 assert nodes
[1].c
== 1, nodes
[1]
365 def test_arg2nodes_target_source(self
) -> None:
366 """Test the arg2nodes method with target= and source= keywords
368 targets
= [DummyNode('t1'), DummyNode('t2')]
369 sources
= [DummyNode('s1'), DummyNode('s2')]
370 env
= SubstitutionEnvironment()
371 nodes
= env
.arg2nodes(['${TARGET}-a',
378 names
= [n
.name
for n
in nodes
]
379 assert names
== ['t1-a', 's1-b', 't2-c', 's2-d'], names
381 def test_gvars(self
) -> None:
382 """Test the base class gvars() method"""
383 env
= SubstitutionEnvironment()
385 assert gvars
== {}, gvars
387 def test_lvars(self
) -> None:
388 """Test the base class lvars() method"""
389 env
= SubstitutionEnvironment()
391 assert lvars
== {}, lvars
393 def test_subst(self
) -> None:
394 """Test substituting construction variables within strings
396 Check various combinations, including recursive expansion
397 of variables into other variables.
399 env
= SubstitutionEnvironment(AAA
= 'a', BBB
= 'b')
400 mystr
= env
.subst("$AAA ${AAA}A $BBBB $BBB")
401 assert mystr
== "a aA b", mystr
403 # Changed the tests below to reflect a bug fix in
405 env
= SubstitutionEnvironment(AAA
= '$BBB', BBB
= 'b', BBBA
= 'foo')
406 mystr
= env
.subst("$AAA ${AAA}A ${AAA}B $BBB")
407 assert mystr
== "b bA bB b", mystr
409 env
= SubstitutionEnvironment(AAA
= '$BBB', BBB
= '$CCC', CCC
= 'c')
410 mystr
= env
.subst("$AAA ${AAA}A ${AAA}B $BBB")
411 assert mystr
== "c cA cB c", mystr
414 env
= SubstitutionEnvironment(AAA
= ['a', 'aa', 'aaa'])
415 mystr
= env
.subst("$AAA")
416 assert mystr
== "a aa aaa", mystr
419 env
= SubstitutionEnvironment(AAA
= ('a', 'aa', 'aaa'))
420 mystr
= env
.subst("$AAA")
421 assert mystr
== "a aa aaa", mystr
428 env
= SubstitutionEnvironment(AAA
= 'aaa')
429 s
= env
.subst('$AAA $TARGET $SOURCES', target
=[t1
, t2
], source
=[s1
, s2
])
430 assert s
== "aaa t1 s1 s2", s
431 s
= env
.subst('$AAA $TARGETS $SOURCE', target
=[t1
, t2
], source
=[s1
, s2
])
432 assert s
== "aaa t1 t2 s1", s
434 # Test callables in the SubstitutionEnvironment
435 def foo(target
, source
, env
, for_signature
):
436 assert str(target
) == 't', target
437 assert str(source
) == 's', source
440 env
= SubstitutionEnvironment(BAR
=foo
, FOO
='baz')
444 subst
= env
.subst('test $BAR', target
=t
, source
=s
)
445 assert subst
== 'test baz', subst
447 # Test not calling callables in the SubstitutionEnvironment
449 # This will take some serious surgery to subst() and
450 # subst_list(), so just leave these tests out until we can
452 def bar(arg
) -> None:
455 env
= SubstitutionEnvironment(BAR
=bar
, FOO
='$BAR')
457 subst
= env
.subst('$BAR', call
=None)
458 assert subst
is bar
, subst
460 subst
= env
.subst('$FOO', call
=None)
461 assert subst
is bar
, subst
463 def test_subst_kw(self
) -> None:
464 """Test substituting construction variables within dictionaries"""
465 env
= SubstitutionEnvironment(AAA
= 'a', BBB
= 'b')
466 kw
= env
.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
467 assert len(kw
) == 2, kw
468 assert kw
['a'] == 'aaa', kw
['a']
469 assert kw
['bbb'] == 'b', kw
['bbb']
471 def test_subst_list(self
) -> None:
472 """Test substituting construction variables in command lists
474 env
= SubstitutionEnvironment(AAA
= 'a', BBB
= 'b')
475 l
= env
.subst_list("$AAA ${AAA}A $BBBB $BBB")
476 assert l
== [["a", "aA", "b"]], l
478 # Changed the tests below to reflect a bug fix in
480 env
= SubstitutionEnvironment(AAA
= '$BBB', BBB
= 'b', BBBA
= 'foo')
481 l
= env
.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
482 assert l
== [["b", "bA", "bB", "b"]], l
484 env
= SubstitutionEnvironment(AAA
= '$BBB', BBB
= '$CCC', CCC
= 'c')
485 l
= env
.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
486 assert l
== [["c", "cA", "cB", "c"]], l
488 env
= SubstitutionEnvironment(AAA
= '$BBB', BBB
= '$CCC', CCC
= [ 'a', 'b\nc' ])
489 lst
= env
.subst_list([ "$AAA", "B $CCC" ])
490 assert lst
== [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
497 env
= SubstitutionEnvironment(AAA
= 'aaa')
498 s
= env
.subst_list('$AAA $TARGET $SOURCES', target
=[t1
, t2
], source
=[s1
, s2
])
499 assert s
== [["aaa", "t1", "s1", "s2"]], s
500 s
= env
.subst_list('$AAA $TARGETS $SOURCE', target
=[t1
, t2
], source
=[s1
, s2
])
501 assert s
== [["aaa", "t1", "t2", "s1"]], s
503 # Test callables in the SubstitutionEnvironment
504 def foo(target
, source
, env
, for_signature
):
505 assert str(target
) == 't', target
506 assert str(source
) == 's', source
509 env
= SubstitutionEnvironment(BAR
=foo
, FOO
='baz')
513 lst
= env
.subst_list('test $BAR', target
=t
, source
=s
)
514 assert lst
== [['test', 'baz']], lst
516 # Test not calling callables in the SubstitutionEnvironment
518 # This will take some serious surgery to subst() and
519 # subst_list(), so just leave these tests out until we can
521 def bar(arg
) -> None:
524 env
= SubstitutionEnvironment(BAR
=bar
, FOO
='$BAR')
526 subst
= env
.subst_list('$BAR', call
=None)
527 assert subst
is bar
, subst
529 subst
= env
.subst_list('$FOO', call
=None)
530 assert subst
is bar
, subst
532 def test_subst_path(self
) -> None:
533 """Test substituting a path list
536 def __init__(self
, val
) -> None:
539 return self
.val
+ '-proxy'
542 def __init__(self
, val
) -> None:
544 def get_subst_proxy(self
):
546 def __str__(self
) -> str:
553 env
= SubstitutionEnvironment(FOO
='foo',
556 PROXY
=MyProxy('my1'))
558 r
= env
.subst_path('$FOO')
559 assert r
== ['foo'], r
561 r
= env
.subst_path(['$FOO', 'xxx', '$BAR'])
562 assert r
== ['foo', 'xxx', 'bar'], r
564 r
= env
.subst_path(['$FOO', '$LIST', '$BAR'])
565 assert list(map(str, r
)) == ['foo', 'one two', 'bar'], r
567 r
= env
.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR'])
568 assert r
== ['foo', '', '', 'bar'], r
570 r
= env
.subst_path(['$FOO', '$TARGET', '$BAR'], target
=MyNode('ttt'))
571 assert list(map(str, r
)) == ['foo', 'ttt', 'bar'], r
573 r
= env
.subst_path(['$FOO', '$SOURCE', '$BAR'], source
=MyNode('sss'))
574 assert list(map(str, r
)) == ['foo', 'sss', 'bar'], r
578 r
= env
.subst_path(['$PROXY', MyProxy('my2'), n
])
579 assert r
== ['my1-proxy', 'my2-proxy', n
], r
582 def __init__(self
, s
) -> None:
584 def __str__(self
) -> str:
587 env
= SubstitutionEnvironment(FOO
=StringableObj("foo"),
588 BAR
=StringableObj("bar"))
590 r
= env
.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
591 assert r
== [ "foo/bar", "bar/baz" ], r
593 r
= env
.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
594 assert r
== [ "bar/foo", "baz/bar" ], r
596 r
= env
.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
597 assert r
== [ "bar/foo/bar", "baz/bar/baz" ], r
599 def test_subst_target_source(self
) -> None:
600 """Test the base environment subst_target_source() method"""
601 env
= SubstitutionEnvironment(AAA
= 'a', BBB
= 'b')
602 mystr
= env
.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
603 assert mystr
== "a aA b", mystr
605 def test_backtick(self
) -> None:
606 """Test the backtick() method for capturing command output"""
607 env
= SubstitutionEnvironment()
609 test
= TestCmd
.TestCmd(workdir
= '')
610 test
.write('stdout.py', """\
612 sys.stdout.write('this came from stdout.py\\n')
615 test
.write('stderr.py', """\
617 sys.stderr.write('this came from stderr.py\\n')
620 test
.write('fail.py', """\
624 test
.write('echo.py', """\
626 sys.stdout.write(os.environ['ECHO'] + '\\n')
630 save_stderr
= sys
.stderr
632 python
= '"' + sys
.executable
+ '"'
635 sys
.stderr
= io
.StringIO()
636 cmd
= '%s %s' % (python
, test
.workpath('stdout.py'))
637 output
= env
.backtick(cmd
)
638 errout
= sys
.stderr
.getvalue()
639 assert output
== 'this came from stdout.py\n', output
640 assert errout
== '', errout
642 sys
.stderr
= io
.StringIO()
643 cmd
= '%s %s' % (python
, test
.workpath('stderr.py'))
644 output
= env
.backtick(cmd
)
645 errout
= sys
.stderr
.getvalue()
646 assert output
== '', output
647 assert errout
== 'this came from stderr.py\n', errout
649 sys
.stderr
= io
.StringIO()
650 cmd
= '%s %s' % (python
, test
.workpath('fail.py'))
654 assert str(e
) == f
'{cmd!r} exited 1', str(e
)
656 self
.fail("did not catch expected OSError")
658 sys
.stderr
= io
.StringIO()
659 cmd
= '%s %s' % (python
, test
.workpath('echo.py'))
660 env
['ENV'] = os
.environ
.copy()
661 env
['ENV']['ECHO'] = 'this came from ECHO'
662 output
= env
.backtick(cmd
)
663 errout
= sys
.stderr
.getvalue()
664 assert output
== 'this came from ECHO\n', output
665 assert errout
== '', errout
668 sys
.stderr
= save_stderr
670 def test_AddMethod(self
) -> None:
671 """Test the AddMethod() method"""
672 env
= SubstitutionEnvironment(FOO
= 'foo')
675 return 'func-' + self
['FOO']
677 assert not hasattr(env
, 'func')
680 assert r
== 'func-foo', r
682 assert not hasattr(env
, 'bar')
683 env
.AddMethod(func
, 'bar')
685 assert r
== 'func-foo', r
687 def func2(self
, arg
: str=''):
688 return 'func2-' + self
['FOO'] + arg
692 assert r
== 'func2-foo', r
693 r
= env
.func2('-xxx')
694 assert r
== 'func2-foo-xxx', r
696 env
.AddMethod(func2
, 'func')
698 assert r
== 'func2-foo', r
700 assert r
== 'func2-foo-yyy', r
702 # Test that clones of clones correctly re-bind added methods.
703 env1
= Environment(FOO
= '1')
704 env1
.AddMethod(func2
)
705 env2
= env1
.Clone(FOO
= '2')
706 env3
= env2
.Clone(FOO
= '3')
707 env4
= env3
.Clone(FOO
= '4')
709 assert r
== 'func2-1', r
711 assert r
== 'func2-2', r
713 assert r
== 'func2-3', r
715 assert r
== 'func2-4', r
717 # Test that clones don't re-bind an attribute that the user set.
718 env1
= Environment(FOO
= '1')
719 env1
.AddMethod(func2
)
720 def replace_func2() -> str:
721 return 'replace_func2'
722 env1
.func2
= replace_func2
723 env2
= env1
.Clone(FOO
= '2')
725 assert r
== 'replace_func2', r
727 # Test clone rebinding if using global AddMethod.
728 env1
= Environment(FOO
='1')
729 SCons
.Util
.AddMethod(env1
, func2
)
731 assert r
== 'func2-1', r
732 r
= env1
.func2('-xxx')
733 assert r
== 'func2-1-xxx', r
734 env2
= env1
.Clone(FOO
='2')
736 assert r
== 'func2-2', r
739 def test_Override(self
) -> None:
740 """Test overriding construction variables"""
741 env
= SubstitutionEnvironment(ONE
=1, TWO
=2, THREE
=3, FOUR
=4)
742 assert env
['ONE'] == 1, env
['ONE']
743 assert env
['TWO'] == 2, env
['TWO']
744 assert env
['THREE'] == 3, env
['THREE']
745 assert env
['FOUR'] == 4, env
['FOUR']
747 env2
= env
.Override({'TWO' : '10',
748 'THREE' :'x $THREE y',
749 'FOUR' : ['x', '$FOUR', 'y']})
750 assert env2
['ONE'] == 1, env2
['ONE']
751 assert env2
['TWO'] == '10', env2
['TWO']
752 assert env2
['THREE'] == 'x 3 y', env2
['THREE']
753 assert env2
['FOUR'] == ['x', 4, 'y'], env2
['FOUR']
755 assert env
['ONE'] == 1, env
['ONE']
756 assert env
['TWO'] == 2, env
['TWO']
757 assert env
['THREE'] == 3, env
['THREE']
758 assert env
['FOUR'] == 4, env
['FOUR']
760 env2
.Replace(ONE
= "won")
761 assert env2
['ONE'] == "won", env2
['ONE']
762 assert env
['ONE'] == 1, env
['ONE']
764 def test_ParseFlags(self
) -> None:
765 """Test the ParseFlags() method
767 env
= SubstitutionEnvironment()
777 'FRAMEWORKPATH' : [],
785 d
= env
.ParseFlags(None)
788 d
= env
.ParseFlags('')
791 d
= env
.ParseFlags([])
795 "-I/usr/include/fum -I bar -X "
796 '-I"C:\\Program Files\\ASCEND\\include" '
797 "-L/usr/fax -L foo -lxxx -l yyy "
798 '-L"C:\\Program Files\\ASCEND" -lascend '
807 "-frameworkdir=fwd1 "
810 "-dylib_file foo-dylib "
812 "-fmerge-all-constants "
814 "-mno-cygwin -mwindows "
817 "-iquote /usr/include/foo1 "
818 "-isystem /usr/include/foo2 "
819 "-idirafter /usr/include/foo3 "
820 "-imacros /usr/include/foo4 "
821 "-include /usr/include/foo5 "
822 "--param l1-cache-size=32 --param l2-cache-size=6144 "
824 "-DFOO -DBAR=value -D BAZ "
826 "-fsanitize-address-use-after-return "
830 d
= env
.ParseFlags(s
)
832 assert d
['ASFLAGS'] == ['-as'], d
['ASFLAGS']
833 assert d
['CFLAGS'] == ['-std=c99']
834 assert d
['CCFLAGS'] == ['-X', '-Wa,-as',
835 '-pthread', '-fmerge-all-constants',
836 '-fopenmp', '-mno-cygwin',
837 ('-arch', 'i386'), ('-isysroot', '/tmp'),
838 ('-iquote', '/usr/include/foo1'),
839 ('-isystem', '/usr/include/foo2'),
840 ('-idirafter', '/usr/include/foo3'),
841 ('-imacros', env
.fs
.File('/usr/include/foo4')),
842 ('-include', env
.fs
.File('/usr/include/foo5')),
843 ('--param', 'l1-cache-size=32'), ('--param', 'l2-cache-size=6144'),
846 '-fsanitize-address-use-after-return'], repr(d
['CCFLAGS'])
847 assert d
['CXXFLAGS'] == ['-std=c++0x', '-stdlib=libc++'], repr(d
['CXXFLAGS'])
848 assert d
['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d
['CPPDEFINES']
849 assert d
['CPPFLAGS'] == ['-Wp,-cpp'], d
['CPPFLAGS']
850 assert d
['CPPPATH'] == ['/usr/include/fum',
852 'C:\\Program Files\\ASCEND\\include'], d
['CPPPATH']
853 assert d
['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d
['FRAMEWORKPATH']
854 assert d
['FRAMEWORKS'] == ['Carbon'], d
['FRAMEWORKS']
855 assert d
['LIBPATH'] == ['/usr/fax',
857 'C:\\Program Files\\ASCEND'], d
['LIBPATH']
858 LIBS
= list(map(str, d
['LIBS']))
859 assert LIBS
== ['xxx', 'yyy', 'ascend'], (d
['LIBS'], LIBS
)
860 assert d
['LINKFLAGS'] == ['-Wl,-link',
861 '-dylib_file', 'foo-dylib',
862 '-pthread', '-fmerge-all-constants', '-fopenmp',
863 '-mno-cygwin', '-mwindows',
865 ('-isysroot', '/tmp'),
868 '-fsanitize-address-use-after-return'], repr(d
['LINKFLAGS'])
869 assert d
['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d
['RPATH']
872 def test_MergeFlags(self
) -> None:
873 """Test the MergeFlags() method."""
875 env
= SubstitutionEnvironment()
876 # does not set flag if value empty
878 assert 'CCFLAGS' not in env
, env
['CCFLAGS']
879 # merges value if flag did not exist
881 assert env
['CCFLAGS'] == ['-X'], env
['CCFLAGS']
883 # avoid SubstitutionEnvironment for these, has no .Append method,
884 # which is needed for unique=False test
885 env
= Environment(CCFLAGS
="")
886 # merge with existing but empty flag
888 assert env
['CCFLAGS'] == ['-X'], env
['CCFLAGS']
889 # default Unique=True enforces no dupes
891 assert env
['CCFLAGS'] == ['-X'], env
['CCFLAGS']
892 # Unique=False allows dupes
893 env
.MergeFlags('-X', unique
=False)
894 assert env
['CCFLAGS'] == ['-X', '-X'], env
['CCFLAGS']
896 # merge from a dict with list values
897 env
= SubstitutionEnvironment(B
='b')
898 env
.MergeFlags({'A': ['aaa'], 'B': ['bb', 'bbb']})
899 assert env
['A'] == ['aaa'], env
['A']
900 assert env
['B'] == ['b', 'bb', 'bbb'], env
['B']
902 # issue #2961: merge from a dict with string values
903 env
= SubstitutionEnvironment(B
='b')
904 env
.MergeFlags({'A': 'aaa', 'B': 'bb bbb'})
905 assert env
['A'] == ['aaa'], env
['A']
906 assert env
['B'] == ['b', 'bb', 'bbb'], env
['B']
908 # issue #4231: CPPDEFINES can be a deque, tripped up merge logic
909 env
= Environment(CPPDEFINES
=deque(['aaa', 'bbb']))
910 env
.MergeFlags({'CPPDEFINES': 'ccc'})
911 self
.assertEqual(env
['CPPDEFINES'], deque(['aaa', 'bbb', 'ccc']))
913 # issue #3665: if merging dict which is a compound object
914 # (i.e. value can be lists, etc.), the value object should not
915 # be modified. per the issue, this happened if key not in env.
916 env
= SubstitutionEnvironment()
918 del env
['CFLAGS'] # just to be sure
921 flags
= {'CFLAGS': ['-pipe', '-pthread', '-g']}
924 saveflags
= copy
.deepcopy(flags
)
925 env
.MergeFlags(flags
)
926 self
.assertEqual(flags
, saveflags
)
929 class BaseTestCase(unittest
.TestCase
,TestEnvironmentFixture
):
931 reserved_variables
= [
942 def test___init__(self
) -> None:
943 """Test construction Environment creation
945 Create two with identical arguments and check that
946 they compare the same.
948 env1
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
949 env2
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
950 assert env1
== env2
, diff_env(env1
, env2
)
952 assert '__env__' not in env1
953 assert '__env__' not in env2
955 def test_variables(self
) -> None:
956 """Test that variables only get applied once."""
958 def __init__(self
, key
, val
) -> None:
964 def Update(self
, env
) -> None:
965 env
[self
.key
] = self
.val
966 self
.calls
= self
.calls
+ 1
968 o
= FakeOptions('AAA', 'fake_opt')
969 env
= Environment(variables
=o
, AAA
='keyword_arg')
970 assert o
.calls
== 1, o
.calls
971 assert env
['AAA'] == 'fake_opt', env
['AAA']
973 def test_get(self
) -> None:
974 """Test the get() method."""
975 env
= self
.TestEnvironment(aaa
= 'AAA')
979 x
= env
.get('aaa', 'XXX')
983 x
= env
.get('bbb', 'XXX')
986 def test_Builder_calls(self
) -> None:
987 """Test Builder calls through different environments
995 env
.Replace(BUILDERS
= { 'builder1' : b1
,
999 assert called_it
['target'] is None, called_it
1000 assert called_it
['source'] == ['in1'], called_it
1003 env
.builder2(source
= 'in2', xyzzy
= 1)
1004 assert called_it
['target'] is None, called_it
1005 assert called_it
['source'] == ['in2'], called_it
1006 assert called_it
['xyzzy'] == 1, called_it
1009 env
.builder1(foo
= 'bar')
1010 assert called_it
['foo'] == 'bar', called_it
1011 assert called_it
['target'] is None, called_it
1012 assert called_it
['source'] is None, called_it
1014 def test_BuilderWrapper_attributes(self
) -> None:
1015 """Test getting and setting of BuilderWrapper attributes."""
1021 e1
.Replace(BUILDERS
={'b': b1
})
1028 assert bw
.builder
is b1
1030 assert bw
.builder
is b2
1032 self
.assertRaises(AttributeError, getattr, bw
, 'foobar')
1034 assert bw
.foobar
== 42
1036 # This unit test is currently disabled because we don't think the
1037 # underlying method it tests (Environment.BuilderWrapper.execute())
1038 # is necessary, but we're leaving the code here for now in case
1040 @unittest.skip("BuilderWrapper.execute method not needed")
1041 def test_Builder_execs(self
) -> None:
1042 """Test Builder execution through different environments
1044 One environment is initialized with a single
1045 Builder object, one with a list of a single Builder
1046 object, and one with a list of two Builder objects.
1054 env3
= Environment()
1055 env3
.Replace(BUILDERS
= { 'builder1' : b1
,
1057 env3
.builder1
.execute(target
= 'out1')
1058 env3
.builder2
.execute(target
= 'out2')
1059 env3
.builder1
.execute(target
= 'out3')
1060 assert built_it
['out1']
1061 assert built_it
['out2']
1062 assert built_it
['out3']
1065 assert env4
.builder1
.env
is env4
, "builder1.env (%s) == env3 (%s)?" % (
1066 env4
.builder1
.env
, env3
)
1067 assert env4
.builder2
.env
is env4
, "builder2.env (%s) == env3 (%s)?" % (
1068 env4
.builder1
.env
, env3
)
1070 # Now test BUILDERS as a dictionary.
1072 env5
= self
.TestEnvironment(BUILDERS
={ 'foo' : b1
})
1073 env5
['BUILDERS']['bar'] = b2
1074 env5
.foo
.execute(target
='out1')
1075 env5
.bar
.execute(target
='out2')
1076 assert built_it
['out1']
1077 assert built_it
['out2']
1080 env6
= Environment()
1081 env6
['BUILDERS'] = { 'foo' : b1
,
1083 env6
.foo
.execute(target
='out1')
1084 env6
.bar
.execute(target
='out2')
1085 assert built_it
['out1']
1086 assert built_it
['out2']
1090 def test_Scanners(self
) -> None:
1091 """Test setting SCANNERS in various ways
1093 One environment is initialized with a single
1094 Scanner object, one with a list of a single Scanner
1095 object, and one with a list of two Scanner objects.
1099 s1
= Scanner(name
= 'scanner1', skeys
= [".c", ".cc"])
1100 s2
= Scanner(name
= 'scanner2', skeys
= [".m4"])
1101 s3
= Scanner(name
= 'scanner3', skeys
= [".m4", ".m5"])
1102 s4
= Scanner(name
= 'scanner4', skeys
= [None])
1104 # XXX Tests for scanner execution through different environments,
1105 # XXX if we ever want to do that some day
1107 # env1 = self.TestEnvironment(SCANNERS = s1)
1108 # env1.scanner1(filename = 'out1')
1109 # assert scanned_it['out1']
1112 # env2 = self.TestEnvironment(SCANNERS = [s1])
1113 # env1.scanner1(filename = 'out1')
1114 # assert scanned_it['out1']
1117 # env3 = Environment()
1118 # env3.Replace(SCANNERS = [s1])
1119 # env3.scanner1(filename = 'out1')
1120 # env3.scanner2(filename = 'out2')
1121 # env3.scanner1(filename = 'out3')
1122 # assert scanned_it['out1']
1123 # assert scanned_it['out2']
1124 # assert scanned_it['out3']
1126 suffixes
= [".c", ".cc", ".cxx", ".m4", ".m5"]
1129 try: del env
['SCANNERS']
1130 except KeyError: pass
1131 s
= list(map(env
.get_scanner
, suffixes
))
1132 assert s
== [None, None, None, None, None], s
1134 env
= self
.TestEnvironment(SCANNERS
= [])
1135 s
= list(map(env
.get_scanner
, suffixes
))
1136 assert s
== [None, None, None, None, None], s
1138 env
.Replace(SCANNERS
= [s1
])
1139 s
= list(map(env
.get_scanner
, suffixes
))
1140 assert s
== [s1
, s1
, None, None, None], s
1142 env
.Append(SCANNERS
= [s2
])
1143 s
= list(map(env
.get_scanner
, suffixes
))
1144 assert s
== [s1
, s1
, None, s2
, None], s
1146 env
.AppendUnique(SCANNERS
= [s3
])
1147 s
= list(map(env
.get_scanner
, suffixes
))
1148 assert s
== [s1
, s1
, None, s2
, s3
], s
1150 env
= env
.Clone(SCANNERS
= [s2
])
1151 s
= list(map(env
.get_scanner
, suffixes
))
1152 assert s
== [None, None, None, s2
, None], s
1154 env
['SCANNERS'] = [s1
]
1155 s
= list(map(env
.get_scanner
, suffixes
))
1156 assert s
== [s1
, s1
, None, None, None], s
1158 env
.PrependUnique(SCANNERS
= [s2
, s1
])
1159 s
= list(map(env
.get_scanner
, suffixes
))
1160 assert s
== [s1
, s1
, None, s2
, None], s
1162 env
.Prepend(SCANNERS
= [s3
])
1163 s
= list(map(env
.get_scanner
, suffixes
))
1164 assert s
== [s1
, s1
, None, s3
, s3
], s
1166 # Verify behavior of case-insensitive suffix matches on Windows.
1167 uc_suffixes
= [_
.upper() for _
in suffixes
]
1169 env
= Environment(SCANNERS
= [s1
, s2
, s3
],
1172 s
= list(map(env
.get_scanner
, suffixes
))
1173 assert s
== [s1
, s1
, None, s2
, s3
], s
1175 s
= list(map(env
.get_scanner
, uc_suffixes
))
1176 assert s
== [None, None, None, None, None], s
1178 env
['PLATFORM'] = 'win32'
1180 s
= list(map(env
.get_scanner
, uc_suffixes
))
1181 assert s
== [s1
, s1
, None, s2
, s3
], s
1183 # Verify behavior for a scanner returning None (on Windows
1184 # where we might try to perform case manipulation on None).
1185 env
.Replace(SCANNERS
= [s4
])
1186 s
= list(map(env
.get_scanner
, suffixes
))
1187 assert s
== [None, None, None, None, None], s
1189 def test_ENV(self
) -> None:
1190 """Test setting the external ENV in Environments
1193 assert 'ENV' in env
.Dictionary()
1195 env
= self
.TestEnvironment(ENV
= { 'PATH' : '/foo:/bar' })
1196 assert env
.Dictionary('ENV')['PATH'] == '/foo:/bar'
1198 def test_ReservedVariables(self
) -> None:
1199 """Test warning generation when reserved variable names are set"""
1200 warning
= SCons
.Warnings
.ReservedVariableWarning
1201 SCons
.Warnings
.enableWarningClass(warning
)
1202 old
= SCons
.Warnings
.warningAsException(1)
1205 env4
= Environment()
1206 for kw
in self
.reserved_variables
:
1212 assert exc_caught
, "Did not catch ReservedVariableWarning for `%s'" % kw
1213 assert kw
not in env4
, "`%s' variable was incorrectly set" % kw
1215 SCons
.Warnings
.warningAsException(old
)
1217 def test_FutureReservedVariables(self
) -> None:
1218 """Test warning generation when future reserved variable names are set"""
1220 future_reserved_variables
= []
1222 warning
= SCons
.Warnings
.FutureReservedVariableWarning
1223 SCons
.Warnings
.enableWarningClass(warning
)
1224 old
= SCons
.Warnings
.warningAsException(1)
1227 env4
= Environment()
1228 for kw
in future_reserved_variables
:
1234 assert exc_caught
, "Did not catch FutureReservedVariableWarning for `%s'" % kw
1235 assert kw
in env4
, "`%s' variable was not set" % kw
1237 SCons
.Warnings
.warningAsException(old
)
1239 def test_IllegalVariables(self
) -> None:
1240 """Test that use of illegal variables raises an exception"""
1242 def test_it(var
, env
=env
) -> None:
1246 except SCons
.Errors
.UserError
:
1248 assert exc_caught
, "did not catch UserError for '%s'" % var
1250 assert env
['aaa'] == 1, env
['aaa']
1255 def test_autogenerate(self
) -> None:
1256 """Test autogenerating variables in a dictionary."""
1258 drive
, p
= os
.path
.splitdrive(os
.getcwd())
1259 def normalize_path(path
, drive
=drive
):
1260 if path
[0] in '\\/':
1262 path
= os
.path
.normpath(path
)
1263 drive
, path
= os
.path
.splitdrive(path
)
1264 return drive
.lower() + path
1266 env
= self
.TestEnvironment(LIBS
= [ 'foo', 'bar', 'baz' ],
1267 LIBLINKPREFIX
= 'foo',
1268 LIBLINKSUFFIX
= 'bar')
1270 def RDirs(pathlist
, fs
=env
.fs
):
1271 return fs
.Dir('xx').Rfindalldirs(pathlist
)
1273 env
['RDirs'] = RDirs
1274 flags
= env
.subst_list('$_LIBFLAGS', 1)[0]
1275 assert flags
== ['foobar', 'foobar', 'foobazbar'], flags
1277 blat
= env
.fs
.Dir('blat')
1279 env
.Replace(CPPPATH
= [ 'foo', '$FOO/bar', blat
],
1283 flags
= env
.subst_list('$_CPPINCFLAGS', 1)[0]
1285 normalize_path('foo'),
1286 normalize_path('xx/foobar'),
1287 normalize_path('foo'),
1288 normalize_path('xx/baz/bar'),
1289 normalize_path('foo'),
1290 normalize_path('blatbar'),
1293 assert flags
== expect
, flags
1295 # do a Replace using the dict form
1297 "F77PATH": ['foo', '$FOO/bar', blat
],
1298 "INCPREFIX": 'foo ',
1302 env
.Replace(**newvalues
)
1303 flags
= env
.subst_list('$_F77INCFLAGS', 1)[0]
1305 normalize_path('foo'),
1306 normalize_path('xx/foobar'),
1307 normalize_path('foo'),
1308 normalize_path('xx/baz/bar'),
1309 normalize_path('foo'),
1310 normalize_path('blatbar'),
1313 assert flags
== expect
, flags
1315 env
.Replace(CPPPATH
= '', F77PATH
= '', LIBPATH
= '')
1316 l
= env
.subst_list('$_CPPINCFLAGS')
1318 l
= env
.subst_list('$_F77INCFLAGS')
1320 l
= env
.subst_list('$_LIBDIRFLAGS')
1323 env
.fs
.Repository('/rep1')
1324 env
.fs
.Repository('/rep2')
1325 env
.Replace(CPPPATH
= [ 'foo', '/__a__/b', '$FOO/bar', blat
],
1329 flags
= env
.subst_list('$_CPPINCFLAGS', 1)[0]
1331 '-I', normalize_path('xx/fooXXX'),
1332 '-I', normalize_path('/rep1/xx/fooXXX'),
1333 '-I', normalize_path('/rep2/xx/fooXXX'),
1334 '-I', normalize_path('/__a__/bXXX'),
1335 '-I', normalize_path('xx/baz/barXXX'),
1336 '-I', normalize_path('/rep1/xx/baz/barXXX'),
1337 '-I', normalize_path('/rep2/xx/baz/barXXX'),
1338 '-I', normalize_path('blatXXX'),
1341 def normalize_if_path(arg
, np
=normalize_path
):
1342 if arg
not in ('$(','$)','-I'):
1345 flags
= list(map(normalize_if_path
, flags
))
1346 assert flags
== expect
, flags
1348 def test_platform(self
) -> None:
1349 """Test specifying a platform callable when instantiating."""
1351 def __str__(self
) -> str: return "TestPlatform"
1352 def __call__(self
, env
) -> None: env
['XYZZY'] = 777
1354 def tool(env
) -> None:
1355 env
['SET_TOOL'] = 'initialized'
1356 assert env
['PLATFORM'] == "TestPlatform"
1358 env
= self
.TestEnvironment(platform
= platform(), tools
= [tool
])
1359 assert env
['XYZZY'] == 777, env
1360 assert env
['PLATFORM'] == "TestPlatform"
1361 assert env
['SET_TOOL'] == "initialized"
1363 def test_Default_PLATFORM(self
) -> None:
1364 """Test overriding the default PLATFORM variable"""
1366 def __str__(self
) -> str: return "DefaultTestPlatform"
1367 def __call__(self
, env
) -> None: env
['XYZZY'] = 888
1369 def tool(env
) -> None:
1370 env
['SET_TOOL'] = 'abcde'
1371 assert env
['PLATFORM'] == "DefaultTestPlatform"
1373 import SCons
.Defaults
1374 save
= SCons
.Defaults
.ConstructionEnvironment
.copy()
1376 import SCons
.Defaults
1377 SCons
.Defaults
.ConstructionEnvironment
.update({
1378 'PLATFORM' : platform(),
1380 env
= self
.TestEnvironment(tools
= [tool
])
1381 assert env
['XYZZY'] == 888, env
1382 assert env
['PLATFORM'] == "DefaultTestPlatform"
1383 assert env
['SET_TOOL'] == "abcde"
1385 SCons
.Defaults
.ConstructionEnvironment
= save
1387 def test_tools(self
) -> None:
1388 """Test specifying a tool callable when instantiating."""
1389 def t1(env
) -> None:
1391 def t2(env
) -> None:
1393 def t3(env
) -> None:
1394 env
['AAA'] = env
['XYZ']
1395 def t4(env
) -> None:
1397 env
= self
.TestEnvironment(tools
= [t1
, t2
, t3
], XYZ
= 'aaa')
1398 assert env
['TOOL1'] == 111, env
['TOOL1']
1399 assert env
['TOOL2'] == 222, env
1400 assert env
['AAA'] == 'aaa', env
1402 assert env
['TOOL4'] == 444, env
1404 test
= TestCmd
.TestCmd(workdir
= '')
1405 test
.write('faketool.py', """\
1406 def generate(env, **kw):
1407 for k, v in kw.items():
1414 env
= self
.TestEnvironment(tools
= [('faketool', {'a':1, 'b':2, 'c':3})],
1415 toolpath
= [test
.workpath('')])
1416 assert env
['a'] == 1, env
['a']
1417 assert env
['b'] == 2, env
['b']
1418 assert env
['c'] == 3, env
['c']
1420 def test_Default_TOOLS(self
) -> None:
1421 """Test overriding the default TOOLS variable"""
1422 def t5(env
) -> None:
1424 def t6(env
) -> None:
1426 def t7(env
) -> None:
1427 env
['BBB'] = env
['XYZ']
1428 def t8(env
) -> None:
1431 import SCons
.Defaults
1432 save
= SCons
.Defaults
.ConstructionEnvironment
.copy()
1434 SCons
.Defaults
.ConstructionEnvironment
.update({
1435 'TOOLS' : [t5
, t6
, t7
],
1437 env
= Environment(XYZ
= 'bbb')
1438 assert env
['TOOL5'] == 555, env
['TOOL5']
1439 assert env
['TOOL6'] == 666, env
1440 assert env
['BBB'] == 'bbb', env
1442 assert env
['TOOL8'] == 888, env
1444 SCons
.Defaults
.ConstructionEnvironment
= save
1446 def test_null_tools(self
) -> None:
1447 """Test specifying a tool of None is OK."""
1448 def t1(env
) -> None:
1450 def t2(env
) -> None:
1452 env
= self
.TestEnvironment(tools
= [t1
, None, t2
], XYZ
= 'aaa')
1453 assert env
['TOOL1'] == 111, env
['TOOL1']
1454 assert env
['TOOL2'] == 222, env
1455 assert env
['XYZ'] == 'aaa', env
1456 env
= self
.TestEnvironment(tools
= [None], XYZ
= 'xyz')
1457 assert env
['XYZ'] == 'xyz', env
1458 env
= self
.TestEnvironment(tools
= [t1
, '', t2
], XYZ
= 'ddd')
1459 assert env
['TOOL1'] == 111, env
['TOOL1']
1460 assert env
['TOOL2'] == 222, env
1461 assert env
['XYZ'] == 'ddd', env
1463 def test_default_copy_cache(self
) -> None:
1466 def copy2(self
, src
, dst
) -> None:
1470 save_copy_from_cache
= SCons
.CacheDir
.CacheDir
.copy_from_cache
1471 SCons
.CacheDir
.CacheDir
.copy_from_cache
= copy2
1473 save_copy_to_cache
= SCons
.CacheDir
.CacheDir
.copy_to_cache
1474 SCons
.CacheDir
.CacheDir
.copy_to_cache
= copy2
1476 env
= self
.TestEnvironment()
1478 SCons
.Environment
.default_copy_from_cache(env
, 'test.in', 'test.out')
1482 SCons
.Environment
.default_copy_to_cache(env
, 'test.in', 'test.out')
1485 SCons
.CacheDir
.CacheDir
.copy_from_cache
= save_copy_from_cache
1486 SCons
.CacheDir
.CacheDir
.copy_to_cache
= save_copy_to_cache
1488 # function is in Defaults.py, tested here to use TestEnvironment
1489 def test__concat(self
) -> None:
1490 """Test _concat()"""
1491 e1
= self
.TestEnvironment(PRE
='pre', SUF
='suf', STR
='a b', LIST
=['a', 'b'])
1493 x
= s("${_concat('', '', '', __env__)}")
1495 x
= s("${_concat('', [], '', __env__)}")
1497 x
= s("${_concat(PRE, '', SUF, __env__)}")
1499 x
= s("${_concat(PRE, STR, SUF, __env__)}")
1500 assert x
== 'prea bsuf', x
1501 x
= s("${_concat(PRE, LIST, SUF, __env__)}")
1502 assert x
== 'preasuf prebsuf', x
1503 x
= s("${_concat(PRE, LIST, SUF, __env__,affect_signature=False)}", raw
=True)
1504 assert x
== '$( preasuf prebsuf $)', x
1507 # function is in Defaults.py, tested here to use TestEnvironment
1508 def test__concat_nested(self
) -> None:
1509 """Test _concat() on a nested substitution strings."""
1510 e
= self
.TestEnvironment(PRE
='pre', SUF
='suf',
1514 x
= e
.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)')
1515 assert x
== 'preasuf prebsuf', x
1516 e
.AppendUnique(L1
= ['$L2'])
1517 x
= e
.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)')
1518 assert x
== 'preasuf prebsuf precsuf predsuf', x
1519 e
.AppendUnique(L1
= ['$L3'])
1520 x
= e
.subst('$( ${_concat(PRE, L1, SUF, __env__)} $)')
1521 assert x
== 'preasuf prebsuf precsuf predsuf precsuf predsuf', x
1524 # function is in Defaults.py, tested here to use TestEnvironment
1525 def test__stripixes(self
) -> None:
1526 """Test _stripixes()"""
1527 # LIBPREFIXES and LIBSUFFIXES are stripped, except if an entry
1528 # begins with LIBLITERALPREFIX. Check this with and without that
1529 # argument being passed, and whether or not LIBLITERALPREFIX is
1531 e
= self
.TestEnvironment(
1534 LIST
=['xxx-a', 'b.yyy', 'zzxxx-c.yyy'],
1535 LIBPREFIXES
=['xxx-'],
1536 LIBSUFFIXES
=['.yyy'],
1539 # e['LIBLITERALPREFIX'] = ''
1540 with self
.subTest():
1541 x
= e
.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__, LIBLITERALPREFIX)} $)')
1542 self
.assertEqual('preasuf prebsuf prezzxxx-csuf', x
)
1544 with self
.subTest():
1545 x
= e
.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__)} $)')
1546 self
.assertEqual('preasuf prebsuf prezzxxx-csuf', x
)
1548 # add it to the env:
1549 e
['LIBLITERALPREFIX'] = 'zz'
1551 with self
.subTest():
1552 x
= e
.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__, LIBLITERALPREFIX)} $)')
1553 self
.assertEqual('preasuf prebsuf prezzxxx-c.yyysuf', x
)
1555 with self
.subTest():
1556 x
= e
.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__)} $)')
1557 self
.assertEqual('preasuf prebsuf prezzxxx-csuf', x
)
1559 # And special case: LIBLITERALPREFIX is the same as os.pathsep:
1560 e
['LIBLITERALPREFIX'] = os
.pathsep
1561 with self
.subTest():
1562 x
= e
.subst('$( ${_stripixes(PRE, LIST, SUF, LIBPREFIXES, LIBSUFFIXES,__env__, LIBLITERALPREFIX)} $)')
1563 self
.assertEqual('preasuf prebsuf prezzxxx-csuf', x
)
1566 def test_gvars(self
) -> None:
1567 """Test the Environment gvars() method."""
1568 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y', ZZZ
= 'z')
1570 assert gvars
['XXX'] == 'x', gvars
['XXX']
1571 assert gvars
['YYY'] == 'y', gvars
['YYY']
1572 assert gvars
['ZZZ'] == 'z', gvars
['ZZZ']
1574 def test__update(self
) -> None:
1575 """Test the _update() method."""
1576 env
= self
.TestEnvironment(X
= 'x', Y
= 'y', Z
= 'z')
1577 assert env
['X'] == 'x', env
['X']
1578 assert env
['Y'] == 'y', env
['Y']
1579 assert env
['Z'] == 'z', env
['Z']
1580 env
._update
({'X' : 'xxx',
1586 assert env
['X'] == 'xxx', env
['X']
1587 assert env
['Y'] == 'y', env
['Y']
1588 assert env
['Z'] == 'zzz', env
['Z']
1589 assert env
['TARGET'] == 't', env
['TARGET']
1590 assert env
['TARGETS'] == 'ttt', env
['TARGETS']
1591 assert env
['SOURCE'] == 's', env
['SOURCE']
1592 assert env
['SOURCES'] == 'sss', env
['SOURCES']
1594 def test_Append(self
) -> None:
1595 """Test appending to construction variables in an Environment."""
1596 b1
= Environment()['BUILDERS']
1597 b2
= Environment()['BUILDERS']
1598 assert b1
== b2
, diff_dict(b1
, b2
)
1601 ('a1', 'A1', 'a1A1'),
1602 ('a2', ['A2'], ['a2', 'A2']),
1603 ('a3', UL(['A3']), UL(['a', '3', 'A3'])),
1606 ('a6', UL([]), UL(['a', '6'])),
1607 ('a7', [''], ['a7', '']),
1608 ('a8', UL(['']), UL(['a', '8', ''])),
1610 (['e1'], 'E1', ['e1', 'E1']),
1611 (['e2'], ['E2'], ['e2', 'E2']),
1612 (['e3'], UL(['E3']), UL(['e3', 'E3'])),
1613 (['e4'], '', ['e4']),
1614 (['e5'], [], ['e5']),
1615 (['e6'], UL([]), UL(['e6'])),
1616 (['e7'], [''], ['e7', '']),
1617 (['e8'], UL(['']), UL(['e8', ''])),
1619 (UL(['i1']), 'I1', UL(['i1', 'I', '1'])),
1620 (UL(['i2']), ['I2'], UL(['i2', 'I2'])),
1621 (UL(['i3']), UL(['I3']), UL(['i3', 'I3'])),
1622 (UL(['i4']), '', UL(['i4'])),
1623 (UL(['i5']), [], UL(['i5'])),
1624 (UL(['i6']), UL([]), UL(['i6'])),
1625 (UL(['i7']), [''], UL(['i7', ''])),
1626 (UL(['i8']), UL(['']), UL(['i8', ''])),
1628 ({'d1':1}, 'D1', {'d1':1, 'D1':None}),
1629 ({'d2':1}, ['D2'], {'d2':1, 'D2':None}),
1630 ({'d3':1}, UL(['D3']), {'d3':1, 'D3':None}),
1631 ({'d4':1}, {'D4':1}, {'d4':1, 'D4':1}),
1632 ({'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1})),
1634 (UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None})),
1635 (UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None})),
1636 (UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None})),
1637 (UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1})),
1638 ((UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}))),
1641 ('', ['M2'], ['M2']),
1642 ('', UL(['M3']), UL(['M3'])),
1645 ('', UL([]), UL([])),
1647 ('', UL(['']), UL([''])),
1650 ([], ['N2'], ['N2']),
1651 ([], UL(['N3']), UL(['N3'])),
1654 ([], UL([]), UL([])),
1656 ([], UL(['']), UL([''])),
1658 (UL([]), 'O1', ['O', '1']),
1659 (UL([]), ['O2'], ['O2']),
1660 (UL([]), UL(['O3']), UL(['O3'])),
1661 (UL([]), '', UL([])),
1662 (UL([]), [], UL([])),
1663 (UL([]), UL([]), UL([])),
1664 (UL([]), [''], UL([''])),
1665 (UL([]), UL(['']), UL([''])),
1667 ([''], 'P1', ['', 'P1']),
1668 ([''], ['P2'], ['', 'P2']),
1669 ([''], UL(['P3']), UL(['', 'P3'])),
1672 ([''], UL([]), UL([''])),
1673 ([''], [''], ['', '']),
1674 ([''], UL(['']), UL(['', ''])),
1676 (UL(['']), 'Q1', ['', 'Q', '1']),
1677 (UL(['']), ['Q2'], ['', 'Q2']),
1678 (UL(['']), UL(['Q3']), UL(['', 'Q3'])),
1679 (UL(['']), '', UL([''])),
1680 (UL(['']), [], UL([''])),
1681 (UL(['']), UL([]), UL([''])),
1682 (UL(['']), [''], UL(['', ''])),
1683 (UL(['']), UL(['']), UL(['', ''])),
1688 for input, append
, expect
in cases
:
1689 env
['XXX'] = copy
.copy(input)
1691 env
.Append(XXX
=append
)
1692 except Exception as e
:
1693 if failed
== 0: print()
1694 print(" %s Append %s exception: %s" % \
1695 (repr(input), repr(append
), e
))
1699 if result
!= expect
:
1700 if failed
== 0: print()
1701 print(" %s Append %s => %s did not match %s" % \
1702 (repr(input), repr(append
), repr(result
), repr(expect
)))
1704 assert failed
== 0, "%d Append() cases failed" % failed
1706 env
['UL'] = UL(['foo'])
1707 env
.Append(UL
= 'bar')
1709 assert isinstance(result
, UL
), repr(result
)
1710 assert result
== ['foo', 'b', 'a', 'r'], result
1712 env
['CLVar'] = CLVar(['foo'])
1713 env
.Append(CLVar
= 'bar')
1714 result
= env
['CLVar']
1715 assert isinstance(result
, CLVar
), repr(result
)
1716 assert result
== ['foo', 'bar'], result
1719 def __init__(self
, name
) -> None:
1721 def __str__(self
) -> str:
1723 def __eq__(self
, other
):
1724 raise Exception("should not compare")
1728 env2
= self
.TestEnvironment(CCC1
= ['c1'], CCC2
= ccc
)
1729 env2
.Append(CCC1
= ccc
, CCC2
= ['c2'])
1730 assert env2
['CCC1'][0] == 'c1', env2
['CCC1']
1731 assert env2
['CCC1'][1] is ccc
, env2
['CCC1']
1732 assert env2
['CCC2'][0] is ccc
, env2
['CCC2']
1733 assert env2
['CCC2'][1] == 'c2', env2
['CCC2']
1735 env3
= self
.TestEnvironment(X
= {'x1' : 7})
1736 env3
.Append(X
= {'x1' : 8, 'x2' : 9}, Y
= {'y1' : 10})
1737 assert env3
['X'] == {'x1': 8, 'x2': 9}, env3
['X']
1738 assert env3
['Y'] == {'y1': 10}, env3
['Y']
1742 env4
= self
.TestEnvironment(BUILDERS
= {'z1' : z1
})
1743 env4
.Append(BUILDERS
= {'z2' : z2
})
1744 assert env4
['BUILDERS'] == {'z1' : z1
, 'z2' : z2
}, env4
['BUILDERS']
1745 assert hasattr(env4
, 'z1')
1746 assert hasattr(env4
, 'z2')
1748 def test_AppendENVPath(self
) -> None:
1749 """Test appending to an ENV path."""
1750 env1
= self
.TestEnvironment(
1751 ENV
={'PATH': r
'C:\dir\num\one;C:\dir\num\two'},
1752 MYENV
={'MYPATH': r
'C:\mydir\num\one;C:\mydir\num\two'},
1755 # have to include the pathsep here so that the test will work on UNIX too.
1756 env1
.AppendENVPath('PATH', r
'C:\dir\num\two', sep
=';')
1757 env1
.AppendENVPath('PATH', r
'C:\dir\num\three', sep
=';')
1759 env1
['ENV']['PATH'] == r
'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three'
1760 ), env1
['ENV']['PATH']
1762 # add nonexisting - at end
1763 env1
.AppendENVPath('MYPATH', r
'C:\mydir\num\three', 'MYENV', sep
=';')
1765 env1
['MYENV']['MYPATH'] == r
'C:\mydir\num\one;C:\mydir\num\two;C:\mydir\num\three'
1766 ), env1
['MYENV']['MYPATH']
1768 # add existing with delete_existing true - moves to the end
1770 'MYPATH', r
'C:\mydir\num\one', 'MYENV', sep
=';', delete_existing
=True
1772 # this should do nothing since delete_existing is false (the default)
1773 env1
.AppendENVPath('MYPATH', r
'C:\mydir\num\three', 'MYENV', sep
=';')
1775 env1
['MYENV']['MYPATH'] == r
'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one'
1776 ), env1
['MYENV']['MYPATH']
1778 test
= TestCmd
.TestCmd(workdir
='')
1779 test
.subdir('sub1', 'sub2')
1780 p
= env1
['ENV']['PATH']
1781 env1
.AppendENVPath('PATH', '#sub1', sep
=';')
1782 env1
.AppendENVPath('PATH', env1
.fs
.Dir('sub2'), sep
=';')
1783 assert env1
['ENV']['PATH'] == p
+ ';sub1;sub2', env1
['ENV']['PATH']
1786 def test_AppendUnique(self
) -> None:
1787 """Test appending to unique values to construction variables
1789 This strips values that are already present when lists are
1791 env
= self
.TestEnvironment(AAA1
= 'a1',
1803 DDD1
= ['a', 'b', 'c'])
1804 env
['LL1'] = [env
.Literal('a literal'), env
.Literal('b literal')]
1805 env
['LL2'] = [env
.Literal('c literal'), env
.Literal('b literal')]
1806 env
.AppendUnique(AAA1
= 'a1',
1808 AAA3
= ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
1813 BBB3
= ['b3', 'c', 'd', 'c', 'b3'],
1819 LL1
= env
.Literal('a literal'),
1820 LL2
= env
.Literal('a literal'))
1822 assert env
['AAA1'] == 'a1a1', env
['AAA1']
1823 assert env
['AAA2'] == ['a2'], env
['AAA2']
1824 assert env
['AAA3'] == ['a3', 'b', 'c'], env
['AAA3']
1825 assert env
['AAA4'] == 'a4a4.new', env
['AAA4']
1826 assert env
['AAA5'] == ['a5', 'a5.new'], env
['AAA5']
1827 assert env
['BBB1'] == ['b1'], env
['BBB1']
1828 assert env
['BBB2'] == ['b2'], env
['BBB2']
1829 assert env
['BBB3'] == ['b3', 'c', 'd'], env
['BBB3']
1830 assert env
['BBB4'] == ['b4', 'b4.new'], env
['BBB4']
1831 assert env
['BBB5'] == ['b5', 'b5.new'], env
['BBB5']
1832 assert env
['CCC1'] == 'c1', env
['CCC1']
1833 assert env
['CCC2'] == ['c2'], env
['CCC2']
1834 assert env
['DDD1'] == ['a', 'b', 'c'], env
['DDD1']
1835 assert env
['LL1'] == [
1836 env
.Literal('a literal'),
1837 env
.Literal('b literal'),
1839 assert env
['LL2'] == [
1840 env
.Literal('c literal'),
1841 env
.Literal('b literal'),
1842 env
.Literal('a literal'),
1843 ], [str(x
) for x
in env
['LL2']]
1845 env
.AppendUnique(DDD1
='b', delete_existing
=True)
1846 assert env
['DDD1'] == ['a', 'c', 'b'], env
['DDD1'] # b moves to end
1848 env
.AppendUnique(DDD1
=['a', 'b'], delete_existing
=True)
1849 assert env
['DDD1'] == ['c', 'a', 'b'], env
['DDD1'] # a & b move to end
1851 env
.AppendUnique(DDD1
=['e', 'f', 'e'], delete_existing
=True)
1852 assert env
['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env
['DDD1'] # add last
1854 # issue regression: substrings should not be deleted
1855 env
.AppendUnique(BBB4
='b4.newer', delete_existing
=True)
1856 assert env
['BBB4'] == ['b4', 'b4.new', 'b4.newer'], env
['BBB4']
1858 env
['CLVar'] = CLVar([])
1859 env
.AppendUnique(CLVar
='bar')
1860 result
= env
['CLVar']
1861 assert isinstance(result
, CLVar
), repr(result
)
1862 assert result
== ['bar'], result
1864 env
['CLVar'] = CLVar(['abc'])
1865 env
.AppendUnique(CLVar
='bar')
1866 result
= env
['CLVar']
1867 assert isinstance(result
, CLVar
), repr(result
)
1868 assert result
== ['abc', 'bar'], result
1870 env
['CLVar'] = CLVar(['bar'])
1871 env
.AppendUnique(CLVar
='bar')
1872 result
= env
['CLVar']
1873 assert isinstance(result
, CLVar
), repr(result
)
1874 assert result
== ['bar'], result
1877 def test_Clone(self
) -> None:
1878 """Test construction environment cloning.
1880 The clone should compare equal if there are no overrides.
1881 Update the clone independently afterwards and check that
1882 the original remains intact (that is, no dangling
1883 references point to objects in the copied environment).
1884 Clone the original with some construction variable
1885 updates and check that the original remains intact
1886 and the copy has the updated values.
1888 with self
.subTest():
1889 env1
= self
.TestEnvironment(XXX
='x', YYY
='y')
1891 env1copy
= env1
.Clone()
1892 self
.assertEqual(env1copy
, env1
)
1893 self
.assertEqual(env2
, env1
)
1894 env2
.Replace(YYY
= 'yyy')
1895 self
.assertNotEqual(env1
, env2
)
1896 self
.assertEqual(env1
, env1copy
)
1898 env3
= env1
.Clone(XXX
='x3', ZZZ
='z3')
1899 self
.assertNotEqual(env3
, env1
)
1900 self
.assertEqual(env3
.Dictionary('XXX'), 'x3')
1901 self
.assertEqual(env1
.Dictionary('XXX'), 'x')
1902 self
.assertEqual(env3
.Dictionary('YYY'), 'y')
1903 self
.assertEqual(env3
.Dictionary('ZZZ'), 'z3')
1904 self
.assertRaises(KeyError, env1
.Dictionary
, 'ZZZ') # leak test
1905 self
.assertEqual(env1
, env1copy
)
1907 # Ensure that lists and dictionaries are deep copied, but not instances
1908 with self
.subTest():
1913 env1
= self
.TestEnvironment(
1919 env2
.Dictionary('YYY').append(4)
1920 env2
.Dictionary('ZZZ')[5] = 6
1921 self
.assertIs(env1
.Dictionary('XXX'), env2
.Dictionary('XXX'))
1922 self
.assertIn(4, env2
.Dictionary('YYY'))
1923 self
.assertNotIn(4, env1
.Dictionary('YYY'))
1924 self
.assertIn(5, env2
.Dictionary('ZZZ'))
1925 self
.assertNotIn(5, env1
.Dictionary('ZZZ'))
1927 # We also need to look at the special cases in semi_deepcopy()
1928 # used when cloning - these should not leak to the original either
1929 with self
.subTest():
1930 env1
= self
.TestEnvironment(
1931 XXX
=deque([1, 2, 3]),
1933 ZZZ
=UD({1: 2, 3: 4}),
1936 env2
['XXX'].append(4)
1937 env2
['YYY'].append(4)
1939 self
.assertIn(4, env2
['XXX'])
1940 self
.assertNotIn(4, env1
['XXX'])
1941 self
.assertIn(4, env2
['YYY'])
1942 self
.assertNotIn(4, env1
['YYY'])
1943 self
.assertIn(5, env2
['ZZZ'])
1944 self
.assertNotIn(5, env1
['ZZZ'])
1946 # BUILDERS is special...
1947 with self
.subTest():
1948 env1
= self
.TestEnvironment(BUILDERS
={'b1': Builder()})
1949 assert hasattr(env1
, 'b1'), "env1.b1 was not set"
1950 assert env1
.b1
.object == env1
, "b1.object doesn't point to env1"
1951 env2
= env1
.Clone(BUILDERS
= {'b2' : Builder()})
1953 assert hasattr(env1
, 'b1'), "b1 was mistakenly cleared from env1"
1954 assert env1
.b1
.object == env1
, "b1.object was changed"
1955 assert not hasattr(env2
, 'b1'), "b1 was not cleared from env2"
1956 assert hasattr(env2
, 'b2'), "env2.b2 was not set"
1957 assert env2
.b2
.object == env2
, "b2.object doesn't point to env2"
1959 # Ensure that specifying new tools in a copied environment works.
1960 with self
.subTest():
1962 def foo(env
) -> None:
1965 def bar(env
) -> None:
1968 def baz(env
) -> None:
1971 env1
= self
.TestEnvironment(tools
=[foo
])
1973 env3
= env1
.Clone(tools
=[bar
, baz
])
1975 assert env1
.get('FOO') == 1
1976 assert env1
.get('BAR') is None
1977 assert env1
.get('BAZ') is None
1978 assert env2
.get('FOO') == 1
1979 assert env2
.get('BAR') is None
1980 assert env2
.get('BAZ') is None
1981 assert env3
.get('FOO') == 1
1982 assert env3
.get('BAR') == 2
1983 assert env3
.get('BAZ') == 3
1985 # Ensure that recursive variable substitution when copying
1986 # environments works properly.
1987 with self
.subTest():
1988 env1
= self
.TestEnvironment(CCFLAGS
='-DFOO', XYZ
='-DXYZ')
1990 CCFLAGS
='$CCFLAGS -DBAR', XYZ
=['-DABC', 'x $XYZ y', '-DDEF']
1992 x
= env2
.get('CCFLAGS')
1993 assert x
== '-DFOO -DBAR', x
1995 assert x
== ['-DABC', 'x -DXYZ y', '-DDEF'], x
1997 # Ensure that special properties of a class don't get
1999 with self
.subTest():
2000 env1
= self
.TestEnvironment(FLAGS
=CLVar('flag1 flag2'))
2001 x
= env1
.get('FLAGS')
2002 assert x
== ['flag1', 'flag2'], x
2004 env2
.Append(FLAGS
='flag3 flag4')
2005 x
= env2
.get('FLAGS')
2006 assert x
== ['flag1', 'flag2', 'flag3', 'flag4'], x
2007 x
= env1
.get('FLAGS')
2008 assert x
== ['flag1', 'flag2'], x
2010 # Ensure that appending directly to a copied CLVar
2011 # doesn't modify the original.
2012 with self
.subTest():
2013 env1
= self
.TestEnvironment(FLAGS
=CLVar('flag1 flag2'))
2014 x
= env1
.get('FLAGS')
2015 assert x
== ['flag1', 'flag2'], x
2017 env2
['FLAGS'] += ['flag3', 'flag4']
2018 x
= env2
.get('FLAGS')
2019 assert x
== ['flag1', 'flag2', 'flag3', 'flag4'], x
2020 x
= env1
.get('FLAGS')
2021 assert x
== ['flag1', 'flag2'], x
2023 # Test that the environment stores the toolpath and
2024 # re-uses it for copies.
2025 with self
.subTest():
2026 test
= TestCmd
.TestCmd(workdir
='')
2028 test
.write('xxx.py', """\
2035 test
.write('yyy.py', """\
2042 env
= self
.TestEnvironment(tools
=['xxx'], toolpath
=[test
.workpath('')])
2043 assert env
['XXX'] == 'one', env
['XXX']
2044 env
= env
.Clone(tools
=['yyy'])
2045 assert env
['YYY'] == 'two', env
['YYY']
2048 with self
.subTest():
2051 def my_tool(env
, rv
=real_value
) -> None:
2052 assert env
['KEY_THAT_I_WANT'] == rv
[0]
2053 env
['KEY_THAT_I_WANT'] = rv
[0] + 1
2055 env
= self
.TestEnvironment()
2058 env
= env
.Clone(KEY_THAT_I_WANT
=5, tools
=[my_tool
])
2059 assert env
['KEY_THAT_I_WANT'] == real_value
[0], env
['KEY_THAT_I_WANT']
2062 env
= env
.Clone(KEY_THAT_I_WANT
=6, tools
=[my_tool
])
2063 assert env
['KEY_THAT_I_WANT'] == real_value
[0], env
['KEY_THAT_I_WANT']
2065 # test for pull request #150
2066 with self
.subTest():
2067 env
= self
.TestEnvironment()
2068 env
._dict
.pop('BUILDERS')
2069 assert ('BUILDERS' in env
) is False
2072 def test_Detect(self
) -> None:
2073 """Test Detect()ing tools"""
2074 test
= TestCmd
.TestCmd(workdir
= '')
2075 test
.subdir('sub1', 'sub2')
2076 sub1
= test
.workpath('sub1')
2077 sub2
= test
.workpath('sub2')
2079 if sys
.platform
== 'win32':
2080 test
.write(['sub1', 'xxx'], "sub1/xxx\n")
2081 test
.write(['sub2', 'xxx'], "sub2/xxx\n")
2083 env
= self
.TestEnvironment(ENV
= { 'PATH' : [sub1
, sub2
] })
2085 x
= env
.Detect('xxx.exe')
2088 test
.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
2090 env
= self
.TestEnvironment(ENV
= { 'PATH' : [sub1
, sub2
] })
2092 x
= env
.Detect('xxx.exe')
2093 assert x
== 'xxx.exe', x
2095 test
.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
2097 x
= env
.Detect('xxx.exe')
2098 assert x
== 'xxx.exe', x
2101 test
.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
2102 test
.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
2104 env
= self
.TestEnvironment(ENV
= { 'PATH' : [sub1
, sub2
] })
2106 x
= env
.Detect('xxx.exe')
2109 sub2_xxx_exe
= test
.workpath('sub2', 'xxx.exe')
2110 os
.chmod(sub2_xxx_exe
, 0o755)
2112 env
= self
.TestEnvironment(ENV
= { 'PATH' : [sub1
, sub2
] })
2114 x
= env
.Detect('xxx.exe')
2115 assert x
== 'xxx.exe', x
2117 sub1_xxx_exe
= test
.workpath('sub1', 'xxx.exe')
2118 os
.chmod(sub1_xxx_exe
, 0o755)
2120 x
= env
.Detect('xxx.exe')
2121 assert x
== 'xxx.exe', x
2123 env
= self
.TestEnvironment(ENV
= { 'PATH' : [] })
2124 x
= env
.Detect('xxx.exe')
2127 def test_Dictionary(self
) -> None:
2128 """Test retrieval of known construction variables
2130 Fetch them from the Dictionary and check for well-known
2131 defaults that get inserted.
2133 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y', ZZZ
= 'z')
2134 assert env
.Dictionary('XXX') == 'x'
2135 assert env
.Dictionary('YYY') == 'y'
2136 assert env
.Dictionary('XXX', 'ZZZ') == ['x', 'z']
2137 xxx
, zzz
= env
.Dictionary('XXX', 'ZZZ')
2140 # added in NEXT_RELEASE: as_dict flag
2141 with self
.subTest():
2142 expect
= {'XXX': 'x'}
2143 self
.assertEqual(env
.Dictionary('XXX', as_dict
=True), expect
)
2144 with self
.subTest():
2145 expect
= {'XXX': 'x', 'YYY': 'y'}
2146 self
.assertEqual(env
.Dictionary('XXX', 'YYY', as_dict
=True), expect
)
2147 assert 'BUILDERS' in env
.Dictionary()
2148 assert 'CC' in env
.Dictionary()
2149 assert 'CCFLAGS' in env
.Dictionary()
2150 assert 'ENV' in env
.Dictionary()
2152 assert env
['XXX'] == 'x'
2154 assert env
.Dictionary('XXX') == 'foo'
2156 assert 'XXX' not in env
.Dictionary()
2158 def test_FindIxes(self
) -> None:
2159 """Test FindIxes()"""
2160 env
= self
.TestEnvironment(LIBPREFIX
='lib',
2167 paths
= [os
.path
.join('dir', 'libfoo.a'),
2168 os
.path
.join('dir', 'libfoo.so')]
2170 assert paths
[0] == env
.FindIxes(paths
, 'LIBPREFIX', 'LIBSUFFIX')
2171 assert paths
[1] == env
.FindIxes(paths
, 'SHLIBPREFIX', 'SHLIBSUFFIX')
2172 assert None is env
.FindIxes(paths
, 'PREFIX', 'POST')
2174 paths
= ['libfoo.a', 'prefoopost']
2176 assert paths
[0] == env
.FindIxes(paths
, 'LIBPREFIX', 'LIBSUFFIX')
2177 assert None is env
.FindIxes(paths
, 'SHLIBPREFIX', 'SHLIBSUFFIX')
2178 assert paths
[1] == env
.FindIxes(paths
, 'PREFIX', 'SUFFIX')
2180 def test_ParseConfig(self
) -> None:
2181 """Test the ParseConfig() method"""
2182 env
= self
.TestEnvironment(COMMAND
='command',
2183 ASFLAGS
='assembler',
2195 orig_backtick
= env
.backtick
2197 """mocked backtick routine so command is not actually issued.
2199 Just returns the string it was given.
2201 def __init__(self
, save_command
, output
) -> None:
2202 self
.save_command
= save_command
2203 self
.output
= output
2204 def __call__(self
, command
):
2205 self
.save_command
.append(command
)
2210 env
.backtick
= my_backtick(save_command
,
2211 "-I/usr/include/fum -I bar -X\n" + \
2212 "-L/usr/fax -L foo -lxxx -l yyy " + \
2213 "-Wa,-as -Wl,-link " + \
2214 "-Wl,-rpath=rpath1 " + \
2215 "-Wl,-R,rpath2 " + \
2218 "-framework Carbon " + \
2219 "-frameworkdir=fwd1 " + \
2223 "-fmerge-all-constants " + \
2224 "-mno-cygwin -mwindows " + \
2225 "-arch i386 -isysroot /tmp " + \
2226 "-iquote /usr/include/foo1 " + \
2227 "-isystem /usr/include/foo2 " + \
2228 "-idirafter /usr/include/foo3 " + \
2230 "-DFOO -DBAR=value")
2231 env
.ParseConfig("fake $COMMAND")
2232 assert save_command
== ['fake command'], save_command
2233 assert env
['ASFLAGS'] == ['assembler', '-as'], env
['ASFLAGS']
2234 assert env
['CCFLAGS'] == ['', '-X', '-Wa,-as',
2235 '-pthread', '-fmerge-all-constants', '-mno-cygwin',
2236 ('-arch', 'i386'), ('-isysroot', '/tmp'),
2237 ('-iquote', '/usr/include/foo1'),
2238 ('-isystem', '/usr/include/foo2'),
2239 ('-idirafter', '/usr/include/foo3'),
2240 '+DD64'], env
['CCFLAGS']
2241 self
.assertEqual(list(env
['CPPDEFINES']), ['FOO', ['BAR', 'value']])
2242 assert env
['CPPFLAGS'] == ['', '-Wp,-cpp'], env
['CPPFLAGS']
2243 assert env
['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env
['CPPPATH']
2244 assert env
['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env
['FRAMEWORKPATH']
2245 assert env
['FRAMEWORKS'] == ['Carbon'], env
['FRAMEWORKS']
2246 assert env
['LIBPATH'] == ['list', '/usr/fax', 'foo'], env
['LIBPATH']
2247 assert env
['LIBS'] == ['xxx', 'yyy', env
.File('abc')], env
['LIBS']
2248 assert env
['LINKFLAGS'] == ['', '-Wl,-link', '-pthread',
2249 '-fmerge-all-constants',
2250 '-mno-cygwin', '-mwindows',
2252 ('-isysroot', '/tmp'),
2253 '+DD64'], env
['LINKFLAGS']
2254 assert env
['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env
['RPATH']
2256 env
.backtick
= my_backtick([], "-Ibar")
2257 env
.ParseConfig("fake2")
2258 assert env
['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env
['CPPPATH']
2259 env
.ParseConfig("fake2", unique
=0)
2260 assert env
['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env
['CPPPATH']
2262 env
.backtick
= orig_backtick
2264 # check that we can pass our own function,
2265 # and that it works for both values of unique
2267 def my_function(myenv
, flags
, unique
: bool=True) -> None:
2270 args
= json
.loads(flags
)
2272 myenv
.AppendUnique(**args
)
2274 myenv
.Append(**args
)
2276 json_str
= '{"LIBS": ["yyy", "xxx", "yyy"]}'
2278 env
= Environment(LIBS
=['xxx'])
2280 env
.backtick
= my_backtick([], json_str
)
2281 env2
.backtick
= my_backtick([], json_str
)
2283 env
.ParseConfig("foo", my_function
)
2284 assert env
['LIBS'] == ['xxx', 'yyy'], env
['LIBS']
2286 env2
.ParseConfig("foo2", my_function
, unique
=False)
2287 assert env2
['LIBS'] == ['xxx', 'yyy', 'xxx', 'yyy'], env2
['LIBS']
2290 def test_ParseDepends(self
) -> None:
2291 """Test the ParseDepends() method"""
2292 test
= TestCmd
.TestCmd(workdir
= '')
2294 test
.write('single', """
2304 test
.write('multiple', """
2315 env
= self
.TestEnvironment(SINGLE
= test
.workpath('single'))
2319 def my_depends(target
, dependency
, tlist
=tlist
, dlist
=dlist
) -> None:
2320 tlist
.extend(target
)
2321 dlist
.extend(dependency
)
2323 env
.Depends
= my_depends
2325 env
.ParseDepends(test
.workpath('does_not_exist'))
2329 env
.ParseDepends(test
.workpath('does_not_exist'), must_exist
=True)
2332 assert exc_caught
, "did not catch expected IOError"
2337 env
.ParseDepends('$SINGLE', only_one
=True)
2338 t
= list(map(str, tlist
))
2339 d
= list(map(str, dlist
))
2340 assert t
== ['f0'], t
2341 assert d
== ['d1', 'd2', 'd3'], d
2346 env
.ParseDepends(test
.workpath('multiple'))
2347 t
= list(map(str, tlist
))
2348 d
= list(map(str, dlist
))
2349 assert t
== ['f1', 'f2', 'f3', 'f4', 'f5'], t
2350 assert d
== ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d
2354 env
.ParseDepends(test
.workpath('multiple'), only_one
=True)
2355 except SCons
.Errors
.UserError
:
2357 assert exc_caught
, "did not catch expected UserError"
2359 def test_Platform(self
) -> None:
2360 """Test the Platform() method"""
2361 env
= self
.TestEnvironment(WIN32
='win32', NONE
='no-such-platform')
2365 env
.Platform('does_not_exist')
2366 except SCons
.Errors
.UserError
:
2368 assert exc_caught
, "did not catch expected UserError"
2372 env
.Platform('$NONE')
2373 except SCons
.Errors
.UserError
:
2375 assert exc_caught
, "did not catch expected UserError"
2377 env
.Platform('posix')
2378 assert env
['OBJSUFFIX'] == '.o', env
['OBJSUFFIX']
2380 env
.Platform('$WIN32')
2381 assert env
['OBJSUFFIX'] == '.obj', env
['OBJSUFFIX']
2383 def test_Prepend(self
) -> None:
2384 """Test prepending to construction variables in an Environment."""
2386 ('a1', 'A1', 'A1a1'),
2387 ('a2', ['A2'], ['A2', 'a2']),
2388 ('a3', UL(['A3']), UL(['A3', 'a', '3'])),
2391 ('a6', UL([]), UL(['a', '6'])),
2392 ('a7', [''], ['', 'a7']),
2393 ('a8', UL(['']), UL(['', 'a', '8'])),
2395 (['e1'], 'E1', ['E1', 'e1']),
2396 (['e2'], ['E2'], ['E2', 'e2']),
2397 (['e3'], UL(['E3']), UL(['E3', 'e3'])),
2398 (['e4'], '', ['e4']),
2399 (['e5'], [], ['e5']),
2400 (['e6'], UL([]), UL(['e6'])),
2401 (['e7'], [''], ['', 'e7']),
2402 (['e8'], UL(['']), UL(['', 'e8'])),
2404 (UL(['i1']), 'I1', UL(['I', '1', 'i1'])),
2405 (UL(['i2']), ['I2'], UL(['I2', 'i2'])),
2406 (UL(['i3']), UL(['I3']), UL(['I3', 'i3'])),
2407 (UL(['i4']), '', UL(['i4'])),
2408 (UL(['i5']), [], UL(['i5'])),
2409 (UL(['i6']), UL([]), UL(['i6'])),
2410 (UL(['i7']), [''], UL(['', 'i7'])),
2411 (UL(['i8']), UL(['']), UL(['', 'i8'])),
2413 ({'d1':1}, 'D1', {'d1':1, 'D1':None}),
2414 ({'d2':1}, ['D2'], {'d2':1, 'D2':None}),
2415 ({'d3':1}, UL(['D3']), {'d3':1, 'D3':None}),
2416 ({'d4':1}, {'D4':1}, {'d4':1, 'D4':1}),
2417 ({'d5':1}, UD({'D5':1}), UD({'d5':1, 'D5':1})),
2419 (UD({'u1':1}), 'U1', UD({'u1':1, 'U1':None})),
2420 (UD({'u2':1}), ['U2'], UD({'u2':1, 'U2':None})),
2421 (UD({'u3':1}), UL(['U3']), UD({'u3':1, 'U3':None})),
2422 (UD({'u4':1}), {'U4':1}, UD({'u4':1, 'U4':1})),
2423 (UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1})),
2426 ('', ['M2'], ['M2']),
2427 ('', UL(['M3']), UL(['M3'])),
2430 ('', UL([]), UL([])),
2432 ('', UL(['']), UL([''])),
2435 ([], ['N2'], ['N2']),
2436 ([], UL(['N3']), UL(['N3'])),
2439 ([], UL([]), UL([])),
2441 ([], UL(['']), UL([''])),
2443 (UL([]), 'O1', UL(['O', '1'])),
2444 (UL([]), ['O2'], UL(['O2'])),
2445 (UL([]), UL(['O3']), UL(['O3'])),
2446 (UL([]), '', UL([])),
2447 (UL([]), [], UL([])),
2448 (UL([]), UL([]), UL([])),
2449 (UL([]), [''], UL([''])),
2450 (UL([]), UL(['']), UL([''])),
2452 ([''], 'P1', ['P1', '']),
2453 ([''], ['P2'], ['P2', '']),
2454 ([''], UL(['P3']), UL(['P3', ''])),
2457 ([''], UL([]), UL([''])),
2458 ([''], [''], ['', '']),
2459 ([''], UL(['']), UL(['', ''])),
2461 (UL(['']), 'Q1', UL(['Q', '1', ''])),
2462 (UL(['']), ['Q2'], UL(['Q2', ''])),
2463 (UL(['']), UL(['Q3']), UL(['Q3', ''])),
2464 (UL(['']), '', UL([''])),
2465 (UL(['']), [], UL([''])),
2466 (UL(['']), UL([]), UL([''])),
2467 (UL(['']), [''], UL(['', ''])),
2468 (UL(['']), UL(['']), UL(['', ''])),
2473 for input, prepend
, expect
in cases
:
2474 env
['XXX'] = copy
.copy(input)
2476 env
.Prepend(XXX
=prepend
)
2477 except Exception as e
:
2478 if failed
== 0: print()
2479 print(" %s Prepend %s exception: %s" % \
2480 (repr(input), repr(prepend
), e
))
2484 if result
!= expect
:
2485 if failed
== 0: print()
2486 print(" %s Prepend %s => %s did not match %s" % \
2487 (repr(input), repr(prepend
), repr(result
), repr(expect
)))
2489 assert failed
== 0, "%d Prepend() cases failed" % failed
2491 env
['UL'] = UL(['foo'])
2492 env
.Prepend(UL
= 'bar')
2494 assert isinstance(result
, UL
), repr(result
)
2495 assert result
== ['b', 'a', 'r', 'foo'], result
2497 env
['CLVar'] = CLVar(['foo'])
2498 env
.Prepend(CLVar
= 'bar')
2499 result
= env
['CLVar']
2500 assert isinstance(result
, CLVar
), repr(result
)
2501 assert result
== ['bar', 'foo'], result
2503 env3
= self
.TestEnvironment(X
= {'x1' : 7})
2504 env3
.Prepend(X
= {'x1' : 8, 'x2' : 9}, Y
= {'y1' : 10})
2505 assert env3
['X'] == {'x1': 8, 'x2' : 9}, env3
['X']
2506 assert env3
['Y'] == {'y1': 10}, env3
['Y']
2510 env4
= self
.TestEnvironment(BUILDERS
= {'z1' : z1
})
2511 env4
.Prepend(BUILDERS
= {'z2' : z2
})
2512 assert env4
['BUILDERS'] == {'z1' : z1
, 'z2' : z2
}, env4
['BUILDERS']
2513 assert hasattr(env4
, 'z1')
2514 assert hasattr(env4
, 'z2')
2516 def test_PrependENVPath(self
) -> None:
2517 """Test prepending to an ENV path."""
2518 env1
= self
.TestEnvironment(
2519 ENV
={'PATH': r
'C:\dir\num\one;C:\dir\num\two'},
2520 MYENV
={'MYPATH': r
'C:\mydir\num\one;C:\mydir\num\two'},
2523 # have to include the pathsep here so that the test will work on UNIX too.
2524 env1
.PrependENVPath('PATH', r
'C:\dir\num\two', sep
=';')
2525 env1
.PrependENVPath('PATH', r
'C:\dir\num\three', sep
=';')
2527 env1
['ENV']['PATH'] == r
'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one'
2528 ), env1
['ENV']['PATH']
2531 # add nonexisting - at front
2532 env1
.PrependENVPath('MYPATH', r
'C:\mydir\num\three', 'MYENV', sep
=';')
2534 env1
['MYENV']['MYPATH'] == r
'C:\mydir\num\three;C:\mydir\num\one;C:\mydir\num\two'
2535 ), env1
['MYENV']['MYPATH']
2537 # add existing - moves to the front
2538 env1
.PrependENVPath('MYPATH', r
'C:\mydir\num\one', 'MYENV', sep
=';')
2539 # this should do nothing since delete_existing is false
2540 env1
.PrependENVPath(
2541 'MYPATH', r
'C:\mydir\num\three', 'MYENV', sep
=';', delete_existing
=False
2544 env1
['MYENV']['MYPATH'] == r
'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two'
2545 ), env1
['MYENV']['MYPATH']
2547 test
= TestCmd
.TestCmd(workdir
='')
2548 test
.subdir('sub1', 'sub2')
2549 p
= env1
['ENV']['PATH']
2550 env1
.PrependENVPath('PATH', '#sub1', sep
=';')
2551 env1
.PrependENVPath('PATH', env1
.fs
.Dir('sub2'), sep
=';')
2552 assert env1
['ENV']['PATH'] == 'sub2;sub1;' + p
, env1
['ENV']['PATH']
2555 def test_PrependUnique(self
) -> None:
2556 """Test prepending unique values to construction variables
2558 This strips values that are already present when lists are
2560 env
= self
.TestEnvironment(AAA1
= 'a1',
2572 DDD1
= ['a', 'b', 'c'])
2573 env
.PrependUnique(AAA1
= 'a1',
2575 AAA3
= ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
2580 BBB3
= ['b3', 'b', 'c', 'b3'],
2586 assert env
['AAA1'] == 'a1a1', env
['AAA1']
2587 assert env
['AAA2'] == ['a2'], env
['AAA2']
2588 assert env
['AAA3'] == ['c', 'b', 'a3'], env
['AAA3']
2589 assert env
['AAA4'] == 'a4.newa4', env
['AAA4']
2590 assert env
['AAA5'] == ['a5.new', 'a5'], env
['AAA5']
2591 assert env
['BBB1'] == ['b1'], env
['BBB1']
2592 assert env
['BBB2'] == ['b2'], env
['BBB2']
2593 assert env
['BBB3'] == ['b', 'c', 'b3'], env
['BBB3']
2594 assert env
['BBB4'] == ['b4.new', 'b4'], env
['BBB4']
2595 assert env
['BBB5'] == ['b5.new', 'b5'], env
['BBB5']
2596 assert env
['CCC1'] == 'c1', env
['CCC1']
2597 assert env
['CCC2'] == ['c2'], env
['CCC2']
2598 assert env
['DDD1'] == ['a', 'b', 'c'], env
['DDD1']
2600 env
.PrependUnique(DDD1
='b', delete_existing
=True)
2601 assert env
['DDD1'] == ['b', 'a', 'c'], env
['DDD1'] # b moves to front
2603 env
.PrependUnique(DDD1
=['a', 'c'], delete_existing
=True)
2604 assert env
['DDD1'] == ['a', 'c', 'b'], env
['DDD1'] # a & c move to front
2606 env
.PrependUnique(DDD1
=['d', 'e', 'd'], delete_existing
=True)
2607 assert env
['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env
['DDD1']
2609 # issue regression: substrings should not be deleted
2610 env
.PrependUnique(BBB4
='b4.newer', delete_existing
=True)
2611 assert env
['BBB4'] == ['b4.newer', 'b4.new', 'b4'], env
['BBB4']
2613 env
['CLVar'] = CLVar([])
2614 env
.PrependUnique(CLVar
='bar')
2615 result
= env
['CLVar']
2616 assert isinstance(result
, CLVar
), repr(result
)
2617 assert result
== ['bar'], result
2619 env
['CLVar'] = CLVar(['abc'])
2620 env
.PrependUnique(CLVar
='bar')
2621 result
= env
['CLVar']
2622 assert isinstance(result
, CLVar
), repr(result
)
2623 assert result
== ['bar', 'abc'], result
2625 env
['CLVar'] = CLVar(['bar'])
2626 env
.PrependUnique(CLVar
='bar')
2627 result
= env
['CLVar']
2628 assert isinstance(result
, CLVar
), repr(result
)
2629 assert result
== ['bar'], result
2632 def test_Replace(self
) -> None:
2633 """Test replacing construction variables in an Environment
2635 After creation of the Environment, of course.
2637 env1
= self
.TestEnvironment(AAA
= 'a', BBB
= 'b')
2638 env1
.Replace(BBB
= 'bbb', CCC
= 'ccc')
2640 env2
= self
.TestEnvironment(AAA
= 'a', BBB
= 'bbb', CCC
= 'ccc')
2641 assert env1
== env2
, diff_env(env1
, env2
)
2645 env3
= self
.TestEnvironment(BUILDERS
= {'b1' : b1
})
2646 assert hasattr(env3
, 'b1'), "b1 was not set"
2647 env3
.Replace(BUILDERS
= {'b2' : b2
})
2648 assert not hasattr(env3
, 'b1'), "b1 was not cleared"
2649 assert hasattr(env3
, 'b2'), "b2 was not set"
2651 def test_ReplaceIxes(self
) -> None:
2652 """Test ReplaceIxes()"""
2653 env
= self
.TestEnvironment(LIBPREFIX
='lib',
2660 assert 'libfoo.a' == env
.ReplaceIxes('libfoo.so',
2661 'SHLIBPREFIX', 'SHLIBSUFFIX',
2662 'LIBPREFIX', 'LIBSUFFIX')
2664 assert os
.path
.join('dir', 'libfoo.a') == env
.ReplaceIxes(os
.path
.join('dir', 'libfoo.so'),
2665 'SHLIBPREFIX', 'SHLIBSUFFIX',
2666 'LIBPREFIX', 'LIBSUFFIX')
2668 assert 'libfoo.a' == env
.ReplaceIxes('prefoopost',
2670 'LIBPREFIX', 'LIBSUFFIX')
2672 def test_SetDefault(self
) -> None:
2673 """Test the SetDefault method"""
2674 env
= self
.TestEnvironment(tools
= [])
2675 env
.SetDefault(V1
= 1)
2676 env
.SetDefault(V1
= 2)
2677 assert env
['V1'] == 1
2679 env
.SetDefault(V2
= 1)
2680 assert env
['V2'] == 2
2682 def test_Tool(self
) -> None:
2683 """Test the Tool() method"""
2684 env
= self
.TestEnvironment(LINK
='link', NONE
='no-such-tool')
2688 tool
= env
.Tool('does_not_exist')
2689 except SCons
.Errors
.UserError
:
2692 assert isinstance(tool
, SCons
.Tool
.Tool
)
2693 assert exc_caught
, "did not catch expected UserError"
2698 except SCons
.Errors
.UserError
:
2700 assert exc_caught
, "did not catch expected UserError"
2702 # Use a non-existent toolpath directory just to make sure we
2703 # can call Tool() with the keyword argument.
2704 env
.Tool('cc', toolpath
=['/no/such/directory'])
2705 assert env
['CC'] == 'cc', env
['CC']
2708 assert env
['LINK'] == '$SMARTLINK', env
['LINK']
2710 # Test that the environment stores the toolpath and
2711 # re-uses it for later calls.
2712 test
= TestCmd
.TestCmd(workdir
= '')
2714 test
.write('xxx.py', """\
2721 test
.write('yyy.py', """\
2728 env
= self
.TestEnvironment(tools
=['xxx'], toolpath
=[test
.workpath('')])
2729 assert env
['XXX'] == 'one', env
['XXX']
2731 assert env
['YYY'] == 'two', env
['YYY']
2733 def test_WhereIs(self
) -> None:
2734 """Test the WhereIs() method"""
2735 test
= TestCmd
.TestCmd(workdir
= '')
2737 sub1_xxx_exe
= test
.workpath('sub1', 'xxx.exe')
2738 sub2_xxx_exe
= test
.workpath('sub2', 'xxx.exe')
2739 sub3_xxx_exe
= test
.workpath('sub3', 'xxx.exe')
2740 sub4_xxx_exe
= test
.workpath('sub4', 'xxx.exe')
2742 test
.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
2744 if sys
.platform
!= 'win32':
2745 test
.write(sub1_xxx_exe
, "\n")
2747 os
.mkdir(sub2_xxx_exe
)
2749 test
.write(sub3_xxx_exe
, "\n")
2750 os
.chmod(sub3_xxx_exe
, 0o777)
2752 test
.write(sub4_xxx_exe
, "\n")
2753 os
.chmod(sub4_xxx_exe
, 0o777)
2755 env_path
= os
.environ
['PATH']
2757 pathdirs_1234
= [ test
.workpath('sub1'),
2758 test
.workpath('sub2'),
2759 test
.workpath('sub3'),
2760 test
.workpath('sub4'),
2761 ] + env_path
.split(os
.pathsep
)
2763 pathdirs_1243
= [ test
.workpath('sub1'),
2764 test
.workpath('sub2'),
2765 test
.workpath('sub4'),
2766 test
.workpath('sub3'),
2767 ] + env_path
.split(os
.pathsep
)
2769 path
= os
.pathsep
.join(pathdirs_1234
)
2770 env
= self
.TestEnvironment(ENV
= {'PATH' : path
})
2771 wi
= env
.WhereIs('')
2773 wi
= env
.WhereIs('xxx.exe')
2774 assert wi
== test
.workpath(sub3_xxx_exe
), wi
2775 wi
= env
.WhereIs('xxx.exe', pathdirs_1243
)
2776 assert wi
== test
.workpath(sub4_xxx_exe
), wi
2777 wi
= env
.WhereIs('xxx.exe', os
.pathsep
.join(pathdirs_1243
))
2778 assert wi
== test
.workpath(sub4_xxx_exe
), wi
2780 wi
= env
.WhereIs('xxx.exe', reject
= sub3_xxx_exe
)
2781 assert wi
== test
.workpath(sub4_xxx_exe
), wi
2782 wi
= env
.WhereIs('xxx.exe', pathdirs_1243
, reject
= sub3_xxx_exe
)
2783 assert wi
== test
.workpath(sub4_xxx_exe
), wi
2785 path
= os
.pathsep
.join(pathdirs_1243
)
2786 env
= self
.TestEnvironment(ENV
= {'PATH' : path
})
2787 wi
= env
.WhereIs('xxx.exe')
2788 assert wi
== test
.workpath(sub4_xxx_exe
), wi
2789 wi
= env
.WhereIs('xxx.exe', pathdirs_1234
)
2790 assert wi
== test
.workpath(sub3_xxx_exe
), wi
2791 wi
= env
.WhereIs('xxx.exe', os
.pathsep
.join(pathdirs_1234
))
2792 assert wi
== test
.workpath(sub3_xxx_exe
), wi
2794 if sys
.platform
== 'win32':
2795 wi
= env
.WhereIs('xxx', pathext
= '')
2796 assert wi
is None, wi
2798 wi
= env
.WhereIs('xxx', pathext
= '.exe')
2799 assert wi
== test
.workpath(sub4_xxx_exe
), wi
2801 wi
= env
.WhereIs('xxx', path
= pathdirs_1234
, pathext
= '.BAT;.EXE')
2802 assert wi
.lower() == test
.workpath(sub3_xxx_exe
).lower(), wi
2804 # Test that we return a normalized path even when
2805 # the path contains forward slashes.
2806 forward_slash
= test
.workpath('') + '/sub3'
2807 wi
= env
.WhereIs('xxx', path
= forward_slash
, pathext
= '.EXE')
2808 assert wi
.lower() == test
.workpath(sub3_xxx_exe
).lower(), wi
2812 def test_Action(self
) -> None:
2813 """Test the Action() method"""
2816 env
= self
.TestEnvironment(FOO
= 'xyzzy')
2818 a
= env
.Action('foo')
2820 assert a
.__class
__ is SCons
.Action
.CommandAction
, a
.__class
__
2822 a
= env
.Action('$FOO')
2824 assert a
.__class
__ is SCons
.Action
.CommandAction
, a
.__class
__
2826 a
= env
.Action('$$FOO')
2828 assert a
.__class
__ is SCons
.Action
.LazyAction
, a
.__class
__
2830 a
= env
.Action(['$FOO', 'foo'])
2832 assert a
.__class
__ is SCons
.Action
.ListAction
, a
.__class
__
2834 def func(arg
) -> None:
2836 a
= env
.Action(func
)
2838 assert a
.__class
__ is SCons
.Action
.FunctionAction
, a
.__class
__
2840 def test_AddPostAction(self
) -> None:
2841 """Test the AddPostAction() method"""
2842 env
= self
.TestEnvironment(FOO
='fff', BAR
='bbb')
2844 n
= env
.AddPostAction('$FOO', lambda x
: x
)
2845 assert str(n
[0]) == 'fff', n
[0]
2847 n
= env
.AddPostAction(['ggg', '$BAR'], lambda x
: x
)
2848 assert str(n
[0]) == 'ggg', n
[0]
2849 assert str(n
[1]) == 'bbb', n
[1]
2851 def test_AddPreAction(self
) -> None:
2852 """Test the AddPreAction() method"""
2853 env
= self
.TestEnvironment(FOO
='fff', BAR
='bbb')
2855 n
= env
.AddPreAction('$FOO', lambda x
: x
)
2856 assert str(n
[0]) == 'fff', n
[0]
2858 n
= env
.AddPreAction(['ggg', '$BAR'], lambda x
: x
)
2859 assert str(n
[0]) == 'ggg', n
[0]
2860 assert str(n
[1]) == 'bbb', n
[1]
2862 def test_Alias(self
) -> None:
2863 """Test the Alias() method"""
2864 env
= self
.TestEnvironment(FOO
='kkk', BAR
='lll', EA
='export_alias')
2866 tgt
= env
.Alias('new_alias')[0]
2867 assert str(tgt
) == 'new_alias', tgt
2868 assert tgt
.sources
== [], tgt
.sources
2869 assert not hasattr(tgt
, 'builder'), tgt
.builder
2871 tgt
= env
.Alias('None_alias', None)[0]
2872 assert str(tgt
) == 'None_alias', tgt
2873 assert tgt
.sources
== [], tgt
.sources
2875 tgt
= env
.Alias('empty_list', [])[0]
2876 assert str(tgt
) == 'empty_list', tgt
2877 assert tgt
.sources
== [], tgt
.sources
2879 tgt
= env
.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
2880 assert str(tgt
) == 'export_alias', tgt
2881 assert len(tgt
.sources
) == 2, list(map(str, tgt
.sources
))
2882 assert str(tgt
.sources
[0]) == 'asrc1', list(map(str, tgt
.sources
))
2883 assert str(tgt
.sources
[1]) == 'kkk', list(map(str, tgt
.sources
))
2885 n
= env
.Alias(tgt
, source
= ['$BAR', 'asrc4'])[0]
2887 assert len(tgt
.sources
) == 4, list(map(str, tgt
.sources
))
2888 assert str(tgt
.sources
[2]) == 'lll', list(map(str, tgt
.sources
))
2889 assert str(tgt
.sources
[3]) == 'asrc4', list(map(str, tgt
.sources
))
2891 n
= env
.Alias('$EA', 'asrc5')[0]
2893 assert len(tgt
.sources
) == 5, list(map(str, tgt
.sources
))
2894 assert str(tgt
.sources
[4]) == 'asrc5', list(map(str, tgt
.sources
))
2896 t1
, t2
= env
.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
2897 assert str(t1
) == 't1', t1
2898 assert str(t2
) == 't2', t2
2899 assert len(t1
.sources
) == 2, list(map(str, t1
.sources
))
2900 assert str(t1
.sources
[0]) == 'asrc6', list(map(str, t1
.sources
))
2901 assert str(t1
.sources
[1]) == 'asrc7', list(map(str, t1
.sources
))
2902 assert len(t2
.sources
) == 2, list(map(str, t2
.sources
))
2903 assert str(t2
.sources
[0]) == 'asrc6', list(map(str, t2
.sources
))
2904 assert str(t2
.sources
[1]) == 'asrc7', list(map(str, t2
.sources
))
2906 tgt
= env
.Alias('add', 's1')
2907 tgt
= env
.Alias('add', 's2')[0]
2908 s
= list(map(str, tgt
.sources
))
2909 assert s
== ['s1', 's2'], s
2910 tgt
= env
.Alias(tgt
, 's3')[0]
2911 s
= list(map(str, tgt
.sources
))
2912 assert s
== ['s1', 's2', 's3'], s
2914 tgt
= env
.Alias('act', None, "action1")[0]
2915 s
= str(tgt
.builder
.action
)
2916 assert s
== "action1", s
2917 tgt
= env
.Alias('act', None, "action2")[0]
2918 s
= str(tgt
.builder
.action
)
2919 assert s
== "action1\naction2", s
2920 tgt
= env
.Alias(tgt
, None, "action3")[0]
2921 s
= str(tgt
.builder
.action
)
2922 assert s
== "action1\naction2\naction3", s
2924 def test_AlwaysBuild(self
) -> None:
2925 """Test the AlwaysBuild() method"""
2926 env
= self
.TestEnvironment(FOO
='fff', BAR
='bbb')
2927 t
= env
.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
2928 env
.fs
.Dir('dir'), env
.fs
.File('file'))
2929 assert t
[0].__class
__.__name
__ == 'Entry'
2930 assert t
[0].get_internal_path() == 'a'
2931 assert t
[0].always_build
2932 assert t
[1].__class
__.__name
__ == 'Entry'
2933 assert t
[1].get_internal_path() == 'bfff'
2934 assert t
[1].always_build
2935 assert t
[2].__class
__.__name
__ == 'Entry'
2936 assert t
[2].get_internal_path() == 'c'
2937 assert t
[2].always_build
2938 assert t
[3].__class
__.__name
__ == 'Entry'
2939 assert t
[3].get_internal_path() == 'd'
2940 assert t
[3].always_build
2941 assert t
[4].__class
__.__name
__ == 'Entry'
2942 assert t
[4].get_internal_path() == 'bbb'
2943 assert t
[4].always_build
2944 assert t
[5].__class
__.__name
__ == 'Dir'
2945 assert t
[5].get_internal_path() == 'dir'
2946 assert t
[5].always_build
2947 assert t
[6].__class
__.__name
__ == 'File'
2948 assert t
[6].get_internal_path() == 'file'
2949 assert t
[6].always_build
2951 def test_VariantDir(self
) -> None:
2952 """Test the VariantDir() method"""
2954 def Dir(self
, name
):
2956 def VariantDir(self
, variant_dir
, src_dir
, duplicate
) -> None:
2957 self
.variant_dir
= variant_dir
2958 self
.src_dir
= src_dir
2959 self
.duplicate
= duplicate
2961 env
= self
.TestEnvironment(FOO
= 'fff', BAR
= 'bbb')
2964 env
.VariantDir('build', 'src')
2965 assert env
.fs
.variant_dir
== 'build', env
.fs
.variant_dir
2966 assert env
.fs
.src_dir
== 'src', env
.fs
.src_dir
2967 assert env
.fs
.duplicate
== 1, env
.fs
.duplicate
2969 env
.VariantDir('build${FOO}', '${BAR}src', 0)
2970 assert env
.fs
.variant_dir
== 'buildfff', env
.fs
.variant_dir
2971 assert env
.fs
.src_dir
== 'bbbsrc', env
.fs
.src_dir
2972 assert env
.fs
.duplicate
== 0, env
.fs
.duplicate
2974 def test_Builder(self
) -> None:
2975 """Test the Builder() method"""
2976 env
= self
.TestEnvironment(FOO
= 'xyzzy')
2978 b
= env
.Builder(action
= 'foo')
2979 assert b
is not None, b
2981 b
= env
.Builder(action
= '$FOO')
2982 assert b
is not None, b
2984 b
= env
.Builder(action
= ['$FOO', 'foo'])
2985 assert b
is not None, b
2987 def func(arg
) -> None:
2989 b
= env
.Builder(action
= func
)
2990 assert b
is not None, b
2991 b
= env
.Builder(generator
= func
)
2992 assert b
is not None, b
2994 def test_CacheDir(self
) -> None:
2995 """Test the CacheDir() method"""
2997 test
= TestCmd
.TestCmd(workdir
= '')
2999 test_cachedir
= os
.path
.join(test
.workpath(),'CacheDir')
3000 test_cachedir_config
= os
.path
.join(test_cachedir
, 'config')
3001 test_foo
= os
.path
.join(test
.workpath(), 'foo-cachedir')
3002 test_foo_config
= os
.path
.join(test_foo
,'config')
3003 test_foo1
= os
.path
.join(test
.workpath(), 'foo1-cachedir')
3004 test_foo1_config
= os
.path
.join(test_foo1
, 'config')
3006 env
= self
.TestEnvironment(CD
= test_cachedir
)
3008 env
.CacheDir(test_foo
)
3009 assert env
._CacheDir
_path
== test_foo
, env
._CacheDir
_path
3010 assert os
.path
.isfile(test_foo_config
), "No file %s"%test_foo_config
3013 assert env
._CacheDir
_path
== test_cachedir
, env
._CacheDir
_path
3014 assert os
.path
.isfile(test_cachedir_config
), "No file %s"%test_cachedir_config
3016 # Now verify that -n/-no_exec wil prevent the CacheDir/config from being created
3018 SCons
.Action
.execute_actions
= False
3019 env
.CacheDir(test_foo1
)
3020 assert env
._CacheDir
_path
== test_foo1
, env
._CacheDir
_path
3021 assert not os
.path
.isfile(test_foo1_config
), "No file %s"%test_foo1_config
3024 def test_Clean(self
) -> None:
3025 """Test the Clean() method"""
3026 env
= self
.TestEnvironment(FOO
= 'fff', BAR
= 'bbb')
3028 CT
= SCons
.Environment
.CleanTargets
3030 foo
= env
.arg2nodes('foo')[0]
3031 fff
= env
.arg2nodes('fff')[0]
3033 t
= env
.Clean('foo', 'aaa')
3034 l
= list(map(str, CT
[foo
]))
3035 assert l
== ['aaa'], l
3037 t
= env
.Clean(foo
, ['$BAR', 'ccc'])
3038 l
= list(map(str, CT
[foo
]))
3039 assert l
== ['aaa', 'bbb', 'ccc'], l
3041 eee
= env
.arg2nodes('eee')[0]
3043 t
= env
.Clean('$FOO', 'ddd')
3044 l
= list(map(str, CT
[fff
]))
3045 assert l
== ['ddd'], l
3046 t
= env
.Clean(fff
, [eee
, 'fff'])
3047 l
= list(map(str, CT
[fff
]))
3048 assert l
== ['ddd', 'eee', 'fff'], l
3050 def test_Command(self
) -> None:
3051 """Test the Command() method."""
3053 t
= env
.Command(target
='foo.out', source
=['foo1.in', 'foo2.in'],
3054 action
='buildfoo $target $source')[0]
3055 assert t
.builder
is not None
3056 assert t
.builder
.action
.__class
__.__name
__ == 'CommandAction'
3057 assert t
.builder
.action
.cmd_list
== 'buildfoo $target $source'
3058 assert 'foo1.in' in [x
.get_internal_path() for x
in t
.sources
]
3059 assert 'foo2.in' in [x
.get_internal_path() for x
in t
.sources
]
3061 sub
= env
.fs
.Dir('sub')
3062 t
= env
.Command(target
='bar.out', source
='sub',
3063 action
='buildbar $target $source')[0]
3064 assert 'sub' in [x
.get_internal_path() for x
in t
.sources
]
3066 def testFunc(env
, target
, source
) -> int:
3067 assert str(target
[0]) == 'foo.out'
3068 srcs
= list(map(str, source
))
3069 assert 'foo1.in' in srcs
and 'foo2.in' in srcs
, srcs
3072 # avoid spurious output from action
3073 act
= env
.Action(testFunc
, cmdstr
=None)
3074 t
= env
.Command(target
='foo.out', source
=['foo1.in','foo2.in'],
3076 assert t
.builder
is not None
3077 assert t
.builder
.action
.__class
__.__name
__ == 'FunctionAction'
3079 assert 'foo1.in' in [x
.get_internal_path() for x
in t
.sources
]
3080 assert 'foo2.in' in [x
.get_internal_path() for x
in t
.sources
]
3083 def test2(baz
, x
=x
) -> None:
3085 env
= self
.TestEnvironment(TEST2
= test2
)
3086 t
= env
.Command(target
='baz.out', source
='baz.in',
3087 action
='${TEST2(XYZ)}',
3088 XYZ
='magic word')[0]
3089 assert t
.builder
is not None
3091 assert x
[0] == 'magic word', x
3093 t
= env
.Command(target
='${X}.out', source
='${X}.in',
3096 assert str(t
) == 'xxx.out', str(t
)
3097 assert 'xxx.in' in [x
.get_internal_path() for x
in t
.sources
]
3099 env
= self
.TestEnvironment(source_scanner
= 'should_not_find_this')
3100 t
= env
.Command(target
='file.out', source
='file.in',
3102 source_scanner
= 'fake')[0]
3103 assert t
.builder
.source_scanner
== 'fake', t
.builder
.source_scanner
3105 def test_Configure(self
) -> None:
3106 """Test the Configure() method"""
3107 # Configure() will write to a local temporary file.
3108 test
= TestCmd
.TestCmd(workdir
= '')
3112 os
.chdir(test
.workpath())
3114 env
= self
.TestEnvironment(FOO
= 'xyzzy')
3116 def func(arg
) -> None:
3120 assert c
is not None, c
3123 c
= env
.Configure(custom_tests
= {'foo' : func
, '$FOO' : func
})
3124 assert c
is not None, c
3125 assert hasattr(c
, 'foo')
3126 assert hasattr(c
, 'xyzzy')
3131 def test_Depends(self
) -> None:
3132 """Test the explicit Depends method."""
3133 env
= self
.TestEnvironment(FOO
= 'xxx', BAR
='yyy')
3138 t
= env
.Depends(target
='EnvironmentTest.py',
3139 dependency
='Environment.py')[0]
3140 assert t
.__class
__.__name
__ == 'Entry', t
.__class
__.__name
__
3141 assert t
.get_internal_path() == 'EnvironmentTest.py'
3142 assert len(t
.depends
) == 1
3144 assert d
.__class
__.__name
__ == 'Entry', d
.__class
__.__name
__
3145 assert d
.get_internal_path() == 'Environment.py'
3147 t
= env
.Depends(target
='${FOO}.py', dependency
='${BAR}.py')[0]
3148 assert t
.__class
__.__name
__ == 'File', t
.__class
__.__name
__
3149 assert t
.get_internal_path() == 'xxx.py'
3150 assert len(t
.depends
) == 1
3152 assert d
.__class
__.__name
__ == 'File', d
.__class
__.__name
__
3153 assert d
.get_internal_path() == 'yyy.py'
3155 t
= env
.Depends(target
='dir1', dependency
='dir2')[0]
3156 assert t
.__class
__.__name
__ == 'Dir', t
.__class
__.__name
__
3157 assert t
.get_internal_path() == 'dir1'
3158 assert len(t
.depends
) == 1
3160 assert d
.__class
__.__name
__ == 'Dir', d
.__class
__.__name
__
3161 assert d
.get_internal_path() == 'dir2'
3163 def test_Dir(self
) -> None:
3164 """Test the Dir() method"""
3166 def Dir(self
, name
) -> str:
3167 return 'Dir(%s)' % name
3169 env
= self
.TestEnvironment(FOO
= 'foodir', BAR
= 'bardir')
3173 assert d
== 'Dir(d)', d
3176 assert d
== 'Dir(foodir)', d
3178 d
= env
.Dir('${BAR}_$BAR')
3179 assert d
== 'Dir(bardir_bardir)', d
3181 d
= env
.Dir(['dir1'])
3182 assert d
== ['Dir(dir1)'], d
3184 d
= env
.Dir(['dir1', 'dir2'])
3185 assert d
== ['Dir(dir1)', 'Dir(dir2)'], d
3187 def test_NoClean(self
) -> None:
3188 """Test the NoClean() method"""
3189 env
= self
.TestEnvironment(FOO
='ggg', BAR
='hhh')
3192 t
= env
.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3194 assert t
[0].__class
__.__name
__ == 'Entry', t
[0].__class
__.__name
__
3195 assert t
[0].get_internal_path() == 'p_a'
3197 assert t
[1].__class
__.__name
__ == 'Dir', t
[1].__class
__.__name
__
3198 assert t
[1].get_internal_path() == 'p_hhhb'
3200 assert t
[2].__class
__.__name
__ == 'Entry', t
[2].__class
__.__name
__
3201 assert t
[2].get_internal_path() == 'p_c'
3203 assert t
[3].__class
__.__name
__ == 'File', t
[3].__class
__.__name
__
3204 assert t
[3].get_internal_path() == 'p_d'
3206 assert t
[4].__class
__.__name
__ == 'Entry', t
[4].__class
__.__name
__
3207 assert t
[4].get_internal_path() == 'p_ggg'
3210 def test_Dump(self
) -> None:
3211 """Test the Dump() method"""
3212 env
= self
.TestEnvironment(FOO
='foo', FOOFLAGS
=CLVar('--bar --baz'))
3214 # changed in NEXT_RELEASE: single arg now displays as a dict,
3215 # not a bare value; more than one arg is allowed.
3216 with self
.subTest(): # one-arg version
3217 self
.assertEqual(env
.Dump('FOO'), "{'FOO': 'foo'}")
3219 with self
.subTest(): # multi-arg version
3220 expect
= "{'FOO': 'foo', 'FOOFLAGS': ['--bar', '--baz']}"
3221 self
.assertEqual(env
.Dump('FOO', 'FOOFLAGS'), expect
)
3223 with self
.subTest(): # no-arg version
3224 self
.assertGreater(len(env
.Dump()), 200)
3226 with self
.subTest(): # one-arg JSON version, simple value
3227 expect
= '{\n "FOO": "foo"\n}'
3228 self
.assertEqual(env
.Dump('FOO', format
='json'), expect
)
3230 with self
.subTest(): # one-arg JSON version, list value
3231 expect
= '{\n "FOOFLAGS": [\n "--bar",\n "--baz"\n ]\n}'
3232 self
.assertEqual(env
.Dump('FOOFLAGS', format
='json'), expect
)
3234 with self
.subTest(): # multi--arg JSON version, list value
3235 expect
= '{\n "FOO": "foo",\n "FOOFLAGS": [\n "--bar",\n "--baz"\n ]\n}'
3236 self
.assertEqual(env
.Dump('FOO', 'FOOFLAGS', format
='json'), expect
)
3238 with self
.subTest(): # full JSON version
3240 env_dict
= json
.loads(env
.Dump(format
='json'))
3241 self
.assertEqual(env_dict
['FOO'], 'foo')
3243 with self
.subTest(): # unsupported format type
3244 with self
.assertRaises(ValueError) as cm
:
3245 env
.Dump(format
='markdown')
3246 self
.assertEqual(str(cm
.exception
), "Unsupported serialization format: markdown.")
3249 def test_Environment(self
) -> None:
3250 """Test the Environment() method"""
3251 env
= self
.TestEnvironment(FOO
= 'xxx', BAR
= 'yyy')
3253 e2
= env
.Environment(X
= '$FOO', Y
= '$BAR')
3254 assert e2
['X'] == 'xxx', e2
['X']
3255 assert e2
['Y'] == 'yyy', e2
['Y']
3257 def test_Execute(self
) -> None:
3258 """Test the Execute() method"""
3261 def __init__(self
, *args
, **kw
) -> None:
3263 def __call__(self
, target
, source
, env
) -> str:
3264 return "%s executed" % self
.args
3267 env
.Action
= MyAction
3269 result
= env
.Execute("foo")
3270 assert result
== "foo executed", result
3272 def test_Entry(self
) -> None:
3273 """Test the Entry() method"""
3275 def Entry(self
, name
) -> str:
3276 return 'Entry(%s)' % name
3278 env
= self
.TestEnvironment(FOO
= 'fooentry', BAR
= 'barentry')
3282 assert e
== 'Entry(e)', e
3284 e
= env
.Entry('$FOO')
3285 assert e
== 'Entry(fooentry)', e
3287 e
= env
.Entry('${BAR}_$BAR')
3288 assert e
== 'Entry(barentry_barentry)', e
3290 e
= env
.Entry(['entry1'])
3291 assert e
== ['Entry(entry1)'], e
3293 e
= env
.Entry(['entry1', 'entry2'])
3294 assert e
== ['Entry(entry1)', 'Entry(entry2)'], e
3296 def test_File(self
) -> None:
3297 """Test the File() method"""
3299 def File(self
, name
) -> str:
3300 return 'File(%s)' % name
3302 env
= self
.TestEnvironment(FOO
= 'foofile', BAR
= 'barfile')
3306 assert f
== 'File(f)', f
3308 f
= env
.File('$FOO')
3309 assert f
== 'File(foofile)', f
3311 f
= env
.File('${BAR}_$BAR')
3312 assert f
== 'File(barfile_barfile)', f
3314 f
= env
.File(['file1'])
3315 assert f
== ['File(file1)'], f
3317 f
= env
.File(['file1', 'file2'])
3318 assert f
== ['File(file1)', 'File(file2)'], f
3320 def test_FindFile(self
) -> None:
3321 """Test the FindFile() method"""
3322 env
= self
.TestEnvironment(FOO
= 'fff', BAR
= 'bbb')
3324 r
= env
.FindFile('foo', ['no_such_directory'])
3329 def test_Flatten(self
) -> None:
3330 """Test the Flatten() method"""
3332 l
= env
.Flatten([1])
3334 l
= env
.Flatten([1, [2, [3, [4]]]])
3335 assert l
== [1, 2, 3, 4], l
3337 def test_GetBuildPath(self
) -> None:
3338 """Test the GetBuildPath() method."""
3339 env
= self
.TestEnvironment(MAGIC
= 'xyzzy')
3341 p
= env
.GetBuildPath('foo')
3342 assert p
== 'foo', p
3344 p
= env
.GetBuildPath('$MAGIC')
3345 assert p
== 'xyzzy', p
3347 def test_Ignore(self
) -> None:
3348 """Test the explicit Ignore method."""
3349 env
= self
.TestEnvironment(FOO
='yyy', BAR
='zzz')
3355 t
= env
.Ignore(target
='targ.py', dependency
='dep.py')[0]
3356 assert t
.__class
__.__name
__ == 'Entry', t
.__class
__.__name
__
3357 assert t
.get_internal_path() == 'targ.py'
3358 assert len(t
.ignore
) == 1
3360 assert i
.__class
__.__name
__ == 'Entry', i
.__class
__.__name
__
3361 assert i
.get_internal_path() == 'dep.py'
3363 t
= env
.Ignore(target
='$FOO$BAR', dependency
='$BAR$FOO')[0]
3364 assert t
.__class
__.__name
__ == 'File', t
.__class
__.__name
__
3365 assert t
.get_internal_path() == 'yyyzzz'
3366 assert len(t
.ignore
) == 1
3368 assert i
.__class
__.__name
__ == 'File', i
.__class
__.__name
__
3369 assert i
.get_internal_path() == 'zzzyyy'
3371 t
= env
.Ignore(target
='dir1', dependency
='dir2')[0]
3372 assert t
.__class
__.__name
__ == 'Dir', t
.__class
__.__name
__
3373 assert t
.get_internal_path() == 'dir1'
3374 assert len(t
.ignore
) == 1
3376 assert i
.__class
__.__name
__ == 'Dir', i
.__class
__.__name
__
3377 assert i
.get_internal_path() == 'dir2'
3379 def test_Literal(self
) -> None:
3380 """Test the Literal() method"""
3381 env
= self
.TestEnvironment(FOO
='fff', BAR
='bbb')
3382 list = env
.subst_list([env
.Literal('$FOO'), '$BAR'])[0]
3383 assert list == ['$FOO', 'bbb'], list
3384 list = env
.subst_list(['$FOO', env
.Literal('$BAR')])[0]
3385 assert list == ['fff', '$BAR'], list
3387 def test_Local(self
) -> None:
3388 """Test the Local() method."""
3389 env
= self
.TestEnvironment(FOO
='lll')
3391 l
= env
.Local(env
.fs
.File('fff'))
3392 assert str(l
[0]) == 'fff', l
[0]
3394 l
= env
.Local('ggg', '$FOO')
3395 assert str(l
[0]) == 'ggg', l
[0]
3396 assert str(l
[1]) == 'lll', l
[1]
3398 def test_Precious(self
) -> None:
3399 """Test the Precious() method"""
3400 env
= self
.TestEnvironment(FOO
='ggg', BAR
='hhh')
3403 t
= env
.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3405 assert t
[0].__class
__.__name
__ == 'Entry', t
[0].__class
__.__name
__
3406 assert t
[0].get_internal_path() == 'p_a'
3407 assert t
[0].precious
3408 assert t
[1].__class
__.__name
__ == 'Dir', t
[1].__class
__.__name
__
3409 assert t
[1].get_internal_path() == 'p_hhhb'
3410 assert t
[1].precious
3411 assert t
[2].__class
__.__name
__ == 'Entry', t
[2].__class
__.__name
__
3412 assert t
[2].get_internal_path() == 'p_c'
3413 assert t
[2].precious
3414 assert t
[3].__class
__.__name
__ == 'File', t
[3].__class
__.__name
__
3415 assert t
[3].get_internal_path() == 'p_d'
3416 assert t
[3].precious
3417 assert t
[4].__class
__.__name
__ == 'Entry', t
[4].__class
__.__name
__
3418 assert t
[4].get_internal_path() == 'p_ggg'
3419 assert t
[4].precious
3421 def test_Pseudo(self
) -> None:
3422 """Test the Pseudo() method"""
3423 env
= self
.TestEnvironment(FOO
='ggg', BAR
='hhh')
3426 t
= env
.Pseudo('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3428 assert t
[0].__class
__.__name
__ == 'Entry', t
[0].__class
__.__name
__
3429 assert t
[0].get_internal_path() == 'p_a'
3431 assert t
[1].__class
__.__name
__ == 'Dir', t
[1].__class
__.__name
__
3432 assert t
[1].get_internal_path() == 'p_hhhb'
3434 assert t
[2].__class
__.__name
__ == 'Entry', t
[2].__class
__.__name
__
3435 assert t
[2].get_internal_path() == 'p_c'
3437 assert t
[3].__class
__.__name
__ == 'File', t
[3].__class
__.__name
__
3438 assert t
[3].get_internal_path() == 'p_d'
3440 assert t
[4].__class
__.__name
__ == 'Entry', t
[4].__class
__.__name
__
3441 assert t
[4].get_internal_path() == 'p_ggg'
3444 def test_Repository(self
) -> None:
3445 """Test the Repository() method."""
3447 def __init__(self
) -> None:
3449 def Repository(self
, *dirs
) -> None:
3450 self
.list.extend(list(dirs
))
3451 def Dir(self
, name
):
3453 env
= self
.TestEnvironment(FOO
='rrr', BAR
='sss')
3455 env
.Repository('/tmp/foo')
3456 env
.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
3457 expect
= ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo']
3458 assert env
.fs
.list == expect
, env
.fs
.list
3460 def test_Scanner(self
) -> None:
3461 """Test the Scanner() method"""
3462 def scan(node
, env
, target
, arg
) -> None:
3465 env
= self
.TestEnvironment(FOO
= scan
)
3467 s
= env
.Scanner('foo')
3468 assert s
is not None, s
3470 s
= env
.Scanner(function
= 'foo')
3471 assert s
is not None, s
3474 s
= env
.Scanner('$FOO')
3475 assert s
is not None, s
3477 s
= env
.Scanner(function
= '$FOO')
3478 assert s
is not None, s
3480 def test_SConsignFile(self
) -> None:
3481 """Test the SConsignFile() method"""
3482 import SCons
.SConsign
3485 SConstruct_dir
= os
.sep
+ 'dir'
3487 env
= self
.TestEnvironment(FOO
= 'SConsign',
3488 BAR
= os
.path
.join(os
.sep
, 'File'))
3490 env
.Execute
= lambda action
: None
3495 def capture(name
, dbm_module
, fnames
=fnames
, dbms
=dbms
) -> None:
3497 dbms
.append(dbm_module
)
3499 save_SConsign_File
= SCons
.SConsign
.File
3500 SCons
.SConsign
.File
= capture
3502 env
.SConsignFile('foo')
3503 assert fnames
[-1] == os
.path
.join(os
.sep
, 'dir', 'foo'), fnames
3504 assert dbms
[-1] is None, dbms
3506 env
.SConsignFile('$FOO')
3507 assert fnames
[-1] == os
.path
.join(os
.sep
, 'dir', 'SConsign'), fnames
3508 assert dbms
[-1] is None, dbms
3510 env
.SConsignFile('/$FOO')
3511 assert fnames
[-1] == os
.sep
+ 'SConsign', fnames
3512 assert dbms
[-1] is None, dbms
3514 env
.SConsignFile(os
.sep
+ '$FOO')
3515 assert fnames
[-1] == os
.sep
+ 'SConsign', fnames
3516 assert dbms
[-1] is None, dbms
3518 env
.SConsignFile('$BAR', 'x')
3519 assert fnames
[-1] == os
.path
.join(os
.sep
, 'File'), fnames
3520 assert dbms
[-1] == 'x', dbms
3522 env
.SConsignFile('__$BAR', 7)
3523 assert fnames
[-1] == os
.path
.join(os
.sep
, 'dir', '__', 'File'), fnames
3524 assert dbms
[-1] == 7, dbms
3527 assert fnames
[-1] == os
.path
.join(os
.sep
, 'dir', current_sconsign_filename()), fnames
3528 assert dbms
[-1] is None, dbms
3530 env
.SConsignFile(None)
3531 assert fnames
[-1] is None, fnames
3532 assert dbms
[-1] is None, dbms
3534 SCons
.SConsign
.File
= save_SConsign_File
3536 def test_SideEffect(self
) -> None:
3537 """Test the SideEffect() method"""
3538 env
= self
.TestEnvironment(LIB
='lll', FOO
='fff', BAR
='bbb')
3539 env
.File('mylll.pdb')
3540 env
.Dir('mymmm.pdb')
3542 foo
= env
.Object('foo.obj', 'foo.cpp')[0]
3543 bar
= env
.Object('bar.obj', 'bar.cpp')[0]
3544 s
= env
.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])
3545 assert len(s
) == 1, len(s
)
3547 assert s
.__class
__.__name
__ == 'Entry', s
.__class
__.__name
__
3548 assert s
.get_internal_path() == 'mylib.pdb'
3549 assert s
.side_effect
3550 assert foo
.side_effects
== [s
]
3551 assert bar
.side_effects
== [s
]
3553 fff
= env
.Object('fff.obj', 'fff.cpp')[0]
3554 bbb
= env
.Object('bbb.obj', 'bbb.cpp')[0]
3555 s
= env
.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])
3556 assert len(s
) == 1, len(s
)
3558 assert s
.__class
__.__name
__ == 'File', s
.__class
__.__name
__
3559 assert s
.get_internal_path() == 'mylll.pdb'
3560 assert s
.side_effect
3561 assert fff
.side_effects
== [s
], fff
.side_effects
3562 assert bbb
.side_effects
== [s
], bbb
.side_effects
3564 ggg
= env
.Object('ggg.obj', 'ggg.cpp')[0]
3565 ccc
= env
.Object('ccc.obj', 'ccc.cpp')[0]
3566 s
= env
.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])
3567 assert len(s
) == 1, len(s
)
3569 assert s
.__class
__.__name
__ == 'Dir', s
.__class
__.__name
__
3570 assert s
.get_internal_path() == 'mymmm.pdb'
3571 assert s
.side_effect
3572 assert ggg
.side_effects
== [s
], ggg
.side_effects
3573 assert ccc
.side_effects
== [s
], ccc
.side_effects
3575 # Verify that duplicate side effects are not allowed.
3576 before
= len(ggg
.side_effects
)
3577 s
= env
.SideEffect('mymmm.pdb', ggg
)
3578 assert len(s
) == 0, len(s
)
3579 assert len(ggg
.side_effects
) == before
, len(ggg
.side_effects
)
3581 def test_Split(self
) -> None:
3582 """Test the Split() method"""
3583 env
= self
.TestEnvironment(FOO
= 'fff', BAR
= 'bbb')
3584 s
= env
.Split("foo bar")
3585 assert s
== ["foo", "bar"], s
3586 s
= env
.Split("$FOO bar")
3587 assert s
== ["fff", "bar"], s
3588 s
= env
.Split(["foo", "bar"])
3589 assert s
== ["foo", "bar"], s
3590 s
= env
.Split(["foo", "${BAR}-bbb"])
3591 assert s
== ["foo", "bbb-bbb"], s
3592 s
= env
.Split("foo")
3593 assert s
== ["foo"], s
3594 s
= env
.Split("$FOO$BAR")
3595 assert s
== ["fffbbb"], s
3598 def test_Value(self
) -> None:
3599 """Test creating a Value() object
3603 assert v1
.value
== 'a', v1
.value
3606 v2
= env
.Value(value2
)
3607 assert v2
.value
== value2
, v2
.value
3608 assert v2
.value
is value2
, v2
.value
3612 v3
= env
.Value('c', 'build-c')
3613 assert v3
.value
== 'c', v3
.value
3615 v4
= env
.Value(b
'\x00\x0F', name
='name')
3616 assert v4
.value
== b
'\x00\x0F', v4
.value
3617 assert v4
.name
== 'name', v4
.name
3620 def test_Environment_global_variable(self
) -> None:
3621 """Test setting Environment variable to an Environment.Base subclass"""
3622 class MyEnv(SCons
.Environment
.Base
):
3623 def xxx(self
, string
):
3624 return self
.subst(string
)
3626 SCons
.Environment
.Environment
= MyEnv
3628 env
= SCons
.Environment
.Environment(FOO
= 'foo')
3630 f
= env
.subst('$FOO')
3631 assert f
== 'foo', f
3634 assert f
== 'foo', f
3636 def test_bad_keywords(self
) -> None:
3637 """Test trying to use reserved keywords in an Environment"""
3640 env
= self
.TestEnvironment(TARGETS
= 'targets',
3641 SOURCES
= 'sources',
3644 CHANGED_SOURCES
= 'changed_sources',
3645 CHANGED_TARGETS
= 'changed_targets',
3646 UNCHANGED_SOURCES
= 'unchanged_sources',
3647 UNCHANGED_TARGETS
= 'unchanged_targets',
3649 bad_msg
= '%s is not reserved, but got omitted; see Environment.construction_var_name_ok'
3650 added
.append('INIT')
3651 for x
in self
.reserved_variables
:
3652 assert x
not in env
, env
[x
]
3654 assert x
in env
, bad_msg
% x
3656 env
.Append(TARGETS
= 'targets',
3657 SOURCES
= 'sources',
3660 CHANGED_SOURCES
= 'changed_sources',
3661 CHANGED_TARGETS
= 'changed_targets',
3662 UNCHANGED_SOURCES
= 'unchanged_sources',
3663 UNCHANGED_TARGETS
= 'unchanged_targets',
3665 added
.append('APPEND')
3666 for x
in self
.reserved_variables
:
3667 assert x
not in env
, env
[x
]
3669 assert x
in env
, bad_msg
% x
3671 env
.AppendUnique(TARGETS
= 'targets',
3672 SOURCES
= 'sources',
3675 CHANGED_SOURCES
= 'changed_sources',
3676 CHANGED_TARGETS
= 'changed_targets',
3677 UNCHANGED_SOURCES
= 'unchanged_sources',
3678 UNCHANGED_TARGETS
= 'unchanged_targets',
3679 APPENDUNIQUE
= 'appendunique')
3680 added
.append('APPENDUNIQUE')
3681 for x
in self
.reserved_variables
:
3682 assert x
not in env
, env
[x
]
3684 assert x
in env
, bad_msg
% x
3686 env
.Prepend(TARGETS
= 'targets',
3687 SOURCES
= 'sources',
3690 CHANGED_SOURCES
= 'changed_sources',
3691 CHANGED_TARGETS
= 'changed_targets',
3692 UNCHANGED_SOURCES
= 'unchanged_sources',
3693 UNCHANGED_TARGETS
= 'unchanged_targets',
3694 PREPEND
= 'prepend')
3695 added
.append('PREPEND')
3696 for x
in self
.reserved_variables
:
3697 assert x
not in env
, env
[x
]
3699 assert x
in env
, bad_msg
% x
3701 env
.Prepend(TARGETS
= 'targets',
3702 SOURCES
= 'sources',
3705 CHANGED_SOURCES
= 'changed_sources',
3706 CHANGED_TARGETS
= 'changed_targets',
3707 UNCHANGED_SOURCES
= 'unchanged_sources',
3708 UNCHANGED_TARGETS
= 'unchanged_targets',
3709 PREPENDUNIQUE
= 'prependunique')
3710 added
.append('PREPENDUNIQUE')
3711 for x
in self
.reserved_variables
:
3712 assert x
not in env
, env
[x
]
3714 assert x
in env
, bad_msg
% x
3716 env
.Replace(TARGETS
= 'targets',
3717 SOURCES
= 'sources',
3720 CHANGED_SOURCES
= 'changed_sources',
3721 CHANGED_TARGETS
= 'changed_targets',
3722 UNCHANGED_SOURCES
= 'unchanged_sources',
3723 UNCHANGED_TARGETS
= 'unchanged_targets',
3724 REPLACE
= 'replace')
3725 added
.append('REPLACE')
3726 for x
in self
.reserved_variables
:
3727 assert x
not in env
, env
[x
]
3729 assert x
in env
, bad_msg
% x
3731 copy
= env
.Clone(TARGETS
= 'targets',
3732 SOURCES
= 'sources',
3735 CHANGED_SOURCES
= 'changed_sources',
3736 CHANGED_TARGETS
= 'changed_targets',
3737 UNCHANGED_SOURCES
= 'unchanged_sources',
3738 UNCHANGED_TARGETS
= 'unchanged_targets',
3740 for x
in self
.reserved_variables
:
3741 assert x
not in copy
, env
[x
]
3742 for x
in added
+ ['COPY']:
3743 assert x
in copy
, bad_msg
% x
3745 over
= env
.Override({'TARGETS' : 'targets',
3746 'SOURCES' : 'sources',
3747 'SOURCE' : 'source',
3748 'TARGET' : 'target',
3749 'CHANGED_SOURCES' : 'changed_sources',
3750 'CHANGED_TARGETS' : 'changed_targets',
3751 'UNCHANGED_SOURCES' : 'unchanged_sources',
3752 'UNCHANGED_TARGETS' : 'unchanged_targets',
3753 'OVERRIDE' : 'override'})
3754 for x
in self
.reserved_variables
:
3755 assert x
not in over
, over
[x
]
3756 for x
in added
+ ['OVERRIDE']:
3757 assert x
in over
, bad_msg
% x
3759 def test_parse_flags(self
) -> None:
3760 """Test the Base class parse_flags argument"""
3761 # all we have to show is that it gets to MergeFlags internally
3762 env
= Environment(tools
=[], parse_flags
= '-X')
3763 assert env
['CCFLAGS'] == ['-X'], env
['CCFLAGS']
3765 env
= Environment(tools
=[], CCFLAGS
=None, parse_flags
= '-Y')
3766 assert env
['CCFLAGS'] == ['-Y'], env
['CCFLAGS']
3768 env
= Environment(tools
=[], CPPDEFINES
='FOO', parse_flags
='-std=c99 -X -DBAR')
3769 assert env
['CFLAGS'] == ['-std=c99'], env
['CFLAGS']
3770 assert env
['CCFLAGS'] == ['-X'], env
['CCFLAGS']
3771 self
.assertEqual(list(env
['CPPDEFINES']), ['FOO', 'BAR'])
3773 def test_clone_parse_flags(self
) -> None:
3774 """Test the env.Clone() parse_flags argument"""
3775 # all we have to show is that it gets to MergeFlags internally
3776 env
= Environment(tools
= [])
3777 env2
= env
.Clone(parse_flags
= '-X')
3778 assert 'CCFLAGS' not in env
3779 assert env2
['CCFLAGS'] == ['-X'], env2
['CCFLAGS']
3781 env
= Environment(tools
= [], CCFLAGS
=None)
3782 env2
= env
.Clone(parse_flags
= '-Y')
3783 assert env
['CCFLAGS'] is None, env
['CCFLAGS']
3784 assert env2
['CCFLAGS'] == ['-Y'], env2
['CCFLAGS']
3786 env
= Environment(tools
= [], CPPDEFINES
= 'FOO')
3787 env2
= env
.Clone(parse_flags
= '-std=c99 -X -DBAR')
3788 assert 'CFLAGS' not in env
3789 assert env2
['CFLAGS'] == ['-std=c99'], env2
['CFLAGS']
3790 assert 'CCFLAGS' not in env
3791 assert env2
['CCFLAGS'] == ['-X'], env2
['CCFLAGS']
3792 assert env
['CPPDEFINES'] == 'FOO', env
['CPPDEFINES']
3793 self
.assertEqual(list(env2
['CPPDEFINES']), ['FOO','BAR'])
3796 class OverrideEnvironmentTestCase(unittest
.TestCase
,TestEnvironmentFixture
):
3798 def setUp(self
) -> None:
3800 env
._dict
= {'XXX' : 'x', 'YYY' : 'y'}
3802 def verify_value(env
, key
, value
, *args
, **kwargs
) -> None:
3803 """Verifies that key is value on the env this is called with."""
3804 self
.assertEqual(env
[key
], value
)
3806 env
.AddMethod(verify_value
)
3807 # env2 does not overrride 'YYY' to test passthrough
3808 env2
= OverrideEnvironment(env
, {'XXX' : 'x2'})
3809 # env3 overrides both, plus sets a new var 'ZZZ'
3810 env3
= OverrideEnvironment(env2
, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
3811 self
.envs
= [env
, env2
, env3
]
3813 def checkpath(self
, node
, expect
):
3814 return str(node
) == os
.path
.normpath(expect
)
3816 def test___init__(self
) -> None:
3817 """Test OverrideEnvironment initialization"""
3818 env
, env2
, env3
= self
.envs
3820 with self
.subTest():
3821 self
.assertEqual(env
['XXX'], 'x')
3822 with self
.subTest():
3823 self
.assertEqual(env2
['XXX'], 'x2')
3824 with self
.subTest():
3825 self
.assertEqual(env3
['XXX'], 'x3')
3826 with self
.subTest():
3827 self
.assertEqual(env
['YYY'], 'y')
3828 with self
.subTest():
3829 self
.assertEqual(env2
['YYY'], 'y')
3830 with self
.subTest():
3831 self
.assertEqual(env3
['YYY'], 'y3')
3832 with self
.subTest():
3833 self
.assertNotIn('ZZZ', env
)
3834 with self
.subTest():
3835 self
.assertNotIn('ZZZ', env2
)
3836 with self
.subTest():
3837 self
.assertEqual(env3
['ZZZ'], 'z3')
3839 def test___setitem__(self
) -> None:
3840 """Test setting a variable does not leak through."""
3841 env
, env2
, env3
= self
.envs
3843 with self
.subTest():
3844 self
.assertEqual(env3
['QQQ'], 'q')
3845 with self
.subTest():
3846 self
.assertNotIn('QQQ', env2
)
3847 with self
.subTest():
3848 self
.assertNotIn('QQQ', env
)
3850 def test___delitem__(self
) -> None:
3851 """Test deleting variables from an OverrideEnvironment"""
3852 env
, env2
, env3
= self
.envs
3854 # changed in NEXT_RELEASE: delete does not cascade to underlying envs
3855 # XXX is in all three, del from env3 should affect only it
3857 with self
.subTest():
3858 self
.assertIn('XXX', env
)
3859 with self
.subTest():
3860 self
.assertIn('XXX', env2
)
3861 with self
.subTest():
3862 self
.assertNotIn('XXX', env3
)
3864 # YYY unique in env and env3, shadowed in env2: env2 should still work.
3866 with self
.subTest():
3867 self
.assertIn('YYY', env
)
3868 with self
.subTest():
3869 self
.assertEqual(env2
['YYY'], 'y')
3870 with self
.subTest():
3871 self
.assertIn('YYY', env2
)
3872 with self
.subTest():
3873 self
.assertNotIn('YYY', env3
)
3875 # ZZZ is only in env3, none should have it
3877 with self
.subTest():
3878 self
.assertNotIn('ZZZ', env
)
3879 with self
.subTest():
3880 self
.assertNotIn('ZZZ', env2
)
3881 with self
.subTest():
3882 self
.assertNotIn('ZZZ', env3
)
3884 # make sure we can write back after deletion
3886 with self
.subTest():
3887 self
.assertEqual(env3
['XXX'], 'x4')
3888 with self
.subTest():
3889 self
.assertEqual(env2
['XXX'], 'x2')
3890 with self
.subTest():
3891 self
.assertEqual(env
['XXX'], 'x')
3893 def test_get(self
) -> None:
3894 """Test the OverrideEnvironment get() method"""
3895 env
, env2
, env3
= self
.envs
3896 assert env
.get('XXX') == 'x', env
.get('XXX')
3897 assert env2
.get('XXX') == 'x2', env2
.get('XXX')
3898 assert env3
.get('XXX') == 'x3', env3
.get('XXX')
3899 assert env
.get('YYY') == 'y', env
.get('YYY')
3900 assert env2
.get('YYY') == 'y', env2
.get('YYY')
3901 assert env3
.get('YYY') == 'y3', env3
.get('YYY')
3902 assert env
.get('ZZZ') is None, env
.get('ZZZ')
3903 assert env2
.get('ZZZ') is None, env2
.get('ZZZ')
3904 assert env3
.get('ZZZ') == 'z3', env3
.get('ZZZ')
3906 def test_contains(self
) -> None:
3907 """Test the OverrideEnvironment __contains__() method"""
3908 env
, env2
, env3
= self
.envs
3909 assert 'XXX' in env
, 'XXX' in env
3910 assert 'XXX' in env2
, 'XXX' in env2
3911 assert 'XXX' in env3
, 'XXX' in env3
3912 assert 'YYY' in env
, 'YYY' in env
3913 assert 'YYY' in env2
, 'YYY' in env2
3914 assert 'YYY' in env3
, 'YYY' in env3
3915 assert 'ZZZ' not in env
, 'ZZZ' in env
3916 assert 'ZZZ' not in env2
, 'ZZZ' in env2
3917 assert 'ZZZ' in env3
, 'ZZZ' in env3
3919 def test_Dictionary(self
) -> None:
3920 """Test the OverrideEnvironment Dictionary() method"""
3921 env
, env2
, env3
= self
.envs
3922 # nothing overrriden
3923 items
= env
.Dictionary()
3924 assert items
== {'XXX' : 'x', 'YYY' : 'y'}, items
3926 # env2 overrides XXX, YYY unchanged
3927 items
= env2
.Dictionary()
3928 assert items
== {'XXX' : 'x2', 'YYY' : 'y'}, items
3930 # env3 overrides XXX, YYY, adds ZZZ
3931 items
= env3
.Dictionary()
3932 assert items
== {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
3934 # test one-arg and multi-arg Dictionary
3935 assert env3
.Dictionary('XXX') == 'x3', env3
.Dictionary('XXX')
3936 xxx
, yyy
= env2
.Dictionary('XXX', 'YYY')
3937 assert xxx
== 'x2', xxx
3938 assert yyy
== 'y', yyy
3939 # added in NEXT_VERSION: as_dict flag
3940 with self
.subTest():
3941 expect
= {'XXX': 'x3'}
3942 self
.assertEqual(env3
.Dictionary('XXX', as_dict
=True), expect
)
3943 with self
.subTest():
3944 expect
= {'XXX': 'x2', 'YYY': 'y'}
3945 self
.assertEqual(env2
.Dictionary('XXX', 'YYY', as_dict
=True), expect
)
3947 # test deletion in top override
3949 self
.assertRaises(KeyError, env3
.Dictionary
, 'XXX')
3950 # changed in NEXT_RELEASE: *not* deleted from underlying envs
3951 assert 'XXX' in env2
.Dictionary()
3952 assert 'XXX' in env
.Dictionary()
3955 def test_items(self
) -> None:
3956 """Test the OverrideEnvironment items() method"""
3957 env
, env2
, env3
= self
.envs
3958 items
= sorted(env
.items())
3959 assert items
== [('XXX', 'x'), ('YYY', 'y')], items
3960 items
= sorted(env2
.items())
3961 assert items
== [('XXX', 'x2'), ('YYY', 'y')], items
3962 items
= sorted(env3
.items())
3963 assert items
== [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items
3965 def test_keys(self
) -> None:
3966 """Test the OverrideEnvironment keys() method"""
3967 env
, env2
, env3
= self
.envs
3968 keys
= sorted(env
.keys())
3969 assert keys
== ['XXX', 'YYY'], keys
3970 keys
= sorted(env2
.keys())
3971 assert keys
== ['XXX', 'YYY'], keys
3972 keys
= sorted(env3
.keys())
3973 assert keys
== ['XXX', 'YYY', 'ZZZ'], keys
3975 def test_values(self
) -> None:
3976 """Test the OverrideEnvironment values() method"""
3977 env
, env2
, env3
= self
.envs
3978 values
= sorted(env
.values())
3979 assert values
== ['x', 'y'], values
3980 values
= sorted(env2
.values())
3981 assert values
== ['x2', 'y'], values
3982 values
= sorted(env3
.values())
3983 assert values
== ['x3', 'y3', 'z3'], values
3985 def test_setdefault(self
) -> None:
3986 """Test the OverrideEnvironment setdefault() method."""
3987 env
, env2
, env3
= self
.envs
3988 # does not set for existing key
3989 assert env2
.setdefault('XXX', 'z') == 'x2', env2
['XXX']
3990 # set/return using default for non-existing key
3991 assert env2
.setdefault('ZZZ', 'z2') == 'z2', env2
['ZZZ']
3992 # set did not leak through to base env
3993 assert 'ZZZ' not in env
3995 def test_gvars(self
) -> None:
3996 """Test the OverrideEnvironment gvars() method"""
3997 env
, env2
, env3
= self
.envs
3999 assert gvars
== {'XXX' : 'x', 'YYY' : 'y'}, gvars
4000 gvars
= env2
.gvars()
4001 assert gvars
== {'XXX' : 'x', 'YYY' : 'y'}, gvars
4002 gvars
= env3
.gvars()
4003 assert gvars
== {'XXX' : 'x', 'YYY' : 'y'}, gvars
4005 def test_lvars(self
) -> None:
4006 """Test the OverrideEnvironment lvars() method"""
4007 env
, env2
, env3
= self
.envs
4009 assert lvars
== {}, lvars
4010 lvars
= env2
.lvars()
4011 assert lvars
== {'XXX' : 'x2'}, lvars
4012 lvars
= env3
.lvars()
4013 assert lvars
== {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars
4015 def test_Replace(self
) -> None:
4016 """Test the OverrideEnvironment Replace() method"""
4017 env
, env2
, env3
= self
.envs
4018 # initial state already proven by test___init__
4020 env
.Replace(YYY
='y4')
4021 assert env
['XXX'] == 'x', env
['XXX']
4022 assert env2
['XXX'] == 'x2', env2
['XXX']
4023 assert env3
['XXX'] == 'x3', env3
['XXX']
4024 assert env
['YYY'] == 'y4', env
['YYY']
4025 assert env2
['YYY'] == 'y4', env2
['YYY']
4026 assert env3
['YYY'] == 'y3', env3
['YYY']
4029 def test_Override_Leakage(self
) -> None:
4030 """Test OverrideEnvironment modifying a variable for leakage."""
4031 env
, env2
, env3
= self
.envs
4032 # initial state already proven by test___init__
4034 # string appending should be additive - only in the override
4036 self
.assertEqual(env2
['WWW'], 'w')
4037 with self
.subTest():
4038 env2
.Append(WWW
='w2')
4039 self
.assertEqual(env2
['WWW'], 'ww2')
4041 self
.assertEqual(env
['WWW'], 'w', "leak error")
4043 # append to a string already in the override
4044 self
.assertEqual(env
['XXX'], 'x')
4045 self
.assertEqual(env2
['XXX'], 'x2')
4046 with self
.subTest():
4047 env2
.Append(XXX
='x4')
4048 self
.assertEqual(env2
['XXX'], 'x2x4')
4050 self
.assertEqual(env
['XXX'], 'x', "leak error")
4052 # add a new mutable key to base env, but copy it before modifying
4053 # This isn't a terribly interesting test, just shows that if you
4054 # "behave carefully", things don't leak.
4055 env
.Append(QQQ
=deque(['q1', 'q2', 'q3']))
4056 self
.assertEqual(env2
['QQQ'], deque(['q1', 'q2', 'q3']))
4057 with self
.subTest():
4058 env2
['QQQ'] = env2
['QQQ'].copy()
4059 env2
.Append(QQQ
='q4')
4060 self
.assertEqual(env2
['QQQ'], deque(['q1', 'q2', 'q3', 'q4']))
4062 self
.assertNotIn('q4', env
['QQQ'], "leak error")
4065 @unittest.expectedFailure
4066 def test_Override_Mutable_Leakage(self
) -> None:
4067 """Test OverrideEnvironment modifying a mutable variable for leakage.
4069 This is factored out from test_Override_Leakage as currently
4070 there is no way to prevent the leakage when updating a mutable
4071 element such as a list - thus it's marked as an xfail. This
4072 gives us something to come back to if we ever invent some sort
4073 of isolation via object copying, etc.
4075 env
, env2
, env3
= self
.envs
4076 # initial state already proven by test___init__
4078 # add a new key to base env, with a mutable value
4079 env
.Append(QQQ
=deque(['q1', 'q2', 'q3']))
4080 self
.assertEqual(env2
['QQQ'], deque(['q1', 'q2', 'q3']))
4081 with self
.subTest():
4082 env2
.Append(QQQ
='q4')
4083 self
.assertEqual(env2
['QQQ'], deque(['q1', 'q2', 'q3', 'q4']))
4085 self
.assertNotIn('q4', env
['QQQ'], "leak error")
4088 # Tests a number of Base methods through an OverrideEnvironment to
4089 # make sure they handle overridden construction variables properly.
4091 # The following Base methods also call self.subst(), and so could
4092 # theoretically be subject to problems with evaluating overridden
4093 # variables, but they're never really called that way in the rest
4094 # of our code, so we won't worry about them (at least for now):
4110 # It's unlikely Clone() will ever be called this way, so let the
4111 # other methods test that handling overridden values works.
4112 #def test_Clone(self):
4113 # """Test the OverrideEnvironment Clone() method"""
4116 def test_FindIxes(self
) -> None:
4117 """Test the OverrideEnvironment FindIxes() method"""
4118 env
, env2
, env3
= self
.envs
4119 x
= env
.FindIxes(['xaaay'], 'XXX', 'YYY')
4120 assert x
== 'xaaay', x
4121 x
= env2
.FindIxes(['x2aaay'], 'XXX', 'YYY')
4122 assert x
== 'x2aaay', x
4123 x
= env3
.FindIxes(['x3aaay3'], 'XXX', 'YYY')
4124 assert x
== 'x3aaay3', x
4126 def test_ReplaceIxes(self
) -> None:
4127 """Test the OverrideEnvironment ReplaceIxes() method"""
4128 env
, env2
, env3
= self
.envs
4129 x
= env
.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX')
4130 assert x
== 'yaaax', x
4131 x
= env2
.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX')
4132 assert x
== 'yaaax2', x
4133 x
= env3
.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX')
4134 assert x
== 'y3aaax3', x
4136 # It's unlikely WhereIs() will ever be called this way, so let the
4137 # other methods test that handling overridden values works.
4138 #def test_WhereIs(self):
4139 # """Test the OverrideEnvironment WhereIs() method"""
4142 def test_PseudoBuilderInherits(self
) -> None:
4143 """Test that pseudo-builders inherit the overrided values."""
4144 env
, env2
, env3
= self
.envs
4145 env
.verify_value('XXX', 'x')
4146 env2
.verify_value('XXX', 'x2')
4147 env3
.verify_value('XXX', 'x3')
4149 def test_Dir(self
) -> None:
4150 """Test the OverrideEnvironment Dir() method"""
4151 env
, env2
, env3
= self
.envs
4152 x
= env
.Dir('ddir/$XXX')
4153 assert self
.checkpath(x
, 'ddir/x'), str(x
)
4154 x
= env2
.Dir('ddir/$XXX')
4155 assert self
.checkpath(x
, 'ddir/x2'), str(x
)
4156 x
= env3
.Dir('ddir/$XXX')
4157 assert self
.checkpath(x
, 'ddir/x3'), str(x
)
4159 def test_Entry(self
) -> None:
4160 """Test the OverrideEnvironment Entry() method"""
4161 env
, env2
, env3
= self
.envs
4162 x
= env
.Entry('edir/$XXX')
4163 assert self
.checkpath(x
, 'edir/x'), str(x
)
4164 x
= env2
.Entry('edir/$XXX')
4165 assert self
.checkpath(x
, 'edir/x2'), str(x
)
4166 x
= env3
.Entry('edir/$XXX')
4167 assert self
.checkpath(x
, 'edir/x3'), str(x
)
4169 def test_File(self
) -> None:
4170 """Test the OverrideEnvironment File() method"""
4171 env
, env2
, env3
= self
.envs
4172 x
= env
.File('fdir/$XXX')
4173 assert self
.checkpath(x
, 'fdir/x'), str(x
)
4174 x
= env2
.File('fdir/$XXX')
4175 assert self
.checkpath(x
, 'fdir/x2'), str(x
)
4176 x
= env3
.File('fdir/$XXX')
4177 assert self
.checkpath(x
, 'fdir/x3'), str(x
)
4179 def test_Split(self
) -> None:
4180 """Test the OverrideEnvironment Split() method"""
4181 env
, env2
, env3
= self
.envs
4182 env
['AAA'] = '$XXX $YYY $ZZZ'
4183 x
= env
.Split('$AAA')
4184 assert x
== ['x', 'y'], x
4185 x
= env2
.Split('$AAA')
4186 assert x
== ['x2', 'y'], x
4187 x
= env3
.Split('$AAA')
4188 assert x
== ['x3', 'y3', 'z3'], x
4190 def test_parse_flags(self
) -> None:
4191 """Test the OverrideEnvironment parse_flags argument"""
4192 # all we have to show is that it gets to MergeFlags internally
4193 env
= SubstitutionEnvironment()
4194 env2
= env
.Override({'parse_flags' : '-X'})
4195 assert 'CCFLAGS' not in env
4196 assert env2
['CCFLAGS'] == ['-X'], env2
['CCFLAGS']
4198 env
= SubstitutionEnvironment(CCFLAGS
=None)
4199 env2
= env
.Override({'parse_flags' : '-Y'})
4200 assert env
['CCFLAGS'] is None, env
['CCFLAGS']
4201 assert env2
['CCFLAGS'] == ['-Y'], env2
['CCFLAGS']
4203 env
= SubstitutionEnvironment(CPPDEFINES
='FOO')
4204 env2
= env
.Override({'parse_flags': '-std=c99 -X -DBAR'})
4205 assert 'CFLAGS' not in env
4206 assert env2
['CFLAGS'] == ['-std=c99'], env2
['CFLAGS']
4207 assert 'CCFLAGS' not in env
4208 assert env2
['CCFLAGS'] == ['-X'], env2
['CCFLAGS']
4209 # make sure they are independent
4210 self
.assertIsNot(env
['CPPDEFINES'], env2
['CPPDEFINES'])
4211 assert env
['CPPDEFINES'] == 'FOO', env
['CPPDEFINES']
4212 self
.assertEqual(list(env2
['CPPDEFINES']), ['FOO','BAR'])
4215 class NoSubstitutionProxyTestCase(unittest
.TestCase
,TestEnvironmentFixture
):
4217 def test___init__(self
) -> None:
4218 """Test NoSubstitutionProxy initialization"""
4219 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
4220 assert env
['XXX'] == 'x', env
['XXX']
4221 assert env
['YYY'] == 'y', env
['YYY']
4223 proxy
= NoSubstitutionProxy(env
)
4224 assert proxy
['XXX'] == 'x', proxy
['XXX']
4225 assert proxy
['YYY'] == 'y', proxy
['YYY']
4227 def test_attributes(self
) -> None:
4228 """Test getting and setting NoSubstitutionProxy attributes"""
4230 setattr(env
, 'env_attr', 'value1')
4232 proxy
= NoSubstitutionProxy(env
)
4233 setattr(proxy
, 'proxy_attr', 'value2')
4235 x
= getattr(env
, 'env_attr')
4236 assert x
== 'value1', x
4237 x
= getattr(proxy
, 'env_attr')
4238 assert x
== 'value1', x
4240 x
= getattr(env
, 'proxy_attr')
4241 assert x
== 'value2', x
4242 x
= getattr(proxy
, 'proxy_attr')
4243 assert x
== 'value2', x
4245 def test_subst(self
) -> None:
4246 """Test the NoSubstitutionProxy.subst() method"""
4247 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
4248 assert env
['XXX'] == 'x', env
['XXX']
4249 assert env
['YYY'] == 'y', env
['YYY']
4251 proxy
= NoSubstitutionProxy(env
)
4252 assert proxy
['XXX'] == 'x', proxy
['XXX']
4253 assert proxy
['YYY'] == 'y', proxy
['YYY']
4255 x
= env
.subst('$XXX')
4257 x
= proxy
.subst('$XXX')
4258 assert x
== '$XXX', x
4260 x
= proxy
.subst('$YYY', raw
=7, target
=None, source
=None,
4262 extra_meaningless_keyword_argument
=None)
4263 assert x
== '$YYY', x
4265 def test_subst_kw(self
) -> None:
4266 """Test the NoSubstitutionProxy.subst_kw() method"""
4267 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
4268 assert env
['XXX'] == 'x', env
['XXX']
4269 assert env
['YYY'] == 'y', env
['YYY']
4271 proxy
= NoSubstitutionProxy(env
)
4272 assert proxy
['XXX'] == 'x', proxy
['XXX']
4273 assert proxy
['YYY'] == 'y', proxy
['YYY']
4275 x
= env
.subst_kw({'$XXX':'$YYY'})
4276 assert x
== {'x':'y'}, x
4277 x
= proxy
.subst_kw({'$XXX':'$YYY'})
4278 assert x
== {'$XXX':'$YYY'}, x
4280 def test_subst_list(self
) -> None:
4281 """Test the NoSubstitutionProxy.subst_list() method"""
4282 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
4283 assert env
['XXX'] == 'x', env
['XXX']
4284 assert env
['YYY'] == 'y', env
['YYY']
4286 proxy
= NoSubstitutionProxy(env
)
4287 assert proxy
['XXX'] == 'x', proxy
['XXX']
4288 assert proxy
['YYY'] == 'y', proxy
['YYY']
4290 x
= env
.subst_list('$XXX')
4291 assert x
== [['x']], x
4292 x
= proxy
.subst_list('$XXX')
4295 x
= proxy
.subst_list('$YYY', raw
=0, target
=None, source
=None, conv
=None)
4298 def test_subst_target_source(self
) -> None:
4299 """Test the NoSubstitutionProxy.subst_target_source() method"""
4300 env
= self
.TestEnvironment(XXX
= 'x', YYY
= 'y')
4301 assert env
['XXX'] == 'x', env
['XXX']
4302 assert env
['YYY'] == 'y', env
['YYY']
4304 proxy
= NoSubstitutionProxy(env
)
4305 assert proxy
['XXX'] == 'x', proxy
['XXX']
4306 assert proxy
['YYY'] == 'y', proxy
['YYY']
4308 args
= ('$XXX $TARGET $SOURCE $YYY',)
4309 kw
= {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
4310 x
= env
.subst_target_source(*args
, **kw
)
4311 assert x
== 'x ttt sss y', x
4312 x
= proxy
.subst_target_source(*args
, **kw
)
4313 assert x
== ' ttt sss ', x
4316 if __name__
== "__main__":
4321 # indent-tabs-mode:nil
4323 # vim: set expandtab tabstop=4 shiftwidth=4: