Check in any changes necessary to make everything work
+ Update the user's guide
+
+ sh bin/docdiff
+
+ sh bin/docupdate
+
END THE CURRENT DEVELOPMENT BRANCH
ae_p scons.0
Things to do to release a new X.Y.Z version of SCons:
+ BEFORE STARTING THE SUB-BRANCH:
+
+ Update the user's guide on the parent
+
+ sh bin/docdiff
+
+ sh bin/docupdate
+
START THE NEW SUB-BRANCH FOR SUBRELEASE
aenbr -p scons.0.{94} {1}
import os.path
-Import('env')
+Import('build_dir', 'env')
files = [
'classes.qmc',
# Guarantee that real copies of these files always exist in
# build/QMTest. If there's a symlink there, then this is an Aegis
# build and we blow them away now so that they'll get "built" later.
- p = os.path.join('build', 'QMTest', file)
+ p = os.path.join(build_dir, 'QMTest', file)
if os.path.islink(p):
os.unlink(p)
- sp = '#' + p
- env.Command(sp, file, copy)
- Local(sp)
+ if not os.path.isabs(p):
+ p = '#' + p
+ env.Command(p, file, copy)
+ Local(p)
default_version = '0.96.94'
copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
-Default('.')
-
SConsignFile()
#
if os.environ.has_key(key):
ENV[key] = os.environ[key]
-cwd_build = os.path.join(os.getcwd(), "build")
+build_dir = ARGUMENTS.get('BUILDDIR', 'build')
+if not os.path.isabs(build_dir):
+ build_dir = os.path.normpath(os.path.join(os.getcwd(), build_dir))
+
+Default('.', build_dir)
packaging_flavors = [
'deb',
'local-zip',
]
-test_deb_dir = os.path.join(cwd_build, "test-deb")
-test_rpm_dir = os.path.join(cwd_build, "test-rpm")
-test_tar_gz_dir = os.path.join(cwd_build, "test-tar-gz")
-test_src_tar_gz_dir = os.path.join(cwd_build, "test-src-tar-gz")
-test_local_tar_gz_dir = os.path.join(cwd_build, "test-local-tar-gz")
-test_zip_dir = os.path.join(cwd_build, "test-zip")
-test_src_zip_dir = os.path.join(cwd_build, "test-src-zip")
-test_local_zip_dir = os.path.join(cwd_build, "test-local-zip")
+test_deb_dir = os.path.join(build_dir, "test-deb")
+test_rpm_dir = os.path.join(build_dir, "test-rpm")
+test_tar_gz_dir = os.path.join(build_dir, "test-tar-gz")
+test_src_tar_gz_dir = os.path.join(build_dir, "test-src-tar-gz")
+test_local_tar_gz_dir = os.path.join(build_dir, "test-local-tar-gz")
+test_zip_dir = os.path.join(build_dir, "test-zip")
+test_src_zip_dir = os.path.join(build_dir, "test-src-zip")
+test_local_zip_dir = os.path.join(build_dir, "test-local-zip")
-unpack_tar_gz_dir = os.path.join(cwd_build, "unpack-tar-gz")
-unpack_zip_dir = os.path.join(cwd_build, "unpack-zip")
+unpack_tar_gz_dir = os.path.join(build_dir, "unpack-tar-gz")
+unpack_zip_dir = os.path.join(build_dir, "unpack-zip")
if platform == "win32":
tar_hflag = ''
ENV = ENV,
BUILD = build_id,
+ BUILDDIR = build_dir,
BUILDSYS = build_system,
COPYRIGHT = copyright,
DATE = date,
'sconsign.1',
'scons-time.1',
'script/scons.bat',
+ 'script/scons-post-install.py',
'setup.cfg',
'setup.py',
],
'filemap' : {
- 'scons.1' : '../build/doc/man/scons.1',
- 'sconsign.1' : '../build/doc/man/sconsign.1',
- 'scons-time.1' : '../build/doc/man/scons-time.1',
+ 'scons.1' : '$BUILDDIR/doc/man/scons.1',
+ 'sconsign.1' : '$BUILDDIR/doc/man/sconsign.1',
+ 'scons-time.1' : '$BUILDDIR/doc/man/scons-time.1',
},
'buildermap' : {
},
}
-scripts = ['scons', 'sconsign']
+scripts = ['scons', 'sconsign', 'scons-time']
src_deps = []
src_files = []
if p.has_key('src_subdir'):
src = os.path.join(src, p['src_subdir'])
- build = os.path.join('build', pkg)
+ build = os.path.join(build_dir, pkg)
tar_gz = os.path.join(build, 'dist', "%s.tar.gz" % pkg_version)
platform_tar_gz = os.path.join(build,
#
for b in src_files:
s = p['filemap'].get(b, b)
+ if not s[0] == '$' and not os.path.isabs(s):
+ s = os.path.join(src, s)
builder = p['buildermap'].get(b, env.SCons_revision)
- x = builder(os.path.join(build, b), os.path.join(src, s))
+ x = builder(os.path.join(build, b), s)
Local(x)
#
#
# Generate portage files for submission to Gentoo Linux.
#
- gentoo = os.path.join('build', 'gentoo')
+ gentoo = os.path.join(build, 'gentoo')
ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version)
digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version)
env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision)
])
if rpmbuild:
- topdir = os.path.join(os.getcwd(), build, 'build',
+ topdir = os.path.join(build, 'build',
'bdist.' + platform, 'rpm')
- buildroot = os.path.join(os.getcwd(), 'build', 'rpm-buildroot')
+ buildroot = os.path.join(build_dir, 'rpm-buildroot')
BUILDdir = os.path.join(topdir, 'BUILD', pkg + '-' + version)
RPMSdir = os.path.join(topdir, 'RPMS', 'noarch')
if dh_builddeb and fakeroot:
# Our Debian packaging builds directly into build/dist,
# so we don't need to add the .debs to install_targets.
- deb = os.path.join('build', 'dist', "%s_%s-1_all.deb" % (pkg, version))
+ deb = os.path.join(build_dir, 'dist', "%s_%s-1_all.deb" % (pkg, version))
for d in p['debian_deps']:
b = env.SCons_revision(os.path.join(build, d), d)
env.Depends(deb, b)
#
s_l_v = '%s-local-%s' % (pkg, version)
- local = os.path.join('build', pkg + '-local')
- cwd_local = os.path.join(os.getcwd(), local)
- cwd_local_slv = os.path.join(os.getcwd(), local, s_l_v)
+ local = pkg + '-local'
+ build_dir_local = os.path.join(build_dir, local)
+ build_dir_local_slv = os.path.join(build_dir, local, s_l_v)
- local_tar_gz = os.path.join('build', 'dist', "%s.tar.gz" % s_l_v)
- local_zip = os.path.join('build', 'dist', "%s.zip" % s_l_v)
+ local_tar_gz = os.path.join(build_dir, 'dist', "%s.tar.gz" % s_l_v)
+ local_zip = os.path.join(build_dir, 'dist', "%s.zip" % s_l_v)
commands = [
- Delete(local),
+ Delete(build_dir_local),
'$PYTHON $PYTHONFLAGS $SETUP_PY install "--install-script=%s" "--install-lib=%s" --no-install-man --no-compile --standalone-lib --no-version-script' % \
- (cwd_local, cwd_local_slv),
+ (build_dir_local, build_dir_local_slv),
]
for script in scripts:
#commands.append("mv %s/%s %s/%s.py" % (local, script, local, script))
- local_script = os.path.join(local, script)
+ local_script = os.path.join(build_dir_local, script)
commands.append(Move(local_script + '.py', local_script))
rf = filter(lambda x: not x in scripts, raw_files)
rf = map(lambda x, slv=s_l_v: os.path.join(slv, x), rf)
for script in scripts:
rf.append("%s.py" % script)
- local_targets = map(lambda x, s=local: os.path.join(s, x), rf)
+ local_targets = map(lambda x, s=build_dir_local: os.path.join(s, x), rf)
env.Command(local_targets, build_src_files, commands)
- scons_LICENSE = os.path.join(local, 'scons-LICENSE')
+ scons_LICENSE = os.path.join(build_dir_local, 'scons-LICENSE')
l = env.SCons_revision(scons_LICENSE, 'LICENSE-local')
local_targets.append(l)
Local(l)
- scons_README = os.path.join(local, 'scons-README')
+ scons_README = os.path.join(build_dir_local, 'scons-README')
l = env.SCons_revision(scons_README, 'README-local')
local_targets.append(l)
Local(l)
if gzip:
env.Command(local_tar_gz,
local_targets,
- "cd %s && tar czf $( ${TARGET.abspath} $) *" % local)
+ "cd %s && tar czf $( ${TARGET.abspath} $) *" % build_dir_local)
unpack_targets = map(lambda x, d=test_local_tar_gz_dir:
os.path.join(d, x),
if zipit:
env.Command(local_zip, local_targets, zipit,
- CD = local, PSV = '.')
+ CD = build_dir_local, PSV = '.')
unpack_targets = map(lambda x, d=test_local_zip_dir:
os.path.join(d, x),
# And, lastly, install the appropriate packages in the
# appropriate subdirectory.
#
- b_d_files = env.Install(os.path.join('build', 'dist'), install_targets)
+ b_d_files = env.Install(os.path.join(build_dir, 'dist'), install_targets)
Local(b_d_files)
#
#
#
-Export('env')
+Export('build_dir', 'env')
SConscript('QMTest/SConscript')
# Guarantee that real copies of these files always exist in
# build/. If there's a symlink there, then this is an Aegis
# build and we blow them away now so that they'll get "built" later.
- p = os.path.join('build', file)
+ p = os.path.join(build_dir, file)
if os.path.islink(p):
os.unlink(p)
- sp = '#' + p
- env.Command(sp, file, copy)
+ if not os.path.isabs(p):
+ p = '#' + p
+ sp = env.Command(p, file, copy)
Local(sp)
#
# Documentation.
#
-Export('env', 'whereis')
+Export('build_dir', 'env', 'whereis')
SConscript('doc/SConscript')
if sfiles:
ps = "%s-src" % project
psv = "%s-%s" % (ps, version)
- b_ps = os.path.join('build', ps)
- b_psv = os.path.join('build', psv)
+ b_ps = os.path.join(build_dir, ps)
+ b_psv = os.path.join(build_dir, psv)
b_psv_stamp = b_psv + '-stamp'
- src_tar_gz = os.path.join('build', 'dist', '%s.tar.gz' % psv)
- src_zip = os.path.join('build', 'dist', '%s.zip' % psv)
+ src_tar_gz = os.path.join(build_dir, 'dist', '%s.tar.gz' % psv)
+ src_zip = os.path.join(build_dir, 'dist', '%s.zip' % psv)
Local(src_tar_gz, src_zip)
+++ /dev/null
-#! /bin/sh
-
-# This script runs pychecker on the SCons source and creates a file called
-# checks.txt with the results. It must be run in the src/engine directory.
-
-base=`basename $PWD`
-if [ "$base" != "engine" ]; then
- echo "You must run this script from the engine directory."
- exit
-fi
-
-DEVDIR=../../doc/developer
-SRCFILE=../../bin/files
-CHKFILE=checks.txt
-
-rm -f $CHKFILE
-for f in `cat $SRCFILE` ; do
- echo >> $CHKFILE
- echo " --- $f ---" >> $CHKFILE
- env PYTHONPATH=. pychecker -T -z -Z --deprecated $f >> $CHKFILE
-done
-
-if [ -e $CHKFILE ]; then
- sed -e "s|$PWD/||" $CHKFILE > /tmp/tmpchk
- mv -f /tmp/tmpchk $CHKFILE
-fi
import sys
import time
-sys.path.append(os.path.join(os.getcwd(), 'etc'))
-sys.path.append(os.path.join(os.getcwd(), 'build', 'etc'))
+sys.path.append(os.path.join(os.getcwd(), 'QMTest'))
+sys.path.append(os.path.join(os.getcwd(), 'build', 'QMTest'))
scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py')
if not os.path.exists(scons_py):
ToolList = {
'posix' : [('cc', ['CCCOM', 'SHCCCOM'], CCCom, ['CCFLAGS', 'CPPDEFINES', 'COLOR', 'COLORS', 'PACKAGE']),
('link', ['LINKCOM', 'SHLINKCOM'], Cat, []),
- ('ar', 'ARCOM', Cat, []),
+ ('ar', ['ARCOM', 'RANLIBCOM'], Cat, []),
('tar', 'TARCOM', Null, []),
('zip', 'ZIPCOM', Null, []),
('BitKeeper', 'BITKEEPERCOM', Cat, []),
os.environ.update(save_vals)
for key in delete_keys:
del(os.environ[key])
- out = string.replace(test.stdout(), test.workpath('ROOT'), '')
+ out = test.stdout()
+ out = string.replace(out, test.workpath('ROOT'), '')
+ out = string.replace(out, test.workpath('WORK/SConstruct'),
+ '/home/my/project/SConstruct')
lines = string.split(out, '\n')
if lines:
while lines[-1] == '':
import os
import os.path
-import getopt
import string
import sys
bootstrap.py script only updates the bootstrap copy if the
content of the source copy is different.
+ --bootstrap_src=DIR
+
+ Searches for the SCons files relative to the specified DIR,
+ then relative to the directory in which this bootstrap.py
+ script is found.
+
--bootstrap_update
Only updates the bootstrap subdirectory, and then exits.
In addition to the above options, the bootstrap.py script understands
-the -Y and --repository= options, which are used under Aegis to specify
-a search path for the source files that may not have been copied in to
-the Aegis change.
+the following SCons options:
+
+ -C, --directory
+
+ Changes to the specified directory before invoking SCons.
+ Because we change directory right away to the specified directory,
+ the SCons script itself doesn't need to, so this option gets
+ "eaten" by the bootstrap.py script.
+
+ -Y, --repository
+
+ These options are used under Aegis to specify a search path
+ for the source files that may not have been copied in to the
+ Aegis change.
This is essentially a minimal build of SCons to bootstrap ourselves into
executing it for the full build of all the packages, as specified in our
bootstrap_dir = 'bootstrap'
pass_through_args = []
-search = ['.']
update_only = None
requires_an_argument = 'bootstrap.py: %s requires an argument\n'
-command_line_args = sys.argv[1:]
-
def must_copy(dst, src):
if not os.path.exists(dst):
return 1
return open(dst, 'rb').read() != open(src, 'rb').read()
+search = [os.path.dirname(sys.argv[0])]
+if search[0] == '': search[0] = '.'
+
+# Note: We don't use the getopt module to process the command-line
+# arguments because we'd have to teach it about all of the SCons options.
+
+command_line_args = sys.argv[1:]
+
while command_line_args:
arg = command_line_args.pop(0)
except IndexError:
sys.stderr.write(requires_an_argument % arg)
sys.exit(1)
-
elif arg[:16] == '--bootstrap_dir=':
bootstrap_dir = arg[16:]
def must_copy(dst, src):
return 1
+ elif arg == '--bootstrap_src':
+ try:
+ search.insert(0, command_line_args.pop(0))
+ except IndexError:
+ sys.stderr.write(requires_an_argument % arg)
+ sys.exit(1)
+ elif arg[:16] == '--bootstrap_src=':
+ search.insert(0, arg[16:])
+
elif arg == '--bootstrap_update':
update_only = 1
+ elif arg in ('-C', '--directory'):
+ try:
+ dir = command_line_args.pop(0)
+ except IndexError:
+ sys.stderr.write(requires_an_argument % arg)
+ sys.exit(1)
+ else:
+ os.chdir(dir)
+ elif arg[:2] == '-C':
+ os.chdir(arg[2:])
+ elif arg[:12] == '--directory=':
+ os.chdir(arg[12:])
+
elif arg in ('-Y', '--repository'):
try:
dir = command_line_args.pop(0)
else:
search.append(dir)
pass_through_args.extend([arg, dir])
-
elif arg[:2] == '-Y':
search.append(arg[2:])
pass_through_args.append(arg)
-
elif arg[:13] == '--repository=':
search.append(arg[13:])
pass_through_args.append(arg)
def find(file, search=search):
for dir in search:
f = os.path.join(dir, file)
- if os.path.exists(f):
- return os.path.normpath(f)
+ if os.path.exists(f):
+ return os.path.normpath(f)
sys.stderr.write("could not find `%s' in search path:\n" % file)
sys.stderr.write("\t" + string.join(search, "\n\t") + "\n")
sys.exit(2)
*
* Look in aesub(5) for more information about command substitutions.
*/
-build_command = "python2.1 ${Source bootstrap.py} -Y${SUBSTitute : \\ -Y $Search_Path} date='${DAte %Y/%m/%d %H:%M:%S}' developer=${DEVeloper} version=${VERsion} change=${Change}";
+build_command = "python2.4 ${Source bootstrap.py} -Y${SUBSTitute : \\ -Y $Search_Path} date='${DAte %Y/%m/%d %H:%M:%S}' developer=${DEVeloper} version=${VERsion} change=${Change}";
/*
* SCons removes its targets before constructing them, which qualifies it
import re
import string
-Import('env', 'whereis')
+Import('build_dir', 'env', 'whereis')
env = env.Clone()
env.TargetSignatures('content')
-build = os.path.join('#build', 'doc')
+build = os.path.join(build_dir, 'doc')
#
#
#
-doc_tar_gz = os.path.join('#build',
+doc_tar_gz = os.path.join(build_dir,
'dist',
'scons-doc-%s.tar.gz' % env.Dictionary('VERSION'))
'pdf' : 1,
'text' : 0,
},
+ # For whenever (if ever?) we start putting developer guide
+ # information in a printable document instead of the wiki.
+ #'developer' : {
+ # 'htmlindex' : 'book1.html',
+ # 'html' : 1,
+ # 'ps' : 1,
+ # 'pdf' : 1,
+ # 'text' : 0,
+ #},
'user' : {
'htmlindex' : 'book1.html',
'html' : 1,
'ps' : 1,
'pdf' : 1,
- 'text' : 0,
+ 'text' : 1,
'graphics' : [
'SCons-win32-install-1.jpg',
'SCons-win32-install-2.jpg',
# Hard-coding the scons-src path is a bit of a hack. This can
# be reworked when a better solution presents itself.
- scons_src_main = os.path.join('#build', 'scons-src', 'doc', main)
+ scons_src_main = os.path.join(build_dir, 'scons-src', 'doc', main)
env.Ignore(scons_src_main, version_sgml)
htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc)
man_page_list = ['scons.1', 'sconsign.1', 'scons-time.1']
for m in man_page_list:
- orig_env.SCons_revision(os.path.join(build, 'man', m),
+ x = orig_env.SCons_revision(os.path.join(build, 'man', m),
os.path.join('man', m))
man_i_files = ['builders.man', 'tools.man', 'variables.man']
return 0
cmds = [
- "( cd build/doc/man && cp %s .. )" % string.join(man_i_files),
+ "( cd %s/man && cp %s .. )" % (build, string.join(man_i_files)),
"( cd ${SOURCE.dir} && man2html ${SOURCE.file} ) > $TARGET",
Action(strip_to_first_html_tag),
]
--- /dev/null
+architecture.sgml
+branches.sgml
+copyright.sgml
+cycle.sgml
+main.sgml
+packaging.sgml
+preface.sgml
+sourcetree.sgml
+testing.sgml
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Architecture</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>&SCons; Branches</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+<blockquote>
+ <para>
+
+ SCons Developer's Guide Copyright (c) 2007 Steven Knight
+
+ </para>
+</blockquote>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Development Cycle</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+<!doctype book PUBLIC "-//OASIS//DTD DocBook V4.1//EN"
+[
+
+ <!ENTITY % version SYSTEM "../version.sgml">
+ %version;
+
+ <!ENTITY % scons SYSTEM "../scons.mod">
+ %scons;
+
+ <!ENTITY architecture SYSTEM "architecture.sgml">
+ <!ENTITY branches SYSTEM "branches.sgml">
+ <!ENTITY copyright SYSTEM "copyright.sgml">
+ <!ENTITY cycle SYSTEM "cycle.sgml">
+ <!ENTITY packaging SYSTEM "packaging.sgml">
+ <!ENTITY preface SYSTEM "preface.sgml">
+ <!ENTITY sourcetree SYSTEM "sourcetree.sgml">
+ <!ENTITY testing SYSTEM "testing.sgml">
+
+]>
+
+<book>
+ <bookinfo>
+ <title>SCons Developer's Guide &buildversion;</title>
+
+ <author>
+ <firstname>Steven</firstname>
+ <surname>Knight</surname>
+ </author>
+
+ <edition>Revision &buildrevision; (&builddate;)</edition>
+
+ <pubdate>2007</pubdate>
+
+ <copyright>
+ <year>2007</year>
+ <holder>Steven Knight</holder>
+ </copyright>
+
+ <legalnotice>
+ ©right;
+ </legalnotice>
+
+ <releaseinfo>version &buildversion;</releaseinfo>
+
+ </bookinfo>
+
+ <preface id="chap-preface">
+ <title>Preface</title>
+ &preface;
+ </preface>
+
+ <chapter id="chap-development-cycle">
+ <title>Development Cycle</title>
+ &cycle;
+ </chapter>
+
+ <chapter id="chap-source-tree">
+ <title>Source Tree</title>
+ &sourcetree;
+ </chapter>
+
+ <chapter id="chap-testing">
+ <title>Testing</title>
+ &testing;
+ </chapter>
+
+ <chapter id="chap-branches">
+ <title>Branches</title>
+ &branches;
+ </chapter>
+
+ <chapter id="chap-packaging">
+ <title>Packaging</title>
+ &packaging;
+ </chapter>
+
+ <chapter id="chap-architecture">
+ <title>Architecture</title>
+ &architecture;
+ </chapter>
+
+</book>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Packaging</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ This document assumes that you already know how &SCons;
+ and that you want to learn how to work on the code.
+
+ </para>
+
+ <section>
+ <title>&SCons; Principles</title>
+
+ <para>
+
+ There are a few overriding principles
+ we try to live up to in designing and implementing &SCons:
+
+ </para>
+
+ <variablelist>
+
+ <varlistentry>
+ <term>Correctness</term>
+
+ <listitem>
+ <para>
+
+ First and foremost,
+ by default, &SCons; guarantees a correct build
+ even if it means sacrificing performance a little.
+ We strive to guarantee the build is correct
+ regardless of how the software being built is structured,
+ how it may have been written,
+ or how unusual the tools are that build it.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Performance</term>
+
+ <listitem>
+ <para>
+
+ Given that the build is correct,
+ we try to make &SCons; build software
+ as quickly as possible.
+ In particular, wherever we may have needed to slow
+ down the default &SCons; behavior to guarantee a correct build,
+ we also try to make it easy to speed up &SCons;
+ through optimization options that let you trade off
+ guaranteed correctness in all end cases for
+ a speedier build in the usual cases.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
+ <term>Convenience</term>
+
+ <listitem>
+ <para>
+
+ &SCons; tries to do as much for you out of the box as reasonable,
+ including detecting the right tools on your system
+ and using them correctly to build the software.
+
+ </para>
+ </listitem>
+ </varlistentry>
+
+ </variablelist>
+
+ <para>
+
+ In a nutshell, we try hard to make &SCons; just
+ "do the right thing" and build software correctly,
+ with a minimum of hassles.
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Acknowledgements</title>
+
+ <para>
+
+ &SCons; would not exist without a lot of help
+ from a lot of people,
+ many of whom may not even be aware
+ that they helped or served as inspiration.
+ So in no particular order,
+ and at the risk of leaving out someone:
+
+ </para>
+
+ <para>
+
+ First and foremost,
+ &SCons; owes a tremendous debt to Bob Sidebotham,
+ the original author of the classic Perl-based &Cons; tool
+ which Bob first released to the world back around 1996.
+ Bob's work on Cons classic provided the underlying architecture
+ and model of specifying a build configuration
+ using a real scripting language.
+ My real-world experience working on Cons
+ informed many of the design decisions in SCons,
+ including the improved parallel build support,
+ making Builder objects easily definable by users,
+ and separating the build engine from the wrapping interface.
+
+ </para>
+
+ <para>
+
+ Greg Wilson was instrumental in getting
+ &SCons; started as a real project
+ when he initiated the Software Carpentry design
+ competition in February 2000.
+ Without that nudge,
+ marrying the advantages of the Cons classic
+ architecture with the readability of Python
+ might have just stayed no more than a nice idea.
+
+ </para>
+
+ <para>
+
+ Thanks to Peter Miller
+ for his splendid change management system, &Aegis;,
+ which has provided the &SCons; project
+ with a robust development methodology from day one,
+ and which showed me how you could
+ integrate incremental regression tests into
+ a practical development cycle
+ (years before eXtreme Programming arrived on the scene).
+
+ </para>
+
+ <para>
+
+ And last, thanks to Guido van Rossum
+ for his elegant scripting language,
+ which is the basis not only for the &SCons; implementation,
+ but for the interface itself.
+
+ </para>
+
+ </section>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Source Tree</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
--- /dev/null
+<!--
+
+ __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.
+
+-->
+
+ <para>
+
+ XXX
+
+ </para>
+
+ <section>
+ <title>Testing</title>
+
+ <para>
+
+ XXX
+ </para>
+
+ </section>
programs, object files, libraries), so that for most software
projects, only the target and input files need be specified.
+Before reading the
+.I SConstruct
+file,
+.B scons
+adds looks for a dir named
+.I site_scons
+in the dir containing the
+.I SConstruct
+file; it adds that
+.I site_scons
+to sys.path, reads the file
+.IR site_scons/site_init.py ,
+and adds the directory
+.I site_scons/site_tools
+to the default toolpath, if those exist. See the
+.I --no-site-dir
+and
+.I --site-dir
+options for more details.
+
.B scons
reads and executes the SConscript files as Python scripts,
so you may use normal Python scripting capabilities
.TP
--debug=dtree
-Print the dependency tree
-after each top-level target is built. This prints out only derived files.
+A synonym for the newer
+.B --tree=derived
+option.
+This will be deprecated in some future release
+and ultimately removed.
.TP
--debug=explain
.TP
--debug=stree
-Print the dependency tree along with status information. This is the
-same as the debug=tree option, but additional status information is
-provided for each node in the tree.
+A synonym for the newer
+.B --tree=all,status
+option.
+This will be deprecated in some future release
+and ultimately removed.
.TP
--debug=time
.TP
--debug=tree
-Print the dependency tree
-after each top-level target is built. This prints out the complete
-dependency tree including implicit dependencies and ignored
-dependencies.
+A synonym for the newer
+.B --tree=all
+option.
+This will be deprecated in some future release
+and ultimately removed.
.TP
.RI --diskcheck= types
options
are used, the directories are searched in the order specified.
+.TP
+.RI --no-site-dir
+Prevents the automatic addition of the standard
+.I site_scons
+dir to
+.IR sys.path .
+Also prevents loading the
+.I site_scons/site_init.py
+module if it exists, and prevents adding
+.I site_scons/site_tools
+to the toolpath.
+
.TP
--implicit-cache
Cache implicit dependencies. This can cause
Ignored for compatibility with GNU
.BR make .
+.TP
+.RI --site-dir= dir
+Uses the named dir as the site dir rather than the default
+.I site_scons
+dir. This dir will get prepended to
+.IR sys.path ,
+the module
+.IR dir /site_init.py
+will get loaded if it exists, and
+.IR dir /site_tools
+will get added to the default toolpath.
+
.TP
-t, --touch
Ignored for compatibility with GNU
.B -
may be used to specify the standard output.
+.TP
+.RI -tree= options
+Prints a tree of the dependencies
+after each top-level target is built.
+This prints out some or all of the tree,
+in various formats,
+depending on the
+.I options
+specified:
+
+.TP
+--tree=all
+Print the entire dependency tree
+after each top-level target is built.
+This prints out the complete dependency tree,
+including implicit dependencies and ignored dependencies.
+
+.TP
+--tree=derived
+Restricts the tree output to only derived (target) files,
+not source files.
+
+.TP
+--tree=status
+Prints status information for each displayed node.
+
+.TP
+--tree=prune
+Prunes the tree to avoid repeating dependency information
+for nodes that have already been displayed.
+Any node that has already been displayed
+will have its name printed in
+.BR "[square brackets]" ,
+as an indication that the dependencies
+for that node can be found by searching
+for the relevant output higher up in the tree.
+
+.IP
+Multiple options may be specified,
+separated by commas:
+
+.ES
+# Prints only derived files, with status information:
+scons --tree=derived,status
+
+# Prints all dependencies of target, with status information
+# and pruning dependencies of already-visited Nodes:
+scons --tree=all,prune,status target
+.EE
+
.TP
-u, --up, --search-up
Walks up the directory structure until an
Print a message containing the working directory before and
after other processing.
+.TP
+--no-print-directory
+Turn off -w, even if it was turned on implicitly.
+
.TP
.RI --warn= type ", --warn=no-" type
Enable or disable warnings.
--warn=all, --warn=no-all
Enables or disables all warnings.
+.TP
+--warn=cache-write-error, --warn=no-cache-write-error
+Enables or disables warnings about errors trying to
+write a copy of a built file to a specified
+.BR CacheDir ().
+These warnings are disabled by default.
+
+.TP
+--warn=corrupt-sconsign, --warn=no-corrupt-sconsign
+Enables or disables warnings about unfamiliar signature data in
+.B .sconsign
+files.
+These warnings are enabled by default.
+
.TP
--warn=dependency, --warn=no-dependency
Enables or disables warnings about dependencies.
These warnings are enabled by default.
.TP
---warn=missing-sconscript, --warn=no-missing-sconscript
+--warn=duplicate-environment, --warn=no-duplicate-environment
Enables or disables warnings about missing SConscript files.
+
+.TP
+--warn=misleading-keywords, --warn=no-misleading-keywords
+Enables or disables warnings about use of the misspelled keywords
+.B targets
+and
+.B sources
+when calling Builders.
+(Note the last
+.B s
+characters, the correct spellings are
+.B target
+and
+.B source.)
These warnings are enabled by default.
.TP
---no-print-directory
-Turn off -w, even if it was turned on implicitly.
+--warn=missing-sconscript, --warn=no-missing-sconscript
+Enables or disables warnings about attempts to specify a build
+of a target with two different construction environments
+that use the same action.
+These warnings are enabled by default.
+
+.TP
+--warn=no-md5-module, --warn=no-no-md5-module
+Enables or disables warnings about the version of Python
+not having an MD5 checksum module available.
+These warnings are enabled by default.
+
+.TP
+--warn=no-metaclass-support, --warn=no-no-metaclass-support
+Enables or disables warnings about the version of Python
+not supporting metaclasses when the
+.B --debug=memoizer
+option is used.
+These warnings are enabled by default.
+
+.TP
+--warn=no-parallel-support, --warn=no-no-parallel-support
+Enables or disables warnings about the version of Python
+not being able to support parallel builds when the
+.B -j
+option is used.
+These warnings are enabled by default.
+
+.TP
+--warn=reserved-variable, --warn=no-reserved-variable
+Enables or disables warnings about attempts to set the
+reserved construction variable names
+.BR TARGET ,
+.BR TARGETS ,
+.BR SOURCE
+or
+.BR SOURCES .
+These warnings are disabled by default.
.\" .TP
.\" .RI --write-filenames= file
.\" Warn when an undefined variable is referenced.
.TP
-.RI -Y " repository" ", --repository=" repository
+.RI -Y " repository" ", --repository=" repository ", --srcdir=" repository
Search the specified repository for any input and target
files not found in the local directory hierarchy. Multiple
.B -Y
a given derived file has been built in-place
or retrieved from the cache.
+The
+.BR NoCache ()
+method can be used to disable caching of specific files. This can be
+useful if inputs and/or outputs of some tool are impossible to
+predict or prohibitively large.
+
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.TP
.RI Clean( targets ", " files_or_dirs )
rename ])
.EE
+.IP
Note that the
.BR Command ()
function will usually assume, by default,
env.Command(env.Dir('$DISTDIR')), None, make_distdir)
.EE
+.IP
(Also note that SCons will usually
automatically create any directory necessary to hold a target file,
so you normally don't need to create directories by hand.)
env=Environment()
print env.Dump('CCCOM')
.EE
+.IP
will print:
.ES
'$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES'
env=Environment()
print env.Dump()
.EE
+.IP
will print:
.ES
{ 'AR': 'ar',
'!pkg-config libpng12 --cflags --libs'])
.EE
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP
+.RI NoCache( target ", ...)"
+.TP
+.RI env.NoCache( target ", ...)"
+Specifies a list of files which should
+.I not
+be cached whenever the
+.BR CacheDir ()
+method has been activated.
+The specified targets may be a list
+or an individual target.
+Multiple calls to
+.BR NoCache ()
+are legal,
+and prevent each specified target
+from being removed by calls to the
+.B -c
+option.
+
+Multiple files should be specified
+either as separate arguments to the
+.BR NoCache ()
+method, or as a list.
+.BR NoCache ()
+will also accept the return value of any of the construction environment
+Builder methods.
+
+Calling
+.BR NoCache ()
+on directories and other non-File Node types has no effect because
+only File Nodes are cached.
+
+Examples:
+
+.ES
+NoCache('foo.elf')
+NoCache(env.Program('hello', 'hello.c'))
+.EE
+
'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
.TP
.RI NoClean( target ", ...)"
+ CCFLAGS, LINKFLAGS
.EE
+.IP
Any other strings not associated with options
are assumed to be the names of libraries
and added to the
which verifies that the specified path is an existing file;
.BR PathOption.PathIsDir ,
which verifies that the specified path is an existing directory;
-and
.BR PathOption.PathIsDirCreate ,
-which verifies that the specified path is a directory,
-and will create the specified directory if the path does not exist.
+which verifies that the specified path is a directory
+and will create the specified directory if the path does not exist;
+and
+.BR PathOption.PathAccept ,
+which simply accepts the specific path name argument without validation,
+and which is suitable if you want your users
+to be able to specify a directory path that will be
+created as part of the build process, for example.
You may supply your own
.I validator
function,
env.MakeDirectory('new_directory', [])
.EE
+.IP
Note that the call to the MakeDirectory Builder
needs to specify an empty source list
to make the string represent the builder's target;
Scanner accepts the following arguments:
.IP function
-A Python function that will process
+This can be either:
+1) a Python function that will process
the Node (file)
and return a list of strings (file names)
representing the implicit
-dependencies found in the contents.
-The function takes three or four arguments:
+dependencies found in the contents;
+or:
+2) a dictionary that maps keys
+(typically the file suffix, but see below for more discussion)
+to other Scanners that should be called.
+
+If the argument is actually a Python function,
+the function must take three or four arguments:
def scanner_function(node, env, path):
- def scanner_function(node, env, path, arg):
+ def scanner_function(node, env, path, arg=None):
The
.B node
on a POSIX system
is <literal>/usr/local/bin:/bin:/usr/bin</literal>.
The default value of the &PATH; environment variable
- on a Win32 system comes from the Windows registry
+ on a Windows system comes from the Windows registry
value for the command interpreter.
If you want to execute any commands--compilers, linkers, etc.--that
are not in these default locations,
<screen>
% <userinput>scons -Q install</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
Install file: "hello" as "/usr/bin/hello"
</screen>
<screen>
% <userinput>scons -Q install-bin</userinput>
- cc -c -o foo.o foo.c
+ cc -o foo.o -c foo.c
cc -o foo foo.o
Install file: "foo" as "/usr/bin/foo"
% <userinput>scons -Q install-lib</userinput>
- cc -c -o bar.o bar.c
- ar r libbar.a bar.o
+ cc -o bar.o -c bar.c
+ ar rc libbar.a bar.o
ranlib libbar.a
Install file: "libbar.a" as "/usr/lib/libbar.a"
% <userinput>scons -Q -c /</userinput>
Removed libbar.a
Removed /usr/lib/libbar.a
% <userinput>scons -Q install</userinput>
- cc -c -o foo.o foo.c
+ cc -o foo.o -c foo.c
cc -o foo foo.o
Install file: "foo" as "/usr/bin/foo"
- cc -c -o bar.o bar.c
- ar r libbar.a bar.o
+ cc -o bar.o -c bar.c
+ ar rc libbar.a bar.o
ranlib libbar.a
Install file: "libbar.a" as "/usr/lib/libbar.a"
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o goodbye.o goodbye.c
- cc -c -o hello.o hello.c
+ cc -o goodbye.o -c goodbye.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o goodbye.o -L/usr/dir1 -Ldir2 -lfoo1 -lfoo2
</screen>
<screen>
% <userinput>scons -Q</userinput>
foobuild < file.input > file.foo
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q -c</userinput>
Removed hello.o
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q -c</userinput>
Removed hello.o
Removed hello
% <userinput>scons -Q --cache-show</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q -c</userinput>
Removed hello.o
Removed hello.o
Removed hello
% <userinput>scons -Q --cache-disable</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q --cache-disable</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q -c</userinput>
Removed hello.o
Removed hello
% <userinput>scons -Q --cache-disable</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q --cache-force</userinput>
scons: `.' is up to date.
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o foo.o foo.c
+ cc -o foo.o -c foo.c
cc -o foo foo.o
% <userinput>scons -Q bar</userinput>
Don't forget to copy `bar' to the archive!
- cc -c -o bar.o bar.c
+ cc -o bar.o -c bar.c
cc -o bar bar.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q</userinput>
scons: `hello' is up to date.
% <userinput>scons -Q goodbye</userinput>
- cc -c -o goodbye.o goodbye.c
+ cc -o goodbye.o -c goodbye.c
cc -o goodbye goodbye.o
</screen>
<screen>
% <userinput>scons -Q .</userinput>
- cc -c -o goodbye.o goodbye.c
+ cc -o goodbye.o -c goodbye.c
cc -o goodbye goodbye.o
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o prog1.o prog1.c
+ cc -o prog1.o -c prog1.c
cc -o prog1 prog1.o
- cc -c -o prog3.o prog3.c
+ cc -o prog3.o -c prog3.c
cc -o prog3 prog3.o
% <userinput>scons -Q .</userinput>
- cc -c -o prog2.o prog2.c
+ cc -o prog2.o -c prog2.c
cc -o prog2 prog2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o prog1/foo.o prog1/foo.c
- cc -c -o prog1/main.o prog1/main.c
+ cc -o prog1/foo.o -c prog1/foo.c
+ cc -o prog1/main.o -c prog1/main.c
cc -o prog1/main prog1/main.o prog1/foo.o
% <userinput>scons -Q</userinput>
scons: `prog1' is up to date.
% <userinput>scons -Q .</userinput>
- cc -c -o prog2/bar.o prog2/bar.c
- cc -c -o prog2/main.o prog2/main.c
+ cc -o prog2/bar.o -c prog2/bar.c
+ cc -o prog2/main.o -c prog2/main.c
cc -o prog2/main prog2/main.o prog2/bar.o
</screen>
% <userinput>scons -Q</userinput>
scons: *** No targets specified and no Default() targets found. Stop.
% <userinput>scons -Q .</userinput>
- cc -c -o prog1.o prog1.c
+ cc -o prog1.o -c prog1.c
cc -o prog1 prog1.o
- cc -c -o prog2.o prog2.c
+ cc -o prog2.o -c prog2.c
cc -o prog2 prog2.o
</screen>
DEFAULT_TARGETS is ['prog1']
scons: done reading SConscript files.
scons: Building targets ...
- cc -c -o prog1.o prog1.c
+ cc -o prog1.o -c prog1.c
cc -o prog1 prog1.o
scons: done building targets.
</screen>
DEFAULT_TARGETS is now ['prog1', 'prog2']
scons: done reading SConscript files.
scons: Building targets ...
- cc -c -o prog1.o prog1.c
+ cc -o prog1.o -c prog1.c
cc -o prog1 prog1.o
- cc -c -o prog2.o prog2.c
+ cc -o prog2.o -c prog2.c
cc -o prog2 prog2.o
scons: done building targets.
</screen>
<screen>
% <userinput>scons -Q</userinput>
BUILD_TARGETS is ['prog1']
- cc -c -o prog1.o prog1.c
+ cc -o prog1.o -c prog1.c
cc -o prog1 prog1.o
% <userinput>scons -Q prog2</userinput>
BUILD_TARGETS is ['prog2']
- cc -c -o prog2.o prog2.c
+ cc -o prog2.o -c prog2.c
cc -o prog2 prog2.o
% <userinput>scons -Q -c .</userinput>
BUILD_TARGETS is ['.']
<screen>
% <userinput>scons -Q debug=0</userinput>
- cc -c -o prog.o prog.c
+ cc -o prog.o -c prog.c
cc -o prog prog.o
% <userinput>scons -Q debug=0</userinput>
scons: `.' is up to date.
% <userinput>scons -Q debug=1</userinput>
- cc -g -c -o prog.o prog.c
+ cc -o prog.o -c -g prog.c
cc -o prog prog.o
% <userinput>scons -Q debug=1</userinput>
scons: `.' is up to date.
<screen>
% <userinput>scons -Q RELEASE=1</userinput>
- cc -DRELEASE_BUILD=1 -c -o bar.o bar.c
- cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+ cc -o bar.o -c -DRELEASE_BUILD=1 bar.c
+ cc -o foo.o -c -DRELEASE_BUILD=1 foo.c
cc -o foo foo.o bar.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -DRELEASE_BUILD=1 -c -o bar.o bar.c
- cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+ cc -o bar.o -c -D['RELEASE_BUILD=', 1] bar.c
+ cc -o foo.o -c -D['RELEASE_BUILD=', 1] foo.c
cc -o foo foo.o bar.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -DRELEASE_BUILD=0 -c -o bar.o bar.c
- cc -DRELEASE_BUILD=0 -c -o foo.o foo.c
+ cc -o bar.o -c -D['RELEASE_BUILD=', 0] bar.c
+ cc -o foo.o -c -D['RELEASE_BUILD=', 0] foo.c
cc -o foo foo.o bar.o
</screen>
<screen>
% <userinput>scons -Q RELEASE=yes foo.o</userinput>
- cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+ cc -o foo.o -c -D['RELEASE_BUILD=', True] foo.c
</screen>
<screen>
% <userinput>scons -Q RELEASE=t foo.o</userinput>
- cc -DRELEASE_BUILD=1 -c -o foo.o foo.c
+ cc -o foo.o -c -D['RELEASE_BUILD=', True] foo.c
</screen>
<para>
<screen>
% <userinput>scons -Q RELEASE=no foo.o</userinput>
- cc -DRELEASE_BUILD=0 -c -o foo.o foo.c
+ cc -o foo.o -c -D['RELEASE_BUILD=', False] foo.c
</screen>
<screen>
% <userinput>scons -Q RELEASE=f foo.o</userinput>
- cc -DRELEASE_BUILD=0 -c -o foo.o foo.c
+ cc -o foo.o -c -D['RELEASE_BUILD=', False] foo.c
</screen>
<para>
scons: *** Error converting option: RELEASE
Invalid value for boolean option: bad_value
- File "SConstruct", line 4, in ?
+ File "/home/my/project/SConstruct", line 4, in ?
</screen>
</section>
<screen>
% <userinput>scons -Q COLOR=red foo.o</userinput>
- cc -DCOLOR="red" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="red" foo.c
% <userinput>scons -Q COLOR=blue foo.o</userinput>
- cc -DCOLOR="blue" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="blue" foo.c
% <userinput>scons -Q COLOR=green foo.o</userinput>
- cc -DCOLOR="green" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="green" foo.c
</screen>
<para>
% <userinput>scons -Q COLOR=magenta foo.o</userinput>
scons: *** Invalid value for option COLOR: magenta
- File "SConstruct", line 5, in ?
+ File "/home/my/project/SConstruct", line 5, in ?
</screen>
<para>
<screen>
% <userinput>scons -Q COLOR=navy foo.o</userinput>
- cc -DCOLOR="blue" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="blue" foo.c
</screen>
<para>
% <userinput>scons -Q COLOR=Red foo.o</userinput>
scons: *** Invalid value for option COLOR: Red
- File "SConstruct", line 5, in ?
+ File "/home/my/project/SConstruct", line 5, in ?
% <userinput>scons -Q COLOR=BLUE foo.o</userinput>
scons: *** Invalid value for option COLOR: BLUE
- File "SConstruct", line 5, in ?
+ File "/home/my/project/SConstruct", line 5, in ?
% <userinput>scons -Q COLOR=nAvY foo.o</userinput>
scons: *** Invalid value for option COLOR: nAvY
- File "SConstruct", line 5, in ?
+ File "/home/my/project/SConstruct", line 5, in ?
</screen>
<para>
<screen>
% <userinput>scons -Q COLOR=Red foo.o</userinput>
- cc -DCOLOR="Red" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="Red" foo.c
% <userinput>scons -Q COLOR=BLUE foo.o</userinput>
- cc -DCOLOR="BLUE" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="BLUE" foo.c
% <userinput>scons -Q COLOR=nAvY foo.o</userinput>
- cc -DCOLOR="blue" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="blue" foo.c
% <userinput>scons -Q COLOR=green foo.o</userinput>
- cc -DCOLOR="green" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="green" foo.c
</screen>
<para>
<screen>
% <userinput>scons -Q COLOR=Red foo.o</userinput>
- cc -DCOLOR="red" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="red" foo.c
% <userinput>scons -Q COLOR=nAvY foo.o</userinput>
- cc -DCOLOR="blue" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="blue" foo.c
% <userinput>scons -Q COLOR=GREEN foo.o</userinput>
- cc -DCOLOR="green" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLOR="green" foo.c
</screen>
</section>
<screen>
% <userinput>scons -Q COLORS=red,blue foo.o</userinput>
- cc -DCOLORS="red blue" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLORS="red blue" foo.c
% <userinput>scons -Q COLORS=blue,green,red foo.o</userinput>
- cc -DCOLORS="blue green red" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLORS="blue green red" foo.c
</screen>
<para>
<screen>
% <userinput>scons -Q COLORS=all foo.o</userinput>
- cc -DCOLORS="red green blue" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLORS="red green blue" foo.c
% <userinput>scons -Q COLORS=none foo.o</userinput>
- cc -DCOLORS="" -c -o foo.o foo.c
+ cc -o foo.o -c -DCOLORS="" foo.c
</screen>
<para>
scons: *** Error converting option: COLORS
Invalid value(s) for option: magenta
- File "SConstruct", line 5, in ?
+ File "/home/my/project/SConstruct", line 5, in ?
</screen>
</section>
<screen>
% <userinput>scons -Q foo.o</userinput>
- cc -DCONFIG_FILE="/etc/my_config" -c -o foo.o foo.c
+ cc -o foo.o -c -DCONFIG_FILE="/etc/my_config" foo.c
% <userinput>scons -Q CONFIG=/usr/local/etc/other_config foo.o</userinput>
scons: `foo.o' is up to date.
</screen>
% <userinput>scons -Q CONFIG=/does/not/exist foo.o</userinput>
scons: *** Path for option CONFIG does not exist: /does/not/exist
- File "SConstruct", line 6, in ?
+ File "/home/my/project/SConstruct", line 6, in ?
</screen>
<para>
<screen>
% <userinput>scons -Q foo.o</userinput>
- cc -DPACKAGE="/opt/location" -c -o foo.o foo.c
+ cc -o foo.o -c -DPACKAGE="/opt/location" foo.c
% <userinput>scons -Q PACKAGE=/usr/local/location foo.o</userinput>
- cc -DPACKAGE="/usr/local/location" -c -o foo.o foo.c
+ cc -o foo.o -c -DPACKAGE="/usr/local/location" foo.c
% <userinput>scons -Q PACKAGE=yes foo.o</userinput>
- cc -DPACKAGE="1" -c -o foo.o foo.c
+ cc -o foo.o -c -D['PACKAGE="', True, '"'] foo.c
% <userinput>scons -Q PACKAGE=no foo.o</userinput>
- cc -DPACKAGE="0" -c -o foo.o foo.c
+ cc -o foo.o -c -D['PACKAGE="', False, '"'] foo.c
</screen>
</section>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q</userinput>
scons: `.' is up to date.
<screen>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q hello</userinput>
scons: `hello' is up to date.
<screen>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>touch hello.c</userinput>
% <userinput>scons -Q hello</userinput>
<screen>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>edit hello.c</userinput>
[CHANGE THE CONTENTS OF hello.c]
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>touch hello.c</userinput>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>edit hello.c</userinput>
[CHANGE THE CONTENTS OF hello.c]
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>edit hello.c</userinput>
[CHANGE A COMMENT IN hello.c]
% <userinput>scons -Q hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
scons: `hello' is up to date.
</screen>
<screen>
% <userinput>scons -Q hello</userinput>
- cc -I. -c -o hello.o hello.c
+ cc -o hello.o -c -I. hello.c
cc -o hello hello.o
% <userinput>scons -Q hello</userinput>
scons: `hello' is up to date.
% <userinput>edit hello.h</userinput>
[CHANGE THE CONTENTS OF hello.h]
% <userinput>scons -Q hello</userinput>
- cc -I. -c -o hello.o hello.c
+ cc -o hello.o -c -I. hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q hello</userinput>
- cc -Iinclude -I/home/project/inc -c -o hello.o hello.c
+ cc -o hello.o -c -Iinclude -I/home/project/inc hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q --implicit-cache hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q hello</userinput>
scons: `hello' is up to date.
<screen>
% <userinput>scons -Q --implicit-deps-changed hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q hello</userinput>
scons: `hello' is up to date.
<screen>
% <userinput>scons -Q --implicit-deps-unchanged hello</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q hello</userinput>
scons: `hello' is up to date.
ENV => { 'PATH' => '/bin:/usr/bin' },
-And on a Win32 system (Windows NT), the default construction variables
+And on a Windows system (Windows NT), the default construction variables
are (unless the default rule style is set using the B<DefaultRules>
method):
<screen>
% <userinput>scons -Q</userinput>
- gcc -O2 -c -o foo.o foo.c
+ gcc -o foo.o -c -O2 foo.c
gcc -o foo foo.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -g -c -o bar.o bar.c
+ cc -o bar.o -c -g bar.c
cc -o bar bar.o
- cc -O2 -c -o foo.o foo.c
+ cc -o foo.o -c -O2 foo.c
cc -o foo foo.o
</screen>
% <userinput>scons -Q</userinput>
scons: *** Two environments with different actions were specified for the same target: foo.o
- File "SConstruct", line 6, in ?
+ File "/home/my/project/SConstruct", line 6, in ?
</screen>
<para>
<screen>
% <userinput>scons -Q</userinput>
- cc -g -c -o foo-dbg.o foo.c
+ cc -o foo-dbg.o -c -g foo.c
cc -o foo-dbg foo-dbg.o
- cc -O2 -c -o foo-opt.o foo.c
+ cc -o foo-opt.o -c -O2 foo.c
cc -o foo-opt foo-opt.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- gcc -c -o foo.o foo.c
+ gcc -o foo.o -c foo.c
gcc -o foo foo.o
- gcc -g -c -o foo-dbg.o foo.c
+ gcc -o foo-dbg.o -c -g foo.c
gcc -o foo-dbg foo-dbg.o
- gcc -O2 -c -o foo-opt.o foo.c
+ gcc -o foo-opt.o -c -O2 foo.c
gcc -o foo-opt foo-opt.o
</screen>
<para>
- And on Win32:
+ And on Windows:
</para>
<screen>
% <userinput>scons -Q</userinput>
- cc -DDEFINE2 -c -o foo.o foo.c
+ cc -o foo.o -c -DDEFINE2 foo.c
cc -o foo foo.o
</screen>
CCFLAGS = -DDEFINE2
scons: done reading SConscript files.
scons: Building targets ...
- cc -DDEFINE2 -c -o bar.o bar.c
+ cc -o bar.o -c -DDEFINE2 bar.c
cc -o bar bar.o
- cc -DDEFINE2 -c -o foo.o foo.c
+ cc -o foo.o -c -DDEFINE2 foo.c
cc -o foo foo.o
scons: done building targets.
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -DMY_VALUE -DLAST -c -o foo.o foo.c
+ cc -o foo.o -c -DMY_VALUE -DLAST foo.c
cc -o foo foo.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -DFIRST -DMY_VALUE -c -o foo.o foo.c
+ cc -o foo.o -c -DFIRST -DMY_VALUE foo.c
cc -o foo foo.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o prog1/foo1.o prog1/foo1.c
- cc -c -o prog1/foo2.o prog1/foo2.c
- cc -c -o prog1/main.o prog1/main.c
+ cc -o prog1/foo1.o -c prog1/foo1.c
+ cc -o prog1/foo2.o -c prog1/foo2.c
+ cc -o prog1/main.o -c prog1/main.c
cc -o prog1/prog1 prog1/main.o prog1/foo1.o prog1/foo2.o
- cc -c -o prog2/bar1.o prog2/bar1.c
- cc -c -o prog2/bar2.o prog2/bar2.c
- cc -c -o prog2/main.o prog2/main.c
+ cc -o prog2/bar1.o -c prog2/bar1.c
+ cc -o prog2/bar2.o -c prog2/bar2.c
+ cc -o prog2/main.o -c prog2/main.c
cc -o prog2/prog2 prog2/main.o prog2/bar1.o prog2/bar2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o lib/foo1.o lib/foo1.c
- cc -c -o src/prog/foo2.o src/prog/foo2.c
- cc -c -o src/prog/main.o src/prog/main.c
+ cc -o lib/foo1.o -c lib/foo1.c
+ cc -o src/prog/foo2.o -c src/prog/foo2.c
+ cc -o src/prog/main.o -c src/prog/main.c
cc -o src/prog/prog src/prog/main.o lib/foo1.o src/prog/foo2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o src/prog/foo2.o src/prog/foo2.c
- cc -c -o src/prog/main.o src/prog/main.c
- cc -c -o /usr/joe/lib/foo1.o /usr/joe/lib/foo1.c
+ cc -o src/prog/foo2.o -c src/prog/foo2.c
+ cc -o src/prog/main.o -c src/prog/main.c
+ cc -o /usr/joe/lib/foo1.o -c /usr/joe/lib/foo1.c
cc -o src/prog/prog src/prog/main.o /usr/joe/lib/foo1.o src/prog/foo2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o bar/bar.o bar/bar.c
- cc -c -o foo/foo.o foo/foo.c
- ar r libprog.a foo/foo.o bar/bar.o
+ cc -o bar/bar.o -c bar/bar.c
+ cc -o foo/foo.o -c foo/foo.c
+ ar rc libprog.a foo/foo.o bar/bar.o
ranlib libprog.a
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q /usr/bin</userinput>
Install file: "hello" as "/usr/bin/hello"
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
% <userinput>scons -Q install</userinput>
Install file: "hello" as "/usr/bin/hello"
<screen>
% <userinput>scons -Q install</userinput>
- cc -c -o goodbye.o goodbye.c
+ cc -o goodbye.o -c goodbye.c
cc -o goodbye goodbye.o
Install file: "goodbye" as "/usr/bin/goodbye"
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
Install file: "hello" as "/usr/bin/hello"
</screen>
<screen>
% <userinput>scons -Q install</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
Install file: "hello" as "/usr/bin/hello-new"
</screen>
<screen>
% <userinput>scons -Q install</userinput>
- cc -c -o goodbye.o goodbye.c
+ cc -o goodbye.o -c goodbye.c
cc -o goodbye goodbye.o
Install file: "goodbye" as "/usr/bin/goodbye-new"
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
Install file: "hello" as "/usr/bin/hello-new"
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o new_hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o file1.o file1.c
- cc -c -o file2.o file2.c
- cc -c -o main.o main.c
+ cc -o file1.o -c file1.c
+ cc -o file2.o -c file2.c
+ cc -o main.o -c main.c
cc -o main main.o file1.o file2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o file1.o file1.c
- cc -c -o file2.o file2.c
- cc -c -o main.o main.c
+ cc -o file1.o -c file1.c
+ cc -o file2.o -c file2.c
+ cc -o main.o -c main.c
cc -o program main.o file1.o file2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o bar1.o bar1.c
- cc -c -o bar2.o bar2.c
+ cc -o bar1.o -c bar1.c
+ cc -o bar2.o -c bar2.c
cc -o bar bar1.o bar2.o
- cc -c -o foo.o foo.c
+ cc -o foo.o -c foo.c
cc -o foo foo.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o bar1.o bar1.c
- cc -c -o bar2.o bar2.c
- cc -c -o common1.o common1.c
- cc -c -o common2.o common2.c
+ cc -o bar1.o -c bar1.c
+ cc -o bar2.o -c bar2.c
+ cc -o common1.o -c common1.c
+ cc -o common2.o -c common2.c
cc -o bar bar1.o bar2.o common1.o common2.o
- cc -c -o foo.o foo.c
+ cc -o foo.o -c foo.c
cc -o foo foo.o common1.o common2.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o f1.o f1.c
- cc -c -o f2.o f2.c
- cc -c -o f3.o f3.c
- ar r libfoo.a f1.o f2.o f3.o
+ cc -o f1.o -c f1.c
+ cc -o f2.o -c f2.c
+ cc -o f3.o -c f3.c
+ ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o f1.os f1.c
- cc -c -o f2.os f2.c
- cc -c -o f3.os f3.c
- cc -shared -o libfoo.so f1.os f2.os f3.os
+ cc -o f1.os -c f1.c
+ cc -o f2.os -c f2.c
+ cc -o f3.os -c f3.c
+ cc -o libfoo.so -shared f1.os f2.os f3.os
</screen>
<para>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o f1.o f1.c
- cc -c -o f2.o f2.c
- cc -c -o f3.o f3.c
- ar r libfoo.a f1.o f2.o f3.o
+ cc -o f1.o -c f1.c
+ cc -o f2.o -c f2.c
+ cc -o f3.o -c f3.c
+ ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a
- cc -c -o prog.o prog.c
+ cc -o prog.o -c prog.c
cc -o prog prog.o -L. -lfoo -lbar
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o prog.o prog.c
+ cc -o prog.o -c prog.c
cc -o prog prog.o -L/usr/lib -L/usr/local/lib -lm
</screen>
</para>
- <scons_output example="print" os="posix">
+ <scons_output example="exists" os="posix">
<scons_output_command>scons -Q</scons_output_command>
</scons_output>
</para>
- <scons_example name="exists">
+ <scons_example name="read">
<file name="SConstruct" printme="1">
hello_c = File('hello.c')
contents = hello_c.read()
</para>
- <scons_output example="print" os="posix">
+ <scons_output example="read" os="posix">
<scons_output_command>scons -Q</scons_output_command>
</scons_output>
<screen>
% <userinput>scons -Q</userinput>
- cc -DGOODBYE -c -o goodbye.o goodbye.c
- cc -DHELLO -c -o hello.o hello.c
+ cc -o goodbye.o -c -DGOODBYE goodbye.c
+ cc -o hello.o -c -DHELLO hello.c
cc -o hello hello.o goodbye.o
</screen>
% <userinput>scons -Q</userinput>
The object file is: hello.o
The program file is: hello
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- The object file is: hello.o
- The program file is: hello
- cc -c -o hello.o hello.c
+ hello does not exist!
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
</para>
- <scons_example name="exists">
+ <scons_example name="read">
<file name="SConstruct" printme="1">
hello_c = File('hello.c')
contents = hello_c.read()
</para>
- <scons_output example="print" os="posix">
+ <scons_output example="read" os="posix">
<scons_output_command>scons -Q</scons_output_command>
</scons_output>
&SCons; has received contributions
from many other people, of course:
- Matt Balvin (extending long command-line support on Win32),
+ Matt Balvin (extending long command-line support on Windows),
Allen Bierbaum (extensions and fixes to Options),
Steve Christensen (help text sorting and function action signature fixes),
Michael Cook (avoiding losing signal bits from executed commands),
Derrick 'dman' Hudson (),
- Alex Jacques (work on the Win32 scons.bat file),
+ Alex Jacques (work on the Windows scons.bat file),
Stephen Kennedy (performance enhancements),
Lachlan O'Dea (SharedObject() support for masm
and normalized paths for the WhereIs() function),
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--->
-
-<!--
-
-
-=head2 Repository dependency analysis
-
-Due to its built-in scanning, Cons will search the specified repository
-trees for included F<.h> files. Unless the compiler also knows about the
-repository trees, though, it will be unable to find F<.h> files that only
-exist in a repository. If, for example, the F<hello.c> file includes the
-F<hello.h> file in its current directory:
-
- % cons -R /usr/all/repository hello
- gcc -c /usr/all/repository/hello.c -o hello.o
- /usr/all/repository/hello.c:1: hello.h: No such file or directory
-
-Solving this problem forces some requirements onto the way construction
-environments are defined and onto the way the C C<#include> preprocessor
-directive is used to include files.
-
-In order to inform the compiler about the repository trees, Cons will add
-appropriate C<-I> flags to the compilation commands. This means that the
-C<CPPPATH> variable in the construction environment must explicitly specify
-all subdirectories which are to be searched for included files, including the
-current directory. Consequently, we can fix the above example by changing
-the environment creation in the F<Construct> file as follows:
-
- $env = new cons(
- CC => 'gcc',
- CPPPATH => '.',
- LIBS => 'libworld.a',
- );
-
-Due to the definition of the C<CPPPATH> variable, this yields, when we
-re-execute the command:
-
- % cons -R /usr/all/repository hello
- gcc -c -I. -I/usr/all/repository /usr/all/repository/hello.c -o hello.o
- gcc -o hello hello.o /usr/all/repository/libworld.a
-
-The order of the C<-I> flags replicates, for the C preprocessor, the same
-repository-directory search path that Cons uses for its own dependency
-analysis. If there are multiple repositories and multiple C<CPPPATH>
-directories, Cons will append the repository directories to the beginning of
-each C<CPPPATH> directory, rapidly multiplying the number of C<-I> flags.
-As an extreme example, a F<Construct> file containing:
-
- Repository qw(
- /u1
- /u2
- );
-
- $env = new cons(
- CPPPATH => 'a:b:c',
- );
-
-Would yield a compilation command of:
-
- cc -Ia -I/u1/a -I/u2/a -Ib -I/u1/b -I/u2/b -Ic -I/u1/c -I/u2/c -c hello.c -o hello.o
-
-In order to shorten the command lines as much as possible, Cons will
-remove C<-I> flags for any directories, locally or in the repositories,
-which do not actually exist. (Note that the C<-I> flags are not included
-in the MD5 signature calculation for the target file, so the target will
-not be recompiled if the compilation command changes due to a directory
-coming into existence.)
-
-Because Cons relies on the compiler's C<-I> flags to communicate the
-order in which repository directories must be searched, Cons' handling
-of repository directories is fundamentally incompatible with using
-double-quotes on the C<#include> directives in any C source code that
-you plan to modify:
-
- #include "file.h" /* DON'T USE DOUBLE-QUOTES LIKE THIS */
-
-This is because most C preprocessors, when faced with such a directive, will
-always first search the directory containing the source file. This
-undermines the elaborate C<-I> options that Cons constructs to make the
-preprocessor conform to its preferred search path.
-
-Consequently, when using repository trees in Cons, B<always> use
-angle-brackets for included files in any C source (.c or .h) files that
-you plan to modify locally:
-
- #include <file.h> /* USE ANGLE-BRACKETS INSTEAD */
-
-Code that will not change can still safely use double quotes on #include
-lines.
-
-
-=head2 Repository_List
-
-Cons provides a C<Repository_List> command to return a list of all
-repository directories in their current search order. This can be used for
-debugging, or to do more complex Perl stuff:
-
- @list = Repository_List;
- print join(' ', @list), "\n";
-
-
-=head2 Repository interaction with other Cons features
-
-Cons' handling of repository trees interacts correctly with other Cons
-features, which is to say, it generally does what you would expect.
-
-Most notably, repository trees interact correctly, and rather powerfully,
-with the 'Link' command. A repository tree may contain one or more
-subdirectories for version builds established via C<Link> to a source
-subdirectory. Cons will search for derived files in the appropriate build
-subdirectories under the repository tree.
-
-->
<para>
a directory copy of the source code tree.
(Note that this is not the sort of repository
maintained by a source code management system
- like BitKeeper, CVS, or Subversion.
+ like BitKeeper, CVS, or Subversion.)
+ <!--
For information about using &SCons;
with these systems, see the section,
"Fetching Files From Source Code Management Systems,"
below.)
+ -->
You use the &Repository; method
to tell &SCons; to search one or more
central code repositories (in order)
<file name="SConstruct" printme="1">
env = Environment()
env.Program('hello.c')
- Repository('/usr/repository1', '/usr/repository2')
+ Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
</file>
<file name="hello.c">
int main() { printf("Hello, world!\n"); }
<file name="SConstruct">
env = Environment()
env.Program('hello.c')
- Repository('/usr/repository1', '/usr/repository2')
+ Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
</file>
- <file name="hello.c">
+ <file name="__ROOT__/usr/repository1/hello.c">
int main() { printf("Hello, world!\n"); }
</file>
</scons_example>
<scons_output example="ex2">
<scons_output_command>scons -Q</scons_output_command>
- gcc -c /usr/repository1/hello.c -o hello.o
- gcc -o hello hello.o
</scons_output>
<para>
<file name="SConstruct">
env = Environment()
env.Program('hello.c')
- Repository('/usr/repository1', '/usr/repository2')
+ Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
</file>
- <file name="hello.c">
+ <file name="__ROOT__/usr/repository2/hello.c">
int main() { printf("Hello, world!\n"); }
</file>
</scons_example>
</section>
+ <section>
+ <title>Finding <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ We've already seen that SCons will scan the contents of
+ a source file for <literal>#include</literal> file names
+ and realize that targets built from that source file
+ also depend on the <literal>#include</literal> file(s).
+ For each directory in the &cv-CPPPATH; list,
+ &SCons; will actually search the corresponding directories
+ in any repository trees and establish the
+ correct dependencies on any
+ <literal>#include</literal> files that it finds
+ in repository directory.
+
+ </para>
+
+ <para>
+
+ Unless the C compiler also knows about these directories
+ in the repository trees, though,
+ it will be unable to find the <literal>#include</literal> files.
+ If, for example, the &hello_c; file in
+ our previous example includes the &hello.h;
+ in its current directory,
+ and the &hello.h; only exists in the repository:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ hello.c:1: hello.h: No such file or directory
+ </screen>
+
+ <para>
+
+ In order to inform the C compiler about the repositories,
+ &SCons; will add appropriate
+ <literal>-I</literal> flags to the compilation commands
+ for each directory in the &cv-CPPPATH; list.
+ So if we add the current directory to the
+ construction environment &cv-CPPPATH; like so:
+
+ </para>
+
+ <scons_example name="CPPPATH">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then re-executing &SCons; yields:
+
+ </para>
+
+ <scons_output example="CPPPATH">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ The order of the <literal>-I</literal> options replicates,
+ for the C preprocessor,
+ the same repository-directory search path
+ that &SCons; uses for its own dependency analysis.
+ If there are multiple repositories and multiple &cv-CPPPATH;
+ directories, &SCons; will add the repository directories
+ to the beginning of each &cv-CPPPATH; directory,
+ rapidly multiplying the number of <literal>-I</literal> flags.
+ If, for example, the &cv-CPPPATH; contains three directories
+ (and shorter repository path names!):
+
+ </para>
+
+ <scons_example name="CPPPATH3">
+ <file name="SConstruct" printme="1">
+ env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3'])
+ env.Program('hello.c')
+ Repository('__ROOT__/r1', '__ROOT__/r2')
+ </file>
+ <file name="hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <para>
+
+ Then we'll end up with nine <literal>-I</literal> options
+ on the command line,
+ three (for each of the &cv-CPPPATH; directories)
+ times three (for the local directory plus the two repositories):
+
+ </para>
+
+ <scons_output example="CPPPATH3">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+<!--
+
+Cons classic did the following, does SCons?
+
+In order to shorten the command lines as much as possible, Cons will
+remove C<-I> flags for any directories, locally or in the repositories,
+which do not actually exist. (Note that the C<-I> flags are not included
+in the MD5 signature calculation for the target file, so the target will
+not be recompiled if the compilation command changes due to a directory
+coming into existence.)
+
+-->
+
+ <section>
+ <title>Limitations on <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ &SCons; relies on the C compiler's
+ <literal>-I</literal> options to control the order in which
+ the preprocessor will search the repository directories
+ for <literal>#include</literal> files.
+ This causes a problem, however, with how the C preprocessor
+ handles <literal>#include</literal> lines with
+ the file name included in double-quotes.
+
+ </para>
+
+ <para>
+
+ As we've seen,
+ &SCons; will compile the &hello_c; file from
+ the repository if it doesn't exist in
+ the local directory.
+ If, however, the &hello_c; file in the repository contains
+ a <literal>#include</literal> line with the file name in
+ double quotes:
+
+ </para>
+
+ <programlisting>
+ #include "hello.h"
+ int
+ main(int argc, char *argv[])
+ {
+ printf(HELLO_MESSAGE);
+ return (0);
+ }
+ </programlisting>
+
+ <para>
+
+ Then the C preprocessor will <emphasis>always</emphasis>
+ use a &hello_h; file from the repository directory first,
+ even if there is a &hello_h; file in the local directory,
+ despite the fact that the command line specifies
+ <literal>-I</literal> as the first option:
+
+ </para>
+
+ <scons_example name="quote1">
+ <file name="SConstruct">
+ env = Environment(CPPPATH = ['.'])
+ env.Program('hello.c')
+ Repository('__ROOT__/usr/repository1')
+ </file>
+ <file name="__ROOT__/usr/repository1/hello.c">
+ int main() { printf("Hello, world!\n"); }
+ </file>
+ </scons_example>
+
+ <scons_output example="quote1">
+ <scons_output_command>scons -Q</scons_output_command>
+ </scons_output>
+
+ <para>
+
+ This behavior of the C preprocessor--always search
+ for a <literal>#include</literal> file in double-quotes
+ first in the same directory as the source file,
+ and only then search the <literal>-I</literal>--can
+ not, in general, be changed.
+ In other words, it's a limitation
+ that must be lived with if you want to use
+ code repositories in this way.
+ There are three ways you can possibly
+ work around this C preprocessor behavior:
+
+ </para>
+
+ <orderedlist>
+
+ <listitem>
+ <para>
+
+ Some modern versions of C compilers do have an option
+ to disable or control this behavior.
+ If so, add that option to &cv-CFLAGS;
+ (or &cv-CXXFLAGS; or both) in your construction environment(s).
+ Make sure the option is used for all construction
+ environments that use C preprocessing!
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Change all occurrences of <literal>#include "file.h"</literal>
+ to <literal>#include &lt;file.h&gt;</literal>.
+ Use of <literal>#include</literal> with angle brackets
+ does not have the same behavior--the <literal>-I</literal>
+ directories are searched first
+ for <literal>#include</literal> files--which
+ gives &SCons; direct control over the list of
+ directories the C preprocessor will search.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Require that everyone working with compilation from
+ repositories check out and work on entire directories of files,
+ not individual files.
+ (If you use local wrapper scripts around
+ your source code control system's command,
+ you could add logic to enforce this restriction there.
+
+ </para>
+ </listitem>
+
+ </orderedlist>
+
+ </section>
+
+ </section>
+
<section>
<title>Finding the &SConstruct; file in repositories</title>
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--->
-
-<!--
-
-
-=head2 Repository dependency analysis
-
-Due to its built-in scanning, Cons will search the specified repository
-trees for included F<.h> files. Unless the compiler also knows about the
-repository trees, though, it will be unable to find F<.h> files that only
-exist in a repository. If, for example, the F<hello.c> file includes the
-F<hello.h> file in its current directory:
-
- % cons -R /usr/all/repository hello
- gcc -c /usr/all/repository/hello.c -o hello.o
- /usr/all/repository/hello.c:1: hello.h: No such file or directory
-
-Solving this problem forces some requirements onto the way construction
-environments are defined and onto the way the C C<#include> preprocessor
-directive is used to include files.
-
-In order to inform the compiler about the repository trees, Cons will add
-appropriate C<-I> flags to the compilation commands. This means that the
-C<CPPPATH> variable in the construction environment must explicitly specify
-all subdirectories which are to be searched for included files, including the
-current directory. Consequently, we can fix the above example by changing
-the environment creation in the F<Construct> file as follows:
-
- $env = new cons(
- CC => 'gcc',
- CPPPATH => '.',
- LIBS => 'libworld.a',
- );
-
-Due to the definition of the C<CPPPATH> variable, this yields, when we
-re-execute the command:
-
- % cons -R /usr/all/repository hello
- gcc -c -I. -I/usr/all/repository /usr/all/repository/hello.c -o hello.o
- gcc -o hello hello.o /usr/all/repository/libworld.a
-
-The order of the C<-I> flags replicates, for the C preprocessor, the same
-repository-directory search path that Cons uses for its own dependency
-analysis. If there are multiple repositories and multiple C<CPPPATH>
-directories, Cons will append the repository directories to the beginning of
-each C<CPPPATH> directory, rapidly multiplying the number of C<-I> flags.
-As an extreme example, a F<Construct> file containing:
-
- Repository qw(
- /u1
- /u2
- );
-
- $env = new cons(
- CPPPATH => 'a:b:c',
- );
-
-Would yield a compilation command of:
-
- cc -Ia -I/u1/a -I/u2/a -Ib -I/u1/b -I/u2/b -Ic -I/u1/c -I/u2/c -c hello.c -o hello.o
-
-In order to shorten the command lines as much as possible, Cons will
-remove C<-I> flags for any directories, locally or in the repositories,
-which do not actually exist. (Note that the C<-I> flags are not included
-in the MD5 signature calculation for the target file, so the target will
-not be recompiled if the compilation command changes due to a directory
-coming into existence.)
-
-Because Cons relies on the compiler's C<-I> flags to communicate the
-order in which repository directories must be searched, Cons' handling
-of repository directories is fundamentally incompatible with using
-double-quotes on the C<#include> directives in any C source code that
-you plan to modify:
-
- #include "file.h" /* DON'T USE DOUBLE-QUOTES LIKE THIS */
-
-This is because most C preprocessors, when faced with such a directive, will
-always first search the directory containing the source file. This
-undermines the elaborate C<-I> options that Cons constructs to make the
-preprocessor conform to its preferred search path.
-
-Consequently, when using repository trees in Cons, B<always> use
-angle-brackets for included files in any C source (.c or .h) files that
-you plan to modify locally:
-
- #include <file.h> /* USE ANGLE-BRACKETS INSTEAD */
-
-Code that will not change can still safely use double quotes on #include
-lines.
-
-
-=head2 Repository_List
-
-Cons provides a C<Repository_List> command to return a list of all
-repository directories in their current search order. This can be used for
-debugging, or to do more complex Perl stuff:
-
- @list = Repository_List;
- print join(' ', @list), "\n";
-
-
-=head2 Repository interaction with other Cons features
-
-Cons' handling of repository trees interacts correctly with other Cons
-features, which is to say, it generally does what you would expect.
-
-Most notably, repository trees interact correctly, and rather powerfully,
-with the 'Link' command. A repository tree may contain one or more
-subdirectories for version builds established via C<Link> to a source
-subdirectory. Cons will search for derived files in the appropriate build
-subdirectories under the repository tree.
-
-->
<para>
a directory copy of the source code tree.
(Note that this is not the sort of repository
maintained by a source code management system
- like BitKeeper, CVS, or Subversion.
+ like BitKeeper, CVS, or Subversion.)
+ <!--
For information about using &SCons;
with these systems, see the section,
"Fetching Files From Source Code Management Systems,"
below.)
+ -->
You use the &Repository; method
to tell &SCons; to search one or more
central code repositories (in order)
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c /usr/repository1/hello.c
cc -o hello hello.o
- gcc -c /usr/repository1/hello.c -o hello.o
- gcc -o hello hello.o
</screen>
<para>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o hello.o hello.c
+ cc -o hello.o -c /usr/repository2/hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ </para>
+
+ </section>
+
+ <section>
+ <title>Finding <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ We've already seen that SCons will scan the contents of
+ a source file for <literal>#include</literal> file names
+ and realize that targets built from that source file
+ also depend on the <literal>#include</literal> file(s).
+ For each directory in the &cv-CPPPATH; list,
+ &SCons; will actually search the corresponding directories
+ in any repository trees and establish the
+ correct dependencies on any
+ <literal>#include</literal> files that it finds
+ in repository directory.
+
+ </para>
+
+ <para>
+
+ Unless the C compiler also knows about these directories
+ in the repository trees, though,
+ it will be unable to find the <literal>#include</literal> files.
+ If, for example, the &hello_c; file in
+ our previous example includes the &hello;.h;
+ in its current directory,
+ and the &hello;.h; only exists in the repository:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c hello.c
+ hello.c:1: hello.h: No such file or directory
+ </screen>
+
+ <para>
+
+ In order to inform the C compiler about the repositories,
+ &SCons; will add appropriate
+ <literal>-I</literal> flags to the compilation commands
+ for each directory in the &cv-CPPPATH; list.
+ So if we add the current directory to the
+ construction environment &cv-CPPPATH; like so:
+
+ </para>
+
+ <programlisting>
+ env = Environment(CPPPATH = ['.'])
+ env.Program('hello.c')
+ Repository('/usr/repository1')
+ </programlisting>
+
+ <para>
+
+ Then re-executing &SCons; yields:
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -I. -I/usr/repository1 hello.c
cc -o hello hello.o
</screen>
<para>
+ The order of the <literal>-I</literal> options replicates,
+ for the C preprocessor,
+ the same repository-directory search path
+ that &SCons; uses for its own dependency analysis.
+ If there are multiple repositories and multiple &cv-CPPPATH;
+ directories, &SCons; will add the repository directories
+ to the beginning of each &cv-CPPPATH; directory,
+ rapidly multiplying the number of <literal>-I</literal> flags.
+ If, for example, the &cv-CPPPATH; contains three directories
+ (and shorter repository path names!):
+
</para>
+ <programlisting>
+ env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3'])
+ env.Program('hello.c')
+ Repository('/r1', '/r2')
+ </programlisting>
+
+ <para>
+
+ Then we'll end up with nine <literal>-I</literal> options
+ on the command line,
+ three (for each of the &cv-CPPPATH; directories)
+ times three (for the local directory plus the two repositories):
+
+ </para>
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -Idir1 -I/r1/dir1 -I/r2/dir1 -Idir2 -I/r1/dir2 -I/r2/dir2 -Idir3 -I/r1/dir3 -I/r2/dir3 hello.c
+ cc -o hello hello.o
+ </screen>
+
+<!--
+
+Cons classic did the following, does SCons?
+
+In order to shorten the command lines as much as possible, Cons will
+remove C<-I> flags for any directories, locally or in the repositories,
+which do not actually exist. (Note that the C<-I> flags are not included
+in the MD5 signature calculation for the target file, so the target will
+not be recompiled if the compilation command changes due to a directory
+coming into existence.)
+
+-->
+
+ <section>
+ <title>Limitations on <literal>#include</literal> files in repositories</title>
+
+ <para>
+
+ &SCons; relies on the C compiler's
+ <literal>-I</literal> options to control the order in which
+ the preprocessor will search the repository directories
+ for <literal>#include</literal> files.
+ This causes a problem, however, with how the C preprocessor
+ handles <literal>#include</literal> lines with
+ the file name included in double-quotes.
+
+ </para>
+
+ <para>
+
+ As we've seen,
+ &SCons; will compile the &hello_c; file from
+ the repository if it doesn't exist in
+ the local directory.
+ If, however, the &hello_c; file in the repository contains
+ a <literal>#include</literal> line with the file name in
+ double quotes:
+
+ </para>
+
+ <programlisting>
+ #include "hello.h"
+ int
+ main(int argc, char *argv[])
+ {
+ printf(HELLO_MESSAGE);
+ return (0);
+ }
+ </programlisting>
+
+ <para>
+
+ Then the C preprocessor will <emphasis>always</emphasis>
+ use a &hello_h; file from the repository directory first,
+ even if there is a &hello_h; file in the local directory,
+ despite the fact that the command line specifies
+ <literal>-I</literal> as the first option:
+
+ </para>
+
+
+
+ <screen>
+ % <userinput>scons -Q</userinput>
+ cc -o hello.o -c -I. -I/usr/repository1 /usr/repository1/hello.c
+ cc -o hello hello.o
+ </screen>
+
+ <para>
+
+ This behavior of the C preprocessor--always search
+ for a <literal>#include</literal> file in double-quotes
+ first in the same directory as the source file,
+ and only then search the <literal>-I</literal>--can
+ not, in general, be changed.
+ In other words, it's a limitation
+ that must be lived with if you want to use
+ code repositories in this way.
+ There are three ways you can possibly
+ work around this C preprocessor behavior:
+
+ </para>
+
+ <orderedlist>
+
+ <listitem>
+ <para>
+
+ Some modern versions of C compilers do have an option
+ to disable or control this behavior.
+ If so, add that option to &cv-CFLAGS;
+ (or &cv-CXXFLAGS; or both) in your construction environment(s).
+ Make sure the option is used for all construction
+ environments that use C preprocessing!
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Change all occurrences of <literal>#include "file.h"</literal>
+ to <literal>#include <file.h></literal>.
+ Use of <literal>#include</literal> with angle brackets
+ does not have the same behavior--the <literal>-I</literal>
+ directories are searched first
+ for <literal>#include</literal> files--which
+ gives &SCons; direct control over the list of
+ directories the C preprocessor will search.
+
+ </para>
+ </listitem>
+
+ <listitem>
+ <para>
+
+ Require that everyone working with compilation from
+ repositories check out and work on entire directories of files,
+ not individual files.
+ (If you use local wrapper scripts around
+ your source code control system's command,
+ you could add logic to enforce this restriction there.
+
+ </para>
+ </listitem>
+
+ </orderedlist>
+
+ </section>
+
</section>
<section>
<screen>
% <userinput>cd /usr/repository1</userinput>
% <userinput>scons -Q</userinput>
- cc -c -o file1.o file1.c
- cc -c -o file2.o file2.c
- cc -c -o hello.o hello.c
+ cc -o file1.o -c file1.c
+ cc -o file2.o -c file2.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o file1.o file2.o
</screen>
% <userinput>ls src</userinput>
SConscript hello.c
% <userinput>scons -Q</userinput>
- cc -c -o build/hello.o build/hello.c
+ cc -o build/hello.o -c build/hello.c
cc -o build/hello build/hello.o
% <userinput>ls build</userinput>
SConscript hello hello.c hello.o
% <userinput>ls src</userinput>
hello.c
% <userinput>scons -Q</userinput>
- cc -c -o build/hello.o build/hello.c
+ cc -o build/hello.o -c build/hello.c
cc -o build/hello build/hello.o
% <userinput>ls build</userinput>
hello hello.c hello.o
% <userinput>ls src</userinput>
hello.c
% <userinput>scons -Q</userinput>
- cc -c -o build/hello.o src/hello.c
+ cc -o build/hello.o -c src/hello.c
cc -o build/hello build/hello.o
% <userinput>ls build</userinput>
hello hello.o
% <userinput>ls src</userinput>
SConscript hello.c
% <userinput>scons -Q</userinput>
- cc -c -o build/hello.o build/hello.c
+ cc -o build/hello.o -c build/hello.c
cc -o build/hello build/hello.o
% <userinput>ls build</userinput>
SConscript hello hello.c hello.o
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
scons: done building targets.
</screen>
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
scons: done building targets.
</screen>
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
scons: done building targets.
% <userinput>scons -c</userinput>
Finished calling Program()
scons: done reading SConscript files.
scons: Building targets ...
- cc -c -o goodbye.o goodbye.c
+ cc -o goodbye.o -c goodbye.c
cc -o goodbye goodbye.o
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
scons: done building targets.
</screen>
% <userinput>scons -Q</userinput>
bk get -
bk get hello.c
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
% <userinput>scons -Q</userinput>
cvs -d /usr/local/CVS co -
cvs -d /usr/local/CVS co hello.c
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
% <userinput>scons -Q</userinput>
co -
co hello.c
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
% <userinput>scons -Q</userinput>
sccs get -
sccs get hello.c
- cc -c -o hello.o hello.c
+ cc -o hello.o -c hello.c
cc -o hello hello.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -c -o file1.o file1.c
- cc -c -o file2.o file2.c
- cc -c -o file3.o file3.c
+ cc -o file1.o -c file1.c
+ cc -o file2.o -c file2.c
+ cc -o file3.o -c file3.c
cc -o prog file1.o file2.o file3.o
% <userinput>edit file2.c</userinput>
[CHANGE THE CONTENTS OF file2.c]
% <userinput>scons -Q --debug=explain</userinput>
scons: rebuilding `file2.o' because `file2.c' changed
- cc -c -o file2.o file2.c
+ cc -o file2.o -c file2.c
scons: rebuilding `prog' because `file2.o' changed
cc -o prog file1.o file2.o file3.o
</screen>
<screen>
% <userinput>scons -Q</userinput>
- cc -I. -c -o file1.o file1.c
- cc -I. -c -o file2.o file2.c
- cc -I. -c -o file3.o file3.c
+ cc -o file1.o -c -I. file1.c
+ cc -o file2.o -c -I. file2.c
+ cc -o file3.o -c -I. file3.c
cc -o prog file1.o file2.o file3.o
% <userinput>edit hello.h</userinput>
[CHANGE THE CONTENTS OF hello.h]
% <userinput>scons -Q --debug=explain</userinput>
scons: rebuilding `file1.o' because `hello.h' changed
- cc -I. -c -o file1.o file1.c
+ cc -o file1.o -c -I. file1.c
scons: rebuilding `file3.o' because `hello.h' changed
- cc -I. -c -o file3.o file3.c
+ cc -o file3.o -c -I. file3.c
scons: rebuilding `prog' because:
`file1.o' changed
`file3.o' changed
% <userinput>scons</userinput>
scons: Reading SConscript files ...
{ 'BUILDERS': {},
+ 'CONFIGUREDIR': '#/.sconf_temp',
+ 'CONFIGURELOG': '#/config.log',
'CPPSUFFIXES': [ '.c',
'.C',
'.cxx',
'.F',
'.fpp',
'.FPP',
+ '.m',
+ '.mm',
'.S',
'.spp',
'.SPP'],
'DSUFFIXES': ['.d'],
- 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0x829dcb4>,
- 'ENV': {'PATH': '/usr/local/bin:/bin:/usr/bin'},
- 'ESCAPE': <function escape at 0x837d2a4>,
- 'File': <SCons.Defaults.Variable_Method_Caller instance at 0x829e0fc>,
+ 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43bec>,
+ 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c0c>,
+ 'ENV': {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'},
+ 'ESCAPE': <function escape at 0xb7b66c34>,
+ 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c2c>,
'IDLSUFFIXES': ['.idl', '.IDL'],
- 'INSTALL': <function copyFunc at 0x829db9c>,
+ 'INSTALL': <function installFunc at 0xb7c41f0c>,
+ 'INSTALLSTR': <function installStr at 0xb7c41f44>,
+ 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'],
'LIBPREFIX': 'lib',
'LIBPREFIXES': '$LIBPREFIX',
'LIBSUFFIX': '.a',
'LIBSUFFIXES': ['$LIBSUFFIX', '$SHLIBSUFFIX'],
+ 'MAXLINELENGTH': 128072,
'OBJPREFIX': '',
'OBJSUFFIX': '.o',
- 'PDFPREFIX': '',
- 'PDFSUFFIX': '.pdf',
'PLATFORM': 'posix',
'PROGPREFIX': '',
'PROGSUFFIX': '',
- 'PSPAWN': <function piped_env_spawn at 0x837d384>,
- 'PSPREFIX': '',
- 'PSSUFFIX': '.ps',
- 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x829e46c>,
+ 'PSPAWN': <function piped_env_spawn at 0xb7b66fb4>,
+ 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c43c4c>,
'SCANNERS': [],
'SHELL': 'sh',
'SHLIBPREFIX': '$LIBPREFIX',
'SHLIBSUFFIX': '.so',
'SHOBJPREFIX': '$OBJPREFIX',
'SHOBJSUFFIX': '$OBJSUFFIX',
- 'SPAWN': <function spawnvpe_spawn at 0x8377fdc>,
- 'TEMPFILE': <class SCons.Defaults.NullCmdGenerator at 0x829ddec>,
+ 'SPAWN': <function spawnvpe_spawn at 0xb7b66a74>,
+ 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7bd37ac>,
+ 'TEMPFILEPREFIX': '@',
'TOOLS': [],
'_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
- '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET)} $)',
- '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET)} $)',
+ '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+ '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
'_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
'__RPATH': '$_RPATH',
- '_concat': <function _concat at 0x829dc0c>,
- '_defines': <function _defines at 0x829dc7c>,
- '_stripixes': <function _stripixes at 0x829dc44>}
+ '_concat': <function _concat at 0xb7c41fb4>,
+ '_defines': <function _defines at 0xb7c47064>,
+ '_installStr': <function installStr at 0xb7c41f44>,
+ '_stripixes': <function _stripixes at 0xb7c4702c>}
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
<screen>
C:\><userinput>scons</userinput>
scons: Reading SConscript files ...
- { 'BUILDERS': {'Object': <SCons.Memoize.MultiStepBuilder object at 0x83493e4>, 'SharedObject': <SCons.Memoize.MultiStepBuilder object at 0x8349fec>, 'StaticObject': <SCons.Memoize.MultiStepBuilder object at 0x83493e4>, 'PCH': <SCons.Memoize.BuilderBase object at 0x83418cc>, 'RES': <SCons.Memoize.BuilderBase object at 0x8367cec>},
+ { 'BUILDERS': {'Object': <SCons.Builder.CompositeBuilder instance at 0xb7b6024c>, 'SharedObject': <SCons.Builder.CompositeBuilder instance at 0xb7b603cc>, 'StaticObject': <SCons.Builder.CompositeBuilder instance at 0xb7b6024c>, 'PCH': <SCons.Builder.BuilderBase instance at 0xb7bd2eac>, 'RES': <SCons.Builder.BuilderBase instance at 0xb7b596ec>},
'CC': 'cl',
- 'CCCOM': <SCons.Memoize.FunctionAction object at 0x8340454>,
+ 'CCCOM': <SCons.Action.FunctionAction instance at 0xb7b6086c>,
'CCCOMFLAGS': '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS',
'CCFLAGS': ['/nologo'],
'CCPCHFLAGS': ['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'],
'CCPDBFLAGS': ['${(PDB and "/Z7") or ""}'],
'CFILESUFFIX': '.c',
+ 'CFLAGS': [],
+ 'CONFIGUREDIR': '#/.sconf_temp',
+ 'CONFIGURELOG': '#/config.log',
'CPPDEFPREFIX': '/D',
'CPPDEFSUFFIX': '',
'CPPSUFFIXES': [ '.c',
'.F',
'.fpp',
'.FPP',
+ '.m',
+ '.mm',
'.S',
'.spp',
'.SPP'],
'CXXFILESUFFIX': '.cc',
'CXXFLAGS': ['$CCFLAGS', '$(', '/TP', '$)'],
'DSUFFIXES': ['.d'],
- 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0x829dcb4>,
+ 'Dir': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58bec>,
+ 'Dirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c0c>,
'ENV': { 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include',
'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib',
'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin',
- 'PATHEXT': '.COM;.EXE;.BAT;.CMD'},
- 'ESCAPE': <function <lambda> at 0x82339ec>,
- 'File': <SCons.Defaults.Variable_Method_Caller instance at 0x829e0fc>,
+ 'PATHEXT': '.COM;.EXE;.BAT;.CMD',
+ 'SystemRoot': 'C:/WINDOWS'},
+ 'ESCAPE': <function escape at 0xb7bc917c>,
+ 'File': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c2c>,
'IDLSUFFIXES': ['.idl', '.IDL'],
'INCPREFIX': '/I',
'INCSUFFIX': '',
- 'INSTALL': <function copyFunc at 0x829db9c>,
+ 'INSTALL': <function installFunc at 0xb7c56f0c>,
+ 'INSTALLSTR': <function installStr at 0xb7c56f44>,
+ 'LATEXSUFFIXES': ['.tex', '.ltx', '.latex'],
'LIBPREFIX': '',
'LIBPREFIXES': ['$LIBPREFIX'],
'LIBSUFFIX': '.lib',
'OBJSUFFIX': '.obj',
'PCHCOM': '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS',
'PCHPDBFLAGS': ['${(PDB and "/Yd") or ""}'],
- 'PDFPREFIX': '',
- 'PDFSUFFIX': '.pdf',
'PLATFORM': 'win32',
'PROGPREFIX': '',
'PROGSUFFIX': '.exe',
- 'PSPAWN': <function piped_spawn at 0x8372bc4>,
- 'PSPREFIX': '',
- 'PSSUFFIX': '.ps',
+ 'PSPAWN': <function piped_spawn at 0xb7bc90d4>,
'RC': 'rc',
'RCCOM': '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES',
'RCFLAGS': [],
- 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0x829e46c>,
+ 'RDirs': <SCons.Defaults.Variable_Method_Caller instance at 0xb7c58c4c>,
'SCANNERS': [],
'SHCC': '$CC',
- 'SHCCCOM': <SCons.Memoize.FunctionAction object at 0x83494bc>,
+ 'SHCCCOM': <SCons.Action.FunctionAction instance at 0xb7b608cc>,
'SHCCFLAGS': ['$CCFLAGS'],
+ 'SHCFLAGS': ['$CFLAGS'],
'SHCXX': '$CXX',
'SHCXXCOM': '$SHCXX $SHCXXFLAGS $CCCOMFLAGS',
'SHCXXFLAGS': ['$CXXFLAGS'],
'SHLIBSUFFIX': '.dll',
'SHOBJPREFIX': '$OBJPREFIX',
'SHOBJSUFFIX': '$OBJSUFFIX',
- 'SPAWN': <function spawn at 0x8374c34>,
+ 'SPAWN': <function spawn at 0xb7bc9144>,
'STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME': 1,
- 'TEMPFILE': <class SCons.Platform.win32.TempFileMunge at 0x835edc4>,
+ 'TEMPFILE': <class SCons.Platform.TempFileMunge at 0xb7be87ac>,
+ 'TEMPFILEPREFIX': '@',
'TOOLS': ['msvc'],
'_CPPDEFFLAGS': '${_defines(CPPDEFPREFIX, CPPDEFINES, CPPDEFSUFFIX, __env__)}',
- '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET)} $)',
- '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET)} $)',
+ '_CPPINCFLAGS': '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
+ '_LIBDIRFLAGS': '$( ${_concat(LIBDIRPREFIX, LIBPATH, LIBDIRSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)',
'_LIBFLAGS': '${_concat(LIBLINKPREFIX, LIBS, LIBLINKSUFFIX, __env__)}',
- '_concat': <function _concat at 0x829dc0c>,
- '_defines': <function _defines at 0x829dc7c>,
- '_stripixes': <function _stripixes at 0x829dc44>}
+ '_concat': <function _concat at 0xb7c56fb4>,
+ '_defines': <function _defines at 0xb7c5c064>,
+ '_installStr': <function installStr at 0xb7c56f44>,
+ '_stripixes': <function _stripixes at 0xb7c5c02c>}
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
<screen>
% <userinput>scons</userinput>
scons: Reading SConscript files ...
- {'PATH': '/usr/local/bin:/bin:/usr/bin'}
+ {'PATH': '/usr/local/bin:/opt/bin:/bin:/usr/bin'}
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
{ 'INCLUDE': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\include',
'LIB': 'C:\\Program Files\\Microsoft Visual Studio/VC98\\lib',
'PATH': 'C:\\Program Files\\Microsoft Visual Studio\\Common\\tools\\WIN95;C:\\Program Files\\Microsoft Visual Studio\\Common\\MSDev98\\bin;C:\\Program Files\\Microsoft Visual Studio\\Common\\tools;C:\\Program Files\\Microsoft Visual Studio/VC98\\bin',
- 'PATHEXT': '.COM;.EXE;.BAT;.CMD'}
+ 'PATHEXT': '.COM;.EXE;.BAT;.CMD',
+ 'SystemRoot': 'C:/WINDOWS'}
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
<screen>
% <userinput>scons -Q OS=linux</userinput>
Install file: "build/linux/world/world.h" as "export/linux/include/world.h"
- cc -Iexport/linux/include -c -o build/linux/hello/hello.o build/linux/hello/hello.c
- cc -Iexport/linux/include -c -o build/linux/world/world.o build/linux/world/world.c
- ar r build/linux/world/libworld.a build/linux/world/world.o
+ cc -o build/linux/hello/hello.o -c -Iexport/linux/include build/linux/hello/hello.c
+ cc -o build/linux/world/world.o -c -Iexport/linux/include build/linux/world/world.c
+ ar rc build/linux/world/libworld.a build/linux/world/world.o
ranlib build/linux/world/libworld.a
Install file: "build/linux/world/libworld.a" as "export/linux/lib/libworld.a"
cc -o build/linux/hello/hello build/linux/hello/hello.o -Lexport/linux/lib -lworld
if not hasattr(os, 'WEXITSTATUS'):
os.WEXITSTATUS = lambda x: x
+cwd = os.getcwd()
+
all = 0
baseline = 0
+builddir = os.path.join(cwd, 'build')
debug = ''
execute_tests = 1
format = None
sp = None
spe = None
-cwd = os.getcwd()
-
helpstr = """\
Usage: runtest.py [OPTIONS] [TEST ...]
Options:
-a, --all Run all tests.
--aegis Print results in Aegis format.
-b BASE, --baseline BASE Run test scripts against baseline BASE.
+ --builddir DIR Directory in which packages were built.
-d, --debug Run test scripts under the Python debugger.
-f FILE, --file FILE Run tests in specified FILE.
-h, --help Print this message and exit.
"""
opts, args = getopt.getopt(sys.argv[1:], "ab:df:hlno:P:p:qv:Xx:t",
- ['all', 'aegis', 'baseline=',
+ ['all', 'aegis', 'baseline=', 'builddir=',
'debug', 'file=', 'help',
'list', 'no-exec', 'noqmtest', 'output=',
'package=', 'passed', 'python=',
all = 1
elif o in ['-b', '--baseline']:
baseline = a
+ elif o in ['--builddir']:
+ builddir = a
+ if not os.path.isabs(builddir):
+ builddir = os.path.normpath(os.path.join(cwd, builddir))
elif o in ['-d', '--debug']:
for dir in sys.path:
pdb = os.path.join(dir, 'pdb.py')
if spe is None:
spe = []
+sp.append(builddir)
sp.append(cwd)
#
sys.stderr.write("Unknown package '%s'\n" % package)
sys.exit(2)
- test_dir = os.path.join(cwd, 'build', 'test-%s' % package)
+ test_dir = os.path.join(builddir, 'test-%s' % package)
if dir[package] is None:
scons_script_dir = test_dir
scons_lib_dir = os.path.join(test_dir, dir[package], 'lib', l)
pythonpath_dir = scons_lib_dir
- scons_runtest_dir = os.path.join(cwd, 'build')
+ scons_runtest_dir = builddir
else:
sd = None
qmtest_args = [ qmtest, ]
if format == '--aegis':
- dir = os.path.join(cwd, 'build')
+ dir = builddir
if not os.path.isdir(dir):
dir = cwd
qmtest_args.extend(['-D', dir])
RELEASE 0.97 - XXX
+ From Anatoly:
+
+ - Add the scons.org URL and a package description to the setup.py
+ arguments.
+
+ - Have the Windows installer add a registry entry for scons.bat in the
+ "App Paths" key, so scons.bat can be executed without adding the
+ directory to the %PATH%. (Python itself works this way.)
+
From Anonymous:
- Fix looking for default paths in Visual Studio 8.0 (and later).
+ - Add -lm to the list of default D libraries for linking.
+
From Matt Doar:
- Provide a more complete write-your-own-Scanner example in the man page.
+ From Ralf W. Grosse-Kunstleve:
+
+ - Contributed upstream Python change to our copied subprocess.py module
+ for more efficient standard input processing.
+
From Steven Knight:
- Fix the Node.FS.Base.rel_path() method when the two nodes are on
different drive letters. (This caused an infinite loop when
trying to write .sconsign files.)
+ - Fully support Scanners that use a dictionary to map file suffixes
+ to other scanners.
+
+ - Support delayed evaluation of the $SPAWN variable to allow selection
+ of a function via ${} string expansions.
+
+ - Add --srcdir as a synonym for -Y/--repository.
+
+ - Document limitations of #include "file.h" with Repository().
+
+ - Fix use of a toolpath under the source directory of a BuildDir().
+
+ - Fix env.Install() with a file name portion that begins with '#'.
+
+ - Fix ParseConfig()'s handling of multiple options in a string that's
+ replaced a *FLAGS construction variable.
+
+ - Have the C++ tools initialize common C compilation variables ($CCFLAGS,
+ $SHCCFLAGS and $_CCCOMCOM) even if the 'cc' Tool isn't loaded.
+
+ From Leanid Nazdrynau:
+
+ - Fix detection of Java anonymous classes if a newline precedes the
+ opening brace.
+
From Gary Oberbrunner:
- Document use of ${} to execute arbitrary Python code.
+ - Add support for:
+ 1) automatically adding a site_scons subdirectory (in the top-level
+ SConstruct directory) to sys.path (PYTHONPATH);
+ 2) automatically importing site_scons/site_init.py;
+ 3) automatically adding site_scons/site_tools to the toolpath.
+
From John Pye:
- Change ParseConfig() to preserve white space in arguments passed in
as a list.
+ From a smith:
+
+ - Fix adding explicitly-named Java inner class files (and any
+ other file names that may contain a '$') to Jar files.
+
+ From David Vitek:
+
+ - Add a NoCache() function to mark targets as unsuitable for propagating
+ to (or retrieving from) a CacheDir().
+
+ From Ben Webb:
+
+ - If the swig -noproxy option is used, it won't generate a .py file,
+ so don't emit it as a target that we expect to be built.
+
RELEASE 0.96.94 - Sun, 07 Jan 2007 18:36:20 -0600
SCons/Action.py
SCons/Builder.py
SCons/compat/__init__.py
+SCons/compat/_sets.py
+SCons/compat/_sets15.py
SCons/compat/_subprocess.py
SCons/compat/_UserString.py
SCons/compat/builtins.py
import dis
import os
import os.path
-import re
import string
import sys
from SCons.Debug import logInstanceCreation
import SCons.Errors
+import SCons.Executor
import SCons.Util
class _Null:
def strfunction(self, target, source, env):
if not self.cmdstr is None:
- c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source)
+ from SCons.Subst import SUBST_RAW
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
if c:
return c
cmd_list, ignore, silent = self.process(target, source, env)
externally.
"""
from SCons.Subst import escape_list
- from SCons.Util import is_String, is_List, flatten
+ import SCons.Util
+ flatten = SCons.Util.flatten
+ is_String = SCons.Util.is_String
+ is_List = SCons.Util.is_List
try:
shell = env['SHELL']
spawn = env['SPAWN']
except KeyError:
raise SCons.Errors.UserError('Missing SPAWN construction variable.')
+ else:
+ if is_String(spawn):
+ spawn = env.subst(spawn, raw=1, conv=lambda x: x)
escape = env.get('ESCAPE', lambda x: x)
if self.cmdstr is None:
return None
if not self.cmdstr is _null:
- c = env.subst(self.cmdstr, SCons.Subst.SUBST_RAW, target, source)
+ from SCons.Subst import SUBST_RAW
+ c = env.subst(self.cmdstr, SUBST_RAW, target, source)
if c:
return c
def array(a):
for k, v in kw.items():
self.d[k] = v
# Just use the underlying scons_subst*() utility methods.
- def subst(self, strSubst, raw=0, target=[], source=[]):
- return SCons.Subst.scons_subst(strSubst, self, raw, target, source, self.d)
+ def subst(self, strSubst, raw=0, target=[], source=[], conv=None):
+ return SCons.Subst.scons_subst(strSubst, self, raw,
+ target, source, self.d, conv=conv)
subst_target_source = subst
- def subst_list(self, strSubst, raw=0, target=[], source=[]):
- return SCons.Subst.scons_subst_list(strSubst, self, raw, target, source, self.d)
+ def subst_list(self, strSubst, raw=0, target=[], source=[], conv=None):
+ return SCons.Subst.scons_subst_list(strSubst, self, raw,
+ target, source, self.d, conv=conv)
def __getitem__(self, item):
return self.d[item]
def __setitem__(self, item, value):
a([], [], e)
assert t.executed == [ 'xyzzy' ], t.executed
+ a = SCons.Action.CommandAction(["xyzzy"])
+ e = Environment(SPAWN = '$FUNC', FUNC = func)
+ a([], [], e)
+ assert t.executed == [ 'xyzzy' ], t.executed
+
a = SCons.Action.CommandAction(["xyzzy"])
e = Environment(SPAWN = func, SHELL = 'fake shell')
a([], [], e)
def func(self):
pass
- scanner = SCons.Scanner.Scanner(func, name='fooscan')
+ scanner = SCons.Scanner.Base(func, name='fooscan')
b1 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
b2 = SCons.Builder.Builder(action='bld', target_scanner=scanner)
sure $CFLAGS, $CPPFLAGS and $LIBS are set correctly.
Returns an empty string for success, an error message for failure.
"""
- from SCons.Debug import Trace
# Include "confdefs.h" first, so that the header can use HAVE_HEADER_H.
if context.headerfilename:
includetext = '#include "%s"' % context.headerfilename
import os.path
import shutil
import stat
-import string
import time
import types
import sys
import copy
import os
import os.path
-import popen2
import string
from UserDict import UserDict
from SCons.Debug import logInstanceCreation
import SCons.Defaults
import SCons.Errors
+import SCons.Memoize
import SCons.Node
import SCons.Node.Alias
import SCons.Node.FS
import SCons.Platform
import SCons.SConsign
import SCons.Sig
-import SCons.Sig.TimeStamp
import SCons.Subst
import SCons.Tool
import SCons.Util
the result of that evaluation is then added to the dict.
"""
dict = {
- 'ASFLAGS' : [],
- 'CFLAGS' : [],
- 'CCFLAGS' : [],
+ 'ASFLAGS' : SCons.Util.CLVar(''),
+ 'CFLAGS' : SCons.Util.CLVar(''),
+ 'CCFLAGS' : SCons.Util.CLVar(''),
'CPPDEFINES' : [],
- 'CPPFLAGS' : [],
+ 'CPPFLAGS' : SCons.Util.CLVar(''),
'CPPPATH' : [],
- 'FRAMEWORKPATH' : [],
- 'FRAMEWORKS' : [],
+ 'FRAMEWORKPATH' : SCons.Util.CLVar(''),
+ 'FRAMEWORKS' : SCons.Util.CLVar(''),
'LIBPATH' : [],
'LIBS' : [],
- 'LINKFLAGS' : [],
+ 'LINKFLAGS' : SCons.Util.CLVar(''),
'RPATH' : [],
}
if arg[2:]:
append_define(arg[2:])
else:
- appencd_next_arg_to = 'CPPDEFINES'
+ append_next_arg_to = 'CPPDEFINES'
elif arg == '-framework':
append_next_arg_to = 'FRAMEWORKS'
elif arg[:14] == '-frameworkdir=':
apply(self.Append, (), args)
return self
for key, value in args.items():
- if value == '':
+ if not value:
continue
try:
orig = self[key]
orig = value
else:
if not orig:
- orig = []
- elif not SCons.Util.is_List(orig):
- orig = [orig]
- orig = orig + value
+ orig = value
+ elif value:
+ # Add orig and value. The logic here was lifted from
+ # part of env.Append() (see there for a lot of comments
+ # about the order in which things are tried) and is
+ # used mainly to handle coercion of strings to CLVar to
+ # "do the right thing" given (e.g.) an original CCFLAGS
+ # string variable like '-pipe -Wall'.
+ try:
+ orig = orig + value
+ except (KeyError, TypeError):
+ try:
+ add_to_orig = orig.append
+ except AttributeError:
+ value.insert(0, orig)
+ orig = value
+ else:
+ add_to_orig(value)
t = []
if key[-4:] == 'PATH':
### keep left-most occurence
del kw[k]
apply(self.Replace, (), kw)
+ def _find_toolpath_dir(self, tp):
+ return self.fs.Dir(self.subst(tp)).srcnode().abspath
+
def Tool(self, tool, toolpath=None, **kw):
if SCons.Util.is_String(tool):
tool = self.subst(tool)
if toolpath is None:
toolpath = self.get('toolpath', [])
- toolpath = map(self.subst, toolpath)
+ toolpath = map(self._find_toolpath_dir, toolpath)
tool = apply(SCons.Tool.Tool, (tool, toolpath), kw)
tool(self)
t.set_noclean()
return tlist
+ def NoCache(self, *targets):
+ """Tags a target so that it will not be cached"""
+ tlist = []
+ for t in targets:
+ tlist.extend(self.arg2nodes(t, self.fs.Entry))
+ for t in tlist:
+ t.set_nocache()
+ return tlist
+
def Entry(self, name, *args, **kw):
"""
"""
tgt = []
for dnode in dnodes:
for src in sources:
- target = self.fs.Entry(src.name, dnode)
+ # Prepend './' so the lookup doesn't interpret an initial
+ # '#' on the file name portion as meaning the Node should
+ # be relative to the top-level SConstruct directory.
+ target = self.fs.Entry('.'+os.sep+src.name, dnode)
tgt.extend(InstallBuilder(self, target, src))
return tgt
arg = self.subst(arg)
nargs.append(arg)
nkw = self.subst_kw(kw)
- return apply(SCons.Scanner.Scanner, nargs, nkw)
+ return apply(SCons.Scanner.Base, nargs, nkw)
def SConsignFile(self, name=".sconsign", dbm_module=None):
if not name is None:
def __getattr__(self, name):
return getattr(self.__dict__['__subject'], name)
def __setattr__(self, name, value):
- return setattr(self.__dict__['__subject'], name, value)
+ setattr(self.__dict__['__subject'], name, value)
# Methods that make this class act like a dictionary.
def __getitem__(self, key):
"-pthread " + \
"-mno-cygwin -mwindows " + \
"-arch i386 -isysroot /tmp +DD64 " + \
- "-DFOO -DBAR=value"
+ "-DFOO -DBAR=value -D BAZ"
d = env.ParseFlags(s)
'-pthread', '-mno-cygwin',
('-arch', 'i386'), ('-isysroot', '/tmp'),
'+DD64'], d['CCFLAGS']
- assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value']], d['CPPDEFINES']
+ assert d['CPPDEFINES'] == ['FOO', ['BAR', 'value'], 'BAZ'], d['CPPDEFINES']
assert d['CPPFLAGS'] == ['-Wp,-cpp'], d['CPPFLAGS']
assert d['CPPPATH'] == ['/usr/include/fum', 'bar'], d['CPPPATH']
assert d['FRAMEWORKPATH'] == ['fwd1', 'fwd2', 'fwd3'], d['FRAMEWORKPATH']
"""
env = SubstitutionEnvironment()
env.MergeFlags('')
- assert env['CCFLAGS'] == [], env['CCFLAGS']
+ assert not env.has_key('CCFLAGS'), env['CCFLAGS']
env.MergeFlags('-X')
assert env['CCFLAGS'] == ['-X'], env['CCFLAGS']
env.MergeFlags('-X')
for tnode in tgt:
assert tnode.builder == InstallBuilder
+ tgt = env.Install('export', 'subdir/#file')
+ assert str(tgt[0]) == os.path.normpath('export/#file'), str(tgt[0])
+
env.File('export/foo1')
exc_caught = None
import string
from SCons.Debug import logInstanceCreation
-import SCons.Util
+import SCons.Memoize
class Executor:
self._memo = {}
def set_action_list(self, action):
+ import SCons.Util
if not SCons.Util.is_List(action):
if not action:
+ import SCons.Errors
raise SCons.Errors.UserError, "Executor must have an action."
action = [action]
self.action_list = action
self.resultsQueue = Queue.Queue(0)
# Create worker threads
- for i in range(num):
+ for _ in range(num):
Worker(self.requestQueue, self.resultsQueue)
def put(self, obj):
import SCons.Action
from SCons.Debug import logInstanceCreation
import SCons.Errors
+import SCons.Memoize
import SCons.Node
import SCons.Subst
import SCons.Util
def CachePushFunc(target, source, env):
t = target[0]
+ if t.nocache:
+ return
fs = t.fs
cachedir, cachefile = t.cachepath()
if fs.exists(cachefile):
morph this Entry."""
try:
self = self.disambiguate(must_exist=1)
- except SCons.Errors.UserError, e:
+ except SCons.Errors.UserError:
# There was nothing on disk with which to disambiguate
# this entry. Leave it as an Entry, but return a null
# string so calls to get_contents() in emitters and the
path_norm = string.split(_my_normcase(name), os.sep)
first_orig = path_orig.pop(0) # strip first element
- first_norm = path_norm.pop(0) # strip first element
+ unused = path_norm.pop(0) # strip first element
drive, path_first = os.path.splitdrive(first_orig)
if path_first:
return result
def get_env_scanner(self, env, kw={}):
+ import SCons.Defaults
return SCons.Defaults.DirEntryScanner
def get_target_scanner(self):
+ import SCons.Defaults
return SCons.Defaults.DirEntryScanner
def get_found_includes(self, env, scanner, path):
Returns true iff the node was successfully retrieved.
"""
+ if self.nocache:
+ return None
b = self.is_derived()
if not b and not self.has_src_builder():
return None
return str(self.rfile())
def cachepath(self):
- if not self.fs.CachePath:
+ if self.nocache or not self.fs.CachePath:
return None, None
ninfo = self.get_binfo().ninfo
if not hasattr(ninfo, 'bsig'):
from SCons.Debug import logInstanceCreation
import SCons.Executor
+import SCons.Memoize
import SCons.SConsign
import SCons.Util
self.state = no_state
self.precious = None
self.noclean = 0
+ self.nocache = 0
self.always_build = None
self.found_includes = {}
self.includes = None
self.attributes = self.Attrs() # Generic place to stick information about the Node.
self.side_effect = 0 # true iff this node is a side effect
self.side_effects = [] # the side effects of building this target
- self.pre_actions = []
- self.post_actions = []
self.linked = 0 # is this node linked to the build directory?
self.clear_memoized_values()
# output in Util.py can use it as an index.
self.noclean = noclean and 1 or 0
+ def set_nocache(self, nocache = 1):
+ """Set the Node's nocache value."""
+ # Make sure nocache is an integer so the --debug=stree
+ # output in Util.py can use it as an index.
+ self.nocache = nocache and 1 or 0
+
def set_always_build(self, always_build = 1):
"""Set the Node's always_build value."""
self.always_build = always_build
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-__all__ = ('BoolOption', 'True', 'False')
+__all__ = ('BoolOption')
import string
+import SCons.compat
import SCons.Errors
__true_strings = ('y', 'yes', 'true', 't', '1', 'on' , 'all' )
__false_strings = ('n', 'no', 'false', 'f', '0', 'off', 'none')
-# we need this since SCons should work version indepentant
-True, False = 1, 0
-
def _text2bool(val):
"""
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
import sys
import unittest
o = opts.options[0]
env = {
- 'T' : SCons.Options.True,
- 'F' : SCons.Options.False,
+ 'T' : True,
+ 'F' : False,
'N' : 'xyzzy',
}
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-__all__ = ('PackageOption', 'True', 'False')
+__all__ = ('PackageOption')
import string
-from BoolOption import True, False
+import SCons.compat
import SCons.Errors
-__enable_strings = (str(True), 'yes', 'true', 'on', 'enable', 'search')
-__disable_strings = (str(False), 'no', 'false', 'off', 'disable')
+__enable_strings = ('1', 'yes', 'true', 'on', 'enable', 'search')
+__disable_strings = ('0', 'no', 'false', 'off', 'disable')
def _converter(val):
"""
"""
# todo: write validator, check for path
import os
- if env[key] == False:
- pass
- elif env[key] == True:
+ if env[key] is True:
if searchfunc:
env[key] = searchfunc(key, val)
- elif not os.path.exists(val):
+ elif env[key] and not os.path.exists(val):
raise SCons.Errors.UserError(
'Path does not exist for option %s: %s' % (key, val))
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
import sys
import unittest
import SCons.Errors
import SCons.Options
-from SCons.Options.BoolOption import True, False
import TestCmd
o = opts.options[0]
- env = {'F':0, 'T':1, 'X':'x'}
+ env = {'F':False, 'T':True, 'X':'x'}
exists = test.workpath('exists')
does_not_exist = test.workpath('does_not_exist')
import SCons.Util
import SCons.Warnings
-from BoolOption import BoolOption, True, False # okay
+from BoolOption import BoolOption # okay
from EnumOption import EnumOption # okay
from ListOption import ListOption # naja
from PackageOption import PackageOption # naja
of the options.
"""
- help_text = ""
-
if sort:
options = self.options[:]
options.sort(lambda x,y,func=sort: func(x.key,y.key))
import os
import string
+import SCons.Memoize
import SCons.Util
#
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import posix
-import os
def generate(env):
posix.generate(env)
try:
ret = exitvalmap[e[0]]
except KeyError:
- result = 127
sys.stderr.write("scons: unknown OSError exception code %d - %s: %s\n" % (e[0], cmd, e[1]))
if stderr != None:
stderr.write("scons: %s: %s\n" % (cmd, e[1]))
import SCons.Action
import SCons.Builder
import SCons.Errors
+import SCons.Job
import SCons.Node.FS
import SCons.Taskmaster
import SCons.Util
fd.write(source[0].get_contents())
fd.close()
def _stringSource( target, source, env ):
- import string
return (str(target[0]) + ' <-\n |' +
string.replace( source[0].get_contents(),
'\n', "\n |" ) )
Return everything written to orig since the Streamer was created.
"""
return self.s.getvalue()
+
+ def flush(self):
+ if self.orig:
+ self.orig.flush()
+ self.s.flush()
class SConfBuildTask(SCons.Taskmaster.Task):
except AttributeError:
# Earlier versions of Python don't have sys.excepthook...
def excepthook(type, value, tb):
- import traceback
traceback.print_tb(tb)
print type, value
apply(excepthook, self.exc_info())
else:
_ac_config_logs[self.logfile] = None
log_mode = "w"
- self.logstream = open(str(self.logfile), log_mode)
+ fp = open(str(self.logfile), log_mode)
+ self.logstream = SCons.Util.Unbuffered(fp)
# logfile may stay in a build directory, so we tell
# the build system not to override it with a eventually
# existing file with the same name in the source directory
import cPickle
import os
import os.path
-import string
-import time
import SCons.dblite
import SCons.Sig
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import string
-
import SCons.Node.FS
import SCons.Scanner
class ScannerTestCase(unittest.TestCase):
def test_creation(self):
- """Test creation of Scanner objects through the Scanner() function"""
+ """Test creation of Scanner objects"""
def func(self):
pass
- s = SCons.Scanner.Scanner(func)
+ s = SCons.Scanner.Base(func)
+ assert isinstance(s, SCons.Scanner.Base), s
+ s = SCons.Scanner.Base({})
assert isinstance(s, SCons.Scanner.Base), s
- s = SCons.Scanner.Scanner({})
- assert isinstance(s, SCons.Scanner.Selector), s
- s = SCons.Scanner.Scanner(func, name='fooscan')
+ s = SCons.Scanner.Base(func, name='fooscan')
assert str(s) == 'fooscan', str(s)
- s = SCons.Scanner.Scanner({}, name='barscan')
+ s = SCons.Scanner.Base({}, name='barscan')
assert str(s) == 'barscan', str(s)
- s = SCons.Scanner.Scanner(func, name='fooscan', argument=9)
+ s = SCons.Scanner.Base(func, name='fooscan', argument=9)
assert str(s) == 'fooscan', str(s)
assert s.argument == 9, s.argument
- s = SCons.Scanner.Scanner({}, name='fooscan', argument=888)
+ s = SCons.Scanner.Base({}, name='fooscan', argument=888)
assert str(s) == 'fooscan', str(s)
assert s.argument == 888, s.argument
class BaseTestCase(unittest.TestCase):
+ class skey_node:
+ def __init__(self, key):
+ self.key = key
+ def scanner_key(self):
+ return self.key
+ def rexists(self):
+ return 1
+
def func(self, filename, env, target, *args):
self.filename = filename
self.env = env
else:
self.failIf(hasattr(self, "arg"), "an argument was given when it shouldn't have been")
+ def test___call__dict(self):
+ """Test calling Scanner.Base objects with a dictionary"""
+ called = []
+ def s1func(node, env, path, called=called):
+ called.append('s1func')
+ called.append(node)
+ return []
+ def s2func(node, env, path, called=called):
+ called.append('s2func')
+ called.append(node)
+ return []
+ s1 = SCons.Scanner.Base(s1func)
+ s2 = SCons.Scanner.Base(s2func)
+ selector = SCons.Scanner.Base({'.x' : s1, '.y' : s2})
+ nx = self.skey_node('.x')
+ env = DummyEnvironment()
+ selector(nx, env, [])
+ assert called == ['s1func', nx], called
+ del called[:]
+ ny = self.skey_node('.y')
+ selector(ny, env, [])
+ assert called == ['s2func', ny], called
+
def test_path(self):
"""Test the Scanner.Base path() method"""
def pf(env, cwd, target, source, argument=None):
s = scanner.select('.x')
assert s is scanner, s
+ selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.x'))
+ assert s == 1, s
+ s = selector.select(self.skey_node('.y'))
+ assert s == 2, s
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+
+ def test_add_scanner(self):
+ """Test the Scanner.Base add_scanner() method"""
+ selector = SCons.Scanner.Base({'.x' : 1, '.y' : 2})
+ s = selector.select(self.skey_node('.z'))
+ assert s is None, s
+ selector.add_scanner('.z', 3)
+ s = selector.select(self.skey_node('.z'))
+ assert s == 3, s
+
def test___str__(self):
"""Test the Scanner.Base __str__() method"""
scanner = SCons.Scanner.Base(function = self.func)
_null = _Null
def Scanner(function, *args, **kw):
- """Public interface factory function for creating different types
+ """
+ Public interface factory function for creating different types
of Scanners based on the different types of "functions" that may
- be supplied."""
+ be supplied.
+
+ TODO: Deprecate this some day. We've moved the functionality
+ inside the Base class and really don't need this factory function
+ any more. It was, however, used by some of our Tool modules, so
+ the call probably ended up in various people's custom modules
+ patterned on SCons code.
+ """
if SCons.Util.is_Dict(function):
return apply(Selector, (function,) + args, kw)
else:
function,
name = "NONE",
argument = _null,
- skeys = [],
+ skeys = _null,
path_function = None,
node_class = SCons.Node.FS.Entry,
node_factory = None,
self.path_function = path_function
self.name = name
self.argument = argument
+
+ if skeys is _null:
+ if SCons.Util.is_Dict(function):
+ skeys = function.keys()
+ else:
+ skeys = []
self.skeys = skeys
+
self.node_class = node_class
self.node_factory = node_factory
self.scan_check = scan_check
if self.scan_check and not self.scan_check(node, env):
return []
+ self = self.select(node)
+
if not self.argument is _null:
list = self.function(node, env, path, self.argument)
else:
list = self.function(node, env, path)
+
kw = {}
if hasattr(node, 'dir'):
kw['directory'] = node.dir
self.skeys.append(skey)
def get_skeys(self, env=None):
- if SCons.Util.is_String(self.skeys):
+ if env and SCons.Util.is_String(self.skeys):
return env.subst_list(self.skeys)[0]
return self.skeys
def select(self, node):
- return self
+ if SCons.Util.is_Dict(self.function):
+ key = node.scanner_key()
+ try:
+ return self.function[key]
+ except KeyError:
+ return None
+ else:
+ return self
def _recurse_all_nodes(self, nodes):
return nodes
recurse_nodes = _recurse_no_nodes
+ def add_scanner(self, skey, scanner):
+ self.function[skey] = scanner
+ self.add_skey(skey)
+
class Selector(Base):
"""
A class for selecting a more specific scanner based on the
scanner_key() (suffix) for a specific Node.
+
+ TODO: This functionality has been moved into the inner workings of
+ the Base class, and this class will be deprecated at some point.
+ (It was never exposed directly as part of the public interface,
+ although it is used by the Scanner() factory function that was
+ used by various Tool modules and therefore was likely a template
+ for custom modules that may be out there.)
"""
def __init__(self, dict, *args, **kw):
apply(Base.__init__, (self, None,)+args, kw)
self.dict = dict
+ self.skeys = dict.keys()
def __call__(self, node, env, path = ()):
return self.select(node)(node, env, path)
def add_scanner(self, skey, scanner):
self.dict[skey] = scanner
+ self.add_skey(skey)
class Current(Base):
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import SCons.compat
+
import os
import os.path
import random
import SCons.Node.FS
from SCons.Optik import OptionParser, SUPPRESS_HELP, OptionValueError
import SCons.SConf
+import SCons.Script
import SCons.Sig
import SCons.Taskmaster
import SCons.Util
def postprocess(self):
if self.top:
t = self.targets[0]
- if print_tree:
- print
- SCons.Util.print_tree(t, get_all_children)
- if print_stree:
- print
- SCons.Util.print_tree(t, get_all_children, showtags=2)
- if print_dtree:
- print
- SCons.Util.print_tree(t, get_derived_children)
+ for tp in tree_printers:
+ tp.display(t)
if print_includes:
tree = t.render_include_tree()
if tree:
def executed(self):
pass
+
+class TreePrinter:
+ def __init__(self, derived=False, prune=False, status=False):
+ self.derived = derived
+ self.prune = prune
+ self.status = status
+ def get_all_children(self, node):
+ return node.all_children()
+ def get_derived_children(self, node):
+ children = node.all_children(None)
+ return filter(lambda x: x.has_builder(), children)
+ def display(self, t):
+ if self.derived:
+ func = self.get_derived_children
+ else:
+ func = self.get_all_children
+ s = self.status and 2 or 0
+ SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
+
+
# Global variables
+tree_printers = []
+
keep_going_on_error = 0
-print_dtree = 0
print_explanations = 0
print_includes = 0
print_objects = 0
print_memoizer = 0
print_stacktrace = 0
-print_stree = 0
print_time = 0
-print_tree = 0
ignore_errors = 0
sconscript_time = 0
command_time = 0
# utility functions
-def get_all_children(node): return node.all_children()
-
-def get_derived_children(node):
- children = node.all_children(None)
- return filter(lambda x: x.has_builder(), children)
-
def _scons_syntax_error(e):
"""Handle syntax errors. Print out a message and show where the error
occurred.
def _set_globals(options):
global keep_going_on_error, ignore_errors
- global count_stats, print_dtree
+ global count_stats
global print_explanations, print_includes, print_memoizer
- global print_objects, print_stacktrace, print_stree
- global print_time, print_tree
+ global print_objects, print_stacktrace, print_time
+ global tree_printers
global memory_stats
keep_going_on_error = options.keep_going
if "count" in debug_values:
count_stats.enable(sys.stdout)
if "dtree" in debug_values:
- print_dtree = 1
+ tree_printers.append(TreePrinter(derived=True))
if "explain" in debug_values:
print_explanations = 1
if "findlibs" in debug_values:
if "stacktrace" in debug_values:
print_stacktrace = 1
if "stree" in debug_values:
- print_stree = 1
+ tree_printers.append(TreePrinter(status=True))
if "time" in debug_values:
print_time = 1
if "tree" in debug_values:
- print_tree = 1
+ tree_printers.append(TreePrinter())
ignore_errors = options.ignore_errors
def _create_path(plist):
path = path + '/' + d
return path
+def _load_site_scons_dir(topdir, site_dir_name=None):
+ """Load the site_scons dir under topdir.
+ Adds site_scons to sys.path, imports site_scons/site_init.py,
+ and adds site_scons/site_tools to default toolpath."""
+ if site_dir_name:
+ err_if_not_found = True # user specified: err if missing
+ else:
+ site_dir_name = "site_scons"
+ err_if_not_found = False
+
+ site_dir = os.path.join(topdir.path, site_dir_name)
+ if not os.path.exists(site_dir):
+ if err_if_not_found:
+ raise SCons.Errors.UserError, "site dir %s not found."%site_dir
+ return
+
+ site_init_filename = "site_init.py"
+ site_init_modname = "site_init"
+ site_tools_dirname = "site_tools"
+ sys.path = [site_dir] + sys.path
+ site_init_file = os.path.join(site_dir, site_init_filename)
+ site_tools_dir = os.path.join(site_dir, site_tools_dirname)
+ if os.path.exists(site_init_file):
+ import imp
+ try:
+ fp, pathname, description = imp.find_module(site_init_modname,
+ [site_dir])
+ try:
+ imp.load_module(site_init_modname, fp, pathname, description)
+ finally:
+ if fp:
+ fp.close()
+ except ImportError, e:
+ sys.stderr.write("Can't import site init file '%s': %s\n"%(site_init_file, e))
+ raise
+ except Exception, e:
+ sys.stderr.write("Site init file '%s' raised exception: %s\n"%(site_init_file, e))
+ raise
+ if os.path.exists(site_tools_dir):
+ SCons.Tool.DefaultToolpath.append(os.path.abspath(site_tools_dir))
+
def version_string(label, module):
fmt = "\t%s: v%s.%s, %s, by %s on %s\n"
return fmt % (label,
"pdb", "presub", "stacktrace", "stree",
"time", "tree"]
- deprecated_debug_options = [ "nomemoizer", ]
+ deprecated_debug_options = {
+ "nomemoizer" : ' and has no effect',
+ }
def opt_debug(option, opt, value, parser, debug_options=debug_options, deprecated_debug_options=deprecated_debug_options):
if value in debug_options:
except AttributeError:
parser.values.debug = []
parser.values.debug.append(value)
- elif value in deprecated_debug_options:
- w = "The --debug=%s option is deprecated and has no effect." % value
+ elif value in deprecated_debug_options.keys():
+ msg = deprecated_debug_options[value]
+ w = "The --debug=%s option is deprecated%s." % (value, msg)
delayed_warnings.append((SCons.Warnings.DeprecatedWarning, w))
else:
raise OptionValueError("Warning: %s is not a valid debug type" % value)
'--recon', action="store_true", dest='noexec',
default=0, help="Don't build; just print commands.")
+ self.add_option('--no-site-dir', action="store_true",
+ dest='no_site_dir', default=0,
+ help="Don't search or use the usual site_scons dir.")
+
self.add_option('--profile', action="store",
dest="profile_file", metavar="FILE",
help="Profile SCons and put results in FILE.")
self.add_option('-s', '--silent', '--quiet', action="store_true",
default=0, help="Don't print commands.")
+ self.add_option('--site-dir', action="store",
+ dest='site_dir', metavar="DIR",
+ help="Use DIR instead of the usual site_scons dir.")
+
self.add_option('--taskmastertrace', action="store",
dest="taskmastertrace_file", metavar="FILE",
help="Trace Node evaluation to FILE.")
+ tree_options = ["all", "derived", "prune", "status"]
+
+ def opt_tree(option, opt, value, parser, tree_options=tree_options):
+ tp = TreePrinter()
+ for o in string.split(value, ','):
+ if o == 'all':
+ tp.derived = False
+ elif o == 'derived':
+ tp.derived = True
+ elif o == 'prune':
+ tp.prune = True
+ elif o == 'status':
+ tp.status = True
+ else:
+ raise OptionValueError("Warning: %s is not a valid --tree option" % o)
+ tree_printers.append(tp)
+
+ self.add_option('--tree', action="callback", type="string",
+ callback=opt_tree, nargs=1, metavar="OPTIONS",
+ help="Print a dependency tree in various formats: "
+ "%s." % string.join(tree_options, ", "))
+
self.add_option('-u', '--up', '--search-up', action="store_const",
dest="climb_up", default=0, const=1,
help="Search up directory tree for SConstruct, "
metavar="WARNING-SPEC",
help="Enable or disable warnings.")
- self.add_option('-Y', '--repository', nargs=1, action="append",
+ self.add_option('-Y', '--repository', '--srcdir',
+ nargs=1, action="append",
help="Search REPOSITORY for source and target files.")
self.add_option('-e', '--environment-overrides', action="callback",
if options.cache_show:
fs.cache_show = 1
+ if options.site_dir:
+ _load_site_scons_dir(d, options.site_dir)
+ elif not options.no_site_dir:
+ _load_site_scons_dir(d)
+
if options.include_dir:
sys.path = options.include_dir + sys.path
SCons.Script._Add_Targets(targets)
SCons.Script._Add_Arguments(xmit_args)
- class Unbuffered:
- def __init__(self, file):
- self.file = file
- def write(self, arg):
- self.file.write(arg)
- self.file.flush()
- def __getattr__(self, attr):
- return getattr(self.file, attr)
-
- sys.stdout = Unbuffered(sys.stdout)
+ sys.stdout = SCons.Util.Unbuffered(sys.stdout)
memory_stats.append('before reading SConscript files:')
count_stats.append(('pre-', 'read'))
def annotate(node):
"""Annotate a node with the stack frame describing the
SConscript file and line number that created it."""
- tb = exc_tb = sys.exc_info()[2]
+ tb = sys.exc_info()[2]
while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
tb = tb.tb_next
if not tb:
# We did not find any exec of an SConscript file: what?!
- raise InternalError, "could not find SConscript stack frame"
+ raise SCons.Errors.InternalError, "could not find SConscript stack frame"
node.creator = traceback.extract_stack(tb)[0]
# The following line would cause each Node to be annotated using the
SConscriptSettableOptions = Main.SConscriptSettableOptions
keep_going_on_error = Main.keep_going_on_error
-print_dtree = Main.print_dtree
+#print_dtree = Main.print_dtree
print_explanations = Main.print_explanations
print_includes = Main.print_includes
print_objects = Main.print_objects
print_time = Main.print_time
-print_tree = Main.print_tree
+#print_tree = Main.print_tree
memory_stats = Main.memory_stats
ignore_errors = Main.ignore_errors
#sconscript_time = Main.sconscript_time
'Depends',
'Dir',
'NoClean',
+ 'NoCache',
'Entry',
'Execute',
'File',
for name in GlobalDefaultEnvironmentFunctions + GlobalDefaultBuilders:
exec "%s = _SConscript.DefaultEnvironmentCall(%s)" % (name, repr(name))
+del name
# There are a handful of variables that used to live in the
# Script/SConscript.py module that some SConscript files out there were
# this way by hanging some attributes off the "SConscript" object here.
SConscript = _SConscript.DefaultEnvironmentCall('SConscript')
+# Make SConscript look enough like the module it used to be so
+# that pychecker doesn't barf.
+SConscript.__name__ = 'SConscript'
+
SConscript.Arguments = ARGUMENTS
SConscript.ArgList = ARGLIST
SConscript.BuildTargets = BUILD_TARGETS
# This probably indicates that it's a callable
# object that doesn't match our calling arguments
# (like an Action).
- s = str(s)
+ if self.mode == SUBST_RAW:
+ return s
+ s = self.conv(s)
return self.substitute(s, lvars)
elif s is None:
return ''
# This probably indicates that it's a callable
# object that doesn't match our calling arguments
# (like an Action).
- s = str(s)
+ if self.mode == SUBST_RAW:
+ self.append(s)
+ return
+ s = self.conv(s)
self.substitute(s, lvars, within_list)
elif s is None:
self.this_word()
MyNode("/bar/ack.cpp"),
MyNode("../foo/ack.c") ]
+ callable_object = TestCallable('callable-1')
+
loc = {
'xxx' : None,
'null' : '',
'SSS' : '$RRR',
# Test callables that don't match the calling arguments.
- 'CALLABLE' : TestCallable('callable-1'),
+ 'CALLABLE' : callable_object,
}
env = DummyEnv(loc)
else:
raise AssertionError, "did not catch expected UserError"
+ # Test that the combination of SUBST_RAW plus a pass-through
+ # conversion routine allows us to fetch a function through the
+ # dictionary. CommandAction uses this to allow delayed evaluation
+ # of $SPAWN variables.
+ x = lambda x: x
+ r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars)
+ assert r is callable_object, repr(r)
+ r = scons_subst("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == 'callable-1', repr(r)
+
# Test how we handle overriding the internal conversion routines.
def s(obj):
return obj
MyNode("/bar/ack.cpp"),
MyNode("../foo/ack.c") ]
+ callable_object = TestCallable('callable-2')
+
def _defines(defs):
l = []
for d in defs:
'SSS' : '$RRR',
# Test callable objects that don't match our calling arguments.
- 'CALLABLE' : TestCallable('callable-2'),
+ 'CALLABLE' : callable_object,
'_defines' : _defines,
'DEFS' : [ ('Q1', '"q1"'), ('Q2', '"$AAA"') ],
# Test callables that don't match our calling arguments.
'$CALLABLE', [['callable-2']],
- # Test
-
# Test handling of quotes.
# XXX Find a way to handle this in the future.
#'aaa "bbb ccc" ddd', [['aaa', 'bbb ccc', 'ddd']],
else:
raise AssertionError, "did not catch expected SyntaxError"
+ # Test that the combination of SUBST_RAW plus a pass-through
+ # conversion routine allows us to fetch a function through the
+ # dictionary.
+ x = lambda x: x
+ r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, conv=x, gvars=gvars)
+ assert r == [[callable_object]], repr(r)
+ r = scons_subst_list("$CALLABLE", env, mode=SUBST_RAW, gvars=gvars)
+ assert r == [['callable-2']], repr(r)
+
# Test we handle overriding the internal conversion routines.
def s(obj):
return obj
which has Task subclasses that handle its specific behavior,
like printing "`foo' is up to date" when a top-level target
doesn't need to be built, and handling the -c option by removing
- targets as its "build" action.
+ targets as its "build" action. There is also a separate subclass
+ for suppressing this output when the -q option is used.
The Taskmaster instantiates a Task object for each (set of)
target(s) that it decides need to be evaluated and/or built.
StateString = SCons.Node.StateString
+
+
# A subsystem for recording stats about how different Nodes are handled by
# the main Taskmaster loop. There's no external control here (no need for
# a --debug= option); enable it by changing the value of CollectStats.
"""
A simple class for holding statistics about the disposition of a
Node by the Taskmaster. If we're collecting statistics, each Node
- processed by the Taskmaster gets one of these attached, in which
+ processed by the Taskmaster gets one of these attached, in which case
the Taskmaster records its decision each time it processes the Node.
(Ideally, that's just once per Node.)
"""
for n in StatsNodes:
print (fmt % n.stats.__dict__) + str(n)
+
+
class Task:
- """Default SCons build engine task.
+ """
+ Default SCons build engine task.
This controls the interaction of the actual building of node
and the rest of the engine.
Note that it's generally a good idea for sub-classes to call
these methods explicitly to update state, etc., rather than
- roll their own interaction with Taskmaster from scratch."""
+ roll their own interaction with Taskmaster from scratch.
+ """
def __init__(self, tm, targets, top, node):
self.tm = tm
self.targets = targets
self.exc_clear()
def display(self, message):
- """Allow the calling interface to display a message
+ """
+ Hook to allow the calling interface to display a message.
+
+ This hook gets called as part of preparing a task for execution
+ (that is, a Node to be built). As part of figuring out what Node
+ should be built next, the actually target list may be altered,
+ along with a message describing the alteration. The calling
+ interface can subclass Task and provide a concrete implementation
+ of this method to see those messages.
"""
pass
def prepare(self):
- """Called just before the task is executed.
+ """
+ Called just before the task is executed.
- This unlinks all targets and makes all directories before
- building anything."""
+ This is mainly intended to give the target Nodes a chance to
+ unlink underlying files and make all necessary directories before
+ the Action is actually called to build the targets.
+ """
# Now that it's the appropriate time, give the TaskMaster a
# chance to raise any exceptions it encountered while preparing
return self.node
def execute(self):
- """Called to execute the task.
+ """
+ Called to execute the task.
This method is called from multiple threads in a parallel build,
so only do thread safe stuff here. Do thread unsafe stuff in
- prepare(), executed() or failed()."""
+ prepare(), executed() or failed().
+ """
try:
everything_was_cached = 1
sys.exc_info())
def executed(self):
- """Called when the task has been successfully executed.
+ """
+ Called when the task has been successfully executed.
- This may have been a do-nothing operation (to preserve
- build order), so check the node's state before updating
- things. Most importantly, this calls back to the
- Taskmaster to put any node tasks waiting on this one
- back on the pending list."""
+ This may have been a do-nothing operation (to preserve build
+ order), so we have to check the node's state before deciding
+ whether it was "built" or just "visited."
+ """
for t in self.targets:
if t.get_state() == SCons.Node.executing:
t.set_state(SCons.Node.executed)
else:
t.visited()
- self.tm.executed(self.node)
-
def failed(self):
- """Default action when a task fails: stop the build."""
+ """
+ Default action when a task fails: stop the build.
+ """
self.fail_stop()
def fail_stop(self):
- """Explicit stop-the-build failure."""
+ """
+ Explicit stop-the-build failure.
+ """
for t in self.targets:
t.set_state(SCons.Node.failed)
- self.tm.failed(self.node)
self.tm.stop()
# We're stopping because of a build failure, but give the
self.top = 1
def fail_continue(self):
- """Explicit continue-the-build failure.
+ """
+ Explicit continue-the-build failure.
This sets failure status on the target nodes and all of
their dependent parent nodes.
def set_state(node): node.set_state(SCons.Node.failed)
t.call_for_all_waiting_parents(set_state)
- self.tm.executed(self.node)
-
def make_ready_all(self):
- """Mark all targets in a task ready for execution.
+ """
+ Marks all targets in a task ready for execution.
This is used when the interface needs every target Node to be
visited--the canonical example being the "scons -c" option.
s.set_state(SCons.Node.executing)
def make_ready_current(self):
- """Mark all targets in a task ready for execution if any target
+ """
+ Marks all targets in a task ready for execution if any target
is not current.
This is the default behavior for building only what's necessary.
make_ready = make_ready_current
def postprocess(self):
- """Post process a task after it's been executed."""
+ """
+ Post-processes a task after it's been executed.
+
+ This examines all the targets just built (or not, we don't care
+ if the build was successful, or even if there was no build
+ because everything was up-to-date) to see if they have any
+ waiting parent Nodes, or Nodes waiting on a common side effect,
+ that can be put back on the candidates list.
+ """
+
+ # We may have built multiple targets, some of which may have
+ # common parents waiting for this build. Count up how many
+ # targets each parent was waiting for so we can subtract the
+ # values later, and so we *don't* put waiting side-effect Nodes
+ # back on the candidates list if the Node is also a waiting
+ # parent.
+
parents = {}
for t in self.targets:
for p in t.waiting_parents.keys():
parents[p] = parents.get(p, 0) + 1
+
for t in self.targets:
for s in t.side_effects:
if s.get_state() == SCons.Node.executing:
for p in s.waiting_s_e.keys():
if p.ref_count == 0:
self.tm.candidates.append(p)
+
for p, subtract in parents.items():
p.ref_count = p.ref_count - subtract
if p.ref_count == 0:
self.tm.candidates.append(p)
+
for t in self.targets:
t.postprocess()
+ # Exception handling subsystem.
+ #
+ # Exceptions that occur while walking the DAG or examining Nodes
+ # must be raised, but must be raised at an appropriate time and in
+ # a controlled manner so we can, if necessary, recover gracefully,
+ # possibly write out signature information for Nodes we've updated,
+ # etc. This is done by having the Taskmaster tell us about the
+ # exception, and letting
+
def exc_info(self):
+ """
+ Returns info about a recorded exception.
+ """
return self.exception
def exc_clear(self):
+ """
+ Clears any recorded exception.
+
+ This also changes the "exception_raise" attribute to point
+ to the appropriate do-nothing method.
+ """
self.exception = (None, None, None)
self.exception_raise = self._no_exception_to_raise
def exception_set(self, exception=None):
+ """
+ Records an exception to be raised at the appropriate time.
+
+ This also changes the "exception_raise" attribute to point
+ to the method that will, in fact
+ """
if not exception:
exception = sys.exc_info()
self.exception = exception
pass
def _exception_raise(self):
- """Raise a pending exception that was recorded while
- getting a Task ready for execution."""
- self.tm.exception_raise(self.exc_info())
-
-
-def order(dependencies):
- """Re-order a list of dependencies (if we need to)."""
- return dependencies
+ """
+ Raises a pending exception that was recorded while getting a
+ Task ready for execution.
+ """
+ exc = self.exc_info()[:]
+ try:
+ exc_type, exc_value, exc_traceback = exc
+ except ValueError:
+ exc_type, exc_value = exc
+ exc_traceback = None
+ raise exc_type, exc_value, exc_traceback
def find_cycle(stack):
class Taskmaster:
- """A generic Taskmaster for handling a bunch of targets.
-
- Classes that override methods of this class should call
- the base class method, so this class can do its thing.
+ """
+ The Taskmaster for walking the dependency DAG.
"""
- def __init__(self, targets=[], tasker=Task, order=order, trace=None):
+ def __init__(self, targets=[], tasker=Task, order=None, trace=None):
self.top_targets = targets[:]
self.top_targets.reverse()
self.candidates = []
self.tasker = tasker
- self.ready = None # the next task that is ready to be executed
+ if not order:
+ order = lambda l: l
self.order = order
self.message = None
self.trace = trace
self.next_candidate = self.find_next_candidate
def find_next_candidate(self):
+ """
+ Returns the next candidate Node for (potential) evaluation.
+
+ The candidate list (really a stack) initially consists of all of
+ the top-level (command line) targets provided when the Taskmaster
+ was initialized. While we walk the DAG, visiting Nodes, all the
+ children that haven't finished processing get pushed on to the
+ candidate list. Each child can then be popped and examined in
+ turn for whether *their* children are all up-to-date, in which
+ case a Task will be created for their actual evaluation and
+ potential building.
+
+ Here is where we also allow candidate Nodes to alter the list of
+ Nodes that should be examined. This is used, for example, when
+ invoking SCons in a source directory. A source directory Node can
+ return its corresponding build directory Node, essentially saying,
+ "Hey, you really need to build this thing over here instead."
+ """
try:
return self.candidates.pop()
except IndexError:
return node
def no_next_candidate(self):
+ """
+ Stops Taskmaster processing by not returning a next candidate.
+ """
return None
def _find_next_ready_node(self):
- """Find the next node that is ready to be built"""
-
- if self.ready:
- return
+ """
+ Finds the next node that is ready to be built.
+
+ This is *the* main guts of the DAG walk. We loop through the
+ list of candidates, looking for something that has no un-built
+ children (i.e., that is a leaf Node or has dependencies that are
+ all leaf Nodes or up-to-date). Candidate Nodes are re-scanned
+ (both the target Node itself and its sources, which are always
+ scanned in the context of a given target) to discover implicit
+ dependencies. A Node that must wait for some children to be
+ built will be put back on the candidates list after the children
+ have finished building. A Node that has been put back on the
+ candidates list in this way may have itself (or its sources)
+ re-scanned, in order to handle generated header files (e.g.) and
+ the implicit dependencies therein.
+
+ Note that this method does not do any signature calculation or
+ up-to-date check itself. All of that is handled by the Task
+ class. This is purely concerned with the dependency graph walk.
+ """
self.ready_exc = None
while 1:
node = self.next_candidate()
if node is None:
- self.ready = None
- break
+ return None
node = node.disambiguate()
state = node.get_state()
exc_value = sys.exc_info()[1]
e = SCons.Errors.ExplicitExit(node, exc_value.code)
self.ready_exc = (SCons.Errors.ExplicitExit, e)
- self.ready = node
if T: T.write(' SystemExit\n')
- break
+ return node
except KeyboardInterrupt:
if T: T.write(' KeyboardInterrupt\n')
raise
# BuildDir, or a Scanner threw something). Arrange to
# raise the exception when the Task is "executed."
self.ready_exc = sys.exc_info()
- self.ready = node
if S: S.problem = S.problem + 1
if T: T.write(' exception\n')
- break
+ return node
if T and children:
c = map(str, children)
continue
# Skip this node if it has side-effects that are currently being
- # built themselves or waiting for something else being built.
+ # built themselves or waiting for something else being built.
side_effects = filter(lambda N:
N.get_state() == SCons.Node.executing,
node.side_effects)
# The default when we've gotten through all of the checks above:
# this node is ready to be built.
- self.ready = node
if S: S.build = S.build + 1
if T: T.write(' evaluating %s\n' % node)
- break
+ return node
- def next_task(self):
- """Return the next task to be executed."""
+ return None
- self._find_next_ready_node()
+ def next_task(self):
+ """
+ Returns the next task to be executed.
- node = self.ready
+ This simply asks for the next Node to be evaluated, and then wraps
+ it in the specific Task subclass with which we were initialized.
+ """
+ node = self._find_next_ready_node()
if node is None:
return None
if self.ready_exc:
task.exception_set(self.ready_exc)
- self.ready = None
self.ready_exc = None
return task
def stop(self):
- """Stop the current build completely."""
+ """
+ Stops the current build completely.
+ """
self.next_candidate = self.no_next_candidate
- self.ready = None
-
- def failed(self, node):
- pass
-
- def executed(self, node):
- pass
-
- def exception_raise(self, exception):
- exc = exception[:]
- try:
- exc_type, exc_value, exc_traceback = exc
- except ValueError:
- exc_type, exc_value = exc
- exc_traceback = None
- raise exc_type, exc_value, exc_traceback
assert built_text == "MyTM.stop()"
assert tm.next_task() is None
- def test_failed(self):
- """Test when a task has failed
- """
- n1 = Node("n1")
- tm = SCons.Taskmaster.Taskmaster([n1])
- t = tm.next_task()
- assert t.targets == [n1], map(str, t.targets)
- tm.failed(n1)
- assert t.targets == [n1], map(str, t.targets)
-
def test_executed(self):
"""Test when a task has been executed
"""
else:
assert 0, "did not catch expected exception"
- t.exception_set(("exception 4", "XYZZY"))
- def fw_exc(exc):
- raise 'exception_forwarded', exc
- tm.exception_raise = fw_exc
- try:
- t.exception_raise()
- except:
- exc_type, exc_value = sys.exc_info()[:2]
- assert exc_type == 'exception_forwarded', exc_type
- assert exc_value[0] == "exception 4", exc_value[0]
- assert exc_value[1] == "XYZZY", exc_value[1]
- else:
- assert 0, "did not catch expected exception"
-
def test_postprocess(self):
"""Test postprocessing targets to give them a chance to clean up
"""
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import os.path
-
import SCons.Action
import SCons.Builder
import SCons.Util
self.outer_state = outer_state
self.tokens_to_find = 2
def parseToken(self, token):
- # This is an anonymous class if and only if the next token
- # is a bracket
+ # This is an anonymous class if and only if the next
+ # non-whitespace token is a bracket
+ if token == '\n':
+ return self
if token == '{':
self.outer_state.addAnonClass()
elif token in ['"', "'"]:
assert classes == ['a'], classes
+ def test_anon_classes(self):
+ """Test anonymous classes"""
+ pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+public abstract class TestClass
+{
+ public void completed()
+ {
+ new Thread()
+ {
+ }.start();
+
+ new Thread()
+ {
+ }.start();
+ }
+}
+""")
+ assert pkg_dir == None, pkg_dir
+ assert classes == ['TestClass$1', 'TestClass$2', 'TestClass'], classes
+
+
if __name__ == "__main__":
suite = unittest.TestSuite()
return self.dict[key]
def __setitem__(self, key, val):
self.dict[key] = val
+ def has_key(self, key):
+ return self.dict.has_key(key)
env = Environment()
env['BUILDERS'] = {}
env['ENV'] = {}
import imp
import sys
+import SCons.Builder
import SCons.Errors
import SCons.Scanner
import SCons.Scanner.C
import SCons.Scanner.LaTeX
import SCons.Scanner.Prog
+DefaultToolpath=[]
+
CScanner = SCons.Scanner.C.CScanner()
DScanner = SCons.Scanner.D.DScanner()
LaTeXScanner = SCons.Scanner.LaTeX.LaTeXScanner()
ProgramScanner = SCons.Scanner.Prog.ProgramScanner()
-SourceFileScanner = SCons.Scanner.Scanner({}, name='SourceFileScanner')
+SourceFileScanner = SCons.Scanner.Base({}, name='SourceFileScanner')
CSuffixes = [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
".h", ".H", ".hxx", ".hpp", ".hh",
class Tool:
def __init__(self, name, toolpath=[], **kw):
self.name = name
- self.toolpath = toolpath
+ self.toolpath = toolpath + DefaultToolpath
# remember these so we can merge them into the call
self.init_kw = kw
Add Builders and construction variables for Visual Age C++ compilers
to an Environment.
"""
+ import SCons.Tool
+ import SCons.Tool.cc
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
for suffix in CXXSuffixes:
shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
+
+ SCons.Tool.cc.add_common_cc_variables(env)
env['CXX'] = 'c++'
env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
if not SCons.Util.case_sensitive_suffixes('.c', '.C'):
CSuffixes.append('.C')
+def add_common_cc_variables(env):
+ """
+ Add underlying common "C compiler" variables that
+ are used by multiple tools (specifically, c++).
+ """
+ if not env.has_key('_CCCOMCOM'):
+ env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
+ # It's a hack to test for darwin here, but the alternative
+ # of creating an applecc.py to contain this seems overkill.
+ # Maybe someday the Apple platform will require more setup and
+ # this logic will be moved.
+ env['FRAMEWORKS'] = SCons.Util.CLVar('')
+ env['FRAMEWORKPATH'] = SCons.Util.CLVar('')
+ if env['PLATFORM'] == 'darwin':
+ env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH'
+
+ if not env.has_key('CCFLAGS'):
+ env['CCFLAGS'] = SCons.Util.CLVar('')
+
+ if not env.has_key('SHCCFLAGS'):
+ env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
+
def generate(env):
"""
Add Builders and construction variables for C compilers to an Environment.
shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
static_obj.add_emitter(suffix, SCons.Defaults.StaticObjectEmitter)
shared_obj.add_emitter(suffix, SCons.Defaults.SharedObjectEmitter)
-
- env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS'
- # It's a hack to test for darwin here, but the alternative of creating
- # an applecc.py to contain this seems overkill. Maybe someday the Apple
- # platform will require more setup and this logic will be moved.
- env['FRAMEWORKS'] = SCons.Util.CLVar('')
- env['FRAMEWORKPATH'] = SCons.Util.CLVar('')
- if env['PLATFORM'] == 'darwin':
- env['_CCCOMCOM'] = env['_CCCOMCOM'] + ' $_FRAMEWORKPATH'
+
+ add_common_cc_variables(env)
env['CC'] = 'cc'
- env['CCFLAGS'] = SCons.Util.CLVar('')
env['CFLAGS'] = SCons.Util.CLVar('')
env['CCCOM'] = '$CC -o $TARGET -c $CFLAGS $CCFLAGS $_CCCOMCOM $SOURCES'
env['SHCC'] = '$CC'
- env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import SCons.Util
import fortran
compilers = ['f90']
env.Append(LIBS = ['phobos'])
if 'pthread' not in libs:
env.Append(LIBS = ['pthread'])
+ if 'm' not in libs:
+ env.Append(LIBS = ['m'])
return defaultLinker
env['SMART_LINKCOM'] = smart_link[linkcom] = _smartLink
def generate(env):
try:
- bld = env['BUILDERS']['DVI']
+ env['BUILDERS']['DVI']
except KeyError:
global DVIBuilder
for suffix in F77Suffixes + F77PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F77Scan)
+del suffix
#
fVLG = fortran.VariableListGenerator
for suffix in F90Suffixes + F90PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F90Scan)
+del suffix
#
fVLG = fortran.VariableListGenerator
for suffix in F95Suffixes + F95PPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, F95Scan)
+del suffix
#
fVLG = fortran.VariableListGenerator
for suffix in FortranSuffixes + FortranPPSuffixes:
SCons.Tool.SourceFileScanner.add_scanner(suffix, FortranScan)
+del suffix
#
def _fortranEmitter(target, source, env):
return v # or v.encode('iso-8859-1', 'replace') to remove unicode?
except SCons.Util.RegError:
raise MissingRegistryError, \
- "%s\\%s was not found in the registry."%(K, value)
+ "%s\\%s was not found in the registry."%(K, valuename)
def get_all_compiler_versions():
import SCons.Action
import SCons.Builder
+import SCons.Subst
import SCons.Util
def jarSources(target, source, env, for_signature):
"""Only include sources that are not a manifest file."""
jarchdir = env.subst('$JARCHDIR')
+ if jarchdir:
+ jarchdir = env.fs.Dir(jarchdir)
result = []
for src in source:
contents = src.get_contents()
if jarchdir:
# If we are changing the dir with -C, then sources should
# be relative to that directory.
- src = src.get_path(src.fs.Dir(jarchdir))
+ src = SCons.Subst.Literal(src.get_path(jarchdir))
result.append('-C')
result.append(jarchdir)
result.append(src)
import SCons.Action
import SCons.Builder
+import SCons.Defaults
import SCons.Tool
import SCons.Util
if len(buildtarget) == 1:
bt = buildtarget[0]
buildtarget = []
- for v in variants:
+ for _ in variants:
buildtarget.append(bt)
if not env.has_key('outdir') or env['outdir'] == None:
self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
for name in confkeys:
- name = name
variant = self.configs[name].variant
platform = self.configs[name].platform
if self.version_num >= 8.0:
try:
idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
r'Software\Microsoft\VisualStudio\8.0')
- id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
+ SCons.Util.RegQueryValueEx(idk, 'InstallDir')
editions = { 'PRO': r'Setup\VS\Pro' } # ToDo: add standard and team editions
edition_name = 'STD'
for name, key_suffix in editions.items():
try:
idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
r'Software\Microsoft\VCExpress\8.0')
- id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
+ SCons.Util.RegQueryValueEx(idk, 'InstallDir')
suites.append('EXPRESS')
except SCons.Util.RegError:
pass
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
-import SCons.Util
import os
import os.path
import string
+import SCons.Util
def set_vars(env):
"""Set MWCW_VERSION, MWCW_VERSIONS, and some codewarrior environment vars
def generate(env):
"""Add Builders and construction variables for the mwcc to an Environment."""
+ import SCons.Defaults
+ import SCons.Tool
set_vars(env)
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import SCons.Tool
-import SCons.Tool.mwcc
def generate(env):
def exists(env):
- return mwcc.set_versions(env)
+ import SCons.Tool.mwcc
+ return SCons.Tool.mwcc.set_vars(env)
def shlib_generator(target, source, env, for_signature):
def generate(env):
try:
- bld = env['BUILDERS']['PDF']
+ env['BUILDERS']['PDF']
except KeyError:
global PDFBuilder
if PDFBuilder is None:
(str(moc), str(cpp)))
def find_file(filename, paths, node_factory):
- retval = None
for dir in paths:
node = node_factory(filename, dir)
if node.rexists():
return target, source
def uicScannerFunc(node, env, path):
- dir = node.dir
lookout = []
lookout.extend(env['CPPPATH'])
lookout.append(str(node.rfile().dir))
result.append(dep)
return result
-uicScanner = SCons.Scanner.Scanner(uicScannerFunc,
- name = "UicScanner",
- node_class = SCons.Node.FS.File,
- node_factory = SCons.Node.FS.File,
- recursive = 0)
+uicScanner = SCons.Scanner.Base(uicScannerFunc,
+ name = "UicScanner",
+ node_class = SCons.Node.FS.File,
+ node_factory = SCons.Node.FS.File,
+ recursive = 0)
def generate(env):
"""Add Builders and construction variables for qt to an Environment."""
CLVar = SCons.Util.CLVar
Action = SCons.Action.Action
Builder = SCons.Builder.Builder
- splitext = SCons.Util.splitext
env.SetDefault(QTDIR = _detect(env),
QT_BINPATH = os.path.join('$QTDIR', 'bin'),
return found
def _scanSwig(node, env, path):
- import sys
r = recurse(str(node), [os.path.abspath(os.path.dirname(str(node))), os.path.abspath(os.path.join("include", "swig"))])
return r
for src in source:
src = str(src)
mname = None
- if "-python" in SCons.Util.CLVar(env.subst("$SWIGFLAGS")):
+ flags = SCons.Util.CLVar(env.subst("$SWIGFLAGS"))
+ if "-python" in flags and "-noproxy" not in flags:
f = open(src)
try:
for l in f.readlines():
# Now decide if latex needs to be run yet again.
logfilename = basename + '.log'
- for trial in range(int(env.subst('$LATEXRETRIES'))):
+ for _ in range(int(env.subst('$LATEXRETRIES'))):
if not os.path.exists(logfilename):
break
content = open(logfilename, "rb").read()
import os.path
import SCons.Builder
+import SCons.Defaults
import SCons.Node.FS
import SCons.Util
import re
import string
import sys
-import stat
import types
from UserDict import UserDict
rname = str(root)
- if visited.has_key(rname):
- return ""
-
children = child_func(root)
retval = ""
for pipe in margin[:-1]:
else:
retval = retval + " "
+ if visited.has_key(rname):
+ return retval + "+-[" + rname + "]\n"
+
retval = retval + "+-" + rname + "\n"
if not prune:
visited = copy.copy(visited)
rname = str(root)
- if visited.has_key(rname):
- return
-
if showtags:
if showtags == 2:
- print ' E = exists'
- print ' R = exists in repository only'
- print ' b = implicit builder'
- print ' B = explicit builder'
- print ' S = side effect'
- print ' P = precious'
- print ' A = always build'
- print ' C = current'
- print ' N = no clean'
+ print ' E = exists'
+ print ' R = exists in repository only'
+ print ' b = implicit builder'
+ print ' B = explicit builder'
+ print ' S = side effect'
+ print ' P = precious'
+ print ' A = always build'
+ print ' C = current'
+ print ' N = no clean'
+ print ' H = no cache'
print ''
tags = ['[']
tags.append(' A'[IDX(root.always_build)])
tags.append(' C'[IDX(root.current())])
tags.append(' N'[IDX(root.noclean)])
+ tags.append(' H'[IDX(root.nocache)])
tags.append(']')
else:
return [" ","| "][m]
margins = map(MMM, margin[:-1])
+ if visited.has_key(rname):
+ print string.join(tags + margins + ['+-[', rname, ']'], '')
+ return
+
print string.join(tags + margins + ['+-', rname], '')
if prune:
else:
def WhereIs(file, path=None, pathext=None, reject=[]):
+ import stat
if path is None:
try:
path = os.environ['PATH']
for x in s:
u[x] = 1
except TypeError:
- del u # move on to the next method
+ pass # move on to the next method
else:
return u.keys()
+ del u
# We can't hash all the elements. Second fastest is to sort,
# which brings the equal elements together; then duplicates are
t = list(s)
t.sort()
except TypeError:
- del t # move on to the next method
+ pass # move on to the next method
else:
assert n > 0
last = t[0]
lasti = lasti + 1
i = i + 1
return t[:lasti]
+ del t
# Brute force is all that's left.
u = []
break
result.append(line)
return result
+
+class Unbuffered:
+ """
+ A proxy class that wraps a file object, flushing after every write,
+ and delegating everything else to the wrapped object.
+ """
+ def __init__(self, file):
+ self.file = file
+ def write(self, arg):
+ self.file.write(arg)
+ self.file.flush()
+ def __getattr__(self, attr):
+ return getattr(self.file, attr)
def __init__(self, name, children=[]):
self.children = children
self.name = name
+ self.nocache = None
def __str__(self):
return self.name
def exists(self):
"""
lines = string.split(expect, '\n')[:-1]
- lines = map(lambda l: '[E BSPACN]'+l, lines)
+ lines = map(lambda l: '[E BSPACN ]'+l, lines)
withtags = string.join(lines, '\n') + '\n'
return foo, expect, withtags
+-blat.h
| +-stdlib.h
+-bar.h
+ +-[stdlib.h]
"""
lines = string.split(expect, '\n')[:-1]
- lines = map(lambda l: '[E BSPACN]'+l, lines)
+ lines = map(lambda l: '[E BSPACN ]'+l, lines)
withtags = string.join(lines, '\n') + '\n'
return blat_o, expect, withtags
pass
+# NOTE: If you add a new warning class, add it to the man page, too!
class CacheWriteErrorWarning(Warning):
pass
class DuplicateEnvironmentWarning(Warning):
pass
+class MisleadingKeywordsWarning(Warning):
+ pass
+
class MissingSConscriptWarning(Warning):
pass
class ReservedVariableWarning(Warning):
pass
-class MisleadingKeywordsWarning(Warning):
- pass
-
_warningAsException = 0
# The below is a list of 2-tuples. The first element is a class object.
__date__ = "__DATE__"
__developer__ = "__DEVELOPER__"
-
-import SCons.Memoize
import builtins
+try:
+ set
+except NameError:
+ # Pre-2.4 Python has no native set type
+ try:
+ # Python 2.2 and 2.3 can use the copy of the 2.[45] sets module
+ # that we grabbed.
+ import_as('_sets', 'sets')
+ except (ImportError, SyntaxError):
+ # Python 1.5 (ImportError, no __future_ module) and 2.1
+ # (SyntaxError, no generators in __future__) will blow up
+ # trying to import the 2.[45] sets module, so back off to a
+ # custom sets module that can be discarded easily when we
+ # stop supporting those versions.
+ import_as('_sets15', 'sets')
+ import __builtin__
+ import sets
+ __builtin__.set = sets.Set
+
try:
import subprocess
except ImportError:
--- /dev/null
+"""Classes to represent arbitrary sets (including sets of sets).
+
+This module implements sets using dictionaries whose values are
+ignored. The usual operations (union, intersection, deletion, etc.)
+are provided as both methods and operators.
+
+Important: sets are not sequences! While they support 'x in s',
+'len(s)', and 'for x in s', none of those operations are unique for
+sequences; for example, mappings support all three as well. The
+characteristic operation for sequences is subscripting with small
+integers: s[i], for i in range(len(s)). Sets don't support
+subscripting at all. Also, sequences allow multiple occurrences and
+their elements have a definite order; sets on the other hand don't
+record multiple occurrences and don't remember the order of element
+insertion (which is why they don't support s[i]).
+
+The following classes are provided:
+
+BaseSet -- All the operations common to both mutable and immutable
+ sets. This is an abstract class, not meant to be directly
+ instantiated.
+
+Set -- Mutable sets, subclass of BaseSet; not hashable.
+
+ImmutableSet -- Immutable sets, subclass of BaseSet; hashable.
+ An iterable argument is mandatory to create an ImmutableSet.
+
+_TemporarilyImmutableSet -- A wrapper around a Set, hashable,
+ giving the same hash value as the immutable set equivalent
+ would have. Do not use this class directly.
+
+Only hashable objects can be added to a Set. In particular, you cannot
+really add a Set as an element to another Set; if you try, what is
+actually added is an ImmutableSet built from it (it compares equal to
+the one you tried adding).
+
+When you ask if `x in y' where x is a Set and y is a Set or
+ImmutableSet, x is wrapped into a _TemporarilyImmutableSet z, and
+what's tested is actually `z in y'.
+
+"""
+
+# Code history:
+#
+# - Greg V. Wilson wrote the first version, using a different approach
+# to the mutable/immutable problem, and inheriting from dict.
+#
+# - Alex Martelli modified Greg's version to implement the current
+# Set/ImmutableSet approach, and make the data an attribute.
+#
+# - Guido van Rossum rewrote much of the code, made some API changes,
+# and cleaned up the docstrings.
+#
+# - Raymond Hettinger added a number of speedups and other
+# improvements.
+
+from __future__ import generators
+try:
+ from itertools import ifilter, ifilterfalse
+except ImportError:
+ # Code to make the module run under Py2.2
+ def ifilter(predicate, iterable):
+ if predicate is None:
+ def predicate(x):
+ return x
+ for x in iterable:
+ if predicate(x):
+ yield x
+ def ifilterfalse(predicate, iterable):
+ if predicate is None:
+ def predicate(x):
+ return x
+ for x in iterable:
+ if not predicate(x):
+ yield x
+ try:
+ True, False
+ except NameError:
+ True, False = (0==0, 0!=0)
+
+__all__ = ['BaseSet', 'Set', 'ImmutableSet']
+
+class BaseSet(object):
+ """Common base class for mutable and immutable sets."""
+
+ __slots__ = ['_data']
+
+ # Constructor
+
+ def __init__(self):
+ """This is an abstract class."""
+ # Don't call this from a concrete subclass!
+ if self.__class__ is BaseSet:
+ raise TypeError, ("BaseSet is an abstract class. "
+ "Use Set or ImmutableSet.")
+
+ # Standard protocols: __len__, __repr__, __str__, __iter__
+
+ def __len__(self):
+ """Return the number of elements of a set."""
+ return len(self._data)
+
+ def __repr__(self):
+ """Return string representation of a set.
+
+ This looks like 'Set([<list of elements>])'.
+ """
+ return self._repr()
+
+ # __str__ is the same as __repr__
+ __str__ = __repr__
+
+ def _repr(self, sorted=False):
+ elements = self._data.keys()
+ if sorted:
+ elements.sort()
+ return '%s(%r)' % (self.__class__.__name__, elements)
+
+ def __iter__(self):
+ """Return an iterator over the elements or a set.
+
+ This is the keys iterator for the underlying dict.
+ """
+ return self._data.iterkeys()
+
+ # Three-way comparison is not supported. However, because __eq__ is
+ # tried before __cmp__, if Set x == Set y, x.__eq__(y) returns True and
+ # then cmp(x, y) returns 0 (Python doesn't actually call __cmp__ in this
+ # case).
+
+ def __cmp__(self, other):
+ raise TypeError, "can't compare sets using cmp()"
+
+ # Equality comparisons using the underlying dicts. Mixed-type comparisons
+ # are allowed here, where Set == z for non-Set z always returns False,
+ # and Set != z always True. This allows expressions like "x in y" to
+ # give the expected result when y is a sequence of mixed types, not
+ # raising a pointless TypeError just because y contains a Set, or x is
+ # a Set and y contain's a non-set ("in" invokes only __eq__).
+ # Subtle: it would be nicer if __eq__ and __ne__ could return
+ # NotImplemented instead of True or False. Then the other comparand
+ # would get a chance to determine the result, and if the other comparand
+ # also returned NotImplemented then it would fall back to object address
+ # comparison (which would always return False for __eq__ and always
+ # True for __ne__). However, that doesn't work, because this type
+ # *also* implements __cmp__: if, e.g., __eq__ returns NotImplemented,
+ # Python tries __cmp__ next, and the __cmp__ here then raises TypeError.
+
+ def __eq__(self, other):
+ if isinstance(other, BaseSet):
+ return self._data == other._data
+ else:
+ return False
+
+ def __ne__(self, other):
+ if isinstance(other, BaseSet):
+ return self._data != other._data
+ else:
+ return True
+
+ # Copying operations
+
+ def copy(self):
+ """Return a shallow copy of a set."""
+ result = self.__class__()
+ result._data.update(self._data)
+ return result
+
+ __copy__ = copy # For the copy module
+
+ def __deepcopy__(self, memo):
+ """Return a deep copy of a set; used by copy module."""
+ # This pre-creates the result and inserts it in the memo
+ # early, in case the deep copy recurses into another reference
+ # to this same set. A set can't be an element of itself, but
+ # it can certainly contain an object that has a reference to
+ # itself.
+ from copy import deepcopy
+ result = self.__class__()
+ memo[id(self)] = result
+ data = result._data
+ value = True
+ for elt in self:
+ data[deepcopy(elt, memo)] = value
+ return result
+
+ # Standard set operations: union, intersection, both differences.
+ # Each has an operator version (e.g. __or__, invoked with |) and a
+ # method version (e.g. union).
+ # Subtle: Each pair requires distinct code so that the outcome is
+ # correct when the type of other isn't suitable. For example, if
+ # we did "union = __or__" instead, then Set().union(3) would return
+ # NotImplemented instead of raising TypeError (albeit that *why* it
+ # raises TypeError as-is is also a bit subtle).
+
+ def __or__(self, other):
+ """Return the union of two sets as a new set.
+
+ (I.e. all elements that are in either set.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.union(other)
+
+ def union(self, other):
+ """Return the union of two sets as a new set.
+
+ (I.e. all elements that are in either set.)
+ """
+ result = self.__class__(self)
+ result._update(other)
+ return result
+
+ def __and__(self, other):
+ """Return the intersection of two sets as a new set.
+
+ (I.e. all elements that are in both sets.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.intersection(other)
+
+ def intersection(self, other):
+ """Return the intersection of two sets as a new set.
+
+ (I.e. all elements that are in both sets.)
+ """
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if len(self) <= len(other):
+ little, big = self, other
+ else:
+ little, big = other, self
+ common = ifilter(big._data.has_key, little)
+ return self.__class__(common)
+
+ def __xor__(self, other):
+ """Return the symmetric difference of two sets as a new set.
+
+ (I.e. all elements that are in exactly one of the sets.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.symmetric_difference(other)
+
+ def symmetric_difference(self, other):
+ """Return the symmetric difference of two sets as a new set.
+
+ (I.e. all elements that are in exactly one of the sets.)
+ """
+ result = self.__class__()
+ data = result._data
+ value = True
+ selfdata = self._data
+ try:
+ otherdata = other._data
+ except AttributeError:
+ otherdata = Set(other)._data
+ for elt in ifilterfalse(otherdata.has_key, selfdata):
+ data[elt] = value
+ for elt in ifilterfalse(selfdata.has_key, otherdata):
+ data[elt] = value
+ return result
+
+ def __sub__(self, other):
+ """Return the difference of two sets as a new Set.
+
+ (I.e. all elements that are in this set and not in the other.)
+ """
+ if not isinstance(other, BaseSet):
+ return NotImplemented
+ return self.difference(other)
+
+ def difference(self, other):
+ """Return the difference of two sets as a new Set.
+
+ (I.e. all elements that are in this set and not in the other.)
+ """
+ result = self.__class__()
+ data = result._data
+ try:
+ otherdata = other._data
+ except AttributeError:
+ otherdata = Set(other)._data
+ value = True
+ for elt in ifilterfalse(otherdata.has_key, self):
+ data[elt] = value
+ return result
+
+ # Membership test
+
+ def __contains__(self, element):
+ """Report whether an element is a member of a set.
+
+ (Called in response to the expression `element in self'.)
+ """
+ try:
+ return element in self._data
+ except TypeError:
+ transform = getattr(element, "__as_temporarily_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ return transform() in self._data
+
+ # Subset and superset test
+
+ def issubset(self, other):
+ """Report whether another set contains this set."""
+ self._binary_sanity_check(other)
+ if len(self) > len(other): # Fast check for obvious cases
+ return False
+ for elt in ifilterfalse(other._data.has_key, self):
+ return False
+ return True
+
+ def issuperset(self, other):
+ """Report whether this set contains another set."""
+ self._binary_sanity_check(other)
+ if len(self) < len(other): # Fast check for obvious cases
+ return False
+ for elt in ifilterfalse(self._data.has_key, other):
+ return False
+ return True
+
+ # Inequality comparisons using the is-subset relation.
+ __le__ = issubset
+ __ge__ = issuperset
+
+ def __lt__(self, other):
+ self._binary_sanity_check(other)
+ return len(self) < len(other) and self.issubset(other)
+
+ def __gt__(self, other):
+ self._binary_sanity_check(other)
+ return len(self) > len(other) and self.issuperset(other)
+
+ # Assorted helpers
+
+ def _binary_sanity_check(self, other):
+ # Check that the other argument to a binary operation is also
+ # a set, raising a TypeError otherwise.
+ if not isinstance(other, BaseSet):
+ raise TypeError, "Binary operation only permitted between sets"
+
+ def _compute_hash(self):
+ # Calculate hash code for a set by xor'ing the hash codes of
+ # the elements. This ensures that the hash code does not depend
+ # on the order in which elements are added to the set. This is
+ # not called __hash__ because a BaseSet should not be hashable;
+ # only an ImmutableSet is hashable.
+ result = 0
+ for elt in self:
+ result ^= hash(elt)
+ return result
+
+ def _update(self, iterable):
+ # The main loop for update() and the subclass __init__() methods.
+ data = self._data
+
+ # Use the fast update() method when a dictionary is available.
+ if isinstance(iterable, BaseSet):
+ data.update(iterable._data)
+ return
+
+ value = True
+
+ if type(iterable) in (list, tuple, xrange):
+ # Optimized: we know that __iter__() and next() can't
+ # raise TypeError, so we can move 'try:' out of the loop.
+ it = iter(iterable)
+ while True:
+ try:
+ for element in it:
+ data[element] = value
+ return
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ data[transform()] = value
+ else:
+ # Safe: only catch TypeError where intended
+ for element in iterable:
+ try:
+ data[element] = value
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ data[transform()] = value
+
+
+class ImmutableSet(BaseSet):
+ """Immutable set class."""
+
+ __slots__ = ['_hashcode']
+
+ # BaseSet + hashing
+
+ def __init__(self, iterable=None):
+ """Construct an immutable set from an optional iterable."""
+ self._hashcode = None
+ self._data = {}
+ if iterable is not None:
+ self._update(iterable)
+
+ def __hash__(self):
+ if self._hashcode is None:
+ self._hashcode = self._compute_hash()
+ return self._hashcode
+
+ def __getstate__(self):
+ return self._data, self._hashcode
+
+ def __setstate__(self, state):
+ self._data, self._hashcode = state
+
+class Set(BaseSet):
+ """ Mutable set class."""
+
+ __slots__ = []
+
+ # BaseSet + operations requiring mutability; no hashing
+
+ def __init__(self, iterable=None):
+ """Construct a set from an optional iterable."""
+ self._data = {}
+ if iterable is not None:
+ self._update(iterable)
+
+ def __getstate__(self):
+ # getstate's results are ignored if it is not
+ return self._data,
+
+ def __setstate__(self, data):
+ self._data, = data
+
+ def __hash__(self):
+ """A Set cannot be hashed."""
+ # We inherit object.__hash__, so we must deny this explicitly
+ raise TypeError, "Can't hash a Set, only an ImmutableSet."
+
+ # In-place union, intersection, differences.
+ # Subtle: The xyz_update() functions deliberately return None,
+ # as do all mutating operations on built-in container types.
+ # The __xyz__ spellings have to return self, though.
+
+ def __ior__(self, other):
+ """Update a set with the union of itself and another."""
+ self._binary_sanity_check(other)
+ self._data.update(other._data)
+ return self
+
+ def union_update(self, other):
+ """Update a set with the union of itself and another."""
+ self._update(other)
+
+ def __iand__(self, other):
+ """Update a set with the intersection of itself and another."""
+ self._binary_sanity_check(other)
+ self._data = (self & other)._data
+ return self
+
+ def intersection_update(self, other):
+ """Update a set with the intersection of itself and another."""
+ if isinstance(other, BaseSet):
+ self &= other
+ else:
+ self._data = (self.intersection(other))._data
+
+ def __ixor__(self, other):
+ """Update a set with the symmetric difference of itself and another."""
+ self._binary_sanity_check(other)
+ self.symmetric_difference_update(other)
+ return self
+
+ def symmetric_difference_update(self, other):
+ """Update a set with the symmetric difference of itself and another."""
+ data = self._data
+ value = True
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if self is other:
+ self.clear()
+ for elt in other:
+ if elt in data:
+ del data[elt]
+ else:
+ data[elt] = value
+
+ def __isub__(self, other):
+ """Remove all elements of another set from this set."""
+ self._binary_sanity_check(other)
+ self.difference_update(other)
+ return self
+
+ def difference_update(self, other):
+ """Remove all elements of another set from this set."""
+ data = self._data
+ if not isinstance(other, BaseSet):
+ other = Set(other)
+ if self is other:
+ self.clear()
+ for elt in ifilter(data.has_key, other):
+ del data[elt]
+
+ # Python dict-like mass mutations: update, clear
+
+ def update(self, iterable):
+ """Add all values from an iterable (such as a list or file)."""
+ self._update(iterable)
+
+ def clear(self):
+ """Remove all elements from this set."""
+ self._data.clear()
+
+ # Single-element mutations: add, remove, discard
+
+ def add(self, element):
+ """Add an element to a set.
+
+ This has no effect if the element is already present.
+ """
+ try:
+ self._data[element] = True
+ except TypeError:
+ transform = getattr(element, "__as_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ self._data[transform()] = True
+
+ def remove(self, element):
+ """Remove an element from a set; it must be a member.
+
+ If the element is not a member, raise a KeyError.
+ """
+ try:
+ del self._data[element]
+ except TypeError:
+ transform = getattr(element, "__as_temporarily_immutable__", None)
+ if transform is None:
+ raise # re-raise the TypeError exception we caught
+ del self._data[transform()]
+
+ def discard(self, element):
+ """Remove an element from a set if it is a member.
+
+ If the element is not a member, do nothing.
+ """
+ try:
+ self.remove(element)
+ except KeyError:
+ pass
+
+ def pop(self):
+ """Remove and return an arbitrary set element."""
+ return self._data.popitem()[0]
+
+ def __as_immutable__(self):
+ # Return a copy of self as an immutable set
+ return ImmutableSet(self)
+
+ def __as_temporarily_immutable__(self):
+ # Return self wrapped in a temporarily immutable set
+ return _TemporarilyImmutableSet(self)
+
+
+class _TemporarilyImmutableSet(BaseSet):
+ # Wrap a mutable set as if it was temporarily immutable.
+ # This only supplies hashing and equality comparisons.
+
+ def __init__(self, set):
+ self._set = set
+ self._data = set._data # Needed by ImmutableSet.__eq__()
+
+ def __hash__(self):
+ return self._set._compute_hash()
--- /dev/null
+#
+# A Set class that works all the way back to Python 1.5. From:
+#
+# Python Cookbook: Yet another Set class for Python
+# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/106469
+# Goncalo Rodriques
+#
+# This is a pure Pythonic implementation of a set class. The syntax
+# and methods implemented are, for the most part, borrowed from
+# PEP 218 by Greg Wilson.
+#
+# Note that this class violates the formal definition of a set() by adding
+# a __getitem__() method so we can iterate over a set's elements under
+# Python 1.5 and 2.1, which don't support __iter__() and iterator types.
+#
+
+import string
+
+class Set:
+ """The set class. It can contain mutable objects."""
+
+ def __init__(self, seq = None):
+ """The constructor. It can take any object giving an iterator as an optional
+ argument to populate the new set."""
+ self.elems = []
+ if seq:
+ for elem in seq:
+ if elem not in self.elems:
+ self.elems.append(elem)
+
+ def __str__(self):
+ return "{%s}" % string.join(map(str, self.elems), ", ")
+
+
+ def copy(self):
+ """Shallow copy of a set object."""
+ return Set(self.elems)
+
+ def __contains__(self, elem):
+ return elem in self.elems
+
+ def __len__(self):
+ return len(self.elems)
+
+ def __getitem__(self, index):
+ # Added so that Python 1.5 can iterate over the elements.
+ # The cookbook recipe's author didn't like this because there
+ # really isn't any order in a set object, but this is necessary
+ # to make the class work well enough for our purposes.
+ return self.elems[index]
+
+ def items(self):
+ """Returns a list of the elements in the set."""
+ return self.elems
+
+ def add(self, elem):
+ """Add one element to the set."""
+ if elem not in self.elems:
+ self.elems.append(elem)
+
+ def remove(self, elem):
+ """Remove an element from the set. Return an error if elem is not in the set."""
+ try:
+ self.elems.remove(elem)
+ except ValueError:
+ raise LookupError, "Object %s is not a member of the set." % str(elem)
+
+ def discard(self, elem):
+ """Remove an element from the set. Do nothing if elem is not in the set."""
+ try:
+ self.elems.remove(elem)
+ except ValueError:
+ pass
+
+ def sort(self, func=cmp):
+ self.elems.sort(func)
+
+ #Define an iterator for a set.
+ def __iter__(self):
+ return iter(self.elems)
+
+ #The basic binary operations with sets.
+ def __or__(self, other):
+ """Union of two sets."""
+ ret = self.copy()
+ for elem in other.elems:
+ if elem not in ret:
+ ret.elems.append(elem)
+ return ret
+
+ def __sub__(self, other):
+ """Difference of two sets."""
+ ret = self.copy()
+ for elem in other.elems:
+ ret.discard(elem)
+ return ret
+
+ def __and__(self, other):
+ """Intersection of two sets."""
+ ret = Set()
+ for elem in self.elems:
+ if elem in other.elems:
+ ret.elems.append(elem)
+ return ret
+
+ def __add__(self, other):
+ """Symmetric difference of two sets."""
+ ret = Set()
+ temp = other.copy()
+ for elem in self.elems:
+ if elem in temp.elems:
+ temp.elems.remove(elem)
+ else:
+ ret.elems.append(elem)
+ #Add remaining elements.
+ for elem in temp.elems:
+ ret.elems.append(elem)
+ return ret
+
+ def __mul__(self, other):
+ """Cartesian product of two sets."""
+ ret = Set()
+ for elemself in self.elems:
+ x = map(lambda other, s=elemself: (s, other), other.elems)
+ ret.elems.extend(x)
+ return ret
+
+ #Some of the binary comparisons.
+ def __lt__(self, other):
+ """Returns 1 if the lhs set is contained but not equal to the rhs set."""
+ if len(self.elems) < len(other.elems):
+ temp = other.copy()
+ for elem in self.elems:
+ if elem in temp.elems:
+ temp.remove(elem)
+ else:
+ return 0
+ return len(temp.elems) == 0
+ else:
+ return 0
+
+ def __le__(self, other):
+ """Returns 1 if the lhs set is contained in the rhs set."""
+ if len(self.elems) <= len(other.elems):
+ ret = 1
+ for elem in self.elems:
+ if elem not in other.elems:
+ ret = 0
+ break
+ return ret
+ else:
+ return 0
+
+ def __eq__(self, other):
+ """Returns 1 if the sets are equal."""
+ if len(self.elems) != len(other.elems):
+ return 0
+ else:
+ return len(self - other) == 0
mswindows = (sys.platform == "win32")
import os
+import string
import types
import traceback
result.extend(bs_buf)
result.append('"')
- return ''.join(result)
+ return string.join(result, '')
try:
# Close pipe fds. Make sure we don't close the same
# fd more than once, or standard fds.
- if p2cread:
- os.close(p2cread)
- if c2pwrite and c2pwrite not in (p2cread,):
- os.close(c2pwrite)
- if errwrite and errwrite not in (p2cread, c2pwrite):
- os.close(errwrite)
+ for fd in set((p2cread, c2pwrite, errwrite))-set((0,1,2)):
+ if fd: os.close(fd)
# Close all other fds, if asked for
if close_fds:
exc_lines = traceback.format_exception(exc_type,
exc_value,
tb)
- exc_value.child_traceback = ''.join(exc_lines)
+ exc_value.child_traceback = string.join(exc_lines, '')
os.write(errpipe_write, pickle.dumps(exc_value))
# This exitcode won't be reported to applications, so it
read_set.append(self.stderr)
stderr = []
+ input_offset = 0
while read_set or write_set:
rlist, wlist, xlist = select.select(read_set, write_set, [])
# When select has indicated that the file is writable,
# we can write up to PIPE_BUF bytes without risk
# blocking. POSIX defines PIPE_BUF >= 512
- bytes_written = os.write(self.stdin.fileno(), input[:512])
- input = input[bytes_written:]
- if not input:
+ bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512))
+ input_offset = input_offset + bytes_written
+ if input_offset >= len(input):
self.stdin.close()
write_set.remove(self.stdin)
# All data exchanged. Translate lists into strings.
if stdout is not None:
- stdout = ''.join(stdout)
+ stdout = string.join(stdout, '')
if stderr is not None:
- stderr = ''.join(stderr)
+ stderr = string.join(stderr, '')
# Translate newlines, if requested. We cannot let the file
# object do the translation: It is based on stdio, which is
import os
import re
import string
-import sys
#
# First "subsystem" of regular expressions that we set up:
--- /dev/null
+#!/usr/bin/env python
+#
+# scons-post-install - SCons post install script for Windows
+#
+# A script for configuring "App Paths" registry key so that SCons could
+# be run from any directory the same way Python is.
+#
+
+#
+# SCons - a Software Constructor
+#
+# __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 sys
+
+scons_bat_path = os.path.join(sys.prefix, 'Scripts', 'scons.bat')
+
+app_paths_key = r'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\SCons.bat'
+
+def install():
+ if sys.platform == 'win32':
+ try:
+ import _winreg
+ except ImportError:
+ pass
+ else:
+ print 'Writing "App Paths" registry entry for %s' % scons_bat_path
+ _winreg.SetValue(
+ _winreg.HKEY_LOCAL_MACHINE,
+ app_paths_key,
+ _winreg.REG_SZ,
+ scons_bat_path)
+ print 'Done.'
+
+
+def remove():
+ if sys.platform == 'win32':
+ try:
+ import _winreg
+ except ImportError:
+ pass
+ else:
+ # print 'Remove "App Paths" registry entry'
+ _winreg.DeleteKey(_winreg.HKEY_LOCAL_MACHINE, app_paths_key)
+
+
+if len(sys.argv) > 1:
+ if sys.argv[1] == '-install':
+ install()
+ elif sys.argv[1] == '-remove':
+ remove()
+
+sys.exit(0)
@REM no way to set exit status of this script for 9x/Me\r
goto endscons\r
:WinNT\r
+set path=%path%;%~dp0\r
python -c "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'scons')] + sys.path; import SCons.Script; SCons.Script.main()" %*\r
if NOT "%COMSPEC%" == "%SystemRoot%\system32\cmd.exe" goto endscons\r
if errorlevel 9009 echo you do not have python in your PATH\r
[bdist_wininst]
title = SCons - a software construction tool
+install-script = scons-post-install.py
else:
self.data_files = []
+description = """Open Source next-generation build tool.
+Improved, cross-platform substitute for the classic Make
+utility. In short, SCons is an easier, more reliable
+and faster way to build software."""
+
+scripts = [
+ 'script/scons',
+ 'script/sconsign',
+ 'script/scons-time',
+
+ # We include scons.bat in the list of scripts, even on UNIX systems,
+ # because we provide an option to allow it be installed explicitly,
+ # for example if you're installing from UNIX on a share that's
+ # accessible to Windows and you want the scons.bat.
+ 'script/scons.bat',
+]
+
+if is_win32:
+ scripts = scripts + [
+ 'script/scons-post-install.py'
+ ]
+
arguments = {
'name' : "scons",
'version' : Version,
+ 'description' : description,
+ 'author' : 'Steven Knight',
+ 'author_email' : 'knight@baldmt.com',
+ 'url' : "http://www.scons.org/",
'packages' : ["SCons",
"SCons.compat",
"SCons.Node",
"SCons.Tool"],
'package_dir' : {'' : 'engine'},
'data_files' : [('man/man1', man_pages)],
- 'scripts' : ['script/scons',
- 'script/sconsign',
- 'script/scons-time',
- 'script/scons.bat'],
+ 'scripts' : scripts,
'cmdclass' : {'install' : install,
'install_lib' : install_lib,
'install_data' : install_data,
--- /dev/null
+#!/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__"
+
+"""
+Use pychecker to catch various Python coding errors.
+"""
+
+import os
+import os.path
+import string
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.skip_test('Not ready for clean pychecker output; skipping test.\n')
+
+try:
+ import pychecker
+except ImportError:
+ pychecker = test.where_is('pychecker')
+ if not pychecker:
+ test.skip_test("Could not find 'pychecker'; skipping test(s).\n")
+ program = pychecker
+ default_arguments = []
+else:
+ pychecker = os.path.join(os.path.split(pychecker.__file__)[0], 'checker.py')
+ program = sys.executable
+ default_arguments = [pychecker]
+
+try:
+ cwd = os.environ['SCONS_CWD']
+except KeyError:
+ src_engine = os.environ['SCONS_LIB_DIR']
+else:
+ src_engine = os.path.join(cwd, 'build', 'scons-src', 'src', 'engine')
+ if not os.path.exists(src_engine):
+ src_engine = os.path.join(cwd, 'src', 'engine')
+
+src_engine_ = os.path.join(src_engine, '')
+
+MANIFEST = os.path.join(src_engine, 'MANIFEST.in')
+files = string.split(open(MANIFEST).read())
+
+files = filter(lambda f: f[-3:] == '.py', files)
+
+ignore = [
+ 'SCons/compat/__init__.py',
+ 'SCons/compat/builtins.py',
+ 'SCons/compat/_subprocess.py',
+ 'SCons/Optik/__init__.py',
+ 'SCons/Optik/errors.py',
+ 'SCons/Optik/option.py',
+ 'SCons/Optik/option_parser.py',
+]
+
+u = {}
+for file in files:
+ u[file] = 1
+for file in ignore:
+ try:
+ del u[file]
+ except KeyError:
+ pass
+
+files = u.keys()
+
+files.sort()
+
+mismatches = []
+
+default_arguments.extend([
+ '--quiet',
+ '--limit=1000',
+])
+
+if sys.platform == 'win32':
+ default_arguments.extend([
+ '--blacklist', '"pywintypes,pywintypes.error"',
+ ])
+
+per_file_arguments = {
+ 'SCons/__init__.py' : [
+ '--varlist', '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"',
+ ],
+}
+
+pywintypes_warning = "warning: couldn't find real module for class pywintypes.error (module name: pywintypes)\n"
+
+os.environ['PYTHONPATH'] = src_engine
+
+for file in files:
+
+ file = os.path.join(src_engine, file)
+ args = default_arguments + per_file_arguments.get(file, []) + [file]
+
+ test.run(program=program, arguments=args, status=None, stderr=None)
+
+ stdout = test.stdout()
+ stdout = string.replace(stdout, src_engine_, '')
+
+ stderr = test.stderr()
+ stderr = string.replace(stderr, src_engine_, '')
+ stderr = string.replace(stderr, pywintypes_warning, '')
+
+ if test.status or stdout or stderr:
+ mismatches.append('\n')
+ mismatches.append(string.join([program] + args) + '\n')
+
+ mismatches.append('STDOUT =====================================\n')
+ mismatches.append(stdout)
+
+ if stderr:
+ mismatches.append('STDERR =====================================\n')
+ mismatches.append(stderr)
+
+if mismatches:
+ print string.join(mismatches[1:], '')
+ test.fail_test()
+
+test.pass_test()
'src',
search_list = [ '*.py' ],
remove_list = [
+ 'engine/SCons/compat/_sets.py',
+ 'engine/SCons/compat/_sets15.py',
'engine/SCons/compat/_subprocess.py',
'engine/SCons/Conftest.py',
'engine/SCons/dblite.py',
'configure-stamp',
'debian',
'dist',
+ 'gentoo',
+ 'engine/SCons/compat/_sets.py',
+ 'engine/SCons/compat/_sets15.py',
'engine/SCons/compat/_subprocess.py',
'engine/SCons/Conftest.py',
'engine/SCons/dblite.py',
CheckExpandedCopyright(
build_local,
remove_list = [
+ 'SCons/compat/_sets.py',
+ 'SCons/compat/_sets15.py',
'SCons/compat/_subprocess.py',
'SCons/Conftest.py',
'SCons/dblite.py',
'bin',
'config',
'debian',
+ 'gentoo',
'doc/design',
'doc/MANIFEST',
'doc/python10',
'doc/reference',
+ 'doc/developer/MANIFEST',
'doc/man/MANIFEST',
'doc/user/cons.pl',
'doc/user/MANIFEST',
'src/engine/MANIFEST.in',
'src/engine/MANIFEST-xml.in',
'src/engine/setup.cfg',
+ 'src/engine/SCons/compat/_sets.py',
+ 'src/engine/SCons/compat/_sets15.py',
'src/engine/SCons/compat/_subprocess.py',
'src/engine/SCons/Conftest.py',
'src/engine/SCons/dblite.py',
--- /dev/null
+#!/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 initializing a construction environment with just the
+g++ tool uses the CCFLAGS.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(tools = ['g++'])
+env.Object(target = 'test.obj', source = 'test.cxx')
+env.MergeFlags('+for_CCFLAGS -Wp,-for_CPPFLAGS')
+""")
+
+test.write('test.cxx', "test.cxx\n")
+
+expect = """\
+g++ -o test.obj -c +for_CCFLAGS -Wp,-for_CPPFLAGS test.cxx
+"""
+
+test.run(arguments = '-Q -n test.obj', stdout=expect)
+
+test.pass_test()
--- /dev/null
+#!/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 the NoCache environment method works.
+"""
+
+import TestSCons, os.path
+
+test = TestSCons.TestSCons()
+
+test.subdir('cache', 'alpha', 'beta')
+
+sconstruct = """
+import os
+CacheDir(r'%s')
+
+
+# This is bad form, but the easiest way to produce a test case.
+# Obviously, this could be cached if the inputs were passed in a
+# reasonable fashion.
+g = '%s'
+
+def ActionWithUndeclaredInputs(target,source,env):
+ open(target[0].abspath,'w').write(g)
+
+Command('foo_cached', [], ActionWithUndeclaredInputs)
+NoCache(Command('foo_notcached', [], ActionWithUndeclaredInputs))
+Command('bar_cached', [], ActionWithUndeclaredInputs)
+Command('bar_notcached', [], ActionWithUndeclaredInputs)
+NoCache('bar_notcached')
+
+# Make sure NoCache doesn't vomit when applied to a Dir
+NoCache(Command(Dir('aoeu'), [], Mkdir('$TARGET')))
+"""
+
+test.write('alpha/SConstruct', sconstruct % (test.workpath('cache'), 'alpha'))
+
+test.write('beta/SConstruct', sconstruct % (test.workpath('cache'), 'beta'))
+
+# First build, would populate the cache without NoCache
+test.run(chdir = 'alpha', arguments = '.')
+
+# Second build, without NoCache there would be a cache hit
+test.run(chdir = 'beta', arguments = '.')
+
+test.must_match(['beta','foo_cached'], 'alpha')
+test.must_match(['beta','foo_notcached'], 'beta')
+test.must_match(['beta','bar_cached'], 'alpha')
+test.must_match(['beta','bar_notcached'], 'beta')
+
+test.pass_test()
--- /dev/null
+#!/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 can install a file if its file name portion begins
+with a '#'. (A previous bug re-interpreted that as relative to
+the top-level SConstruct directory.)
+
+Thanks to Dave Weber for the test case.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir(['test'])
+
+test.write(['SConstruct'], """\
+env = Environment()
+i = env.Install("#/install", "#/test/#testfile.txt#");
+env.Default(i);
+""")
+
+test.write(['test', '#testfile.txt#'], "test/#testfile.txt#\n")
+
+test.run()
+
+test.must_match(['install', '#testfile.txt#'], "test/#testfile.txt#\n")
+
+test.pass_test()
JARCHDIR = dir)
bin = env.Java(dir, Dir('./'))
jar = env.Jar(File('c.jar', dir), bin)
-Default(bin, jar)
+
+# Make sure we handle class files with $ in them, such as typically
+# created for inner classes.
+env = env.Clone(JARCHDIR = '.')
+inner = env.Jar('inner.jar', 'Inner$$Class.class')
+
+Default(bin, jar, inner)
""" % locals())
test.write('a.java', """\
public class b {}
""")
+test.write('Inner$Class.class', "Inner$Class.class\n")
+
test.run(arguments = '.')
test.pass_test()
import os.path
import string
+try:
+ True, False
+except NameError:
+ True = (0 == 0)
+ False = (0 != 0)
+
import TestSCons
test = TestSCons.TestSCons()
test.run()
-check(['1', '0'])
+check([str(True), str(False)])
test.run(arguments='warnings=0 profile=no profile=true')
-check(['0', '1'])
+check([str(False), str(True)])
expect_stderr = """
scons: *** Error converting option: warnings
import os.path
import string
+try:
+ True, False
+except NameError:
+ True = (0 == 0)
+ False = (0 != 0)
+
import TestSCons
test = TestSCons.TestSCons()
""")
test.run()
-check(['1'])
-test.run(arguments='x11=no'); check(['0'])
-test.run(arguments='x11=0'); check(['0'])
-test.run(arguments=['x11=%s' % test.workpath()]); check([test.workpath()])
+check([str(True)])
+
+test.run(arguments='x11=no')
+check([str(False)])
+
+test.run(arguments='x11=0')
+check([str(False)])
+
+test.run(arguments=['x11=%s' % test.workpath()])
+check([test.workpath()])
expect_stderr = """
scons: *** Path does not exist for option x11: /non/existing/path/
import os.path
import string
+try:
+ True, False
+except NameError:
+ True = (0 == 0)
+ False = (0 != 0)
+
+str_True = str(True)
+str_False = str(False)
+
import TestSCons
test = TestSCons.TestSCons()
PackageOption, PathOption
list_of_libs = Split('x11 gl qt ical')
-qtdir = r'%(qtdir)s'
+qtdir = r'%(qtpath)s'
opts = Options(args=ARGUMENTS)
opts.AddOptions(
print env['profile']
Default(env.Alias('dummy', None))
-""" % {'qtdir': qtpath, 'libdirvar': libdirvar, 'libdir': libpath})
+""" % locals())
test.run(arguments='-h',
stdout = """\
scons: Reading SConscript files ...
-1
-0
+%(str_True)s
+%(str_False)s
scons: done reading SConscript files.
warnings: compilation with -Wall and similiar (yes|no)
default: 1
- actual: 1
+ actual: %(str_True)s
profile: create profiling informations (yes|no)
default: 0
- actual: 0
+ actual: %(str_False)s
debug: debug output and symbols (yes|no|full)
default: no
x11: use X11 installed here (yes = search some places)
( yes | no | /path/to/x11 )
default: yes
- actual: 1
+ actual: %(str_True)s
qtdir: where the root of Qt is installed ( /path/to/qtdir )
- default: %(qtdir)s
- actual: %(qtdir)s
+ default: %(qtpath)s
+ actual: %(qtpath)s
qt_libraries: where the Qt library is installed ( /path/to/qt_libraries )
- default: %(qtdir_lib)s
- actual: %(libdir)s
+ default: %(libdirvar)s
+ actual: %(libpath)s
Use scons -H for help about command-line options.
-""" % {'qtdir': qtpath, 'qtdir_lib' : os.path.join('$qtdir', 'lib'),
- 'libdirvar': libdirvar, 'libdir': libpath})
+""" % locals())
print "-L foo -L lib_dir -isysroot /tmp -arch ppc -arch i386"
""")
-test.write('SConstruct', """
-env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], CCFLAGS = '')
+test.write('SConstruct1', """
+env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [],
+ CCFLAGS = '-pipe -Wall')
env.ParseConfig([r'%(_python_)s', r"%(test_config1)s", "--libs --cflags"])
env.ParseConfig([r'%(_python_)s', r"%(test_config2)s", "--libs --cflags"])
print env['CPPPATH']
""" % locals())
test.write('SConstruct2', """
-env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], CCFLAGS = '',
+env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [],
+ CCFLAGS = '-pipe -Wall',
PYTHON = r'%(_python_)s')
env.ParseConfig(r"$PYTHON %(test_config1)s --libs --cflags")
env.ParseConfig(r"$PYTHON %(test_config2)s --libs --cflags")
""" % locals())
test.write('SConstruct3', """
-env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [], CCFLAGS = '',
+env = Environment(CPPPATH = [], LIBPATH = [], LIBS = [],
+ CCFLAGS = '-pipe -Wall',
PYTHON = r'%(_python_)s')
env.ParseConfig(r"$PYTHON %(test_config3)s --libs --cflags")
print env['CPPPATH']
print env['CCFLAGS']
""" % locals())
-good_stdout = test.wrap_stdout(read_str = """\
+good_stdout = """\
['/usr/include/fum', 'bar']
['/usr/fax', 'foo', 'lib_dir']
['xxx', 'abc']
-['-X', ('-arch', 'i386')]
-""", build_str = "scons: `.' is up to date.\n")
+['-pipe', '-Wall', '-X', ('-arch', 'i386')]
+"""
-stdout3 = test.wrap_stdout(read_str = """\
+stdout3 = """\
[]
['foo', 'lib_dir']
[]
-[('-isysroot', '/tmp'), ('-arch', 'ppc'), ('-arch', 'i386')]
-""", build_str = "scons: `.' is up to date.\n")
+['-pipe', '-Wall', ('-isysroot', '/tmp'), ('-arch', 'ppc'), ('-arch', 'i386')]
+"""
-test.run(arguments = ".", stdout = good_stdout)
+test.run(arguments = "-q -Q -f SConstruct1 .", stdout = good_stdout)
-test.run(arguments = "-f SConstruct2 .", stdout = good_stdout)
+test.run(arguments = "-q -Q -f SConstruct2 .", stdout = good_stdout)
-test.run(arguments = "-f SConstruct3 .", stdout = stdout3)
+test.run(arguments = "-q -Q -f SConstruct3 .", stdout = stdout3)
test.pass_test()
import os
import string
import sys
-def my_spawn(sh, escape, cmd, args, env):
- s = string.join(args + ['extra.txt'])
+def my_spawn1(sh, escape, cmd, args, env):
+ s = string.join(args + ['extra1.txt'])
if sys.platform in ['win32']:
s = '"' + s + '"'
os.system(s)
-env = Environment(SPAWN = my_spawn)
-env.Command('file.out', 'file.in', '%(_python_)s cat.py $TARGET $SOURCES')
-env = Environment()
+def my_spawn2(sh, escape, cmd, args, env):
+ s = string.join(args + ['extra2.txt'])
+ if sys.platform in ['win32']:
+ s = '"' + s + '"'
+ os.system(s)
+env = Environment(MY_SPAWN1 = my_spawn1,
+ MY_SPAWN2 = my_spawn2,
+ COMMAND = r'%(_python_)s cat.py $TARGET $SOURCES')
+env1 = env.Clone(SPAWN = my_spawn1)
+env1.Command('file1.out', 'file1.in', '$COMMAND')
+
+env2 = env.Clone(SPAWN = '$MY_SPAWN2')
+env2.Command('file2.out', 'file2.in', '$COMMAND')
+
+env3 = env.Clone(SPAWN = '${USE_TWO and MY_SPAWN2 or MY_SPAWN1}')
+env3.Command('file3.out', 'file3.in', '$COMMAND', USE_TWO=0)
+env3.Command('file4.out', 'file4.in', '$COMMAND', USE_TWO=1)
""" % locals())
-test.write('file.in', "file.in\n")
-test.write('extra.txt', "extra.txt\n")
+test.write('file1.in', "file1.in\n")
+test.write('file2.in', "file2.in\n")
+test.write('file3.in', "file3.in\n")
+test.write('file4.in', "file4.in\n")
+test.write('extra1.txt', "extra1.txt\n")
+test.write('extra2.txt', "extra2.txt\n")
test.run(arguments = '.')
-test.must_match('file.out', "file.in\nextra.txt\n")
+test.must_match('file1.out', "file1.in\nextra1.txt\n")
+test.must_match('file2.out', "file2.in\nextra2.txt\n")
+test.must_match('file3.out', "file3.in\nextra1.txt\n")
+test.must_match('file4.out', "file4.in\nextra2.txt\n")
test.pass_test()
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+"""
+Verify that the swig tool generates file names that we expect.
+"""
+
import os
import string
import sys
python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python"
_python_ = '"' + python + '"'
else:
- python = TestSCons.python
_python_ = TestSCons._python_
_exe = TestSCons._exe
_obj = TestSCons._obj
-# swig-python expects specific filenames.
-# the platform specific suffix won't necessarily work.
-if sys.platform == 'win32':
- _dll = '.dll'
-else:
- _dll = '.so'
-
test = TestSCons.TestSCons()
test.run(arguments = '.', stderr = None)
test.run(program = test.workpath('test1' + _exe), stdout = "test1.i\n")
-test.fail_test(not os.path.exists(test.workpath('test1_wrap.c')))
-test.fail_test(not os.path.exists(test.workpath('test1_wrap' + _obj)))
+test.must_exist(test.workpath('test1_wrap.c'))
+test.must_exist(test.workpath('test1_wrap' + _obj))
-test.fail_test(test.read('test2_wrap.c') != "test2.i\n")
+test.must_match('test2_wrap.c', "test2.i\n")
test.run(program = test.workpath('test3' + _exe), stdout = "test3.i\n")
-test.fail_test(not os.path.exists(test.workpath('test3_wrap.cc')))
-test.fail_test(not os.path.exists(test.workpath('test3_wrap' + _obj)))
-
-
-
-swig = test.where_is('swig')
-
-if swig:
-
- version = sys.version[:3] # see also sys.prefix documentation
-
- # handle testing on other platforms:
- ldmodule_prefix = '_'
-
- frameworks = ''
- platform_sys_prefix = sys.prefix
- if sys.platform == 'darwin':
- # OS X has a built-in Python but no static libpython
- # so you should link to it using apple's 'framework' scheme.
- # (see top of file for further explanation)
- frameworks = '-framework Python'
- platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version
-
- test.write("wrapper.py",
-"""import os
-import string
-import sys
-open('%s', 'wb').write("wrapper.py\\n")
-os.system(string.join(sys.argv[1:], " "))
-""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
-
- test.write('SConstruct', """
-foo = Environment(SWIGFLAGS='-python',
- CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
- LDMODULEPREFIX='%(ldmodule_prefix)s',
- LDMODULESUFFIX='%(_dll)s',
- FRAMEWORKSFLAGS='%(frameworks)s',
- )
-
-swig = foo.Dictionary('SWIG')
-bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
-foo.LoadableModule(target = 'foo', source = ['foo.c', 'foo.i'])
-bar.LoadableModule(target = 'bar', source = ['bar.c', 'bar.i'])
-""" % locals())
-
- test.write("foo.c", """\
-char *
-foo_string()
-{
- return "This is foo.c!";
-}
-""")
-
- test.write("foo.i", """\
-%module foo
-%{
-/* Put header files here (optional) */
-%}
-
-extern char *foo_string();
-""")
-
- test.write("bar.c", """\
-char *
-bar_string()
-{
- return "This is bar.c!";
-}
-""")
-
- test.write("bar.i", """\
-%module \t bar
-%{
-/* Put header files here (optional) */
-%}
-
-extern char *bar_string();
-""")
-
- test.run(arguments = ldmodule_prefix+'foo' + _dll)
-
- test.must_not_exist(test.workpath('wrapper.out'))
-
- test.run(program = python, stdin = """\
-import foo
-print foo.foo_string()
-""", stdout="""\
-This is foo.c!
-""")
-
- test.up_to_date(arguments = ldmodule_prefix+'foo' + _dll)
-
- test.run(arguments = ldmodule_prefix+'bar' + _dll)
-
- test.must_match('wrapper.out', "wrapper.py\n")
-
- test.run(program = python, stdin = """\
-import foo
-import bar
-print foo.foo_string()
-print bar.bar_string()
-""", stdout="""\
-This is foo.c!
-This is bar.c!
-""")
-
- test.up_to_date(arguments = '.')
-
- # Test that swig-generated modules are removed
- # The %module directive specifies the module name
- test.write("module.i", """\
-%module modulename
-""")
- test.write('SConstruct', """
-foo = Environment(SWIGFLAGS='-python',
- CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
- LDMODULEPREFIX='%(ldmodule_prefix)s',
- LDMODULESUFFIX='%(_dll)s',
- FRAMEWORKSFLAGS='%(frameworks)s',
- )
-
-foo.LoadableModule(target = 'modulename', source = ['module.i'])
-""" % locals())
- test.run()
- test.must_exist(test.workpath("modulename.py"))
- test.run(arguments = "-c")
- test.must_not_exist(test.workpath("modulename.py"))
-
- # Test that implicit dependencies are caught
-
- test.write("dependency.i", """\
-%module dependency
-""")
- test.write("dependent.i", """\
-%module dependent
-
-%include dependency.i
-""")
- test.write('SConstruct', """
-foo = Environment(SWIGFLAGS='-python',
- CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
- LDMODULEPREFIX='%(ldmodule_prefix)s',
- LDMODULESUFFIX='%(_dll)s',
- FRAMEWORKSFLAGS='%(frameworks)s',
- )
-
-swig = foo.Dictionary('SWIG')
-bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
-foo.CFile(target = 'dependent', source = ['dependent.i'])
-""" % locals())
-
- test.run()
- test.write("dependency.i", """%module dependency
-
-extern char *dependency_string();
-""")
- test.not_up_to_date(arguments = "dependent_wrap.c")
+test.must_exist(test.workpath('test3_wrap.cc'))
+test.must_exist(test.workpath('test3_wrap' + _obj))
test.pass_test()
--- /dev/null
+#!/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 SWIG implicit dependencies are caught.
+"""
+
+import sys
+
+import TestSCons
+
+if sys.platform =='darwin':
+ # change to make it work with stock OS X python framework
+ # we can't link to static libpython because there isn't one on OS X
+ # so we link to a framework version. However, testing must also
+ # use the same version, or else you get interpreter errors.
+ python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python"
+ _python_ = '"' + python + '"'
+else:
+ python = TestSCons.python
+ _python_ = TestSCons._python_
+
+# swig-python expects specific filenames.
+# the platform specific suffix won't necessarily work.
+if sys.platform == 'win32':
+ _dll = '.dll'
+else:
+ _dll = '.so'
+
+test = TestSCons.TestSCons()
+
+swig = test.where_is('swig')
+
+if not swig:
+ test.skip_test('Can not find installed "swig", skipping test.\n')
+
+
+
+version = sys.version[:3] # see also sys.prefix documentation
+
+# handle testing on other platforms:
+ldmodule_prefix = '_'
+
+frameworks = ''
+platform_sys_prefix = sys.prefix
+if sys.platform == 'darwin':
+ # OS X has a built-in Python but no static libpython
+ # so you should link to it using apple's 'framework' scheme.
+ # (see top of file for further explanation)
+ frameworks = '-framework Python'
+ platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version
+
+test.write("dependency.i", """\
+%module dependency
+""")
+
+test.write("dependent.i", """\
+%module dependent
+
+%include dependency.i
+""")
+
+test.write('SConstruct', """
+foo = Environment(SWIGFLAGS='-python',
+ CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
+ LDMODULEPREFIX='%(ldmodule_prefix)s',
+ LDMODULESUFFIX='%(_dll)s',
+ FRAMEWORKSFLAGS='%(frameworks)s',
+ )
+
+swig = foo.Dictionary('SWIG')
+bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
+foo.CFile(target = 'dependent', source = ['dependent.i'])
+""" % locals())
+
+test.run()
+
+test.write("dependency.i", """%module dependency
+
+extern char *dependency_string();
+""")
+
+test.not_up_to_date(arguments = "dependent_wrap.c")
+
+
+
+test.pass_test()
--- /dev/null
+#!/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 SWIG behavior with a live, installed SWIG.
+"""
+
+import string
+import sys
+
+import TestSCons
+
+if sys.platform =='darwin':
+ # change to make it work with stock OS X python framework
+ # we can't link to static libpython because there isn't one on OS X
+ # so we link to a framework version. However, testing must also
+ # use the same version, or else you get interpreter errors.
+ python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python"
+ _python_ = '"' + python + '"'
+else:
+ python = TestSCons.python
+ _python_ = TestSCons._python_
+
+# swig-python expects specific filenames.
+# the platform specific suffix won't necessarily work.
+if sys.platform == 'win32':
+ _dll = '.dll'
+else:
+ _dll = '.so'
+
+test = TestSCons.TestSCons()
+
+swig = test.where_is('swig')
+
+if not swig:
+ test.skip_test('Can not find installed "swig", skipping test.\n')
+
+
+
+version = sys.version[:3] # see also sys.prefix documentation
+
+# handle testing on other platforms:
+ldmodule_prefix = '_'
+
+frameworks = ''
+platform_sys_prefix = sys.prefix
+if sys.platform == 'darwin':
+ # OS X has a built-in Python but no static libpython
+ # so you should link to it using apple's 'framework' scheme.
+ # (see top of file for further explanation)
+ frameworks = '-framework Python'
+ platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version
+
+test.write("wrapper.py",
+"""import os
+import string
+import sys
+open('%s', 'wb').write("wrapper.py\\n")
+os.system(string.join(sys.argv[1:], " "))
+""" % string.replace(test.workpath('wrapper.out'), '\\', '\\\\'))
+
+test.write('SConstruct', """
+foo = Environment(SWIGFLAGS='-python',
+ CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
+ LDMODULEPREFIX='%(ldmodule_prefix)s',
+ LDMODULESUFFIX='%(_dll)s',
+ FRAMEWORKSFLAGS='%(frameworks)s',
+ )
+
+swig = foo.Dictionary('SWIG')
+bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
+foo.LoadableModule(target = 'foo', source = ['foo.c', 'foo.i'])
+bar.LoadableModule(target = 'bar', source = ['bar.c', 'bar.i'])
+""" % locals())
+
+test.write("foo.c", """\
+char *
+foo_string()
+{
+ return "This is foo.c!";
+}
+""")
+
+test.write("foo.i", """\
+%module foo
+%{
+/* Put header files here (optional) */
+%}
+
+extern char *foo_string();
+""")
+
+test.write("bar.c", """\
+char *
+bar_string()
+{
+ return "This is bar.c!";
+}
+""")
+
+test.write("bar.i", """\
+%module \t bar
+%{
+/* Put header files here (optional) */
+%}
+
+extern char *bar_string();
+""")
+
+test.run(arguments = ldmodule_prefix+'foo' + _dll)
+
+test.must_not_exist(test.workpath('wrapper.out'))
+
+test.run(program = python, stdin = """\
+import foo
+print foo.foo_string()
+""", stdout="""\
+This is foo.c!
+""")
+
+test.up_to_date(arguments = ldmodule_prefix+'foo' + _dll)
+
+test.run(arguments = ldmodule_prefix+'bar' + _dll)
+
+test.must_match('wrapper.out', "wrapper.py\n")
+
+test.run(program = python, stdin = """\
+import foo
+import bar
+print foo.foo_string()
+print bar.bar_string()
+""", stdout="""\
+This is foo.c!
+This is bar.c!
+""")
+
+test.up_to_date(arguments = '.')
+
+
+
+test.pass_test()
--- /dev/null
+#!/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 SCons realizes the -noproxy option means no .py file will
+be created.
+"""
+
+import sys
+
+import TestSCons
+
+if sys.platform =='darwin':
+ # change to make it work with stock OS X python framework
+ # we can't link to static libpython because there isn't one on OS X
+ # so we link to a framework version. However, testing must also
+ # use the same version, or else you get interpreter errors.
+ python = "/System/Library/Frameworks/Python.framework/Versions/Current/bin/python"
+ _python_ = '"' + python + '"'
+else:
+ python = TestSCons.python
+ _python_ = TestSCons._python_
+
+# swig-python expects specific filenames.
+# the platform specific suffix won't necessarily work.
+if sys.platform == 'win32':
+ _dll = '.dll'
+else:
+ _dll = '.so'
+
+test = TestSCons.TestSCons()
+
+swig = test.where_is('swig')
+
+if not swig:
+ test.skip_test('Can not find installed "swig", skipping test.\n')
+
+
+
+version = sys.version[:3] # see also sys.prefix documentation
+
+# handle testing on other platforms:
+ldmodule_prefix = '_'
+
+frameworks = ''
+platform_sys_prefix = sys.prefix
+if sys.platform == 'darwin':
+ # OS X has a built-in Python but no static libpython
+ # so you should link to it using apple's 'framework' scheme.
+ # (see top of file for further explanation)
+ frameworks = '-framework Python'
+ platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version
+
+test.write("dependency.i", """\
+%module dependency
+""")
+
+test.write("dependent.i", """\
+%module dependent
+
+%include dependency.i
+""")
+
+test.write('SConstruct', """
+foo = Environment(SWIGFLAGS=['-python', '-noproxy'],
+ CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
+ LDMODULEPREFIX='%(ldmodule_prefix)s',
+ LDMODULESUFFIX='%(_dll)s',
+ FRAMEWORKSFLAGS='%(frameworks)s',
+ )
+
+swig = foo.Dictionary('SWIG')
+bar = foo.Clone(SWIG = r'%(_python_)s wrapper.py ' + swig)
+foo.CFile(target = 'dependent', source = ['dependent.i'])
+""" % locals())
+
+test.run(arguments = '.')
+
+# If we mistakenly depend on the .py file that SWIG didn't create
+# (suppressed by the -noproxy option) then the build won't be up-to-date.
+test.up_to_date(arguments = '.')
+
+
+
+test.pass_test()
--- /dev/null
+#!/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 swig-generated modules are removed.
+The %module directive specifies the module name.
+"""
+
+import sys
+
+import TestSCons
+
+# swig-python expects specific filenames.
+# the platform specific suffix won't necessarily work.
+if sys.platform == 'win32':
+ _dll = '.dll'
+else:
+ _dll = '.so'
+
+test = TestSCons.TestSCons()
+
+swig = test.where_is('swig')
+
+if not swig:
+ test.skip_test('Can not find installed "swig", skipping test.\n')
+
+
+
+version = sys.version[:3] # see also sys.prefix documentation
+
+# handle testing on other platforms:
+ldmodule_prefix = '_'
+
+frameworks = ''
+platform_sys_prefix = sys.prefix
+if sys.platform == 'darwin':
+ # OS X has a built-in Python but no static libpython
+ # so you should link to it using apple's 'framework' scheme.
+ # (see top of file for further explanation)
+ frameworks = '-framework Python'
+ platform_sys_prefix = '/System/Library/Frameworks/Python.framework/Versions/%s/' % version
+
+
+test.write("module.i", """\
+%module modulename
+""")
+
+test.write('SConstruct', """
+foo = Environment(SWIGFLAGS='-python',
+ CPPPATH='%(platform_sys_prefix)s/include/python%(version)s/',
+ LDMODULEPREFIX='%(ldmodule_prefix)s',
+ LDMODULESUFFIX='%(_dll)s',
+ FRAMEWORKSFLAGS='%(frameworks)s',
+ )
+
+foo.LoadableModule(target = 'modulename', source = ['module.i'])
+""" % locals())
+
+test.run()
+
+test.must_exist(test.workpath("modulename.py"))
+
+test.run(arguments = "-c")
+
+test.must_not_exist(test.workpath("modulename.py"))
+
+
+
+test.pass_test()
--- /dev/null
+#!/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_contents()
+ includes = include1_re.findall(contents)
+ return includes
+
+def kfile_scan2(node, env, scanpaths, arg=None):
+ contents = node.get_contents()
+ includes = include2_re.findall(contents)
+ return includes
+
+def kfile_scan3(node, env, scanpaths, arg=None):
+ contents = node.get_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()
SCons.Script.SConscriptSettableOptions
SCons.Script.keep_going_on_error
-SCons.Script.print_dtree
+#SCons.Script.print_dtree
SCons.Script.print_explanations
SCons.Script.print_includes
SCons.Script.print_objects
SCons.Script.print_time
-SCons.Script.print_tree
+#SCons.Script.print_tree
SCons.Script.memory_stats
SCons.Script.ignore_errors
#SCons.Script.sconscript_time
""")
stree = """
-[E B C ]+-foo.xxx
-[E B C ] +-foo.ooo
-[E ] | +-foo.c
-[E ] | +-foo.h
-[E ] | +-bar.h
-[E B C ] +-bar.ooo
-[E ] +-bar.c
-[E ] +-bar.h
-[E ] +-foo.h
+[E B C ]+-foo.xxx
+[E B C ] +-foo.ooo
+[E ] | +-foo.c
+[E ] | +-foo.h
+[E ] | +-bar.h
+[E B C ] +-bar.ooo
+[E ] +-bar.c
+[E ] +-bar.h
+[E ] +-foo.h
"""
test.run(arguments = "--debug=stree foo.xxx")
test.fail_test(string.find(test.stdout(), stree) == -1)
stree2 = """
- E = exists
- R = exists in repository only
- b = implicit builder
- B = explicit builder
- S = side effect
- P = precious
- A = always build
- C = current
- N = no clean
-
-[ B ]+-foo.xxx
-[ B ] +-foo.ooo
-[E ] | +-foo.c
-[E ] | +-foo.h
-[E ] | +-bar.h
-[ B ] +-bar.ooo
-[E ] +-bar.c
-[E ] +-bar.h
-[E ] +-foo.h
+ E = exists
+ R = exists in repository only
+ b = implicit builder
+ B = explicit builder
+ S = side effect
+ P = precious
+ A = always build
+ C = current
+ N = no clean
+ H = no cache
+
+[ B ]+-foo.xxx
+[ B ] +-foo.ooo
+[E ] | +-foo.c
+[E ] | +-foo.h
+[E ] | +-bar.h
+[ B ] +-bar.ooo
+[E ] +-bar.c
+[E ] +-bar.h
+[E ] +-foo.h
"""
test.run(arguments = '-c foo.xxx')
--- /dev/null
+#!/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 the --srcdir option works to fetch things from a repository.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('repository', 'work1')
+
+repository = test.workpath('repository')
+
+test.write(['repository', 'SConstruct'], """\
+env = Environment()
+env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE'))
+""")
+
+test.write(['repository', 'file.in'], "repository/file.in\n")
+
+opts = '--repository ' + repository
+
+# Make the entire repository non-writable, so we'll detect
+# if we try to write into it accidentally.
+test.writable('repository', 0)
+
+test.run(chdir = 'work1', options = opts, arguments = '.')
+
+test.must_match(['work1', 'file.out'], "repository/file.in\n")
+
+test.up_to_date(chdir = 'work1', options = opts, arguments = '.')
+
+
+
+#
+test.pass_test()
--- /dev/null
+#!/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 the --srcdir option works to fetch things from a repository.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('repository', 'work1')
+
+repository = test.workpath('repository')
+
+test.write(['repository', 'SConstruct'], r"""
+env = Environment()
+env.Command('file.out', 'file.in', Copy('$TARGET', '$SOURCE'))
+""")
+
+test.write(['repository', 'file.in'], "repository/file.in\n")
+
+opts = '--srcdir ' + repository
+
+# Make the entire repository non-writable, so we'll detect
+# if we try to write into it accidentally.
+test.writable('repository', 0)
+
+test.run(chdir = 'work1', options = opts, arguments = '.')
+
+test.must_match(['work1', 'file.out'], "repository/file.in\n")
+
+test.up_to_date(chdir = 'work1', options = opts, arguments = '.')
+
+
+
+#
+test.pass_test()
--- /dev/null
+#!/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 that the --tree=all option prints a tree representation of the
+complete dependencies of a target.
+"""
+
+import TestSCons
+import sys
+import string
+import re
+import time
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx')
+env.Program('Foo', Split('Foo.c Bar.c'))
+""")
+
+# N.B.: We use upper-case file names (Foo* and Bar*) so that the sorting
+# order with our upper-case SConstruct file is the same on case-sensitive
+# (UNIX/Linux) and case-insensitive (Windows) systems.
+
+test.write('Foo.c', r"""
+#include <stdio.h>
+#include <stdlib.h>
+#include "Foo.h"
+int main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("f1.c\n");
+ exit (0);
+}
+""")
+
+test.write('Bar.c', """
+#include "Bar.h"
+""")
+
+test.write('Foo.h', """
+#ifndef FOO_H
+#define FOO_H
+#include "Bar.h"
+#endif
+""")
+
+test.write('Bar.h', """
+#ifndef BAR_H
+#define BAR_H
+#include "Foo.h"
+#endif
+""")
+
+tree1 = """
++-Foo.xxx
+ +-Foo.ooo
+ | +-Foo.c
+ | +-Foo.h
+ | +-Bar.h
+ +-Bar.ooo
+ +-Bar.c
+ +-Bar.h
+ +-Foo.h
+"""
+
+test.run(arguments = "--tree=all Foo.xxx")
+test.fail_test(string.find(test.stdout(), tree1) == -1)
+
+tree2 = """
++-.
+ +-Bar.c
+ +-Bar.ooo
+ | +-Bar.c
+ | +-Bar.h
+ | +-Foo.h
+ +-Foo.c
+ +-Foo.ooo
+ | +-Foo.c
+ | +-Foo.h
+ | +-Bar.h
+ +-Foo.xxx
+ | +-Foo.ooo
+ | | +-Foo.c
+ | | +-Foo.h
+ | | +-Bar.h
+ | +-Bar.ooo
+ | +-Bar.c
+ | +-Bar.h
+ | +-Foo.h
+ +-SConstruct
+"""
+
+test.run(arguments = "--tree=all .")
+test.fail_test(string.find(test.stdout(), tree2) == -1)
+
+tree3 = """
++-.
+ +-Bar.c
+ +-Bar.ooo
+ | +-[Bar.c]
+ | +-Bar.h
+ | +-Foo.h
+ +-Foo.c
+ +-Foo.ooo
+ | +-[Foo.c]
+ | +-[Foo.h]
+ | +-[Bar.h]
+ +-Foo.xxx
+ | +-[Foo.ooo]
+ | +-[Bar.ooo]
+ +-SConstruct
+"""
+
+test.run(arguments = "--tree=all,prune .")
+test.fail_test(string.find(test.stdout(), tree3) == -1)
+
+test.run(arguments = "--tree=prune .")
+test.fail_test(string.find(test.stdout(), tree3) == -1)
+
+tree4 = """
+ E = exists
+ R = exists in repository only
+ b = implicit builder
+ B = explicit builder
+ S = side effect
+ P = precious
+ A = always build
+ C = current
+ N = no clean
+ H = no cache
+
+[ B ]+-Foo.xxx
+[ B ] +-Foo.ooo
+[E ] | +-Foo.c
+[E ] | +-Foo.h
+[E ] | +-Bar.h
+[ B ] +-Bar.ooo
+[E ] +-Bar.c
+[E ] +-Bar.h
+[E ] +-Foo.h
+"""
+
+test.run(arguments = '-c Foo.xxx')
+
+test.run(arguments = "--no-exec --tree=all,status Foo.xxx")
+test.fail_test(string.find(test.stdout(), tree4) == -1)
+
+test.run(arguments = "--no-exec --tree=status Foo.xxx")
+test.fail_test(string.find(test.stdout(), tree4) == -1)
+
+# Make sure we print the debug stuff even if there's a build failure.
+test.write('Bar.h', """
+#ifndef BAR_H
+#define BAR_H
+#include "Foo.h"
+#endif
+THIS SHOULD CAUSE A BUILD FAILURE
+""")
+
+test.run(arguments = "--tree=all Foo.xxx",
+ status = 2,
+ stderr = None)
+test.fail_test(string.find(test.stdout(), tree1) == -1)
+
+test.pass_test()
--- /dev/null
+#!/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 that the --debug=dtree option correctly prints just the explicit
+dependencies (sources or Depends()) of a target.
+"""
+
+import TestSCons
+import sys
+import string
+import re
+import time
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+env = Environment(OBJSUFFIX = '.ooo', PROGSUFFIX = '.xxx')
+env.Program('foo', Split('foo.c bar.c'))
+""")
+
+test.write('foo.c', r"""
+#include <stdio.h>
+#include <stdlib.h>
+#include "foo.h"
+int main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+ printf("f1.c\n");
+ exit (0);
+}
+""")
+
+test.write('bar.c', """
+#include "bar.h"
+""")
+
+test.write('foo.h', """
+#ifndef FOO_H
+#define FOO_H
+#include "bar.h"
+#endif
+""")
+
+test.write('bar.h', """
+#ifndef BAR_H
+#define BAR_H
+#include "foo.h"
+#endif
+""")
+
+dtree1 = """
++-foo.xxx
+ +-foo.ooo
+ +-bar.ooo
+"""
+
+test.run(arguments = "--tree=derived foo.xxx")
+test.fail_test(string.find(test.stdout(), dtree1) == -1)
+
+dtree2 = """
++-.
+ +-bar.ooo
+ +-foo.ooo
+ +-foo.xxx
+ +-foo.ooo
+ +-bar.ooo
+"""
+
+test.run(arguments = "--tree=derived .")
+test.fail_test(string.find(test.stdout(), dtree2) == -1)
+
+dtree3 = """
++-.
+ +-bar.ooo
+ +-foo.ooo
+ +-foo.xxx
+ +-[foo.ooo]
+ +-[bar.ooo]
+"""
+
+test.run(arguments = "--tree=derived,prune .")
+test.fail_test(string.find(test.stdout(), dtree3) == -1)
+
+dtree4 = """
+ E = exists
+ R = exists in repository only
+ b = implicit builder
+ B = explicit builder
+ S = side effect
+ P = precious
+ A = always build
+ C = current
+ N = no clean
+ H = no cache
+
+[ B ]+-foo.xxx
+[ B ] +-foo.ooo
+[ B ] +-bar.ooo
+"""
+
+test.run(arguments = '-c foo.xxx')
+
+test.run(arguments = "--no-exec --tree=derived,status foo.xxx")
+test.fail_test(string.find(test.stdout(), dtree4) == -1)
+
+# Make sure we print the debug stuff even if there's a build failure.
+test.write('bar.h', """
+#ifndef BAR_H
+#define BAR_H
+#include "foo.h"
+#endif
+THIS SHOULD CAUSE A BUILD FAILURE
+""")
+
+test.run(arguments = "--tree=derived foo.xxx",
+ status = 2,
+ stderr = None)
+test.fail_test(string.find(test.stdout(), dtree1) == -1)
+
+test.pass_test()
--- /dev/null
+#!/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
+
+"""
+Verify basic functionality of the site_scons dir and
+site_scons/site_init.py file:
+Make sure the site_scons/site_init.py file gets loaded,
+and make sure a tool can be loaded from the site_scons/site_tools subdir,
+even when executing out of a subdirectory.
+"""
+
+test = TestSCons.TestSCons()
+
+test.subdir('site_scons', ['site_scons', 'site_tools'])
+
+test.write(['site_scons', 'site_init.py'], """
+from SCons.Script import *
+print "Hi there, I am in site_scons/site_init.py!"
+""")
+
+test.write(['site_scons', 'site_tools', 'mytool.py'], """
+import SCons.Tool
+def generate(env):
+ env['MYTOOL']='mytool'
+def exists(env):
+ return 1
+""")
+
+
+test.write('SConstruct', """
+e=Environment(tools=['default', 'mytool'])
+print e.subst('My site tool is $MYTOOL')
+""")
+
+test.run(arguments = '-Q .',
+ stdout = """Hi there, I am in site_scons/site_init.py!
+My site tool is mytool
+scons: `.' is up to date.\n""")
+
+
+
+test.pass_test()
+
+# end of file
--- /dev/null
+#!/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 use of the --no-site-dir option:
+the site_scons/site_init.py script should NOT be loaded.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('site_scons', ['site_scons', 'site_tools'])
+
+test.write(['site_scons', 'site_init.py'], """
+from SCons.Script import *
+print "Hi there, I am in site_scons/site_init.py!"
+""")
+
+test.write(['site_scons', 'site_tools', 'mytool.py'], """
+import SCons.Tool
+def generate(env):
+ env['MYTOOL']='mytool'
+def exists(env):
+ return 1
+""")
+
+test.write(['site_scons', 'site_tools', 'm4.py'], """
+import SCons.Tool
+def generate(env):
+ env['M4']='my_m4'
+ env['M4_MINE']=1
+def exists(env):
+ return 1
+""")
+
+test.write('SConstruct', """
+e=Environment()
+""")
+
+test.run(arguments = '-Q --no-site-dir .',
+ stdout = "scons: `.' is up to date.\n")
+
+# With --no-site-dir, shouldn't override default m4 tool
+
+test.write('SConstruct', """
+e=Environment()
+print e.subst('no site: M4 is $M4, M4_MINE is $M4_MINE')
+""")
+
+test.run(arguments = '-Q --no-site-dir .',
+stdout = """no site: M4 is m4, M4_MINE is
+scons: `.' is up to date.\n""")
+
+
+
+test.pass_test()
+
+# end of file
--- /dev/null
+#!/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 --site-dir= with a nonexistent directory
+gives an error and nonzero status.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', "\n")
+
+test.run(arguments = '-Q --site-dir=whatever .',
+ stderr = r".*site dir .*whatever not found.*",
+ status = 2,
+ match = TestSCons.match_re_dotall)
+
+
+
+test.pass_test()
+
+# end of file
--- /dev/null
+#!/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 that a tool module in site_tools overrides base tool.
+
+Use 'm4' as test tool since it's likely to be found,
+and not commonly overridden by platform-specific stuff the way cc is.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('site_scons', ['site_scons', 'site_tools'])
+
+test.write(['site_scons', 'site_tools', 'm4.py'], """
+import SCons.Tool
+def generate(env):
+ env['M4']='my_m4'
+ env['M4_MINE']=1
+def exists(env):
+ return 1
+""")
+
+test.write('SConstruct', """
+e=Environment()
+print e.subst('M4 is $M4, M4_MINE is $M4_MINE')
+""")
+test.run(arguments = '-Q .',
+ stdout = """M4 is my_m4, M4_MINE is 1
+scons: `.' is up to date.\n""")
+
+
+
+test.pass_test()
+
+# end of file
--- /dev/null
+#!/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 --site-dir=otherdir loads the site_init.py script
+from the other dir;
+the usual site_scons/site_init.py should NOT be loaded.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('site_scons', ['site_scons', 'site_tools'])
+
+test.write(['site_scons', 'site_init.py'], """
+from SCons.Script import *
+print "Hi there, I am in site_scons/site_init.py!"
+""")
+
+test.write(['site_scons', 'site_tools', 'mytool.py'], """
+import SCons.Tool
+def generate(env):
+ env['MYTOOL']='mytool'
+def exists(env):
+ return 1
+""")
+
+
+
+test.subdir('alt_site', ['alt_site', 'site_tools'])
+
+test.write(['alt_site', 'site_init.py'], """
+from SCons.Script import *
+print "Hi there, I am in alt_site/site_init.py!"
+""")
+
+test.write('SConstruct', """
+e=Environment()
+""")
+
+test.run(arguments = '-Q --site-dir=alt_site .',
+ stdout = """Hi there, I am in alt_site/site_init.py!
+scons: `.' is up to date.\n""")
+
+
+
+test.pass_test()
+
+# end of file
--- /dev/null
+#!/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 toolpath works with BuildDir() for an SConscript.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('subdir', ['subdir', 'src'], ['subdir', 'src', 'tools'])
+
+test.write('SConstruct', """\
+BuildDir('build', 'subdir', duplicate=0)
+SConscript('build/SConscript')
+""")
+
+test.write(['subdir', 'SConscript'], """\
+env = Environment(tools = ['MyBuilder'], toolpath = ['src/tools'])
+env.MyCopy('src/file.out', 'src/file.in')
+""")
+
+test.write(['subdir', 'src', 'file.in'], "subdir/src/file.in\n")
+
+test.write(['subdir', 'src', 'tools', 'MyBuilder.py'], """\
+from SCons.Script import Builder
+def generate(env):
+ def my_copy(target, source, env):
+ content = open(str(source[0]), 'rb').read()
+ open(str(target[0]), 'wb').write(content)
+ env['BUILDERS']['MyCopy'] = Builder(action = my_copy)
+
+def exists(env):
+ return 1
+""")
+
+test.run()
+
+test.must_match(['build', 'src', 'file.out'], "subdir/src/file.in\n")
+
+# We should look for the underlying tool in both the build/src/tools
+# (which doesn't exist) and subdir/src/tools (which still does). If we
+# don't, the following would fail because the execution directory is
+# now relative to the created BuildDir.
+test.run()
+
+test.pass_test()