Merged revisions 1738-1754,1756 via svnmerge from
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 7 Jan 2007 07:23:05 +0000 (07:23 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 7 Jan 2007 07:23:05 +0000 (07:23 +0000)
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.
........

git-svn-id: http://scons.tigris.org/svn/scons/trunk@1757 fdb21ef1-2011-0410-befe-b5e4ea1792b1

71 files changed:
QMTest/TestSCons_time.py
README
SConstruct
bootstrap.py
doc/man/scons-time.1
doc/man/scons.1
doc/man/sconsign.1
doc/user/copyright.in
doc/user/copyright.sgml
doc/user/main.in
doc/user/main.sgml
doc/user/nodes.in
doc/user/nodes.sgml
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/FSTests.py
src/engine/SCons/Node/__init__.py
src/engine/SCons/SConfTests.py
src/engine/SCons/Script/Main.py
src/engine/SCons/Taskmaster.py
src/engine/SCons/TaskmasterTests.py
src/engine/SCons/Tool/bcc32.py
src/engine/SCons/Tool/cc.py
src/engine/SCons/Tool/cc.xml
src/engine/SCons/Tool/f90.py
src/engine/SCons/Tool/f95.py
src/engine/SCons/Tool/icc.py
src/engine/SCons/Tool/lex.py
src/engine/SCons/Tool/mingw.py
src/engine/SCons/Tool/msvc.py
src/engine/SCons/Tool/mwcc.py
src/engine/SCons/Tool/qt.py
src/engine/SCons/Tool/yacc.py
src/engine/SCons/Tool/yacc.xml
src/engine/SCons/Warnings.py
src/script/scons-time.py
src/test_copyrights.py
test/Builder/multi/different-actions.py [new file with mode: 0644]
test/Builder/multi/different-environments.py [new file with mode: 0644]
test/Builder/multi/different-multi.py [new file with mode: 0644]
test/Builder/multi/different-order.py [new file with mode: 0644]
test/Builder/multi/different-overrides.py [new file with mode: 0644]
test/Builder/multi/different-target-lists.py [new file with mode: 0644]
test/Builder/multi/error.py [new file with mode: 0644]
test/Builder/multi/lone-target-list.py [new file with mode: 0644]
test/Builder/multi/multi.py [new file with mode: 0644]
test/Builder/multi/same-actions.py [new file with mode: 0644]
test/Builder/multi/same-overrides.py [new file with mode: 0644]
test/Builder/multi/same-targets.py [new file with mode: 0644]
test/Builder/non-multi.py [new file with mode: 0644]
test/CC/CFLAGS.py [new file with mode: 0644]
test/CC/SHCFLAGS.py [new file with mode: 0644]
test/CacheDir/up-to-date-q.py [new file with mode: 0644]
test/LEX/LEX.py
test/LEX/LEXFLAGS.py
test/LEX/live.py [new file with mode: 0644]
test/TEX/build_dir.py
test/TEX/subdir-input.py
test/YACC/YACC.py
test/YACC/YACCFLAGS.py
test/YACC/YACCVCGFILESUFFIX.py [new file with mode: 0644]
test/YACC/live.py [new file with mode: 0644]
test/ignore-command.py
test/multi.py [deleted file]
test/option-v.py
test/silent-command.py
test/timestamp-fallback.py

index 90eb5a8ee6de78b9657b0a447314a0aee7f641c8..34caa062ebaddda9acfd3830241664f7ce93c528 100644 (file)
@@ -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 7d5c84b5252dd4c611ba3f7cfa346b61e089aa01..518de06e156a61b0f13a07101dc68f0f77334215 100644 (file)
--- 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
index 811ed4a1786afe7cabd288816528d6d978edb3f8..a61466af55f38b303a97e1cdcea84a15ff1cbaa6 100644 (file)
@@ -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,
index c6a7db3a68cd28f7452637ad7afc3b81f237bc03..9a88c1c85823e9fd9dd4ea5ff57a4c0d541a5b61 100644 (file)
@@ -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)
index 50f490fa7ebae54907db02443ba4f5c56c4ac2c2..b2de0029b871d19f5ef868ba5c40de8061459213 100644 (file)
@@ -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
 '\"==========================================================================
index d45333b1311fffcaf608904ff5d7a86a5c734cc4..f4bef97cab36c575305702e72ed076a9ed28dba0 100644 (file)
@@ -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
index 7fc32ec2538574c3d2e4a6d092f8a5f4ea0314f0..3d01cf4e332ee2598483cfb966622c89769daa77 100644 (file)
@@ -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
index 119f74940aec18069142de27780dea59939220ea..76e3e50f72e3cff548edf5352173d878c478682e 100644 (file)
@@ -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>
index 119f74940aec18069142de27780dea59939220ea..76e3e50f72e3cff548edf5352173d878c478682e 100644 (file)
@@ -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>
index af3b0eed9cf53a8f2d2d2aa8280fcd5716af3a5c..be302d7e8306423f3e045a7bbecba0eb2dd97a70 100644 (file)
 
     <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>
 
index af3b0eed9cf53a8f2d2d2aa8280fcd5716af3a5c..be302d7e8306423f3e045a7bbecba0eb2dd97a70 100644 (file)
 
     <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>
 
index a5d05dcb6b3412ff1d2c86cc4b6ed1035a556ad1..f5faf5ae7d885a2a27fcf57967ed8b4bc2838e68 100644 (file)
       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">
index b6bcb899f30ea184ca20982dd546bdbb9ead8a43..114e9e0a8448115ee564886e177053c96bb0ca53 100644 (file)
       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>
 
index e7f49370921e86e83ff6e4985ecb6c54efdf04c4..3678dd18ac7dcdc7aa4ef66a9e4d6b7fdc79fe9e 100644 (file)
@@ -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
index d5f566a325353c2c9b4eae05a31746dc9f6c0acb..d625ed7a631623c6d5ef512784200cf15ada7ab9 100644 (file)
@@ -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):
index 4e196e269c764590d4613d915b11f25bda706b3e..acf07220b163f1a3fe57d550a5b9c900212f5d60 100644 (file)
@@ -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]
index 4761ea050e53862d1662a00ceac9c4fe123fe0eb..e2883f47f9d46be3cda1a5fbc3e916f061adecc8 100644 (file)
@@ -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
index f0f73dac6cb1e58d1c795cdb99a93958a39f60f1..c015bc1ed0e6f3fb30e63be817b82bfe7836cbb6 100644 (file)
@@ -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'),
index 08b8d7d813c81380d48ebdb2aa1b42b038a8fba6..8db292804af3c2f6dea191fd23c8b864200063a1 100644 (file)
@@ -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)
index 434709c4be92ebed8a1f86f3788f839e87b1ee94..ec3c322d054c7c675d2c9904d0e612f96fd33ad6 100644 (file)
@@ -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")
index e5d064e581f07575629890ff28ca0dd88ed72386..e17666bb756760a4aaa1466205b7f70057ffd6f0 100644 (file)
@@ -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):
index 3ad4cc717e1e81be99457c5abe8cbc2211d60e43..22ec188b6dc17d04af40470726019e0f16489ae6 100644 (file)
@@ -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()})
index 6eedbabf880ec8da3a58871a726a7e6550c100d2..96f152618329446efaa927c444fd338c98f0fbef 100644 (file)
@@ -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, ]
index 2ea3f0dad857037d58804b34dfa48ff7b7e92ded..04ed19a882659eccca01ff8ba21ea8d7fa021d6b 100644 (file)
@@ -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[:]
index 4fefb9d201a1085eeebd4b3bb76a3939131e4ae1..1803eee25495a7111ac7d80886e5599d67042ea0 100644 (file)
@@ -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
 
index 826373f58c9929d82e6df13fee512ef825c69e01..86ca0764f563b2032d0be074bfac410ec185061d 100644 (file)
@@ -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'
index c4114b739e966eb61aaf3a78f40aa4720416836a..62b945f67f3e9edc4bb3e18dad76c288b1bdaaef 100644 (file)
@@ -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']  = ''
index 84980db1f653b2bc91db0fabd5b82fe04c15c2b5..0ebaf146341ff41ae1a9340109248f33fff0b1dc 100644 (file)
@@ -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>
index 2e2b5b114fc0ba5ed98a751afc4ea040d718e815..cb450b6dd92b4a762143c7fa320429e1efe3ab52 100644 (file)
@@ -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'
index 9cd2664cb3570a5e9d5b29db4c87d9570a618295..7adc80b36f9c7be7e70e3154c0eca81e00524641 100644 (file)
@@ -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'
index 20bf17ced9440831e4d7cb8d9f12bdb2dbcce6c9..90dece727137c91b7dcb80906d4fffa8f821ad2a 100644 (file)
@@ -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']  = ''
index 3331f6ccab3428338ce2973127f3f20ea5aa11ca..31f21a9d687bae39265f60520bec07f0768e23be 100644 (file)
@@ -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"])
index d679b53efbfea06d9450833258dc472bdeedb882..0639535cf229f66d30ed8372ded0c62f9c2ba227 100644 (file)
@@ -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:
index 80b59263265d3e005e5d037ca25886ac6c73a9c0..7e476f57c2dfe06e8151d06a4c8d51c7ae5430c9 100644 (file)
@@ -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'
index 52f55ee1adab4406533eb1b59cfc636d963be794..a1ede44a7a6f5d34c8f9ef3f4df483065cccd0d1 100644 (file)
@@ -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'
index 5077901d06cdb9759d4ba01ebfffe3d7a0b9a6e9..4d290c777310c502f85b4125b82cc57243841a6e 100644 (file)
@@ -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
index b8916ae60b260cbcebb061b35f2cc09ddff65565..cbccb294474f28c570f681331ce174317bf296e9 100644 (file)
@@ -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'])
index 48bb3236d0b380bf1097d4f9fa01f6557f54bc3b..8a23d0b15e59617496ce4afb03238bcfa4dbcd3d 100644 (file)
@@ -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>
index 27614bf9a9d14fe5cf864b0102732f76595caa71..1b13c96a0a27bd0c1b13a0f206dafdb5581009fe 100644 (file)
@@ -54,6 +54,9 @@ class DuplicateEnvironmentWarning(Warning):
 class MissingSConscriptWarning(Warning):
     pass
 
+class NoMD5ModuleWarning(Warning):
+    pass
+
 class NoMetaclassSupportWarning(Warning):
     pass
 
index 1867d44fa0f2559656c90cc7c9f2a8a8a835c815..b0feaab2180a5d9846a3267cc495420a7b867a27 100644 (file)
@@ -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
index ce78a8bcc1e033f193e8b04d6a99de6310d34a8a..2bc951ed84f364f792b1dd280b212f67bc03c0b4 100644 (file)
 __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 (file)
index 0000000..ed1c676
--- /dev/null
@@ -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 (file)
index 0000000..9d1dcc8
--- /dev/null
@@ -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 (file)
index 0000000..1f0ef2a
--- /dev/null
@@ -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 (file)
index 0000000..f4a6443
--- /dev/null
@@ -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 (file)
index 0000000..5420a49
--- /dev/null
@@ -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 (file)
index 0000000..5822199
--- /dev/null
@@ -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 (file)
index 0000000..4201cfc
--- /dev/null
@@ -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 (file)
index 0000000..a305b54
--- /dev/null
@@ -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 (file)
index 0000000..315599b
--- /dev/null
@@ -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 (file)
index 0000000..afccb65
--- /dev/null
@@ -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 (file)
index 0000000..d12af21
--- /dev/null
@@ -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 (file)
index 0000000..19efff7
--- /dev/null
@@ -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 (file)
index 0000000..1d5822e
--- /dev/null
@@ -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 (file)
index 0000000..f14fcc5
--- /dev/null
@@ -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 (file)
index 0000000..ef20120
--- /dev/null
@@ -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 (file)
index 0000000..07123c9
--- /dev/null
@@ -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()
index 3fd4db3d027ce681145432373b72d7d58086f9cc..91b4614dfc05a5296c119848701400098cd0a65b 100644 (file)
@@ -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()
index 59239349201ff011eb44ddbbe8cc3099461a044e..b6a06fa13bdf70c7c398c96b659ea990bbfd1a23 100644 (file)
@@ -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 (file)
index 0000000..e917039
--- /dev/null
@@ -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()
index 146f6e1bbcdf168ad0f88797fe51e851bdb6483c..49675336235e520e45c1f156c84bf297ea7d15e4 100644 (file)
@@ -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'),
index b92ab732a051162701ccc11c1657bbecc8c1bc2e..d16ba5fc34f368a9443bb3fb487c28ab7cadc9a4 100644 (file)
@@ -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'],
index d3bc679fe4dcf59edbfa7a4c070aadd0bb162f05..bef959f233906bd0c81d0d0bd18444330638c0d3 100644 (file)
@@ -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()
index a94bc8f980f4be5fdc67010f877ffb4ca4f2a659..b7e2167bcfb7bceca5a1d0e747a575e9c12546c3 100644 (file)
@@ -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 (file)
index 0000000..b05471a
--- /dev/null
@@ -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 (file)
index 0000000..4934570
--- /dev/null
@@ -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()
index d4de8d3acbf9fa42425918f0151932abda1f2926..c1e6149a98eba2659a5a15b7941a48c9839ab84d 100644 (file)
@@ -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 (file)
index 8bcb1f7..0000000
+++ /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()
index 0626106d5c7ef5e1bdbd68ca8229a38d056975b5..55278abc971cf4e539b39a166a1dbc6db9794bb0 100644 (file)
@@ -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'
 
index 91880bd32df89920ed3cd2068de76311719d835b..476158a53c0744b92ca72c364b7f08a5020a8dfe 100644 (file)
@@ -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
index 288f8d8e7edbcba3f598c2ba0670f838411d0d7f..75760233ae5b807576e4ca2e74ac4a3efb5819a9 100644 (file)
@@ -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')