diff options
Diffstat (limited to 'test/Scanner')
| -rw-r--r-- | test/Scanner/Dir.py | 84 | ||||
| -rw-r--r-- | test/Scanner/Entry-to-Dir.py | 65 | ||||
| -rw-r--r-- | test/Scanner/FindPathDirs.py | 155 | ||||
| -rw-r--r-- | test/Scanner/Scanner.py | 244 | ||||
| -rw-r--r-- | test/Scanner/dictionary.py | 205 | ||||
| -rw-r--r-- | test/Scanner/empty-implicit.py | 87 | ||||
| -rw-r--r-- | test/Scanner/exception.py | 116 | ||||
| -rw-r--r-- | test/Scanner/generated.py | 441 | ||||
| -rw-r--r-- | test/Scanner/multi-env.py | 122 | ||||
| -rw-r--r-- | test/Scanner/no-Dir-node.py | 139 | ||||
| -rw-r--r-- | test/Scanner/parallel-rescan.py | 78 | ||||
| -rw-r--r-- | test/Scanner/scan-once.py | 100 | ||||
| -rw-r--r-- | test/Scanner/source_scanner-dict.py | 224 | ||||
| -rw-r--r-- | test/Scanner/unicode.py | 277 |
14 files changed, 2337 insertions, 0 deletions
diff --git a/test/Scanner/Dir.py b/test/Scanner/Dir.py new file mode 100644 index 0000000..86b80e9 --- /dev/null +++ b/test/Scanner/Dir.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that a simple scanner that returns Dir nodes works correctly. + +Submitted as http://scons.tigris.org/issues/show_bug.cgi?id=2534 +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir(['src']) + +test.write(['SConstruct'], """\ +env = Environment() +Export('env') + +env.VariantDir('build', 'src') +env.SConscript('build/SConscript.py') +""") + +test.write(['src', 'SConscript.py'], """\ +Import('env') + +def myscanner(node, env, path): + return [ env.Dir('#/install/dir2') ] # Gives error + +def mybuilder(target, source, env): + env.Execute(Copy(target[0], source[0])) + return None + +env['BUILDERS']['MyBuilder'] = env.Builder(action=mybuilder, source_scanner=env.Scanner(function=myscanner)) + +out = env.MyBuilder('outfile1', 'infile1') + +env.Install('#/install/dir1', out) +env.Install('#/install/dir2','infile2') +""") + +test.write(['src', 'infile1'], """\ +src/infile1 +""") + +test.write(['src', 'infile2'], """\ +src/infile2 +""") + +test.run(arguments = '.') + +test.must_match(['install', 'dir1', 'outfile1'], "src/infile1\n") +test.must_match(['install', 'dir2', 'infile2'], "src/infile2\n") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/Entry-to-Dir.py b/test/Scanner/Entry-to-Dir.py new file mode 100644 index 0000000..423a717 --- /dev/null +++ b/test/Scanner/Entry-to-Dir.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that an implicit dependency search for a directory for which +we have an Entry Node works as expected, converting the Entry into a +Dir Node. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +env = Environment() +tc = env.Program('testcase', 'testcase.cpp') +foo = env.Entry('foo') +tc[0].all_children()[0].all_children() +""") + +test.write('testcase.cpp', """\ +#if 0 +#include "foo/bar/widget.h" +#endif +int main(int argc, char *argv[]) +{ + return 0; +} +""") + +test.run(arguments = '.') + +# In 0.98.2, re-running failed with a stack trace. +test.run(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/FindPathDirs.py b/test/Scanner/FindPathDirs.py new file mode 100644 index 0000000..ef3ea46 --- /dev/null +++ b/test/Scanner/FindPathDirs.py @@ -0,0 +1,155 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that use of the FindPathDirs() function (actually a class) +can be used to specify a path_function of a scanner. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.subdir('inc1', 'inc2') + +test.write('build.py', r""" +import os.path +import sys +path = sys.argv[1].split() +input = open(sys.argv[2], 'rb') +output = open(sys.argv[3], 'wb') + +def find_file(f): + for dir in path: + p = dir + os.sep + f + if os.path.exists(p): + return open(p, 'rb') + return None + +def process(infp, outfp): + for line in infp.readlines(): + if line[:8] == 'include ': + file = line[8:-1] + process(find_file(file), outfp) + else: + outfp.write(line) + +process(input, output) + +sys.exit(0) +""") + +# Execute a subsidiary SConscript just to make sure we can +# get at the Scanner keyword from there. + +test.write('SConstruct', """\ +SConscript('SConscript') +""") + +test.write('SConscript', """\ +import os.path +import re + +include_re = re.compile(r'^include\s+(\S+)$', re.M) + +def kfile_scan(node, env, path, arg): + contents = node.get_text_contents() + includes = include_re.findall(contents) + if includes == []: + return [] + results = [] + for inc in includes: + for dir in path: + file = str(dir) + os.sep + inc + if os.path.exists(file): + results.append(file) + break + return results + +kscan = Scanner(name = 'kfile', + function = kfile_scan, + argument = None, + skeys = ['.k'], + path_function = FindPathDirs('KPATH')) + +########################################################## +# Test scanner as found automatically from the environment +# (backup_source_scanner) + +env = Environment(KPATH=['inc1', 'inc2']) +env.Append(SCANNERS = kscan) + +env.Command('foo', 'foo.k', r'%(_python_)s build.py "$KPATH" $SOURCES $TARGET') +""" % locals()) + + + +test.write('foo.k', +"""foo.k 1 line 1 +include xxx +include yyy +foo.k 1 line 4 +""") + +test.write(['inc1', 'xxx'], "inc1/xxx 1\n") +test.write(['inc2', 'yyy'], "inc2/yyy 1\n") + + + + +test.run() + +test.must_match('foo', "foo.k 1 line 1\ninc1/xxx 1\ninc2/yyy 1\nfoo.k 1 line 4\n") + +test.up_to_date(arguments = '.') + + + +test.write(['inc1', 'xxx'], "inc1/xxx 2\n") + +test.run() + +test.must_match('foo', "foo.k 1 line 1\ninc1/xxx 2\ninc2/yyy 1\nfoo.k 1 line 4\n") + + + +test.write(['inc1', 'yyy'], "inc1/yyy 2\n") + +test.run() + +test.must_match('foo', "foo.k 1 line 1\ninc1/xxx 2\ninc1/yyy 2\nfoo.k 1 line 4\n") + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/Scanner.py b/test/Scanner/Scanner.py new file mode 100644 index 0000000..8d48b73 --- /dev/null +++ b/test/Scanner/Scanner.py @@ -0,0 +1,244 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +input = open(sys.argv[1], 'rb') +output = open(sys.argv[2], 'wb') + +def process(infp, outfp): + for line in infp.readlines(): + if line[:8] == 'include ': + file = line[8:-1] + process(open(file, 'rb'), outfp) + elif line[:8] == 'getfile ': + outfp.write('include ') + outfp.write(line[8:]) + # note: converted, but not acted upon + else: + outfp.write(line) + +process(input, output) + +sys.exit(0) +""") + +# Execute a subsidiary SConscript just to make sure we can +# get at the Scanner keyword from there. + +test.write('SConstruct', """ +SConscript('SConscript') +""") + +test.write('SConscript', """ +import re + +include_re = re.compile(r'^include\s+(\S+)$', re.M) + +def kfile_scan(node, env, scanpaths, arg): + contents = node.get_text_contents() + includes = include_re.findall(contents) + return includes + +kscan = Scanner(name = 'kfile', + function = kfile_scan, + argument = None, + skeys = ['.k']) + +env = Environment(K2SCAN=kfile_scan) + +k2scan = env.Scanner(name = 'k2', + # We'd like to do the following, but it will take + # some major surgery to subst() and subst_list(), + # so comment it out for now. + # function = '$K2SCAN', + function = kfile_scan, + argument = None, + skeys = ['.k2']) + +########################################################## +# Test scanner as found automatically from the environment +# (backup_source_scanner) + +env = Environment() +env.Append(SCANNERS = kscan) + +env.Command('foo', 'foo.k', r'%(_python_)s build.py $SOURCES $TARGET') + +########################################################## +# Test resetting the environment scanners (and specifying as a list). + +env2 = env.Clone() +env2.Append(SCANNERS = [k2scan]) +env2.Command('junk', 'junk.k2', r'%(_python_)s build.py $SOURCES $TARGET') + +########################################################## +# Test specifying a specific source scanner for a target Node + +barbld = Builder(action=r'%(_python_)s build.py $SOURCES $TARGET', + source_scanner=kscan) +env.Append(BUILDERS={'BarBld':barbld}) +bar = env.BarBld(target='bar', source='bar.in') + +########################################################## +# Test specifying a source scanner for a Builder that gets +# automatically applied to targets generated from that Builder + +def blork(env, target, source): + open(str(target[0]), 'wb').write( + source[0].get_text_contents().replace('getfile', 'MISSEDME')) + +kbld = Builder(action=r'%(_python_)s build.py $SOURCES $TARGET', + src_suffix='.lork', + suffix='.blork', + source_scanner=kscan) +blorkbld = Builder(action=blork, + src_suffix='.blork', + suffix='.ork') + +env.Append(BUILDERS={'BLORK':blorkbld, 'KB':kbld}) + +blork = env.KB('moo.lork') +ork = env.BLORK(blork) +Alias('make_ork', ork) + +""" % locals()) + +test.write('foo.k', +"""foo.k 1 line 1 +include xxx +include yyy +foo.k 1 line 4 +""") + +test.write('bar.in', +"""include yyy +bar.in 1 line 2 +bar.in 1 line 3 +include zzz +""") + +test.write('junk.k2', +"""include yyy +junk.k2 1 line 2 +junk.k2 1 line 3 +include zzz +""") + +test.write('moo.lork', +"""include xxx +moo.lork 1 line 2 +include yyy +moo.lork 1 line 4 +include moo.inc +""") + +test.write('moo.inc', +"""getfile zzz +""") + +test.write('xxx', "xxx 1\n") +test.write('yyy', "yyy 1\n") +test.write('zzz', "zzz 1\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py bar.in bar +%(_python_)s build.py foo.k foo +%(_python_)s build.py junk.k2 junk +%(_python_)s build.py moo.lork moo.blork +blork(["moo.ork"], ["moo.blork"]) +""" % locals()) + +test.run(arguments = '.', stdout=expect) + +test.must_match('foo', "foo.k 1 line 1\nxxx 1\nyyy 1\nfoo.k 1 line 4\n") +test.must_match('bar', "yyy 1\nbar.in 1 line 2\nbar.in 1 line 3\nzzz 1\n") +test.must_match('junk', "yyy 1\njunk.k2 1 line 2\njunk.k2 1 line 3\nzzz 1\n") +test.must_match('moo.ork', "xxx 1\nmoo.lork 1 line 2\nyyy 1\nmoo.lork 1 line 4\ninclude zzz\n") + +test.up_to_date(arguments = '.') + +test.write('xxx', "xxx 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py foo.k foo +%(_python_)s build.py moo.lork moo.blork +blork(["moo.ork"], ["moo.blork"]) +""" % locals()) + +test.run(arguments = '.', stdout=expect) + +test.must_match('foo', "foo.k 1 line 1\nxxx 2\nyyy 1\nfoo.k 1 line 4\n") +test.must_match('bar', "yyy 1\nbar.in 1 line 2\nbar.in 1 line 3\nzzz 1\n") +test.must_match('junk', "yyy 1\njunk.k2 1 line 2\njunk.k2 1 line 3\nzzz 1\n") +test.must_match('moo.ork', "xxx 2\nmoo.lork 1 line 2\nyyy 1\nmoo.lork 1 line 4\ninclude zzz\n") + +test.write('yyy', "yyy 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py bar.in bar +%(_python_)s build.py foo.k foo +%(_python_)s build.py junk.k2 junk +%(_python_)s build.py moo.lork moo.blork +blork(["moo.ork"], ["moo.blork"]) +""" % locals()) + +test.run(arguments = '.', stdout=expect) + +test.must_match('foo', "foo.k 1 line 1\nxxx 2\nyyy 2\nfoo.k 1 line 4\n") +test.must_match('bar', "yyy 2\nbar.in 1 line 2\nbar.in 1 line 3\nzzz 1\n") +test.must_match('junk', "yyy 2\njunk.k2 1 line 2\njunk.k2 1 line 3\nzzz 1\n") +test.must_match('moo.ork', "xxx 2\nmoo.lork 1 line 2\nyyy 2\nmoo.lork 1 line 4\ninclude zzz\n") + +test.write('zzz', "zzz 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py bar.in bar +%(_python_)s build.py junk.k2 junk +""" % locals()) + +test.run(arguments = '.', stdout=expect) + +test.must_match('foo', "foo.k 1 line 1\nxxx 2\nyyy 2\nfoo.k 1 line 4\n") +test.must_match('bar', "yyy 2\nbar.in 1 line 2\nbar.in 1 line 3\nzzz 2\n") +test.must_match('junk', "yyy 2\njunk.k2 1 line 2\njunk.k2 1 line 3\nzzz 2\n") +test.must_match('moo.ork', "xxx 2\nmoo.lork 1 line 2\nyyy 2\nmoo.lork 1 line 4\ninclude zzz\n") + +test.up_to_date(arguments = 'foo') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/dictionary.py b/test/Scanner/dictionary.py new file mode 100644 index 0000000..a65e777 --- /dev/null +++ b/test/Scanner/dictionary.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +input = open(sys.argv[1], 'rb') +output = open(sys.argv[2], 'wb') + +include_prefix = 'include%s ' % sys.argv[1][-1] + +def process(infp, outfp): + for line in infp.readlines(): + if line[:len(include_prefix)] == include_prefix: + file = line[len(include_prefix):-1] + process(open(file, 'rb'), outfp) + else: + outfp.write(line) + +process(input, output) + +sys.exit(0) +""") + +# Execute a subsidiary SConscript just to make sure we can +# get at the Scanner keyword from there. + +test.write('SConstruct', """ +SConscript('SConscript') +""") + +test.write('SConscript', """ +import re + +include1_re = re.compile(r'^include1\s+(\S+)$', re.M) +include2_re = re.compile(r'^include2\s+(\S+)$', re.M) +include3_re = re.compile(r'^include3\s+(\S+)$', re.M) + +def kfile_scan1(node, env, scanpaths, arg=None): + contents = node.get_text_contents() + includes = include1_re.findall(contents) + return includes + +def kfile_scan2(node, env, scanpaths, arg=None): + contents = node.get_text_contents() + includes = include2_re.findall(contents) + return includes + +def kfile_scan3(node, env, scanpaths, arg=None): + contents = node.get_text_contents() + includes = include3_re.findall(contents) + return includes + +scan1 = Scanner(kfile_scan1) + +scan2 = Scanner(kfile_scan2) + +scan3 = Scanner(kfile_scan3) + +kscanner = Scanner({'.k1' : scan1, '.k2': scan2}) + +env = Environment(SCANNERS = [kscanner]) + +kscanner.add_scanner('.k3', scan3) + +env.Command('aaa', 'aaa.k1', r'%(_python_)s build.py $SOURCES $TARGET') +env.Command('bbb', 'bbb.k2', r'%(_python_)s build.py $SOURCES $TARGET') +env.Command('ccc', 'ccc.k3', r'%(_python_)s build.py $SOURCES $TARGET') +""" % locals()) + +test.write('aaa.k1', +"""aaa.k1 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('bbb.k2', +"""bbb.k2 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('ccc.k3', +"""ccc.k3 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('xxx', "xxx 1\n") +test.write('yyy', "yyy 1\n") +test.write('zzz', "zzz 1\n") + + + + +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa.k1 aaa +%(_python_)s build.py bbb.k2 bbb +%(_python_)s build.py ccc.k3 ccc +""" % locals()) + +test.run(stdout=expect) + +expect_aaa = 'aaa.k1 1\nline 2\nxxx 1\ninclude2 yyy\ninclude3 zzz\nline 6\n' +expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 1\ninclude3 zzz\nline 6\n' +expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 1\nline 6\n' + +test.must_match('aaa', expect_aaa) +test.must_match('bbb', expect_bbb) +test.must_match('ccc', expect_ccc) + +test.up_to_date(arguments = '.') + + + +test.write('zzz', "zzz 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py ccc.k3 ccc +""" % locals()) + +test.run(stdout=expect) + +expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 2\nline 6\n' + +test.must_match('bbb', expect_bbb) + + + +test.write('yyy', "yyy 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py bbb.k2 bbb +""" % locals()) + +test.run(stdout=expect) + +expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 2\ninclude3 zzz\nline 6\n' + +test.must_match('bbb', expect_bbb) + + + +test.write('xxx', "xxx 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa.k1 aaa +""" % locals()) + +test.run(stdout=expect) + +expect_aaa = 'aaa.k1 1\nline 2\nxxx 2\ninclude2 yyy\ninclude3 zzz\nline 6\n' + +test.must_match('bbb', expect_bbb) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/empty-implicit.py b/test/Scanner/empty-implicit.py new file mode 100644 index 0000000..0323240 --- /dev/null +++ b/test/Scanner/empty-implicit.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that Scanners are not called if a previous --implicit-cache +run stored an empty list of implicit dependencies. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', r""" +import os.path + +def scan(node, env, envkey, arg): + print 'XScanner: node =', os.path.split(str(node))[1] + return [] + +def exists_check(node, env): + return os.path.exists(str(node)) + +XScanner = Scanner(name = 'XScanner', + function = scan, + argument = None, + scan_check = exists_check, + skeys = ['.x']) + +def echo(env, target, source): + t = os.path.split(str(target[0]))[1] + s = os.path.split(str(source[0]))[1] + print 'create %s from %s' % (t, s) + open(t, 'wb').write(open(s, 'rb').read()) + +Echo = Builder(action = Action(echo, None), + src_suffix = '.x', + suffix = '.x') + +env = Environment(BUILDERS = {'Echo':Echo}, SCANNERS = [XScanner]) + +f1 = env.Echo(source=['f1_in'], target=['f1_out']) +""") + +test.write('f1_in.x', 'f1_in.x\n') + +expect = test.wrap_stdout("""\ +XScanner: node = f1_in.x +create f1_out.x from f1_in.x +""") + +test.run(arguments = '--implicit-cache .', stdout = expect) + +# Run it again, and the output must contain only the "up to date" message, +# *not* the line printed by the XScanner function. + +test.up_to_date(options = '--implicit-cache', arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/exception.py b/test/Scanner/exception.py new file mode 100644 index 0000000..1a14152 --- /dev/null +++ b/test/Scanner/exception.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import TestSCons + +python = TestSCons.python + +test = TestSCons.TestSCons() + +# Execute a subsidiary SConscript just to make sure we can +# get at the SCanners keyword from there. + +test.write('SConstruct', """ +SConscript('SConscript') +""") + +test.write('SConscript', """ +import re + +include_re = re.compile(r'^include\s+(\S+)$', re.M) +exception_re = re.compile(r'^exception\s+(.+)$', re.M) + +def kfile_scan(node, env, target, arg): + contents = node.get_text_contents() + exceptions = exception_re.findall(contents) + if exceptions: + raise Exception("kfile_scan error: %s" % exceptions[0]) + includes = include_re.findall(contents) + return includes + +kscan = Scanner(name = 'kfile', + function = kfile_scan, + argument = None, + recursive = 1, + skeys = ['.k']) + +def process(outf, inf): + for line in inf.readlines(): + if line[:8] == 'include ': + file = line[8:-1] + process(outf, open(file, 'rb')) + else: + outf.write(line) + +def cat(env, source, target): + target = str(target[0]) + outf = open(target, 'wb') + for src in source: + process(outf, open(str(src), 'rb')) + outf.close() + +env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env.Append(SCANNERS = [kscan]) + +env.Cat('foo', 'foo.k') + +bar_in = File('bar.in') +env.Cat('bar', bar_in) +""") + +test.write('foo.k', +"""foo.k 1 line 1 +include xxx +include yyy +foo.k 1 line 4 +""") + +test.write('bar.in', +"""include yyy +bar.in 1 line 2 +bar.in 1 line 3 +include zzz +""") + +test.write('xxx', "xxx 1\n") + +test.write('yyy', "exception yyy 1\n") + +test.write('zzz', "zzz 1\n") + +test.run(arguments = '.', + status = 2, + stderr = """\ +scons: *** [foo] Exception : kfile_scan error: yyy 1 +""") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/generated.py b/test/Scanner/generated.py new file mode 100644 index 0000000..845111c --- /dev/null +++ b/test/Scanner/generated.py @@ -0,0 +1,441 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that we only scan generated .h files once. + +This originated as a real-life bug report submitted by Scott Lystig +Fritchie. It's been left as-is, rather than stripped down to bare +minimum, partly because it wasn't completely clear what combination of +factors triggered the bug Scott saw, and partly because the real-world +complexity is valuable in its own right. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('reftree', + ['reftree', 'include'], + 'src', + ['src', 'lib_geng']) + +test.write('SConstruct', """\ +### +### QQQ !@#$!@#$! I need to move the SConstruct file to be "above" +### both the source and install dirs, or the install dependencies +### don't seem to work well! ARRGH!!!! +### + +experimenttop = r"%s" +import os +import Mylib + +BStaticLibMerge = Builder(generator = Mylib.Gen_StaticLibMerge) +builders = Environment().Dictionary('BUILDERS') +builders["StaticLibMerge"] = BStaticLibMerge + +env = Environment(BUILDERS = builders) +e = env.Dictionary() # Slightly easier to type + +global_env = env +e["GlobalEnv"] = global_env + +e["REF_INCLUDE"] = os.path.join(experimenttop, "reftree", "include") +e["REF_LIB"] = os.path.join(experimenttop, "reftree", "lib") +e["EXPORT_INCLUDE"] = os.path.join(experimenttop, "export", "include") +e["EXPORT_LIB"] = os.path.join(experimenttop, "export", "lib") +e["INSTALL_BIN"] = os.path.join(experimenttop, "install", "bin") + +variant_dir = os.path.join(experimenttop, "tmp-bld-dir") +src_dir = os.path.join(experimenttop, "src") + +env.Append(CPPPATH = [e["EXPORT_INCLUDE"]]) +env.Append(CPPPATH = [e["REF_INCLUDE"]]) +Mylib.AddLibDirs(env, "/via/Mylib.AddLibPath") +env.Append(LIBPATH = [e["EXPORT_LIB"]]) +env.Append(LIBPATH = [e["REF_LIB"]]) + +Mylib.Subdirs(env, "src") +""" % test.workpath()) + +test.write('Mylib.py', """\ +import os +import re + +def Subdirs(env, dirlist): + for file in _subconf_list(dirlist): + env.SConscript(file, "env") + +def _subconf_list(dirlist): + return [os.path.join(x, "SConscript") for x in dirlist.split()] + +def StaticLibMergeMembers(local_env, libname, hackpath, files): + for file in files.split(): + # QQQ Fix limits in grok'ed regexp + tmp = re.sub(".c$", ".o", file) + objname = re.sub(".cpp", ".o", tmp) + local_env.Object(target = objname, source = file) + e = 'local_env["GlobalEnv"].Append(%s = ["%s"])' % (libname, os.path.join(hackpath, objname)) + exec(e) + +def CreateMergedStaticLibrary(env, libname): + objpaths = env["GlobalEnv"][libname] + libname = "lib%s.a" % (libname) + env.StaticLibMerge(target = libname, source = objpaths) + +# I put the main body of the generator code here to avoid +# namespace problems +def Gen_StaticLibMerge(source, target, env, for_signature): + target_string = "" + for t in target: + target_string = str(t) + subdir = os.path.dirname(target_string) + srclist = [] + for src in source: + srclist.append(src) + return [["ar", "cq"] + target + srclist, ["ranlib"] + target] + +def StaticLibrary(env, target, source): + env.StaticLibrary(target, source.split()) + +def SharedLibrary(env, target, source): + env.SharedLibrary(target, source.split()) + +def ExportHeader(env, headers): + env.Install(dir = env["EXPORT_INCLUDE"], source = headers.split()) + +def ExportLib(env, libs): + env.Install(dir = env["EXPORT_LIB"], source = libs.split()) + +def InstallBin(env, bins): + env.Install(dir = env["INSTALL_BIN"], source = bins.split()) + +def Program(env, target, source): + env.Program(target, source.split()) + +def AddCFlags(env, str): + env.Append(CPPFLAGS = " " + str) + +# QQQ Synonym needed? +#def AddCFLAGS(env, str): +# AddCFlags(env, str) + +def AddIncludeDirs(env, str): + env.Append(CPPPATH = str.split()) + +def AddLibs(env, str): + env.Append(LIBS = str.split()) + +def AddLibDirs(env, str): + env.Append(LIBPATH = str.split()) + +""") + +test.write(['reftree', 'include', 'lib_a.h'], """\ +char *a_letter(void); +""") + +test.write(['reftree', 'include', 'lib_b.h'], """\ +char *b_letter(void); +""") + +test.write(['reftree', 'include', 'lib_ja.h'], """\ +char *j_letter_a(void); +""") + +test.write(['reftree', 'include', 'lib_jb.h.intentionally-moved'], """\ +char *j_letter_b(void); +""") + +test.write(['src', 'SConscript'], """\ +# --- Begin SConscript boilerplate --- +import Mylib +Import("env") + +#env = env.Clone() # Yes, clobber intentionally +#Make environment changes, such as: Mylib.AddCFlags(env, "-g -D_TEST") +#Mylib.Subdirs(env, "lib_a lib_b lib_mergej prog_x") +Mylib.Subdirs(env, "lib_geng") + +env = env.Clone() # Yes, clobber intentionally +# --- End SConscript boilerplate --- + +""") + +test.write(['src', 'lib_geng', 'SConscript'], """\ +# --- Begin SConscript boilerplate --- +import sys +import Mylib +Import("env") + +#env = env.Clone() # Yes, clobber intentionally +#Make environment changes, such as: Mylib.AddCFlags(env, "-g -D_TEST") +#Mylib.Subdirs(env, "foo_dir") + +env = env.Clone() # Yes, clobber intentionally +# --- End SConscript boilerplate --- + +Mylib.AddCFlags(env, "-DGOOFY_DEMO") +Mylib.AddIncludeDirs(env, ".") + +# Not part of Scott Lystig Fritchies's original stuff: +# On Windows, it's import to use the original test environment +# when we invoke SCons recursively. +import os +recurse_env = env.Clone() +recurse_env["ENV"] = os.environ + +# Icky code to set up process environment for "make" +# I really ought to drop this into Mylib.... + +fromdict = env.Dictionary() +todict = env["ENV"] +import SCons.Util +import re +for k in fromdict.keys(): + if k != "ENV" and k != "SCANNERS" and k != "CFLAGS" and k != "CXXFLAGS" \ + and not SCons.Util.is_Dict(fromdict[k]): + # The next line can fail on some systems because it would try to + # do env.subst on: + # $RMIC $RMICFLAGS -d ${TARGET.attributes.java_lookupdir} ... + # When $TARGET is None, so $TARGET.attributes would throw an + # exception, which SCons would turn into a UserError. They're + # not important for this test, so just catch 'em. + f = fromdict[k] + try: + todict[k] = env.subst(f) + except SCons.Errors.UserError: + pass +todict["CFLAGS"] = fromdict["CPPFLAGS"] + " " + \ + ' '.join(["-I" + x for x in env["CPPPATH"]]) + " " + \ + ' '.join(["-L" + x for x in env["LIBPATH"]]) +todict["CXXFLAGS"] = todict["CFLAGS"] + +generated_hdrs = "libg_gx.h libg_gy.h libg_gz.h" +static_hdrs = "libg_w.h" +#exported_hdrs = generated_hdrs + " " + static_hdrs +exported_hdrs = static_hdrs +lib_name = "g" +lib_fullname = env.subst("${LIBPREFIX}g${LIBSUFFIX}") +lib_srcs = "libg_1.c libg_2.c libg_3.c".split() +import re +lib_objs = [re.sub("\.c$", ".o", x) for x in lib_srcs] + +Mylib.ExportHeader(env, exported_hdrs) +Mylib.ExportLib(env, lib_fullname) + +# The following were the original commands from Scott Lystic Fritchie, +# making use of a shell script and a Makefile to build the library. +# These have been preserved, commented out below, but in order to make +# this test portable, we've replaced them with a Python script and a +# recursive invocation of SCons (!). +#cmd_both = "cd %s ; make generated ; make" % Dir(".") +#cmd_generated = "cd %s ; sh MAKE-HEADER.sh" % Dir(".") +#cmd_justlib = "cd %s ; make" % Dir(".") + +_ws = re.compile('\s') + +def escape(s): + if _ws.search(s): + s = '"' + s + '"' + return s + +cmd_generated = "%s $SOURCE" % escape(sys.executable) +cmd_justlib = "%s %s -C ${SOURCES[0].dir}" % (escape(sys.executable), + escape(sys.argv[0])) + +##### Deps appear correct ... but wacky scanning? +# Why? +# +# SCons bug?? + +env.Command(generated_hdrs.split(), + ["MAKE-HEADER.py"], + cmd_generated) +recurse_env.Command([lib_fullname] + lib_objs, + lib_srcs + (generated_hdrs + " " + static_hdrs).split(), + cmd_justlib) +""") + +test.write(['src', 'lib_geng', 'MAKE-HEADER.py'], """\ +#!/usr/bin/env python + +import os +import os.path +import sys + +# chdir to the directory in which this script lives +os.chdir(os.path.split(sys.argv[0])[0]) + +for h in ['libg_gx.h', 'libg_gy.h', 'libg_gz.h']: + open(h, 'w').write('') +""") + +test.write(['src', 'lib_geng', 'SConstruct'], """\ +import os + +Scanned = {} + +def write_out(file, dict): + f = open(file, 'wb') + for k in sorted(dict.keys()): + file = os.path.split(k)[1] + f.write(file + ": " + str(dict[k]) + "\\n") + f.close() + +# A hand-coded new-style class proxy to wrap the underlying C Scanner +# with a method that counts the calls. +# +# This is more complicated than it used to be with old-style classes +# because the .__*__() methods in new-style classes are not looked +# up on the instance, but resolve to the actual wrapped class methods, +# so we have to handle those directly. +class CScannerCounter(object): + def __init__(self, original_CScanner, *args, **kw): + self.original_CScanner = original_CScanner + def __cmp__(self, *args, **kw): + return self.original_CScanner.__cmp__(*args, **kw) + def __hash__(self, *args, **kw): + return self.original_CScanner.__hash__(*args, **kw) + def __str__(self, *args, **kw): + return self.original_CScanner.__str__(*args, **kw) + def __getattr__(self, *args, **kw): + return self.original_CScanner.__getattribute__(*args, **kw) + def __call__(self, node, *args, **kw): + global Scanned + n = str(node) + try: + Scanned[n] = Scanned[n] + 1 + except KeyError: + Scanned[n] = 1 + write_out(r'%s', Scanned) + return self.original_CScanner(node, *args, **kw) + +import SCons.Tool +MyCScanner = CScannerCounter(SCons.Script.CScanner) +SCons.Tool.SourceFileScanner.add_scanner('.c', MyCScanner) + +env = Environment(CPPPATH = ".") +l = env.StaticLibrary("g", Split("libg_1.c libg_2.c libg_3.c")) +Default(l) +""" % test.workpath('MyCScan.out')) + +# These were the original shell script and Makefile from SLF's original +# bug report. We're not using them--in order to make this script as +# portable as possible, we're using a Python script and a recursive +# invocation of SCons--but we're preserving them here for history. +#test.write(['src', 'lib_geng', 'MAKE-HEADER.sh'], """\ +##!/bin/sh +# +#exec touch $* +#""") +# +#test.write(['src', 'lib_geng', 'Makefile'], """\ +#all: libg.a +# +#GEN_HDRS = libg_gx.h libg_gy.h libg_gz.h +#STATIC_HDRS = libg_w.h +# +#$(GEN_HDRS): generated +# +#generated: MAKE-HEADER.sh +# sh ./MAKE-HEADER.sh $(GEN_HDRS) +# +#libg.a: libg_1.o libg_2.o libg_3.o +# ar r libg.a libg_1.o libg_2.o libg_3.o +# +#libg_1.c: $(STATIC_HDRS) $(GEN_HDRS) +#libg_2.c: $(STATIC_HDRS) $(GEN_HDRS) +#libg_3.c: $(STATIC_HDRS) $(GEN_HDRS) +# +#clean: +# -rm -f $(GEN_HDRS) +# -rm -f libg.a *.o core core.* +#""") + +test.write(['src', 'lib_geng', 'libg_w.h'], """\ +""") + +test.write(['src', 'lib_geng', 'libg_1.c'], """\ +#include <libg_w.h> +#include <libg_gx.h> + +int g_1() +{ + return 1; +} +""") + +test.write(['src', 'lib_geng', 'libg_2.c'], """\ +#include <libg_w.h> +#include <libg_gx.h> +#include <libg_gy.h> +#include <libg_gz.h> + +int g_2() +{ + return 2; +} +""") + +test.write(['src', 'lib_geng', 'libg_3.c'], """\ +#include <libg_w.h> +#include <libg_gx.h> + +int g_3() +{ + return 3; +} +""") + +test.run(stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) + +# Note that the generated .h files still get scanned twice, +# but that's really once each as a child of libg_1.o and libg_2.o. +# +# TODO(sgk): can the duplication be eliminated safely? Batch build +# support "eliminated" the duplication before in a way that broke a +# use case that ended up in test/Depends/no-Builder.py (issue 2647). + +test.must_match("MyCScan.out", """\ +libg_1.c: 1 +libg_2.c: 1 +libg_3.c: 1 +libg_gx.h: 2 +libg_gy.h: 1 +libg_gz.h: 1 +libg_w.h: 2 +""") + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/multi-env.py b/test/Scanner/multi-env.py new file mode 100644 index 0000000..3d9a62a --- /dev/null +++ b/test/Scanner/multi-env.py @@ -0,0 +1,122 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + + +""" +Verifies that using the same source file in different environments +will get the proper scanner for the environment being used. +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + + +test.write('SConstruct', """ +import re + +include_re = re.compile(r'^include\s+(\S+)$', re.M) +input_re = re.compile(r'^input\s+(\S+)$', re.M) + +scan1 = Scanner(name = 'Include', + function = lambda N,E,P,A: A.findall(N.get_text_contents()), + argument = include_re, + skeys = ['.inp']) + +scan2 = Scanner(name = 'Input', + function = lambda N,E,P,A: A.findall(N.get_text_contents()), + argument = input_re, + skeys = ['.inp']) + +env1 = Environment() +env2 = Environment() + +env1.Append(SCANNERS=scan1) +env2.Append(SCANNERS=scan2) + +env1.Command('frog.1', 'frog.inp', r'%(_python_)s do_incl.py $TARGET $SOURCES') +env2.Command('frog.2', 'frog.inp', r'%(_python_)s do_inp.py $TARGET $SOURCES') + +""" % locals()) + +process = r""" +import sys + +def process(infp, outfp): + prefix = '%(command)s ' + l = len(prefix) + for line in infp.readlines(): + if line[:l] == prefix: + process(open(line[l:-1], 'rb'), outfp) + else: + outfp.write(line) + +process(open(sys.argv[2], 'rb'), + open(sys.argv[1], 'wb')) +sys.exit(0) +""" + +test.write('do_incl.py', process % { 'command' : 'include' }) +test.write('do_inp.py', process % { 'command' : 'input' }) + +test.write('frog.inp', """\ +include sound1 +input sound2 +""") + +test.write('sound1', 'croak\n') +test.write('sound2', 'ribbet\n') + +expect = test.wrap_stdout("""\ +%(_python_)s do_incl.py frog.1 frog.inp +%(_python_)s do_inp.py frog.2 frog.inp +""" % locals()) + +test.run(arguments='.', stdout=expect) + +test.must_match('frog.1', 'croak\ninput sound2\n') +test.must_match('frog.2', 'include sound1\nribbet\n') + +test.write('sound2', 'rudeep\n') + +expect = test.wrap_stdout("""\ +%(_python_)s do_inp.py frog.2 frog.inp +""" % locals()) + +test.run(arguments='.', stdout=expect) + +test.must_match('frog.1', 'croak\ninput sound2\n') +test.must_match('frog.2', 'include sound1\nrudeep\n') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/no-Dir-node.py b/test/Scanner/no-Dir-node.py new file mode 100644 index 0000000..3a918bf --- /dev/null +++ b/test/Scanner/no-Dir-node.py @@ -0,0 +1,139 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that use of a Scanner that searches a *PATH list doesn't create +nodes for directories that don't exist, so they don't get picked up +by DirScanner. + +Under the covers, this tests the behavior of the SCons.Node.FS.find_file() +utility function that is used by the Scanner.Classic class to search +directories in variables such as $CPPPATH. +""" + +import os.path + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +subdir_SConscript = os.path.join('subdir', 'SConscript') +subdir_foo = os.path.join('subdir', 'foo') +subdir_foo_k = os.path.join('subdir', 'foo.k') + +test.subdir('subdir', 'inc1', 'inc2') + +inc2_include_h = test.workpath('inc2', 'include.h') + +test.write('build.py', r""" +import os.path +import sys +path = sys.argv[1].split() +input = open(sys.argv[2], 'rb') +output = open(sys.argv[3], 'wb') + +def find_file(f): + if os.path.isabs(f): + return open(f, 'rb') + for dir in path: + p = dir + os.sep + f + if os.path.exists(p): + return open(p, 'rb') + return None + +def process(infp, outfp): + for line in infp.readlines(): + if line[:8] == 'include ': + file = line[8:-1] + process(find_file(file), outfp) + else: + outfp.write(line) + +process(input, output) + +sys.exit(0) +""") + +test.write('SConstruct', """\ +def foo(target, source, env): + fp = open(str(target[0]), 'wb') + for c in sorted(source[0].children(), key=lambda t: t.name): + fp.write('%s\\n' % c) + fp.close() +Command('list.out', 'subdir', foo, source_scanner = DirScanner) +SConscript('subdir/SConscript') +""") + +test.write(['subdir', 'SConscript'], """\ +import SCons.Scanner +kscan = SCons.Scanner.Classic(name = 'kfile', + suffixes = ['.k'], + path_variable = 'KPATH', + regex = r'^include\s+(\S+)$') + +env = Environment(KPATH=['.', '..']) +env.Append(SCANNERS = kscan) + +env.Command('foo', 'foo.k', r'%(_python_)s build.py "$KPATH" $SOURCES $TARGET') +""" % locals()) + +test.write(['subdir', 'foo.k'], """\ +subdir/foo.k +include inc1/include.h +include %(inc2_include_h)s +""" % locals()) + +test.write(['inc1', 'include.h'], """\ +inc1/include.h +""") + +test.write(['inc2', 'include.h'], """\ +inc2/include.h +""") + +test.run(arguments = '.') + +test.must_match('subdir/foo', """\ +subdir/foo.k +inc1/include.h +inc2/include.h +""") + +test.must_match('list.out', """\ +%(subdir_SConscript)s +%(subdir_foo)s +%(subdir_foo_k)s +""" % locals()) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/parallel-rescan.py b/test/Scanner/parallel-rescan.py new file mode 100644 index 0000000..9f8e3f1 --- /dev/null +++ b/test/Scanner/parallel-rescan.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that when a source file is generated and the -j option is used, +the source file correctly gets re-scanned for implicit dependencies +after it's built. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +env = Environment() +env['BUILDERS']['COPY'] = Builder(action = Copy("$TARGET", "$SOURCE")) + +env.COPY('a.c', 'a.in') +env.COPY('b.c', 'b.in') + +env.StaticLibrary('lib', ['a.c', 'b.c']) +""") + +test.write("a.in", """\ +#include "a.h" +""") + +test.write("b.in", """\ +#include "b.h" +""") + +test.write("a.h", """\ +char *A_FILE = "b.in"; +""") + +test.write("b.h", """\ +char *B_FILE = "b.in"; +""") + +test.run(arguments = '-j4 .', + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) + +# If the dependencies weren't re-scanned properly, the .h files won't +# show up in the previous run's dependency lists, and the .o files and +# library will get rebuilt here. +test.up_to_date(arguments = '.') + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/scan-once.py b/test/Scanner/scan-once.py new file mode 100644 index 0000000..d68f918 --- /dev/null +++ b/test/Scanner/scan-once.py @@ -0,0 +1,100 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that Scanners are called just once. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', r""" +import os.path + +def scan(node, env, envkey, arg): + print 'XScanner: node =', os.path.split(str(node))[1] + return [] + +def exists_check(node, env): + return os.path.exists(str(node)) + +XScanner = Scanner(name = 'XScanner', + function = scan, + argument = None, + scan_check = exists_check, + skeys = ['.x']) + +def echo(env, target, source): + t = os.path.split(str(target[0]))[1] + s = os.path.split(str(source[0]))[1] + print 'create %s from %s' % (t, s) + +Echo = Builder(action = Action(echo, None), + src_suffix = '.x', + suffix = '.x') + +env = Environment(BUILDERS = {'Echo':Echo}, SCANNERS = [XScanner]) + +f1 = env.Echo(source=['file1'], target=['file2']) +f2 = env.Echo(source=['file2'], target=['file3']) +f3 = env.Echo(source=['file3'], target=['file4']) +""") + +test.write('file1.x', 'file1.x\n') + +test.run(stdout = test.wrap_stdout("""\ +XScanner: node = file1.x +create file2.x from file1.x +create file3.x from file2.x +create file4.x from file3.x +""")) + +test.write('file2.x', 'file2.x\n') + +test.run(stdout = test.wrap_stdout("""\ +XScanner: node = file1.x +XScanner: node = file2.x +create file3.x from file2.x +create file4.x from file3.x +""")) + +test.write('file3.x', 'file3.x\n') + +test.run(stdout = test.wrap_stdout("""\ +XScanner: node = file1.x +XScanner: node = file2.x +XScanner: node = file3.x +create file4.x from file3.x +""")) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/source_scanner-dict.py b/test/Scanner/source_scanner-dict.py new file mode 100644 index 0000000..f796368 --- /dev/null +++ b/test/Scanner/source_scanner-dict.py @@ -0,0 +1,224 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify that a source_scanner that uses a dictionary to select more +specific scanners for source file suffixes works correctly, even +when it's handed a file suffix that it doesn't know how to scan +(i.e., for which it doesn't have a specific scanner in its dictionary). +""" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +test.write('build.py', r""" +import sys +output = open(sys.argv[1], 'wb') +for infile in sys.argv[2:]: + input = open(infile, 'rb') + + include_prefix = 'include%s ' % infile[-1] + + def process(infp, outfp, include_prefix=include_prefix): + for line in infp.readlines(): + if line[:len(include_prefix)] == include_prefix: + file = line[len(include_prefix):-1] + process(open(file, 'rb'), outfp) + else: + outfp.write(line) + + process(input, output) + +sys.exit(0) +""") + +# Execute a subsidiary SConscript just to make sure we can +# get at the Scanner keyword from there. + +test.write('SConstruct', """ +SConscript('SConscript') +""") + +test.write('SConscript', """ +import re + +include1_re = re.compile(r'^include1\s+(\S+)$', re.M) +include2_re = re.compile(r'^include2\s+(\S+)$', re.M) +include3_re = re.compile(r'^include3\s+(\S+)$', re.M) + +def k1_scan(node, env, scanpaths, arg=None): + contents = node.get_text_contents() + includes = include1_re.findall(contents) + return includes + +def k2_scan(node, env, scanpaths, arg=None): + contents = node.get_text_contents() + includes = include2_re.findall(contents) + return includes + +def k3_scan(node, env, scanpaths, arg=None): + contents = node.get_text_contents() + includes = include3_re.findall(contents) + return includes + +kscanner = Scanner({'.k1' : Scanner(k1_scan), '.k2': Scanner(k2_scan)}) + +b = Builder(action=r'%(_python_)s build.py $TARGET $SOURCES', + source_scanner=kscanner) +env = Environment(BUILDERS={'Build':b}) + +kscanner.add_scanner('.k3', Scanner(k3_scan)) + +env.Build('aaa', 'aaa.k1') +env.Build('bbb', 'bbb.k2') +env.Build('ccc', 'ccc.k3') +env.Build('ddd', ['ddd.k4', 'aaa.k1', 'bbb.k2', 'ccc.k3']) +""" % locals()) + +test.write('aaa.k1', +"""aaa.k1 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('bbb.k2', +"""bbb.k2 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('ccc.k3', +"""ccc.k3 1 +line 2 +include1 xxx +include2 yyy +include3 zzz +line 6 +""") + +test.write('ddd.k4', +"""ddd.k4 1 +line 2 +line 3 +""") + +test.write('xxx', "xxx 1\n") +test.write('yyy', "yyy 1\n") +test.write('zzz', "zzz 1\n") + + + + +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa aaa.k1 +%(_python_)s build.py bbb bbb.k2 +%(_python_)s build.py ccc ccc.k3 +%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3 +""" % locals()) + +test.run(stdout=expect) + +expect_aaa = 'aaa.k1 1\nline 2\nxxx 1\ninclude2 yyy\ninclude3 zzz\nline 6\n' +expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 1\ninclude3 zzz\nline 6\n' +expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 1\nline 6\n' +expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc + +test.must_match('aaa', expect_aaa) +test.must_match('bbb', expect_bbb) +test.must_match('ccc', expect_ccc) +test.must_match('ddd', expect_ddd) + +test.up_to_date(arguments = '.') + + + +test.write('zzz', "zzz 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py ccc ccc.k3 +%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3 +""" % locals()) + +test.run(stdout=expect) + +expect_ccc = 'ccc.k3 1\nline 2\ninclude1 xxx\ninclude2 yyy\nzzz 2\nline 6\n' +expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc + +test.must_match('bbb', expect_bbb) +test.must_match('ddd', expect_ddd) + + + +test.write('yyy', "yyy 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py bbb bbb.k2 +%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3 +""" % locals()) + +test.run(stdout=expect) + +expect_bbb = 'bbb.k2 1\nline 2\ninclude1 xxx\nyyy 2\ninclude3 zzz\nline 6\n' +expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc + +test.must_match('bbb', expect_bbb) +test.must_match('ddd', expect_ddd) + + + +test.write('xxx', "xxx 2\n") + +expect = test.wrap_stdout("""\ +%(_python_)s build.py aaa aaa.k1 +%(_python_)s build.py ddd ddd.k4 aaa.k1 bbb.k2 ccc.k3 +""" % locals()) + +test.run(stdout=expect) + +expect_aaa = 'aaa.k1 1\nline 2\nxxx 2\ninclude2 yyy\ninclude3 zzz\nline 6\n' +expect_ddd = 'ddd.k4 1\nline 2\nline 3\n' + expect_aaa + expect_bbb + expect_ccc + +test.must_match('aaa', expect_aaa) +test.must_match('ddd', expect_ddd) + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Scanner/unicode.py b/test/Scanner/unicode.py new file mode 100644 index 0000000..55e22bd --- /dev/null +++ b/test/Scanner/unicode.py @@ -0,0 +1,277 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +""" +Verify that we can scan Unicode-encoded files for implicit +dependencies. +""" + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import TestSCons + +_python_ = TestSCons._python_ + +test = TestSCons.TestSCons() + +try: + unicode +except NameError: + import sys + msg = "Unicode not supported by Python version %s; skipping test\n" + test.skip_test(msg % sys.version[:3]) + +import codecs + +test.write('build.py', r""" +import codecs +import sys + +def process(outfp, infile): + contents = open(infile, 'rb').read() + if contents.startswith(codecs.BOM_UTF8): + contents = contents[len(codecs.BOM_UTF8):].decode('utf-8') + elif contents.startswith(codecs.BOM_UTF16_LE): + contents = contents[len(codecs.BOM_UTF16_LE):].decode('utf-16-le') + elif contents.startswith(codecs.BOM_UTF16_BE): + contents = contents[len(codecs.BOM_UTF16_BE):].decode('utf-16-be') + for line in contents.split('\n')[:-1]: + if line[:8] == 'include ': + process(outfp, line[8:]) + elif line[:8] == 'getfile ': + outfp.write('include ' + line[8:] + '\n') + # note: converted, but not acted upon + else: + outfp.write(line + '\n') + +output = open(sys.argv[2], 'wb') +process(output, sys.argv[1]) + +sys.exit(0) +""") + +test.write('SConstruct', """ +import re + +include_re = re.compile(r'^include\s+(\S+)$', re.M) + +def kfile_scan(node, env, scanpaths, arg): + contents = node.get_text_contents() + includes = include_re.findall(contents) + return includes + +kscan = Scanner(name = 'kfile', + function = kfile_scan, + argument = None, + skeys = ['.k'], + recursive = True) + +env = Environment() +env.Append(SCANNERS = kscan) + +env.Command('foo', 'foo.k', r'%(_python_)s build.py $SOURCES $TARGET') +""" % locals()) + +test.write('foo.k', """\ +foo.k 1 line 1 +include ascii.k +include utf8.k +include utf16le.k +include utf16be.k +foo.k 1 line 4 +""") + +contents = unicode("""\ +ascii.k 1 line 1 +include ascii.inc +ascii.k 1 line 3 +""") +test.write('ascii.k', contents.encode('ascii')) + +contents = unicode("""\ +utf8.k 1 line 1 +include utf8.inc +utf8.k 1 line 3 +""") +test.write('utf8.k', codecs.BOM_UTF8 + contents.encode('utf-8')) + +contents = unicode("""\ +utf16le.k 1 line 1 +include utf16le.inc +utf16le.k 1 line 3 +""") +test.write('utf16le.k', codecs.BOM_UTF16_LE + contents.encode('utf-16-le')) + +contents = unicode("""\ +utf16be.k 1 line 1 +include utf16be.inc +utf16be.k 1 line 3 +""") +test.write('utf16be.k', codecs.BOM_UTF16_BE + contents.encode('utf-16-be')) + +test.write('ascii.inc', "ascii.inc 1\n") +test.write('utf8.inc', "utf8.inc 1\n") +test.write('utf16le.inc', "utf16le.inc 1\n") +test.write('utf16be.inc', "utf16be.inc 1\n") + +test.run(arguments='foo') + +expect = """\ +foo.k 1 line 1 +ascii.k 1 line 1 +ascii.inc 1 +ascii.k 1 line 3 +utf8.k 1 line 1 +utf8.inc 1 +utf8.k 1 line 3 +utf16le.k 1 line 1 +utf16le.inc 1 +utf16le.k 1 line 3 +utf16be.k 1 line 1 +utf16be.inc 1 +utf16be.k 1 line 3 +foo.k 1 line 4 +""" + +test.must_match('foo', expect) + +test.up_to_date(arguments='foo') + + + +test.write('ascii.inc', "ascii.inc 2\n") + +test.not_up_to_date(arguments = 'foo') + +expect = """\ +foo.k 1 line 1 +ascii.k 1 line 1 +ascii.inc 2 +ascii.k 1 line 3 +utf8.k 1 line 1 +utf8.inc 1 +utf8.k 1 line 3 +utf16le.k 1 line 1 +utf16le.inc 1 +utf16le.k 1 line 3 +utf16be.k 1 line 1 +utf16be.inc 1 +utf16be.k 1 line 3 +foo.k 1 line 4 +""" + +test.must_match('foo', expect) + +test.up_to_date(arguments = 'foo') + + + +test.write('utf8.inc', "utf8.inc 2\n") + +test.not_up_to_date(arguments = 'foo') + +expect = """\ +foo.k 1 line 1 +ascii.k 1 line 1 +ascii.inc 2 +ascii.k 1 line 3 +utf8.k 1 line 1 +utf8.inc 2 +utf8.k 1 line 3 +utf16le.k 1 line 1 +utf16le.inc 1 +utf16le.k 1 line 3 +utf16be.k 1 line 1 +utf16be.inc 1 +utf16be.k 1 line 3 +foo.k 1 line 4 +""" + +test.must_match('foo', expect) + +test.up_to_date(arguments = 'foo') + + + +test.write('utf16le.inc', "utf16le.inc 2\n") + +test.not_up_to_date(arguments = 'foo') + +expect = """\ +foo.k 1 line 1 +ascii.k 1 line 1 +ascii.inc 2 +ascii.k 1 line 3 +utf8.k 1 line 1 +utf8.inc 2 +utf8.k 1 line 3 +utf16le.k 1 line 1 +utf16le.inc 2 +utf16le.k 1 line 3 +utf16be.k 1 line 1 +utf16be.inc 1 +utf16be.k 1 line 3 +foo.k 1 line 4 +""" + +test.must_match('foo', expect) + +test.up_to_date(arguments = 'foo') + + + +test.write('utf16be.inc', "utf16be.inc 2\n") + +test.not_up_to_date(arguments = 'foo') + +expect = """\ +foo.k 1 line 1 +ascii.k 1 line 1 +ascii.inc 2 +ascii.k 1 line 3 +utf8.k 1 line 1 +utf8.inc 2 +utf8.k 1 line 3 +utf16le.k 1 line 1 +utf16le.inc 2 +utf16le.k 1 line 3 +utf16be.k 1 line 1 +utf16be.inc 2 +utf16be.k 1 line 3 +foo.k 1 line 4 +""" + +test.must_match('foo', expect) + +test.up_to_date(arguments = 'foo') + + + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: |
