diff options
Diffstat (limited to 'test/Scanner')
-rw-r--r-- | test/Scanner/Scanner.py | 240 | ||||
-rw-r--r-- | test/Scanner/empty-implicit.py | 81 | ||||
-rw-r--r-- | test/Scanner/exception.py | 115 | ||||
-rw-r--r-- | test/Scanner/generated.py | 427 | ||||
-rw-r--r-- | test/Scanner/multi-env.py | 116 | ||||
-rw-r--r-- | test/Scanner/parallel-rescan.py | 72 | ||||
-rw-r--r-- | test/Scanner/scan-once.py | 94 |
7 files changed, 1145 insertions, 0 deletions
diff --git a/test/Scanner/Scanner.py b/test/Scanner/Scanner.py new file mode 100644 index 0000000..09c5680 --- /dev/null +++ b/test/Scanner/Scanner.py @@ -0,0 +1,240 @@ +#!/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_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.Copy() +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 + +import string + +def blork(env, target, source): + open(str(target[0]), 'wb').write( + string.replace(source[0].get_contents(), '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() diff --git a/test/Scanner/empty-implicit.py b/test/Scanner/empty-implicit.py new file mode 100644 index 0000000..2112fd8 --- /dev/null +++ b/test/Scanner/empty-implicit.py @@ -0,0 +1,81 @@ +#!/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() diff --git a/test/Scanner/exception.py b/test/Scanner/exception.py new file mode 100644 index 0000000..7615888 --- /dev/null +++ b/test/Scanner/exception.py @@ -0,0 +1,115 @@ +#!/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 string +import sys +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_contents() + exceptions = exception_re.findall(contents) + if exceptions: + raise "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]) + source = map(str, source) + + outf = open(target, 'wb') + for src in source: + process(outf, open(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) +bar_in.source_scanner = kscan +""") + +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: *** kfile_scan error: yyy 1 +""") + +test.pass_test() diff --git a/test/Scanner/generated.py b/test/Scanner/generated.py new file mode 100644 index 0000000..36f35f6 --- /dev/null +++ b/test/Scanner/generated.py @@ -0,0 +1,427 @@ +#!/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 bear +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 os.path +import sys + +import TestCmd +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 os.path +import string +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") + +build_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 string +import re + +def Subdirs(env, dirlist): + for file in _subconf_list(dirlist): + env.SConscript(file, "env") + +def _subconf_list(dirlist): + return map(lambda x: os.path.join(x, "SConscript"), string.split(dirlist)) + +def StaticLibMergeMembers(local_env, libname, hackpath, files): + for file in string.split(files): + # 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, string.split(source)) + +def SharedLibrary(env, target, source): + env.SharedLibrary(target, string.split(source)) + +def ExportHeader(env, headers): + env.Install(dir = env["EXPORT_INCLUDE"], source = string.split(headers)) + +def ExportLib(env, libs): + env.Install(dir = env["EXPORT_LIB"], source = string.split(libs)) + +def InstallBin(env, bins): + env.Install(dir = env["INSTALL_BIN"], source = string.split(bins)) + +def Program(env, target, source): + env.Program(target, string.split(source)) + +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 = string.split(str)) + +def AddLibs(env, str): + env.Append(LIBS = string.split(str)) + +def AddLibDirs(env, str): + env.Append(LIBPATH = string.split(str)) + +""") + +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.Copy() # 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.Copy() # Yes, clobber intentionally +# --- End SConscript boilerplate --- + +""") + +test.write(['src', 'lib_geng', 'SConscript'], """\ +# --- Begin SConscript boilerplate --- +import string +import sys +import Mylib +Import("env") + +#env = env.Copy() # Yes, clobber intentionally +#Make environment changes, such as: Mylib.AddCFlags(env, "-g -D_TEST") +#Mylib.Subdirs(env, "foo_dir") + +env = env.Copy() # 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.Copy() +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. + f = fromdict[k] + if SCons.Util.is_String(f) and \ + string.find(f, "TARGET") == -1 and \ + string.find(f, "SOURCE") == -1: + todict[k] = env.subst(f) +todict["CFLAGS"] = fromdict["CPPFLAGS"] + " " + \ + string.join(map(lambda x: "-I" + x, env["CPPPATH"])) + " " + \ + string.join(map(lambda x: "-L" + x, 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 = string.split("libg_1.c libg_2.c libg_3.c") +import re +lib_objs = map(lambda x: re.sub("\.c$", ".o", x), 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(string.split(generated_hdrs), + ["MAKE-HEADER.py"], + cmd_generated) +recurse_env.Command([lib_fullname] + lib_objs, + lib_srcs + string.split(generated_hdrs + " " + static_hdrs), + 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): + keys = dict.keys() + keys.sort() + f = open(file, 'wb') + for k in keys: + file = os.path.split(k)[1] + f.write(file + ": " + str(dict[k]) + "\\n") + f.close() + +orig_function = CScan.scan + +def MyCScan(node, paths, orig_function=orig_function): + deps = orig_function(node, paths) + + global Scanned + n = str(node) + try: + Scanned[n] = Scanned[n] + 1 + except KeyError: + Scanned[n] = 1 + write_out(r'%s', Scanned) + + return deps + +CScan.scan = MyCScan + +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) + +# XXX Note that the generated .h files still get scanned twice, +# once before they're generated and once after. That's the +# next thing to fix here. + +test.must_match("MyCScan.out", """\ +libg_1.c: 1 +libg_2.c: 1 +libg_3.c: 1 +libg_gx.h: 1 +libg_gy.h: 1 +libg_gz.h: 1 +libg_w.h: 1 +""") + +test.pass_test() diff --git a/test/Scanner/multi-env.py b/test/Scanner/multi-env.py new file mode 100644 index 0000000..514fbca --- /dev/null +++ b/test/Scanner/multi-env.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__" + + +""" +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_contents()), + argument = include_re, + skeys = ['.inp']) + +scan2 = Scanner(name = 'Input', + function = lambda N,E,P,A: A.findall(N.get_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() diff --git a/test/Scanner/parallel-rescan.py b/test/Scanner/parallel-rescan.py new file mode 100644 index 0000000..4a1f27e --- /dev/null +++ b/test/Scanner/parallel-rescan.py @@ -0,0 +1,72 @@ +#!/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() diff --git a/test/Scanner/scan-once.py b/test/Scanner/scan-once.py new file mode 100644 index 0000000..7e02dd2 --- /dev/null +++ b/test/Scanner/scan-once.py @@ -0,0 +1,94 @@ +#!/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() |