b692e96ff41090e99b6ee7d1bf335bf7c2503a37
[scons.git] / src / engine / SCons / EnvironmentTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import SCons.compat
27
28 import copy
29 import os
30 import StringIO
31 import sys
32 import TestCmd
33 import unittest
34 import UserList
35
36 from SCons.Environment import *
37 import SCons.Warnings
38
39 def diff_env(env1, env2):
40     s1 = "env1 = {\n"
41     s2 = "env2 = {\n"
42     d = {}
43     for k in env1._dict.keys() + env2._dict.keys():
44         d[k] = None
45     for k in sorted(d.keys()):
46         if k in env1:
47            if k in env2:
48                if env1[k] != env2[k]:
49                    s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
50                    s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
51            else:
52                s1 = s1 + "    " + repr(k) + " : " + repr(env1[k]) + "\n"
53         elif k in env2:
54            s2 = s2 + "    " + repr(k) + " : " + repr(env2[k]) + "\n"
55     s1 = s1 + "}\n"
56     s2 = s2 + "}\n"
57     return s1 + s2
58
59 def diff_dict(d1, d2):
60     s1 = "d1 = {\n"
61     s2 = "d2 = {\n"
62     d = {}
63     for k in d1.keys() + d2.keys():
64         d[k] = None
65     for k in sorted(d.keys()):
66         if k in d1:
67            if k in d2:
68                if d1[k] != d2[k]:
69                    s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
70                    s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
71            else:
72                s1 = s1 + "    " + repr(k) + " : " + repr(d1[k]) + "\n"
73         elif k in env2:
74            s2 = s2 + "    " + repr(k) + " : " + repr(d2[k]) + "\n"
75     s1 = s1 + "}\n"
76     s2 = s2 + "}\n"
77     return s1 + s2
78
79 called_it = {}
80 built_it = {}
81
82 class Builder(SCons.Builder.BuilderBase):
83     """A dummy Builder class for testing purposes.  "Building"
84     a target is simply setting a value in the dictionary.
85     """
86     def __init__(self, name = None):
87         self.name = name
88
89     def __call__(self, env, target=None, source=None, **kw):
90         global called_it
91         called_it['target'] = target
92         called_it['source'] = source
93         called_it.update(kw)
94
95     def execute(self, target = None, **kw):
96         global built_it
97         built_it[target] = 1
98
99
100
101 scanned_it = {}
102
103 class Scanner:
104     """A dummy Scanner class for testing purposes.  "Scanning"
105     a target is simply setting a value in the dictionary.
106     """
107     def __init__(self, name, skeys=[]):
108         self.name = name
109         self.skeys = skeys
110
111     def __call__(self, filename):
112         global scanned_it
113         scanned_it[filename] = 1
114
115     def __cmp__(self, other):
116         try:
117             return cmp(self.__dict__, other.__dict__)
118         except AttributeError:
119             return 1
120
121     def get_skeys(self, env):
122         return self.skeys
123
124     def __str__(self):
125         return self.name
126
127
128
129 class CLVar(UserList.UserList):
130     def __init__(self, seq):
131         if type(seq) == type(''):
132             seq = seq.split()
133         UserList.UserList.__init__(self, seq)
134     def __add__(self, other):
135         return UserList.UserList.__add__(self, CLVar(other))
136     def __radd__(self, other):
137         return UserList.UserList.__radd__(self, CLVar(other))
138     def __coerce__(self, other):
139         return (self, CLVar(other))
140
141
142
143 class DummyNode:
144     def __init__(self, name):
145         self.name = name
146     def __str__(self):
147         return self.name
148     def rfile(self):
149         return self
150     def get_subst_proxy(self):
151         return self
152
153 def test_tool( env ):
154     env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
155
156 class TestEnvironmentFixture:
157     def TestEnvironment(self, *args, **kw):
158         if not kw or 'tools' not in kw:
159             kw['tools'] = [test_tool]
160         default_keys = { 'CC' : 'cc',
161                          'CCFLAGS' : '-DNDEBUG',
162                          'ENV' : { 'TMP' : '/tmp' } }
163         for key, value in default_keys.items():
164             if key not in kw:
165                 kw[key] = value
166         if 'BUILDERS' not in kw:
167             static_obj = SCons.Builder.Builder(action = {},
168                                                emitter = {},
169                                                suffix = '.o',
170                                                single_source = 1)
171             kw['BUILDERS'] = {'Object' : static_obj}
172             static_obj.add_action('.cpp', 'fake action')
173             
174         env = Environment(*args, **kw)
175         return env
176
177 class SubstitutionTestCase(unittest.TestCase):
178
179     def test___init__(self):
180         """Test initializing a SubstitutionEnvironment
181         """
182         env = SubstitutionEnvironment()
183         assert '__env__' not in env
184
185     def test___cmp__(self):
186         """Test comparing SubstitutionEnvironments
187         """
188
189         env1 = SubstitutionEnvironment(XXX = 'x')
190         env2 = SubstitutionEnvironment(XXX = 'x')
191         env3 = SubstitutionEnvironment(XXX = 'xxx')
192         env4 = SubstitutionEnvironment(XXX = 'x', YYY = 'x')
193
194         assert env1 == env2
195         assert env1 != env3
196         assert env1 != env4
197
198     def test___delitem__(self):
199         """Test deleting a variable from a SubstitutionEnvironment
200         """
201         env1 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
202         env2 = SubstitutionEnvironment(XXX = 'x')
203         del env1['YYY']
204         assert env1 == env2
205
206     def test___getitem__(self):
207         """Test fetching a variable from a SubstitutionEnvironment
208         """
209         env = SubstitutionEnvironment(XXX = 'x')
210         assert env['XXX'] == 'x', env['XXX']
211
212     def test___setitem__(self):
213         """Test setting a variable in a SubstitutionEnvironment
214         """
215         env1 = SubstitutionEnvironment(XXX = 'x')
216         env2 = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
217         env1['YYY'] = 'y'
218         assert env1 == env2
219
220     def test_get(self):
221         """Test the SubstitutionEnvironment get() method
222         """
223         env = SubstitutionEnvironment(XXX = 'x')
224         assert env.get('XXX') == 'x', env.get('XXX')
225         assert env.get('YYY') is None, env.get('YYY')
226
227     def test_has_key(self):
228         """Test the SubstitutionEnvironment has_key() method
229         """
230         env = SubstitutionEnvironment(XXX = 'x')
231         assert 'XXX' in env
232         assert 'YYY' not in env
233
234     def test_contains(self):
235         """Test the SubstitutionEnvironment __contains__() method
236         """
237         try:
238             'x' in {'x':1}
239         except TypeError:
240             # TODO(1.5)
241             # An early version of Python that doesn't support "in"
242             # on dictionaries.  Just pass the test.
243             pass
244         else:
245             env = SubstitutionEnvironment(XXX = 'x')
246             assert 'XXX' in env
247             assert not 'YYY' in env
248
249     def test_items(self):
250         """Test the SubstitutionEnvironment items() method
251         """
252         env = SubstitutionEnvironment(XXX = 'x', YYY = 'y')
253         items = env.items()
254         assert items == [('XXX','x'), ('YYY','y')], items
255
256     def test_arg2nodes(self):
257         """Test the arg2nodes method
258         """
259         env = SubstitutionEnvironment()
260         dict = {}
261         class X(SCons.Node.Node):
262             pass
263         def Factory(name, directory = None, create = 1, dict=dict, X=X):
264             if name not in dict:
265                 dict[name] = X()
266                 dict[name].name = name
267             return dict[name]
268
269         nodes = env.arg2nodes("Util.py UtilTests.py", Factory)
270         assert len(nodes) == 1, nodes
271         assert isinstance(nodes[0], X)
272         assert nodes[0].name == "Util.py UtilTests.py"
273
274         import types
275         if hasattr(types, 'UnicodeType'):
276             code = """if 1:
277                 nodes = env.arg2nodes(u"Util.py UtilTests.py", Factory)
278                 assert len(nodes) == 1, nodes
279                 assert isinstance(nodes[0], X)
280                 assert nodes[0].name == u"Util.py UtilTests.py"
281                 \n"""
282             exec code in globals(), locals()
283
284         nodes = env.arg2nodes(["Util.py", "UtilTests.py"], Factory)
285         assert len(nodes) == 2, nodes
286         assert isinstance(nodes[0], X)
287         assert isinstance(nodes[1], X)
288         assert nodes[0].name == "Util.py"
289         assert nodes[1].name == "UtilTests.py"
290
291         n1 = Factory("Util.py")
292         nodes = env.arg2nodes([n1, "UtilTests.py"], Factory)
293         assert len(nodes) == 2, nodes
294         assert isinstance(nodes[0], X)
295         assert isinstance(nodes[1], X)
296         assert nodes[0].name == "Util.py"
297         assert nodes[1].name == "UtilTests.py"
298
299         class SConsNode(SCons.Node.Node):
300             pass
301         nodes = env.arg2nodes(SConsNode())
302         assert len(nodes) == 1, nodes
303         assert isinstance(nodes[0], SConsNode), node
304
305         class OtherNode:
306             pass
307         nodes = env.arg2nodes(OtherNode())
308         assert len(nodes) == 1, nodes
309         assert isinstance(nodes[0], OtherNode), node
310
311         def lookup_a(str, F=Factory):
312             if str[0] == 'a':
313                 n = F(str)
314                 n.a = 1
315                 return n
316             else:
317                 return None
318
319         def lookup_b(str, F=Factory):
320             if str[0] == 'b':
321                 n = F(str)
322                 n.b = 1
323                 return n
324             else:
325                 return None
326
327         env_ll = SubstitutionEnvironment()
328         env_ll.lookup_list = [lookup_a, lookup_b]
329
330         nodes = env_ll.arg2nodes(['aaa', 'bbb', 'ccc'], Factory)
331         assert len(nodes) == 3, nodes
332
333         assert nodes[0].name == 'aaa', nodes[0]
334         assert nodes[0].a == 1, nodes[0]
335         assert not hasattr(nodes[0], 'b'), nodes[0]
336
337         assert nodes[1].name == 'bbb'
338         assert not hasattr(nodes[1], 'a'), nodes[1]
339         assert nodes[1].b == 1, nodes[1]
340
341         assert nodes[2].name == 'ccc'
342         assert not hasattr(nodes[2], 'a'), nodes[1]
343         assert not hasattr(nodes[2], 'b'), nodes[1]
344
345         def lookup_bbbb(str, F=Factory):
346             if str == 'bbbb':
347                 n = F(str)
348                 n.bbbb = 1
349                 return n
350             else:
351                 return None
352
353         def lookup_c(str, F=Factory):
354             if str[0] == 'c':
355                 n = F(str)
356                 n.c = 1
357                 return n
358             else:
359                 return None
360
361         nodes = env.arg2nodes(['bbbb', 'ccc'], Factory,
362                                      [lookup_c, lookup_bbbb, lookup_b])
363         assert len(nodes) == 2, nodes
364
365         assert nodes[0].name == 'bbbb'
366         assert not hasattr(nodes[0], 'a'), nodes[1]
367         assert not hasattr(nodes[0], 'b'), nodes[1]
368         assert nodes[0].bbbb == 1, nodes[1]
369         assert not hasattr(nodes[0], 'c'), nodes[0]
370
371         assert nodes[1].name == 'ccc'
372         assert not hasattr(nodes[1], 'a'), nodes[1]
373         assert not hasattr(nodes[1], 'b'), nodes[1]
374         assert not hasattr(nodes[1], 'bbbb'), nodes[0]
375         assert nodes[1].c == 1, nodes[1]
376
377     def test_arg2nodes_target_source(self):
378         """Test the arg2nodes method with target= and source= keywords
379         """
380         targets = [DummyNode('t1'), DummyNode('t2')]
381         sources = [DummyNode('s1'), DummyNode('s2')]
382         env = SubstitutionEnvironment()
383         nodes = env.arg2nodes(['${TARGET}-a',
384                                '${SOURCE}-b',
385                                '${TARGETS[1]}-c',
386                                '${SOURCES[1]}-d'],
387                               DummyNode,
388                               target=targets,
389                               source=sources)
390         names = [n.name for n in nodes]
391         assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names
392
393     def test_gvars(self):
394         """Test the base class gvars() method"""
395         env = SubstitutionEnvironment()
396         gvars = env.gvars()
397         assert gvars == {}, gvars
398
399     def test_lvars(self):
400         """Test the base class lvars() method"""
401         env = SubstitutionEnvironment()
402         lvars = env.lvars()
403         assert lvars == {}, lvars
404
405     def test_subst(self):
406         """Test substituting construction variables within strings
407
408         Check various combinations, including recursive expansion
409         of variables into other variables.
410         """
411         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
412         mystr = env.subst("$AAA ${AAA}A $BBBB $BBB")
413         assert mystr == "a aA b", mystr
414
415         # Changed the tests below to reflect a bug fix in
416         # subst()
417         env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
418         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
419         assert mystr == "b bA bB b", mystr
420
421         env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
422         mystr = env.subst("$AAA ${AAA}A ${AAA}B $BBB")
423         assert mystr == "c cA cB c", mystr
424
425         # Lists:
426         env = SubstitutionEnvironment(AAA = ['a', 'aa', 'aaa'])
427         mystr = env.subst("$AAA")
428         assert mystr == "a aa aaa", mystr
429
430         # Tuples:
431         env = SubstitutionEnvironment(AAA = ('a', 'aa', 'aaa'))
432         mystr = env.subst("$AAA")
433         assert mystr == "a aa aaa", mystr
434
435         t1 = DummyNode('t1')
436         t2 = DummyNode('t2')
437         s1 = DummyNode('s1')
438         s2 = DummyNode('s2')
439
440         env = SubstitutionEnvironment(AAA = 'aaa')
441         s = env.subst('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
442         assert s == "aaa t1 s1 s2", s
443         s = env.subst('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
444         assert s == "aaa t1 t2 s1", s
445
446         # Test callables in the SubstitutionEnvironment
447         def foo(target, source, env, for_signature):
448             assert str(target) == 't', target
449             assert str(source) == 's', source
450             return env["FOO"]
451
452         env = SubstitutionEnvironment(BAR=foo, FOO='baz')
453         t = DummyNode('t')
454         s = DummyNode('s')
455
456         subst = env.subst('test $BAR', target=t, source=s)
457         assert subst == 'test baz', subst
458
459         # Test not calling callables in the SubstitutionEnvironment
460         if 0:
461             # This will take some serious surgery to subst() and
462             # subst_list(), so just leave these tests out until we can
463             # do that.
464             def bar(arg):
465                 pass
466
467             env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
468
469             subst = env.subst('$BAR', call=None)
470             assert subst is bar, subst
471
472             subst = env.subst('$FOO', call=None)
473             assert subst is bar, subst
474
475     def test_subst_kw(self):
476         """Test substituting construction variables within dictionaries"""
477         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
478         kw = env.subst_kw({'$AAA' : 'aaa', 'bbb' : '$BBB'})
479         assert len(kw) == 2, kw
480         assert kw['a'] == 'aaa', kw['a']
481         assert kw['bbb'] == 'b', kw['bbb']
482
483     def test_subst_list(self):
484         """Test substituting construction variables in command lists
485         """
486         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
487         l = env.subst_list("$AAA ${AAA}A $BBBB $BBB")
488         assert l == [["a", "aA", "b"]], l
489
490         # Changed the tests below to reflect a bug fix in
491         # subst()
492         env = SubstitutionEnvironment(AAA = '$BBB', BBB = 'b', BBBA = 'foo')
493         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
494         assert l == [["b", "bA", "bB", "b"]], l
495
496         env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = 'c')
497         l = env.subst_list("$AAA ${AAA}A ${AAA}B $BBB")
498         assert l == [["c", "cA", "cB", "c"]], mystr
499
500         env = SubstitutionEnvironment(AAA = '$BBB', BBB = '$CCC', CCC = [ 'a', 'b\nc' ])
501         lst = env.subst_list([ "$AAA", "B $CCC" ])
502         assert lst == [[ "a", "b"], ["c", "B a", "b"], ["c"]], lst
503
504         t1 = DummyNode('t1')
505         t2 = DummyNode('t2')
506         s1 = DummyNode('s1')
507         s2 = DummyNode('s2')
508
509         env = SubstitutionEnvironment(AAA = 'aaa')
510         s = env.subst_list('$AAA $TARGET $SOURCES', target=[t1, t2], source=[s1, s2])
511         assert s == [["aaa", "t1", "s1", "s2"]], s
512         s = env.subst_list('$AAA $TARGETS $SOURCE', target=[t1, t2], source=[s1, s2])
513         assert s == [["aaa", "t1", "t2", "s1"]], s
514
515         # Test callables in the SubstitutionEnvironment
516         def foo(target, source, env, for_signature):
517             assert str(target) == 't', target
518             assert str(source) == 's', source
519             return env["FOO"]
520
521         env = SubstitutionEnvironment(BAR=foo, FOO='baz')
522         t = DummyNode('t')
523         s = DummyNode('s')
524
525         lst = env.subst_list('test $BAR', target=t, source=s)
526         assert lst == [['test', 'baz']], lst
527
528         # Test not calling callables in the SubstitutionEnvironment
529         if 0:
530             # This will take some serious surgery to subst() and
531             # subst_list(), so just leave these tests out until we can
532             # do that.
533             def bar(arg):
534                 pass
535
536             env = SubstitutionEnvironment(BAR=bar, FOO='$BAR')
537
538             subst = env.subst_list('$BAR', call=None)
539             assert subst is bar, subst
540
541             subst = env.subst_list('$FOO', call=None)
542             assert subst is bar, subst
543
544     def test_subst_path(self):
545         """Test substituting a path list
546         """
547         class MyProxy:
548             def __init__(self, val):
549                 self.val = val
550             def get(self):
551                 return self.val + '-proxy'
552
553         class MyNode:
554             def __init__(self, val):
555                 self.val = val
556             def get_subst_proxy(self):
557                 return self
558             def __str__(self):
559                 return self.val
560
561         class MyObj:
562             def get(self):
563                 return self
564
565         env = SubstitutionEnvironment(FOO='foo',
566                                       BAR='bar',
567                                       LIST=['one', 'two'],
568                                       PROXY=MyProxy('my1'))
569
570         r = env.subst_path('$FOO')
571         assert r == ['foo'], r
572
573         r = env.subst_path(['$FOO', 'xxx', '$BAR'])
574         assert r == ['foo', 'xxx', 'bar'], r
575
576         r = env.subst_path(['$FOO', '$LIST', '$BAR'])
577         assert list(map(str, r)) == ['foo', 'one two', 'bar'], r
578
579         r = env.subst_path(['$FOO', '$TARGET', '$SOURCE', '$BAR'])
580         assert r == ['foo', '', '', 'bar'], r
581
582         r = env.subst_path(['$FOO', '$TARGET', '$BAR'], target=MyNode('ttt'))
583         assert list(map(str, r)) == ['foo', 'ttt', 'bar'], r
584
585         r = env.subst_path(['$FOO', '$SOURCE', '$BAR'], source=MyNode('sss'))
586         assert list(map(str, r)) == ['foo', 'sss', 'bar'], r
587
588         n = MyObj()
589
590         r = env.subst_path(['$PROXY', MyProxy('my2'), n])
591         assert r == ['my1-proxy', 'my2-proxy', n], r
592
593         class StringableObj:
594             def __init__(self, s):
595                 self.s = s
596             def __str__(self):
597                 return self.s
598
599         env = SubstitutionEnvironment(FOO=StringableObj("foo"),
600                           BAR=StringableObj("bar"))
601
602         r = env.subst_path([ "${FOO}/bar", "${BAR}/baz" ])
603         assert r == [ "foo/bar", "bar/baz" ], r
604
605         r = env.subst_path([ "bar/${FOO}", "baz/${BAR}" ])
606         assert r == [ "bar/foo", "baz/bar" ], r
607
608         r = env.subst_path([ "bar/${FOO}/bar", "baz/${BAR}/baz" ])
609         assert r == [ "bar/foo/bar", "baz/bar/baz" ], r
610
611     def test_subst_target_source(self):
612         """Test the base environment subst_target_source() method"""
613         env = SubstitutionEnvironment(AAA = 'a', BBB = 'b')
614         mystr = env.subst_target_source("$AAA ${AAA}A $BBBB $BBB")
615         assert mystr == "a aA b", mystr
616
617     def test_backtick(self):
618         """Test the backtick() method for capturing command output"""
619         env = SubstitutionEnvironment()
620
621         test = TestCmd.TestCmd(workdir = '')
622         test.write('stdout.py', """\
623 import sys
624 sys.stdout.write('this came from stdout.py\\n')
625 sys.exit(0)
626 """)
627         test.write('stderr.py', """\
628 import sys
629 sys.stderr.write('this came from stderr.py\\n')
630 sys.exit(0)
631 """)
632         test.write('fail.py', """\
633 import sys
634 sys.exit(1)
635 """)
636         test.write('echo.py', """\
637 import os, sys
638 sys.stdout.write(os.environ['ECHO'] + '\\n')
639 sys.exit(0)
640 """)
641
642         save_stderr = sys.stderr
643
644         python = '"' + sys.executable + '"'
645
646         try:
647             sys.stderr = StringIO.StringIO()
648             cmd = '%s %s' % (python, test.workpath('stdout.py'))
649             output = env.backtick(cmd)
650             errout = sys.stderr.getvalue()
651             assert output == 'this came from stdout.py\n', output
652             assert errout == '', errout
653
654             sys.stderr = StringIO.StringIO()
655             cmd = '%s %s' % (python, test.workpath('stderr.py'))
656             output = env.backtick(cmd)
657             errout = sys.stderr.getvalue()
658             assert output == '', output
659             assert errout == 'this came from stderr.py\n', errout
660
661             sys.stderr = StringIO.StringIO()
662             cmd = '%s %s' % (python, test.workpath('fail.py'))
663             try:
664                 env.backtick(cmd)
665             except OSError, e:
666                 assert str(e) == "'%s' exited 1" % cmd, str(e)
667             else:
668                 self.fail("did not catch expected OSError")
669
670             sys.stderr = StringIO.StringIO()
671             cmd = '%s %s' % (python, test.workpath('echo.py'))
672             env['ENV'] = os.environ.copy()
673             env['ENV']['ECHO'] = 'this came from ECHO'
674             output = env.backtick(cmd)
675             errout = sys.stderr.getvalue()
676             assert output == 'this came from ECHO\n', output
677             assert errout == '', errout
678
679         finally:
680             sys.stderr = save_stderr
681
682     def test_AddMethod(self):
683         """Test the AddMethod() method"""
684         env = SubstitutionEnvironment(FOO = 'foo')
685
686         def func(self):
687             return 'func-' + self['FOO']
688
689         assert not hasattr(env, 'func')
690         env.AddMethod(func)
691         r = env.func()
692         assert r == 'func-foo', r
693
694         assert not hasattr(env, 'bar')
695         env.AddMethod(func, 'bar')
696         r = env.bar()
697         assert r == 'func-foo', r
698
699         def func2(self, arg=''):
700             return 'func2-' + self['FOO'] + arg
701
702         env.AddMethod(func2)
703         r = env.func2()
704         assert r == 'func2-foo', r
705         r = env.func2('-xxx')
706         assert r == 'func2-foo-xxx', r
707
708         env.AddMethod(func2, 'func')
709         r = env.func()
710         assert r == 'func2-foo', r
711         r = env.func('-yyy')
712         assert r == 'func2-foo-yyy', r
713
714         # Test that clones of clones correctly re-bind added methods.
715         env1 = Environment(FOO = '1')
716         env1.AddMethod(func2)
717         env2 = env1.Clone(FOO = '2')
718         env3 = env2.Clone(FOO = '3')
719         env4 = env3.Clone(FOO = '4')
720         r = env1.func2()
721         assert r == 'func2-1', r
722         r = env2.func2()
723         assert r == 'func2-2', r
724         r = env3.func2()
725         assert r == 'func2-3', r
726         r = env4.func2()
727         assert r == 'func2-4', r
728
729         # Test that clones don't re-bind an attribute that the user
730         env1 = Environment(FOO = '1')
731         env1.AddMethod(func2)
732         def replace_func2():
733             return 'replace_func2'
734         env1.func2 = replace_func2
735         env2 = env1.Clone(FOO = '2')
736         r = env2.func2()
737         assert r == 'replace_func2', r
738
739     def test_Override(self):
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']
746
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']
754
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']
759
760         env2.Replace(ONE = "won")
761         assert env2['ONE'] == "won", env2['ONE']
762         assert env['ONE'] == 1, env['ONE']
763
764     def test_ParseFlags(self):
765         """Test the ParseFlags() method
766         """
767         env = SubstitutionEnvironment()
768
769         empty = {
770             'ASFLAGS'       : [],
771             'CFLAGS'        : [],
772             'CCFLAGS'       : [],
773             'CPPDEFINES'    : [],
774             'CPPFLAGS'      : [],
775             'CPPPATH'       : [],
776             'FRAMEWORKPATH' : [],
777             'FRAMEWORKS'    : [],
778             'LIBPATH'       : [],
779             'LIBS'          : [],
780             'LINKFLAGS'     : [],
781             'RPATH'         : [],
782         }
783
784         d = env.ParseFlags(None)
785         assert d == empty, d
786
787         d = env.ParseFlags('')
788         assert d == empty, d
789
790         d = env.ParseFlags([])
791         assert d == empty, d
792
793         s = "-I/usr/include/fum -I bar -X\n" + \
794             '-I"C:\\Program Files\\ASCEND\\include" ' + \
795             "-L/usr/fax -L foo -lxxx -l yyy " + \
796             '-L"C:\\Program Files\\ASCEND" -lascend ' + \
797             "-Wa,-as -Wl,-link " + \
798             "-Wl,-rpath=rpath1 " + \
799             "-Wl,-R,rpath2 " + \
800             "-Wl,-Rrpath3 " + \
801             "-Wp,-cpp " + \
802             "-std=c99 " + \
803             "-framework Carbon " + \
804             "-frameworkdir=fwd1 " + \
805             "-Ffwd2 " + \
806             "-F fwd3 " + \
807             "-pthread " + \
808             "-mno-cygwin -mwindows " + \
809             "-arch i386 -isysroot /tmp +DD64 " + \
810             "-DFOO -DBAR=value -D BAZ "
811
812         d = env.ParseFlags(s)
813
814         assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
815         assert d['CFLAGS']  == ['-std=c99']
816         assert d['CCFLAGS'] == ['-X', '-Wa,-as',
817                                   '-pthread', '-mno-cygwin',
818                                   ('-arch', 'i386'), ('-isysroot', '/tmp'),
819                                   '+DD64'], d['CCFLAGS']
820         assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
821         assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
822         assert d['CPPPATH'] == ['/usr/include/fum',
823                                 'bar',
824                                 'C:\\Program Files\\ASCEND\\include'], d['CPPPATH']
825         assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
826         assert d['FRAMEWORKS'] == ['Carbon'], d['FRAMEWORKS']
827         assert d['LIBPATH'] == ['/usr/fax',
828                                 'foo',
829                                 'C:\\Program Files\\ASCEND'], d['LIBPATH']
830         LIBS = list(map(str, d['LIBS']))
831         assert LIBS == ['xxx', 'yyy', 'ascend'], (d['LIBS'], LIBS)
832         assert d['LINKFLAGS'] == ['-Wl,-link', '-pthread',
833                                   '-mno-cygwin', '-mwindows',
834                                   ('-arch', 'i386'),
835                                   ('-isysroot', '/tmp'),
836                                   '+DD64'], d['LINKFLAGS']
837         assert d['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], d['RPATH']
838
839
840     def test_MergeFlags(self):
841         """Test the MergeFlags() method
842         """
843         env = SubstitutionEnvironment()
844         env.MergeFlags('')
845         assert 'CCFLAGS' not in env, env['CCFLAGS']
846         env.MergeFlags('-X')
847         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
848         env.MergeFlags('-X')
849         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
850
851         env = SubstitutionEnvironment(CCFLAGS=None)
852         env.MergeFlags('-Y')
853         assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
854
855         env = SubstitutionEnvironment()
856         env.MergeFlags({'A':['aaa'], 'B':['bbb']})
857         assert env['A'] == ['aaa'], env['A']
858         assert env['B'] == ['bbb'], env['B']
859
860 #     def test_MergeShellPaths(self):
861 #         """Test the MergeShellPaths() method
862 #         """
863 #         env = Environment()
864 #         env.MergeShellPaths({})
865 #         assert not env['ENV'].has_key('INCLUDE'), env['INCLUDE']
866 #         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
867 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
868 #         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'})
869 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
870 #         env.MergeShellPaths({'INCLUDE': r'xyz'})
871 #         assert env['ENV']['INCLUDE'] == r'xyz%sc:\Program Files\Stuff'%os.pathsep, env['ENV']['INCLUDE']
872
873 #         env = Environment()
874 #         env['ENV']['INCLUDE'] = 'xyz'
875 #         env.MergeShellPaths({'INCLUDE':['c:/inc1', 'c:/inc2']} )
876 #         assert env['ENV']['INCLUDE'] == r'c:/inc1%sc:/inc2%sxyz'%(os.pathsep, os.pathsep), env['ENV']['INCLUDE']
877
878 #         # test prepend=0
879 #         env = Environment()
880 #         env.MergeShellPaths({'INCLUDE': r'c:\Program Files\Stuff'}, prepend=0)
881 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff', env['ENV']['INCLUDE']
882 #         env.MergeShellPaths({'INCLUDE': r'xyz'}, prepend=0)
883 #         assert env['ENV']['INCLUDE'] == r'c:\Program Files\Stuff%sxyz'%os.pathsep, env['ENV']['INCLUDE']
884
885
886 class BaseTestCase(unittest.TestCase,TestEnvironmentFixture):
887
888     reserved_variables = [
889         'CHANGED_SOURCES',
890         'CHANGED_TARGETS',
891         'SOURCE',
892         'SOURCES',
893         'TARGET',
894         'TARGETS',
895         'UNCHANGED_SOURCES',
896         'UNCHANGED_TARGETS',
897     ]
898
899     def test___init__(self):
900         """Test construction Environment creation
901
902         Create two with identical arguments and check that
903         they compare the same.
904         """
905         env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
906         env2 = self.TestEnvironment(XXX = 'x', YYY = 'y')
907         assert env1 == env2, diff_env(env1, env2)
908
909         assert '__env__' not in env1
910         assert '__env__' not in env2
911
912     def test_variables(self):
913         """Test that variables only get applied once."""
914         class FakeOptions:
915             def __init__(self, key, val):
916                 self.calls = 0
917                 self.key = key
918                 self.val = val
919             def keys(self):
920                 return [self.key]
921             def Update(self, env):
922                 env[self.key] = self.val
923                 self.calls = self.calls + 1
924
925         o = FakeOptions('AAA', 'fake_opt')
926         env = Environment(variables=o, AAA='keyword_arg')
927         assert o.calls == 1, o.calls
928         assert env['AAA'] == 'fake_opt', env['AAA']
929
930     def test_get(self):
931         """Test the get() method."""
932         env = self.TestEnvironment(aaa = 'AAA')
933
934         x = env.get('aaa')
935         assert x == 'AAA', x
936         x = env.get('aaa', 'XXX')
937         assert x == 'AAA', x
938         x = env.get('bbb')
939         assert x is None, x
940         x = env.get('bbb', 'XXX')
941         assert x == 'XXX', x
942
943     def test_Builder_calls(self):
944         """Test Builder calls through different environments
945         """
946         global called_it
947
948         b1 = Builder()
949         b2 = Builder()
950
951         env = Environment()
952         env.Replace(BUILDERS = { 'builder1' : b1,
953                                  'builder2' : b2 })
954         called_it = {}
955         env.builder1('in1')
956         assert called_it['target'] is None, called_it
957         assert called_it['source'] == ['in1'], called_it
958
959         called_it = {}
960         env.builder2(source = 'in2', xyzzy = 1)
961         assert called_it['target'] is None, called_it
962         assert called_it['source'] == ['in2'], called_it
963         assert called_it['xyzzy'] == 1, called_it
964
965         called_it = {}
966         env.builder1(foo = 'bar')
967         assert called_it['foo'] == 'bar', called_it
968         assert called_it['target'] is None, called_it
969         assert called_it['source'] is None, called_it
970
971     def test_BuilderWrapper_attributes(self):
972         """Test getting and setting of BuilderWrapper attributes
973         """
974         b1 = Builder()
975         b2 = Builder()
976         e1 = Environment()
977         e2 = Environment()
978
979         e1.Replace(BUILDERS = {'b' : b1})
980         bw = e1.b
981
982         assert bw.env is e1
983         bw.env = e2
984         assert bw.env is e2
985
986         assert bw.builder is b1
987         bw.builder = b2
988         assert bw.builder is b2
989
990         self.assertRaises(AttributeError, getattr, bw, 'foobar')
991         bw.foobar = 42
992         assert bw.foobar is 42
993
994     # This unit test is currently disabled because we don't think the
995     # underlying method it tests (Environment.BuilderWrapper.execute())
996     # is necessary, but we're leaving the code here for now in case
997     # that's mistaken.
998     def _DO_NOT_test_Builder_execs(self):
999         """Test Builder execution through different environments
1000
1001         One environment is initialized with a single
1002         Builder object, one with a list of a single Builder
1003         object, and one with a list of two Builder objects.
1004         """
1005         global built_it
1006
1007         b1 = Builder()
1008         b2 = Builder()
1009
1010         built_it = {}
1011         env3 = Environment()
1012         env3.Replace(BUILDERS = { 'builder1' : b1,
1013                                   'builder2' : b2 })
1014         env3.builder1.execute(target = 'out1')
1015         env3.builder2.execute(target = 'out2')
1016         env3.builder1.execute(target = 'out3')
1017         assert built_it['out1']
1018         assert built_it['out2']
1019         assert built_it['out3']
1020
1021         env4 = env3.Clone()
1022         assert env4.builder1.env is env4, "builder1.env (%s) == env3 (%s)?" % (
1023 env4.builder1.env, env3)
1024         assert env4.builder2.env is env4, "builder2.env (%s) == env3 (%s)?" % (
1025 env4.builder1.env, env3)
1026
1027         # Now test BUILDERS as a dictionary.
1028         built_it = {}
1029         env5 = self.TestEnvironment(BUILDERS={ 'foo' : b1 })
1030         env5['BUILDERS']['bar'] = b2
1031         env5.foo.execute(target='out1')
1032         env5.bar.execute(target='out2')
1033         assert built_it['out1']
1034         assert built_it['out2']
1035
1036         built_it = {}
1037         env6 = Environment()
1038         env6['BUILDERS'] = { 'foo' : b1,
1039                              'bar' : b2 }
1040         env6.foo.execute(target='out1')
1041         env6.bar.execute(target='out2')
1042         assert built_it['out1']
1043         assert built_it['out2']
1044
1045
1046
1047     def test_Scanners(self):
1048         """Test setting SCANNERS in various ways
1049
1050         One environment is initialized with a single
1051         Scanner object, one with a list of a single Scanner
1052         object, and one with a list of two Scanner objects.
1053         """
1054         global scanned_it
1055
1056         s1 = Scanner(name = 'scanner1', skeys = [".c", ".cc"])
1057         s2 = Scanner(name = 'scanner2', skeys = [".m4"])
1058         s3 = Scanner(name = 'scanner3', skeys = [".m4", ".m5"])
1059         s4 = Scanner(name = 'scanner4', skeys = [None])
1060
1061 #        XXX Tests for scanner execution through different environments,
1062 #        XXX if we ever want to do that some day
1063 #        scanned_it = {}
1064 #        env1 = self.TestEnvironment(SCANNERS = s1)
1065 #        env1.scanner1(filename = 'out1')
1066 #        assert scanned_it['out1']
1067 #
1068 #        scanned_it = {}
1069 #        env2 = self.TestEnvironment(SCANNERS = [s1])
1070 #        env1.scanner1(filename = 'out1')
1071 #        assert scanned_it['out1']
1072 #
1073 #        scanned_it = {}
1074 #        env3 = Environment()
1075 #        env3.Replace(SCANNERS = [s1])
1076 #        env3.scanner1(filename = 'out1')
1077 #        env3.scanner2(filename = 'out2')
1078 #        env3.scanner1(filename = 'out3')
1079 #        assert scanned_it['out1']
1080 #        assert scanned_it['out2']
1081 #        assert scanned_it['out3']
1082
1083         suffixes = [".c", ".cc", ".cxx", ".m4", ".m5"]
1084
1085         env = Environment()
1086         try: del env['SCANNERS']
1087         except KeyError: pass
1088         s = list(map(env.get_scanner, suffixes))
1089         assert s == [None, None, None, None, None], s
1090
1091         env = self.TestEnvironment(SCANNERS = [])
1092         s = list(map(env.get_scanner, suffixes))
1093         assert s == [None, None, None, None, None], s
1094
1095         env.Replace(SCANNERS = [s1])
1096         s = list(map(env.get_scanner, suffixes))
1097         assert s == [s1, s1, None, None, None], s
1098
1099         env.Append(SCANNERS = [s2])
1100         s = list(map(env.get_scanner, suffixes))
1101         assert s == [s1, s1, None, s2, None], s
1102
1103         env.AppendUnique(SCANNERS = [s3])
1104         s = list(map(env.get_scanner, suffixes))
1105         assert s == [s1, s1, None, s2, s3], s
1106
1107         env = env.Clone(SCANNERS = [s2])
1108         s = list(map(env.get_scanner, suffixes))
1109         assert s == [None, None, None, s2, None], s
1110
1111         env['SCANNERS'] = [s1]
1112         s = list(map(env.get_scanner, suffixes))
1113         assert s == [s1, s1, None, None, None], s
1114
1115         env.PrependUnique(SCANNERS = [s2, s1])
1116         s = list(map(env.get_scanner, suffixes))
1117         assert s == [s1, s1, None, s2, None], s
1118
1119         env.Prepend(SCANNERS = [s3])
1120         s = list(map(env.get_scanner, suffixes))
1121         assert s == [s1, s1, None, s3, s3], s
1122
1123         # Verify behavior of case-insensitive suffix matches on Windows.
1124         uc_suffixes = [_.upper() for _ in suffixes]
1125
1126         env = Environment(SCANNERS = [s1, s2, s3],
1127                           PLATFORM = 'linux')
1128
1129         s = list(map(env.get_scanner, suffixes))
1130         assert s == [s1, s1, None, s2, s3], s
1131
1132         s = list(map(env.get_scanner, uc_suffixes))
1133         assert s == [None, None, None, None, None], s
1134
1135         env['PLATFORM'] = 'win32'
1136
1137         s = list(map(env.get_scanner, uc_suffixes))
1138         assert s == [s1, s1, None, s2, s3], s
1139
1140         # Verify behavior for a scanner returning None (on Windows
1141         # where we might try to perform case manipulation on None).
1142         env.Replace(SCANNERS = [s4])
1143         s = list(map(env.get_scanner, suffixes))
1144         assert s == [None, None, None, None, None], s
1145
1146     def test_ENV(self):
1147         """Test setting the external ENV in Environments
1148         """
1149         env = Environment()
1150         assert 'ENV' in env.Dictionary()
1151
1152         env = self.TestEnvironment(ENV = { 'PATH' : '/foo:/bar' })
1153         assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
1154
1155     def test_ReservedVariables(self):
1156         """Test warning generation when reserved variable names are set"""
1157
1158         reserved_variables = [
1159             'CHANGED_SOURCES',
1160             'CHANGED_TARGETS',
1161             'SOURCE',
1162             'SOURCES',
1163             'TARGET',
1164             'TARGETS',
1165             'UNCHANGED_SOURCES',
1166             'UNCHANGED_TARGETS',
1167         ]
1168
1169         warning = SCons.Warnings.ReservedVariableWarning
1170         SCons.Warnings.enableWarningClass(warning)
1171         old = SCons.Warnings.warningAsException(1)
1172
1173         try:
1174             env4 = Environment()
1175             for kw in self.reserved_variables:
1176                 exc_caught = None
1177                 try:
1178                     env4[kw] = 'xyzzy'
1179                 except warning:
1180                     exc_caught = 1
1181                 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
1182                 assert kw not in env4, "`%s' variable was incorrectly set" % kw
1183         finally:
1184             SCons.Warnings.warningAsException(old)
1185
1186     def test_FutureReservedVariables(self):
1187         """Test warning generation when future reserved variable names are set"""
1188
1189         future_reserved_variables = []
1190
1191         warning = SCons.Warnings.FutureReservedVariableWarning
1192         SCons.Warnings.enableWarningClass(warning)
1193         old = SCons.Warnings.warningAsException(1)
1194
1195         try:
1196             env4 = Environment()
1197             for kw in future_reserved_variables:
1198                 exc_caught = None
1199                 try:
1200                     env4[kw] = 'xyzzy'
1201                 except warning:
1202                     exc_caught = 1
1203                 assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw
1204                 assert kw in env4, "`%s' variable was not set" % kw
1205         finally:
1206             SCons.Warnings.warningAsException(old)
1207
1208     def test_IllegalVariables(self):
1209         """Test that use of illegal variables raises an exception"""
1210         env = Environment()
1211         def test_it(var, env=env):
1212             exc_caught = None
1213             try:
1214                 env[var] = 1
1215             except SCons.Errors.UserError:
1216                 exc_caught = 1
1217             assert exc_caught, "did not catch UserError for '%s'" % var
1218         env['aaa'] = 1
1219         assert env['aaa'] == 1, env['aaa']
1220         test_it('foo/bar')
1221         test_it('foo.bar')
1222         test_it('foo-bar')
1223
1224     def test_autogenerate(dict):
1225         """Test autogenerating variables in a dictionary."""
1226
1227         drive, p = os.path.splitdrive(os.getcwd())
1228         def normalize_path(path, drive=drive):
1229             if path[0] in '\\/':
1230                 path = drive + path
1231             path = os.path.normpath(path)
1232             drive, path = os.path.splitdrive(path)
1233             return drive.lower() + path
1234
1235         env = dict.TestEnvironment(LIBS = [ 'foo', 'bar', 'baz' ],
1236                           LIBLINKPREFIX = 'foo',
1237                           LIBLINKSUFFIX = 'bar')
1238
1239         def RDirs(pathlist, fs=env.fs):
1240             return fs.Dir('xx').Rfindalldirs(pathlist)
1241
1242         env['RDirs'] = RDirs
1243         flags = env.subst_list('$_LIBFLAGS', 1)[0]
1244         assert flags == ['foobar', 'foobar', 'foobazbar'], flags
1245
1246         blat = env.fs.Dir('blat')
1247
1248         env.Replace(CPPPATH = [ 'foo', '$FOO/bar', blat ],
1249                     INCPREFIX = 'foo ',
1250                     INCSUFFIX = 'bar',
1251                     FOO = 'baz')
1252         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
1253         expect = [ '$(',
1254                    normalize_path('foo'),
1255                    normalize_path('xx/foobar'),
1256                    normalize_path('foo'),
1257                    normalize_path('xx/baz/bar'),
1258                    normalize_path('foo'),
1259                    normalize_path('blatbar'),
1260                    '$)',
1261         ]
1262         assert flags == expect, flags
1263
1264         env.Replace(F77PATH = [ 'foo', '$FOO/bar', blat ],
1265                     INCPREFIX = 'foo ',
1266                     INCSUFFIX = 'bar',
1267                     FOO = 'baz')
1268         flags = env.subst_list('$_F77INCFLAGS', 1)[0]
1269         expect = [ '$(',
1270                    normalize_path('foo'),
1271                    normalize_path('xx/foobar'),
1272                    normalize_path('foo'),
1273                    normalize_path('xx/baz/bar'),
1274                    normalize_path('foo'),
1275                    normalize_path('blatbar'),
1276                    '$)',
1277         ]
1278         assert flags == expect, flags
1279
1280         env.Replace(CPPPATH = '', F77PATH = '', LIBPATH = '')
1281         l = env.subst_list('$_CPPINCFLAGS')
1282         assert l == [[]], l
1283         l = env.subst_list('$_F77INCFLAGS')
1284         assert l == [[]], l
1285         l = env.subst_list('$_LIBDIRFLAGS')
1286         assert l == [[]], l
1287
1288         env.fs.Repository('/rep1')
1289         env.fs.Repository('/rep2')
1290         env.Replace(CPPPATH = [ 'foo', '/a/b', '$FOO/bar', blat],
1291                     INCPREFIX = '-I ',
1292                     INCSUFFIX = 'XXX',
1293                     FOO = 'baz')
1294         flags = env.subst_list('$_CPPINCFLAGS', 1)[0]
1295         expect = [ '$(',
1296                    '-I', normalize_path('xx/fooXXX'),
1297                    '-I', normalize_path('/rep1/xx/fooXXX'),
1298                    '-I', normalize_path('/rep2/xx/fooXXX'),
1299                    '-I', normalize_path('/a/bXXX'),
1300                    '-I', normalize_path('xx/baz/barXXX'),
1301                    '-I', normalize_path('/rep1/xx/baz/barXXX'),
1302                    '-I', normalize_path('/rep2/xx/baz/barXXX'),
1303                    '-I', normalize_path('blatXXX'),
1304                    '$)'
1305         ]
1306         def normalize_if_path(arg, np=normalize_path):
1307             if arg not in ('$(','$)','-I'):
1308                 return np(str(arg))
1309             return arg
1310         flags = list(map(normalize_if_path, flags))
1311         assert flags == expect, flags
1312
1313     def test_platform(self):
1314         """Test specifying a platform callable when instantiating."""
1315         class platform:
1316             def __str__(self):        return "TestPlatform"
1317             def __call__(self, env):  env['XYZZY'] = 777
1318
1319         def tool(env):
1320             env['SET_TOOL'] = 'initialized'
1321             assert env['PLATFORM'] == "TestPlatform"
1322
1323         env = self.TestEnvironment(platform = platform(), tools = [tool])
1324         assert env['XYZZY'] == 777, env
1325         assert env['PLATFORM'] == "TestPlatform"
1326         assert env['SET_TOOL'] == "initialized"
1327
1328     def test_Default_PLATFORM(self):
1329         """Test overriding the default PLATFORM variable"""
1330         class platform:
1331             def __str__(self):        return "DefaultTestPlatform"
1332             def __call__(self, env):  env['XYZZY'] = 888
1333
1334         def tool(env):
1335             env['SET_TOOL'] = 'abcde'
1336             assert env['PLATFORM'] == "DefaultTestPlatform"
1337
1338         import SCons.Defaults
1339         save = SCons.Defaults.ConstructionEnvironment.copy()
1340         try:
1341             import SCons.Defaults
1342             SCons.Defaults.ConstructionEnvironment.update({
1343                 'PLATFORM' : platform(),
1344             })
1345             env = self.TestEnvironment(tools = [tool])
1346             assert env['XYZZY'] == 888, env
1347             assert env['PLATFORM'] == "DefaultTestPlatform"
1348             assert env['SET_TOOL'] == "abcde"
1349         finally:
1350             SCons.Defaults.ConstructionEnvironment = save
1351
1352     def test_tools(self):
1353         """Test specifying a tool callable when instantiating."""
1354         def t1(env):
1355             env['TOOL1'] = 111
1356         def t2(env):
1357             env['TOOL2'] = 222
1358         def t3(env):
1359             env['AAA'] = env['XYZ']
1360         def t4(env):
1361             env['TOOL4'] = 444
1362         env = self.TestEnvironment(tools = [t1, t2, t3], XYZ = 'aaa')
1363         assert env['TOOL1'] == 111, env['TOOL1']
1364         assert env['TOOL2'] == 222, env
1365         assert env['AAA'] == 'aaa', env
1366         t4(env)
1367         assert env['TOOL4'] == 444, env
1368
1369         test = TestCmd.TestCmd(workdir = '')
1370         test.write('faketool.py', """\
1371 def generate(env, **kw):
1372     for k, v in kw.items():
1373         env[k] = v
1374
1375 def exists(env):
1376     return 1
1377 """)
1378
1379         env = self.TestEnvironment(tools = [('faketool', {'a':1, 'b':2, 'c':3})],
1380                           toolpath = [test.workpath('')])
1381         assert env['a'] == 1, env['a']
1382         assert env['b'] == 2, env['b']
1383         assert env['c'] == 3, env['c']
1384
1385     def test_Default_TOOLS(self):
1386         """Test overriding the default TOOLS variable"""
1387         def t5(env):
1388             env['TOOL5'] = 555
1389         def t6(env):
1390             env['TOOL6'] = 666
1391         def t7(env):
1392             env['BBB'] = env['XYZ']
1393         def t8(env):
1394             env['TOOL8'] = 888
1395
1396         import SCons.Defaults
1397         save = SCons.Defaults.ConstructionEnvironment.copy()
1398         try:
1399             SCons.Defaults.ConstructionEnvironment.update({
1400                 'TOOLS' : [t5, t6, t7],
1401             })
1402             env = Environment(XYZ = 'bbb')
1403             assert env['TOOL5'] == 555, env['TOOL5']
1404             assert env['TOOL6'] == 666, env
1405             assert env['BBB'] == 'bbb', env
1406             t8(env)
1407             assert env['TOOL8'] == 888, env
1408         finally:
1409             SCons.Defaults.ConstructionEnvironment = save
1410
1411     def test_null_tools(self):
1412         """Test specifying a tool of None is OK."""
1413         def t1(env):
1414             env['TOOL1'] = 111
1415         def t2(env):
1416             env['TOOL2'] = 222
1417         env = self.TestEnvironment(tools = [t1, None, t2], XYZ = 'aaa')
1418         assert env['TOOL1'] == 111, env['TOOL1']
1419         assert env['TOOL2'] == 222, env
1420         assert env['XYZ'] == 'aaa', env
1421         env = self.TestEnvironment(tools = [None], XYZ = 'xyz')
1422         assert env['XYZ'] == 'xyz', env
1423         env = self.TestEnvironment(tools = [t1, '', t2], XYZ = 'ddd')
1424         assert env['TOOL1'] == 111, env['TOOL1']
1425         assert env['TOOL2'] == 222, env
1426         assert env['XYZ'] == 'ddd', env
1427
1428     def test_concat(self):
1429         "Test _concat()"
1430         e1 = self.TestEnvironment(PRE='pre', SUF='suf', STR='a b', LIST=['a', 'b'])
1431         s = e1.subst
1432         x = s("${_concat('', '', '', __env__)}")
1433         assert x == '', x
1434         x = s("${_concat('', [], '', __env__)}")
1435         assert x == '', x
1436         x = s("${_concat(PRE, '', SUF, __env__)}")
1437         assert x == '', x
1438         x = s("${_concat(PRE, STR, SUF, __env__)}")
1439         assert x == 'prea bsuf', x
1440         x = s("${_concat(PRE, LIST, SUF, __env__)}")
1441         assert x == 'preasuf prebsuf', x
1442
1443     def test_gvars(self):
1444         """Test the Environment gvars() method"""
1445         env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
1446         gvars = env.gvars()
1447         assert gvars['XXX'] == 'x', gvars['XXX']
1448         assert gvars['YYY'] == 'y', gvars['YYY']
1449         assert gvars['ZZZ'] == 'z', gvars['ZZZ']
1450
1451     def test__update(self):
1452         """Test the _update() method"""
1453         env = self.TestEnvironment(X = 'x', Y = 'y', Z = 'z')
1454         assert env['X'] == 'x', env['X']
1455         assert env['Y'] == 'y', env['Y']
1456         assert env['Z'] == 'z', env['Z']
1457         env._update({'X'       : 'xxx',
1458                      'TARGET'  : 't',
1459                      'TARGETS' : 'ttt',
1460                      'SOURCE'  : 's',
1461                      'SOURCES' : 'sss',
1462                      'Z'       : 'zzz'})
1463         assert env['X'] == 'xxx', env['X']
1464         assert env['Y'] == 'y', env['Y']
1465         assert env['Z'] == 'zzz', env['Z']
1466         assert env['TARGET'] == 't', env['TARGET']
1467         assert env['TARGETS'] == 'ttt', env['TARGETS']
1468         assert env['SOURCE'] == 's', env['SOURCE']
1469         assert env['SOURCES'] == 'sss', env['SOURCES']
1470
1471
1472
1473     def test_Append(self):
1474         """Test appending to construction variables in an Environment
1475         """
1476
1477         b1 = Environment()['BUILDERS']
1478         b2 = Environment()['BUILDERS']
1479         assert b1 == b2, diff_dict(b1, b2)
1480
1481         import UserDict
1482         UD = UserDict.UserDict
1483         import UserList
1484         UL = UserList.UserList
1485
1486         cases = [
1487             'a1',       'A1',           'a1A1',
1488             'a2',       ['A2'],         ['a2', 'A2'],
1489             'a3',       UL(['A3']),     UL(['a', '3', 'A3']),
1490             'a4',       '',             'a4',
1491             'a5',       [],             ['a5'],
1492             'a6',       UL([]),         UL(['a', '6']),
1493             'a7',       [''],           ['a7', ''],
1494             'a8',       UL(['']),       UL(['a', '8', '']),
1495
1496             ['e1'],     'E1',           ['e1', 'E1'],
1497             ['e2'],     ['E2'],         ['e2', 'E2'],
1498             ['e3'],     UL(['E3']),     UL(['e3', 'E3']),
1499             ['e4'],     '',             ['e4'],
1500             ['e5'],     [],             ['e5'],
1501             ['e6'],     UL([]),         UL(['e6']),
1502             ['e7'],     [''],           ['e7', ''],
1503             ['e8'],     UL(['']),       UL(['e8', '']),
1504
1505             UL(['i1']), 'I1',           UL(['i1', 'I', '1']),
1506             UL(['i2']), ['I2'],         UL(['i2', 'I2']),
1507             UL(['i3']), UL(['I3']),     UL(['i3', 'I3']),
1508             UL(['i4']), '',             UL(['i4']),
1509             UL(['i5']), [],             UL(['i5']),
1510             UL(['i6']), UL([]),         UL(['i6']),
1511             UL(['i7']), [''],           UL(['i7', '']),
1512             UL(['i8']), UL(['']),       UL(['i8', '']),
1513
1514             {'d1':1},   'D1',           {'d1':1, 'D1':None},
1515             {'d2':1},   ['D2'],         {'d2':1, 'D2':None},
1516             {'d3':1},   UL(['D3']),     {'d3':1, 'D3':None},
1517             {'d4':1},   {'D4':1},       {'d4':1, 'D4':1},
1518             {'d5':1},   UD({'D5':1}),   UD({'d5':1, 'D5':1}),
1519
1520             UD({'u1':1}), 'U1',         UD({'u1':1, 'U1':None}),
1521             UD({'u2':1}), ['U2'],       UD({'u2':1, 'U2':None}),
1522             UD({'u3':1}), UL(['U3']),   UD({'u3':1, 'U3':None}),
1523             UD({'u4':1}), {'U4':1},     UD({'u4':1, 'U4':1}),
1524             UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
1525
1526             '',         'M1',           'M1',
1527             '',         ['M2'],         ['M2'],
1528             '',         UL(['M3']),     UL(['M3']),
1529             '',         '',             '',
1530             '',         [],             [],
1531             '',         UL([]),         UL([]),
1532             '',         [''],           [''],
1533             '',         UL(['']),       UL(['']),
1534
1535             [],         'N1',           ['N1'],
1536             [],         ['N2'],         ['N2'],
1537             [],         UL(['N3']),     UL(['N3']),
1538             [],         '',             [],
1539             [],         [],             [],
1540             [],         UL([]),         UL([]),
1541             [],         [''],           [''],
1542             [],         UL(['']),       UL(['']),
1543
1544             UL([]),     'O1',           ['O', '1'],
1545             UL([]),     ['O2'],         ['O2'],
1546             UL([]),     UL(['O3']),     UL(['O3']),
1547             UL([]),     '',             UL([]),
1548             UL([]),     [],             UL([]),
1549             UL([]),     UL([]),         UL([]),
1550             UL([]),     [''],           UL(['']),
1551             UL([]),     UL(['']),       UL(['']),
1552
1553             [''],       'P1',           ['', 'P1'],
1554             [''],       ['P2'],         ['', 'P2'],
1555             [''],       UL(['P3']),     UL(['', 'P3']),
1556             [''],       '',             [''],
1557             [''],       [],             [''],
1558             [''],       UL([]),         UL(['']),
1559             [''],       [''],           ['', ''],
1560             [''],       UL(['']),       UL(['', '']),
1561
1562             UL(['']),   'Q1',           ['', 'Q', '1'],
1563             UL(['']),   ['Q2'],         ['', 'Q2'],
1564             UL(['']),   UL(['Q3']),     UL(['', 'Q3']),
1565             UL(['']),   '',             UL(['']),
1566             UL(['']),   [],             UL(['']),
1567             UL(['']),   UL([]),         UL(['']),
1568             UL(['']),   [''],           UL(['', '']),
1569             UL(['']),   UL(['']),       UL(['', '']),
1570         ]
1571
1572         env = Environment()
1573         failed = 0
1574         while cases:
1575             input, append, expect = cases[:3]
1576             env['XXX'] = copy.copy(input)
1577             try:
1578                 env.Append(XXX = append)
1579             except Exception, e:
1580                 if failed == 0: print
1581                 print "    %s Append %s exception: %s" % \
1582                       (repr(input), repr(append), e)
1583                 failed = failed + 1
1584             else:
1585                 result = env['XXX']
1586                 if result != expect:
1587                     if failed == 0: print
1588                     print "    %s Append %s => %s did not match %s" % \
1589                           (repr(input), repr(append), repr(result), repr(expect))
1590                     failed = failed + 1
1591             del cases[:3]
1592         assert failed == 0, "%d Append() cases failed" % failed
1593
1594         env['UL'] = UL(['foo'])
1595         env.Append(UL = 'bar')
1596         result = env['UL']
1597         assert isinstance(result, UL), repr(result)
1598         assert result == ['foo', 'b', 'a', 'r'], result
1599
1600         env['CLVar'] = CLVar(['foo'])
1601         env.Append(CLVar = 'bar')
1602         result = env['CLVar']
1603         assert isinstance(result, CLVar), repr(result)
1604         assert result == ['foo', 'bar'], result
1605
1606         class C:
1607             def __init__(self, name):
1608                 self.name = name
1609             def __str__(self):
1610                 return self.name
1611             def __cmp__(self, other):
1612                 raise "should not compare"
1613
1614         ccc = C('ccc')
1615
1616         env2 = self.TestEnvironment(CCC1 = ['c1'], CCC2 = ccc)
1617         env2.Append(CCC1 = ccc, CCC2 = ['c2'])
1618         assert env2['CCC1'][0] == 'c1', env2['CCC1']
1619         assert env2['CCC1'][1] is ccc, env2['CCC1']
1620         assert env2['CCC2'][0] is ccc, env2['CCC2']
1621         assert env2['CCC2'][1] == 'c2', env2['CCC2']
1622
1623         env3 = self.TestEnvironment(X = {'x1' : 7})
1624         env3.Append(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
1625         assert env3['X'] == {'x1': 8, 'x2': 9}, env3['X']
1626         assert env3['Y'] == {'y1': 10}, env3['Y']
1627
1628         z1 = Builder()
1629         z2 = Builder()
1630         env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
1631         env4.Append(BUILDERS = {'z2' : z2})
1632         assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
1633         assert hasattr(env4, 'z1')
1634         assert hasattr(env4, 'z2')
1635
1636     def test_AppendENVPath(self):
1637         """Test appending to an ENV path."""
1638         env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
1639                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
1640         # have to include the pathsep here so that the test will work on UNIX too.
1641         env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
1642         env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
1643         env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
1644         env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
1645         # this should do nothing since delete_existing is 0
1646         env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
1647         assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
1648         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
1649
1650         test = TestCmd.TestCmd(workdir = '')
1651         test.subdir('sub1', 'sub2')
1652         p=env1['ENV']['PATH']
1653         env1.AppendENVPath('PATH','#sub1', sep = ';')
1654         env1.AppendENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
1655         assert env1['ENV']['PATH'] == p + ';sub1;sub2', env1['ENV']['PATH']
1656
1657     def test_AppendUnique(self):
1658         """Test appending to unique values to construction variables
1659
1660         This strips values that are already present when lists are
1661         involved."""
1662         env = self.TestEnvironment(AAA1 = 'a1',
1663                           AAA2 = 'a2',
1664                           AAA3 = 'a3',
1665                           AAA4 = 'a4',
1666                           AAA5 = 'a5',
1667                           BBB1 = ['b1'],
1668                           BBB2 = ['b2'],
1669                           BBB3 = ['b3'],
1670                           BBB4 = ['b4'],
1671                           BBB5 = ['b5'],
1672                           CCC1 = '',
1673                           CCC2 = '',
1674                           DDD1 = ['a', 'b', 'c'])
1675         env.AppendUnique(AAA1 = 'a1',
1676                          AAA2 = ['a2'],
1677                          AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
1678                          AAA4 = 'a4.new',
1679                          AAA5 = ['a5.new'],
1680                          BBB1 = 'b1',
1681                          BBB2 = ['b2'],
1682                          BBB3 = ['b3', 'c', 'd', 'c', 'b3'],
1683                          BBB4 = 'b4.new',
1684                          BBB5 = ['b5.new'],
1685                          CCC1 = 'c1',
1686                          CCC2 = ['c2'],
1687                          DDD1 = 'b')
1688
1689         assert env['AAA1'] == 'a1a1', env['AAA1']
1690         assert env['AAA2'] == ['a2'], env['AAA2']
1691         assert env['AAA3'] == ['a3', 'b', 'c'], env['AAA3']
1692         assert env['AAA4'] == 'a4a4.new', env['AAA4']
1693         assert env['AAA5'] == ['a5', 'a5.new'], env['AAA5']
1694         assert env['BBB1'] == ['b1'], env['BBB1']
1695         assert env['BBB2'] == ['b2'], env['BBB2']
1696         assert env['BBB3'] == ['b3', 'c', 'd'], env['BBB3']
1697         assert env['BBB4'] == ['b4', 'b4.new'], env['BBB4']
1698         assert env['BBB5'] == ['b5', 'b5.new'], env['BBB5']
1699         assert env['CCC1'] == 'c1', env['CCC1']
1700         assert env['CCC2'] == ['c2'], env['CCC2']
1701         assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
1702
1703         env.AppendUnique(DDD1 = 'b', delete_existing=1)
1704         assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end
1705         env.AppendUnique(DDD1 = ['a','b'], delete_existing=1)
1706         assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
1707         env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
1708         assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
1709         
1710         env['CLVar'] = CLVar([])
1711         env.AppendUnique(CLVar = 'bar')
1712         result = env['CLVar']
1713         if sys.version[0] == '1' or sys.version[:3] == '2.0':
1714             # Python 2.0 and before have a quirky behavior where CLVar([])
1715             # actually matches '' and [] due to different __coerce__()
1716             # semantics in the UserList implementation.  It isn't worth a
1717             # lot of effort to get this corner case to work identically
1718             # (support for Python 1.5 support will die soon anyway),
1719             # so just treat it separately for now.
1720             assert result == 'bar', result
1721         else:
1722             assert isinstance(result, CLVar), repr(result)
1723             assert result == ['bar'], result
1724
1725         env['CLVar'] = CLVar(['abc'])
1726         env.AppendUnique(CLVar = 'bar')
1727         result = env['CLVar']
1728         assert isinstance(result, CLVar), repr(result)
1729         assert result == ['abc', 'bar'], result
1730
1731         env['CLVar'] = CLVar(['bar'])
1732         env.AppendUnique(CLVar = 'bar')
1733         result = env['CLVar']
1734         assert isinstance(result, CLVar), repr(result)
1735         assert result == ['bar'], result
1736
1737     def test_Clone(self):
1738         """Test construction environment copying
1739
1740         Update the copy independently afterwards and check that
1741         the original remains intact (that is, no dangling
1742         references point to objects in the copied environment).
1743         Clone the original with some construction variable
1744         updates and check that the original remains intact
1745         and the copy has the updated values.
1746         """
1747         env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
1748         env2 = env1.Clone()
1749         env1copy = env1.Clone()
1750         assert env1copy == env1copy
1751         assert env2 == env2
1752         env2.Replace(YYY = 'yyy')
1753         assert env2 == env2
1754         assert env1 != env2
1755         assert env1 == env1copy
1756
1757         env3 = env1.Clone(XXX = 'x3', ZZZ = 'z3')
1758         assert env3 == env3
1759         assert env3.Dictionary('XXX') == 'x3'
1760         assert env3.Dictionary('YYY') == 'y'
1761         assert env3.Dictionary('ZZZ') == 'z3'
1762         assert env1 == env1copy
1763
1764         # Ensure that lists and dictionaries are
1765         # deep copied, but not instances.
1766         class TestA:
1767             pass
1768         env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ],
1769                            ZZZ = { 1:2, 3:4 })
1770         env2=env1.Clone()
1771         env2.Dictionary('YYY').append(4)
1772         env2.Dictionary('ZZZ')[5] = 6
1773         assert env1.Dictionary('XXX') is env2.Dictionary('XXX')
1774         assert 4 in env2.Dictionary('YYY')
1775         assert not 4 in env1.Dictionary('YYY')
1776         assert 5 in env2.Dictionary('ZZZ')
1777         assert 5 not in env1.Dictionary('ZZZ')
1778
1779         #
1780         env1 = self.TestEnvironment(BUILDERS = {'b1' : Builder()})
1781         assert hasattr(env1, 'b1'), "env1.b1 was not set"
1782         assert env1.b1.object == env1, "b1.object doesn't point to env1"
1783         env2 = env1.Clone(BUILDERS = {'b2' : Builder()})
1784         assert env2 is env2
1785         assert env2 == env2
1786         assert hasattr(env1, 'b1'), "b1 was mistakenly cleared from env1"
1787         assert env1.b1.object == env1, "b1.object was changed"
1788         assert not hasattr(env2, 'b1'), "b1 was not cleared from env2"
1789         assert hasattr(env2, 'b2'), "env2.b2 was not set"
1790         assert env2.b2.object == env2, "b2.object doesn't point to env2"
1791
1792         # Ensure that specifying new tools in a copied environment
1793         # works.
1794         def foo(env): env['FOO'] = 1
1795         def bar(env): env['BAR'] = 2
1796         def baz(env): env['BAZ'] = 3
1797         env1 = self.TestEnvironment(tools=[foo])
1798         env2 = env1.Clone()
1799         env3 = env1.Clone(tools=[bar, baz])
1800
1801         assert env1.get('FOO') is 1
1802         assert env1.get('BAR') is None
1803         assert env1.get('BAZ') is None
1804         assert env2.get('FOO') is 1
1805         assert env2.get('BAR') is None
1806         assert env2.get('BAZ') is None
1807         assert env3.get('FOO') is 1
1808         assert env3.get('BAR') is 2
1809         assert env3.get('BAZ') is 3
1810
1811         # Ensure that recursive variable substitution when copying
1812         # environments works properly.
1813         env1 = self.TestEnvironment(CCFLAGS = '-DFOO', XYZ = '-DXYZ')
1814         env2 = env1.Clone(CCFLAGS = '$CCFLAGS -DBAR',
1815                          XYZ = ['-DABC', 'x $XYZ y', '-DDEF'])
1816         x = env2.get('CCFLAGS')
1817         assert x == '-DFOO -DBAR', x
1818         x = env2.get('XYZ')
1819         assert x == ['-DABC', 'x -DXYZ y', '-DDEF'], x
1820
1821         # Ensure that special properties of a class don't get
1822         # lost on copying.
1823         env1 = self.TestEnvironment(FLAGS = CLVar('flag1 flag2'))
1824         x = env1.get('FLAGS')
1825         assert x == ['flag1', 'flag2'], x
1826         env2 = env1.Clone()
1827         env2.Append(FLAGS = 'flag3 flag4')
1828         x = env2.get('FLAGS')
1829         assert x == ['flag1', 'flag2', 'flag3', 'flag4'], x
1830
1831         # Test that the environment stores the toolpath and
1832         # re-uses it for copies.
1833         test = TestCmd.TestCmd(workdir = '')
1834
1835         test.write('xxx.py', """\
1836 def exists(env):
1837     1
1838 def generate(env):
1839     env['XXX'] = 'one'
1840 """)
1841
1842         test.write('yyy.py', """\
1843 def exists(env):
1844     1
1845 def generate(env):
1846     env['YYY'] = 'two'
1847 """)
1848
1849         env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
1850         assert env['XXX'] == 'one', env['XXX']
1851         env = env.Clone(tools=['yyy'])
1852         assert env['YYY'] == 'two', env['YYY']
1853
1854
1855         # Test that
1856         real_value = [4]
1857
1858         def my_tool(env, rv=real_value):
1859             assert env['KEY_THAT_I_WANT'] == rv[0]
1860             env['KEY_THAT_I_WANT'] = rv[0] + 1
1861
1862         env = self.TestEnvironment()
1863
1864         real_value[0] = 5
1865         env = env.Clone(KEY_THAT_I_WANT=5, tools=[my_tool])
1866         assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
1867
1868         real_value[0] = 6
1869         env = env.Clone(KEY_THAT_I_WANT=6, tools=[my_tool])
1870         assert env['KEY_THAT_I_WANT'] == real_value[0], env['KEY_THAT_I_WANT']
1871
1872
1873     def test_Copy(self):
1874         """Test copying using the old env.Copy() method"""
1875         env1 = self.TestEnvironment(XXX = 'x', YYY = 'y')
1876         env2 = env1.Copy()
1877         env1copy = env1.Copy()
1878         assert env1copy == env1copy
1879         assert env2 == env2
1880         env2.Replace(YYY = 'yyy')
1881         assert env2 == env2
1882         assert env1 != env2
1883         assert env1 == env1copy
1884
1885     def test_Detect(self):
1886         """Test Detect()ing tools"""
1887         test = TestCmd.TestCmd(workdir = '')
1888         test.subdir('sub1', 'sub2')
1889         sub1 = test.workpath('sub1')
1890         sub2 = test.workpath('sub2')
1891
1892         if sys.platform == 'win32':
1893             test.write(['sub1', 'xxx'], "sub1/xxx\n")
1894             test.write(['sub2', 'xxx'], "sub2/xxx\n")
1895
1896             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1897
1898             x = env.Detect('xxx.exe')
1899             assert x is None, x
1900
1901             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
1902
1903             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1904
1905             x = env.Detect('xxx.exe')
1906             assert x == 'xxx.exe', x
1907
1908             test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
1909
1910             x = env.Detect('xxx.exe')
1911             assert x == 'xxx.exe', x
1912
1913         else:
1914             test.write(['sub1', 'xxx.exe'], "sub1/xxx.exe\n")
1915             test.write(['sub2', 'xxx.exe'], "sub2/xxx.exe\n")
1916
1917             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1918
1919             x = env.Detect('xxx.exe')
1920             assert x is None, x
1921
1922             sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
1923             os.chmod(sub2_xxx_exe, 0755)
1924
1925             env = self.TestEnvironment(ENV = { 'PATH' : [sub1, sub2] })
1926
1927             x = env.Detect('xxx.exe')
1928             assert x == 'xxx.exe', x
1929
1930             sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
1931             os.chmod(sub1_xxx_exe, 0755)
1932
1933             x = env.Detect('xxx.exe')
1934             assert x == 'xxx.exe', x
1935
1936         env = self.TestEnvironment(ENV = { 'PATH' : [] })
1937         x = env.Detect('xxx.exe')
1938         assert x is None, x
1939
1940     def test_Dictionary(self):
1941         """Test retrieval of known construction variables
1942
1943         Fetch them from the Dictionary and check for well-known
1944         defaults that get inserted.
1945         """
1946         env = self.TestEnvironment(XXX = 'x', YYY = 'y', ZZZ = 'z')
1947         assert env.Dictionary('XXX') == 'x'
1948         assert env.Dictionary('YYY') == 'y'
1949         assert env.Dictionary('XXX', 'ZZZ') == ['x', 'z']
1950         xxx, zzz = env.Dictionary('XXX', 'ZZZ')
1951         assert xxx == 'x'
1952         assert zzz == 'z'
1953         assert 'BUILDERS' in env.Dictionary()
1954         assert 'CC' in env.Dictionary()
1955         assert 'CCFLAGS' in env.Dictionary()
1956         assert 'ENV' in env.Dictionary()
1957
1958         assert env['XXX'] == 'x'
1959         env['XXX'] = 'foo'
1960         assert env.Dictionary('XXX') == 'foo'
1961         del env['XXX']
1962         assert 'XXX' not in env.Dictionary()
1963
1964     def test_FindIxes(self):
1965         "Test FindIxes()"
1966         env = self.TestEnvironment(LIBPREFIX='lib',
1967                           LIBSUFFIX='.a',
1968                           SHLIBPREFIX='lib',
1969                           SHLIBSUFFIX='.so',
1970                           PREFIX='pre',
1971                           SUFFIX='post')
1972
1973         paths = [os.path.join('dir', 'libfoo.a'),
1974                  os.path.join('dir', 'libfoo.so')]
1975
1976         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
1977         assert paths[1] == env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
1978         assert None is env.FindIxes(paths, 'PREFIX', 'POST')
1979
1980         paths = ['libfoo.a', 'prefoopost']
1981
1982         assert paths[0] == env.FindIxes(paths, 'LIBPREFIX', 'LIBSUFFIX')
1983         assert None is env.FindIxes(paths, 'SHLIBPREFIX', 'SHLIBSUFFIX')
1984         assert paths[1] == env.FindIxes(paths, 'PREFIX', 'SUFFIX')
1985
1986     def test_ParseConfig(self):
1987         """Test the ParseConfig() method"""
1988         env = self.TestEnvironment(COMMAND='command',
1989                           ASFLAGS='assembler',
1990                           CCFLAGS=[''],
1991                           CPPDEFINES=[],
1992                           CPPFLAGS=[''],
1993                           CPPPATH='string',
1994                           FRAMEWORKPATH=[],
1995                           FRAMEWORKS=[],
1996                           LIBPATH=['list'],
1997                           LIBS='',
1998                           LINKFLAGS=[''],
1999                           RPATH=[])
2000
2001         orig_backtick = env.backtick
2002         class my_backtick:
2003             def __init__(self, save_command, output):
2004                 self.save_command = save_command
2005                 self.output = output
2006             def __call__(self, command):
2007                 self.save_command.append(command)
2008                 return self.output
2009
2010         try:
2011             save_command = []
2012             env.backtick = my_backtick(save_command, 
2013                                  "-I/usr/include/fum -I bar -X\n" + \
2014                                  "-L/usr/fax -L foo -lxxx -l yyy " + \
2015                                  "-Wa,-as -Wl,-link " + \
2016                                  "-Wl,-rpath=rpath1 " + \
2017                                  "-Wl,-R,rpath2 " + \
2018                                  "-Wl,-Rrpath3 " + \
2019                                  "-Wp,-cpp abc " + \
2020                                  "-framework Carbon " + \
2021                                  "-frameworkdir=fwd1 " + \
2022                                  "-Ffwd2 " + \
2023                                  "-F fwd3 " + \
2024                                  "-pthread " + \
2025                                  "-mno-cygwin -mwindows " + \
2026                                  "-arch i386 -isysroot /tmp +DD64 " + \
2027                                  "-DFOO -DBAR=value")
2028             env.ParseConfig("fake $COMMAND")
2029             assert save_command == ['fake command'], save_command
2030             assert env['ASFLAGS'] == ['assembler', '-as'], env['ASFLAGS']
2031             assert env['CCFLAGS'] == ['', '-X', '-Wa,-as',
2032                                       '-pthread', '-mno-cygwin',
2033                                       ('-arch', 'i386'), ('-isysroot', '/tmp'),
2034                                       '+DD64'], env['CCFLAGS']
2035             assert env['CPPDEFINES'] == ['FOO', ['BAR', 'value']], env['CPPDEFINES']
2036             assert env['CPPFLAGS'] == ['', '-Wp,-cpp'], env['CPPFLAGS']
2037             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
2038             assert env['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], env['FRAMEWORKPATH']
2039             assert env['FRAMEWORKS'] == ['Carbon'], env['FRAMEWORKS']
2040             assert env['LIBPATH'] == ['list', '/usr/fax', 'foo'], env['LIBPATH']
2041             assert env['LIBS'] == ['xxx', 'yyy', env.File('abc')], env['LIBS']
2042             assert env['LINKFLAGS'] == ['', '-Wl,-link', '-pthread',
2043                                         '-mno-cygwin', '-mwindows',
2044                                         ('-arch', 'i386'),
2045                                         ('-isysroot', '/tmp'),
2046                                         '+DD64'], env['LINKFLAGS']
2047             assert env['RPATH'] == ['rpath1', 'rpath2', 'rpath3'], env['RPATH']
2048
2049             env.backtick = my_backtick([], "-Ibar")
2050             env.ParseConfig("fake2")
2051             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar'], env['CPPPATH']
2052             env.ParseConfig("fake2", unique=0)
2053             assert env['CPPPATH'] == ['string', '/usr/include/fum', 'bar', 'bar'], env['CPPPATH']
2054         finally:
2055             env.backtick = orig_backtick
2056
2057     def test_ParseDepends(self):
2058         """Test the ParseDepends() method"""
2059         test = TestCmd.TestCmd(workdir = '')
2060
2061         test.write('single', """
2062 #file: dependency
2063
2064 f0: \
2065    d1 \
2066    d2 \
2067    d3 \
2068
2069 """)
2070
2071         test.write('multiple', """
2072 f1: foo
2073 f2 f3: bar
2074 f4: abc def
2075 #file: dependency
2076 f5: \
2077    ghi \
2078    jkl \
2079    mno \
2080 """)
2081
2082         env = self.TestEnvironment(SINGLE = test.workpath('single'))
2083
2084         tlist = []
2085         dlist = []
2086         def my_depends(target, dependency, tlist=tlist, dlist=dlist):
2087             tlist.extend(target)
2088             dlist.extend(dependency)
2089
2090         env.Depends = my_depends
2091
2092         env.ParseDepends(test.workpath('does_not_exist'))
2093
2094         exc_caught = None
2095         try:
2096             env.ParseDepends(test.workpath('does_not_exist'), must_exist=1)
2097         except IOError:
2098             exc_caught = 1
2099         assert exc_caught, "did not catch expected IOError"
2100
2101         del tlist[:]
2102         del dlist[:]
2103
2104         env.ParseDepends('$SINGLE', only_one=1)
2105         t = list(map(str, tlist))
2106         d = list(map(str, dlist))
2107         assert t == ['f0'], t
2108         assert d == ['d1', 'd2', 'd3'], d
2109
2110         del tlist[:]
2111         del dlist[:]
2112
2113         env.ParseDepends(test.workpath('multiple'))
2114         t = list(map(str, tlist))
2115         d = list(map(str, dlist))
2116         assert t == ['f1', 'f2', 'f3', 'f4', 'f5'], t
2117         assert d == ['foo', 'bar', 'abc', 'def', 'ghi', 'jkl', 'mno'], d
2118
2119         exc_caught = None
2120         try:
2121             env.ParseDepends(test.workpath('multiple'), only_one=1)
2122         except SCons.Errors.UserError:
2123             exc_caught = 1
2124         assert exc_caught, "did not catch expected UserError"
2125
2126     def test_Platform(self):
2127         """Test the Platform() method"""
2128         env = self.TestEnvironment(WIN32='win32', NONE='no-such-platform')
2129
2130         exc_caught = None
2131         try:
2132             env.Platform('does_not_exist')
2133         except SCons.Errors.UserError:
2134             exc_caught = 1
2135         assert exc_caught, "did not catch expected UserError"
2136
2137         exc_caught = None
2138         try:
2139             env.Platform('$NONE')
2140         except SCons.Errors.UserError:
2141             exc_caught = 1
2142         assert exc_caught, "did not catch expected UserError"
2143
2144         env.Platform('posix')
2145         assert env['OBJSUFFIX'] == '.o', env['OBJSUFFIX']
2146
2147         env.Platform('$WIN32')
2148         assert env['OBJSUFFIX'] == '.obj', env['OBJSUFFIX']
2149
2150     def test_Prepend(self):
2151         """Test prepending to construction variables in an Environment
2152         """
2153         import UserDict
2154         UD = UserDict.UserDict
2155         import UserList
2156         UL = UserList.UserList
2157
2158         cases = [
2159             'a1',       'A1',           'A1a1',
2160             'a2',       ['A2'],         ['A2', 'a2'],
2161             'a3',       UL(['A3']),     UL(['A3', 'a', '3']),
2162             'a4',       '',             'a4',
2163             'a5',       [],             ['a5'],
2164             'a6',       UL([]),         UL(['a', '6']),
2165             'a7',       [''],           ['', 'a7'],
2166             'a8',       UL(['']),       UL(['', 'a', '8']),
2167
2168             ['e1'],     'E1',           ['E1', 'e1'],
2169             ['e2'],     ['E2'],         ['E2', 'e2'],
2170             ['e3'],     UL(['E3']),     UL(['E3', 'e3']),
2171             ['e4'],     '',             ['e4'],
2172             ['e5'],     [],             ['e5'],
2173             ['e6'],     UL([]),         UL(['e6']),
2174             ['e7'],     [''],           ['', 'e7'],
2175             ['e8'],     UL(['']),       UL(['', 'e8']),
2176
2177             UL(['i1']), 'I1',           UL(['I', '1', 'i1']),
2178             UL(['i2']), ['I2'],         UL(['I2', 'i2']),
2179             UL(['i3']), UL(['I3']),     UL(['I3', 'i3']),
2180             UL(['i4']), '',             UL(['i4']),
2181             UL(['i5']), [],             UL(['i5']),
2182             UL(['i6']), UL([]),         UL(['i6']),
2183             UL(['i7']), [''],           UL(['', 'i7']),
2184             UL(['i8']), UL(['']),       UL(['', 'i8']),
2185
2186             {'d1':1},   'D1',           {'d1':1, 'D1':None},
2187             {'d2':1},   ['D2'],         {'d2':1, 'D2':None},
2188             {'d3':1},   UL(['D3']),     {'d3':1, 'D3':None},
2189             {'d4':1},   {'D4':1},       {'d4':1, 'D4':1},
2190             {'d5':1},   UD({'D5':1}),   UD({'d5':1, 'D5':1}),
2191
2192             UD({'u1':1}), 'U1',         UD({'u1':1, 'U1':None}),
2193             UD({'u2':1}), ['U2'],       UD({'u2':1, 'U2':None}),
2194             UD({'u3':1}), UL(['U3']),   UD({'u3':1, 'U3':None}),
2195             UD({'u4':1}), {'U4':1},     UD({'u4':1, 'U4':1}),
2196             UD({'u5':1}), UD({'U5':1}), UD({'u5':1, 'U5':1}),
2197
2198             '',         'M1',           'M1',
2199             '',         ['M2'],         ['M2'],
2200             '',         UL(['M3']),     UL(['M3']),
2201             '',         '',             '',
2202             '',         [],             [],
2203             '',         UL([]),         UL([]),
2204             '',         [''],           [''],
2205             '',         UL(['']),       UL(['']),
2206
2207             [],         'N1',           ['N1'],
2208             [],         ['N2'],         ['N2'],
2209             [],         UL(['N3']),     UL(['N3']),
2210             [],         '',             [],
2211             [],         [],             [],
2212             [],         UL([]),         UL([]),
2213             [],         [''],           [''],
2214             [],         UL(['']),       UL(['']),
2215
2216             UL([]),     'O1',           UL(['O', '1']),
2217             UL([]),     ['O2'],         UL(['O2']),
2218             UL([]),     UL(['O3']),     UL(['O3']),
2219             UL([]),     '',             UL([]),
2220             UL([]),     [],             UL([]),
2221             UL([]),     UL([]),         UL([]),
2222             UL([]),     [''],           UL(['']),
2223             UL([]),     UL(['']),       UL(['']),
2224
2225             [''],       'P1',           ['P1', ''],
2226             [''],       ['P2'],         ['P2', ''],
2227             [''],       UL(['P3']),     UL(['P3', '']),
2228             [''],       '',             [''],
2229             [''],       [],             [''],
2230             [''],       UL([]),         UL(['']),
2231             [''],       [''],           ['', ''],
2232             [''],       UL(['']),       UL(['', '']),
2233
2234             UL(['']),   'Q1',           UL(['Q', '1', '']),
2235             UL(['']),   ['Q2'],         UL(['Q2', '']),
2236             UL(['']),   UL(['Q3']),     UL(['Q3', '']),
2237             UL(['']),   '',             UL(['']),
2238             UL(['']),   [],             UL(['']),
2239             UL(['']),   UL([]),         UL(['']),
2240             UL(['']),   [''],           UL(['', '']),
2241             UL(['']),   UL(['']),       UL(['', '']),
2242         ]
2243
2244         env = Environment()
2245         failed = 0
2246         while cases:
2247             input, prepend, expect = cases[:3]
2248             env['XXX'] = copy.copy(input)
2249             try:
2250                 env.Prepend(XXX = prepend)
2251             except Exception, e:
2252                 if failed == 0: print
2253                 print "    %s Prepend %s exception: %s" % \
2254                       (repr(input), repr(prepend), e)
2255                 failed = failed + 1
2256             else:
2257                 result = env['XXX']
2258                 if result != expect:
2259                     if failed == 0: print
2260                     print "    %s Prepend %s => %s did not match %s" % \
2261                           (repr(input), repr(prepend), repr(result), repr(expect))
2262                     failed = failed + 1
2263             del cases[:3]
2264         assert failed == 0, "%d Prepend() cases failed" % failed
2265
2266         env['UL'] = UL(['foo'])
2267         env.Prepend(UL = 'bar')
2268         result = env['UL']
2269         assert isinstance(result, UL), repr(result)
2270         assert result == ['b', 'a', 'r', 'foo'], result
2271
2272         env['CLVar'] = CLVar(['foo'])
2273         env.Prepend(CLVar = 'bar')
2274         result = env['CLVar']
2275         assert isinstance(result, CLVar), repr(result)
2276         assert result == ['bar', 'foo'], result
2277
2278         env3 = self.TestEnvironment(X = {'x1' : 7})
2279         env3.Prepend(X = {'x1' : 8, 'x2' : 9}, Y = {'y1' : 10})
2280         assert env3['X'] == {'x1': 8, 'x2' : 9}, env3['X']
2281         assert env3['Y'] == {'y1': 10}, env3['Y']
2282
2283         z1 = Builder()
2284         z2 = Builder()
2285         env4 = self.TestEnvironment(BUILDERS = {'z1' : z1})
2286         env4.Prepend(BUILDERS = {'z2' : z2})
2287         assert env4['BUILDERS'] == {'z1' : z1, 'z2' : z2}, env4['BUILDERS']
2288         assert hasattr(env4, 'z1')
2289         assert hasattr(env4, 'z2')
2290
2291     def test_PrependENVPath(self):
2292         """Test prepending to an ENV path."""
2293         env1 = self.TestEnvironment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
2294                            MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
2295         # have to include the pathsep here so that the test will work on UNIX too.
2296         env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';')
2297         env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
2298         env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
2299         env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
2300         # this should do nothing since delete_existing is 0
2301         env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';', delete_existing=0)
2302         assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
2303         assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
2304
2305         test = TestCmd.TestCmd(workdir = '')
2306         test.subdir('sub1', 'sub2')
2307         p=env1['ENV']['PATH']
2308         env1.PrependENVPath('PATH','#sub1', sep = ';')
2309         env1.PrependENVPath('PATH',env1.fs.Dir('sub2'), sep = ';')
2310         assert env1['ENV']['PATH'] == 'sub2;sub1;' + p, env1['ENV']['PATH']
2311
2312     def test_PrependUnique(self):
2313         """Test prepending unique values to construction variables
2314
2315         This strips values that are already present when lists are
2316         involved."""
2317         env = self.TestEnvironment(AAA1 = 'a1',
2318                           AAA2 = 'a2',
2319                           AAA3 = 'a3',
2320                           AAA4 = 'a4',
2321                           AAA5 = 'a5',
2322                           BBB1 = ['b1'],
2323                           BBB2 = ['b2'],
2324                           BBB3 = ['b3'],
2325                           BBB4 = ['b4'],
2326                           BBB5 = ['b5'],
2327                           CCC1 = '',
2328                           CCC2 = '',
2329                           DDD1 = ['a', 'b', 'c'])
2330         env.PrependUnique(AAA1 = 'a1',
2331                           AAA2 = ['a2'],
2332                           AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
2333                           AAA4 = 'a4.new',
2334                           AAA5 = ['a5.new'],
2335                           BBB1 = 'b1',
2336                           BBB2 = ['b2'],
2337                           BBB3 = ['b3', 'b', 'c', 'b3'],
2338                           BBB4 = 'b4.new',
2339                           BBB5 = ['b5.new'],
2340                           CCC1 = 'c1',
2341                           CCC2 = ['c2'],
2342                           DDD1 = 'b')
2343         assert env['AAA1'] == 'a1a1', env['AAA1']
2344         assert env['AAA2'] == ['a2'], env['AAA2']
2345         assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3']
2346         assert env['AAA4'] == 'a4.newa4', env['AAA4']
2347         assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5']
2348         assert env['BBB1'] == ['b1'], env['BBB1']
2349         assert env['BBB2'] == ['b2'], env['BBB2']
2350         assert env['BBB3'] == ['b', 'c', 'b3'], env['BBB3']
2351         assert env['BBB4'] == ['b4.new', 'b4'], env['BBB4']
2352         assert env['BBB5'] == ['b5.new', 'b5'], env['BBB5']
2353         assert env['CCC1'] == 'c1', env['CCC1']
2354         assert env['CCC2'] == ['c2'], env['CCC2']
2355         assert env['DDD1'] == ['a', 'b', 'c'], env['DDD1']
2356
2357         env.PrependUnique(DDD1 = 'b', delete_existing=1)
2358         assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front
2359         env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
2360         assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
2361         env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
2362         assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] 
2363
2364
2365         env['CLVar'] = CLVar([])
2366         env.PrependUnique(CLVar = 'bar')
2367         result = env['CLVar']
2368         if sys.version[0] == '1' or sys.version[:3] == '2.0':
2369             # Python 2.0 and before have a quirky behavior where CLVar([])
2370             # actually matches '' and [] due to different __coerce__()
2371             # semantics in the UserList implementation.  It isn't worth a
2372             # lot of effort to get this corner case to work identically
2373             # (support for Python 1.5 support will die soon anyway),
2374             # so just treat it separately for now.
2375             assert result == 'bar', result
2376         else:
2377             assert isinstance(result, CLVar), repr(result)
2378             assert result == ['bar'], result
2379
2380         env['CLVar'] = CLVar(['abc'])
2381         env.PrependUnique(CLVar = 'bar')
2382         result = env['CLVar']
2383         assert isinstance(result, CLVar), repr(result)
2384         assert result == ['bar', 'abc'], result
2385
2386         env['CLVar'] = CLVar(['bar'])
2387         env.PrependUnique(CLVar = 'bar')
2388         result = env['CLVar']
2389         assert isinstance(result, CLVar), repr(result)
2390         assert result == ['bar'], result
2391
2392     def test_Replace(self):
2393         """Test replacing construction variables in an Environment
2394
2395         After creation of the Environment, of course.
2396         """
2397         env1 = self.TestEnvironment(AAA = 'a', BBB = 'b')
2398         env1.Replace(BBB = 'bbb', CCC = 'ccc')
2399
2400         env2 = self.TestEnvironment(AAA = 'a', BBB = 'bbb', CCC = 'ccc')
2401         assert env1 == env2, diff_env(env1, env2)
2402
2403         b1 = Builder()
2404         b2 = Builder()
2405         env3 = self.TestEnvironment(BUILDERS = {'b1' : b1})
2406         assert hasattr(env3, 'b1'), "b1 was not set"
2407         env3.Replace(BUILDERS = {'b2' : b2})
2408         assert not hasattr(env3, 'b1'), "b1 was not cleared"
2409         assert hasattr(env3, 'b2'), "b2 was not set"
2410
2411     def test_ReplaceIxes(self):
2412         "Test ReplaceIxes()"
2413         env = self.TestEnvironment(LIBPREFIX='lib',
2414                           LIBSUFFIX='.a',
2415                           SHLIBPREFIX='lib',
2416                           SHLIBSUFFIX='.so',
2417                           PREFIX='pre',
2418                           SUFFIX='post')
2419
2420         assert 'libfoo.a' == env.ReplaceIxes('libfoo.so',
2421                                              'SHLIBPREFIX', 'SHLIBSUFFIX',
2422                                              'LIBPREFIX', 'LIBSUFFIX')
2423
2424         assert os.path.join('dir', 'libfoo.a') == env.ReplaceIxes(os.path.join('dir', 'libfoo.so'),
2425                                                                    'SHLIBPREFIX', 'SHLIBSUFFIX',
2426                                                                    'LIBPREFIX', 'LIBSUFFIX')
2427
2428         assert 'libfoo.a' == env.ReplaceIxes('prefoopost',
2429                                              'PREFIX', 'SUFFIX',
2430                                              'LIBPREFIX', 'LIBSUFFIX')
2431
2432     def test_SetDefault(self):
2433         """Test the SetDefault method"""
2434         env = self.TestEnvironment(tools = [])
2435         env.SetDefault(V1 = 1)
2436         env.SetDefault(V1 = 2)
2437         assert env['V1'] == 1
2438         env['V2'] = 2
2439         env.SetDefault(V2 = 1)
2440         assert env['V2'] == 2
2441
2442     def test_Tool(self):
2443         """Test the Tool() method"""
2444         env = self.TestEnvironment(LINK='link', NONE='no-such-tool')
2445
2446         exc_caught = None
2447         try:
2448             env.Tool('does_not_exist')
2449         except SCons.Errors.EnvironmentError:
2450             exc_caught = 1
2451         assert exc_caught, "did not catch expected EnvironmentError"
2452
2453         exc_caught = None
2454         try:
2455             env.Tool('$NONE')
2456         except SCons.Errors.EnvironmentError:
2457             exc_caught = 1
2458         assert exc_caught, "did not catch expected EnvironmentError"
2459
2460         # Use a non-existent toolpath directory just to make sure we
2461         # can call Tool() with the keyword argument.
2462         env.Tool('cc', toolpath=['/no/such/directory'])
2463         assert env['CC'] == 'cc', env['CC']
2464
2465         env.Tool('$LINK')
2466         assert env['LINK'] == '$SMARTLINK', env['LINK']
2467
2468         # Test that the environment stores the toolpath and
2469         # re-uses it for later calls.
2470         test = TestCmd.TestCmd(workdir = '')
2471
2472         test.write('xxx.py', """\
2473 def exists(env):
2474     1
2475 def generate(env):
2476     env['XXX'] = 'one'
2477 """)
2478
2479         test.write('yyy.py', """\
2480 def exists(env):
2481     1
2482 def generate(env):
2483     env['YYY'] = 'two'
2484 """)
2485
2486         env = self.TestEnvironment(tools=['xxx'], toolpath=[test.workpath('')])
2487         assert env['XXX'] == 'one', env['XXX']
2488         env.Tool('yyy')
2489         assert env['YYY'] == 'two', env['YYY']
2490
2491     def test_WhereIs(self):
2492         """Test the WhereIs() method"""
2493         test = TestCmd.TestCmd(workdir = '')
2494
2495         sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
2496         sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
2497         sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
2498         sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
2499
2500         test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
2501
2502         if sys.platform != 'win32':
2503             test.write(sub1_xxx_exe, "\n")
2504
2505         os.mkdir(sub2_xxx_exe)
2506
2507         test.write(sub3_xxx_exe, "\n")
2508         os.chmod(sub3_xxx_exe, 0777)
2509
2510         test.write(sub4_xxx_exe, "\n")
2511         os.chmod(sub4_xxx_exe, 0777)
2512
2513         env_path = os.environ['PATH']
2514
2515         pathdirs_1234 = [ test.workpath('sub1'),
2516                           test.workpath('sub2'),
2517                           test.workpath('sub3'),
2518                           test.workpath('sub4'),
2519                         ] + env_path.split(os.pathsep)
2520
2521         pathdirs_1243 = [ test.workpath('sub1'),
2522                           test.workpath('sub2'),
2523                           test.workpath('sub4'),
2524                           test.workpath('sub3'),
2525                         ] + env_path.split(os.pathsep)
2526
2527         path = os.pathsep.join(pathdirs_1234)
2528         env = self.TestEnvironment(ENV = {'PATH' : path})
2529         wi = env.WhereIs('xxx.exe')
2530         assert wi == test.workpath(sub3_xxx_exe), wi
2531         wi = env.WhereIs('xxx.exe', pathdirs_1243)
2532         assert wi == test.workpath(sub4_xxx_exe), wi
2533         wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1243))
2534         assert wi == test.workpath(sub4_xxx_exe), wi
2535
2536         wi = env.WhereIs('xxx.exe', reject = sub3_xxx_exe)
2537         assert wi == test.workpath(sub4_xxx_exe), wi
2538         wi = env.WhereIs('xxx.exe', pathdirs_1243, reject = sub3_xxx_exe)
2539         assert wi == test.workpath(sub4_xxx_exe), wi
2540
2541         path = os.pathsep.join(pathdirs_1243)
2542         env = self.TestEnvironment(ENV = {'PATH' : path})
2543         wi = env.WhereIs('xxx.exe')
2544         assert wi == test.workpath(sub4_xxx_exe), wi
2545         wi = env.WhereIs('xxx.exe', pathdirs_1234)
2546         assert wi == test.workpath(sub3_xxx_exe), wi
2547         wi = env.WhereIs('xxx.exe', os.pathsep.join(pathdirs_1234))
2548         assert wi == test.workpath(sub3_xxx_exe), wi
2549
2550         if sys.platform == 'win32':
2551             wi = env.WhereIs('xxx', pathext = '')
2552             assert wi is None, wi
2553
2554             wi = env.WhereIs('xxx', pathext = '.exe')
2555             assert wi == test.workpath(sub4_xxx_exe), wi
2556
2557             wi = env.WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
2558             assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
2559
2560             # Test that we return a normalized path even when
2561             # the path contains forward slashes.
2562             forward_slash = test.workpath('') + '/sub3'
2563             wi = env.WhereIs('xxx', path = forward_slash, pathext = '.EXE')
2564             assert wi.lower() == test.workpath(sub3_xxx_exe).lower(), wi
2565
2566
2567
2568     def test_Action(self):
2569         """Test the Action() method"""
2570         import SCons.Action
2571
2572         env = self.TestEnvironment(FOO = 'xyzzy')
2573
2574         a = env.Action('foo')
2575         assert a, a
2576         assert a.__class__ is SCons.Action.CommandAction, a.__class__
2577
2578         a = env.Action('$FOO')
2579         assert a, a
2580         assert a.__class__ is SCons.Action.CommandAction, a.__class__
2581
2582         a = env.Action('$$FOO')
2583         assert a, a
2584         assert a.__class__ is SCons.Action.LazyAction, a.__class__
2585
2586         a = env.Action(['$FOO', 'foo'])
2587         assert a, a
2588         assert a.__class__ is SCons.Action.ListAction, a.__class__
2589
2590         def func(arg):
2591             pass
2592         a = env.Action(func)
2593         assert a, a
2594         assert a.__class__ is SCons.Action.FunctionAction, a.__class__
2595
2596     def test_AddPostAction(self):
2597         """Test the AddPostAction() method"""
2598         env = self.TestEnvironment(FOO='fff', BAR='bbb')
2599
2600         n = env.AddPostAction('$FOO', lambda x: x)
2601         assert str(n[0]) == 'fff', n[0]
2602
2603         n = env.AddPostAction(['ggg', '$BAR'], lambda x: x)
2604         assert str(n[0]) == 'ggg', n[0]
2605         assert str(n[1]) == 'bbb', n[1]
2606
2607     def test_AddPreAction(self):
2608         """Test the AddPreAction() method"""
2609         env = self.TestEnvironment(FOO='fff', BAR='bbb')
2610
2611         n = env.AddPreAction('$FOO', lambda x: x)
2612         assert str(n[0]) == 'fff', n[0]
2613
2614         n = env.AddPreAction(['ggg', '$BAR'], lambda x: x)
2615         assert str(n[0]) == 'ggg', n[0]
2616         assert str(n[1]) == 'bbb', n[1]
2617
2618     def test_Alias(self):
2619         """Test the Alias() method"""
2620         env = self.TestEnvironment(FOO='kkk', BAR='lll', EA='export_alias')
2621
2622         tgt = env.Alias('new_alias')[0]
2623         assert str(tgt) == 'new_alias', tgt
2624         assert tgt.sources == [], tgt.sources
2625         assert not hasattr(tgt, 'builder'), tgt.builder
2626
2627         tgt = env.Alias('None_alias', None)[0]
2628         assert str(tgt) == 'None_alias', tgt
2629         assert tgt.sources == [], tgt.sources
2630
2631         tgt = env.Alias('empty_list', [])[0]
2632         assert str(tgt) == 'empty_list', tgt
2633         assert tgt.sources == [], tgt.sources
2634
2635         tgt = env.Alias('export_alias', [ 'asrc1', '$FOO' ])[0]
2636         assert str(tgt) == 'export_alias', tgt
2637         assert len(tgt.sources) == 2, list(map(str, tgt.sources))
2638         assert str(tgt.sources[0]) == 'asrc1', list(map(str, tgt.sources))
2639         assert str(tgt.sources[1]) == 'kkk', list(map(str, tgt.sources))
2640
2641         n = env.Alias(tgt, source = ['$BAR', 'asrc4'])[0]
2642         assert n is tgt, n
2643         assert len(tgt.sources) == 4, list(map(str, tgt.sources))
2644         assert str(tgt.sources[2]) == 'lll', list(map(str, tgt.sources))
2645         assert str(tgt.sources[3]) == 'asrc4', list(map(str, tgt.sources))
2646
2647         n = env.Alias('$EA', 'asrc5')[0]
2648         assert n is tgt, n
2649         assert len(tgt.sources) == 5, list(map(str, tgt.sources))
2650         assert str(tgt.sources[4]) == 'asrc5', list(map(str, tgt.sources))
2651
2652         t1, t2 = env.Alias(['t1', 't2'], ['asrc6', 'asrc7'])
2653         assert str(t1) == 't1', t1
2654         assert str(t2) == 't2', t2
2655         assert len(t1.sources) == 2, list(map(str, t1.sources))
2656         assert str(t1.sources[0]) == 'asrc6', list(map(str, t1.sources))
2657         assert str(t1.sources[1]) == 'asrc7', list(map(str, t1.sources))
2658         assert len(t2.sources) == 2, list(map(str, t2.sources))
2659         assert str(t2.sources[0]) == 'asrc6', list(map(str, t2.sources))
2660         assert str(t2.sources[1]) == 'asrc7', list(map(str, t2.sources))
2661
2662         tgt = env.Alias('add', 's1')
2663         tgt = env.Alias('add', 's2')[0]
2664         s = list(map(str, tgt.sources))
2665         assert s == ['s1', 's2'], s
2666         tgt = env.Alias(tgt, 's3')[0]
2667         s = list(map(str, tgt.sources))
2668         assert s == ['s1', 's2', 's3'], s
2669
2670         tgt = env.Alias('act', None, "action1")[0]
2671         s = str(tgt.builder.action)
2672         assert s == "action1", s
2673         tgt = env.Alias('act', None, "action2")[0]
2674         s = str(tgt.builder.action)
2675         assert s == "action1\naction2", s
2676         tgt = env.Alias(tgt, None, "action3")[0]
2677         s = str(tgt.builder.action)
2678         assert s == "action1\naction2\naction3", s
2679
2680     def test_AlwaysBuild(self):
2681         """Test the AlwaysBuild() method"""
2682         env = self.TestEnvironment(FOO='fff', BAR='bbb')
2683         t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
2684                             env.fs.Dir('dir'), env.fs.File('file'))
2685         assert t[0].__class__.__name__ == 'Entry'
2686         assert t[0].path == 'a'
2687         assert t[0].always_build
2688         assert t[1].__class__.__name__ == 'Entry'
2689         assert t[1].path == 'bfff'
2690         assert t[1].always_build
2691         assert t[2].__class__.__name__ == 'Entry'
2692         assert t[2].path == 'c'
2693         assert t[2].always_build
2694         assert t[3].__class__.__name__ == 'Entry'
2695         assert t[3].path == 'd'
2696         assert t[3].always_build
2697         assert t[4].__class__.__name__ == 'Entry'
2698         assert t[4].path == 'bbb'
2699         assert t[4].always_build
2700         assert t[5].__class__.__name__ == 'Dir'
2701         assert t[5].path == 'dir'
2702         assert t[5].always_build
2703         assert t[6].__class__.__name__ == 'File'
2704         assert t[6].path == 'file'
2705         assert t[6].always_build
2706
2707     def test_VariantDir(self):
2708         """Test the VariantDir() method"""
2709         class MyFS:
2710              def Dir(self, name):
2711                  return name
2712              def VariantDir(self, variant_dir, src_dir, duplicate):
2713                  self.variant_dir = variant_dir
2714                  self.src_dir = src_dir
2715                  self.duplicate = duplicate
2716
2717         env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
2718         env.fs = MyFS()
2719
2720         env.VariantDir('build', 'src')
2721         assert env.fs.variant_dir == 'build', env.fs.variant_dir
2722         assert env.fs.src_dir == 'src', env.fs.src_dir
2723         assert env.fs.duplicate == 1, env.fs.duplicate
2724
2725         env.VariantDir('build${FOO}', '${BAR}src', 0)
2726         assert env.fs.variant_dir == 'buildfff', env.fs.variant_dir
2727         assert env.fs.src_dir == 'bbbsrc', env.fs.src_dir
2728         assert env.fs.duplicate == 0, env.fs.duplicate
2729
2730     def test_Builder(self):
2731         """Test the Builder() method"""
2732         env = self.TestEnvironment(FOO = 'xyzzy')
2733
2734         b = env.Builder(action = 'foo')
2735         assert b is not None, b
2736
2737         b = env.Builder(action = '$FOO')
2738         assert b is not None, b
2739
2740         b = env.Builder(action = ['$FOO', 'foo'])
2741         assert b is not None, b
2742
2743         def func(arg):
2744             pass
2745         b = env.Builder(action = func)
2746         assert b is not None, b
2747         b = env.Builder(generator = func)
2748         assert b is not None, b
2749
2750     def test_CacheDir(self):
2751         """Test the CacheDir() method"""
2752         env = self.TestEnvironment(CD = 'CacheDir')
2753
2754         env.CacheDir('foo')
2755         assert env._CacheDir_path == 'foo', env._CacheDir_path
2756
2757         env.CacheDir('$CD')
2758         assert env._CacheDir_path == 'CacheDir', env._CacheDir_path
2759
2760     def test_Clean(self):
2761         """Test the Clean() method"""
2762         env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
2763
2764         CT = SCons.Environment.CleanTargets
2765
2766         foo = env.arg2nodes('foo')[0]
2767         fff = env.arg2nodes('fff')[0]
2768
2769         t = env.Clean('foo', 'aaa')
2770         l = list(map(str, CT[foo]))
2771         assert l == ['aaa'], l
2772
2773         t = env.Clean(foo, ['$BAR', 'ccc'])
2774         l = list(map(str, CT[foo]))
2775         assert l == ['aaa', 'bbb', 'ccc'], l
2776
2777         eee = env.arg2nodes('eee')[0]
2778
2779         t = env.Clean('$FOO', 'ddd')
2780         l = list(map(str, CT[fff]))
2781         assert l == ['ddd'], l
2782         t = env.Clean(fff, [eee, 'fff'])
2783         l = list(map(str, CT[fff]))
2784         assert l == ['ddd', 'eee', 'fff'], l
2785
2786     def test_Command(self):
2787         """Test the Command() method."""
2788         env = Environment()
2789         t = env.Command(target='foo.out', source=['foo1.in', 'foo2.in'],
2790                         action='buildfoo $target $source')[0]
2791         assert t.builder is not None
2792         assert t.builder.action.__class__.__name__ == 'CommandAction'
2793         assert t.builder.action.cmd_list == 'buildfoo $target $source'
2794         assert 'foo1.in' in [x.path for x in t.sources]
2795         assert 'foo2.in' in [x.path for x in t.sources]
2796
2797         sub = env.fs.Dir('sub')
2798         t = env.Command(target='bar.out', source='sub',
2799                         action='buildbar $target $source')[0]
2800         assert 'sub' in [x.path for x in t.sources]
2801
2802         def testFunc(env, target, source):
2803             assert str(target[0]) == 'foo.out'
2804             assert 'foo1.in' in list(map(str, source)) and 'foo2.in' in list(map(str, source)), list(map(str, source))
2805             return 0
2806         t = env.Command(target='foo.out', source=['foo1.in','foo2.in'],
2807                         action=testFunc)[0]
2808         assert t.builder is not None
2809         assert t.builder.action.__class__.__name__ == 'FunctionAction'
2810         t.build()
2811         assert 'foo1.in' in [x.path for x in t.sources]
2812         assert 'foo2.in' in [x.path for x in t.sources]
2813
2814         x = []
2815         def test2(baz, x=x):
2816             x.append(baz)
2817         env = self.TestEnvironment(TEST2 = test2)
2818         t = env.Command(target='baz.out', source='baz.in',
2819                         action='${TEST2(XYZ)}',
2820                         XYZ='magic word')[0]
2821         assert t.builder is not None
2822         t.build()
2823         assert x[0] == 'magic word', x
2824
2825         t = env.Command(target='${X}.out', source='${X}.in',
2826                         action = 'foo',
2827                         X = 'xxx')[0]
2828         assert str(t) == 'xxx.out', str(t)
2829         assert 'xxx.in' in [x.path for x in t.sources]
2830
2831         env = self.TestEnvironment(source_scanner = 'should_not_find_this')
2832         t = env.Command(target='file.out', source='file.in',
2833                         action = 'foo',
2834                         source_scanner = 'fake')[0]
2835         assert t.builder.source_scanner == 'fake', t.builder.source_scanner
2836
2837     def test_Configure(self):
2838         """Test the Configure() method"""
2839         # Configure() will write to a local temporary file.
2840         test = TestCmd.TestCmd(workdir = '')
2841         save = os.getcwd()
2842
2843         try:
2844             os.chdir(test.workpath())
2845
2846             env = self.TestEnvironment(FOO = 'xyzzy')
2847
2848             def func(arg):
2849                 pass
2850
2851             c = env.Configure()
2852             assert c is not None, c
2853             c.Finish()
2854
2855             c = env.Configure(custom_tests = {'foo' : func, '$FOO' : func})
2856             assert c is not None, c
2857             assert hasattr(c, 'foo')
2858             assert hasattr(c, 'xyzzy')
2859             c.Finish()
2860         finally:
2861             os.chdir(save)
2862
2863     def test_Depends(self):
2864         """Test the explicit Depends method."""
2865         env = self.TestEnvironment(FOO = 'xxx', BAR='yyy')
2866         env.Dir('dir1')
2867         env.Dir('dir2')
2868         env.File('xxx.py')
2869         env.File('yyy.py')
2870         t = env.Depends(target='EnvironmentTest.py',
2871                         dependency='Environment.py')[0]
2872         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
2873         assert t.path == 'EnvironmentTest.py'
2874         assert len(t.depends) == 1
2875         d = t.depends[0]
2876         assert d.__class__.__name__ == 'Entry', d.__class__.__name__
2877         assert d.path == 'Environment.py'
2878
2879         t = env.Depends(target='${FOO}.py', dependency='${BAR}.py')[0]
2880         assert t.__class__.__name__ == 'File', t.__class__.__name__
2881         assert t.path == 'xxx.py'
2882         assert len(t.depends) == 1
2883         d = t.depends[0]
2884         assert d.__class__.__name__ == 'File', d.__class__.__name__
2885         assert d.path == 'yyy.py'
2886
2887         t = env.Depends(target='dir1', dependency='dir2')[0]
2888         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
2889         assert t.path == 'dir1'
2890         assert len(t.depends) == 1
2891         d = t.depends[0]
2892         assert d.__class__.__name__ == 'Dir', d.__class__.__name__
2893         assert d.path == 'dir2'
2894
2895     def test_Dir(self):
2896         """Test the Dir() method"""
2897         class MyFS:
2898             def Dir(self, name):
2899                 return 'Dir(%s)' % name
2900
2901         env = self.TestEnvironment(FOO = 'foodir', BAR = 'bardir')
2902         env.fs = MyFS()
2903
2904         d = env.Dir('d')
2905         assert d == 'Dir(d)', d
2906
2907         d = env.Dir('$FOO')
2908         assert d == 'Dir(foodir)', d
2909
2910         d = env.Dir('${BAR}_$BAR')
2911         assert d == 'Dir(bardir_bardir)', d
2912
2913         d = env.Dir(['dir1'])
2914         assert d == ['Dir(dir1)'], d
2915
2916         d = env.Dir(['dir1', 'dir2'])
2917         assert d == ['Dir(dir1)', 'Dir(dir2)'], d
2918
2919     def test_NoClean(self):
2920         """Test the NoClean() method"""
2921         env = self.TestEnvironment(FOO='ggg', BAR='hhh')
2922         env.Dir('p_hhhb')
2923         env.File('p_d')
2924         t = env.NoClean('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
2925
2926         assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
2927         assert t[0].path == 'p_a'
2928         assert t[0].noclean
2929         assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
2930         assert t[1].path == 'p_hhhb'
2931         assert t[1].noclean
2932         assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
2933         assert t[2].path == 'p_c'
2934         assert t[2].noclean
2935         assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
2936         assert t[3].path == 'p_d'
2937         assert t[3].noclean
2938         assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
2939         assert t[4].path == 'p_ggg'
2940         assert t[4].noclean
2941
2942     def test_Dump(self):
2943         """Test the Dump() method"""
2944
2945         env = self.TestEnvironment(FOO = 'foo')
2946         assert env.Dump('FOO') == "'foo'", env.Dump('FOO')
2947         assert len(env.Dump()) > 200, env.Dump()    # no args version
2948
2949     def test_Environment(self):
2950         """Test the Environment() method"""
2951         env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy')
2952
2953         e2 = env.Environment(X = '$FOO', Y = '$BAR')
2954         assert e2['X'] == 'xxx', e2['X']
2955         assert e2['Y'] == 'yyy', e2['Y']
2956
2957     def test_Execute(self):
2958         """Test the Execute() method"""
2959
2960         class MyAction:
2961             def __init__(self, *args, **kw):
2962                 self.args = args
2963             def __call__(self, target, source, env):
2964                 return "%s executed" % self.args
2965
2966         env = Environment()
2967         env.Action = MyAction
2968
2969         result = env.Execute("foo")
2970         assert result == "foo executed", result
2971
2972     def test_Entry(self):
2973         """Test the Entry() method"""
2974         class MyFS:
2975             def Entry(self, name):
2976                 return 'Entry(%s)' % name
2977
2978         env = self.TestEnvironment(FOO = 'fooentry', BAR = 'barentry')
2979         env.fs = MyFS()
2980
2981         e = env.Entry('e')
2982         assert e == 'Entry(e)', e
2983
2984         e = env.Entry('$FOO')
2985         assert e == 'Entry(fooentry)', e
2986
2987         e = env.Entry('${BAR}_$BAR')
2988         assert e == 'Entry(barentry_barentry)', e
2989
2990         e = env.Entry(['entry1'])
2991         assert e == ['Entry(entry1)'], e
2992
2993         e = env.Entry(['entry1', 'entry2'])
2994         assert e == ['Entry(entry1)', 'Entry(entry2)'], e
2995
2996     def test_File(self):
2997         """Test the File() method"""
2998         class MyFS:
2999             def File(self, name):
3000                 return 'File(%s)' % name
3001
3002         env = self.TestEnvironment(FOO = 'foofile', BAR = 'barfile')
3003         env.fs = MyFS()
3004
3005         f = env.File('f')
3006         assert f == 'File(f)', f
3007
3008         f = env.File('$FOO')
3009         assert f == 'File(foofile)', f
3010
3011         f = env.File('${BAR}_$BAR')
3012         assert f == 'File(barfile_barfile)', f
3013
3014         f = env.File(['file1'])
3015         assert f == ['File(file1)'], f
3016
3017         f = env.File(['file1', 'file2'])
3018         assert f == ['File(file1)', 'File(file2)'], f
3019
3020     def test_FindFile(self):
3021         """Test the FindFile() method"""
3022         env = self.TestEnvironment(FOO = 'fff', BAR = 'bbb')
3023
3024         r = env.FindFile('foo', ['no_such_directory'])
3025         assert r is None, r
3026
3027         # XXX
3028
3029     def test_Flatten(self):
3030         """Test the Flatten() method"""
3031         env = Environment()
3032         l = env.Flatten([1])
3033         assert l == [1]
3034         l = env.Flatten([1, [2, [3, [4]]]])
3035         assert l == [1, 2, 3, 4], l
3036
3037     def test_GetBuildPath(self):
3038         """Test the GetBuildPath() method."""
3039         env = self.TestEnvironment(MAGIC = 'xyzzy')
3040
3041         p = env.GetBuildPath('foo')
3042         assert p == 'foo', p
3043
3044         p = env.GetBuildPath('$MAGIC')
3045         assert p == 'xyzzy', p
3046
3047     def test_Ignore(self):
3048         """Test the explicit Ignore method."""
3049         env = self.TestEnvironment(FOO='yyy', BAR='zzz')
3050         env.Dir('dir1')
3051         env.Dir('dir2')
3052         env.File('yyyzzz')
3053         env.File('zzzyyy')
3054
3055         t = env.Ignore(target='targ.py', dependency='dep.py')[0]
3056         assert t.__class__.__name__ == 'Entry', t.__class__.__name__
3057         assert t.path == 'targ.py'
3058         assert len(t.ignore) == 1
3059         i = t.ignore[0]
3060         assert i.__class__.__name__ == 'Entry', i.__class__.__name__
3061         assert i.path == 'dep.py'
3062
3063         t = env.Ignore(target='$FOO$BAR', dependency='$BAR$FOO')[0]
3064         assert t.__class__.__name__ == 'File', t.__class__.__name__
3065         assert t.path == 'yyyzzz'
3066         assert len(t.ignore) == 1
3067         i = t.ignore[0]
3068         assert i.__class__.__name__ == 'File', i.__class__.__name__
3069         assert i.path == 'zzzyyy'
3070
3071         t = env.Ignore(target='dir1', dependency='dir2')[0]
3072         assert t.__class__.__name__ == 'Dir', t.__class__.__name__
3073         assert t.path == 'dir1'
3074         assert len(t.ignore) == 1
3075         i = t.ignore[0]
3076         assert i.__class__.__name__ == 'Dir', i.__class__.__name__
3077         assert i.path == 'dir2'
3078
3079     def test_Literal(self):
3080         """Test the Literal() method"""
3081         env = self.TestEnvironment(FOO='fff', BAR='bbb')
3082         list = env.subst_list([env.Literal('$FOO'), '$BAR'])[0]
3083         assert list == ['$FOO', 'bbb'], list
3084         list = env.subst_list(['$FOO', env.Literal('$BAR')])[0]
3085         assert list == ['fff', '$BAR'], list
3086
3087     def test_Local(self):
3088         """Test the Local() method."""
3089         env = self.TestEnvironment(FOO='lll')
3090
3091         l = env.Local(env.fs.File('fff'))
3092         assert str(l[0]) == 'fff', l[0]
3093
3094         l = env.Local('ggg', '$FOO')
3095         assert str(l[0]) == 'ggg', l[0]
3096         assert str(l[1]) == 'lll', l[1]
3097
3098     def test_Precious(self):
3099         """Test the Precious() method"""
3100         env = self.TestEnvironment(FOO='ggg', BAR='hhh')
3101         env.Dir('p_hhhb')
3102         env.File('p_d')
3103         t = env.Precious('p_a', 'p_${BAR}b', ['p_c', 'p_d'], 'p_$FOO')
3104
3105         assert t[0].__class__.__name__ == 'Entry', t[0].__class__.__name__
3106         assert t[0].path == 'p_a'
3107         assert t[0].precious
3108         assert t[1].__class__.__name__ == 'Dir', t[1].__class__.__name__
3109         assert t[1].path == 'p_hhhb'
3110         assert t[1].precious
3111         assert t[2].__class__.__name__ == 'Entry', t[2].__class__.__name__
3112         assert t[2].path == 'p_c'
3113         assert t[2].precious
3114         assert t[3].__class__.__name__ == 'File', t[3].__class__.__name__
3115         assert t[3].path == 'p_d'
3116         assert t[3].precious
3117         assert t[4].__class__.__name__ == 'Entry', t[4].__class__.__name__
3118         assert t[4].path == 'p_ggg'
3119         assert t[4].precious
3120
3121     def test_Repository(self):
3122         """Test the Repository() method."""
3123         class MyFS:
3124             def __init__(self):
3125                 self.list = []
3126             def Repository(self, *dirs):
3127                 self.list.extend(list(dirs))
3128             def Dir(self, name):
3129                 return name
3130         env = self.TestEnvironment(FOO='rrr', BAR='sss')
3131         env.fs = MyFS()
3132         env.Repository('/tmp/foo')
3133         env.Repository('/tmp/$FOO', '/tmp/$BAR/foo')
3134         expect = ['/tmp/foo', '/tmp/rrr', '/tmp/sss/foo']
3135         assert env.fs.list == expect, env.fs.list
3136
3137     def test_Scanner(self):
3138         """Test the Scanner() method"""
3139         def scan(node, env, target, arg):
3140             pass
3141
3142         env = self.TestEnvironment(FOO = scan)
3143
3144         s = env.Scanner('foo')
3145         assert s is not None, s
3146
3147         s = env.Scanner(function = 'foo')
3148         assert s is not None, s
3149
3150         if 0:
3151             s = env.Scanner('$FOO')
3152             assert s is not None, s
3153
3154             s = env.Scanner(function = '$FOO')
3155             assert s is not None, s
3156
3157     def test_SConsignFile(self):
3158         """Test the SConsignFile() method"""
3159         import SCons.SConsign
3160
3161         class MyFS:
3162             SConstruct_dir = os.sep + 'dir'
3163
3164         env = self.TestEnvironment(FOO = 'SConsign',
3165                           BAR = os.path.join(os.sep, 'File'))
3166         env.fs = MyFS()
3167         env.Execute = lambda action: None
3168
3169         try:
3170             fnames = []
3171             dbms = []
3172             def capture(name, dbm_module, fnames=fnames, dbms=dbms):
3173                 fnames.append(name)
3174                 dbms.append(dbm_module)
3175
3176             save_SConsign_File = SCons.SConsign.File
3177             SCons.SConsign.File = capture
3178
3179             env.SConsignFile('foo')
3180             assert fnames[-1] == os.path.join(os.sep, 'dir', 'foo'), fnames
3181             assert dbms[-1] is None, dbms
3182
3183             env.SConsignFile('$FOO')
3184             assert fnames[-1] == os.path.join(os.sep, 'dir', 'SConsign'), fnames
3185             assert dbms[-1] is None, dbms
3186
3187             env.SConsignFile('/$FOO')
3188             assert fnames[-1] == os.sep + 'SConsign', fnames
3189             assert dbms[-1] is None, dbms
3190
3191             env.SConsignFile(os.sep + '$FOO')
3192             assert fnames[-1] == os.sep + 'SConsign', fnames
3193             assert dbms[-1] is None, dbms
3194
3195             env.SConsignFile('$BAR', 'x')
3196             assert fnames[-1] == os.path.join(os.sep, 'File'), fnames
3197             assert dbms[-1] == 'x', dbms
3198
3199             env.SConsignFile('__$BAR', 7)
3200             assert fnames[-1] == os.path.join(os.sep, 'dir', '__', 'File'), fnames
3201             assert dbms[-1] == 7, dbms
3202
3203             env.SConsignFile()
3204             assert fnames[-1] == os.path.join(os.sep, 'dir', '.sconsign'), fnames
3205             assert dbms[-1] is None, dbms
3206
3207             env.SConsignFile(None)
3208             assert fnames[-1] is None, fnames
3209             assert dbms[-1] is None, dbms
3210         finally:
3211             SCons.SConsign.File = save_SConsign_File
3212
3213     def test_SideEffect(self):
3214         """Test the SideEffect() method"""
3215         env = self.TestEnvironment(LIB='lll', FOO='fff', BAR='bbb')
3216         env.File('mylll.pdb')
3217         env.Dir('mymmm.pdb')
3218
3219         foo = env.Object('foo.obj', 'foo.cpp')[0]
3220         bar = env.Object('bar.obj', 'bar.cpp')[0]
3221         s = env.SideEffect('mylib.pdb', ['foo.obj', 'bar.obj'])[0]
3222         assert s.__class__.__name__ == 'Entry', s.__class__.__name__
3223         assert s.path == 'mylib.pdb'
3224         assert s.side_effect
3225         assert foo.side_effects == [s]
3226         assert bar.side_effects == [s]
3227
3228         fff = env.Object('fff.obj', 'fff.cpp')[0]
3229         bbb = env.Object('bbb.obj', 'bbb.cpp')[0]
3230         s = env.SideEffect('my${LIB}.pdb', ['${FOO}.obj', '${BAR}.obj'])[0]
3231         assert s.__class__.__name__ == 'File', s.__class__.__name__
3232         assert s.path == 'mylll.pdb'
3233         assert s.side_effect
3234         assert fff.side_effects == [s], fff.side_effects
3235         assert bbb.side_effects == [s], bbb.side_effects
3236
3237         ggg = env.Object('ggg.obj', 'ggg.cpp')[0]
3238         ccc = env.Object('ccc.obj', 'ccc.cpp')[0]
3239         s = env.SideEffect('mymmm.pdb', ['ggg.obj', 'ccc.obj'])[0]
3240         assert s.__class__.__name__ == 'Dir', s.__class__.__name__
3241         assert s.path == 'mymmm.pdb'
3242         assert s.side_effect
3243         assert ggg.side_effects == [s], ggg.side_effects
3244         assert ccc.side_effects == [s], ccc.side_effects
3245
3246     def test_SourceCode(self):
3247         """Test the SourceCode() method."""
3248         env = self.TestEnvironment(FOO='mmm', BAR='nnn')
3249         e = env.SourceCode('foo', None)[0]
3250         assert e.path == 'foo'
3251         s = e.src_builder()
3252         assert s is None, s
3253
3254         b = Builder()
3255         e = env.SourceCode(e, b)[0]
3256         assert e.path == 'foo'
3257         s = e.src_builder()
3258         assert s is b, s
3259
3260         e = env.SourceCode('$BAR$FOO', None)[0]
3261         assert e.path == 'nnnmmm'
3262         s = e.src_builder()
3263         assert s is None, s
3264
3265     def test_SourceSignatures(type):
3266         """Test the SourceSignatures() method"""
3267         import SCons.Errors
3268
3269         env = type.TestEnvironment(M = 'MD5', T = 'timestamp')
3270
3271         exc_caught = None
3272         try:
3273             env.SourceSignatures('invalid_type')
3274         except SCons.Errors.UserError:
3275             exc_caught = 1
3276         assert exc_caught, "did not catch expected UserError"
3277
3278         env.SourceSignatures('MD5')
3279         assert env.src_sig_type == 'MD5', env.src_sig_type
3280
3281         env.SourceSignatures('$M')
3282         assert env.src_sig_type == 'MD5', env.src_sig_type
3283
3284         env.SourceSignatures('timestamp')
3285         assert env.src_sig_type == 'timestamp', env.src_sig_type
3286
3287         env.SourceSignatures('$T')
3288         assert env.src_sig_type == 'timestamp', env.src_sig_type
3289
3290         try:
3291             import SCons.Util
3292             save_md5 = SCons.Util.md5
3293             SCons.Util.md5 = None
3294             try:
3295                 env.SourceSignatures('MD5')
3296             except SCons.Errors.UserError:
3297                 pass
3298             else:
3299                 self.fail('Did not catch expected UserError')
3300         finally:
3301             SCons.Util.md5 = save_md5
3302
3303     def test_Split(self):
3304         """Test the Split() method"""
3305         env = self.TestEnvironment(FOO='fff', BAR='bbb')
3306         s = env.Split("foo bar")
3307         assert s == ["foo", "bar"], s
3308         s = env.Split("$FOO bar")
3309         assert s == ["fff", "bar"], s
3310         s = env.Split(["foo", "bar"])
3311         assert s == ["foo", "bar"], s
3312         s = env.Split(["foo", "${BAR}-bbb"])
3313         assert s == ["foo", "bbb-bbb"], s
3314         s = env.Split("foo")
3315         assert s == ["foo"], s
3316         s = env.Split("$FOO$BAR")
3317         assert s == ["fffbbb"], s
3318
3319     def test_TargetSignatures(type):
3320         """Test the TargetSignatures() method"""
3321         import SCons.Errors
3322
3323         env = type.TestEnvironment(B = 'build', C = 'content')
3324
3325         exc_caught = None
3326         try:
3327             env.TargetSignatures('invalid_type')
3328         except SCons.Errors.UserError:
3329             exc_caught = 1
3330         assert exc_caught, "did not catch expected UserError"
3331         assert not hasattr(env, '_build_signature')
3332
3333         env.TargetSignatures('build')
3334         assert env.tgt_sig_type == 'build', env.tgt_sig_type
3335
3336         env.TargetSignatures('$B')
3337         assert env.tgt_sig_type == 'build', env.tgt_sig_type
3338
3339         env.TargetSignatures('content')
3340         assert env.tgt_sig_type == 'content', env.tgt_sig_type
3341
3342         env.TargetSignatures('$C')
3343         assert env.tgt_sig_type == 'content', env.tgt_sig_type
3344
3345         env.TargetSignatures('MD5')
3346         assert env.tgt_sig_type == 'MD5', env.tgt_sig_type
3347
3348         env.TargetSignatures('timestamp')
3349         assert env.tgt_sig_type == 'timestamp', env.tgt_sig_type
3350
3351         try:
3352             import SCons.Util
3353             save_md5 = SCons.Util.md5
3354             SCons.Util.md5 = None
3355             try:
3356                 env.TargetSignatures('MD5')
3357             except SCons.Errors.UserError:
3358                 pass
3359             else:
3360                 self.fail('Did not catch expected UserError')
3361             try:
3362                 env.TargetSignatures('content')
3363             except SCons.Errors.UserError:
3364                 pass
3365             else:
3366                 self.fail('Did not catch expected UserError')
3367         finally:
3368             SCons.Util.md5 = save_md5
3369
3370     def test_Value(self):
3371         """Test creating a Value() object
3372         """
3373         env = Environment()
3374         v1 = env.Value('a')
3375         assert v1.value == 'a', v1.value
3376
3377         value2 = 'a'
3378         v2 = env.Value(value2)
3379         assert v2.value == value2, v2.value
3380         assert v2.value is value2, v2.value
3381
3382         assert not v1 is v2
3383         assert v1.value == v2.value
3384
3385         v3 = env.Value('c', 'build-c')
3386         assert v3.value == 'c', v3.value
3387
3388
3389
3390     def test_Environment_global_variable(type):
3391         """Test setting Environment variable to an Environment.Base subclass"""
3392         class MyEnv(SCons.Environment.Base):
3393             def xxx(self, string):
3394                 return self.subst(string)
3395
3396         SCons.Environment.Environment = MyEnv
3397
3398         env = SCons.Environment.Environment(FOO = 'foo')
3399
3400         f = env.subst('$FOO')
3401         assert f == 'foo', f
3402
3403         f = env.xxx('$FOO')
3404         assert f == 'foo', f
3405
3406     def test_bad_keywords(self):
3407         """Test trying to use reserved keywords in an Environment"""
3408         added = []
3409
3410         env = self.TestEnvironment(TARGETS = 'targets',
3411                                    SOURCES = 'sources',
3412                                    SOURCE = 'source',
3413                                    TARGET = 'target',
3414                                    CHANGED_SOURCES = 'changed_sources',
3415                                    CHANGED_TARGETS = 'changed_targets',
3416                                    UNCHANGED_SOURCES = 'unchanged_sources',
3417                                    UNCHANGED_TARGETS = 'unchanged_targets',
3418                                    INIT = 'init')
3419         bad_msg = '%s is not reserved, but got omitted; see Environment.construction_var_name_ok'
3420         added.append('INIT')
3421         for x in self.reserved_variables:
3422             assert x not in env, env[x]
3423         for x in added:
3424             assert x in env, bad_msg % x
3425
3426         env.Append(TARGETS = 'targets',
3427                    SOURCES = 'sources',
3428                    SOURCE = 'source',
3429                    TARGET = 'target',
3430                    CHANGED_SOURCES = 'changed_sources',
3431                    CHANGED_TARGETS = 'changed_targets',
3432                    UNCHANGED_SOURCES = 'unchanged_sources',
3433                    UNCHANGED_TARGETS = 'unchanged_targets',
3434                    APPEND = 'append')
3435         added.append('APPEND')
3436         for x in self.reserved_variables:
3437             assert x not in env, env[x]
3438         for x in added:
3439             assert x in env, bad_msg % x
3440
3441         env.AppendUnique(TARGETS = 'targets',
3442                          SOURCES = 'sources',
3443                          SOURCE = 'source',
3444                          TARGET = 'target',
3445                          CHANGED_SOURCES = 'changed_sources',
3446                          CHANGED_TARGETS = 'changed_targets',
3447                          UNCHANGED_SOURCES = 'unchanged_sources',
3448                          UNCHANGED_TARGETS = 'unchanged_targets',
3449                          APPENDUNIQUE = 'appendunique')
3450         added.append('APPENDUNIQUE')
3451         for x in self.reserved_variables:
3452             assert x not in env, env[x]
3453         for x in added:
3454             assert x in env, bad_msg % x
3455
3456         env.Prepend(TARGETS = 'targets',
3457                     SOURCES = 'sources',
3458                     SOURCE = 'source',
3459                     TARGET = 'target',
3460                     CHANGED_SOURCES = 'changed_sources',
3461                     CHANGED_TARGETS = 'changed_targets',
3462                     UNCHANGED_SOURCES = 'unchanged_sources',
3463                     UNCHANGED_TARGETS = 'unchanged_targets',
3464                     PREPEND = 'prepend')
3465         added.append('PREPEND')
3466         for x in self.reserved_variables:
3467             assert x not in env, env[x]
3468         for x in added:
3469             assert x in env, bad_msg % x
3470
3471         env.Prepend(TARGETS = 'targets',
3472                     SOURCES = 'sources',
3473                     SOURCE = 'source',
3474                     TARGET = 'target',
3475                     CHANGED_SOURCES = 'changed_sources',
3476                     CHANGED_TARGETS = 'changed_targets',
3477                     UNCHANGED_SOURCES = 'unchanged_sources',
3478                     UNCHANGED_TARGETS = 'unchanged_targets',
3479                     PREPENDUNIQUE = 'prependunique')
3480         added.append('PREPENDUNIQUE')
3481         for x in self.reserved_variables:
3482             assert x not in env, env[x]
3483         for x in added:
3484             assert x in env, bad_msg % x
3485
3486         env.Replace(TARGETS = 'targets',
3487                     SOURCES = 'sources',
3488                     SOURCE = 'source',
3489                     TARGET = 'target',
3490                     CHANGED_SOURCES = 'changed_sources',
3491                     CHANGED_TARGETS = 'changed_targets',
3492                     UNCHANGED_SOURCES = 'unchanged_sources',
3493                     UNCHANGED_TARGETS = 'unchanged_targets',
3494                     REPLACE = 'replace')
3495         added.append('REPLACE')
3496         for x in self.reserved_variables:
3497             assert x not in env, env[x]
3498         for x in added:
3499             assert x in env, bad_msg % x
3500
3501         copy = env.Clone(TARGETS = 'targets',
3502                          SOURCES = 'sources',
3503                          SOURCE = 'source',
3504                          TARGET = 'target',
3505                          CHANGED_SOURCES = 'changed_sources',
3506                          CHANGED_TARGETS = 'changed_targets',
3507                          UNCHANGED_SOURCES = 'unchanged_sources',
3508                          UNCHANGED_TARGETS = 'unchanged_targets',
3509                          COPY = 'copy')
3510         for x in self.reserved_variables:
3511             assert x not in copy, env[x]
3512         for x in added + ['COPY']:
3513             assert x in copy, bad_msg % x
3514
3515         over = env.Override({'TARGETS' : 'targets',
3516                              'SOURCES' : 'sources',
3517                              'SOURCE' : 'source',
3518                              'TARGET' : 'target',
3519                              'CHANGED_SOURCES' : 'changed_sources',
3520                              'CHANGED_TARGETS' : 'changed_targets',
3521                              'UNCHANGED_SOURCES' : 'unchanged_sources',
3522                              'UNCHANGED_TARGETS' : 'unchanged_targets',
3523                              'OVERRIDE' : 'override'})
3524         for x in self.reserved_variables:
3525             assert x not in over, over[x]
3526         for x in added + ['OVERRIDE']:
3527             assert x in over, bad_msg % x
3528
3529     def test_parse_flags(self):
3530         '''Test the Base class parse_flags argument'''
3531         # all we have to show is that it gets to MergeFlags internally
3532         env = Environment(tools=[], parse_flags = '-X')
3533         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
3534
3535         env = Environment(tools=[], CCFLAGS=None, parse_flags = '-Y')
3536         assert env['CCFLAGS'] == ['-Y'], env['CCFLAGS']
3537
3538         env = Environment(tools=[], CPPDEFINES = 'FOO', parse_flags = '-std=c99 -X -DBAR')
3539         assert env['CFLAGS']  == ['-std=c99'], env['CFLAGS']
3540         assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
3541         assert env['CPPDEFINES'] == ['FOO', 'BAR'], env['CPPDEFINES']
3542
3543     def test_clone_parse_flags(self):
3544         '''Test the env.Clone() parse_flags argument'''
3545         # all we have to show is that it gets to MergeFlags internally
3546         env = Environment(tools = [])
3547         env2 = env.Clone(parse_flags = '-X')
3548         assert 'CCFLAGS' not in env
3549         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3550
3551         env = Environment(tools = [], CCFLAGS=None)
3552         env2 = env.Clone(parse_flags = '-Y')
3553         assert env['CCFLAGS'] is None, env['CCFLAGS']
3554         assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
3555
3556         env = Environment(tools = [], CPPDEFINES = 'FOO')
3557         env2 = env.Clone(parse_flags = '-std=c99 -X -DBAR')
3558         assert 'CFLAGS' not in env
3559         assert env2['CFLAGS']  == ['-std=c99'], env2['CFLAGS']
3560         assert 'CCFLAGS' not in env
3561         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3562         assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
3563         assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
3564
3565
3566
3567 class OverrideEnvironmentTestCase(unittest.TestCase,TestEnvironmentFixture):
3568
3569     def setUp(self):
3570         env = Environment()
3571         env._dict = {'XXX' : 'x', 'YYY' : 'y'}
3572         env2 = OverrideEnvironment(env, {'XXX' : 'x2'})
3573         env3 = OverrideEnvironment(env2, {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'})
3574         self.envs = [ env, env2, env3 ]
3575
3576     def checkpath(self, node, expect):
3577         return str(node) == os.path.normpath(expect)
3578
3579     def test___init__(self):
3580         """Test OverrideEnvironment initialization"""
3581         env, env2, env3 = self.envs
3582         assert env['XXX'] == 'x', env['XXX']
3583         assert env2['XXX'] == 'x2', env2['XXX']
3584         assert env3['XXX'] == 'x3', env3['XXX']
3585         assert env['YYY'] == 'y', env['YYY']
3586         assert env2['YYY'] == 'y', env2['YYY']
3587         assert env3['YYY'] == 'y3', env3['YYY']
3588
3589     def test___delitem__(self):
3590         """Test deleting variables from an OverrideEnvironment"""
3591         env, env2, env3 = self.envs
3592
3593         del env3['XXX']
3594         assert 'XXX' not in env, "env has XXX?"
3595         assert 'XXX' not in env2, "env2 has XXX?"
3596         assert 'XXX' not in env3, "env3 has XXX?"
3597
3598         del env3['YYY']
3599         assert 'YYY' not in env, "env has YYY?"
3600         assert 'YYY' not in env2, "env2 has YYY?"
3601         assert 'YYY' not in env3, "env3 has YYY?"
3602
3603         del env3['ZZZ']
3604         assert 'ZZZ' not in env, "env has ZZZ?"
3605         assert 'ZZZ' not in env2, "env2 has ZZZ?"
3606         assert 'ZZZ' not in env3, "env3 has ZZZ?"
3607
3608     def test_get(self):
3609         """Test the OverrideEnvironment get() method"""
3610         env, env2, env3 = self.envs
3611         assert env.get('XXX') == 'x', env.get('XXX')
3612         assert env2.get('XXX') == 'x2', env2.get('XXX')
3613         assert env3.get('XXX') == 'x3', env3.get('XXX')
3614         assert env.get('YYY') == 'y', env.get('YYY')
3615         assert env2.get('YYY') == 'y', env2.get('YYY')
3616         assert env3.get('YYY') == 'y3', env3.get('YYY')
3617         assert env.get('ZZZ') is None, env.get('ZZZ')
3618         assert env2.get('ZZZ') is None, env2.get('ZZZ')
3619         assert env3.get('ZZZ') == 'z3', env3.get('ZZZ')
3620
3621     def test_has_key(self):
3622         """Test the OverrideEnvironment has_key() method"""
3623         env, env2, env3 = self.envs
3624         assert 'XXX' in env, 'XXX' in env
3625         assert 'XXX' in env2, 'XXX' in env2
3626         assert 'XXX' in env3, 'XXX' in env3
3627         assert 'YYY' in env, 'YYY' in env
3628         assert 'YYY' in env2, 'YYY' in env2
3629         assert 'YYY' in env3, 'YYY' in env3
3630         assert 'ZZZ' not in env, 'ZZZ' in env
3631         assert 'ZZZ' not in env2, 'ZZZ' in env2
3632         assert 'ZZZ' in env3, 'ZZZ' in env3
3633
3634     def test_contains(self):
3635         """Test the OverrideEnvironment __contains__() method"""
3636         try:
3637             'x' in {'x':1}
3638         except TypeError:
3639             # TODO(1.5)
3640             # An early version of Python that doesn't support "in"
3641             # on dictionaries.  Just pass the test.
3642             pass
3643         else:
3644             env, env2, env3 = self.envs
3645             assert 'XXX' in env
3646             assert 'XXX' in env2
3647             assert 'XXX' in env3
3648             assert 'YYY' in env
3649             assert 'YYY' in env2
3650             assert 'YYY' in env3
3651             assert not 'ZZZ' in env
3652             assert not 'ZZZ' in env2
3653             assert 'ZZZ' in env3
3654
3655     def test_items(self):
3656         """Test the OverrideEnvironment Dictionary() method"""
3657         env, env2, env3 = self.envs
3658         items = env.Dictionary()
3659         assert items == {'XXX' : 'x', 'YYY' : 'y'}, items
3660         items = env2.Dictionary()
3661         assert items == {'XXX' : 'x2', 'YYY' : 'y'}, items
3662         items = env3.Dictionary()
3663         assert items == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, items
3664
3665     def test_items(self):
3666         """Test the OverrideEnvironment items() method"""
3667         env, env2, env3 = self.envs
3668         items = sorted(env.items())
3669         assert items == [('XXX', 'x'), ('YYY', 'y')], items
3670         items = sorted(env2.items())
3671         assert items == [('XXX', 'x2'), ('YYY', 'y')], items
3672         items = sorted(env3.items())
3673         assert items == [('XXX', 'x3'), ('YYY', 'y3'), ('ZZZ', 'z3')], items
3674
3675     def test_gvars(self):
3676         """Test the OverrideEnvironment gvars() method"""
3677         env, env2, env3 = self.envs
3678         gvars = env.gvars()
3679         assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3680         gvars = env2.gvars()
3681         assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3682         gvars = env3.gvars()
3683         assert gvars == {'XXX' : 'x', 'YYY' : 'y'}, gvars
3684
3685     def test_lvars(self):
3686         """Test the OverrideEnvironment lvars() method"""
3687         env, env2, env3 = self.envs
3688         lvars = env.lvars()
3689         assert lvars == {}, lvars
3690         lvars = env2.lvars()
3691         assert lvars == {'XXX' : 'x2'}, lvars
3692         lvars = env3.lvars()
3693         assert lvars == {'XXX' : 'x3', 'YYY' : 'y3', 'ZZZ' : 'z3'}, lvars
3694
3695     def test_Replace(self):
3696         """Test the OverrideEnvironment Replace() method"""
3697         env, env2, env3 = self.envs
3698         assert env['XXX'] == 'x', env['XXX']
3699         assert env2['XXX'] == 'x2', env2['XXX']
3700         assert env3['XXX'] == 'x3', env3['XXX']
3701         assert env['YYY'] == 'y', env['YYY']
3702         assert env2['YYY'] == 'y', env2['YYY']
3703         assert env3['YYY'] == 'y3', env3['YYY']
3704
3705         env.Replace(YYY = 'y4')
3706
3707         assert env['XXX'] == 'x', env['XXX']
3708         assert env2['XXX'] == 'x2', env2['XXX']
3709         assert env3['XXX'] == 'x3', env3['XXX']
3710         assert env['YYY'] == 'y4', env['YYY']
3711         assert env2['YYY'] == 'y4', env2['YYY']
3712         assert env3['YYY'] == 'y3', env3['YYY']
3713
3714     # Tests a number of Base methods through an OverrideEnvironment to
3715     # make sure they handle overridden constructionv variables properly.
3716     #
3717     # The following Base methods also call self.subst(), and so could
3718     # theoretically be subject to problems with evaluating overridden
3719     # variables, but they're never really called that way in the rest
3720     # of our code, so we won't worry about them (at least for now):
3721     #
3722     # ParseConfig()
3723     # ParseDepends()
3724     # Platform()
3725     # Tool()
3726     #
3727     # Action()
3728     # Alias()
3729     # Builder()
3730     # CacheDir()
3731     # Configure()
3732     # Environment()
3733     # FindFile()
3734     # Scanner()
3735     # SourceSignatures()
3736     # TargetSignatures()
3737
3738     # It's unlikely Clone() will ever be called this way, so let the
3739     # other methods test that handling overridden values works.
3740     #def test_Clone(self):
3741     #    """Test the OverrideEnvironment Clone() method"""
3742     #    pass
3743
3744     def test_FindIxes(self):
3745         """Test the OverrideEnvironment FindIxes() method"""
3746         env, env2, env3 = self.envs
3747         x = env.FindIxes(['xaaay'], 'XXX', 'YYY')
3748         assert x == 'xaaay', x
3749         x = env2.FindIxes(['x2aaay'], 'XXX', 'YYY')
3750         assert x == 'x2aaay', x
3751         x = env3.FindIxes(['x3aaay3'], 'XXX', 'YYY')
3752         assert x == 'x3aaay3', x
3753
3754     def test_ReplaceIxes(self):
3755         """Test the OverrideEnvironment ReplaceIxes() method"""
3756         env, env2, env3 = self.envs
3757         x = env.ReplaceIxes('xaaay', 'XXX', 'YYY', 'YYY', 'XXX')
3758         assert x == 'yaaax', x
3759         x = env2.ReplaceIxes('x2aaay', 'XXX', 'YYY', 'YYY', 'XXX')
3760         assert x == 'yaaax2', x
3761         x = env3.ReplaceIxes('x3aaay3', 'XXX', 'YYY', 'YYY', 'XXX')
3762         assert x == 'y3aaax3', x
3763
3764     # It's unlikely WhereIs() will ever be called this way, so let the
3765     # other methods test that handling overridden values works.
3766     #def test_WhereIs(self):
3767     #    """Test the OverrideEnvironment WhereIs() method"""
3768     #    pass
3769
3770     def test_Dir(self):
3771         """Test the OverrideEnvironment Dir() method"""
3772         env, env2, env3 = self.envs
3773         x = env.Dir('ddir/$XXX')
3774         assert self.checkpath(x, 'ddir/x'), str(x)
3775         x = env2.Dir('ddir/$XXX')
3776         assert self.checkpath(x, 'ddir/x2'), str(x)
3777         x = env3.Dir('ddir/$XXX')
3778         assert self.checkpath(x, 'ddir/x3'), str(x)
3779
3780     def test_Entry(self):
3781         """Test the OverrideEnvironment Entry() method"""
3782         env, env2, env3 = self.envs
3783         x = env.Entry('edir/$XXX')
3784         assert self.checkpath(x, 'edir/x'), str(x)
3785         x = env2.Entry('edir/$XXX')
3786         assert self.checkpath(x, 'edir/x2'), str(x)
3787         x = env3.Entry('edir/$XXX')
3788         assert self.checkpath(x, 'edir/x3'), str(x)
3789
3790     def test_File(self):
3791         """Test the OverrideEnvironment File() method"""
3792         env, env2, env3 = self.envs
3793         x = env.File('fdir/$XXX')
3794         assert self.checkpath(x, 'fdir/x'), str(x)
3795         x = env2.File('fdir/$XXX')
3796         assert self.checkpath(x, 'fdir/x2'), str(x)
3797         x = env3.File('fdir/$XXX')
3798         assert self.checkpath(x, 'fdir/x3'), str(x)
3799
3800     def test_Split(self):
3801         """Test the OverrideEnvironment Split() method"""
3802         env, env2, env3 = self.envs
3803         env['AAA'] = '$XXX $YYY $ZZZ'
3804         x = env.Split('$AAA')
3805         assert x == ['x', 'y'], x
3806         x = env2.Split('$AAA')
3807         assert x == ['x2', 'y'], x
3808         x = env3.Split('$AAA')
3809         assert x == ['x3', 'y3', 'z3'], x
3810
3811     def test_parse_flags(self):
3812         '''Test the OverrideEnvironment parse_flags argument'''
3813         # all we have to show is that it gets to MergeFlags internally
3814         env = SubstitutionEnvironment()
3815         env2 = env.Override({'parse_flags' : '-X'})
3816         assert 'CCFLAGS' not in env
3817         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3818
3819         env = SubstitutionEnvironment(CCFLAGS=None)
3820         env2 = env.Override({'parse_flags' : '-Y'})
3821         assert env['CCFLAGS'] is None, env['CCFLAGS']
3822         assert env2['CCFLAGS'] == ['-Y'], env2['CCFLAGS']
3823
3824         env = SubstitutionEnvironment(CPPDEFINES = 'FOO')
3825         env2 = env.Override({'parse_flags' : '-std=c99 -X -DBAR'})
3826         assert 'CFLAGS' not in env
3827         assert env2['CFLAGS']  == ['-std=c99'], env2['CFLAGS']
3828         assert 'CCFLAGS' not in env
3829         assert env2['CCFLAGS'] == ['-X'], env2['CCFLAGS']
3830         assert env['CPPDEFINES'] == 'FOO', env['CPPDEFINES']
3831         assert env2['CPPDEFINES'] == ['FOO','BAR'], env2['CPPDEFINES']
3832
3833
3834
3835 class NoSubstitutionProxyTestCase(unittest.TestCase,TestEnvironmentFixture):
3836
3837     def test___init__(self):
3838         """Test NoSubstitutionProxy initialization"""
3839         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3840         assert env['XXX'] == 'x', env['XXX']
3841         assert env['YYY'] == 'y', env['YYY']
3842
3843         proxy = NoSubstitutionProxy(env)
3844         assert proxy['XXX'] == 'x', proxy['XXX']
3845         assert proxy['YYY'] == 'y', proxy['YYY']
3846
3847     def test_attributes(self):
3848         """Test getting and setting NoSubstitutionProxy attributes"""
3849         env = Environment()
3850         setattr(env, 'env_attr', 'value1')
3851
3852         proxy = NoSubstitutionProxy(env)
3853         setattr(proxy, 'proxy_attr', 'value2')
3854
3855         x = getattr(env, 'env_attr')
3856         assert x == 'value1', x
3857         x = getattr(proxy, 'env_attr')
3858         assert x == 'value1', x
3859
3860         x = getattr(env, 'proxy_attr')
3861         assert x == 'value2', x
3862         x = getattr(proxy, 'proxy_attr')
3863         assert x == 'value2', x
3864
3865     def test_subst(self):
3866         """Test the NoSubstitutionProxy.subst() method"""
3867         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3868         assert env['XXX'] == 'x', env['XXX']
3869         assert env['YYY'] == 'y', env['YYY']
3870
3871         proxy = NoSubstitutionProxy(env)
3872         assert proxy['XXX'] == 'x', proxy['XXX']
3873         assert proxy['YYY'] == 'y', proxy['YYY']
3874
3875         x = env.subst('$XXX')
3876         assert x == 'x', x
3877         x = proxy.subst('$XXX')
3878         assert x == '$XXX', x
3879
3880         x = proxy.subst('$YYY', raw=7, target=None, source=None,
3881                         conv=None,
3882                         extra_meaningless_keyword_argument=None)
3883         assert x == '$YYY', x
3884
3885     def test_subst_kw(self):
3886         """Test the NoSubstitutionProxy.subst_kw() method"""
3887         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3888         assert env['XXX'] == 'x', env['XXX']
3889         assert env['YYY'] == 'y', env['YYY']
3890
3891         proxy = NoSubstitutionProxy(env)
3892         assert proxy['XXX'] == 'x', proxy['XXX']
3893         assert proxy['YYY'] == 'y', proxy['YYY']
3894
3895         x = env.subst_kw({'$XXX':'$YYY'})
3896         assert x == {'x':'y'}, x
3897         x = proxy.subst_kw({'$XXX':'$YYY'})
3898         assert x == {'$XXX':'$YYY'}, x
3899
3900     def test_subst_list(self):
3901         """Test the NoSubstitutionProxy.subst_list() method"""
3902         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3903         assert env['XXX'] == 'x', env['XXX']
3904         assert env['YYY'] == 'y', env['YYY']
3905
3906         proxy = NoSubstitutionProxy(env)
3907         assert proxy['XXX'] == 'x', proxy['XXX']
3908         assert proxy['YYY'] == 'y', proxy['YYY']
3909
3910         x = env.subst_list('$XXX')
3911         assert x == [['x']], x
3912         x = proxy.subst_list('$XXX')
3913         assert x == [[]], x
3914
3915         x = proxy.subst_list('$YYY', raw=0, target=None, source=None, conv=None)
3916         assert x == [[]], x
3917
3918     def test_subst_target_source(self):
3919         """Test the NoSubstitutionProxy.subst_target_source() method"""
3920         env = self.TestEnvironment(XXX = 'x', YYY = 'y')
3921         assert env['XXX'] == 'x', env['XXX']
3922         assert env['YYY'] == 'y', env['YYY']
3923
3924         proxy = NoSubstitutionProxy(env)
3925         assert proxy['XXX'] == 'x', proxy['XXX']
3926         assert proxy['YYY'] == 'y', proxy['YYY']
3927
3928         args = ('$XXX $TARGET $SOURCE $YYY',)
3929         kw = {'target' : DummyNode('ttt'), 'source' : DummyNode('sss')}
3930         x = env.subst_target_source(*args, **kw)
3931         assert x == 'x ttt sss y', x
3932         x = proxy.subst_target_source(*args, **kw)
3933         assert x == ' ttt sss ', x
3934
3935 class EnvironmentVariableTestCase(unittest.TestCase):
3936
3937     def test_is_valid_construction_var(self):
3938         """Testing is_valid_construction_var()"""
3939         r = is_valid_construction_var("_a")
3940         assert r is not None, r
3941         r = is_valid_construction_var("z_")
3942         assert r is not None, r
3943         r = is_valid_construction_var("X_")
3944         assert r is not None, r
3945         r = is_valid_construction_var("2a")
3946         assert r is None, r
3947         r = is_valid_construction_var("a2_")
3948         assert r is not None, r
3949         r = is_valid_construction_var("/")
3950         assert r is None, r
3951         r = is_valid_construction_var("_/")
3952         assert r is None, r
3953         r = is_valid_construction_var("a/")
3954         assert r is None, r
3955         r = is_valid_construction_var(".b")
3956         assert r is None, r
3957         r = is_valid_construction_var("_.b")
3958         assert r is None, r
3959         r = is_valid_construction_var("b1._")
3960         assert r is None, r
3961         r = is_valid_construction_var("-b")
3962         assert r is None, r
3963         r = is_valid_construction_var("_-b")
3964         assert r is None, r
3965         r = is_valid_construction_var("b1-_")
3966         assert r is None, r
3967
3968
3969
3970 if __name__ == "__main__":
3971     suite = unittest.TestSuite()
3972     tclasses = [ SubstitutionTestCase,
3973                  BaseTestCase,
3974                  OverrideEnvironmentTestCase,
3975                  NoSubstitutionProxyTestCase,
3976                  EnvironmentVariableTestCase ]
3977     for tclass in tclasses:
3978         names = unittest.getTestCaseNames(tclass, 'test_')
3979         suite.addTests(list(map(tclass, names)))
3980     if not unittest.TextTestRunner().run(suite).wasSuccessful():
3981         sys.exit(1)
3982
3983 # Local Variables:
3984 # tab-width:4
3985 # indent-tabs-mode:nil
3986 # End:
3987 # vim: set expandtab tabstop=4 shiftwidth=4: