summaryrefslogtreecommitdiffstats
path: root/test/BuildDir
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2005-03-05 15:25:36 (GMT)
committerSteven Knight <knight@baldmt.com>2005-03-05 15:25:36 (GMT)
commit81e49da9da4df0dab0041dbc2d499048f38ed4f4 (patch)
tree6a6a9c1d857263a41a7185db0f4e5f49c35cc18e /test/BuildDir
parent7504c9d138b5a0a43c75918dc39614af12a7439a (diff)
downloadSCons-81e49da9da4df0dab0041dbc2d499048f38ed4f4.zip
SCons-81e49da9da4df0dab0041dbc2d499048f38ed4f4.tar.gz
SCons-81e49da9da4df0dab0041dbc2d499048f38ed4f4.tar.bz2
Fix a regression in handling CPPPATH='.' when using a BuildDir with subdirectories in the search path.
Diffstat (limited to 'test/BuildDir')
-rw-r--r--test/BuildDir/BuildDir.py393
-rw-r--r--test/BuildDir/CPPPATH-subdir.py70
-rw-r--r--test/BuildDir/Sconscript-build_dir.py263
-rw-r--r--test/BuildDir/errors.py171
-rw-r--r--test/BuildDir/reflect.py136
5 files changed, 1033 insertions, 0 deletions
diff --git a/test/BuildDir/BuildDir.py b/test/BuildDir/BuildDir.py
new file mode 100644
index 0000000..181d8aa
--- /dev/null
+++ b/test/BuildDir/BuildDir.py
@@ -0,0 +1,393 @@
+#!/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 os.path
+import string
+import sys
+import time
+import TestSCons
+
+_exe = TestSCons._exe
+fortran_runtime = TestSCons.fortran_lib
+
+test = TestSCons.TestSCons()
+
+fortran = test.detect('FORTRAN')
+
+foo11 = test.workpath('work1', 'build', 'var1', 'foo1' + _exe)
+foo12 = test.workpath('work1', 'build', 'var1', 'foo2' + _exe)
+foo21 = test.workpath('work1', 'build', 'var2', 'foo1' + _exe)
+foo22 = test.workpath('work1', 'build', 'var2', 'foo2' + _exe)
+foo31 = test.workpath('work1', 'build', 'var3', 'foo1' + _exe)
+foo32 = test.workpath('work1', 'build', 'var3', 'foo2' + _exe)
+foo41 = test.workpath('work1', 'build', 'var4', 'foo1' + _exe)
+foo42 = test.workpath('work1', 'build', 'var4', 'foo2' + _exe)
+foo51 = test.workpath('build', 'var5', 'foo1' + _exe)
+foo52 = test.workpath('build', 'var5', 'foo2' + _exe)
+
+bar11 = test.workpath('work1', 'build', 'var1', 'bar1' + _exe)
+bar12 = test.workpath('work1', 'build', 'var1', 'bar2' + _exe)
+bar21 = test.workpath('work1', 'build', 'var2', 'bar1' + _exe)
+bar22 = test.workpath('work1', 'build', 'var2', 'bar2' + _exe)
+bar31 = test.workpath('work1', 'build', 'var3', 'bar1' + _exe)
+bar32 = test.workpath('work1', 'build', 'var3', 'bar2' + _exe)
+bar41 = test.workpath('work1', 'build', 'var4', 'bar1' + _exe)
+bar42 = test.workpath('work1', 'build', 'var4', 'bar2' + _exe)
+bar51 = test.workpath('build', 'var5', 'bar1' + _exe)
+bar52 = test.workpath('build', 'var5', 'bar2' + _exe)
+
+test.subdir('work1', 'work2', 'work3')
+
+test.write(['work1', 'SConstruct'], """
+src = Dir('src')
+var2 = Dir('build/var2')
+var3 = Dir('build/var3')
+var4 = Dir('build/var4')
+var5 = Dir('../build/var5')
+var6 = Dir('../build/var6')
+
+env = Environment(BUILD = 'build', SRC = 'src')
+
+BuildDir('build/var1', src)
+BuildDir(var2, src)
+BuildDir(var3, src, duplicate=0)
+env.BuildDir("$BUILD/var4", "$SRC", duplicate=0)
+BuildDir(var5, src, duplicate=0)
+BuildDir(var6, src)
+
+env = Environment(CPPPATH='#src', FORTRANPATH='#src')
+SConscript('build/var1/SConscript', "env")
+SConscript('build/var2/SConscript', "env")
+
+env = Environment(CPPPATH=src, FORTRANPATH=src)
+SConscript('build/var3/SConscript', "env")
+SConscript(File('SConscript', var4), "env")
+
+env = Environment(CPPPATH='.', FORTRANPATH='.')
+SConscript('../build/var5/SConscript', "env")
+SConscript('../build/var6/SConscript', "env")
+""")
+
+test.subdir(['work1', 'src'])
+test.write(['work1', 'src', 'SConscript'], """
+import os
+import os.path
+
+def buildIt(target, source, env):
+ if not os.path.exists('build'):
+ os.mkdir('build')
+ f1=open(str(source[0]), 'r')
+ f2=open(str(target[0]), 'w')
+ f2.write(f1.read())
+ f2.close()
+ f1.close()
+ return 0
+Import("env")
+env.Command(target='f2.c', source='f2.in', action=buildIt)
+env.Program(target='foo2', source='f2.c')
+env.Program(target='foo1', source='f1.c')
+env.Command(target='f3.h', source='f3h.in', action=buildIt)
+env.Command(target='f4.h', source='f4h.in', action=buildIt)
+env.Command(target='f4.c', source='f4.in', action=buildIt)
+
+env2=env.Copy(CPPPATH='.')
+env2.Program(target='foo3', source='f3.c')
+env2.Program(target='foo4', source='f4.c')
+
+try:
+ fortran = env.subst('$FORTRAN')
+except:
+ fortran = None
+
+if fortran and env.Detect(fortran):
+ env.Command(target='b2.f', source='b2.in', action=buildIt)
+ env.Copy(LIBS = %s).Program(target='bar2', source='b2.f')
+ env.Copy(LIBS = %s).Program(target='bar1', source='b1.f')
+""" % (fortran_runtime, fortran_runtime))
+
+test.write(['work1', 'src', 'f1.c'], r"""
+#include "f1.h"
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf(F1_STR);
+ exit (0);
+}
+""")
+
+test.write(['work1', 'src', 'f2.in'], r"""
+#include "f2.h"
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf(F2_STR);
+ exit (0);
+}
+""")
+
+test.write(['work1', 'src', 'f3.c'], r"""
+#include "f3.h"
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf(F3_STR);
+ exit (0);
+}
+""")
+
+test.write(['work1', 'src', 'f4.in'], r"""
+#include "f4.h"
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf(F4_STR);
+ exit (0);
+}
+""")
+
+test.write(['work1', 'src', 'f1.h'], r"""
+#define F1_STR "f1.c\n"
+""")
+
+test.write(['work1', 'src', 'f2.h'], r"""
+#define F2_STR "f2.c\n"
+""")
+
+test.write(['work1', 'src', 'f3h.in'], r"""
+#define F3_STR "f3.c\n"
+""")
+
+test.write(['work1', 'src', 'f4h.in'], r"""
+#define F4_STR "f4.c\n"
+""")
+
+test.write(['work1', 'src', 'b1.f'], r"""
+ PROGRAM FOO
+ INCLUDE 'b1.for'
+ STOP
+ END
+""")
+
+test.write(['work1', 'src', 'b2.in'], r"""
+ PROGRAM FOO
+ INCLUDE 'b2.for'
+ STOP
+ END
+""")
+
+test.write(['work1', 'src', 'b1.for'], r"""
+ PRINT *, 'b1.for'
+""")
+
+test.write(['work1', 'src', 'b2.for'], r"""
+ PRINT *, 'b2.for'
+""")
+
+# Some releases of freeBSD seem to have library complaints about
+# tempnam(). Filter out these annoying messages before checking for
+# error output.
+def blank_output(err):
+ if not err:
+ return 1
+ stderrlines = filter(lambda l: l, string.split(err, '\n'))
+ msg = "warning: tempnam() possibly used unsafely"
+ stderrlines = filter(lambda l, msg=msg: string.find(l, msg) == -1,
+ stderrlines)
+ return len(stderrlines) == 0
+
+test.run(chdir='work1', arguments = '. ../build', stderr=None)
+
+test.fail_test(not blank_output(test.stderr()))
+
+test.run(program = foo11, stdout = "f1.c\n")
+test.run(program = foo12, stdout = "f2.c\n")
+test.run(program = foo21, stdout = "f1.c\n")
+test.run(program = foo22, stdout = "f2.c\n")
+test.run(program = foo31, stdout = "f1.c\n")
+test.run(program = foo32, stdout = "f2.c\n")
+test.run(program = foo41, stdout = "f1.c\n")
+test.run(program = foo42, stdout = "f2.c\n")
+test.run(program = foo51, stdout = "f1.c\n")
+test.run(program = foo52, stdout = "f2.c\n")
+
+if fortran:
+ test.run(program = bar11, stdout = " b1.for\n")
+ test.run(program = bar12, stdout = " b2.for\n")
+ test.run(program = bar21, stdout = " b1.for\n")
+ test.run(program = bar22, stdout = " b2.for\n")
+ test.run(program = bar31, stdout = " b1.for\n")
+ test.run(program = bar32, stdout = " b2.for\n")
+ test.run(program = bar41, stdout = " b1.for\n")
+ test.run(program = bar42, stdout = " b2.for\n")
+ test.run(program = bar51, stdout = " b1.for\n")
+ test.run(program = bar52, stdout = " b2.for\n")
+
+test.run(chdir='work1', arguments='. ../build', stdout=test.wrap_stdout("""\
+scons: `.' is up to date.
+scons: `%s' is up to date.
+""" % test.workpath('build')))
+
+import os
+import stat
+def equal_stats(x,y):
+ x = os.stat(x)
+ y = os.stat(y)
+ return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and
+ x[stat.ST_MTIME] == y[stat.ST_MTIME])
+
+# Make sure we did duplicate the source files in build/var2,
+# and that their stats are the same:
+test.must_exist(['work1', 'build', 'var2', 'f1.c'])
+test.must_exist(['work1', 'build', 'var2', 'f2.in'])
+test.fail_test(not equal_stats(test.workpath('work1', 'build', 'var2', 'f1.c'), test.workpath('work1', 'src', 'f1.c')))
+test.fail_test(not equal_stats(test.workpath('work1', 'build', 'var2', 'f2.in'), test.workpath('work1', 'src', 'f2.in')))
+
+# Make sure we didn't duplicate the source files in build/var3.
+test.must_not_exist(['work1', 'build', 'var3', 'f1.c'])
+test.must_not_exist(['work1', 'build', 'var3', 'f2.in'])
+test.must_not_exist(['work1', 'build', 'var3', 'b1.f'])
+test.must_not_exist(['work1', 'build', 'var3', 'b2.in'])
+
+# Make sure we didn't duplicate the source files in build/var4.
+test.must_not_exist(['work1', 'build', 'var4', 'f1.c'])
+test.must_not_exist(['work1', 'build', 'var4', 'f2.in'])
+test.must_not_exist(['work1', 'build', 'var4', 'b1.f'])
+test.must_not_exist(['work1', 'build', 'var4', 'b2.in'])
+
+# Make sure we didn't duplicate the source files in build/var5.
+test.must_not_exist(['build', 'var5', 'f1.c'])
+test.must_not_exist(['build', 'var5', 'f2.in'])
+test.must_not_exist(['build', 'var5', 'b1.f'])
+test.must_not_exist(['build', 'var5', 'b2.in'])
+
+# verify that header files in the source directory are scanned properly:
+test.write(['work1', 'src', 'f1.h'], r"""
+#define F1_STR "f1.c 2\n"
+""")
+
+test.write(['work1', 'src', 'f3h.in'], r"""
+#define F3_STR "f3.c 2\n"
+""")
+
+test.write(['work1', 'src', 'f4h.in'], r"""
+#define F4_STR "f4.c 2\n"
+""")
+
+test.run(chdir='work1', arguments = '../build/var5', stderr=None)
+
+test.fail_test(not blank_output(test.stderr()))
+
+test.run(program = foo51, stdout = "f1.c 2\n")
+test.run(program = test.workpath('build', 'var5', 'foo3' + _exe),
+ stdout = "f3.c 2\n")
+test.run(program = test.workpath('build', 'var5', 'foo4' + _exe),
+ stdout = "f4.c 2\n")
+
+test.run(chdir='work1', arguments='../build/var5', stdout=test.wrap_stdout("""\
+scons: `%s' is up to date.
+""" % test.workpath('build', 'var5')))
+
+#
+test.write(['work2', 'SConstruct'], """\
+env = Environment()
+env.Program('prog.c')
+""")
+
+test.write(['work2', 'prog.c'], r"""
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("work2/prog.c\n");
+ exit (0);
+}
+""")
+
+test.run(chdir='work2', arguments='.')
+
+test.up_to_date(chdir='work2', arguments='.')
+
+#
+test.write(['work2', 'SConstruct'], """\
+env = Environment()
+BuildDir('build', '.')
+Export('env')
+SConscript('build/SConscript')
+""")
+
+test.write(['work2', 'SConscript'], """\
+Import('env')
+env.Program('prog.c')
+""")
+
+test.run(chdir='work2', arguments='.', stderr=None)
+
+test.fail_test(not blank_output(test.stderr()))
+
+test.run(chdir='work2', arguments='.',
+ stdout=test.wrap_stdout("""\
+scons: building associated BuildDir targets: build
+scons: `.' is up to date.
+"""))
+
+test.write( ['work3', 'SConstruct'], """\
+SConscriptChdir(0)
+BuildDir('build', '.', duplicate=1 )
+SConscript( 'build/SConscript' )
+""")
+
+test.write( ['work3', 'SConscript'], """\
+import sys
+headers = ['existing.h', 'non_existing.h']
+for header in headers:
+ h = File( header )
+ contents = h.get_contents()
+ sys.stderr.write( '%s:%s\\n' % (header, contents))
+""")
+
+test.write( ['work3', 'existing.h'], """\
+/* a header file */\
+""")
+
+test.run(chdir='work3',
+ stdout=test.wrap_stdout("""\
+scons: building associated BuildDir targets: build
+scons: `.' is up to date.
+"""),
+ stderr="""\
+existing.h:/* a header file */
+non_existing.h:
+""")
+
+test.pass_test()
diff --git a/test/BuildDir/CPPPATH-subdir.py b/test/BuildDir/CPPPATH-subdir.py
new file mode 100644
index 0000000..2ae50b5
--- /dev/null
+++ b/test/BuildDir/CPPPATH-subdir.py
@@ -0,0 +1,70 @@
+#!/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__"
+
+"""
+Test handling of the current directory (.) in CPPPATH when
+the include path contains a subdirectory.
+
+This tests for a regression found in 0.96.90 by Chad Austin.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('src', ['src', 'glscry'])
+
+test.write('SConstruct', """\
+env = Environment()
+Export('env')
+SConscript(dirs=['src'], build_dir='build', duplicate=0)
+""")
+
+
+test.write(['src', 'SConscript'], """\
+SConscript(dirs=['glscry'])
+""")
+
+
+test.write(['src', 'glscry', 'SConscript'], """\
+Import('*')
+env = env.Copy()
+env.Append(CPPPATH=['.'])
+env.Library('foo', 'foo.c')
+""")
+
+test.write(['src', 'glscry', 'foo.c'], """\
+#include <foo.h>
+""")
+
+
+test.write(['src', 'glscry', 'foo.h'], "\n")
+
+test.run(arguments = '.',
+ stderr = TestSCons.noisy_ar,
+ match = TestSCons.match_re_dotall)
+
+test.pass_test()
diff --git a/test/BuildDir/Sconscript-build_dir.py b/test/BuildDir/Sconscript-build_dir.py
new file mode 100644
index 0000000..298eb9b
--- /dev/null
+++ b/test/BuildDir/Sconscript-build_dir.py
@@ -0,0 +1,263 @@
+#!/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 specifying a build_dir argument to SConscript works properly.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+all1 = test.workpath('test', 'build', 'var1', 'all')
+all2 = test.workpath('test', 'build', 'var2', 'all')
+all3 = test.workpath('test', 'build', 'var3', 'all')
+all4 = test.workpath('test', 'build', 'var4', 'all')
+all5 = test.workpath('build', 'var5', 'all')
+all6 = test.workpath('build', 'var6', 'all')
+all7 = test.workpath('build', 'var7', 'all')
+all8 = test.workpath('build', 'var8', 'all')
+all9 = test.workpath('test', 'build', 'var9', 'src', 'all')
+
+test.subdir('test')
+
+test.write(['test', 'SConstruct'], """
+src = Dir('src')
+alt = Dir('alt')
+var1 = Dir('build/var1')
+var2 = Dir('build/var2')
+var3 = Dir('build/var3')
+var4 = Dir('build/var4')
+var5 = Dir('../build/var5')
+var6 = Dir('../build/var6')
+var7 = Dir('../build/var7')
+var8 = Dir('../build/var8')
+var9 = Dir('../build/var9')
+
+def cat(env, source, target):
+ target = str(target[0])
+ source = map(str, source)
+ f = open(target, "wb")
+ for src in source:
+ f.write(open(src, "rb").read())
+ f.close()
+
+env = Environment(BUILDERS={'Cat':Builder(action=cat)},
+ BUILD='build')
+
+Export("env")
+
+SConscript('src/SConscript', build_dir=var1)
+SConscript('src/SConscript', build_dir='build/var2', src_dir=src)
+
+SConscript('src/SConscript', build_dir='build/var3', duplicate=0)
+
+#XXX We can't support var4 and var5 yet, because our BuildDir linkage
+#XXX is to an entire source directory. We haven't yet generalized our
+#XXX infrastructure to be able to take the SConscript file from one source
+#XXX directory, but the rest of the files from a different one.
+#XXX SConscript('src/SConscript', build_dir=var4, src_dir=alt, duplicate=0)
+
+#XXX SConscript('src/SConscript', build_dir='../build/var5', src_dir='alt')
+SConscript('src/SConscript', build_dir=var6)
+
+SConscript('src/SConscript', build_dir=var7, src_dir=src, duplicate=0)
+env.SConscript('src/SConscript', build_dir='../$BUILD/var8', duplicate=0)
+
+# This tests the fact that if you specify a src_dir that is above
+# the dir a SConscript is in, that we do the intuitive thing, i.e.,
+# we set the path of the SConscript accordingly. The below is
+# equivalent to saying:
+#
+# BuildDir('build/var9', '.')
+# SConscript('build/var9/src/SConscript')
+SConscript('src/SConscript', build_dir='build/var9', src_dir='.')
+""")
+
+test.subdir(['test', 'src'], ['test', 'alt'])
+
+test.write(['test', 'src', 'SConscript'], """
+Import("env")
+env.Cat('aaa.out', 'aaa.in')
+env.Cat('bbb.out', 'bbb.in')
+env.Cat('ccc.out', 'ccc.in')
+env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out'])
+""")
+
+test.write('test/src/aaa.in', "test/src/aaa.in\n")
+test.write('test/src/bbb.in', "test/src/bbb.in\n")
+test.write('test/src/ccc.in', "test/src/ccc.in\n")
+
+test.write('test/alt/aaa.in', "test/alt/aaa.in\n")
+test.write('test/alt/bbb.in', "test/alt/bbb.in\n")
+test.write('test/alt/ccc.in', "test/alt/ccc.in\n")
+
+test.run(chdir='test', arguments = '. ../build')
+
+all_src = "test/src/aaa.in\ntest/src/bbb.in\ntest/src/ccc.in\n"
+all_alt = "test/alt/aaa.in\ntest/alt/bbb.in\ntest/alt/ccc.in\n"
+
+test.must_match(all1, all_src)
+test.must_match(all2, all_src)
+test.must_match(all3, all_src)
+#XXX We can't support var4 and var5 yet, because our BuildDir linkage
+#XXX is to an entire source directory. We haven't yet generalized our
+#XXX infrastructure to be able to take the SConscript file from one source
+#XXX directory, but the rest of the files from a different one.
+#XXX test.must_match(all4, all_alt)
+#XXX test.must_match(all5, all_alt)
+test.must_match(all6, all_src)
+test.must_match(all7, all_src)
+test.must_match(all8, all_src)
+test.must_match(all9, all_src)
+
+import os
+import stat
+def equal_stats(x,y):
+ x = os.stat(x)
+ y = os.stat(y)
+ return (stat.S_IMODE(x[stat.ST_MODE]) == stat.S_IMODE(y[stat.ST_MODE]) and
+ x[stat.ST_MTIME] == y[stat.ST_MTIME])
+
+# Make sure we did duplicate the source files in build/var1,
+# and that their stats are the same:
+for file in ['aaa.in', 'bbb.in', 'ccc.in']:
+ test.must_exist(test.workpath('test', 'build', 'var1', file))
+ test.fail_test(not equal_stats(test.workpath('test', 'build', 'var1', file),
+ test.workpath('test', 'src', file)))
+
+# Make sure we did duplicate the source files in build/var2,
+# and that their stats are the same:
+for file in ['aaa.in', 'bbb.in', 'ccc.in']:
+ test.must_exist(test.workpath('test', 'build', 'var2', file))
+ test.fail_test(not equal_stats(test.workpath('test', 'build', 'var2', file),
+ test.workpath('test', 'src', file)))
+
+# Make sure we didn't duplicate the source files in build/var3.
+test.must_not_exist(test.workpath('test', 'build', 'var3', 'aaa.in'))
+test.must_not_exist(test.workpath('test', 'build', 'var3', 'bbb.in'))
+test.must_not_exist(test.workpath('test', 'build', 'var3', 'ccc.in'))
+
+#XXX We can't support var4 and var5 yet, because our BuildDir linkage
+#XXX is to an entire source directory. We haven't yet generalized our
+#XXX infrastructure to be able to take the SConscript file from one source
+#XXX directory, but the rest of the files from a different one.
+#XXX Make sure we didn't duplicate the source files in build/var4.
+#XXXtest.must_not_exist(test.workpath('test', 'build', 'var4', 'aaa.in'))
+#XXXtest.must_not_exist(test.workpath('test', 'build', 'var4', 'bbb.in'))
+#XXXtest.must_not_exist(test.workpath('test', 'build', 'var4', 'ccc.in'))
+
+#XXX We can't support var4 and var5 yet, because our BuildDir linkage
+#XXX is to an entire source directory. We haven't yet generalized our
+#XXX infrastructure to be able to take the SConscript file from one source
+#XXX directory, but the rest of the files from a different one.
+#XXX Make sure we did duplicate the source files in build/var5,
+#XXX and that their stats are the same:
+#XXXfor file in ['aaa.in', 'bbb.in', 'ccc.in']:
+#XXX test.must_exist(test.workpath('build', 'var5', file))
+#XXX test.fail_test(not equal_stats(test.workpath('build', 'var5', file),
+#XXX test.workpath('test', 'src', file)))
+
+# Make sure we did duplicate the source files in build/var6,
+# and that their stats are the same:
+for file in ['aaa.in', 'bbb.in', 'ccc.in']:
+ test.must_exist(test.workpath('build', 'var6', file))
+ test.fail_test(not equal_stats(test.workpath('build', 'var6', file),
+ test.workpath('test', 'src', file)))
+
+# Make sure we didn't duplicate the source files in build/var7.
+test.must_not_exist(test.workpath('build', 'var7', 'aaa.in'))
+test.must_not_exist(test.workpath('build', 'var7', 'bbb.in'))
+test.must_not_exist(test.workpath('build', 'var7', 'ccc.in'))
+
+# Make sure we didn't duplicate the source files in build/var8.
+test.must_not_exist(test.workpath('build', 'var8', 'aaa.in'))
+test.must_not_exist(test.workpath('build', 'var8', 'bbb.in'))
+test.must_not_exist(test.workpath('build', 'var8', 'ccc.in'))
+
+###################
+test.subdir('test2')
+
+test.write(['test2', 'SConstruct'], """\
+SConscript('SConscript', build_dir='Build', src_dir='.', duplicate=0)
+""")
+
+test.write(['test2', 'SConscript'], """\
+env = Environment()
+foo_obj = env.Object('foo.c')
+env.Program('foo', [foo_obj, 'bar.c'])
+""")
+
+test.write(['test2', 'bar.c'], r"""
+void
+bar(void) {
+ printf("bar.c\n");
+}
+""")
+
+test.write(['test2', 'foo.c'], r"""
+int
+main(int argc, char *argv[]) {
+ bar();
+ printf("foo.c\n");
+}
+""")
+
+test.run(chdir="test2")
+
+_obj = TestSCons._obj
+
+test.must_not_exist(test.workpath('test2', 'foo' + _obj))
+test.must_not_exist(test.workpath('test2', 'bar' + _obj))
+test.must_exist(test.workpath('test2', 'Build', 'foo' + _obj))
+test.must_exist(test.workpath('test2', 'Build', 'bar' + _obj))
+
+###################
+# Make sure that directories for subsidiary SConscript() calls
+# in a build_dir get created if they don't already exist.
+test.subdir('test3')
+
+test.subdir(['test3', 'src'], ['test3', 'src', '_glscry'])
+
+test.write(['test3', 'SConstruct'], """\
+SConscript(dirs=['src'], build_dir='build', duplicate=0)
+""")
+
+test.write(['test3', 'src', 'SConscript'], """\
+SConscript(dirs=['_glscry'])
+""")
+
+test.write(['test3', 'src', '_glscry', 'SConscript'], """\
+""")
+
+test.write(['test3', 'src', 'file.in'], "file.in\n")
+
+test.write(['test3', 'src', '_glscry', 'file.in'], "file.in\n")
+
+test.run(chdir='test3')
+
+
+test.pass_test()
diff --git a/test/BuildDir/errors.py b/test/BuildDir/errors.py
new file mode 100644
index 0000000..93cd3ec
--- /dev/null
+++ b/test/BuildDir/errors.py
@@ -0,0 +1,171 @@
+#!/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__"
+
+"""
+Validate successful handling of errors when duplicating things in
+BuildDirs. This is generally when the BuildDir, or something in it,
+is read-only.
+"""
+
+import os
+import os.path
+import stat
+import sys
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+for dir in ['normal', 'ro-dir', 'ro-SConscript', 'ro-src']:
+ test.subdir(dir, [dir, 'src'])
+
+ test.write([dir, 'SConstruct'], """\
+import os.path
+BuildDir('build', 'src')
+SConscript(os.path.join('build', 'SConscript'))
+""")
+
+ test.write([dir, 'src', 'SConscript'], """\
+def fake_scan(node, env, target):
+ # We fetch the contents here, even though we don't examine
+ # them, because get_contents() will cause the engine to
+ # try to link the source file into the build directory,
+ # potentially triggering a different failure case.
+ contents = node.get_contents()
+ return []
+
+def cat(env, source, target):
+ target = str(target[0])
+ source = map(str, source)
+ f = open(target, "wb")
+ for src in source:
+ f.write(open(src, "rb").read())
+ f.close()
+
+env = Environment(BUILDERS={'Build':Builder(action=cat)},
+ SCANNERS=[Scanner(fake_scan, skeys = ['.in'])])
+
+# Do some Node test operations to ensure no side-effects cause failures
+File('file.in').exists()
+File('file.in').is_derived()
+File('file.in').is_pseudo_derived()
+
+env.Build('file.out', 'file.in')
+""")
+
+ test.write([dir, 'src', 'file.in'], dir + "/src/file.in\n")
+
+# Just verify that the normal case works fine.
+test.run(chdir = 'normal', arguments = ".")
+
+test.fail_test(test.read(['normal', 'build', 'file.out']) != "normal/src/file.in\n")
+
+# Verify the error when the BuildDir itself is read-only. Don't bother
+# to test this on Win32, because the ACL (I think) still allows the
+# owner to create files in the directory even when it's read-only.
+if sys.platform != 'win32':
+ dir = os.path.join('ro-dir', 'build')
+ test.subdir(dir)
+ os.chmod(dir, os.stat(dir)[stat.ST_MODE] & ~stat.S_IWUSR)
+
+ test.run(chdir = 'ro-dir',
+ arguments = ".",
+ status = 2,
+ stderr = "scons: *** Cannot duplicate `%s' in `build': Permission denied. Stop.\n" % os.path.join('src', 'SConscript'))
+
+# Verify the error when the SConscript file within the BuildDir is
+# read-only. Note that we have to make the directory read-only too,
+# because otherwise our duplication logic will be able to unlink
+# the read-only SConscript and duplicate the new one.
+dir = os.path.join('ro-SConscript', 'build')
+test.subdir(dir)
+SConscript = test.workpath(dir, 'SConscript')
+test.write(SConscript, '')
+os.chmod(SConscript, os.stat(SConscript)[stat.ST_MODE] & ~stat.S_IWUSR)
+f = open(SConscript, 'r')
+os.chmod(dir, os.stat(dir)[stat.ST_MODE] & ~stat.S_IWUSR)
+
+test.run(chdir = 'ro-SConscript',
+ arguments = ".",
+ status = 2,
+ stderr = "scons: *** Cannot duplicate `%s' in `build': Permission denied. Stop.\n" % os.path.join('src', 'SConscript'))
+
+os.chmod('ro-SConscript', os.stat('ro-SConscript')[stat.ST_MODE] | stat.S_IWUSR)
+f.close()
+
+test.run(chdir = 'ro-SConscript',
+ arguments = ".",
+ status = 2,
+ stderr = "scons: *** Cannot duplicate `%s' in `build': Permission denied. Stop.\n" % os.path.join('src', 'SConscript'))
+
+# Verify the error when the source file within the BuildDir is
+# read-only. Note that we have to make the directory read-only too,
+# because otherwise our duplication logic will be able to unlink the
+# read-only source file and duplicate the new one. But because we've
+# made the BuildDir read-only, we must also create a writable SConscript
+# file there so it can be duplicated from the source directory.
+dir = os.path.join('ro-src', 'build')
+test.subdir(dir)
+test.write([dir, 'SConscript'], '')
+file_in = test.workpath(dir, 'file.in')
+test.write(file_in, '')
+os.chmod(file_in, os.stat(file_in)[stat.ST_MODE] & ~stat.S_IWUSR)
+f = open(file_in, 'r')
+os.chmod(dir, os.stat(dir)[stat.ST_MODE] & ~stat.S_IWUSR)
+
+test.run(chdir = 'ro-src',
+ arguments = ".",
+ status = 2,
+ stderr = """\
+scons: *** Cannot duplicate `%s' in `build': Permission denied. Stop.
+""" % (os.path.join('src', 'file.in')))
+
+test.run(chdir = 'ro-src',
+ arguments = "-k .",
+ status = 2,
+ stderr = """\
+scons: *** Cannot duplicate `%s' in `build': Permission denied. Stop.
+""" % (os.path.join('src', 'file.in')))
+
+f.close()
+
+# ensure that specifying multiple source directories for one
+# build directory results in an error message, rather
+# than just silently failing.
+test.subdir('duplicate', ['duplicate', 'src1'], ['duplicate', 'src2'])
+test.write(['duplicate', 'SConstruct'], """\
+BuildDir('build', 'src1')
+BuildDir('build', 'src2')
+""")
+
+test.run(chdir = 'duplicate',
+ arguments = ".",
+ status = 2,
+ stderr = """
+scons: *** 'build' already has a source directory: 'src1'.
+File "SConstruct", line 2, in ?
+""")
+
+test.pass_test()
diff --git a/test/BuildDir/reflect.py b/test/BuildDir/reflect.py
new file mode 100644
index 0000000..beb7df0
--- /dev/null
+++ b/test/BuildDir/reflect.py
@@ -0,0 +1,136 @@
+#!/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__"
+
+"""
+This test validates the correct operation of a BuildDir specification
+in avoiding reflection: reflection is the case where the build_dir is
+located under the corresponding source dir, and trying to use elements
+in the build_dir as sources for that same build dir.
+
+Test based on bug #1055521 filed by Gary Oberbrunner.
+"""
+
+import os.path
+import re
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+python = TestSCons.python
+re_python = re.escape(python)
+
+test.write("mycc.py", """
+print 'Compile'
+""")
+
+test.write("mylink.py", """
+print 'Link'
+""")
+
+sconstruct = """
+env = Environment(CC = r'%(python)s mycc.py',
+ LINK = r'%(python)s mylink.py',
+ INCPREFIX = 'INC_',
+ INCSUFFIX = '_CNI',
+ CPPPATH='%(cpppath)s') # note no leading '#'
+Export("env")
+SConscript('SConscript', build_dir="dir1/dir2", src_dir=".")
+"""
+
+test.write('SConscript', """\
+Import("env")
+env.Program("foo", "src1/foo.c")
+Default(".")
+""")
+
+test.write('foo.h', '#define HI_STR "hello, there!"\n')
+
+test.subdir('src1')
+
+test.write(['src1', 'foo.c'], """\
+#include <stdio.h>
+#include "foo.h"
+main() { printf(HI_STR);}
+""")
+
+# Test the bad cpppath; make sure it doesn't reflect dir1/dir2/foo.h
+# into dir1/dir2/dir1/dir2/foo.h, and make sure the target/message for
+# builds is correct.
+
+cpppath = 'dir1/dir2' # note, no leading '#'
+test.write('SConstruct', sconstruct % locals() )
+
+targets = re.escape(os.path.join('dir1', 'dir2'))
+INC_CNI = re.escape(os.path.join('INC_dir1', 'dir2', 'dir1', 'dir2_CNI'))
+
+# The .* after mycc\\.py below handles /nologo flags from Visual C/C++.
+test.run(arguments = '',
+ stdout=test.wrap_stdout("""\
+scons: building associated BuildDir targets: %(targets)s
+%(re_python)s mycc\\.py.* %(INC_CNI)s .+
+Compile
+%(re_python)s mylink\\.py .+
+Link
+""" % locals()),
+ match=TestSCons.match_re,
+ )
+
+# Note that we don't check for the existence of dir1/dir2/foo.h, because
+# this bad cpppath will expand to dir1/dir2/dir1/dir2, which means it
+# won't pick up the srcdir copy of dir/dir2/foo.h. That's all right,
+# we just need to make sure it doesn't create dir1/dir2/dir1/dir2/foo.h.
+test.must_exist(['dir1', 'dir2', 'src1', 'foo.c'])
+test.must_not_exist(['dir1', 'dir2', 'dir1', 'dir2', 'foo.h'])
+
+import shutil
+shutil.rmtree('dir1', ignore_errors=1)
+test.must_not_exist('dir1')
+
+# Now test the good cpppath and make sure everything looks right.
+
+cpppath = '#dir1/dir2' # note leading '#'
+test.write('SConstruct', sconstruct % locals() )
+
+INC_CNI = re.escape(os.path.join('INC_dir1', 'dir2_CNI'))
+
+# The .* after mycc\\.py below handles /nologo flags from Visual C/C++.
+test.run(arguments = '',
+ stdout=test.wrap_stdout("""\
+scons: building associated BuildDir targets: %(targets)s
+%(re_python)s mycc\\.py.* %(INC_CNI)s .+
+Compile
+%(re_python)s mylink\\.py .+
+Link
+""" % locals()),
+ match=TestSCons.match_re,
+ )
+
+test.must_exist(['dir1', 'dir2', 'foo.h'])
+test.must_exist(['dir1', 'dir2', 'src1', 'foo.c'])
+test.must_not_exist(['dir1', 'dir2', 'dir1', 'dir2', 'foo.h'])
+
+
+test.pass_test()