summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2007-01-07 07:23:05 +0000
committerSteven Knight <knight@baldmt.com>2007-01-07 07:23:05 +0000
commited6878a4af79fb56fcf5564cb3aca9cbbeac4a69 (patch)
tree05e6aee9b46b530443e01bbb762b68775a6bb0ff
parent761c99b86da4f807e30c6aaafa876eed0c0f0f3e (diff)
downloadscons-ed6878a4af79fb56fcf5564cb3aca9cbbeac4a69.tar.gz
Merged revisions 1738-1754,1756 via svnmerge from
http://scons.tigris.org/svn/scons/branches/core ........ r1741 | stevenknight | 2006-12-16 22:51:07 -0600 (Sat, 16 Dec 2006) | 1 line 0.96.D527 - Give the f90 and f95 Tool modules knowledge of how to build source files of earlier Fortran versions. ........ r1742 | stevenknight | 2006-12-16 23:22:54 -0600 (Sat, 16 Dec 2006) | 1 line 0.96.D528 - Better handling of timestamp fallback if there's no md5 module. ........ r1743 | stevenknight | 2006-12-17 00:21:31 -0600 (Sun, 17 Dec 2006) | 1 line 0.96.D529 - Fix portability of new tests on systems that don't have TeX installed. ........ r1744 | stevenknight | 2006-12-19 15:30:16 -0600 (Tue, 19 Dec 2006) | 1 line 0.96.D530 - Eliminate the ListBuilder subclass in favor of using the Executor's target lists. ........ r1745 | stevenknight | 2006-12-19 18:54:26 -0600 (Tue, 19 Dec 2006) | 1 line 0.96.D531 - Eliminate of MultiStepBuilder as a separate Builder subclass. ........ r1746 | garyo | 2006-12-21 13:21:08 -0600 (Thu, 21 Dec 2006) | 1 line Minor doc fix, thanks to Douglas Landgraf. ........ r1747 | stevenknight | 2006-12-21 17:13:55 -0600 (Thu, 21 Dec 2006) | 1 line 0.96.D533 - Add CFLAGS for options common to C/C++. (Gary Oberbrunner) ........ r1748 | stevenknight | 2007-01-03 19:48:05 -0600 (Wed, 03 Jan 2007) | 1 line 0.96.D534 - Fix signature storage when targets are retrieved from CacheDir(). ........ r1749 | stevenknight | 2007-01-04 16:48:47 -0600 (Thu, 04 Jan 2007) | 1 line 0.96.D535 - Teach the lex and yacc tools about target files generated by different flex/bison options, and about Objective C suffixes. (Pupeno) ........ r1750 | stevenknight | 2007-01-04 17:14:38 -0600 (Thu, 04 Jan 2007) | 1 line 0.96.D536 - Refactor duplicate disambiguation logic in Entry.get_contents(). ........ r1751 | stevenknight | 2007-01-05 13:00:54 -0600 (Fri, 05 Jan 2007) | 1 line 0.96.D537 - Fix lprof regression from 0.96.92. ........ r1752 | stevenknight | 2007-01-05 20:43:48 -0600 (Fri, 05 Jan 2007) | 1 line 0.96.D538 - Fix caching of Builder suffix matching (to fix lprof regression). ........ r1753 | stevenknight | 2007-01-06 00:03:16 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D539 - Fix --include-dir when using MinGW. (Paul) ........ r1754 | stevenknight | 2007-01-06 00:24:53 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D540 - Make bootstrap.py something useful to execute SCons out of a source directory. ........ r1756 | stevenknight | 2007-01-06 21:32:11 -0600 (Sat, 06 Jan 2007) | 1 line 0.96.D541 - Update the Copyright year string to include 2007. Automate updating the month+year string in man page title headers. Fix hard-coded __revision__ strings that crept into some older tests. ........
-rw-r--r--QMTest/TestSCons_time.py4
-rw-r--r--README43
-rw-r--r--SConstruct7
-rw-r--r--bootstrap.py139
-rw-r--r--doc/man/scons-time.14
-rw-r--r--doc/man/scons.15
-rw-r--r--doc/man/sconsign.12
-rw-r--r--doc/user/copyright.in2
-rw-r--r--doc/user/copyright.sgml2
-rw-r--r--doc/user/main.in4
-rw-r--r--doc/user/main.sgml4
-rw-r--r--doc/user/nodes.in2
-rw-r--r--doc/user/nodes.sgml2
-rw-r--r--src/CHANGES.txt32
-rw-r--r--src/engine/SCons/Builder.py301
-rw-r--r--src/engine/SCons/BuilderTests.py62
-rw-r--r--src/engine/SCons/Environment.py15
-rw-r--r--src/engine/SCons/EnvironmentTests.py3
-rw-r--r--src/engine/SCons/Node/FS.py55
-rw-r--r--src/engine/SCons/Node/FSTests.py19
-rw-r--r--src/engine/SCons/Node/__init__.py2
-rw-r--r--src/engine/SCons/SConfTests.py6
-rw-r--r--src/engine/SCons/Script/Main.py1
-rw-r--r--src/engine/SCons/Taskmaster.py10
-rw-r--r--src/engine/SCons/TaskmasterTests.py7
-rw-r--r--src/engine/SCons/Tool/bcc32.py6
-rw-r--r--src/engine/SCons/Tool/cc.py6
-rw-r--r--src/engine/SCons/Tool/cc.xml26
-rw-r--r--src/engine/SCons/Tool/f90.py4
-rw-r--r--src/engine/SCons/Tool/f95.py7
-rw-r--r--src/engine/SCons/Tool/icc.py2
-rw-r--r--src/engine/SCons/Tool/lex.py53
-rw-r--r--src/engine/SCons/Tool/mingw.py2
-rw-r--r--src/engine/SCons/Tool/msvc.py6
-rw-r--r--src/engine/SCons/Tool/mwcc.py5
-rw-r--r--src/engine/SCons/Tool/qt.py4
-rw-r--r--src/engine/SCons/Tool/yacc.py53
-rw-r--r--src/engine/SCons/Tool/yacc.xml17
-rw-r--r--src/engine/SCons/Warnings.py3
-rw-r--r--src/script/scons-time.py2
-rw-r--r--src/test_copyrights.py265
-rw-r--r--test/Builder/multi/different-actions.py60
-rw-r--r--test/Builder/multi/different-environments.py64
-rw-r--r--test/Builder/multi/different-multi.py64
-rw-r--r--test/Builder/multi/different-order.py59
-rw-r--r--test/Builder/multi/different-overrides.py58
-rw-r--r--test/Builder/multi/different-target-lists.py64
-rw-r--r--test/Builder/multi/error.py57
-rw-r--r--test/Builder/multi/lone-target-list.py59
-rw-r--r--test/Builder/multi/multi.py55
-rw-r--r--test/Builder/multi/same-actions.py61
-rw-r--r--test/Builder/multi/same-overrides.py71
-rw-r--r--test/Builder/multi/same-targets.py57
-rw-r--r--test/Builder/non-multi.py54
-rw-r--r--test/CC/CFLAGS.py113
-rw-r--r--test/CC/SHCFLAGS.py131
-rw-r--r--test/CacheDir/up-to-date-q.py82
-rw-r--r--test/LEX/LEX.py91
-rw-r--r--test/LEX/LEXFLAGS.py63
-rw-r--r--test/LEX/live.py103
-rw-r--r--test/TEX/build_dir.py7
-rw-r--r--test/TEX/subdir-input.py13
-rw-r--r--test/YACC/YACC.py154
-rw-r--r--test/YACC/YACCFLAGS.py76
-rw-r--r--test/YACC/YACCVCGFILESUFFIX.py87
-rw-r--r--test/YACC/live.py165
-rw-r--r--test/ignore-command.py4
-rw-r--r--test/multi.py418
-rw-r--r--test/option-v.py2
-rw-r--r--test/silent-command.py4
-rw-r--r--test/timestamp-fallback.py3
71 files changed, 2243 insertions, 1250 deletions
diff --git a/QMTest/TestSCons_time.py b/QMTest/TestSCons_time.py
index 90eb5a8e..34caa062 100644
--- a/QMTest/TestSCons_time.py
+++ b/QMTest/TestSCons_time.py
@@ -11,9 +11,9 @@ from those classes, as well as any overridden or additional methods or
attributes defined in this subclass.
"""
-# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
+# __COPYRIGHT__
-__revision__ = "QMTest/TestSCons_time.py 0.96.C629 2006/11/19 06:39:17 knight"
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
diff --git a/README b/README
index 7d5c84b5..518de06e 100644
--- a/README
+++ b/README
@@ -122,13 +122,21 @@ system, populate the build/scons/ directory by running:
$ scons build/scons
-If you don't have SCons version 0.96 or later already installed on your
-system, you can use SCons itself to populate the build/scons/ directory
-with a little more typing. You must first set the SCONS_LIB_DIR
-environment variable to the local src/engine subdirectory, and then
-execute the local src/script/scons.py script to populate the build/scons/
-subdirectory. You would do this as follows on a Linux or UNIX system
-(using sh or a derivative like bash or ksh):
+You can also use this version of SCons to populate its own build directory
+by using a supplied bootstrap.py script:
+
+ $ python bootstrap.py build/scons
+
+The bootstrap.py keeps the src/ subdirectory free of compiled Python
+(*.pyc or *.pyo) files by copying the necessary SCons files to a local
+bootstrap/ subdirectory and executing it from there.
+
+You can also execute the local SCons directly from the src/ subdirectory
+by first setting the SCONS_LIB_DIR environment variable to the local
+src/engine subdirectory, and then execute the local src/script/scons.py
+script to populate the build/scons/ subdirectory. You would do this as
+follows on a Linux or UNIX system (using sh or a derivative like bash
+or ksh):
$ export SCONS_LIB_DIR=`pwd`/src/engine
$ python src/script/scons.py build/scons
@@ -136,11 +144,11 @@ subdirectory. You would do this as follows on a Linux or UNIX system
Or as follows on Windows:
C:\scons\>set SCONS_LIB_DIR=%cd%\src\engine
- C:\scons\>python src/script/scons.py build/scons
+ C:\scons\>python src\script\scons.py build/scons
-Either command will populate the build/scons/ directory with the necessary
-files and directory structure to use the Python-standard setup script
-as follows on Linux or UNIX:
+Any of the above commands will populate the build/scons/ directory with
+the necessary files and directory structure to use the Python-standard
+setup script as follows on Linux or UNIX:
# cd build/scons
# python setup.py install
@@ -238,9 +246,14 @@ modules that make up SCons. The src/script/scons.py wrapper script exists
mainly to find the appropriate build engine library and then execute it.
In order to make your own change locally and test them by hand, simply
-edit modules in the local src/engine/SCons and set the SCONS_LIB_DIR
-to point to that directory. Here is one way you can set up environment
-variables to do this on a UNIX or Linux system:
+edit modules in the local src/engine/SCons subdirectory tree and
+either use the local bootstrap.py script:
+
+ $ python bootstrap.py [arguments]
+
+Or set the SCONS_LIB_DIR to point to the src/engine/SCons directory and
+then execute the src/script/scons.py script. Here is one way you can
+set up environment variables to do this on a UNIX or Linux system:
$ setenv MYSCONS=`pwd`/src
$ setenv SCONS_LIB_DIR=$MYSCONS
@@ -258,7 +271,7 @@ if the SCons configuration for your project seems to be blocked by
an SCons bug, and you want to see if a patch you make actually fixes
that bug):
- $ python $MYSCONS/script/scons.py -C /some/other/location [arguments]
+ $ python bootstrap.py -C /some/other/location [arguments]
Lastly, if you want to be able to just execute your modified version
of SCons from the command line, you can make it executable and add its
diff --git a/SConstruct b/SConstruct
index 811ed4a1..a61466af 100644
--- a/SConstruct
+++ b/SConstruct
@@ -6,7 +6,10 @@
# When this gets changed, you also need to change test/option-v.py
# so it looks for the right string.
-copyright_years = '2001, 2002, 2003, 2004, 2005, 2006'
+copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007'
+
+# This gets inserted into the man pages to reflect the month of release.
+month_year = 'January 2007'
#
# __COPYRIGHT__
@@ -252,6 +255,7 @@ def SCons_revision(target, source, env):
contents = string.replace(contents, '__DATE' + '__', env['DATE'])
contents = string.replace(contents, '__DEVELOPER' + '__', env['DEVELOPER'])
contents = string.replace(contents, '__FILE' + '__', str(source[0]))
+ contents = string.replace(contents, '__MONTH_YEAR'+ '__', env['MONTH_YEAR'])
contents = string.replace(contents, '__REVISION' + '__', env['REVISION'])
contents = string.replace(contents, '__VERSION' + '__', env['VERSION'])
contents = string.replace(contents, '__NULL' + '__', '')
@@ -305,6 +309,7 @@ env = Environment(
COPYRIGHT = copyright,
DATE = date,
DEVELOPER = developer,
+ MONTH_YEAR = month_year,
REVISION = revision,
VERSION = version,
DH_COMPAT = 2,
diff --git a/bootstrap.py b/bootstrap.py
index c6a7db3a..9a88c1c8 100644
--- a/bootstrap.py
+++ b/bootstrap.py
@@ -1,20 +1,3 @@
-"""bootstrap.py
-
-This is an Aegis-to-SCons build script that collects a copy of the
-current SCons into a bootstrap/ subdirectory and then executes it with
-the supplied command-line options.
-
-Right now, it only understands the SCons -Y option, which is the only
-one currently used. It collects the repositories specified by -Y and
-searches them, in order, for the pieces of SCons to copy into the local
-bootstrap/ subdirectory.
-
-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
-local SConstruct file.
-
-"""
-
#
# __COPYRIGHT__
#
@@ -44,13 +27,97 @@ import getopt
import string
import sys
+__doc__ = """bootstrap.py
+
+This script supports "bootstrap" execution of the current SCons in
+this local source tree by copying of all necessary Python scripts and
+modules from underneath the src/ subdirectory into a subdirectory (named
+"bootstrap/" by default), and then executing the copied SCons with the
+supplied command-line arguments.
+
+There are a handful of options that are specific to this bootstrap.py
+script and which are *not* passed on to the underlying SCons script.
+All of these begin with the string "bootstrap_":
+
+ --bootstrap_dir=DIR
+
+ Sets the name of the directory into which the SCons files will
+ be copied. The default is "bootstrap" in the local subdirectory.
+
+ --bootstrap_force
+
+ Forces a copy of all necessary files. By default, the
+ bootstrap.py script only updates the bootstrap copy if the
+ content of the source copy is different.
+
+ --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.
+
+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
+local SConstruct file.
+"""
+
+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()
-opts, args = getopt.getopt(sys.argv[1:], "Y:", [])
+while command_line_args:
+ arg = command_line_args.pop(0)
-for o, a in opts:
- if o == '-Y':
- search.append(a)
+ if arg == '--bootstrap_dir':
+ try:
+ bootstrap_dir = 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:]
+
+ elif arg == '--bootstrap_force':
+ def must_copy(dst, src):
+ return 1
+
+ elif arg == '--bootstrap_update':
+ update_only = 1
+
+ elif arg in ('-Y', '--repository'):
+ try:
+ dir = command_line_args.pop(0)
+ except IndexError:
+ sys.stderr.write(requires_an_argument % arg)
+ sys.exit(1)
+ 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)
+
+ else:
+ pass_through_args.append(arg)
def find(file, search=search):
for dir in search:
@@ -68,24 +135,28 @@ MANIFEST_in = find(os.path.join(src_engine, 'MANIFEST.in'))
files = [ scons_py ] + map(lambda x: os.path.join(src_engine, x[:-1]),
open(MANIFEST_in).readlines())
-subdir = 'bootstrap'
-
for file in files:
src = find(file)
- dst = os.path.join(subdir, file)
- dir, _ = os.path.split(dst)
- if not os.path.isdir(dir):
- os.makedirs(dir)
- contents = open(src, 'rb').read()
- try: os.unlink(dst)
- except: pass
- open(dst, 'wb').write(contents)
-
-args = [ sys.executable, os.path.join(subdir, scons_py) ] + sys.argv[1:]
+ dst = os.path.join(bootstrap_dir, file)
+ if must_copy(dst, src):
+ dir = os.path.split(dst)[0]
+ if not os.path.isdir(dir):
+ os.makedirs(dir)
+ try: os.unlink(dst)
+ except: pass
+ open(dst, 'wb').write( open(src, 'rb').read() )
+
+if update_only:
+ sys.exit(0)
+
+args = [
+ os.path.split(sys.executable)[1],
+ os.path.join(bootstrap_dir, scons_py)
+ ] + pass_through_args
sys.stdout.write(string.join(args, " ") + '\n')
sys.stdout.flush()
-os.environ['SCONS_LIB_DIR'] = os.path.join(subdir, src_engine)
+os.environ['SCONS_LIB_DIR'] = os.path.join(bootstrap_dir, src_engine)
os.execve(sys.executable, args, os.environ)
diff --git a/doc/man/scons-time.1 b/doc/man/scons-time.1
index 50f490fa..b2de0029 100644
--- a/doc/man/scons-time.1
+++ b/doc/man/scons-time.1
@@ -19,7 +19,7 @@
.\" OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
.\" WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
.\"
-.\" doc/man/scons-time.1 0.96.C629 2006/11/18 11:50:43 knight
+.\" __FILE__ __REVISION__ __DATE__ __DEVELOPER__
.\"
.\" ES - Example Start - indents and turns off line fill
.de ES
@@ -98,7 +98,7 @@
[\fB--which=\fIWHICH\fR]
[\fIARGUMENTS\fR]
..
-.TH SCONS-TIME 1 "November 2006"
+.TH SCONS-TIME 1 "__MONTH_YEAR__"
.SH NAME
scons-time \- generate and display SCons timing information
'\"==========================================================================
diff --git a/doc/man/scons.1 b/doc/man/scons.1
index d45333b1..f4bef97c 100644
--- a/doc/man/scons.1
+++ b/doc/man/scons.1
@@ -31,7 +31,7 @@
.fi
.RE
..
-.TH SCONS 1 "December 2006"
+.TH SCONS 1 "__MONTH_YEAR__"
.SH NAME
scons \- a software construction tool
.SH SYNOPSIS
@@ -639,7 +639,7 @@ Output looks something like this:
.ES
$ scons --debug=presub
Building myprog.o with action(s):
- $SHCC $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES
+ $SHCC $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES
...
.EE
@@ -3149,6 +3149,7 @@ and added to the following construction variables:
-mno-cygwin CCFLAGS, LINKFLAGS
-mwindows LINKFLAGS
-pthread CCFLAGS, LINKFLAGS
+-std= CFLAGS
-Wa, ASFLAGS, CCFLAGS
-Wl,-rpath= RPATH
-Wl,-R, RPATH
diff --git a/doc/man/sconsign.1 b/doc/man/sconsign.1
index 7fc32ec2..3d01cf4e 100644
--- a/doc/man/sconsign.1
+++ b/doc/man/sconsign.1
@@ -31,7 +31,7 @@
.RE
.fi
..
-.TH SCONSIGN 1 "August 2004"
+.TH SCONSIGN 1 "__MONTH_YEAR__"
.SH NAME
sconsign \- print SCons .sconsign file information
.SH SYNOPSIS
diff --git a/doc/user/copyright.in b/doc/user/copyright.in
index 119f7494..76e3e50f 100644
--- a/doc/user/copyright.in
+++ b/doc/user/copyright.in
@@ -26,7 +26,7 @@
<blockquote>
<para>
- SCons User's Guide Copyright (c) 2004 Steven Knight
+ SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
</para>
</blockquote>
diff --git a/doc/user/copyright.sgml b/doc/user/copyright.sgml
index 119f7494..76e3e50f 100644
--- a/doc/user/copyright.sgml
+++ b/doc/user/copyright.sgml
@@ -26,7 +26,7 @@
<blockquote>
<para>
- SCons User's Guide Copyright (c) 2004 Steven Knight
+ SCons User's Guide Copyright (c) 2004, 2005, 2006, 2007 Steven Knight
</para>
</blockquote>
diff --git a/doc/user/main.in b/doc/user/main.in
index af3b0eed..be302d7e 100644
--- a/doc/user/main.in
+++ b/doc/user/main.in
@@ -101,10 +101,10 @@
<edition>Revision &buildrevision; (&builddate;)</edition>
- <pubdate>2004</pubdate>
+ <pubdate>2004, 2005, 2006, 2007</pubdate>
<copyright>
- <year>2004</year>
+ <year>2004, 2005, 2006, 2007</year>
<holder>Steven Knight</holder>
</copyright>
diff --git a/doc/user/main.sgml b/doc/user/main.sgml
index af3b0eed..be302d7e 100644
--- a/doc/user/main.sgml
+++ b/doc/user/main.sgml
@@ -101,10 +101,10 @@
<edition>Revision &buildrevision; (&builddate;)</edition>
- <pubdate>2004</pubdate>
+ <pubdate>2004, 2005, 2006, 2007</pubdate>
<copyright>
- <year>2004</year>
+ <year>2004, 2005, 2006, 2007</year>
<holder>Steven Knight</holder>
</copyright>
diff --git a/doc/user/nodes.in b/doc/user/nodes.in
index a5d05dcb..f5faf5ae 100644
--- a/doc/user/nodes.in
+++ b/doc/user/nodes.in
@@ -296,7 +296,7 @@
import os.path
program_list = Program('hello.c')
program_name = str(program_list[0])
- if not os.path.exists(program_name)
+ if not os.path.exists(program_name):
print program_name, "does not exist!"
</file>
<file name="hello.c">
diff --git a/doc/user/nodes.sgml b/doc/user/nodes.sgml
index b6bcb899..114e9e0a 100644
--- a/doc/user/nodes.sgml
+++ b/doc/user/nodes.sgml
@@ -300,7 +300,7 @@
import os.path
program_list = Program('hello.c')
program_name = str(program_list[0])
- if not os.path.exists(program_name)
+ if not os.path.exists(program_name):
print program_name, "does not exist!"
</programlisting>
diff --git a/src/CHANGES.txt b/src/CHANGES.txt
index e7f49370..3678dd18 100644
--- a/src/CHANGES.txt
+++ b/src/CHANGES.txt
@@ -14,6 +14,11 @@ RELEASE 0.97 - XXX
- Allow arbitrary white space after a SWIG %module declaration.
+ From Paul:
+
+ - When compiling resources under MinGW, make sure there's a space
+ between the --include-dir option and its argument.
+
From Jay Kint:
- Alleviate long command line issues on Windows by executing command
@@ -65,6 +70,19 @@ RELEASE 0.97 - XXX
files control over what exceptions cause a string to expand to ''
vs. terminating processing with an error.
+ - Allow the f90.py and f95.py Tool modules to compile earlier source
+ source files of earlier Fortran version.
+
+ - Fix storing signatures of files retrieved from CacheDir() so they're
+ correctly identified as up-to-date next invocation.
+
+ - Make sure lists of computed source suffixes cached by Builder objects
+ don't persist across changes to the list of source Builders (so the
+ addition of suffixes like .ui by the qt.py Tool module take effect).
+
+ - Enhance the bootstrap.py script to allow it to be used to execute
+ SCons more easily from a checked-out source tree.
+
From Ben Leslie:
- Fix post-Memoizer value caching misspellings in Node.FS._doLookup().
@@ -93,6 +111,9 @@ RELEASE 0.97 - XXX
- Eliminate some unnecessary os.path.normpath() calls.
+ - Add a $CFLAGS variable for C-specific options, leaving $CCFLAGS
+ for options common to C and C++.
+
From Tom Parker:
- Have the error message print the missing file that Qt can't find.
@@ -107,6 +128,17 @@ RELEASE 0.97 - XXX
specified on the command line (and not intuited from the old way of
calling it with just ".sconsign").
+ From Jose Pablo Ezequiel "Pupeno" Fernandez Silva:
+
+ - Give the 'lex' tool knowledge of the additional target files produced
+ by the flex "--header-file=" and "--tables-file=" options.
+
+ - Give the 'yacc' tool knowledge of the additional target files produced
+ by the bison "-g", "--defines=" and "--graph=" options.
+
+ - Generate intermediate files with Objective C file suffixes (.m) when
+ the lex and yacc source files have appropriate suffixes (.lm and .ym).
+
From Sohail Somain:
- Have the mslink.py Tool only look for a 'link' executable on Windows
diff --git a/src/engine/SCons/Builder.py b/src/engine/SCons/Builder.py
index d5f566a3..d625ed7a 100644
--- a/src/engine/SCons/Builder.py
+++ b/src/engine/SCons/Builder.py
@@ -16,20 +16,9 @@ building new types of files in their configurations, without having to
dive any deeper into this subsystem.
The base class here is BuilderBase. This is a concrete base class which
-does, in fact, represent most Builder objects that we (or users) create.
+does, in fact, represent the Builder objects that we (or users) create.
-There is (at present) one subclasses:
-
- MultiStepBuilder
-
- This is a Builder that knows how to "chain" Builders so that
- users can specify a source file that requires multiple steps
- to turn into a target file. A canonical example is building a
- program from yacc input file, which requires invoking a builder
- to turn the .y into a .c, the .c into a .o, and the .o into an
- executable program.
-
-There is also two proxies that look like Builders:
+There is also a proxy that looks like a Builder:
CompositeBuilder
@@ -39,11 +28,6 @@ There is also two proxies that look like Builders:
(compilers, compile options) for different flavors of source
files.
- ListBuilder
-
- This proxies for a Builder *invocation* where the target
- is a list of files, not a single file.
-
Builders and their proxies have the following public interface methods
used by other modules:
@@ -227,10 +211,9 @@ class OverrideWarner(UserDict.UserDict):
"""A class for warning about keyword arguments that we use as
overrides in a Builder call.
- This class exists to handle the fact that a single MultiStepBuilder
- call can actually invoke multiple builders as a result of a single
- user-level Builder call. This class only emits the warnings once,
- no matter how many Builders are invoked.
+ This class exists to handle the fact that a single Builder call
+ can actually invoke multiple builders. This class only emits the
+ warnings once, no matter how many Builders are invoked.
"""
def __init__(self, dict):
UserDict.UserDict.__init__(self, dict)
@@ -240,13 +223,10 @@ class OverrideWarner(UserDict.UserDict):
if self.already_warned:
return
for k in self.keys():
- try:
+ if misleading_keywords.has_key(k):
alt = misleading_keywords[k]
- except KeyError:
- pass
- else:
- SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning,
- "Did you mean to use `%s' instead of `%s'?" % (alt, k))
+ msg = "Did you mean to use `%s' instead of `%s'?" % (alt, k)
+ SCons.Warnings.warn(SCons.Warnings.MisleadingKeywordsWarning, msg)
self.already_warned = 1
def Builder(**kw):
@@ -284,15 +264,12 @@ def Builder(**kw):
elif SCons.Util.is_List(emitter):
kw['emitter'] = ListEmitter(emitter)
- if kw.has_key('src_builder'):
- ret = apply(MultiStepBuilder, (), kw)
- else:
- ret = apply(BuilderBase, (), kw)
+ result = apply(BuilderBase, (), kw)
if not composite is None:
- ret = CompositeBuilder(ret, composite)
+ result = CompositeBuilder(result, composite)
- return ret
+ return result
def _node_errors(builder, env, tlist, slist):
"""Validate that the lists of target and source nodes are
@@ -304,7 +281,7 @@ def _node_errors(builder, env, tlist, slist):
# were specified.
for t in tlist:
if t.side_effect:
- raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
+ raise UserError, "Multiple ways to build the same target were specified for: %s" % t
if t.has_explicit_builder():
if not t.env is None and not t.env is env:
action = t.builder.action
@@ -312,22 +289,21 @@ def _node_errors(builder, env, tlist, slist):
contents = action.get_contents(tlist, slist, env)
if t_contents == contents:
- SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning,
- "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), action.genstring(tlist, slist, t.env)))
-
+ msg = "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s" % (t, action.genstring(tlist, slist, t.env))
+ SCons.Warnings.warn(SCons.Warnings.DuplicateEnvironmentWarning, msg)
else:
- raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t)
-
+ msg = "Two environments with different actions were specified for the same target: %s" % t
+ raise UserError, msg
if builder.multi:
if t.builder != builder:
- if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
- raise UserError, "Two different target sets have a target in common: %s"%str(t)
- else:
- raise UserError, "Two different builders (%s and %s) were specified for the same target: %s"%(t.builder.get_name(env), builder.get_name(env), str(t))
- elif isinstance(t.builder, ListBuilder) ^ isinstance(builder, ListBuilder):
- raise UserError, "Cannot build same target `%s' as singular and list"%str(t)
+ msg = "Two different builders (%s and %s) were specified for the same target: %s" % (t.builder.get_name(env), builder.get_name(env), t)
+ raise UserError, msg
+ if t.get_executor().targets != tlist:
+ msg = "Two different target lists have a target in common: %s (from %s and from %s)" % (t, map(str, t.get_executor().targets), map(str, tlist))
+ raise UserError, msg
elif t.sources != slist:
- raise UserError, "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (str(t), map(str,t.sources), map(str,slist))
+ msg = "Multiple ways to build the same target were specified for: %s (from %s and from %s)" % (t, map(str, t.sources), map(str, slist))
+ raise UserError, msg
if builder.single_source:
if len(slist) > 1:
@@ -388,6 +364,7 @@ class BuilderBase:
name = None,
chdir = _null,
is_explicit = 1,
+ src_builder = [],
**overrides):
if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
self._memo = {}
@@ -432,6 +409,10 @@ class BuilderBase:
self.executor_kw['chdir'] = chdir
self.is_explicit = is_explicit
+ if not SCons.Util.is_List(src_builder):
+ src_builder = [ src_builder ]
+ self.src_builder = src_builder
+
def __nonzero__(self):
raise InternalError, "Do not test for the Node.builder attribute directly; use Node.has_builder() instead"
@@ -557,6 +538,9 @@ class BuilderBase:
def _execute(self, env, target, source, overwarn={}, executor_kw={}):
# We now assume that target and source are lists or None.
+ if self.src_builder:
+ source = self.src_builder_sources(env, source, overwarn)
+
if self.single_source and len(source) > 1 and target is None:
result = []
if target is None: target = [None]*len(source)
@@ -570,31 +554,26 @@ class BuilderBase:
tlist, slist = self._create_nodes(env, target, source)
- if len(tlist) == 1:
- builder = self
- else:
- builder = ListBuilder(self, env, tlist)
-
# Check for errors with the specified target/source lists.
- _node_errors(builder, env, tlist, slist)
+ _node_errors(self, env, tlist, slist)
# The targets are fine, so find or make the appropriate Executor to
# build this particular list of targets from this particular list of
# sources.
- if builder.multi:
- get_executor = builder.get_multi_executor
+ if self.multi:
+ get_executor = self.get_multi_executor
else:
- get_executor = builder.get_single_executor
+ get_executor = self.get_single_executor
executor = get_executor(env, tlist, slist, executor_kw)
# Now set up the relevant information in the target Nodes themselves.
for t in tlist:
t.cwd = env.fs.getcwd()
- t.builder_set(builder)
+ t.builder_set(self)
t.env_set(env)
t.add_source(slist)
t.set_executor(executor)
- t.set_explicit(builder.is_explicit)
+ t.set_explicit(self.is_explicit)
return SCons.Node.NodeList(tlist)
@@ -650,35 +629,6 @@ class BuilderBase:
suffix = suffix(env, sources)
return env.subst(suffix)
- def _src_suffixes_key(self, env):
- return id(env)
-
- memoizer_counters.append(SCons.Memoize.CountDict('src_suffixes', _src_suffixes_key))
-
- def src_suffixes(self, env):
- """
- Returns the list of source suffixes for this Builder.
-
- The suffix list may contain construction variable expansions,
- so we have to evaluate the individual strings. To avoid doing
- this over and over, we memoize the results for each construction
- environment.
- """
- memo_key = id(env)
- try:
- memo_dict = self._memo['src_suffixes']
- except KeyError:
- memo_dict = {}
- self._memo['src_suffixes'] = memo_dict
- else:
- try:
- return memo_dict[memo_key]
- except KeyError:
- pass
- result = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
- memo_dict[memo_key] = result
- return result
-
def set_src_suffix(self, src_suffix):
if not src_suffix:
src_suffix = []
@@ -712,72 +662,15 @@ class BuilderBase:
"""
self.emitter[suffix] = emitter
-
-
-class ListBuilder(SCons.Util.Proxy):
- """A Proxy to support building an array of targets (for example,
- foo.o and foo.h from foo.y) from a single Action execution.
- """
-
- def __init__(self, builder, env, tlist):
- if __debug__: logInstanceCreation(self, 'Builder.ListBuilder')
- SCons.Util.Proxy.__init__(self, builder)
- self.builder = builder
- self.target_scanner = builder.target_scanner
- self.source_scanner = builder.source_scanner
- self.env = env
- self.tlist = tlist
- self.multi = builder.multi
- self.single_source = builder.single_source
-
- def targets(self, node):
- """Return the list of targets for this builder instance.
+ def add_src_builder(self, builder):
"""
- return self.tlist
-
- def get_name(self, env):
- """Attempts to get the name of the Builder."""
-
- return "ListBuilder(%s)" % self.builder.get_name(env)
-
-class MultiStepBuilder(BuilderBase):
- """This is a builder subclass that can build targets in
- multiple steps. The src_builder parameter to the constructor
- accepts a builder that is called to build sources supplied to
- this builder. The targets of that first build then become
- the sources of this builder.
-
- If this builder has a src_suffix supplied, then the src_builder
- builder is NOT invoked if the suffix of a source file matches
- src_suffix.
- """
-
- memoizer_counters = []
-
- def __init__(self, src_builder,
- action = None,
- prefix = '',
- suffix = '',
- src_suffix = '',
- target_factory = None,
- source_factory = None,
- target_scanner = None,
- source_scanner = None,
- emitter=None,
- single_source=0):
- if __debug__: logInstanceCreation(self, 'Builder.MultiStepBuilder')
- BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
- target_factory, source_factory,
- target_scanner, source_scanner, emitter,
- single_source = single_source)
- if not SCons.Util.is_List(src_builder):
- src_builder = [ src_builder ]
- self.src_builder = src_builder
-
- def _get_sdict_key(self, env):
- return id(env)
+ Add a new Builder to the list of src_builders.
- memoizer_counters.append(SCons.Memoize.CountDict('_get_sdict', _get_sdict_key))
+ This requires wiping out cached values so that the computed
+ lists of source suffixes get re-calculated.
+ """
+ self._memo = {}
+ self.src_builder.append(builder)
def _get_sdict(self, env):
"""
@@ -788,35 +681,26 @@ class MultiStepBuilder(BuilderBase):
This dictionary is used for each target specified, so we save a
lot of extra computation by memoizing it for each construction
environment.
+
+ Note that this is re-computed each time, not cached, because there
+ might be changes to one of our source Builders (or one of their
+ source Builders, and so on, and so on...) that we can't "see."
+
+ The underlying methods we call cache their computed values,
+ though, so we hope repeatedly aggregating them into a dictionary
+ like this won't be too big a hit. We may need to look for a
+ better way to do this if performance data show this has turned
+ into a significant bottleneck.
"""
- memo_key = id(env)
- try:
- memo_dict = self._memo['_get_sdict']
- except KeyError:
- memo_dict = {}
- self._memo['_get_sdict'] = memo_dict
- else:
- try:
- return memo_dict[memo_key]
- except KeyError:
- pass
sdict = {}
- for bld in self.src_builder:
- if SCons.Util.is_String(bld):
- try:
- bld = env['BUILDERS'][bld]
- except KeyError:
- continue
+ for bld in self.get_src_builders(env):
for suf in bld.src_suffixes(env):
sdict[suf] = bld
- memo_dict[memo_key] = sdict
return sdict
- def _execute(self, env, target, source, overwarn={}, executor_kw={}):
- # We now assume that target and source are lists or None.
+ def src_builder_sources(self, env, source, overwarn={}):
source_factory = env.get_factory(self.source_factory)
slist = env.arg2nodes(source, source_factory)
- final_sources = []
sdict = self._get_sdict(env)
@@ -834,13 +718,15 @@ class MultiStepBuilder(BuilderBase):
return suf
return None
+ result = []
+
for snode in slist:
match_suffix = match_src_suffix(snode)
if match_suffix:
try:
bld = sdict[match_suffix]
except KeyError:
- final_sources.append(snode)
+ result.append(snode)
else:
tlist = bld._execute(env, None, [snode], overwarn)
# If the subsidiary Builder returned more than one
@@ -848,39 +734,56 @@ class MultiStepBuilder(BuilderBase):
# Builder isn't capable of building.
if len(tlist) > 1:
tlist = filter(match_src_suffix, tlist)
- final_sources.extend(tlist)
+ result.extend(tlist)
else:
- final_sources.append(snode)
+ result.append(snode)
- return BuilderBase._execute(self, env, target, final_sources, overwarn)
+ return result
+
+ def _get_src_builders_key(self, env):
+ return id(env)
+
+ memoizer_counters.append(SCons.Memoize.CountDict('get_src_builders', _get_src_builders_key))
def get_src_builders(self, env):
- """Return all the src_builders for this Builder.
+ """
+ Returns the list of source Builders for this Builder.
- This is essentially a recursive descent of the src_builder "tree."
+ This exists mainly to look up Builders referenced as
+ strings in the 'BUILDER' variable of the construction
+ environment and cache the result.
"""
- ret = []
+ memo_key = id(env)
+ try:
+ memo_dict = self._memo['get_src_builders']
+ except KeyError:
+ memo_dict = {}
+ self._memo['get_src_builders'] = memo_dict
+ else:
+ try:
+ return memo_dict[memo_key]
+ except KeyError:
+ pass
+
+ builders = []
for bld in self.src_builder:
if SCons.Util.is_String(bld):
- # All Environments should have a BUILDERS
- # variable, so no need to check for it.
try:
bld = env['BUILDERS'][bld]
except KeyError:
continue
- ret.append(bld)
- return ret
+ builders.append(bld)
+
+ memo_dict[memo_key] = builders
+ return builders
- def _src_suffixes_key(self, env):
+ def _subst_src_suffixes_key(self, env):
return id(env)
- memoizer_counters.append(SCons.Memoize.CountDict('src_suffixes', _src_suffixes_key))
+ memoizer_counters.append(SCons.Memoize.CountDict('subst_src_suffixes', _subst_src_suffixes_key))
- def src_suffixes(self, env):
+ def subst_src_suffixes(self, env):
"""
- Returns the list of source suffixes for all src_builders of this
- Builder.
-
The suffix list may contain construction variable expansions,
so we have to evaluate the individual strings. To avoid doing
this over and over, we memoize the results for each construction
@@ -888,19 +791,31 @@ class MultiStepBuilder(BuilderBase):
"""
memo_key = id(env)
try:
- memo_dict = self._memo['src_suffixes']
+ memo_dict = self._memo['subst_src_suffixes']
except KeyError:
memo_dict = {}
- self._memo['src_suffixes'] = memo_dict
+ self._memo['subst_src_suffixes'] = memo_dict
else:
try:
return memo_dict[memo_key]
except KeyError:
pass
- suffixes = BuilderBase.src_suffixes(self, env)
+ suffixes = map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
+ memo_dict[memo_key] = suffixes
+ return suffixes
+
+ def src_suffixes(self, env):
+ """
+ Returns the list of source suffixes for all src_builders of this
+ Builder.
+
+ This is essentially a recursive descent of the src_builder "tree."
+ (This value isn't cached because there may be changes in a
+ src_builder many levels deep that we can't see.)
+ """
+ suffixes = self.subst_src_suffixes(env)
for builder in self.get_src_builders(env):
suffixes.extend(builder.src_suffixes(env))
- memo_dict[memo_key] = suffixes
return suffixes
class CompositeBuilder(SCons.Util.Proxy):
diff --git a/src/engine/SCons/BuilderTests.py b/src/engine/SCons/BuilderTests.py
index 4e196e26..acf07220 100644
--- a/src/engine/SCons/BuilderTests.py
+++ b/src/engine/SCons/BuilderTests.py
@@ -147,6 +147,8 @@ class Environment:
class MyAction:
def __init__(self, action):
self.action = action
+ def __call__(self, *args, **kw):
+ pass
def get_executor(self, env, overrides, tlist, slist, executor_kw):
return ['executor'] + [self.action]
@@ -716,8 +718,8 @@ class BuilderTestCase(unittest.TestCase):
assert 0
- def test_ListBuilder(self):
- """Testing ListBuilder class."""
+ def test_lists(self):
+ """Testing handling lists of targets and source"""
def function2(target, source, env, tlist = [outfile, outfile2], **kw):
for t in target:
open(str(t), 'w').write("function2\n")
@@ -770,15 +772,17 @@ class BuilderTestCase(unittest.TestCase):
assert os.path.exists(test.workpath('sub1'))
assert os.path.exists(test.workpath('sub2'))
- def test_MultiStepBuilder(self):
- """Testing MultiStepBuilder class."""
+ def test_src_builder(self):
+ """Testing Builders with src_builder"""
+ # These used to be MultiStepBuilder objects until we
+ # eliminated it as a separate class
env = Environment()
builder1 = SCons.Builder.Builder(action='foo',
src_suffix='.bar',
suffix='.foo')
- builder2 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
- src_builder = builder1,
- src_suffix = '.foo')
+ builder2 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder = builder1,
+ src_suffix = '.foo')
tgt = builder2(env, source=[])
assert tgt == [], tgt
@@ -800,22 +804,22 @@ class BuilderTestCase(unittest.TestCase):
s = map(str, tgt.sources[0].sources)
assert s == ['aaa.bar'], s
- builder3 = SCons.Builder.MultiStepBuilder(action = 'foo',
- src_builder = 'xyzzy',
- src_suffix = '.xyzzy')
+ builder3 = SCons.Builder.Builder(action = 'foo',
+ src_builder = 'xyzzy',
+ src_suffix = '.xyzzy')
assert builder3.get_src_builders(Environment()) == []
builder4 = SCons.Builder.Builder(action='bld4',
src_suffix='.i',
suffix='_wrap.c')
- builder5 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
- src_builder=builder4,
- suffix='.obj',
- src_suffix='.c')
- builder6 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
- src_builder=builder5,
- suffix='.exe',
- src_suffix='.obj')
+ builder5 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder=builder4,
+ suffix='.obj',
+ src_suffix='.c')
+ builder6 = SCons.Builder.Builder(action=MyAction('act'),
+ src_builder=builder5,
+ suffix='.exe',
+ src_suffix='.obj')
tgt = builder6(env, 'test', 'test.i')[0]
s = str(tgt)
assert s == 'test.exe', s
@@ -1359,9 +1363,8 @@ class BuilderTestCase(unittest.TestCase):
b1 = SCons.Builder.Builder(action='foo', suffix='.o')
b2 = SCons.Builder.Builder(action='foo', suffix='.c')
- b3 = SCons.Builder.MultiStepBuilder(action='bar',
- src_suffix = '.foo',
- src_builder = b1)
+ b3 = SCons.Builder.Builder(action='bar', src_suffix = '.foo',
+ src_builder = b1)
b4 = SCons.Builder.Builder(action={})
b5 = SCons.Builder.Builder(action='foo', name='builder5')
b6 = SCons.Builder.Builder(action='foo')
@@ -1407,17 +1410,6 @@ class BuilderTestCase(unittest.TestCase):
for B in b3.get_src_builders(env2):
assert B.get_name(env2) == 'B1'
- tgts = b1(env, target = [outfile, outfile2], source='moo')
- for t in tgts:
- name = t.builder.get_name(env)
- assert name == 'ListBuilder(bldr1)', name
- # The following are not symbolically correct, because the
- # ListBuilder was only created on behalf of env, so it
- # would probably be OK if better correctness
- # env-to-builder mappings caused this to fail in the
- # future.
- assert t.builder.get_name(env2) == 'ListBuilder(B1)'
-
tgt = b4(env, target = 'moo', source='cow')
assert tgt[0].builder.get_name(env) == 'bldr4'
@@ -1529,7 +1521,7 @@ class CompositeBuilderTestCase(unittest.TestCase):
assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
tgt = builder(env, target='t2', source='t2a.foo t2b.ina')[0]
- assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder), tgt.builder.__dict__
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase), tgt.builder.__dict__
bar_bld = SCons.Builder.Builder(action = 'a-bar',
src_suffix = '.inb',
@@ -1543,10 +1535,10 @@ class CompositeBuilderTestCase(unittest.TestCase):
builder.add_action('.bar', 'bar')
tgt = builder(env, target='t3-foo', source='t3a.foo t3b.ina')[0]
- assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
tgt = builder(env, target='t3-bar', source='t3a.bar t3b.inb')[0]
- assert isinstance(tgt.builder, SCons.Builder.MultiStepBuilder)
+ assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
flag = 0
tgt = builder(env, target='t5', source=['test5a.foo', 'test5b.inb'])[0]
diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py
index 4761ea05..e2883f47 100644
--- a/src/engine/SCons/Environment.py
+++ b/src/engine/SCons/Environment.py
@@ -54,7 +54,6 @@ import SCons.Node.Python
import SCons.Platform
import SCons.SConsign
import SCons.Sig
-import SCons.Sig.MD5
import SCons.Sig.TimeStamp
import SCons.Subst
import SCons.Tool
@@ -513,6 +512,7 @@ class SubstitutionEnvironment:
"""
dict = {
'ASFLAGS' : [],
+ 'CFLAGS' : [],
'CCFLAGS' : [],
'CPPDEFINES' : [],
'CPPFLAGS' : [],
@@ -641,6 +641,8 @@ class SubstitutionEnvironment:
elif arg == '-pthread':
dict['CCFLAGS'].append(arg)
dict['LINKFLAGS'].append(arg)
+ elif arg[:5] == '-std=':
+ dict['CFLAGS'].append(arg) # C only
elif arg[0] == '+':
dict['CCFLAGS'].append(arg)
dict['LINKFLAGS'].append(arg)
@@ -1667,8 +1669,15 @@ class Base(SubstitutionEnvironment):
def SourceSignatures(self, type):
type = self.subst(type)
if type == 'MD5':
- import SCons.Sig.MD5
- self._calc_module = SCons.Sig.MD5
+ try:
+ import SCons.Sig.MD5
+ except ImportError:
+ msg = "No MD5 module available, using time stamps"
+ SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
+ import SCons.Sig.TimeStamp
+ self._calc_module = SCons.Sig.TimeStamp
+ else:
+ self._calc_module = SCons.Sig.MD5
elif type == 'timestamp':
import SCons.Sig.TimeStamp
self._calc_module = SCons.Sig.TimeStamp
diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py
index f0f73dac..c015bc1e 100644
--- a/src/engine/SCons/EnvironmentTests.py
+++ b/src/engine/SCons/EnvironmentTests.py
@@ -658,6 +658,7 @@ sys.exit(1)
empty = {
'ASFLAGS' : [],
+ 'CFLAGS' : [],
'CCFLAGS' : [],
'CPPDEFINES' : [],
'CPPFLAGS' : [],
@@ -686,6 +687,7 @@ sys.exit(1)
"-Wl,-R,rpath2 " + \
"-Wl,-Rrpath3 " + \
"-Wp,-cpp " + \
+ "-std=c99 " + \
"-framework Carbon " + \
"-frameworkdir=fwd1 " + \
"-Ffwd2 " + \
@@ -698,6 +700,7 @@ sys.exit(1)
d = env.ParseFlags(s)
assert d['ASFLAGS'] == ['-as'], d['ASFLAGS']
+ assert d['CFLAGS'] == ['-std=c99']
assert d['CCFLAGS'] == ['-X', '-Wa,-as',
'-pthread', '-mno-cygwin',
('-arch', 'i386'), ('-isysroot', '/tmp'),
diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py
index 08b8d7d8..8db29280 100644
--- a/src/engine/SCons/Node/FS.py
+++ b/src/engine/SCons/Node/FS.py
@@ -48,7 +48,6 @@ import SCons.Action
from SCons.Debug import logInstanceCreation
import SCons.Errors
import SCons.Node
-import SCons.Sig.MD5
import SCons.Subst
import SCons.Util
import SCons.Warnings
@@ -776,7 +775,7 @@ class Entry(Base):
def diskcheck_match(self):
pass
- def disambiguate(self):
+ def disambiguate(self, must_exist=None):
"""
"""
if self.isdir():
@@ -802,6 +801,9 @@ class Entry(Base):
self.srcnode().isdir():
self.__class__ = Dir
self._morph()
+ elif must_exist:
+ msg = "No such file or directory: '%s'" % self.abspath
+ raise SCons.Errors.UserError, msg
else:
self.__class__ = File
self._morph()
@@ -825,18 +827,17 @@ class Entry(Base):
Since this should return the real contents from the file
system, we check to see into what sort of subclass we should
morph this Entry."""
- if self.isfile():
- self.__class__ = File
- self._morph()
- return self.get_contents()
- if self.isdir():
- self.__class__ = Dir
- self._morph()
+ try:
+ self = self.disambiguate(must_exist=1)
+ except SCons.Errors.UserError, e:
+ # 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
+ # like (e.g. in qt.py) don't have to disambiguate by hand
+ # or catch the exception.
+ return ''
+ else:
return self.get_contents()
- if self.islink():
- return '' # avoid errors for dangling symlinks
- msg = "No such file or directory: '%s'" % self.abspath
- raise SCons.Errors.UserError, msg
def must_be_a_Dir(self):
"""Called to make sure a Node is a Dir. Since we're an
@@ -1259,7 +1260,13 @@ class FS(LocalFS):
self.CacheDebug = self.CacheDebugWrite
def CacheDir(self, path):
- self.CachePath = path
+ try:
+ import SCons.Sig.MD5
+ except ImportError:
+ msg = "No MD5 module available, CacheDir() not supported"
+ SCons.Warnings.warn(SCons.Warnings.NoMD5ModuleWarning, msg)
+ else:
+ self.CachePath = path
def build_dir_target_climb(self, orig, dir, tail):
"""Create targets in corresponding build directories
@@ -2026,16 +2033,23 @@ class File(Base):
b = self.is_derived()
if not b and not self.has_src_builder():
return None
+
+ retrieved = None
if b and self.fs.CachePath:
if self.fs.cache_show:
if CacheRetrieveSilent(self, [], None, execute=1) == 0:
self.build(presub=0, execute=0)
- self.set_state(SCons.Node.executed)
- return 1
- elif CacheRetrieve(self, [], None, execute=1) == 0:
+ retrieved = 1
+ else:
+ if CacheRetrieve(self, [], None, execute=1) == 0:
+ retrieved = 1
+ if retrieved:
+ # Record build signature information, but don't
+ # push it out to cache. (We just got it from there!)
self.set_state(SCons.Node.executed)
- return 1
- return None
+ SCons.Node.Node.built(self)
+
+ return retrieved
def built(self):
@@ -2285,12 +2299,15 @@ class File(Base):
return None, None
ninfo = self.get_binfo().ninfo
if not hasattr(ninfo, 'bsig'):
+ import SCons.Errors
raise SCons.Errors.InternalError, "cachepath(%s) found no bsig" % self.path
elif ninfo.bsig is None:
+ import SCons.Errors
raise SCons.Errors.InternalError, "cachepath(%s) found a bsig of None" % self.path
# Add the path to the cache signature, because multiple
# targets built by the same action will all have the same
# build signature, and we have to differentiate them somehow.
+ import SCons.Sig.MD5
cache_sig = SCons.Sig.MD5.collect([ninfo.bsig, self.path])
subdir = string.upper(cache_sig[0])
dir = os.path.join(self.fs.CachePath, subdir)
diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py
index 434709c4..ec3c322d 100644
--- a/src/engine/SCons/Node/FSTests.py
+++ b/src/engine/SCons/Node/FSTests.py
@@ -1224,12 +1224,9 @@ class FSTestCase(_tempdirTestCase):
# test Entry.get_contents()
e = fs.Entry('does_not_exist')
- exc_caught = 0
- try:
- e.get_contents()
- except SCons.Errors.UserError:
- exc_caught = 1
- assert exc_caught, "Should have caught an IOError"
+ c = e.get_contents()
+ assert c == "", c
+ assert e.__class__ == SCons.Node.FS.Entry
test.write("file", "file\n")
try:
@@ -1250,7 +1247,7 @@ class FSTestCase(_tempdirTestCase):
os.symlink('nonexistent', test.workpath('dangling_symlink'))
e = fs.Entry('dangling_symlink')
c = e.get_contents()
- assert e.__class__ == SCons.Node.FS.Entry
+ assert e.__class__ == SCons.Node.FS.Entry, e.__class__
assert c == "", c
test.write("tstamp", "tstamp\n")
@@ -1866,12 +1863,8 @@ class EntryTestCase(_tempdirTestCase):
assert e3f.__class__ is SCons.Node.FS.File, e3f.__class__
e3n = fs.Entry('e3n')
- exc_caught = None
- try:
- e3n.get_contents()
- except SCons.Errors.UserError:
- exc_caught = 1
- assert exc_caught, "did not catch expected SCons.Errors.UserError"
+ e3n.get_contents()
+ assert e3n.__class__ is SCons.Node.FS.Entry, e3n.__class__
test.subdir('e4d')
test.write('e4f', "e4f\n")
diff --git a/src/engine/SCons/Node/__init__.py b/src/engine/SCons/Node/__init__.py
index e5d064e5..e17666bb 100644
--- a/src/engine/SCons/Node/__init__.py
+++ b/src/engine/SCons/Node/__init__.py
@@ -219,7 +219,7 @@ class Node:
# what line in what file created the node, for example).
Annotate(self)
- def disambiguate(self):
+ def disambiguate(self, must_exist=None):
return self
def get_suffix(self):
diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py
index 3ad4cc71..22ec188b 100644
--- a/src/engine/SCons/SConfTests.py
+++ b/src/engine/SCons/SConfTests.py
@@ -201,6 +201,12 @@ class SConfTestCase(unittest.TestCase):
pass
def calc_signature(self, calc):
pass
+ def get_executor(self):
+ class Executor:
+ pass
+ e = Executor()
+ e.targets = [self]
+ return e
return [MyNode('n1'), MyNode('n2')]
try:
self.scons_env.Append(BUILDERS = {'SConfActionBuilder' : MyBuilder()})
diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py
index 6eedbabf..96f15261 100644
--- a/src/engine/SCons/Script/Main.py
+++ b/src/engine/SCons/Script/Main.py
@@ -961,6 +961,7 @@ def _main(args, parser):
SCons.Warnings.DeprecatedWarning,
SCons.Warnings.DuplicateEnvironmentWarning,
SCons.Warnings.MissingSConscriptWarning,
+ SCons.Warnings.NoMD5ModuleWarning,
SCons.Warnings.NoMetaclassSupportWarning,
SCons.Warnings.NoParallelSupportWarning,
SCons.Warnings.MisleadingKeywordsWarning, ]
diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py
index 2ea3f0da..04ed19a8 100644
--- a/src/engine/SCons/Taskmaster.py
+++ b/src/engine/SCons/Taskmaster.py
@@ -546,10 +546,7 @@ class Taskmaster:
if node is None:
return None
- try:
- tlist = node.builder.targets(node)
- except AttributeError:
- tlist = [node]
+ tlist = node.get_executor().targets
task = self.tasker(self, tlist, node is self.current_top, node)
try:
@@ -580,10 +577,7 @@ class Taskmaster:
pass
def executed(self, node):
- try:
- tlist = node.builder.targets(node)
- except AttributeError:
- tlist = [node]
+ pass
def exception_raise(self, exception):
exc = exception[:]
diff --git a/src/engine/SCons/TaskmasterTests.py b/src/engine/SCons/TaskmasterTests.py
index 4fefb9d2..1803eee2 100644
--- a/src/engine/SCons/TaskmasterTests.py
+++ b/src/engine/SCons/TaskmasterTests.py
@@ -162,6 +162,13 @@ class Node:
def postprocess(self):
self.postprocessed = 1
+ def get_executor(self):
+ class Executor:
+ pass
+ e = Executor()
+ e.targets = self.targets
+ return e
+
class OtherError(Exception):
pass
diff --git a/src/engine/SCons/Tool/bcc32.py b/src/engine/SCons/Tool/bcc32.py
index 826373f5..86ca0764 100644
--- a/src/engine/SCons/Tool/bcc32.py
+++ b/src/engine/SCons/Tool/bcc32.py
@@ -63,10 +63,12 @@ def generate(env):
env['CC'] = 'bcc32'
env['CCFLAGS'] = SCons.Util.CLVar('')
- env['CCCOM'] = '$CC -q $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES'
+ env['CFLAGS'] = SCons.Util.CLVar('')
+ env['CCCOM'] = '$CC -q $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES'
env['SHCC'] = '$CC'
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
- env['SHCCCOM'] = '$SHCC -WD $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES'
+ env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
+ env['SHCCCOM'] = '$SHCC -WD $SHCFLAGS $SHCCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o$TARGET $SOURCES'
env['CPPDEFPREFIX'] = '-D'
env['CPPDEFSUFFIX'] = ''
env['INCPREFIX'] = '-I'
diff --git a/src/engine/SCons/Tool/cc.py b/src/engine/SCons/Tool/cc.py
index c4114b73..62b945f6 100644
--- a/src/engine/SCons/Tool/cc.py
+++ b/src/engine/SCons/Tool/cc.py
@@ -63,10 +63,12 @@ def generate(env):
env['CC'] = 'cc'
env['CCFLAGS'] = SCons.Util.CLVar('')
- env['CCCOM'] = '$CC -o $TARGET -c $CCFLAGS $_CCCOMCOM $SOURCES'
+ 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['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCCFLAGS $_CCCOMCOM $SOURCES'
+ env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
+ env['SHCCCOM'] = '$SHCC -o $TARGET -c $SHCFLAGS $SHCCFLAGS $_CCCOMCOM $SOURCES'
env['CPPDEFPREFIX'] = '-D'
env['CPPDEFSUFFIX'] = ''
diff --git a/src/engine/SCons/Tool/cc.xml b/src/engine/SCons/Tool/cc.xml
index 84980db1..0ebaf146 100644
--- a/src/engine/SCons/Tool/cc.xml
+++ b/src/engine/SCons/Tool/cc.xml
@@ -18,9 +18,10 @@ The C compiler.
<cvar name="CCCOM">
<summary>
-The command line used to compile a C source file to a (static) object file.
-Any options specified in the &cv-CCFLAGS; and &cv-CPPFLAGS; construction variables
-are included on this command line.
+The command line used to compile a C source file to a (static) object
+file. Any options specified in the &cv-CFLAGS;, &cv-CCFLAGS; and
+&cv-CPPFLAGS; construction variables are included on this command
+line.
</summary>
</cvar>
@@ -38,7 +39,13 @@ env = Environment(CCCOMSTR = "Compiling static object $TARGET")
<cvar name="CCFLAGS">
<summary>
-General options that are passed to the C compiler.
+General options that are passed to the C and C++ compilers.
+</summary>
+</cvar>
+
+<cvar name="CFLAGS">
+<summary>
+General options that are passed to the C compiler (C only; not C++).
</summary>
</cvar>
@@ -92,7 +99,7 @@ The C compiler used for generating shared-library objects.
<summary>
The command line used to compile a C source file
to a shared-library object file.
-Any options specified in the &cv-SHCCFLAGS; and &cv-CPPFLAGS; construction variables
+Any options specified in the &cv-SHCFLAGS;, &cv-SHCCFLAGS; and &cv-CPPFLAGS; construction variables
are included on this command line.
</summary>
</cvar>
@@ -111,7 +118,14 @@ env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET")
<cvar name="SHCCFLAGS">
<summary>
-Options that are passed to the C compiler
+Options that are passed to the C and C++ compilers
+to generate shared-library objects.
+</summary>
+</cvar>
+
+<cvar name="SHCFLAGS">
+<summary>
+Options that are passed to the C compiler (only; not C++)
to generate shared-library objects.
</summary>
</cvar>
diff --git a/src/engine/SCons/Tool/f90.py b/src/engine/SCons/Tool/f90.py
index 2e2b5b11..cb450b6d 100644
--- a/src/engine/SCons/Tool/f90.py
+++ b/src/engine/SCons/Tool/f90.py
@@ -119,6 +119,10 @@ def add_to_env(env):
def generate(env):
fortran.add_to_env(env)
+
+ import f77
+ f77.add_to_env(env)
+
add_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'f90'
diff --git a/src/engine/SCons/Tool/f95.py b/src/engine/SCons/Tool/f95.py
index 9cd2664c..7adc80b3 100644
--- a/src/engine/SCons/Tool/f95.py
+++ b/src/engine/SCons/Tool/f95.py
@@ -119,6 +119,13 @@ def add_to_env(env):
def generate(env):
fortran.add_to_env(env)
+
+ import f77
+ f77.add_to_env(env)
+
+ import f90
+ f90.add_to_env(env)
+
add_to_env(env)
env['_FORTRAND'] = env.Detect(compilers) or 'f95'
diff --git a/src/engine/SCons/Tool/icc.py b/src/engine/SCons/Tool/icc.py
index 20bf17ce..90dece72 100644
--- a/src/engine/SCons/Tool/icc.py
+++ b/src/engine/SCons/Tool/icc.py
@@ -40,7 +40,7 @@ def generate(env):
cc.generate(env)
env['CC'] = 'icc'
- env['CCCOM'] = '$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
+ env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
env['CXXCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET'
env['CPPDEFPREFIX'] = '/D'
env['CPPDEFSUFFIX'] = ''
diff --git a/src/engine/SCons/Tool/lex.py b/src/engine/SCons/Tool/lex.py
index 3331f6cc..31f21a9d 100644
--- a/src/engine/SCons/Tool/lex.py
+++ b/src/engine/SCons/Tool/lex.py
@@ -33,23 +33,60 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+import os.path
+
+import string
+
import SCons.Action
import SCons.Tool
import SCons.Util
LexAction = SCons.Action.Action("$LEXCOM", "$LEXCOMSTR")
+def lexEmitter(target, source, env):
+ sourceBase, sourceExt = os.path.splitext(SCons.Util.to_String(source[0]))
+
+ if sourceExt == ".lm": # If using Objective-C
+ target = [sourceBase + ".m"] # the extension is ".m".
+
+ # This emitter essentially tries to add to the target all extra
+ # files generated by flex.
+
+ # Different options that are used to trigger the creation of extra files.
+ fileGenOptions = ["--header-file=", "--tables-file="]
+
+ for option in SCons.Util.CLVar(env.subst("$LEXFLAGS")):
+ for fileGenOption in fileGenOptions:
+ l = len(fileGenOption)
+ if option[:l] == fileGenOption:
+ # A file generating option is present, so add the
+ # file name to the target list.
+ fileName = string.strip(option[l:])
+ target.append(fileName)
+ return (target, source)
+
def generate(env):
"""Add Builders and construction variables for lex to an Environment."""
c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
- c_file.add_action('.l', LexAction)
- c_file.add_action('.lex', LexAction)
- cxx_file.add_action('.ll', LexAction)
+ # C
+ c_file.add_action(".l", LexAction)
+ c_file.add_emitter(".l", lexEmitter)
+
+ c_file.add_action(".lex", LexAction)
+ c_file.add_emitter(".lex", lexEmitter)
+
+ # Objective-C
+ cxx_file.add_action(".lm", LexAction)
+ cxx_file.add_emitter(".lm", lexEmitter)
+
+ # C++
+ cxx_file.add_action(".ll", LexAction)
+ cxx_file.add_emitter(".ll", lexEmitter)
+
+ env["LEX"] = env.Detect("flex") or "lex"
+ env["LEXFLAGS"] = SCons.Util.CLVar("")
+ env["LEXCOM"] = "$LEX $LEXFLAGS -t $SOURCES > $TARGET"
- env['LEX'] = env.Detect('flex') or 'lex'
- env['LEXFLAGS'] = SCons.Util.CLVar('')
- env['LEXCOM'] = '$LEX $LEXFLAGS -t $SOURCES > $TARGET'
-
def exists(env):
- return env.Detect(['flex', 'lex'])
+ return env.Detect(["flex", "lex"])
diff --git a/src/engine/SCons/Tool/mingw.py b/src/engine/SCons/Tool/mingw.py
index d679b53e..0639535c 100644
--- a/src/engine/SCons/Tool/mingw.py
+++ b/src/engine/SCons/Tool/mingw.py
@@ -145,7 +145,7 @@ def generate(env):
env['RCINCFLAGS'] = '$( ${_concat(RCINCPREFIX, CPPPATH, RCINCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)'
env['RCINCPREFIX'] = '--include-dir '
env['RCINCSUFFIX'] = ''
- env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX}${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET'
+ env['RCCOM'] = '$RC $_CPPDEFFLAGS $RCINCFLAGS ${RCINCPREFIX} ${SOURCE.dir} $RCFLAGS -i $SOURCE -o $TARGET'
env['BUILDERS']['RES'] = res_builder
# Some setting from the platform also have to be overridden:
diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py
index 80b59263..7e476f57 100644
--- a/src/engine/SCons/Tool/msvc.py
+++ b/src/engine/SCons/Tool/msvc.py
@@ -687,10 +687,12 @@ def generate(env):
env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
env['CC'] = 'cl'
env['CCFLAGS'] = SCons.Util.CLVar('/nologo')
- env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
+ env['CFLAGS'] = SCons.Util.CLVar('')
+ env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS'
env['SHCC'] = '$CC'
env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
- env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
+ env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
+ env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS'
env['CXX'] = '$CC'
env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
diff --git a/src/engine/SCons/Tool/mwcc.py b/src/engine/SCons/Tool/mwcc.py
index 52f55ee1..a1ede44a 100644
--- a/src/engine/SCons/Tool/mwcc.py
+++ b/src/engine/SCons/Tool/mwcc.py
@@ -171,14 +171,15 @@ def generate(env):
env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -nolink -o $TARGET $SOURCES'
env['CC'] = 'mwcc'
- env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
+ env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS'
env['CXX'] = 'mwcc'
env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
env['SHCC'] = '$CC'
env['SHCCFLAGS'] = '$CCFLAGS'
- env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
+ env['SHCFLAGS'] = '$CFLAGS'
+ env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS'
env['SHCXX'] = '$CXX'
env['SHCXXFLAGS'] = '$CXXFLAGS'
diff --git a/src/engine/SCons/Tool/qt.py b/src/engine/SCons/Tool/qt.py
index 5077901d..4d290c77 100644
--- a/src/engine/SCons/Tool/qt.py
+++ b/src/engine/SCons/Tool/qt.py
@@ -313,8 +313,8 @@ def generate(env):
env['BUILDERS']['Uic'] = uicBld
env['BUILDERS']['Moc'] = mocBld
static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
- static_obj.src_builder.append('Uic')
- shared_obj.src_builder.append('Uic')
+ static_obj.add_src_builder('Uic')
+ shared_obj.add_src_builder('Uic')
# We use the emitters of Program / StaticLibrary / SharedLibrary
# to scan for moc'able files
diff --git a/src/engine/SCons/Tool/yacc.py b/src/engine/SCons/Tool/yacc.py
index b8916ae6..cbccb294 100644
--- a/src/engine/SCons/Tool/yacc.py
+++ b/src/engine/SCons/Tool/yacc.py
@@ -34,6 +34,7 @@ selection method.
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os.path
+import string
import SCons.Defaults
import SCons.Tool
@@ -42,31 +43,64 @@ import SCons.Util
YaccAction = SCons.Action.Action("$YACCCOM", "$YACCCOMSTR")
def _yaccEmitter(target, source, env, ysuf, hsuf):
+ flags = SCons.Util.CLVar(env.subst("$YACCFLAGS"))
+ targetBase, targetExt = os.path.splitext(SCons.Util.to_String(target[0]))
+
+ if '.ym' in ysuf: # If using Objective-C
+ target = [targetBase + ".m"] # the extension is ".m".
+
+
# If -d is specified on the command line, yacc will emit a .h
- # or .hpp file as well as a .c or .cpp file, depending on whether
- # the input file is a .y or .yy, respectively.
- if len(source) and '-d' in SCons.Util.CLVar(env.subst("$YACCFLAGS")):
+ # or .hpp file with the same name as the .c or .cpp output file.
+ if '-d' in flags:
+ target.append(targetBase + env.subst(hsuf))
+
+ # If -g is specified on the command line, yacc will emit a .vcg
+ # file with the same base name as the .y, .yacc, .ym or .yy file.
+ if "-g" in flags:
base, ext = os.path.splitext(SCons.Util.to_String(source[0]))
- if ext in ysuf:
- base, ext = os.path.splitext(SCons.Util.to_String(target[0]))
- target.append(base + env.subst(hsuf))
+ target.append(base + env.subst("$YACCVCGFILESUFFIX"))
+
+ # With --defines and --graph, the name of the file is totally defined
+ # in the options.
+ fileGenOptions = ["--defines=", "--graph="]
+ for option in flags:
+ for fileGenOption in fileGenOptions:
+ l = len(fileGenOption)
+ if option[:l] == fileGenOption:
+ # A file generating option is present, so add the file
+ # name to the list of targets.
+ fileName = string.strip(option[l:])
+ target.append(fileName)
+
return (target, source)
def yEmitter(target, source, env):
return _yaccEmitter(target, source, env, ['.y', '.yacc'], '$YACCHFILESUFFIX')
+def ymEmitter(target, source, env):
+ return _yaccEmitter(target, source, env, ['.ym'], '$YACCHFILESUFFIX')
+
def yyEmitter(target, source, env):
return _yaccEmitter(target, source, env, ['.yy'], '$YACCHXXFILESUFFIX')
def generate(env):
"""Add Builders and construction variables for yacc to an Environment."""
c_file, cxx_file = SCons.Tool.createCFileBuilders(env)
-
+
+ # C
c_file.add_action('.y', YaccAction)
- c_file.add_action('.yacc', YaccAction)
- cxx_file.add_action('.yy', YaccAction)
c_file.add_emitter('.y', yEmitter)
+
+ c_file.add_action('.yacc', YaccAction)
c_file.add_emitter('.yacc', yEmitter)
+
+ # Objective-C
+ c_file.add_action('.ym', YaccAction)
+ c_file.add_emitter('.ym', ymEmitter)
+
+ # C++
+ cxx_file.add_action('.yy', YaccAction)
cxx_file.add_emitter('.yy', yyEmitter)
env['YACC'] = env.Detect('bison') or 'yacc'
@@ -74,6 +108,7 @@ def generate(env):
env['YACCCOM'] = '$YACC $YACCFLAGS -o $TARGET $SOURCES'
env['YACCHFILESUFFIX'] = '.h'
env['YACCHXXFILESUFFIX'] = '.hpp'
+ env['YACCVCGFILESUFFIX'] = '.vcg'
def exists(env):
return env.Detect(['bison', 'yacc'])
diff --git a/src/engine/SCons/Tool/yacc.xml b/src/engine/SCons/Tool/yacc.xml
index 48bb3236..8a23d0b1 100644
--- a/src/engine/SCons/Tool/yacc.xml
+++ b/src/engine/SCons/Tool/yacc.xml
@@ -79,3 +79,20 @@ The default value is
<filename>.hpp</filename>.
</summary>
</cvar>
+
+<cvar name="YACCVCGFILESUFFIX">
+<summary>
+The suffix of the file
+containing the VCG grammar automaton definition
+when the
+<option>--graph=</option>
+option is used.
+Note that setting this variable does not cause
+the parser generator to generate a VCG
+file with the specified suffix,
+it exists to allow you to specify
+what suffix the parser generator will use of its own accord.
+The default value is
+<filename>.vcg</filename>.
+</summary>
+</cvar>
diff --git a/src/engine/SCons/Warnings.py b/src/engine/SCons/Warnings.py
index 27614bf9..1b13c96a 100644
--- a/src/engine/SCons/Warnings.py
+++ b/src/engine/SCons/Warnings.py
@@ -54,6 +54,9 @@ class DuplicateEnvironmentWarning(Warning):
class MissingSConscriptWarning(Warning):
pass
+class NoMD5ModuleWarning(Warning):
+ pass
+
class NoMetaclassSupportWarning(Warning):
pass
diff --git a/src/script/scons-time.py b/src/script/scons-time.py
index 1867d44f..b0feaab2 100644
--- a/src/script/scons-time.py
+++ b/src/script/scons-time.py
@@ -31,6 +31,8 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
import getopt
import glob
import os
diff --git a/src/test_copyrights.py b/src/test_copyrights.py
index ce78a8bc..2bc951ed 100644
--- a/src/test_copyrights.py
+++ b/src/test_copyrights.py
@@ -25,129 +25,188 @@
__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
"""
-Verify that we have proper Copyright notices on all the right files
-in our distributions.
+Verify that we have proper strings like Copyright notices on all the
+right files in our distributions.
-Note that this is a packaging test, not a functional test, so the
-name of this script doesn't end in *Tests.py.
+Note that this is a source file and packaging test, not a functional test,
+so the name of this script doesn't end in *Tests.py.
"""
+import fnmatch
import os
import os.path
import re
import string
+import TestCmd
import TestSCons
-test = TestSCons.TestSCons()
+# Use TestCmd, not TestSCons, so we don't chdir to a temporary directory.
+test = TestCmd.TestCmd()
-try:
- cwd = os.environ['SCONS_CWD']
-except KeyError:
- cwd = os.getcwd()
+scons_version = TestSCons.SConsVersion
def build_path(*args):
- return apply(os.path.join, (cwd, 'build',)+args)
+ return apply(os.path.join, ('build',)+args)
build_scons = build_path('scons')
-build_local = build_path('scons-local', 'scons-local-'+test.scons_version)
+build_local = build_path('scons-local', 'scons-local-'+scons_version)
build_src = build_path('scons-src')
-class Collect:
- expression = re.compile('Copyright.*The SCons Foundation')
- def __init__(self, directory, remove_list):
- self.copyright = []
- self.no_copyright = []
- self.remove = {}
+class Checker:
+ def __init__(self, directory, search_list = [], remove_list=[]):
+ self.directory = directory
+ self.search_list = search_list
+ self.remove_dict = {}
for r in remove_list:
- self.remove[os.path.join(directory, r)] = 1
-
-def visit(collect, dirname, names):
- make_path_tuple = lambda n, d=dirname: (n, os.path.join(d, n))
- for name, path in map(make_path_tuple, names):
- if collect.remove.get(path):
- names.remove(name)
- elif os.path.isfile(path):
- if collect.expression.search(open(path, 'r').read()):
- collect.copyright.append(path)
- else:
- collect.no_copyright.append(path)
-
-# Map each directory to search (dictionary keys) to a list of its
-# subsidiary files and directories to exclude from copyright checks.
-check = {
- build_scons : [
- 'build',
- 'build-stamp',
- 'configure-stamp',
- 'debian',
- 'dist',
- 'engine/SCons/Conftest.py',
- 'engine/SCons/dblite.py',
- 'engine/SCons/Optik',
- 'MANIFEST',
- 'os_spawnv_fix.diff',
- 'setup.cfg',
- ],
- build_local : [
- 'SCons/Conftest.py',
- 'SCons/dblite.py',
- 'SCons/Optik',
- ],
- build_src : [
- 'bin',
- 'config',
- 'debian',
- 'doc/design',
- 'doc/MANIFEST',
- 'doc/python10',
- 'doc/reference',
- 'doc/man/MANIFEST',
- 'doc/user/cons.pl',
- 'doc/user/MANIFEST',
- 'doc/user/SCons-win32-install-1.jpg',
- 'doc/user/SCons-win32-install-2.jpg',
- 'doc/user/SCons-win32-install-3.jpg',
- 'doc/user/SCons-win32-install-4.jpg',
- 'gentoo',
- 'QMTest/classes.qmc',
- 'QMTest/configuration',
- 'QMTest/TestCmd.py',
- 'QMTest/TestCommon.py',
- 'QMTest/unittest.py',
- 'src/os_spawnv_fix.diff',
- 'src/MANIFEST.in',
- 'src/setup.cfg',
- 'src/engine/MANIFEST.in',
- 'src/engine/MANIFEST-xml.in',
- 'src/engine/setup.cfg',
- 'src/engine/SCons/Conftest.py',
- 'src/engine/SCons/dblite.py',
- 'src/engine/SCons/Optik',
- 'src/script/MANIFEST.in',
- 'src/script/setup.cfg',
- ],
-}
-
-no_copyright = []
-no_result = []
-
-for directory, remove_list in check.items():
- if os.path.exists(directory):
- c = Collect(directory, remove_list)
- os.path.walk(directory, visit, c)
- no_copyright.extend(c.no_copyright)
- else:
- no_result.append(directory)
-
-if no_copyright:
- print "Found the following files with no copyrights:"
- print "\t" + string.join(no_copyright, "\n\t")
+ self.remove_dict[os.path.join(directory, r)] = 1
+
+ def directory_exists(self):
+ return os.path.exists(self.directory)
+
+ def remove_path(self, path):
+ return self.remove_dict.get(path)
+
+ def search_this(self, path):
+ if self.search_list:
+ for pattern in self.search_list:
+ if fnmatch.fnmatch(path, pattern):
+ return 1
+ return None
+ else:
+ return os.path.isfile(path)
+
+ def visit(self, result, dirname, names):
+ make_path_tuple = lambda n, d=dirname: (n, os.path.join(d, n))
+ for name, path in map(make_path_tuple, names):
+ if self.remove_path(path):
+ names.remove(name)
+ elif self.search_this(path):
+ body = open(path, 'r').read()
+ for expr in self.expressions:
+ if not expr.search(body):
+ msg = '%s: missing %s' % (path, repr(expr.pattern))
+ result.append(msg)
+
+ def find_missing(self):
+ result = []
+ os.path.walk(self.directory, self.visit, result)
+ return result
+
+class CheckUnexpandedStrings(Checker):
+ expressions = [
+ re.compile('__COPYRIGHT__'),
+ re.compile('__FILE__ __REVISION__ __DATE__ __DEVELOPER__'),
+ ]
+ def must_be_built(self):
+ return None
+
+class CheckExpandedCopyright(Checker):
+ expressions = [
+ re.compile('Copyright.*The SCons Foundation'),
+ ]
+ def must_be_built(self):
+ return 1
+
+check_list = [
+
+ CheckUnexpandedStrings(
+ 'src',
+ search_list = [ '*.py' ],
+ remove_list = [
+ 'engine/SCons/Conftest.py',
+ 'engine/SCons/dblite.py',
+ 'engine/SCons/Optik',
+ ],
+ ),
+
+ CheckUnexpandedStrings(
+ 'test',
+ search_list = [ '*.py' ],
+ ),
+
+ CheckExpandedCopyright(
+ build_scons,
+ remove_list = [
+ 'build',
+ 'build-stamp',
+ 'configure-stamp',
+ 'debian',
+ 'dist',
+ 'engine/SCons/Conftest.py',
+ 'engine/SCons/dblite.py',
+ 'engine/SCons/Optik',
+ 'MANIFEST',
+ 'os_spawnv_fix.diff',
+ 'setup.cfg',
+ ],
+ ),
+
+ CheckExpandedCopyright(
+ build_local,
+ remove_list = [
+ 'SCons/Conftest.py',
+ 'SCons/dblite.py',
+ 'SCons/Optik',
+ ],
+ ),
+
+ CheckExpandedCopyright(
+ build_src,
+ remove_list = [
+ 'bin',
+ 'config',
+ 'debian',
+ 'doc/design',
+ 'doc/MANIFEST',
+ 'doc/python10',
+ 'doc/reference',
+ 'doc/man/MANIFEST',
+ 'doc/user/cons.pl',
+ 'doc/user/MANIFEST',
+ 'doc/user/SCons-win32-install-1.jpg',
+ 'doc/user/SCons-win32-install-2.jpg',
+ 'doc/user/SCons-win32-install-3.jpg',
+ 'doc/user/SCons-win32-install-4.jpg',
+ 'gentoo',
+ 'QMTest/classes.qmc',
+ 'QMTest/configuration',
+ 'QMTest/TestCmd.py',
+ 'QMTest/TestCommon.py',
+ 'QMTest/unittest.py',
+ 'src/os_spawnv_fix.diff',
+ 'src/MANIFEST.in',
+ 'src/setup.cfg',
+ 'src/engine/MANIFEST.in',
+ 'src/engine/MANIFEST-xml.in',
+ 'src/engine/setup.cfg',
+ 'src/engine/SCons/Conftest.py',
+ 'src/engine/SCons/dblite.py',
+ 'src/engine/SCons/Optik',
+ 'src/script/MANIFEST.in',
+ 'src/script/setup.cfg',
+ ],
+ ),
+
+]
+
+missing_strings = []
+not_built = []
+
+for collector in check_list:
+ if collector.directory_exists():
+ missing_strings.extend(collector.find_missing())
+ elif collector.must_be_built():
+ not_built.append(collector.directory)
+
+if missing_strings:
+ print "Found the following files with missing strings:"
+ print "\t" + string.join(missing_strings, "\n\t")
test.fail_test(1)
-if no_result:
- print "Cannot check copyrights, the following have apparently not been built:"
- print "\t" + string.join(no_result, "\n\t")
+if not_built:
+ print "Cannot check all strings, the following have apparently not been built:"
+ print "\t" + string.join(not_built, "\n\t")
test.no_result(1)
test.pass_test()
diff --git a/test/Builder/multi/different-actions.py b/test/Builder/multi/different-actions.py
new file mode 100644
index 00000000..ed1c6766
--- /dev/null
+++ b/test/Builder/multi/different-actions.py
@@ -0,0 +1,60 @@
+#!/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 environments with actions that have different signatures
+generate an error.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=Action(build, varlist=['XXX']), multi=1)
+env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo')
+env2 = env.Clone(XXX = 'var')
+env.B(target = 'file6.out', source = 'file6a.in')
+env2.B(target = 'file6.out', source = 'file6b.in')
+""")
+
+test.write('file6a.in', 'file6a.in\n')
+test.write('file6b.in', 'file6b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Two environments with different actions were specified for the same target: file6.out
+""") + TestSCons.file_expr
+
+test.pass_test()
+
+test.run(arguments='file6.out', status=2, stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/different-environments.py b/test/Builder/multi/different-environments.py
new file mode 100644
index 00000000..9d1dcc89
--- /dev/null
+++ b/test/Builder/multi/different-environments.py
@@ -0,0 +1,64 @@
+#!/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 warning is generated if the calls have different overrides
+but the overrides don't appear to affect the build operation.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+_python_ = TestSCons._python_
+
+test.write('build.py', r"""#!/usr/bin/env python
+import sys
+def build(num, target, source):
+ file = open(str(target), 'wb')
+ file.write('%s\n'%num)
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+build(sys.argv[1],sys.argv[2],sys.argv[3:])
+""")
+
+test.write('SConstruct', """\
+B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'file03.out', source = 'file03a.in', foo=1)
+env.B(target = 'file03.out', source = 'file03b.in', foo=2)
+""" % locals())
+
+test.write('file03a.in', 'file03a.in\n')
+test.write('file03b.in', 'file03b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Two environments with different actions were specified for the same target: file03.out
+""") + TestSCons.file_expr
+
+test.run(arguments='file03.out', status=2, stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/different-multi.py b/test/Builder/multi/different-multi.py
new file mode 100644
index 00000000..1f0ef2a1
--- /dev/null
+++ b/test/Builder/multi/different-multi.py
@@ -0,0 +1,64 @@
+#!/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 trying to call a target with two different "multi" builders
+generates an error.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+def build2(env, target, source):
+ build(env, target, source)
+
+# Put the names on the Builder objects and in the environment so
+# the error output should be consistent regardless of Python version
+# or how we mess with the Builder internals.
+B = Builder(action=build, multi=1, name='B')
+C = Builder(action=build2, multi=1, name='C')
+env = Environment(BUILDERS = { 'B' : B, 'C' : C })
+env.B(target = 'file8.out', source = 'file8.in')
+env.C(target = 'file8.out', source = 'file8.in')
+""")
+
+test.write('file8a.in', 'file8a.in\n')
+test.write('file8b.in', 'file8b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Two different builders (B and C) were specified for the same target: file8.out
+""") + TestSCons.file_expr
+
+test.run(arguments='file8.out', status=2, stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/different-order.py b/test/Builder/multi/different-order.py
new file mode 100644
index 00000000..f4a64435
--- /dev/null
+++ b/test/Builder/multi/different-order.py
@@ -0,0 +1,59 @@
+#!/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 "multi" builder can NOT be called multiple times with
+target lists that have different orders. This is intentional; the
+order of the targets matter to the builder because the build command
+can contain things like ${TARGET[0]}.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ for t in target:
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = ['file10a.out', 'file10b.out'], source = 'file10.in')
+env.B(target = ['file10b.out', 'file10a.out'], source = 'file10.in')
+""")
+
+test.write('file10.in', 'file10.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Two different target lists have a target in common: file10b.out (from ['file10a.out', 'file10b.out'] and from ['file10b.out', 'file10a.out'])
+""") + TestSCons.file_expr
+
+test.run(arguments='file10.out', status=2, stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/different-overrides.py b/test/Builder/multi/different-overrides.py
new file mode 100644
index 00000000..5420a49c
--- /dev/null
+++ b/test/Builder/multi/different-overrides.py
@@ -0,0 +1,58 @@
+#!/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 warning is generated if the calls have different overrides
+but the overrides don't appear to affect the build operation.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'file3.out', source = 'file3a.in', foo=1)
+env.B(target = 'file3.out', source = 'file3b.in', foo=2)
+""")
+
+test.write('file3a.in', 'file3a.in\n')
+test.write('file3b.in', 'file3b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: warning: Two different environments were specified for target file3.out,
+\tbut they appear to have the same action: build(target, source, env)
+""") + TestSCons.file_expr
+
+test.run(arguments='file3.out', stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/different-target-lists.py b/test/Builder/multi/different-target-lists.py
new file mode 100644
index 00000000..58221993
--- /dev/null
+++ b/test/Builder/multi/different-target-lists.py
@@ -0,0 +1,64 @@
+#!/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 target file can't be in two different target lists.
+"""
+
+# XXX It would be nice if the following two tests could be made to work
+# by executing the action once for each unique set of targets. This
+# would make it simple to deal with PDB files on Windows like so:
+#
+# env.Object(['foo.obj', 'vc60.pdb'], 'foo.c')
+# env.Object(['bar.obj', 'vc60.pdb'], 'bar.c')
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ for t in target:
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = ['file11a.out', 'file11b.out'], source = 'file11a.in')
+env.B(target = ['file11b.out', 'file11c.out'], source = 'file11b.in')
+""")
+
+test.write('file11a.in', 'file11a.in\n')
+test.write('file11b.in', 'file11b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Two different target lists have a target in common: file11b.out (from ['file11a.out', 'file11b.out'] and from ['file11b.out', 'file11c.out'])
+""") + TestSCons.file_expr
+
+test.run(arguments='file11.out', status=2, stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/error.py b/test/Builder/multi/error.py
new file mode 100644
index 00000000..4201cfc5
--- /dev/null
+++ b/test/Builder/multi/error.py
@@ -0,0 +1,57 @@
+#!/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 builder with "multi" not set generates an error on the
+second call.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=0)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'file2.out', source = 'file2a.in')
+env.B(target = 'file2.out', source = 'file2b.in')
+""")
+
+test.write('file2a.in', 'file2a.in\n')
+test.write('file2b.in', 'file2b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Multiple ways to build the same target were specified for: file2.out (from ['file2a.in'] and from ['file2b.in'])
+""") + TestSCons.file_expr
+
+test.run(arguments='file2.out', status=2, stderr=expect)
+
+test.pass_test()
diff --git a/test/Builder/multi/lone-target-list.py b/test/Builder/multi/lone-target-list.py
new file mode 100644
index 00000000..a305b544
--- /dev/null
+++ b/test/Builder/multi/lone-target-list.py
@@ -0,0 +1,59 @@
+#!/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 target file can't be a lone target and in a list.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ for t in target:
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = ['file12a.out', 'file12b.out'], source = 'file12a.in')
+env.B(target = 'file12a.out', source = 'file12b.in')
+""")
+
+test.write('file12a.in', 'file12a.in\n')
+test.write('file12b.in', 'file12b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: *** Two different target lists have a target in common: file12a.out (from ['file12a.out', 'file12b.out'] and from ['file12a.out'])
+""") + TestSCons.file_expr
+
+test.run(arguments='file12.out', status=2, stderr=expect)
+
+
+
+test.pass_test()
diff --git a/test/Builder/multi/multi.py b/test/Builder/multi/multi.py
new file mode 100644
index 00000000..315599b3
--- /dev/null
+++ b/test/Builder/multi/multi.py
@@ -0,0 +1,55 @@
+#!/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 builder with "multi" set can be called multiple times
+and the source files are added to the list.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'file1.out', source = 'file1a.in')
+env.B(target = 'file1.out', source = 'file1b.in')
+""")
+
+test.write('file1a.in', 'file1a.in\n')
+test.write('file1b.in', 'file1b.in\n')
+
+test.run(arguments='file1.out')
+
+test.must_match('file1.out', "file1a.in\nfile1b.in\n")
+
+test.pass_test()
diff --git a/test/Builder/multi/same-actions.py b/test/Builder/multi/same-actions.py
new file mode 100644
index 00000000..afccb652
--- /dev/null
+++ b/test/Builder/multi/same-actions.py
@@ -0,0 +1,61 @@
+#!/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 two different environments can be used for the same target,
+so long as the actions have the same signature; a warning is generated.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env2 = env.Clone(DIFFERENT_VARIABLE = 'true')
+env.B(target = 'file5.out', source = 'file5a.in')
+env2.B(target = 'file5.out', source = 'file5b.in')
+""")
+
+test.write('file5a.in', 'file5a.in\n')
+test.write('file5b.in', 'file5b.in\n')
+
+expect = TestSCons.re_escape("""
+scons: warning: Two different environments were specified for target file5.out,
+\tbut they appear to have the same action: build(target, source, env)
+""") + TestSCons.file_expr
+
+test.run(arguments='file5.out', stderr=expect)
+
+test.must_match('file5.out', "file5a.in\nfile5b.in\n")
+
+test.pass_test()
diff --git a/test/Builder/multi/same-overrides.py b/test/Builder/multi/same-overrides.py
new file mode 100644
index 00000000..d12af218
--- /dev/null
+++ b/test/Builder/multi/same-overrides.py
@@ -0,0 +1,71 @@
+#!/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 everything works if two multi calls have the same overrides.
+"""
+
+import string
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+_python_ = TestSCons._python_
+
+test.write('build.py', r"""#!/usr/bin/env python
+import sys
+def build(num, target, source):
+ file = open(str(target), 'wb')
+ file.write('%s\n'%num)
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+build(sys.argv[1],sys.argv[2],sys.argv[3:])
+""")
+
+test.write('SConstruct', """\
+B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'file4.out', source = 'file4a.in', foo=3)
+env.B(target = 'file4.out', source = 'file4b.in', foo=3)
+""" % locals())
+
+test.write('file4a.in', 'file4a.in\n')
+test.write('file4b.in', 'file4b.in\n')
+
+python_expr = string.replace(TestSCons.python, '\\', '\\\\')
+act = TestSCons.re_escape('"%s" build.py \$foo \$TARGET \$SOURCES' % python_expr)
+
+expect = ("""
+scons: warning: Two different environments were specified for target file4.out,
+\tbut they appear to have the same action: %s
+""" % act) + TestSCons.file_expr
+
+test.run(arguments='file4.out', stderr=expect)
+
+test.must_match('file4.out', "3\nfile4a.in\nfile4b.in\n")
+
+test.pass_test()
diff --git a/test/Builder/multi/same-targets.py b/test/Builder/multi/same-targets.py
new file mode 100644
index 00000000..19efff76
--- /dev/null
+++ b/test/Builder/multi/same-targets.py
@@ -0,0 +1,57 @@
+#!/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 "multi" builder can be called multiple times with the same
+target list if everything is identical.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """\
+def build(env, target, source):
+ for t in target:
+ file = open(str(t), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=1)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = ['file9a.out', 'file9b.out'], source = 'file9a.in')
+env.B(target = ['file9a.out', 'file9b.out'], source = 'file9b.in')
+""")
+
+test.write('file9a.in', 'file9a.in\n')
+test.write('file9b.in', 'file9b.in\n')
+
+test.run(arguments='file9b.out')
+
+test.must_match('file9a.out', "file9a.in\nfile9b.in\n")
+test.must_match('file9b.out', "file9a.in\nfile9b.in\n")
+
+test.pass_test()
diff --git a/test/Builder/non-multi.py b/test/Builder/non-multi.py
new file mode 100644
index 00000000..1d5822ef
--- /dev/null
+++ b/test/Builder/non-multi.py
@@ -0,0 +1,54 @@
+#!/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 builder without "multi" set can still be called multiple
+times if the calls are the same.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match=TestSCons.match_re)
+
+test.write('SConstruct', """
+def build(env, target, source):
+ file = open(str(target[0]), 'wb')
+ for s in source:
+ file.write(open(str(s), 'rb').read())
+
+B = Builder(action=build, multi=0)
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'file7.out', source = 'file7.in')
+env.B(target = 'file7.out', source = 'file7.in')
+""")
+
+test.write('file7.in', 'file7.in\n')
+
+test.run(arguments='file7.out')
+
+test.must_match('file7.out', "file7.in\n")
+
+test.pass_test()
diff --git a/test/CC/CFLAGS.py b/test/CC/CFLAGS.py
new file mode 100644
index 00000000..f14fcc54
--- /dev/null
+++ b/test/CC/CFLAGS.py
@@ -0,0 +1,113 @@
+#!/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 sys, string
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+# Make sure CFLAGS is not passed to CXX by just expanding CXXCOM
+test.write('SConstruct', """
+env = Environment(CFLAGS='-xyz', CCFLAGS='-abc')
+print env.subst('$CXXCOM')
+print env.subst('$CXXCOMSTR')
+print env.subst('$SHCXXCOM')
+print env.subst('$SHCXXCOMSTR')
+""")
+test.run(arguments = '.')
+test.fail_test(string.find(test.stdout(), "-xyz") != -1)
+test.fail_test(string.find(test.stdout(), "-abc") == -1)
+
+
+# Test passing CFLAGS to C compiler by actually compiling programs
+if sys.platform == 'win32':
+ _obj = '.obj'
+ fooflags = '/nologo -DFOO'
+ barflags = '/nologo -DBAR'
+else:
+ _obj = '.o'
+ fooflags = '-DFOO'
+ barflags = '-DBAR'
+
+
+test.write('SConstruct', """
+foo = Environment(CFLAGS = '%s')
+bar = Environment(CFLAGS = '%s')
+foo.Object(target = 'foo%s', source = 'prog.c')
+bar.Object(target = 'bar%s', source = 'prog.c')
+foo.Program(target = 'foo', source = 'foo%s')
+bar.Program(target = 'bar', source = 'bar%s')
+foo.Program(target = 'prog', source = 'prog.c',
+ CFLAGS = '$CFLAGS -DBAR $BAZ', BAZ = '-DBAZ')
+""" % (fooflags, barflags, _obj, _obj, _obj, _obj))
+
+test.write('prog.c', r"""
+#include <stdio.h>
+#include <stdlib.h>
+
+int
+main(int argc, char *argv[])
+{
+ argv[argc++] = "--";
+#ifdef FOO
+ printf("prog.c: FOO\n");
+#endif
+#ifdef BAR
+ printf("prog.c: BAR\n");
+#endif
+#ifdef BAZ
+ printf("prog.c: BAZ\n");
+#endif
+ exit (0);
+}
+""")
+
+
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('foo'), stdout = "prog.c: FOO\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+test.run(program = test.workpath('prog'), stdout = """\
+prog.c: FOO
+prog.c: BAR
+prog.c: BAZ
+""")
+
+test.write('SConstruct', """
+bar = Environment(CFLAGS = '%s')
+bar.Object(target = 'foo%s', source = 'prog.c')
+bar.Object(target = 'bar%s', source = 'prog.c')
+bar.Program(target = 'foo', source = 'foo%s')
+bar.Program(target = 'bar', source = 'bar%s')
+""" % (barflags, _obj, _obj, _obj, _obj))
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('foo'), stdout = "prog.c: BAR\n")
+test.run(program = test.workpath('bar'), stdout = "prog.c: BAR\n")
+
+test.pass_test()
diff --git a/test/CC/SHCFLAGS.py b/test/CC/SHCFLAGS.py
new file mode 100644
index 00000000..ef20120b
--- /dev/null
+++ b/test/CC/SHCFLAGS.py
@@ -0,0 +1,131 @@
+#!/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 sys
+import TestSCons
+import os
+import string
+
+test = TestSCons.TestSCons()
+
+e = test.Environment()
+fooflags = e['SHCFLAGS'] + ' -DFOO'
+barflags = e['SHCFLAGS'] + ' -DBAR'
+
+if os.name == 'posix':
+ os.environ['LD_LIBRARY_PATH'] = '.'
+if string.find(sys.platform, 'irix') > -1:
+ os.environ['LD_LIBRARYN32_PATH'] = '.'
+
+test.write('SConstruct', """
+foo = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1)
+bar = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1)
+
+foo_obj = foo.SharedObject(target = 'foo', source = 'prog.c')
+foo.SharedLibrary(target = 'foo', source = foo_obj)
+
+bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c')
+bar.SharedLibrary(target = 'bar', source = bar_obj)
+
+fooMain = foo.Clone(LIBS='foo', LIBPATH='.')
+foomain_obj = fooMain.Object(target='foomain', source='main.c')
+fooMain.Program(target='fooprog', source=foomain_obj)
+
+barMain = bar.Clone(LIBS='bar', LIBPATH='.')
+barmain_obj = barMain.Object(target='barmain', source='main.c')
+barMain.Program(target='barprog', source=barmain_obj)
+""" % (fooflags, barflags))
+
+test.write('foo.def', r"""
+LIBRARY "foo"
+DESCRIPTION "Foo Shared Library"
+
+EXPORTS
+ doIt
+""")
+
+test.write('bar.def', r"""
+LIBRARY "bar"
+DESCRIPTION "Bar Shared Library"
+
+EXPORTS
+ doIt
+""")
+
+test.write('prog.c', r"""
+#include <stdio.h>
+
+void
+doIt()
+{
+#ifdef FOO
+ printf("prog.c: FOO\n");
+#endif
+#ifdef BAR
+ printf("prog.c: BAR\n");
+#endif
+}
+""")
+
+test.write('main.c', r"""
+
+void doIt();
+
+int
+main(int argc, char* argv[])
+{
+ doIt();
+ return 0;
+}
+""")
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('fooprog'), stdout = "prog.c: FOO\n")
+test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n")
+
+test.write('SConstruct', """
+bar = Environment(SHCFLAGS = '%s', WINDOWS_INSERT_DEF=1)
+
+foo_obj = bar.SharedObject(target = 'foo', source = 'prog.c')
+bar.SharedLibrary(target = 'foo', source = foo_obj)
+
+bar_obj = bar.SharedObject(target = 'bar', source = 'prog.c')
+bar.SharedLibrary(target = 'bar', source = bar_obj)
+
+barMain = bar.Clone(LIBS='bar', LIBPATH='.')
+foomain_obj = barMain.Object(target='foomain', source='main.c')
+barmain_obj = barMain.Object(target='barmain', source='main.c')
+barMain.Program(target='barprog', source=foomain_obj)
+barMain.Program(target='fooprog', source=barmain_obj)
+""" % (barflags))
+
+test.run(arguments = '.')
+
+test.run(program = test.workpath('fooprog'), stdout = "prog.c: BAR\n")
+test.run(program = test.workpath('barprog'), stdout = "prog.c: BAR\n")
+
+test.pass_test()
diff --git a/test/CacheDir/up-to-date-q.py b/test/CacheDir/up-to-date-q.py
new file mode 100644
index 00000000..07123c90
--- /dev/null
+++ b/test/CacheDir/up-to-date-q.py
@@ -0,0 +1,82 @@
+#!/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 targets retrieved from CacheDir() are reported as
+up-to-date by the -q option.
+
+Thanks to dvitek for the test case.
+"""
+
+# Demonstrate a regression between 0.96.1 and 0.96.93.
+#
+# SCons would incorrectly believe files are stale if they were retrieved
+# from the cache in a previous invocation.
+#
+# What this script does:
+# 1. Set up two identical C project directories called 'alpha' and
+# 'beta', which use the same cache
+# 2. Invoke scons on 'alpha'
+# 3. Invoke scons on 'beta', which successfully draws output
+# files from the cache
+# 4. Invoke scons again, asserting (with -q) that 'beta' is up to date
+#
+# Step 4 failed in 0.96.93. In practice, this problem would lead to
+# lots of unecessary fetches from the cache during incremental
+# builds (because they behaved like non-incremental builds).
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.subdir('cache', 'alpha', 'beta')
+
+foo_c = """
+int main(){ return 0; }
+"""
+
+sconstruct = """
+import os
+CacheDir('%s')
+Program('foo', 'foo.c')
+""" % test.workpath('cache')
+
+test.write('alpha/foo.c', foo_c)
+test.write('alpha/SConstruct', sconstruct)
+
+test.write('beta/foo.c', foo_c)
+test.write('beta/SConstruct', sconstruct)
+
+# First build, populates the cache
+test.run(chdir = 'alpha', arguments = '.')
+
+# Second build, everything is a cache hit
+test.run(chdir = 'beta', arguments = '.')
+
+# Since we just built 'beta', it ought to be up to date.
+test.run(chdir = 'beta', arguments = '. -q')
+
+test.pass_test()
diff --git a/test/LEX/LEX.py b/test/LEX/LEX.py
index 3fd4db3d..91b4614d 100644
--- a/test/LEX/LEX.py
+++ b/test/LEX/LEX.py
@@ -50,89 +50,26 @@ sys.exit(0)
test.write('SConstruct', """
env = Environment(LEX = r'%(_python_)s mylex.py', tools=['default', 'lex'])
-env.Program(target = 'aaa', source = 'aaa.l')
-env.Program(target = 'bbb', source = 'bbb.lex')
+env.CFile(target = 'aaa', source = 'aaa.l')
+env.CFile(target = 'bbb', source = 'bbb.lex')
+env.CXXFile(target = 'ccc', source = 'ccc.ll')
+env.CXXFile(target = 'ddd', source = 'ddd.lm')
""" % locals())
-test.write('aaa.l', r"""
-int
-main(int argc, char *argv[])
-{
- argv[argc++] = "--";
- printf("LEX\n");
- printf("aaa.l\n");
- exit (0);
-}
-""")
-
-test.write('bbb.lex', r"""
-int
-main(int argc, char *argv[])
-{
- argv[argc++] = "--";
- printf("LEX\n");
- printf("bbb.lex\n");
- exit (0);
-}
-""")
+test.write('aaa.l', "aaa.l\nLEX\n")
+test.write('bbb.lex', "bbb.lex\nLEX\n")
+test.write('ccc.ll', "ccc.ll\nLEX\n")
+test.write('ddd.lm', "ddd.lm\nLEX\n")
test.run(arguments = '.', stderr = None)
-test.run(program = test.workpath('aaa' + _exe), stdout = "mylex.py\naaa.l\n")
-test.run(program = test.workpath('bbb' + _exe), stdout = "mylex.py\nbbb.lex\n")
-
-
-
-lex = test.where_is('lex')
-
-if lex:
-
- 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()
-lex = foo.Dictionary('LEX')
-bar = Environment(LEX = r'%(_python_)s wrapper.py ' + lex)
-foo.Program(target = 'foo', source = 'foo.l')
-bar.Program(target = 'bar', source = 'bar.l')
-""" % locals())
-
- lex = r"""
-%%%%
-a printf("A%sA");
-b printf("B%sB");
-%%%%
-int
-yywrap()
-{
- return 1;
-}
-
-main()
-{
- yylex();
-}
-"""
-
- test.write('foo.l', lex % ('foo.l', 'foo.l'))
-
- test.write('bar.l', lex % ('bar.l', 'bar.l'))
-
- test.run(arguments = 'foo' + _exe, stderr = None)
-
- test.fail_test(os.path.exists(test.workpath('wrapper.out')))
-
- test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n")
-
- test.run(arguments = 'bar' + _exe)
+# Read in with mode='r' because mylex.py implicitley wrote to stdout
+# with mode='w'.
+test.must_match('aaa.c', "aaa.l\nmylex.py\n", mode='r')
+test.must_match('bbb.c', "bbb.lex\nmylex.py\n", mode='r')
+test.must_match('ccc.cc', "ccc.ll\nmylex.py\n", mode='r')
+test.must_match('ddd.m', "ddd.lm\nmylex.py\n", mode='r')
- test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
- test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n")
test.pass_test()
diff --git a/test/LEX/LEXFLAGS.py b/test/LEX/LEXFLAGS.py
index 59239349..b6a06fa1 100644
--- a/test/LEX/LEXFLAGS.py
+++ b/test/LEX/LEXFLAGS.py
@@ -55,68 +55,17 @@ test.write('SConstruct', """
env = Environment(LEX = r'%(_python_)s mylex.py',
LEXFLAGS = '-x',
tools=['default', 'lex'])
-env.Program(target = 'aaa', source = 'aaa.l')
+env.CFile(target = 'aaa', source = 'aaa.l')
""" % locals())
-test.write('aaa.l', r"""
-int
-main(int argc, char *argv[])
-{
- argv[argc++] = "--";
- printf("LEXFLAGS\n");
- printf("aaa.l\n");
- exit (0);
-}
-""")
-
-test.run(arguments = 'aaa' + _exe, stderr = None)
-
-test.run(program = test.workpath('aaa' + _exe), stdout = " -x -t\naaa.l\n")
-
-
-
-lex = test.where_is('lex')
-
-if lex:
-
- test.write('SConstruct', """
-foo = Environment()
-bar = Environment(LEXFLAGS = '-b')
-foo.Program(target = 'foo', source = 'foo.l')
-bar.Program(target = 'bar', source = 'bar.l')
-""")
-
- lex = r"""
-%%%%
-a printf("A%sA");
-b printf("B%sB");
-%%%%
-int
-yywrap()
-{
- return 1;
-}
-
-main()
-{
- yylex();
-}
-"""
-
- test.write('foo.l', lex % ('foo.l', 'foo.l'))
-
- test.write('bar.l', lex % ('bar.l', 'bar.l'))
-
- test.run(arguments = 'foo' + _exe, stderr = None)
-
- test.fail_test(os.path.exists(test.workpath('lex.backup')))
+test.write('aaa.l', "aaa.l\nLEXFLAGS\n")
- test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n")
+test.run('.', stderr = None)
- test.run(arguments = 'bar' + _exe)
+# Read in with mode='r' because mylex.py implicitley wrote to stdout
+# with mode='w'.
+test.must_match('aaa.c', "aaa.l\n -x -t\n", mode='r')
- test.fail_test(not os.path.exists(test.workpath('lex.backup')))
- test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n")
test.pass_test()
diff --git a/test/LEX/live.py b/test/LEX/live.py
new file mode 100644
index 00000000..e917039a
--- /dev/null
+++ b/test/LEX/live.py
@@ -0,0 +1,103 @@
+#!/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 LEX and LEXFLAGS with a live lex.
+"""
+
+import string
+
+import TestSCons
+
+_exe = TestSCons._exe
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+lex = test.where_is('lex') or test.where_is('flex')
+
+if not lex:
+ test.skip_test('No lex or flex found; skipping test.\n')
+
+
+
+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()
+lex = foo.Dictionary('LEX')
+bar = Environment(LEX = r'%(_python_)s wrapper.py ' + lex,
+ LEXFLAGS = '-b')
+foo.Program(target = 'foo', source = 'foo.l')
+bar.Program(target = 'bar', source = 'bar.l')
+""" % locals())
+
+lex = r"""
+%%%%
+a printf("A%sA");
+b printf("B%sB");
+%%%%
+int
+yywrap()
+{
+ return 1;
+}
+
+main()
+{
+ yylex();
+}
+"""
+
+test.write('foo.l', lex % ('foo.l', 'foo.l'))
+
+test.write('bar.l', lex % ('bar.l', 'bar.l'))
+
+test.run(arguments = 'foo' + _exe, stderr = None)
+
+test.must_not_exist(test.workpath('wrapper.out'))
+test.must_not_exist(test.workpath('lex.backup'))
+
+test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "Afoo.lA\n")
+
+
+
+
+test.run(arguments = 'bar' + _exe)
+
+test.must_match(test.workpath('wrapper.out'), "wrapper.py\n")
+test.must_exist(test.workpath('lex.backup'))
+
+test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "Bbar.lB\n")
+
+
+
+test.pass_test()
diff --git a/test/TEX/build_dir.py b/test/TEX/build_dir.py
index 146f6e1b..49675336 100644
--- a/test/TEX/build_dir.py
+++ b/test/TEX/build_dir.py
@@ -35,13 +35,18 @@ import TestSCons
test = TestSCons.TestSCons()
+latex = test.where_is('latex')
+if not latex:
+ test.skip_test("Could not find 'latex'; skipping test.\n")
+
test.subdir(['docs'])
test.write(['SConstruct'], """\
import os
-env = Environment(ENV = { 'PATH' : os.environ['PATH'] })
+env = Environment(ENV = { 'PATH' : os.environ['PATH'] },
+ TOOLS = ['tex', 'latex', 'dvipdf'])
Export(['env'])
SConscript(os.path.join('docs', 'SConscript'),
diff --git a/test/TEX/subdir-input.py b/test/TEX/subdir-input.py
index b92ab732..d16ba5fc 100644
--- a/test/TEX/subdir-input.py
+++ b/test/TEX/subdir-input.py
@@ -38,11 +38,20 @@ import TestSCons
test = TestSCons.TestSCons()
+latex = test.where_is('latex')
+if not latex:
+ test.skip_test("Could not find 'latex'; skipping test.\n")
+
+pdflatex = test.where_is('pdflatex')
+if not pdflatex:
+ test.skip_test("Could not find 'pdflatex'; skipping test.\n")
+
test.subdir('sub')
test.write('SConstruct', """\
-PDF( 'sub/x.tex' )
-DVI( 'sub/x.tex' )
+env = Environment(TOOLS = ['tex', 'pdftex'])
+env.PDF( 'sub/x.tex' )
+env.DVI( 'sub/x.tex' )
""")
test.write(['sub', 'x.tex'],
diff --git a/test/YACC/YACC.py b/test/YACC/YACC.py
index d3bc679f..bef959f2 100644
--- a/test/YACC/YACC.py
+++ b/test/YACC/YACC.py
@@ -65,155 +65,25 @@ sys.exit(0)
test.write('SConstruct', """
env = Environment(YACC = r'%(_python_)s myyacc.py', tools=['default', 'yacc'])
-env.Program(target = 'aaa', source = 'aaa.y')
-env.Program(target = 'bbb', source = 'bbb.yacc')
+env.CFile(target = 'aaa', source = 'aaa.y')
+env.CFile(target = 'bbb', source = 'bbb.yacc')
+env.CXXFile(target = 'ccc', source = 'ccc.yy')
+env.CFile(target = 'ddd', source = 'ddd.ym')
""" % locals())
-test.write('aaa.y', r"""
-int
-main(int argc, char *argv[])
-{
- argv[argc++] = "--";
- printf("YACC\n");
- printf("aaa.y\n");
- exit (0);
-}
-""")
-
-test.write('bbb.yacc', r"""
-int
-main(int argc, char *argv[])
-{
- argv[argc++] = "--";
- printf("YACC\n");
- printf("bbb.yacc\n");
- exit (0);
-}
-""")
+test.write('aaa.y', "aaa.y\nYACC\n")
+test.write('bbb.yacc', "bbb.yacc\nYACC\n")
+test.write('ccc.yy', "ccc.yacc\nYACC\n")
+test.write('ddd.ym', "ddd.yacc\nYACC\n")
test.run(arguments = '.', stderr = None)
-test.run(program = test.workpath('aaa' + _exe), stdout = "myyacc.py\naaa.y\n")
-test.run(program = test.workpath('bbb' + _exe), stdout = "myyacc.py\nbbb.yacc\n")
-
-
-
-yacc = test.where_is('yacc')
-
-if yacc:
-
- 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(YACCFLAGS='-d')
-yacc = foo.Dictionary('YACC')
-bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc)
-foo.Program(target = 'foo', source = 'foo.y')
-bar.Program(target = 'bar', source = 'bar.y')
-foo.Program(target = 'hello', source = ['hello.cpp'])
-foo.CXXFile(target = 'file.cpp', source = ['file.yy'], YACCFLAGS='-d')
-foo.CFile(target = 'not_foo', source = 'foo.y')
-""" % locals())
-
- yacc = r"""
-%%{
-#include <stdio.h>
-
-main()
-{
- yyparse();
-}
-
-yyerror(s)
-char *s;
-{
- fprintf(stderr, "%%s\n", s);
- return 0;
-}
-
-yylex()
-{
- int c;
-
- c = fgetc(stdin);
- return (c == EOF) ? 0 : c;
-}
-%%}
-%%%%
-input: letter newline { printf("%s\n"); };
-letter: 'a' | 'b';
-newline: '\n';
-"""
-
- test.write("file.yy", """\
-%token GRAPH_T NODE_T EDGE_T DIGRAPH_T EDGEOP_T SUBGRAPH_T
-
-%pure_parser
-
-%%
-graph: GRAPH_T
- ;
-
-%%
-""")
-
- test.write("hello.cpp", """\
-#include "file.hpp"
-
-int main()
-{
-}
-""")
-
- test.write('foo.y', yacc % 'foo.y')
-
- test.write('bar.y', yacc % 'bar.y')
-
- # Build the foo program
- test.run(arguments = 'foo' + _exe, stderr = None)
-
- test.up_to_date(arguments = 'foo' + _exe)
-
- test.fail_test(os.path.exists(test.workpath('wrapper.out')))
-
- test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "foo.y\n")
-
- test.fail_test(not os.path.exists(test.workpath('foo.h')))
-
- test.run(arguments = '-c .')
-
- test.fail_test(os.path.exists(test.workpath('foo.h')))
-
- #
- test.run(arguments = 'not_foo.c')
-
- test.up_to_date(arguments = 'not_foo.c')
-
- test.fail_test(os.path.exists(test.workpath('foo.h')))
- test.fail_test(not os.path.exists(test.workpath('not_foo.h')))
-
- test.run(arguments = '-c .')
-
- test.fail_test(os.path.exists(test.workpath('not_foo.h')))
-
- #
- test.run(arguments = 'bar' + _exe)
-
- test.up_to_date(arguments = 'bar' + _exe)
-
- test.fail_test(test.read('wrapper.out') != "wrapper.py\n")
+test.must_match('aaa.c', "aaa.y\nmyyacc.py\n")
+test.must_match('bbb.c', "bbb.yacc\nmyyacc.py\n")
+test.must_match('ccc.cc', "ccc.yacc\nmyyacc.py\n")
+test.must_match('ddd.m', "ddd.yacc\nmyyacc.py\n")
- test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n")
- #
- test.run(arguments = '.')
- test.up_to_date(arguments = '.')
test.pass_test()
diff --git a/test/YACC/YACCFLAGS.py b/test/YACC/YACCFLAGS.py
index a94bc8f9..b7e2167b 100644
--- a/test/YACC/YACCFLAGS.py
+++ b/test/YACC/YACCFLAGS.py
@@ -65,83 +65,15 @@ test.write('SConstruct', """
env = Environment(YACC = r'%(_python_)s myyacc.py',
YACCFLAGS = '-x',
tools=['yacc', '%(linker)s', '%(compiler)s'])
-env.Program(target = 'aaa', source = 'aaa.y')
+env.CFile(target = 'aaa', source = 'aaa.y')
""" % locals())
-test.write('aaa.y', r"""
-int
-main(int argc, char *argv[])
-{
- argv[argc++] = "--";
- printf("YACCFLAGS\n");
- printf("aaa.y\n");
- exit (0);
-}
-""")
-
-test.run(arguments = 'aaa' + _exe, stderr = None)
-
-test.run(program = test.workpath('aaa' + _exe), stdout = " -x\naaa.y\n")
-
-
-
-yacc = test.where_is('yacc')
-
-if yacc:
-
- test.write('SConstruct', """
-foo = Environment()
-bar = Environment(YACCFLAGS = '-v')
-foo.Program(target = 'foo', source = 'foo.y')
-bar.Program(target = 'bar', source = 'bar.y')
-""")
-
- yacc = r"""
-%%{
-#include <stdio.h>
-
-main()
-{
- yyparse();
-}
-
-yyerror(s)
-char *s;
-{
- fprintf(stderr, "%%s\n", s);
- return 0;
-}
-
-yylex()
-{
- int c;
-
- c = fgetc(stdin);
- return (c == EOF) ? 0 : c;
-}
-%%}
-%%%%
-input: letter newline { printf("%s\n"); };
-letter: 'a' | 'b';
-newline: '\n';
-"""
-
- test.write('foo.y', yacc % 'foo.y')
-
- test.write('bar.y', yacc % 'bar.y')
-
- test.run(arguments = 'foo' + _exe, stderr = None)
-
- test.fail_test(os.path.exists(test.workpath('foo.output'))
- or os.path.exists(test.workpath('y.output')))
+test.write('aaa.y', "aaa.y\nYACCFLAGS\n")
- test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "foo.y\n")
+test.run('.', stderr = None)
- test.run(arguments = 'bar' + _exe)
+test.must_match('aaa.c', "aaa.y\n -x\n")
- test.fail_test(not os.path.exists(test.workpath('bar.output'))
- and not os.path.exists(test.workpath('y.output')))
- test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n")
test.pass_test()
diff --git a/test/YACC/YACCVCGFILESUFFIX.py b/test/YACC/YACCVCGFILESUFFIX.py
new file mode 100644
index 00000000..b05471a3
--- /dev/null
+++ b/test/YACC/YACCVCGFILESUFFIX.py
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Test setting the YACCVCGFILESUFFIX variable.
+"""
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+
+
+test.write('myyacc.py', """\
+import getopt
+import os.path
+import string
+import sys
+vcg = None
+opts, args = getopt.getopt(sys.argv[1:], 'go:')
+for o, a in opts:
+ if o == '-g':
+ vcg = 1
+ elif o == '-o':
+ outfile = open(a, 'wb')
+for f in args:
+ infile = open(f, 'rb')
+ for l in filter(lambda l: l != '/*yacc*/\\n', infile.readlines()):
+ outfile.write(l)
+outfile.close()
+if vcg:
+ base, ext = os.path.splitext(args[0])
+ open(base+'.vcgsuffix', 'wb').write(string.join(sys.argv)+'\\n')
+sys.exit(0)
+""")
+
+test.write('SConstruct', """
+env = Environment(tools=['default', 'yacc'],
+ YACC = r'%(_python_)s myyacc.py',
+ YACCVCGFILESUFFIX = '.vcgsuffix')
+env.CXXFile(target = 'aaa', source = 'aaa.yy')
+env.CXXFile(target = 'bbb', source = 'bbb.yy', YACCFLAGS = '-g')
+""" % locals())
+
+test.write('aaa.yy', "aaa.yy\n/*yacc*/\n")
+test.write('bbb.yy', "bbb.yy\n/*yacc*/\n")
+
+test.run(arguments = '.')
+
+test.must_match('aaa.cc', "aaa.yy\n")
+test.must_not_exist('aaa.vcg')
+test.must_not_exist('aaa.vcgsuffix')
+
+test.must_match('bbb.cc', "bbb.yy\n")
+test.must_not_exist('bbb.vcg')
+test.must_match('bbb.vcgsuffix', "myyacc.py -g -o bbb.cc bbb.yy\n")
+
+test.up_to_date(arguments = '.')
+
+
+
+test.pass_test()
diff --git a/test/YACC/live.py b/test/YACC/live.py
new file mode 100644
index 00000000..4934570d
--- /dev/null
+++ b/test/YACC/live.py
@@ -0,0 +1,165 @@
+#!/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 YACC and YACCFLAGS with a live yacc compiler.
+"""
+
+import string
+
+import TestSCons
+
+_exe = TestSCons._exe
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+yacc = test.where_is('yacc') or test.where_is('bison')
+
+if not yacc:
+ test.skip_test('No yacc or bison found; skipping test.\n')
+
+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(YACCFLAGS='-d')
+yacc = foo.Dictionary('YACC')
+bar = Environment(YACC = r'%(_python_)s wrapper.py ' + yacc)
+foo.Program(target = 'foo', source = 'foo.y')
+bar.Program(target = 'bar', source = 'bar.y')
+foo.Program(target = 'hello', source = ['hello.cpp'])
+foo.CXXFile(target = 'file.cpp', source = ['file.yy'], YACCFLAGS='-d')
+foo.CFile(target = 'not_foo', source = 'foo.y')
+""" % locals())
+
+yacc = r"""
+%%{
+#include <stdio.h>
+
+main()
+{
+ yyparse();
+}
+
+yyerror(s)
+char *s;
+{
+ fprintf(stderr, "%%s\n", s);
+ return 0;
+}
+
+yylex()
+{
+ int c;
+
+ c = fgetc(stdin);
+ return (c == EOF) ? 0 : c;
+}
+%%}
+%%%%
+input: letter newline { printf("%s\n"); };
+letter: 'a' | 'b';
+newline: '\n';
+"""
+
+test.write("file.yy", """\
+%token GRAPH_T NODE_T EDGE_T DIGRAPH_T EDGEOP_T SUBGRAPH_T
+
+%pure_parser
+
+%%
+graph: GRAPH_T
+ ;
+
+%%
+""")
+
+test.write("hello.cpp", """\
+#include "file.hpp"
+
+int main()
+{
+}
+""")
+
+test.write('foo.y', yacc % 'foo.y')
+
+test.write('bar.y', yacc % 'bar.y')
+
+
+
+test.run(arguments = 'foo' + _exe, stderr = None)
+
+test.up_to_date(arguments = 'foo' + _exe)
+
+test.must_not_exist(test.workpath('wrapper.out'))
+
+test.run(program = test.workpath('foo'), stdin = "a\n", stdout = "foo.y\n")
+
+test.must_exist(test.workpath('foo.h'))
+
+test.run(arguments = '-c .')
+
+test.must_not_exist(test.workpath('foo.h'))
+
+
+
+test.run(arguments = 'not_foo.c')
+
+test.up_to_date(arguments = 'not_foo.c')
+
+test.must_not_exist(test.workpath('foo.h'))
+test.must_exist(test.workpath('not_foo.h'))
+
+test.run(arguments = '-c .')
+
+test.must_not_exist(test.workpath('not_foo.h'))
+
+
+
+test.run(arguments = 'bar' + _exe)
+
+test.up_to_date(arguments = 'bar' + _exe)
+
+test.must_match(test.workpath('wrapper.out'), "wrapper.py\n")
+
+test.run(program = test.workpath('bar'), stdin = "b\n", stdout = "bar.y\n")
+
+
+
+test.run(arguments = '.')
+
+test.up_to_date(arguments = '.')
+
+
+
+test.pass_test()
diff --git a/test/ignore-command.py b/test/ignore-command.py
index d4de8d3a..c1e6149a 100644
--- a/test/ignore-command.py
+++ b/test/ignore-command.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
+# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -26,7 +26,7 @@
Test use of a preceding - to ignore the return value from a command.
"""
-__revision__ = "/home/scons/scons/branch.0/branch.96/baseline/test/option-n.py 0.96.C352 2005/03/26 00:09:23 knight"
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
diff --git a/test/multi.py b/test/multi.py
deleted file mode 100644
index 8bcb1f76..00000000
--- a/test/multi.py
+++ /dev/null
@@ -1,418 +0,0 @@
-#!/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 various cases where a target is "built" by multiple builder calls.
-"""
-
-import os.path
-import string
-
-import TestCmd
-import TestSCons
-
-test = TestSCons.TestSCons(match=TestCmd.match_re)
-
-_python_ = TestSCons._python_
-
-#
-# A builder with "multi" set can be called multiple times and
-# the source files are added to the list.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'file1.out', source = 'file1a.in')
-env.B(target = 'file1.out', source = 'file1b.in')
-""")
-
-test.write('file1a.in', 'file1a.in\n')
-test.write('file1b.in', 'file1b.in\n')
-
-test.run(arguments='file1.out')
-
-test.must_match('file1.out', "file1a.in\nfile1b.in\n")
-
-
-#
-# A builder with "multi" not set generates an error on the second call.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=0)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'file2.out', source = 'file2a.in')
-env.B(target = 'file2.out', source = 'file2b.in')
-""")
-
-test.write('file2a.in', 'file2a.in\n')
-test.write('file2b.in', 'file2b.in\n')
-
-test.run(arguments='file2.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Multiple ways to build the same target were specified for: file2.out (from ['file2a.in'] and from ['file2b.in'])
-""") + TestSCons.file_expr)
-
-
-#
-# A warning is generated if the calls have different overrides but the
-# overrides don't appear to affect the build operation.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'file3.out', source = 'file3a.in', foo=1)
-env.B(target = 'file3.out', source = 'file3b.in', foo=2)
-""")
-
-test.write('file3a.in', 'file3a.in\n')
-test.write('file3b.in', 'file3b.in\n')
-
-test.run(arguments='file3.out',
- stderr=TestSCons.re_escape("""
-scons: warning: Two different environments were specified for target file3.out,
-\tbut they appear to have the same action: build(target, source, env)
-""") + TestSCons.file_expr)
-
-#
-# A warning is generated if the calls have different overrides but the
-# overrides don't appear to affect the build operation.
-#
-
-test.write('build.py',r"""#!/usr/bin/env python
-import sys
-def build(num, target, source):
- file = open(str(target), 'wb')
- file.write('%s\n'%num)
- for s in source:
- file.write(open(str(s), 'rb').read())
-build(sys.argv[1],sys.argv[2],sys.argv[3:])
-""")
-
-test.write('SConstruct', """
-
-B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'file03.out', source = 'file03a.in', foo=1)
-env.B(target = 'file03.out', source = 'file03b.in', foo=2)
-""" % locals())
-
-test.write('file03a.in', 'file03a.in\n')
-test.write('file03b.in', 'file03b.in\n')
-
-test.run(arguments='file03.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Two environments with different actions were specified for the same target: file03.out
-""") + TestSCons.file_expr)
-
-#
-# Everything works if the two calls have the same overrides.
-#
-
-test.write('build.py',r"""#!/usr/bin/env python
-import sys
-def build(num, target, source):
- file = open(str(target), 'wb')
- file.write('%s\n'%num)
- for s in source:
- file.write(open(str(s), 'rb').read())
-build(sys.argv[1],sys.argv[2],sys.argv[3:])
-""")
-
-test.write('SConstruct', """
-
-B = Builder(action='%(_python_)s build.py $foo $TARGET $SOURCES', multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'file4.out', source = 'file4a.in', foo=3)
-env.B(target = 'file4.out', source = 'file4b.in', foo=3)
-""" % locals())
-
-test.write('file4a.in', 'file4a.in\n')
-test.write('file4b.in', 'file4b.in\n')
-
-python_expr = string.replace(TestSCons.python, '\\', '\\\\')
-act = TestSCons.re_escape('"%s" build.py \$foo \$TARGET \$SOURCES' % python_expr)
-
-test.run(arguments='file4.out',
- stderr=("""
-scons: warning: Two different environments were specified for target file4.out,
-\tbut they appear to have the same action: %s
-""" % act) + TestSCons.file_expr)
-
-test.must_match('file4.out', "3\nfile4a.in\nfile4b.in\n")
-
-
-#
-# Two different environments can be used for the same target, so long
-# as the actions have the same signature; a warning is generated.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env2 = env.Clone(DIFFERENT_VARIABLE = 'true')
-env.B(target = 'file5.out', source = 'file5a.in')
-env2.B(target = 'file5.out', source = 'file5b.in')
-""")
-
-test.write('file5a.in', 'file5a.in\n')
-test.write('file5b.in', 'file5b.in\n')
-
-test.run(arguments='file5.out',
- stderr=TestSCons.re_escape("""
-scons: warning: Two different environments were specified for target file5.out,
-\tbut they appear to have the same action: build(target, source, env)
-""") + TestSCons.file_expr)
-
-test.must_match('file5.out', "file5a.in\nfile5b.in\n")
-
-
-#
-# Environments with actions that have different signatures generate
-# an error.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=Action(build, varlist=['XXX']), multi=1)
-env = Environment(BUILDERS = { 'B' : B }, XXX = 'foo')
-env2 = env.Clone(XXX = 'var')
-env.B(target = 'file6.out', source = 'file6a.in')
-env2.B(target = 'file6.out', source = 'file6b.in')
-""")
-
-test.write('file6a.in', 'file6a.in\n')
-test.write('file6b.in', 'file6b.in\n')
-
-test.run(arguments='file6.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Two environments with different actions were specified for the same target: file6.out
-""") + TestSCons.file_expr)
-
-
-#
-# A builder without "multi" set can still be called multiple times
-# if the calls are the same.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=0)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = 'file7.out', source = 'file7.in')
-env.B(target = 'file7.out', source = 'file7.in')
-""")
-
-test.write('file7.in', 'file7.in\n')
-
-test.run(arguments='file7.out')
-
-test.must_match('file7.out', "file7.in\n")
-
-
-#
-# Trying to call a target with two different "multi" builders
-# generates an error.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-def build2(env, target, source):
- build(env, target, source)
-
-B = Builder(action=build, multi=1)
-C = Builder(action=build2, multi=1)
-env = Environment(BUILDERS = { 'B' : B, 'C' : C })
-env.B(target = 'file8.out', source = 'file8.in')
-env.C(target = 'file8.out', source = 'file8.in')
-""")
-
-test.write('file8a.in', 'file8a.in\n')
-test.write('file8b.in', 'file8b.in\n')
-
-test.run(arguments='file8.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Two different builders (B and C) were specified for the same target: file8.out
-""") + TestSCons.file_expr)
-
-
-#
-# A "multi" builder can be called multiple times with the same target list
-# if everything is identical.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- for t in target:
- file = open(str(t), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = ['file9a.out', 'file9b.out'], source = 'file9a.in')
-env.B(target = ['file9a.out', 'file9b.out'], source = 'file9b.in')
-""")
-
-test.write('file9a.in', 'file9a.in\n')
-test.write('file9b.in', 'file9b.in\n')
-
-test.run(arguments='file9b.out')
-
-test.must_match('file9a.out', "file9a.in\nfile9b.in\n")
-test.must_match('file9b.out', "file9a.in\nfile9b.in\n")
-
-
-#
-# A "multi" builder can NOT be called multiple times with target lists
-# that have different orders. This is intentional; the order of the
-# targets matter to the builder because the build command can contain
-# things like ${TARGET[0]}.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- for t in target:
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = ['file10a.out', 'file10b.out'], source = 'file10.in')
-env.B(target = ['file10b.out', 'file10a.out'], source = 'file10.in')
-""")
-
-test.write('file10.in', 'file10.in\n')
-
-test.run(arguments='file10.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Two different target sets have a target in common: file10b.out
-""") + TestSCons.file_expr)
-
-
-#
-# A target file can't be in two different target lists.
-#
-
-# XXX It would be nice if the following two tests could be made to work
-# by executing the action once for each unique set of targets. This
-# would make it simple to deal with PDB files on Windows like so:
-#
-# env.Object(['foo.obj', 'vc60.pdb'], 'foo.c')
-# env.Object(['bar.obj', 'vc60.pdb'], 'bar.c')
-
-test.write('SConstruct', """
-def build(env, target, source):
- for t in target:
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = ['file11a.out', 'file11b.out'], source = 'file11a.in')
-env.B(target = ['file11b.out', 'file11c.out'], source = 'file11b.in')
-""")
-
-test.write('file11a.in', 'file11a.in\n')
-test.write('file11b.in', 'file11b.in\n')
-
-test.run(arguments='file11.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Two different target sets have a target in common: file11b.out
-""") + TestSCons.file_expr)
-
-
-#
-# A target file can't be a lone target and in a list.
-#
-
-test.write('SConstruct', """
-def build(env, target, source):
- for t in target:
- file = open(str(target[0]), 'wb')
- for s in source:
- file.write(open(str(s), 'rb').read())
-
-B = Builder(action=build, multi=1)
-env = Environment(BUILDERS = { 'B' : B })
-env.B(target = ['file12a.out', 'file12b.out'], source = 'file12a.in')
-env.B(target = 'file12a.out', source = 'file12b.in')
-""")
-
-test.write('file12a.in', 'file12a.in\n')
-test.write('file12b.in', 'file12b.in\n')
-
-test.run(arguments='file12.out',
- status=2,
- stderr=TestSCons.re_escape("""
-scons: *** Cannot build same target `file12a.out' as singular and list
-""") + TestSCons.file_expr)
-
-
-
-test.pass_test()
diff --git a/test/option-v.py b/test/option-v.py
index 0626106d..55278abc 100644
--- a/test/option-v.py
+++ b/test/option-v.py
@@ -38,7 +38,7 @@ test.write('SConstruct', "")
# by the packaging build.
copyright_marker = '__' + 'COPYRIGHT' + '__'
-copyright_years = '2001, 2002, 2003, 2004, 2005, 2006'
+copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007'
fmt = '(%s|Copyright \\(c\\) %s The SCons Foundation)\n'
diff --git a/test/silent-command.py b/test/silent-command.py
index 91880bd3..476158a5 100644
--- a/test/silent-command.py
+++ b/test/silent-command.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python
#
-# Copyright (c) 2001, 2002, 2003, 2004 The SCons Foundation
+# __COPYRIGHT__
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
@@ -26,7 +26,7 @@
Test the use of a preceding @ to suppress printing a command.
"""
-__revision__ = "/home/scons/scons/branch.0/branch.96/baseline/test/option-n.py 0.96.C352 2005/03/26 00:09:23 knight"
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
import os
import os.path
diff --git a/test/timestamp-fallback.py b/test/timestamp-fallback.py
index 288f8d8e..75760233 100644
--- a/test/timestamp-fallback.py
+++ b/test/timestamp-fallback.py
@@ -49,10 +49,11 @@ raise ImportError
os.environ['PYTHONPATH'] = test.workpath('.')
test.write('SConstruct', """
+DefaultEnvironment(tools=[])
def build(env, target, source):
open(str(target[0]), 'wt').write(open(str(source[0]), 'rt').read())
B = Builder(action = build)
-env = Environment(BUILDERS = { 'B' : B })
+env = Environment(tools = [], BUILDERS = { 'B' : B })
env.B(target = 'f1.out', source = 'f1.in')
env.B(target = 'f2.out', source = 'f2.in')
env.B(target = 'f3.out', source = 'f3.in')