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