Anonymous avatar Anonymous committed 9130ef3

Merged revisions 1738-1754,1756 via svnmerge from
http://scons.tigris.org/svn/scons/branches/core

........
r1741 | stevenknight | 2006-12-16 22:51:07 -0600 (Sat, 16 Dec 2006) | 1 line

0.96.D527 - Give the f90 and f95 Tool modules knowledge of how to build source files of earlier Fortran versions.
........
r1742 | stevenknight | 2006-12-16 23:22:54 -0600 (Sat, 16 Dec 2006) | 1 line

0.96.D528 - Better handling of timestamp fallback if there's no md5 module.
........
r1743 | stevenknight | 2006-12-17 00:21:31 -0600 (Sun, 17 Dec 2006) | 1 line

0.96.D529 - Fix portability of new tests on systems that don't have TeX installed.
........
r1744 | stevenknight | 2006-12-19 15:30:16 -0600 (Tue, 19 Dec 2006) | 1 line

0.96.D530 - Eliminate the ListBuilder subclass in favor of using the Executor's target lists.
........
r1745 | stevenknight | 2006-12-19 18:54:26 -0600 (Tue, 19 Dec 2006) | 1 line

0.96.D531 - Eliminate of MultiStepBuilder as a separate Builder subclass.
........
r1746 | garyo | 2006-12-21 13:21:08 -0600 (Thu, 21 Dec 2006) | 1 line

Minor doc fix, thanks to Douglas Landgraf.
........
r1747 | stevenknight | 2006-12-21 17:13:55 -0600 (Thu, 21 Dec 2006) | 1 line

0.96.D533 - Add CFLAGS for options common to C/C++. (Gary Oberbrunner)
........
r1748 | stevenknight | 2007-01-03 19:48:05 -0600 (Wed, 03 Jan 2007) | 1 line

0.96.D534 - Fix signature storage when targets are retrieved from CacheDir().
........
r1749 | stevenknight | 2007-01-04 16:48:47 -0600 (Thu, 04 Jan 2007) | 1 line

0.96.D535 - Teach the lex and yacc tools about target files generated by different flex/bison options, and about Objective C suffixes. (Pupeno)
........
r1750 | stevenknight | 2007-01-04 17:14:38 -0600 (Thu, 04 Jan 2007) | 1 line

0.96.D536 - Refactor duplicate disambiguation logic in Entry.get_contents().
........
r1751 | stevenknight | 2007-01-05 13:00:54 -0600 (Fri, 05 Jan 2007) | 1 line

0.96.D537 - Fix lprof regression from 0.96.92.
........
r1752 | stevenknight | 2007-01-05 20:43:48 -0600 (Fri, 05 Jan 2007) | 1 line

0.96.D538 - Fix caching of Builder suffix matching (to fix lprof regression).
........
r1753 | stevenknight | 2007-01-06 00:03:16 -0600 (Sat, 06 Jan 2007) | 1 line

0.96.D539 - Fix --include-dir when using MinGW. (Paul)
........
r1754 | stevenknight | 2007-01-06 00:24:53 -0600 (Sat, 06 Jan 2007) | 1 line

0.96.D540 - Make bootstrap.py something useful to execute SCons out of a source directory.
........
r1756 | stevenknight | 2007-01-06 21:32:11 -0600 (Sat, 06 Jan 2007) | 1 line

0.96.D541 - Update the Copyright year string to include 2007. Automate updating the month+year string in man page title headers. Fix hard-coded __revision__ strings that crept into some older tests.
........

Comments (0)

Files changed (71)

QMTest/TestSCons_time.py

 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
 
         $ 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
 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
 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
 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
 
 # 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__
     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'      + '__', '')
                    COPYRIGHT           = copyright,
                    DATE                = date,
                    DEVELOPER           = developer,
+                   MONTH_YEAR          = month_year,
                    REVISION            = revision,
                    VERSION             = version,
                    DH_COMPAT           = 2,
-"""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__
 #
 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
 
-opts, args = getopt.getopt(sys.argv[1:], "Y:", [])
+requires_an_argument = 'bootstrap.py:  %s requires an argument\n'
 
-for o, a in opts:
-    if o == '-Y':
-        search.append(a)
+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()
+
+while command_line_args:
+    arg = command_line_args.pop(0)
+
+    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:
 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)
+    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() )
 
-args = [ sys.executable, os.path.join(subdir, scons_py) ] + sys.argv[1:]
+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)

doc/man/scons-time.1

 .\" 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
 [\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
 '\"==========================================================================
 .fi
 .RE
 ..
-.TH SCONS 1 "December 2006"
+.TH SCONS 1 "__MONTH_YEAR__"
 .SH NAME
 scons \- a software construction tool
 .SH SYNOPSIS
 .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
 
 -mno-cygwin         CCFLAGS, LINKFLAGS
 -mwindows           LINKFLAGS
 -pthread            CCFLAGS, LINKFLAGS
+-std=               CFLAGS
 -Wa,                ASFLAGS, CCFLAGS
 -Wl,-rpath=         RPATH
 -Wl,-R,             RPATH

doc/man/sconsign.1

 .RE
 .fi
 ..
-.TH SCONSIGN 1 "August 2004"
+.TH SCONSIGN 1 "__MONTH_YEAR__"
 .SH NAME
 sconsign \- print SCons .sconsign file information
 .SH SYNOPSIS

doc/user/copyright.in

 <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>

doc/user/copyright.sgml

 <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>
 
     <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>
 

doc/user/main.sgml

 
     <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>
 

doc/user/nodes.in

       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">

doc/user/nodes.sgml

       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>
 
 
   - 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
     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().
 
   - 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.
     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

src/engine/SCons/Builder.py

 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
 
         (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:
 
     """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)
         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):
         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
     # 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
                 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:
                         name = None,
                         chdir = _null,
                         is_explicit = 1,
+                        src_builder = [],
                         **overrides):
         if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
         self._memo = {}
             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"
 
 
     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)
         
         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)
 
             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 = []
         """
         self.emitter[suffix] = emitter
 
+    def add_src_builder(self, builder):
+        """
+        Add a new Builder to the list of src_builders.
 
-
-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.
+        This requires wiping out cached values so that the computed
+        lists of source suffixes get re-calculated.
         """
-        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)
-
-    memoizer_counters.append(SCons.Memoize.CountDict('_get_sdict', _get_sdict_key))
+        self._memo = {}
+        self.src_builder.append(builder)
 
     def _get_sdict(self, env):
         """
         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)
 
                     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
                     # 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)
 
-    def _src_suffixes_key(self, env):
+        memo_dict[memo_key] = builders
+        return builders
+
+    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
         """
         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):

src/engine/SCons/BuilderTests.py

 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]
 
             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")
         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
         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
 
         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')
         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'
 
         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',
         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]

src/engine/SCons/Environment.py

 import SCons.Platform
 import SCons.SConsign
 import SCons.Sig
-import SCons.Sig.MD5
 import SCons.Sig.TimeStamp
 import SCons.Subst
 import SCons.Tool
         """
         dict = {
             'ASFLAGS'       : [],
+            'CFLAGS'        : [],
             'CCFLAGS'       : [],
             'CPPDEFINES'    : [],
             'CPPFLAGS'      : [],
                 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)
     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

src/engine/SCons/EnvironmentTests.py

 
         empty = {
             'ASFLAGS'       : [],
+            'CFLAGS'        : [],
             'CCFLAGS'       : [],
             'CPPDEFINES'    : [],
             'CPPFLAGS'      : [],
             "-Wl,-R,rpath2 " + \
             "-Wl,-Rrpath3 " + \
             "-Wp,-cpp " + \
+            "-std=c99 " + \
             "-framework Carbon " + \
             "-frameworkdir=fwd1 " + \
             "-Ffwd2 " + \
         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'),

src/engine/SCons/Node/FS.py

 from SCons.Debug import logInstanceCreation
 import SCons.Errors
 import SCons.Node
-import SCons.Sig.MD5
 import SCons.Subst
 import SCons.Util
 import SCons.Warnings
     def diskcheck_match(self):
         pass
 
-    def disambiguate(self):
+    def disambiguate(self, must_exist=None):
         """
         """
         if self.isdir():
                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()
         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()
+        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.isdir():
-            self.__class__ = Dir
-            self._morph()
-            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
         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
         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):
             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)

src/engine/SCons/Node/FSTests.py

 
         # 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:
             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")
         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")

src/engine/SCons/Node/__init__.py

         # 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):

src/engine/SCons/SConfTests.py

                         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()})

src/engine/SCons/Script/Main.py

                          SCons.Warnings.DeprecatedWarning,
                          SCons.Warnings.DuplicateEnvironmentWarning,
                          SCons.Warnings.MissingSConscriptWarning,
+                         SCons.Warnings.NoMD5ModuleWarning,
                          SCons.Warnings.NoMetaclassSupportWarning,
                          SCons.Warnings.NoParallelSupportWarning,
                          SCons.Warnings.MisleadingKeywordsWarning, ]

src/engine/SCons/Taskmaster.py

         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:
         pass
 
     def executed(self, node):
-        try:
-            tlist = node.builder.targets(node)
-        except AttributeError:
-            tlist = [node]
+        pass
 
     def exception_raise(self, exception):
         exc = exception[:]

src/engine/SCons/TaskmasterTests.py

     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
 

src/engine/SCons/Tool/bcc32.py

 
     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'

src/engine/SCons/Tool/cc.py

 
     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']  = ''

src/engine/SCons/Tool/cc.xml

 
 <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>
 
 
 <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>
 
 <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>
 
 <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>

src/engine/SCons/Tool/f90.py

 
 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'

src/engine/SCons/Tool/f95.py

 
 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'

src/engine/SCons/Tool/icc.py

     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']  = ''

src/engine/SCons/Tool/lex.py

 
 __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)
 
-    env['LEX']      = env.Detect('flex') or 'lex'
-    env['LEXFLAGS'] = SCons.Util.CLVar('')
-    env['LEXCOM']   = '$LEX $LEXFLAGS -t $SOURCES > $TARGET'
-    
+    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"
+
 def exists(env):
-    return env.Detect(['flex', 'lex'])
+    return env.Detect(["flex", "lex"])

src/engine/SCons/Tool/mingw.py

     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:

src/engine/SCons/Tool/msvc.py

     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'

src/engine/SCons/Tool/mwcc.py

     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'

src/engine/SCons/Tool/qt.py

     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

src/engine/SCons/Tool/yacc.py

 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os.path
+import string
 
 import SCons.Defaults
 import SCons.Tool
 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_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)
-    c_file.add_emitter('.y', yEmitter)
-    c_file.add_emitter('.yacc', yEmitter)
     cxx_file.add_emitter('.yy', yyEmitter)
 
     env['YACC']      = env.Detect('bison') or 'yacc'
     env['YACCCOM']   = '$YACC $YACCFLAGS -o $TARGET $SOURCES'
     env['YACCHFILESUFFIX'] = '.h'
     env['YACCHXXFILESUFFIX'] = '.hpp'
+    env['YACCVCGFILESUFFIX'] = '.vcg'
 
 def exists(env):
     return env.Detect(['bison', 'yacc'])

src/engine/SCons/Tool/yacc.xml

 <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>

src/engine/SCons/Warnings.py

 class MissingSConscriptWarning(Warning):
     pass
 
+class NoMD5ModuleWarning(Warning):
+    pass
+
 class NoMetaclassSupportWarning(Warning):
     pass
 

src/script/scons-time.py

 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
 
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
 import getopt
 import glob
 import os

src/test_copyrights.py

 __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
+            self.remove_dict[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)
+    def directory_exists(self):
+        return os.path.exists(self.directory)
 
-# 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',
-    ],
-}
+    def remove_path(self, path):
+        return self.remove_dict.get(path)
 
-no_copyright = []
-no_result = []
+    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)
 
-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)
+    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)
 
-if no_copyright:
-    print "Found the following files with no copyrights:"
-    print "\t" + string.join(no_copyright, "\n\t")
+    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',