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:
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
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.
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35 from SCons.Util import *
44 self.buffer = self.buffer + str
47 def __init__(self, dict={}):
50 def Dictionary(self, key = None):
56 dict = self.dict.copy()
57 dict["TARGETS"] = 'tsig'
58 dict["SOURCES"] = 'ssig'
61 def CmdGen1(target, source, env):
62 # Nifty trick...since Environment references are interpolated,
63 # instantiate an instance of a callable class with this one,
64 # which will then get evaluated.
65 assert target == 't', target
66 assert source == 's', source
67 return "${CMDGEN2('foo')}"
70 def __init__(self, mystr):
73 def __call__(self, target, source, env):
74 assert target == 't', target
75 assert source == 's', source
76 return [ self.mystr, env.Dictionary('BAR') ]
78 class UtilTestCase(unittest.TestCase):
80 """Test the subst function"""
82 target = [ "./foo/bar.exe",
85 source = [ "./foo/blah.cpp",
93 loc['CMDGEN1'] = CmdGen1
94 loc['CMDGEN2'] = CmdGen2
103 return string.replace(str, '/', os.sep)
105 newcom = scons_subst("test $TARGETS $SOURCES", env,
106 target=target, source=source)
107 assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp /bar/ack.cpp ../foo/ack.c")
109 newcom = scons_subst("test ${TARGETS[:]} ${SOURCES[0]}", env,
110 target=target, source=source)
111 assert newcom == cvt("test foo/bar.exe /bar/baz.obj ../foo/baz.obj foo/blah.cpp")
113 newcom = scons_subst("test ${TARGETS[1:]}v", env,
114 target=target, source=source)
115 assert newcom == cvt("test /bar/baz.obj ../foo/baz.objv")
117 newcom = scons_subst("test $TARGET", env,
118 target=target, source=source)
119 assert newcom == cvt("test foo/bar.exe")
121 newcom = scons_subst("test $TARGET$FOO[0]", env,
122 target=target, source=source)
123 assert newcom == cvt("test foo/bar.exe[0]")
125 newcom = scons_subst("test ${TARGET.file}", env,
126 target=target, source=source)
127 assert newcom == cvt("test bar.exe")
129 newcom = scons_subst("test ${TARGET.filebase}", env,
130 target=target, source=source)
131 assert newcom == cvt("test bar")
133 newcom = scons_subst("test ${TARGET.suffix}", env,
134 target=target, source=source)
135 assert newcom == cvt("test .exe")
137 newcom = scons_subst("test ${TARGET.base}", env,
138 target=target, source=source)
139 assert newcom == cvt("test foo/bar")
141 newcom = scons_subst("test ${TARGET.dir}", env,
142 target=target, source=source)
143 assert newcom == cvt("test foo")
145 newcom = scons_subst("test ${TARGET.abspath}", env,
146 target=target, source=source)
147 assert newcom == cvt("test %s/foo/bar.exe"%SCons.Util.updrive(os.getcwd())), newcom
149 newcom = scons_subst("test ${SOURCES.abspath}", env,
150 target=target, source=source)
151 assert newcom == cvt("test %s/foo/blah.cpp %s %s/foo/ack.c"%(SCons.Util.updrive(os.getcwd()),
152 SCons.Util.updrive(os.path.abspath(os.path.normpath("/bar/ack.cpp"))),
153 SCons.Util.updrive(os.path.normpath(os.getcwd()+"/..")))), newcom
155 newcom = scons_subst("test ${SOURCE.abspath}", env,
156 target=target, source=source)
157 assert newcom == cvt("test %s/foo/blah.cpp"%SCons.Util.updrive(os.getcwd())), newcom
159 # Note that we don't use the cvt() helper function here,
160 # because we're testing that the .posix attribute does its own
161 # conversion of the path name backslashes to slashes.
162 newcom = scons_subst("test ${TARGET.posix} ${SOURCE.posix}", env,
163 target=target, source=source)
164 assert newcom == "test foo/bar.exe foo/blah.cpp", newcom
166 SCons.Node.FS.default_fs.BuildDir("#baz","#foo")
168 newcom = scons_subst("test ${SOURCE.srcdir}", env,
169 target=target, source=['baz/bar.c'])
171 assert newcom == cvt("test foo"), newcom
173 newcom = scons_subst("test ${SOURCE.srcpath}", env,
174 target=target, source=['baz/bar.c'])
176 assert newcom == cvt("test foo/bar.c"), newcom
178 newcom = scons_subst("test $xxx", env)
179 assert newcom == cvt("test"), newcom
181 newcom = scons_subst("test $($xxx$)", env)
182 assert newcom == cvt("test $($)"), newcom
184 newcom = scons_subst("test $( $xxx $)", env)
185 assert newcom == cvt("test $( $)"), newcom
187 newcom = scons_subst("test $($xxx$)", env, re.compile('\$[()]'))
188 assert newcom == cvt("test"), newcom
190 newcom = scons_subst("test $( $xxx $)", env, re.compile('\$[()]'))
191 assert newcom == cvt("test"), newcom
193 newcom = scons_subst("test $zero", env)
194 assert newcom == cvt("test 0"), newcom
196 newcom = scons_subst("test $one", env)
197 assert newcom == cvt("test 1"), newcom
199 newcom = scons_subst("test aXbXcXd", env, re.compile('X'))
200 assert newcom == cvt("test abcd"), newcom
202 newcom = scons_subst("test $CMDGEN1 $SOURCES $TARGETS",
203 env, target='t', source='s')
204 assert newcom == cvt("test foo baz s t"), newcom
206 # Test against a former bug in scons_subst_list()
207 glob = { "FOO" : "$BAR",
210 "BARXYX" : "BADNEWS" }
211 newcom = scons_subst("$FOO$BLAT", DummyEnv(glob))
212 assert newcom == "BAZXYX", newcom
214 # Test for double-dollar-sign behavior
215 glob = { "FOO" : "BAR",
217 newcom = scons_subst("$$FOO$BAZ", DummyEnv(glob))
218 assert newcom == "$FOOBLAT", newcom
220 def test_splitext(self):
221 assert splitext('foo') == ('foo','')
222 assert splitext('foo.bar') == ('foo','.bar')
223 assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'),'')
225 def test_subst_list(self):
226 """Testing the scons_subst_list() method..."""
229 def __init__(self, name):
233 def is_literal(self):
237 target = [ "./foo/bar.exe",
238 "/bar/baz with spaces.obj",
240 source = [ "./foo/blah with spaces.cpp",
244 loc['NEWLINE'] = 'before\nafter'
246 loc['DO'] = Node('do something')
247 loc['FOO'] = Node('foo.in')
248 loc['BAR'] = Node('bar with spaces.out')
249 loc['CRAZY'] = Node('crazy\nfile.in')
251 loc['CMDGEN1'] = CmdGen1
252 loc['CMDGEN2'] = CmdGen2
261 return string.replace(str, '/', os.sep)
263 cmd_list = scons_subst_list("$TARGETS", env,
266 assert cmd_list[0][1] == cvt("/bar/baz with spaces.obj"), cmd_list[0][1]
268 cmd_list = scons_subst_list("$SOURCES $NEWLINE $TARGETS", env,
271 assert len(cmd_list) == 2, cmd_list
272 assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
273 assert cmd_list[1][2] == cvt("/bar/baz with spaces.obj"), cmd_list[1]
275 cmd_list = scons_subst_list("$SOURCES$NEWLINE", env,
278 assert len(cmd_list) == 2, cmd_list
279 assert cmd_list[1][0] == 'after', cmd_list[1][0]
280 assert cmd_list[0][2] == cvt('../foo/ack.cbefore'), cmd_list[0][2]
282 cmd_list = scons_subst_list("$DO --in=$FOO --out=$BAR", env)
283 assert len(cmd_list) == 1, cmd_list
284 assert len(cmd_list[0]) == 3, cmd_list
285 assert cmd_list[0][0] == 'do something', cmd_list[0][0]
286 assert cmd_list[0][1] == '--in=foo.in', cmd_list[0][1]
287 assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
289 # This test is now fixed, and works like it should.
290 cmd_list = scons_subst_list("$DO --in=$CRAZY --out=$BAR", env)
291 assert len(cmd_list) == 1, map(str, cmd_list[0])
292 assert len(cmd_list[0]) == 3, cmd_list
293 assert cmd_list[0][0] == 'do something', cmd_list[0][0]
294 assert cmd_list[0][1] == '--in=crazy\nfile.in', cmd_list[0][1]
295 assert cmd_list[0][2] == '--out=bar with spaces.out', cmd_list[0][2]
297 # Test inputting a list to scons_subst_list()
298 cmd_list = scons_subst_list([ "$SOURCES$NEWLINE", "$TARGETS",
303 assert len(cmd_list) == 2, len(cmd_list)
304 assert cmd_list[0][0] == cvt('foo/blah with spaces.cpp'), cmd_list[0][0]
305 assert cmd_list[1][0] == cvt("after"), cmd_list[1]
306 assert cmd_list[1][4] == "This is a test", cmd_list[1]
308 # Test interpolating a callable.
309 cmd_list = scons_subst_list("testing $CMDGEN1 $TARGETS $SOURCES", env,
310 target='t', source='s')
311 assert len(cmd_list) == 1, len(cmd_list)
312 assert cmd_list[0][0] == 'testing', cmd_list[0][0]
313 assert cmd_list[0][1] == 'foo', cmd_list[0][1]
314 assert cmd_list[0][2] == 'bar with spaces.out', cmd_list[0][2]
315 assert cmd_list[0][3] == 't', cmd_list[0][3]
316 assert cmd_list[0][4] == 's', cmd_list[0][4]
319 # Test against a former bug in scons_subst_list()
320 glob = { "FOO" : "$BAR",
323 "BARXYX" : "BADNEWS" }
324 cmd_list = scons_subst_list("$FOO$BLAT", DummyEnv(glob))
325 assert cmd_list[0][0] == "BAZXYX", cmd_list[0][0]
327 # Test for double-dollar-sign behavior
328 glob = { "FOO" : "BAR",
330 cmd_list = scons_subst_list("$$FOO$BAZ", DummyEnv(glob))
331 assert cmd_list[0][0] == "$FOOBLAT", cmd_list[0][0]
333 # Now test escape functionality
334 def escape_func(foo):
335 return '**' + foo + '**'
338 glob = { "FOO" : PathList([ 'foo\nwith\nnewlines',
339 'bar\nwith\nnewlines' ]) }
340 cmd_list = scons_subst_list("$FOO", DummyEnv(glob))
341 assert cmd_list[0][0] == 'foo\nwith\nnewlines', cmd_list[0][0]
342 cmd_list[0][0].escape(escape_func)
343 assert cmd_list[0][0] == '**foo\nwith\nnewlines**', cmd_list[0][0]
344 assert cmd_list[0][1] == 'bar\nwith\nnewlines', cmd_list[0][0]
345 cmd_list[0][1].escape(escape_func)
346 assert cmd_list[0][1] == '**bar\nwith\nnewlines**', cmd_list[0][0]
348 def test_quote_spaces(self):
349 """Testing the quote_spaces() method..."""
350 q = quote_spaces('x')
353 q = quote_spaces('x x')
354 assert q == '"x x"', q
356 q = quote_spaces('x\tx')
357 assert q == '"x\tx"', q
359 def test_render_tree(self):
361 def __init__(self, name, children=[]):
362 self.children = children
367 def get_children(node):
370 windows_h = Node("windows.h")
371 stdlib_h = Node("stdlib.h")
372 stdio_h = Node("stdio.h")
373 bar_c = Node("bar.c", [stdlib_h, windows_h])
374 bar_o = Node("bar.o", [bar_c])
375 foo_c = Node("foo.c", [stdio_h])
376 foo_o = Node("foo.o", [foo_c])
377 foo = Node("foo", [foo_o, bar_o])
390 actual = render_tree(foo, get_children)
391 assert expect == actual, (expect, actual)
393 bar_h = Node('bar.h', [stdlib_h])
394 blat_h = Node('blat.h', [stdlib_h])
395 blat_c = Node('blat.c', [blat_h, bar_h])
396 blat_o = Node('blat.o', [blat_c])
406 actual = render_tree(blat_o, get_children, 1)
407 assert expect == actual, (expect, actual)
409 def test_is_Dict(self):
412 assert is_Dict(UserDict.UserDict())
413 assert not is_Dict([])
414 assert not is_Dict("")
415 if hasattr(types, 'UnicodeType'):
416 exec "assert not is_Dict(u'')"
418 def test_is_List(self):
421 assert is_List(UserList.UserList())
422 assert not is_List({})
423 assert not is_List("")
424 if hasattr(types, 'UnicodeType'):
425 exec "assert not is_List(u'')"
427 def test_Split(self):
428 assert Split("foo bar") == ["foo", "bar"]
429 assert Split(["foo", "bar"]) == ["foo", "bar"]
430 assert Split("foo") == ["foo"]
432 def test_is_String(self):
434 if hasattr(types, 'UnicodeType'):
435 exec "assert is_String(u'')"
441 assert is_String(UserString.UserString(''))
442 assert not is_String({})
443 assert not is_String([])
445 def test_to_String(self):
446 """Test the to_String() method."""
447 assert to_String(1) == "1", to_String(1)
448 assert to_String([ 1, 2, 3]) == str([1, 2, 3]), to_String([1,2,3])
449 assert to_String("foo") == "foo", to_String("foo")
454 s1=UserString.UserString('blah')
455 assert to_String(s1) == s1, s1
456 assert to_String(s1) == 'blah', s1
458 class Derived(UserString.UserString):
461 assert to_String(s2) == s2, s2
462 assert to_String(s2) == 'foo', s2
464 if hasattr(types, 'UnicodeType'):
465 s3=UserString.UserString(unicode('bar'))
466 assert to_String(s3) == s3, s3
467 assert to_String(s3) == unicode('bar'), s3
468 assert type(to_String(s3)) is types.UnicodeType, \
473 if hasattr(types, 'UnicodeType'):
475 assert to_String(s4) == unicode('baz'), to_String(s4)
476 assert type(to_String(s4)) is types.UnicodeType, \
479 def test_WhereIs(self):
480 test = TestCmd.TestCmd(workdir = '')
482 sub1_xxx_exe = test.workpath('sub1', 'xxx.exe')
483 sub2_xxx_exe = test.workpath('sub2', 'xxx.exe')
484 sub3_xxx_exe = test.workpath('sub3', 'xxx.exe')
485 sub4_xxx_exe = test.workpath('sub4', 'xxx.exe')
487 test.subdir('subdir', 'sub1', 'sub2', 'sub3', 'sub4')
489 if sys.platform != 'win32':
490 test.write(sub1_xxx_exe, "\n")
492 os.mkdir(sub2_xxx_exe)
494 test.write(sub3_xxx_exe, "\n")
495 os.chmod(sub3_xxx_exe, 0777)
497 test.write(sub4_xxx_exe, "\n")
498 os.chmod(sub4_xxx_exe, 0777)
500 env_path = os.environ['PATH']
502 pathdirs_1234 = [ test.workpath('sub1'),
503 test.workpath('sub2'),
504 test.workpath('sub3'),
505 test.workpath('sub4'),
506 ] + string.split(env_path, os.pathsep)
508 pathdirs_1243 = [ test.workpath('sub1'),
509 test.workpath('sub2'),
510 test.workpath('sub4'),
511 test.workpath('sub3'),
512 ] + string.split(env_path, os.pathsep)
514 os.environ['PATH'] = string.join(pathdirs_1234, os.pathsep)
515 wi = WhereIs('xxx.exe')
516 assert wi == test.workpath(sub3_xxx_exe), wi
517 wi = WhereIs('xxx.exe', pathdirs_1243)
518 assert wi == test.workpath(sub4_xxx_exe), wi
519 wi = WhereIs('xxx.exe', string.join(pathdirs_1243, os.pathsep))
520 assert wi == test.workpath(sub4_xxx_exe), wi
522 os.environ['PATH'] = string.join(pathdirs_1243, os.pathsep)
523 wi = WhereIs('xxx.exe')
524 assert wi == test.workpath(sub4_xxx_exe), wi
525 wi = WhereIs('xxx.exe', pathdirs_1234)
526 assert wi == test.workpath(sub3_xxx_exe), wi
527 wi = WhereIs('xxx.exe', string.join(pathdirs_1234, os.pathsep))
528 assert wi == test.workpath(sub3_xxx_exe), wi
530 if sys.platform == 'win32':
531 wi = WhereIs('xxx', pathext = '')
532 assert wi is None, wi
534 wi = WhereIs('xxx', pathext = '.exe')
535 assert wi == test.workpath(sub4_xxx_exe), wi
537 wi = WhereIs('xxx', path = pathdirs_1234, pathext = '.BAT;.EXE')
538 assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
540 # Test that we return a normalized path even when
541 # the path contains forward slashes.
542 forward_slash = test.workpath('') + '/sub3'
543 wi = WhereIs('xxx', path = forward_slash, pathext = '.EXE')
544 assert string.lower(wi) == string.lower(test.workpath(sub3_xxx_exe)), wi
546 def test_get_env_var(self):
547 """Testing get_environment_var()."""
548 assert get_environment_var("$FOO") == "FOO", get_environment_var("$FOO")
549 assert get_environment_var("${BAR}") == "BAR", get_environment_var("${BAR}")
550 assert get_environment_var("$FOO_BAR1234") == "FOO_BAR1234", get_environment_var("$FOO_BAR1234")
551 assert get_environment_var("${BAR_FOO1234}") == "BAR_FOO1234", get_environment_var("${BAR_FOO1234}")
552 assert get_environment_var("${BAR}FOO") == None, get_environment_var("${BAR}FOO")
553 assert get_environment_var("$BAR ") == None, get_environment_var("$BAR ")
554 assert get_environment_var("FOO$BAR") == None, get_environment_var("FOO$BAR")
555 assert get_environment_var("$FOO[0]") == None, get_environment_var("$FOO[0]")
556 assert get_environment_var("${some('complex expression')}") == None, get_environment_var("${some('complex expression')}")
558 def test_Proxy(self):
559 """Test generic Proxy class."""
569 class ProxyTest(Proxy):
575 assert p.foo() == 1, p.foo()
576 assert p.bar() == 4, p.bar()
577 assert p.baz == 3, p.baz
582 assert p.baz == 5, p.baz
584 def test_Literal(self):
585 """Test the Literal() function."""
586 cmd_list = [ '$FOO', Literal('$BAR') ]
587 cmd_list = scons_subst_list(cmd_list,
588 DummyEnv({ 'FOO' : 'BAZ',
590 def escape_func(cmd):
591 return '**' + cmd + '**'
593 map(lambda x, e=escape_func: x.escape(e), cmd_list[0])
594 cmd_list = map(str, cmd_list[0])
595 assert cmd_list[0] == 'BAZ', cmd_list[0]
596 assert cmd_list[1] == '**$BAR**', cmd_list[1]
598 def test_mapPaths(self):
599 """Test the mapPaths function"""
600 fs = SCons.Node.FS.FS()
602 file=fs.File('bar/file')
605 def subst(self, arg):
608 res = mapPaths([ file, 'baz', 'blat/boo', '#test' ], dir)
609 assert res[0] == file, res[0]
610 assert res[1] == os.path.join('foo', 'baz'), res[1]
611 assert res[2] == os.path.join('foo', 'blat/boo'), res[2]
612 assert res[3] == '#test', res[3]
615 res=mapPaths('bleh', dir, env)
616 assert res[0] == os.path.normpath('foo/bar'), res[1]
618 def test_display(self):
619 old_stdout = sys.stdout
620 sys.stdout = OutBuffer()
621 SCons.Util.display("line1")
623 SCons.Util.display("line2")
625 SCons.Util.display("line3")
627 assert sys.stdout.buffer == "line1\nline3\n"
628 sys.stdout = old_stdout
630 def test_fs_delete(self):
631 test = TestCmd.TestCmd(workdir = '')
632 base = test.workpath('')
633 xxx = test.workpath('xxx.xxx')
634 ZZZ = test.workpath('ZZZ.ZZZ')
635 sub1_yyy = test.workpath('sub1', 'yyy.yyy')
638 test.write(xxx, "\n")
639 test.write(ZZZ, "\n")
640 test.write(sub1_yyy, "\n")
642 old_stdout = sys.stdout
643 sys.stdout = OutBuffer()
645 exp = "Removed " + os.path.join(base, ZZZ) + "\n" + \
646 "Removed " + os.path.join(base, sub1_yyy) + '\n' + \
647 "Removed directory " + os.path.join(base, 'sub1') + '\n' + \
648 "Removed " + os.path.join(base, xxx) + '\n' + \
649 "Removed directory " + base + '\n'
651 SCons.Util.fs_delete(base, remove=0)
652 assert sys.stdout.buffer == exp, sys.stdout.buffer
653 assert os.path.exists(sub1_yyy)
655 sys.stdout.buffer = ""
656 SCons.Util.fs_delete(base, remove=1)
657 assert sys.stdout.buffer == exp
658 assert not os.path.exists(base)
661 sys.stdout = old_stdout
663 def test_get_native_path(self):
664 """Test the get_native_path() function."""
666 filename = tempfile.mktemp()
667 str = '1234567890 ' + filename
668 open(filename, 'w').write(str)
669 assert open(SCons.Util.get_native_path(filename)).read() == str
671 def test_subst_dict(self):
672 """Test substituting dictionary values in an Action
674 d = subst_dict([], [], DummyEnv({'a' : 'A', 'b' : 'B'}))
675 assert d['a'] == 'A', d
676 assert d['b'] == 'B', d
678 d = subst_dict(target = 't', source = 's', env=DummyEnv())
679 assert str(d['TARGETS']) == 't', d['TARGETS']
680 assert str(d['TARGET']) == 't', d['TARGET']
681 assert str(d['SOURCES']) == 's', d['SOURCES']
682 assert str(d['SOURCE']) == 's', d['SOURCE']
684 d = subst_dict(target = ['t1', 't2'],
685 source = ['s1', 's2'],
687 TARGETS = map(lambda x: str(x), d['TARGETS'])
689 assert TARGETS == ['t1', 't2'], d['TARGETS']
690 assert str(d['TARGET']) == 't1', d['TARGET']
691 SOURCES = map(lambda x: str(x), d['SOURCES'])
693 assert SOURCES == ['s1', 's2'], d['SOURCES']
694 assert str(d['SOURCE']) == 's1', d['SOURCE']
697 def __init__(self, name):
702 return 'rstr-' + self.name
704 d = subst_dict(target = [N('t3'), 't4'],
705 source = ['s3', N('s4')],
707 TARGETS = map(lambda x: str(x), d['TARGETS'])
709 assert TARGETS == ['t3', 't4'], d['TARGETS']
710 SOURCES = map(lambda x: str(x), d['SOURCES'])
712 assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
715 if __name__ == "__main__":
716 suite = unittest.makeSuite(UtilTestCase, 'test_')
717 if not unittest.TextTestRunner().run(suite).wasSuccessful():