summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2003-02-10 07:33:07 (GMT)
committerSteven Knight <knight@baldmt.com>2003-02-10 07:33:07 (GMT)
commit6cc65ff3e32487eaa455776111d26a5bf07fb862 (patch)
tree9f038e6874fc1823ac8a97b21f39820e700d9d10
parent54856ad6003f3e8aa5c3bf59ed1d92ab1598137a (diff)
downloadSCons-6cc65ff3e32487eaa455776111d26a5bf07fb862.zip
SCons-6cc65ff3e32487eaa455776111d26a5bf07fb862.tar.gz
SCons-6cc65ff3e32487eaa455776111d26a5bf07fb862.tar.bz2
Support more intuitive build directory specifications as arguments to SConscript().
-rw-r--r--doc/man/scons.1130
-rw-r--r--src/CHANGES.txt3
-rw-r--r--src/engine/SCons/Script/SConscript.py22
-rw-r--r--test/SConscript-build_dir.py188
4 files changed, 322 insertions, 21 deletions
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index 257dbd6..19237dc 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -2399,10 +2399,10 @@ can be converted into an Action object
.TP
.RI BuildDir( build_dir ", " src_dir ", [" duplicate ])
-This specifies a build directory to use for all derived files.
+This specifies a build directory
.I build_dir
-specifies the build directory to be used for all derived files that would
-normally be built under
+in which to build all derived files
+that would normally be built under
.IR src_dir .
Multiple build directories can be set up for multiple build variants, for
example.
@@ -2413,20 +2413,80 @@ and
may not be underneath the
.I src_dir .
+The default behavior is for
.B scons
-will link or copy (depending on the platform) all the source files into the
-build directory if
-.I duplicate
-is set to 1 (the default). If
+to duplicate all of the files in the tree underneath
+.I src_dir
+into
+.IR build_dir ,
+and then build the derived files within the copied tree.
+(The duplication is performed by
+linking or copying,
+depending on the platform.)
+This guarantees correct builds
+regardless of whether intermediate source files
+are generated during the build,
+where preprocessors or other scanners search
+for included files,
+or whether individual compilers or other invoked tools
+are hard-coded to put derived files in the same directory as source files.
+
+This behavior of making a complete copy of the source tree
+may be disabled by setting
.I duplicate
-is set to 0, then
-.B scons
-will not copy or link any source files, which may cause build problems in
-certain situations (e.g. C source files that are generated by the
-build).
-.IR duplicate =0
-is usually safe, and is always more efficient than
-.IR duplicate =1.
+to 0.
+This will cause
+.B scons
+to invoke Builders using the
+path names of source files in
+.I src_dir
+and the path names of derived files within
+.IR build_dir .
+This is always more efficient than
+.IR duplicate =1,
+and is usually safe for most builds.
+Specifying
+.IR duplicate =0,
+however,
+may cause build problems
+if source files are generated during the build,
+if any invoked tools are hard-coded to
+put derived files in the same directory as the source files.
+
+Note that specifying a
+.B BuildDir
+works most naturally
+with a subsidiary SConscript file
+in the source directory.
+However,
+you would then call the subsidiary SConscript file
+not in the source directory,
+but in the
+.I build_dir ,
+as if
+.B scons
+had made a virtual copy of the source tree
+regardless of the value of
+.IR duplicate .
+This is how you tell
+.B scons
+which variant of a source tree to build.
+For example:
+
+.ES
+BuildDir('build-variant1', 'src')
+SConscript('build-variant1/SConscript')
+BuildDir('build-variant2', 'src')
+SConscript('build-variant2/SConscript')
+.EE
+
+See also the
+.BR SConscript ()
+function, described below,
+for another way to
+specify a build directory
+in conjunction with calling a subsidiary
+SConscript file.)
.TP
.RI AddPostAction ( target, action )
@@ -2820,7 +2880,7 @@ Return(["foo", "bar"])
.EE
.TP
-.RI SConscript( script ", [" exports ])
+.RI SConscript( script ", [" exports ", " build_dir ", " duplicate ])
This tells
.B scons
to execute
@@ -2832,17 +2892,53 @@ argument provides a list of variable names to export to
.I script
must use the
.BR Import ()
-function to import the variables. Any variables returned by
+function to import the variables.
+
+The optional
+.I build_dir
+argument specifies that all of the target files
+(for example, object files and executables)
+that would normally be built in the subdirectory in which
+.I script
+resides should actually
+be built in
+.IR build_dir .
+By default,
+.B scons
+will link or copy (depending on the platform)
+all the source files into the build directory.
+This behavior may be disabled by
+setting the optional
+.I duplicate
+argument to 0
+(it is set to 1 by default),
+in which case
+.B scons
+will refer directly to
+the source files in their source directory
+when building target files.
+(Setting
+.IR duplicate =0
+is usually safe, and always more efficient
+than the default of
+.IR duplicate =1,
+but it may cause build problems in certain end-cases,
+such as compiling from source files that
+are generated by the build.
+
+Any variables returned by
.I script
using
.BR Return ()
will be returned by the call to
.BR SConscript ().
+
Examples:
.ES
SConscript('dir/SConscript')
foo = SConscript('subdir/SConscript', "env")
+SConscript('src/SConscript', build_dir='build', duplicate=0)
.EE
.TP
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index 14ec5e2..068dc3d 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -80,6 +80,9 @@ RELEASE 0.11 - XXX
- Add support for allowing an embedding interface to annotate a node
when it's created.
+ - Extend the SConscript() function to accept build_dir and duplicate
+ keyword arguments that function like a BuildDir() call.
+
From Steve Leblanc:
- Fix the output of -c -n when directories are involved, so it
diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py
index a8aa732..c94ff2c 100644
--- a/src/engine/SCons/Script/SConscript.py
+++ b/src/engine/SCons/Script/SConscript.py
@@ -112,9 +112,9 @@ def Return(*vars):
stack[-1].retval = tuple(retval)
# This function is responsible for converting the parameters passed to
-# SConscript() calls into a list of files and export variables. If
-# the parameters are invalid, throws SCons.Errors.UserError. Returns a
-# tuple (l, e) where l is a list of filenames and e is a list of
+# SConscript() calls into a list of files and export variables. If the
+# parameters are invalid, throws SCons.Errors.UserError. Returns a tuple
+# (l, e) where l is a list of SConscript filenames and e is a list of
# exports.
def GetSConscriptFilenames(ls, kw):
@@ -147,12 +147,26 @@ def GetSConscriptFilenames(ls, kw):
exports = SCons.Util.argmunge(ls[1])
if kw.get('exports'):
- exports.extend(SCons.Util.argmunge(kw.get('exports')))
+ exports.extend(SCons.Util.argmunge(kw['exports']))
else:
raise SCons.Errors.UserError, \
"Invalid SConscript() usage - too many arguments"
+ build_dir = kw.get('build_dir')
+ if build_dir:
+ if len(files) != 1:
+ raise SCons.Errors.UserError, \
+ "Invalid SConscript() usage - can only specify one SConscript with a build_dir"
+ duplicate = kw.get('duplicate', 1)
+ src_dir = kw.get('src_dir')
+ if not src_dir:
+ src_dir, fname = os.path.split(str(files[0]))
+ else:
+ fname = os.path.split(files[0])[1]
+ BuildDir(build_dir, src_dir, duplicate)
+ files = [os.path.join(str(build_dir), fname)]
+
return (files, exports)
def SConscript(*ls, **kw):
diff --git a/test/SConscript-build_dir.py b/test/SConscript-build_dir.py
new file mode 100644
index 0000000..c0b92f3
--- /dev/null
+++ b/test/SConscript-build_dir.py
@@ -0,0 +1,188 @@
+#!/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')
+
+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')
+
+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)})
+
+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)
+SConscript('src/SConscript', build_dir='../build/var8', duplicate=0)
+""")
+
+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.fail_test(test.read(all1) != all_src)
+test.fail_test(test.read(all2) != all_src)
+test.fail_test(test.read(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.fail_test(test.read(all4) != all_alt)
+#XXX test.fail_test(test.read(all5) != all_alt)
+test.fail_test(test.read(all6) != all_src)
+test.fail_test(test.read(all7) != all_src)
+test.fail_test(test.read(all8) != 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.fail_test(not os.path.exists(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.fail_test(not os.path.exists(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.fail_test(os.path.exists(test.workpath('test', 'build', 'var3', 'aaa.in')))
+test.fail_test(os.path.exists(test.workpath('test', 'build', 'var3', 'bbb.in')))
+test.fail_test(os.path.exists(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.fail_test(os.path.exists(test.workpath('test', 'build', 'var4', 'aaa.in')))
+#XXXtest.fail_test(os.path.exists(test.workpath('test', 'build', 'var4', 'bbb.in')))
+#XXXtest.fail_test(os.path.exists(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.fail_test(not os.path.exists(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.fail_test(not os.path.exists(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.fail_test(os.path.exists(test.workpath('build', 'var7', 'aaa.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var7', 'bbb.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var7', 'ccc.in')))
+
+# Make sure we didn't duplicate the source files in build/var8.
+test.fail_test(os.path.exists(test.workpath('build', 'var8', 'aaa.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var8', 'bbb.in')))
+test.fail_test(os.path.exists(test.workpath('build', 'var8', 'ccc.in')))
+
+test.pass_test()