Allow explicit target_factory=Dir with Builders that make a directory to override...
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 15 Nov 2005 14:33:25 +0000 (14:33 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Tue, 15 Nov 2005 14:33:25 +0000 (14:33 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@1386 fdb21ef1-2011-0410-befe-b5e4ea1792b1

doc/man/scons.1
src/CHANGES.txt
src/engine/SCons/Builder.py
src/engine/SCons/BuilderTests.py
src/engine/SCons/Node/FS.py
src/engine/SCons/Node/NodeTests.py
src/engine/SCons/Node/__init__.py

index 46ad8134d8baa240dda218d0de57da03dbe4e24d..b94d680e508fd09e99e3d3c77e09cf334293b889 100644 (file)
@@ -31,7 +31,7 @@
 .fi
 .RE
 ..
-.TH SCONS 1 "October 2005"
+.TH SCONS 1 "October 2004"
 .SH NAME
 scons \- a software construction tool
 .SH SYNOPSIS
@@ -149,28 +149,6 @@ import os
 env = Environment(ENV = {'PATH' : os.environ['PATH']})
 .EE
 
-Similarly, if the commands use external environment variables
-like $PATH, $HOME, $JAVA_HOME, $LANG, $SHELL, $TERM, etc.,
-these variables can also be explicitly propagated:
-
-.ES
-import os
-env = Environment(ENV = {'PATH' : os.environ['PATH'],
-                         'HOME' : os.environ['HOME']})
-.EE
-
-Or you may explicitly propagate the invoking user's
-complete external environment:
-
-.ES
-import os
-env = Environment(ENV = os.environ['PATH'])
-.EE
-
-This comes at the expense of making your build
-dependent on the user's environment being set correctly,
-but it may be more convenient for many configurations.
-
 .B scons
 can scan known input files automatically for dependency
 information (for example, #include statements
@@ -532,10 +510,8 @@ specifies what type of debugging:
 
 .TP
 --debug=count
-Print how many objects are created
-of the various classes used internally by SCons
-before and after reading the SConscript files
-and before and after building targets.
+Print a count of how many objects are created
+of the various classes used internally by SCons.
 This only works when run under Python 2.1 or later.
 
 .TP
@@ -543,16 +519,6 @@ This only works when run under Python 2.1 or later.
 Print the dependency tree
 after each top-level target is built. This prints out only derived files.
 
-.TP
---debug=explain
-Print an explanation of precisely why
-.B scons
-is deciding to (re-)build any targets.
-(Note:  this does not print anything
-for targets that are
-.I not
-rebuilt.)
-
 .TP
 --debug=findlibs
 Instruct the scanner that searches for libraries
@@ -570,34 +536,11 @@ of a given derived file:
 $ scons --debug=includes foo.o
 .EE
 
-.TP
---debug=memoizer
-Prints a summary of hits and misses in the Memoizer,
-the internal SCons subsystem for caching
-various values in memory instead of
-recomputing them each time they're needed.
-
 .TP
 --debug=memory
 Prints how much memory SCons uses
 before and after reading the SConscript files
-and before and after building targets.
-
-.TP
---debug=nomemoizer
-Disables use of the Memoizer,
-the internal SCons subsystem for caching
-various values in memory instead of
-recomputing them each time they're needed.
-This provides more accurate counts of the
-underlying function calls in the 
-Python profiler output when using the
-.R --profile=
-option.
-(When the Memoizer is used,
-the profiler counts all
-memoized functions as being executed
-by the Memoizer's wrapper calls.)
+and before and after building.
 
 .TP
 --debug=objects
@@ -610,6 +553,7 @@ This only works when run under Python 2.1 or later.
 Re-run SCons under the control of the
 .RI pdb
 Python debugger.
+.EE
 
 .TP
 --debug=presub
@@ -629,12 +573,6 @@ Building myprog.o with action(s):
 Prints an internal Python stack trace
 when encountering an otherwise unexplained error.
 
-.TP
---debug=stree
-Print the dependency tree along with status information.  This is the
-same as the debug=tree option, but additional status information is
-provided for each node in the tree.
-
 .TP
 --debug=time
 Prints various time profiling information: the time spent
@@ -649,47 +587,6 @@ after each top-level target is built. This prints out the complete
 dependency tree including implicit dependencies and ignored
 dependencies.
 
-.TP
-.RI --diskcheck= types
-Enable specific checks for
-whether or not there is a file on disk
-where the SCons configuration expects a directory
-(or vice versa),
-and whether or not RCS or SCCS sources exist
-when searching for source and include files.
-The
-.I types
-argument can be set to:
-.BR all ,
-to enable all checks explicitly
-(the default behavior);
-.BR none ,
-to disable all such checks;
-.BR match ,
-to check that files and directories on disk
-match SCons' expected configuration;
-.BR rcs ,
-to check for the existence of an RCS source
-for any missing source or include files;
-.BR sccs ,
-to check for the existence of an SCCS source
-for any missing source or include files.
-Multiple checks can be specified separated by commas;
-for example,
-.B --diskcheck=sccs,rcs
-would still check for SCCS and RCS sources,
-but disable the check for on-disk matches of files and directories.
-Disabling some or all of these checks
-can provide a performance boost for large configurations,
-or when the configuration will check for files and/or directories
-across networked or shared file systems,
-at the slight increased risk of an incorrect build
-or of not handling errors gracefully
-(if include files really should be
-found in SCCS or RCS, for example,
-or if a file really does exist
-where the SCons configuration expects a directory).
-
 .\" .TP
 .\" -e, --environment-overrides
 .\" Variables from the execution environment override construction
@@ -827,18 +724,12 @@ Ignored for compatibility with non-GNU versions of
 .RI --max-drift= SECONDS
 Set the maximum expected drift in the modification time of files to 
 .IR SECONDS .
-This value determines how long a file must be unmodified
-before its cached content signature
-will be used instead of
-calculating a new content signature (MD5 checksum)
-of the file's contents.
-The default value is 2 days, which means a file must have a
-modification time of at least two days ago in order to have its
-cached content signature used.
-A negative value means to never cache the content
+This value determines how old a file must be before its content signature
+is cached. The default value is 2 days, which means a file must have a
+modification time of at least two days ago in order to have its content
+signature cached. A negative value means to never cache the content
 signature and to ignore the cached value if there already is one. A value
-of 0 means to always use the cached signature,
-no matter how old the file is.
+of 0 means to always cache the signature, no matter how old the file is.
 
 .TP
 -n, --just-print, --dry-run, --recon
@@ -1060,10 +951,8 @@ and suffixes appropriate for the platform.
 Note that the
 .B win32
 platform adds the
-.B SYSTEMDRIVE
-and
 .B SYSTEMROOT
-variables from the user's external environment
+variable from the user's external environment
 to the construction environment's
 .B ENV
 dictionary.
@@ -1106,7 +995,7 @@ have two functions: generate(env, **kw) and exists(env).
 The
 .B generate()
 function
-modifies the passed-in environment
+modifies the passed in environment
 to set up variables so that the tool
 can be executed;
 it may use any keyword arguments
@@ -1119,19 +1008,6 @@ value if the tool is available.
 Tools in the toolpath are used before
 any of the built-in ones.  For example, adding gcc.py to the toolpath
 would override the built-in gcc tool.
-Also note that the toolpath is
-stored in the environment for use
-by later calls to
-.BR Copy ()
-and
-.BR Tool ()
-methods:
-
-.ES
-base = Environment(toolpath=['custom_path'])
-derived = base.Copy(tools=['custom_tool'])
-derived.CustomBuilder()
-.EE
 
 The elements of the tools list may also
 be functions or callable objects,
@@ -1221,7 +1097,6 @@ ifl
 ifort
 ilink
 ilink32
-intelc
 jar
 javac
 javah
@@ -1237,8 +1112,6 @@ mslib
 mslink
 msvc
 msvs
-mwcc
-mwld
 nasm
 pdflatex
 pdftex
@@ -1375,15 +1248,6 @@ environment that consists of the tools and values that
 .B scons
 has determined are appropriate for the local system.
 
-Builder methods that can be called without an explicit
-environment may be called from custom Python modules that you
-import into an SConscript file by adding the following
-to the Python module:
-
-.ES
-from SCons.Script import *
-.EE
-
 All builder methods return a list of Nodes
 that represent the target or targets that will be built.
 A
@@ -1402,7 +1266,7 @@ to add a specific
 flag when compiling one specific object file:
 
 .ES
-bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR')
+bar_obj_list = env.StaticObject('bar.c', CCFLAGS='-DBAR')
 env.Program(source = ['foo.c', bar_obj_list, 'main.c'])
 .EE
 
@@ -1449,7 +1313,7 @@ by passing the Node to the Python-builtin
 function:
 
 .ES
-bar_obj_list = env.StaticObject('bar.c', CPPDEFINES='-DBAR')
+bar_obj_list = env.StaticObject('bar.c', CCFLAGS='-DBAR')
 print "The path to bar_obj is:", str(bar_obj_list[0])
 .EE
 
@@ -1572,14 +1436,11 @@ the
 .B DVI
 builder method will also examine the contents
 of the
-.B .aux
-file and invoke the $BIBTEX command line
+.B .aux file
+and invoke the $BIBTEX command line
 if the string
 .B bibdata
 is found,
-start $MAKEINDEX to generate an index if a
-.B .ind
-file is found
 and will examine the contents
 .B .log
 file and re-run the $LATEXCOM command
@@ -1711,16 +1572,6 @@ A synonym for the
 .B StaticLibrary
 builder method.
 
-'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.IP LoadableModule()
-.IP env.LoadableModule()
-On most systems,
-this is the same as
-.BR SharedLibrary ().
-On Mac OS X (Darwin) platforms,
-this creates a loadable module bundle.
-
-
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP M4()
 .IP env.M4()
@@ -1752,101 +1603,55 @@ env.Moc('foo.cpp') # generates foo.moc
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP MSVSProject()
 .IP env.MSVSProject()
-Builds a Microsoft Visual Studio project file,
-and by default builds a solution file as well.
-
+Builds Microsoft Visual Studio project files.
 This builds a Visual Studio project file, based on the version of
 Visual Studio that is configured (either the latest installed version,
-or the version specified by 
+or the version set by 
 .B MSVS_VERSION
-in the construction environment).
-For Visual Studio 6, it will generate a
+in the Environment constructor).
+For VS 6, it will generate 
 .B .dsp
-file.
-For Visual Studio 7 (.NET), it will
-generate a
-.B .vcproj
-file.
-
-By default,
-this also generates a solution file
-for the specified project,
-a
+and
 .B .dsw
-file for Visual Studio 6
-or a
+files, for VS 7, it will
+generate
+.B .vcproj
+and
 .B .sln
-file for Visual Studio 7 (.NET).
-This behavior may be disabled by specifying
-.B auto_build_solution=0
-when you call
-.BR MSVSProject (),
-in which case you presumably want to
-build the solution file(s)
-by calling the
-.BR MSVSSolution ()
-Builder (see below).
+files.
 
 It takes several lists of filenames to be placed into the project
-file.
-These are currently limited to
-.BR srcs ,
-.BR incs ,
-.BR localincs ,
-.BR resources ,
+file, currently these are limited to 
+.B srcs, incs, localincs, resources,
 and
-.BR misc .
-These are pretty self-explanatory, but it should be noted that these
-lists are added to the $SOURCES construction variable as strings,
-NOT as SCons File Nodes.  This is because they represent file
-names to be added to the project file, not the source files used to
-build the project file.
-
-In addition to the above lists of values (which are all optional,
-although not specifying any of them results in an empty project file),
-the following values may be specified:
-
-.BR target :
-The name of the target
-.B .dsp
-or
-.B .vcproj
-file.
-The correct
-suffix for the version of Visual Studio must be used, but the
-.B env['MSVSPROJECTSUFFIX']
-construction variable
+.B misc.
+These are pretty self explanatory, but it
+should be noted that the 'srcs' list is NOT added to the $SOURCES
+environment variable.  This is because it represents a list of files
+to be added to the project file, not the source used to build the
+project file (in this case, the 'source' is the SConscript file used
+to call MSVSProject).
+
+In addition to these values (which are all optional, although not
+specifying any of them results in an empty project file), the
+following values must be specified:
+
+target: The name of the target .dsp or .vcproj file.  The correct
+suffix for the version of Visual Studio must be used, but the value
+
+env['MSVSPROJECTSUFFIX']
+
 will be defined to the correct value (see example below).
 
-.BR variant :
-The name of this particular variant.
-For Visual Studio 7 projects,
-this can also be a list of variant names.
-These are typically things like "Debug" or "Release", but really
-can be anything you want.
-For Visual Studio 7 projects,
-they may also specify a target platform
-separated from the variant name by a
-.B |
-(vertical pipe)
-character:
-.BR Debug|Xbox .
-The default target platform is Win32.
-Multiple calls to
-.BR MSVSProject ()
-with different variants are allowed;
-all variants will be added to the project file with their appropriate
+variant: The name of this particular variant.  These are typically
+things like "Debug" or "Release", but really can be anything you want.
+Multiple calls to MSVSProject with different variants are allowed: all
+variants will be added to the project file with their appropriate
 build targets and sources.
 
-.BR buildtarget :
-An optional string, node, or list of strings or nodes
-(one per build variant), to tell the Visual Studio debugger
-what output target to use in what build variant.
-The number of
-.B buildtarget
-entries must match the number of
-.B variant
-entries.
+buildtarget: A list of SCons.Node.FS objects which is returned from
+the command which builds the target.  This is used to tell SCons what
+to build when the 'build' button is pressed inside of the IDE.
 
 Example Usage:
 
@@ -1870,59 +1675,6 @@ Example Usage:
                           variant = 'Release')
 .EE
 
-'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.IP MSVSSolution()
-.IP env.MSVSSolution()
-Builds a Microsoft Visual Studio solution file.
-
-This builds a Visual Studio solution file,
-based on the version of Visual Studio that is configured
-(either the latest installed version,
-or the version specified by 
-.B MSVS_VERSION
-in the construction environment).
-For Visual Studio 6, it will generate a
-.B .dsw
-file.
-For Visual Studio 7 (.NET), it will
-generate a
-.B .sln
-file.
-
-The following values must be specified:
-
-.BR target :
-The name of the target .dsw or .sln file.  The correct
-suffix for the version of Visual Studio must be used, but the value
-.B env['MSVSSOLUTIONSUFFIX']
-will be defined to the correct value (see example below).
-
-.BR variant :
-The name of this particular variant, or a list of variant
-names (the latter is only supported for MSVS 7 solutions). These are
-typically things like "Debug" or "Release", but really can be anything
-you want. For MSVS 7 they may also specify target platform, like this
-"Debug|Xbox". Default platform is Win32.
-
-.BR projects :
-A list of project file names, or Project nodes returned by calls to the
-.BR MSVSProject ()
-Builder,
-to be placed into the solution file.
-(NOTE: Currently only one project is supported per solution.)
-It should be noted that these file names are NOT added to the $SOURCES
-environment variable in form of files, but rather as strings.   This
-is because they represent file names to be added to the solution file,
-not the source files used to build the solution file.
-
-Example Usage:
-
-.ES
-        local.MSVSSolution(target = 'Bar' + env['MSVSSOLUTIONSUFFIX'],
-                          projects = ['bar' + env['MSVSPROJECTSUFFIX']],
-                          variant = 'Release')
-.EE
-
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP Object()
 .IP env.Object()
@@ -2210,13 +1962,6 @@ env.SharedObject(target = 'eee.o', source = 'eee.cpp')
 env.SharedObject(target = 'fff.obj', source = 'fff.for')
 .EE
 
-Note that the source files will be scanned
-according to the suffix mappings in
-.B SourceFileScanner
-object.
-See the section "Scanner Objects,"
-below, for a more information.
-
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP StaticLibrary()
 .IP env.StaticLibrary()
@@ -2277,8 +2022,6 @@ Source files must have one of the following extensions:
   .FOR    Fortran file
   .fpp    Fortran file + C pre-processor
   .FPP    Fortran file + C pre-processor
-  .m      Objective C file
-  .mm     Objective C++ file
   .s      assembly language file
   .S      WIN32:  assembly language file
           POSIX:  assembly language file + C pre-processor
@@ -2300,13 +2043,6 @@ env.StaticObject(target = 'bbb.o', source = 'bbb.c++')
 env.StaticObject(target = 'ccc.obj', source = 'ccc.f')
 .EE
 
-Note that the source files will be scanned
-according to the suffix mappings in
-.B SourceFileScanner
-object.
-See the section "Scanner Objects,"
-below, for a more information.
-
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .IP Tar()
 .IP env.Tar()
@@ -2320,12 +2056,6 @@ for a given target;
 each additional call
 adds to the list of entries
 that will be built into the archive.
-Any source directories will
-be scanned for changes to
-any on-disk files,
-regardless of whether or not
-.B scons
-knows about them from other Builder or function calls.
 
 .ES
 env.Tar('src.tar', 'src')
@@ -2392,12 +2122,6 @@ for a given target;
 each additional call
 adds to the list of entries
 that will be built into the archive.
-Any source directories will
-be scanned for changes to
-any on-disk files,
-regardless of whether or not
-.B scons
-knows about them from other Builder or function calls.
 
 .ES
 env.Zip('src.zip', 'src')
@@ -2408,21 +2132,9 @@ env.Zip('stuff', ['subdir1', 'subdir2'])
 env.Zip('stuff', 'another')
 .EE
 
-All
-targets of builder methods automatically depend on their sources.
-An explicit dependency can
-be specified using the 
-.B Depends 
-method of a construction environment (see below).
-
-In addition,
 .B scons
 automatically scans
-source files for various programming languages,
-so the dependencies do not need to be specified explicitly.
-By default, SCons can
-C source files,
-C++ source files,
+C source files, C++ source files,
 Fortran source files with
 .B .F
 (POSIX systems only),
@@ -2437,26 +2149,14 @@ and assembly language files with
 or
 .B .SPP
 files extensions
-for C preprocessor dependencies.
-SCons also has default support
-for scanning D source files,
-You can also write your own Scanners
-to add support for additional source file types.
-These can be added to the default
-Scanner object used by
-the
-.BR Object ()
-.BR StaticObject ()
-and
-.BR SharedObject ()
-Builders by adding them
-to the
-.B SourceFileScanner
-object as follows:
-
-See the section "Scanner Objects,"
-below, for a more information about
-defining your own Scanner objects.
+for C preprocessor dependencies,
+so the dependencies do not need to be specified explicitly.
+In addition, all
+targets of builder methods automatically depend on their sources.
+An explicit dependency can
+be specified using the 
+.B Depends 
+method of a construction environment (see below).
 
 .SS Methods and Functions to Do Things
 In addition to Builder methods,
@@ -2484,14 +2184,6 @@ environment it looks like:
 If you can call the functionality in both ways,
 then both forms are listed.
 
-Global functions may be called from custom Python modules that you
-import into an SConscript file by adding the following
-to the Python module:
-
-.ES
-from SCons.Script import *
-.EE
-
 Except where otherwise noted,
 the same-named
 construction environment method
@@ -2574,17 +2266,11 @@ can be converted into an Action object
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
-.RI Alias( alias ", [" targets ", [" action ]])
+.RI Alias( alias ", [" targets ])
 .TP
-.RI env.Alias( alias ", [" targets ", [" action ]])
+.RI env.Alias( alias ", [" targets ])
 Creates one or more phony targets that
 expand to one or more other targets.
-An optional
-.I action
-(command)
-or list of actions
-can be specified that will be executed
-whenever the any of the alias targets are out-of-date.
 Returns the Node object representing the alias,
 which exists outside of any file system.
 This Node object, or the alias name,
@@ -2592,8 +2278,7 @@ may be used as a dependency of any other target,
 including another alias.
 .B Alias
 can be called multiple times for the same
-alias to add additional targets to the alias,
-or additional actions to the list for this alias.
+alias to add additional targets to the alias.
 
 .ES
 Alias('install')
@@ -2602,8 +2287,6 @@ Alias(['install', 'install-lib'], '/usr/local/lib')
 
 env.Alias('install', ['/usr/local/bin', '/usr/local/lib'])
 env.Alias('install', ['/usr/local/man'])
-
-env.Alias('update', ['file1', 'file2'], "update_database $SOURCES")
 .EE
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@@ -2671,7 +2354,7 @@ Example:
 .ES
 print 'before:',env['ENV']['INCLUDE']
 include_path = '/foo/bar:/foo'
-env.AppendENVPath('INCLUDE', include_path)
+env.PrependENVPath('INCLUDE', include_path)
 print 'after:',env['ENV']['INCLUDE']
 
 yields:
@@ -2936,47 +2619,22 @@ Clean(['foo', 'bar'], 'something_else_to_clean')
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
-.RI Command( target ", " source ", " action ", [" key = val ", ...])"
+.RI Command( target ", " source ", " commands ", [" key = val ", ...])"
 .TP
-.RI env.Command( target ", " source ", " action ", [" key = val ", ...])"
+.RI env.Command( target ", " source ", " commands ", [" key = val ", ...])"
 Executes a specific action
 (or list of actions)
 to build a target file or files.
 This is more convenient
 than defining a separate Builder object
 for a single special-case build.
-
-As a special case, the
-.B source_scanner
-keyword argument can
-be used to specify
-a Scanner object
-that will be used to scan the sources.
-(The global
-.B DirScanner
-object can be used
-if any of the sources will be directories
-that must be scanned on-disk for
-changes to files that aren't
-already specified in other Builder of function calls.)
-
-Any other keyword arguments specified override any
+Any keyword arguments specified override any
 same-named existing construction variables.
 
-An action can be an external command,
+Note that an action can be an external command,
 specified as a string,
 or a callable Python object;
-see "Action Objects," below,
-for more complete information.
-Also note that a string specifying an external command
-may be preceded by an
-.B @
-(at-sign)
-to suppress printing the command in question,
-or by a
-.B \-
-(hyphen)
-to ignore the exit status of the external command.
+see "Action Objects," below.
 Examples:
 
 .ES
@@ -3232,24 +2890,17 @@ EnsurePythonVersion(2,2)
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
-.RI EnsureSConsVersion( major ", " minor ", [" revision ])
+.RI EnsureSConsVersion( major ", " minor )
 .TP
-.RI env.EnsureSConsVersion( major ", " minor ", [" revision ])
+.RI env.EnsureSConsVersion( major ", " minor )
 Ensure that the SCons version is at least 
-.IR major.minor ,
-or
-.IR major.minor.revision . 
-if
-.I revision
-is specified.
+.IR major . minor . 
 This function will
 print out an error message and exit SCons with a non-zero exit code if the
 actual SCons version is not late enough.
 
 .ES
-EnsureSConsVersion(0,14)
-
-EnsureSConsVersion(0,96,90)
+EnsureSConsVersion(0,9)
 .EE
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@@ -3597,7 +3248,7 @@ Returns a list of the target Node or Nodes.
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
-.RI env.ParseConfig( command ", [" function ", " unique ])
+.RI env.ParseConfig( command ", [" function ])
 Calls the specified
 .I function
 to modify the environment as specified by the output of
@@ -3608,25 +3259,15 @@ expects the output of a typical
 .I *-config command
 (for example,
 .BR gtk-config )
-and adds the options
-to the appropriate construction variables.
-By default, 
-duplicate values are not
-added to any construction variables;
-you can specify
-.B unique=0
-to allow duplicate
-values to be added.
-
-By default,
+and parses the returned
 .BR -L ,
 .BR -l ,
 .BR -Wa ,
 .BR -Wl ,
 .BR -Wp ,
 .B -I
-and other options,
-are add to the
+and other options
+into the
 .BR LIBPATH ,
 .BR LIBS ,
 .BR ASFLAGS ,
@@ -3635,7 +3276,7 @@ are add to the
 .B CPPPATH
 and
 .B CCFLAGS
-construction variables,
+variables,
 respectively.
 A returned
 .B -pthread
@@ -3659,7 +3300,7 @@ construction variable.
 .TP
 .RI ParseDepends( filename ", [" must_exist ])
 .TP
-.RI env.ParseDepends( filename ", [" must_exist " " only_one ])
+.RI env.ParseDepends( filename ", [" must_exist ])
 Parses the contents of the specified
 .I filename
 as a list of dependencies in the style of
@@ -3667,7 +3308,6 @@ as a list of dependencies in the style of
 or
 .BR mkdep ,
 and explicitly establishes all of the listed dependencies.
-
 By default,
 it is not an error
 if the specified
@@ -3681,34 +3321,13 @@ scons
 throw an exception and
 generate an error if the file does not exist,
 or is otherwise inaccessible.
-
-The optional
-.I only_one
-argument may be set to a non-zero
-value to have
-scons
-thrown an exception and
-generate an error
-if the file contains dependency
-information for more than one target.
-This can provide a small sanity check
-for files intended to be generated
-by, for example, the
-.B gcc -M
-flag,
-which should typically only
-write dependency information for
-one output file into a corresponding
-.B .d
-file.
-
 The
 .I filename
 and all of the files listed therein
 will be interpreted relative to
 the directory of the
 .I SConscript
-file which calls the
+file which called the
 .B ParseDepends
 function.
 
@@ -3769,10 +3388,8 @@ env.Platform('posix')
 Note that the
 .B win32
 platform adds the
-.B SYSTEMDRIVE
-and
 .B SYSTEMROOT
-variables from the user's external environment
+variable from the user's external environment
 to the construction environment's
 .B ENV
 dictionary.
@@ -3850,7 +3467,7 @@ after: /foo/bar:/foo:/biz
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
-.RI env.PrependUnique( key = val ", [...])"
+.RI env.AppendUnique( key = val ", [...])"
 Appends the specified keyword arguments
 to the beginning of construction variables in the environment.
 If the Environment does not have
@@ -4190,17 +3807,13 @@ SConscript('bar/SConscript')    # will chdir to bar
 This tells
 .B scons
 to store all file signatures
-in the specified database
+in the specified
 .IR file .
 If the
 .I file
-name is omitted,
-.B .sconsign
+is omitted,
+.B .sconsign.dbm
 is used by default.
-(The actual file name(s) stored on disk
-may have an appropriated suffix appended
-by the
-.IR  dbm_module .)
 If
 .I file
 is not an absolute path name,
@@ -4208,20 +3821,6 @@ the file is placed in the same directory as the top-level
 .B SConstruct
 file.
 
-If
-.I file
-is
-.BR None ,
-then
-.B scons
-will store file signatures
-in a separate
-.B .sconsign
-file in each directory,
-not in one global database file.
-(This was the default behavior
-prior to SCons 0.96.91 and 0.97.)
-
 The optional
 .I dbm_module
 argument can be used to specify
@@ -4235,9 +3834,8 @@ and which works on all Python versions from 1.5.2 on.
 Examples:
 
 .ES
-# Explicitly stores signatures in ".sconsign.dblite"
-# in the top-level SConstruct directory (the
-# default behavior).
+# Stores signatures in ".sconsign.dbm"
+# in the top-level SConstruct directory.
 SConsignFile()
 
 # Stores signatures in the file "etc/scons-signatures"
@@ -4246,10 +3844,6 @@ SConsignFile("etc/scons-signatures")
 
 # Stores signatures in the specified absolute file name.
 SConsignFile("/home/me/SCons/signatures")
-
-# Stores signatures in a separate .sconsign file
-# in each directory.
-SConsignFile(None)
 .EE
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
@@ -4392,92 +3986,7 @@ env.SourceCode(['f1.c', 'f2.c'], env.SCCS())
 env.SourceCode('no_source.c', None)
 .EE
 '\"env.SourceCode('.', env.Subversion('file:///usr/local/Subversion'))
-
-'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
-.TP
-.RI env.subst( string ", [" raw ", " target ", " source ", " conv ])
-Performs construction variable interpolation
-on the specified string argument.
-
-By default,
-leading or trailing white space will
-be removed from the result.
-and all sequences of white space
-will be compressed to a single space character.
-Additionally, any
-.B $(
-and
-.B $)
-character sequences will be stripped from the returned string,
-The optional
-.I raw
-argument may be set to
-.B 1
-if you want to preserve white space and
-.BR $( - $)
-sequences.
-The
-.I raw
-argument may be set to
-.B 2
-if you want to strip
-all characters between
-any
-.B $(
-and
-.B $)
-pairs
-(as is done for signature calculation).
-
-The optional
-.I target
-and
-.I source
-keyword arguments
-must be set to lists of
-target and source nodes, respectively,
-if you want the
-.BR $TARGET ,
-.BR $TARGETS ,
-.BR $SOURCE
-and
-.BR $SOURCES
-to be available for expansion.
-This is usually necessary if you are
-calling
-.BR env.subst ()
-from within a Python function used
-as an SCons action.
-
-By default,
-all returned values are converted
-to their string representation.
-The optional
-.I conv
-argument
-may specify a conversion function
-that will be used in place of
-the default.
-For example, if you want Python objects
-(including SCons Nodes)
-to be returned as Python objects,
-you can use the Python
-.B lambda
-idiom to pass in an unnamed function
-that simply returns its unconverted argument.
-
-.ES
-print env.subst("The C compiler is: $CC")
-
-def compile(target, source, env):
-    sourceDir = env.subst("${SOURCE.srcdir}",
-                          target=target,
-                          source=source)
-
-source_nodes = env.subst('$EXPAND_TO_NODELIST',
-                         conv=lambda x: x)
-.EE
-
+'\"
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 '\".TP
 '\".RI Subversion( repository ", " module )
@@ -4711,13 +4220,6 @@ In addition to the global functions and methods,
 supports a number of Python variables
 that can be used in SConscript files
 to affect how you want the build to be performed.
-These variables may be accessed from custom Python modules that you
-import into an SConscript file by adding the following
-to the Python module:
-
-.ES
-from SCons.Script import *
-.EE
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
@@ -4920,15 +4422,6 @@ The static library archiver.
 .IP ARCOM
 The command line used to generate a static library from object files.
 
-.IP ARCOMSTR
-The string displayed when an object file
-is generated from an assembly-language source file.
-If this is not set, then $ARCOM (the command line) is displayed.
-
-.ES
-env = Environment(ARCOMSTR = "Archiving $TARGET")
-.EE
-
 .IP ARFLAGS
 General options passed to the static library archiver.
 
@@ -4939,15 +4432,6 @@ The assembler.
 The command line used to generate an object file
 from an assembly-language source file.
 
-.IP ASCOMSTR
-The string displayed when an object file
-is generated from an assembly-language source file.
-If this is not set, then $ASCOM (the command line) is displayed.
-
-.ES
-env = Environment(ASCOMSTR = "Assembling $TARGET")
-.EE
-
 .IP ASFLAGS
 General options passed to the assembler.
 
@@ -4958,22 +4442,6 @@ after first running the file through the C preprocessor.
 Any options specified in the $ASFLAGS and $CPPFLAGS construction variables
 are included on this command line.
 
-.IP ASPPCOMSTR
-The string displayed when an object file
-is generated from an assembly-language source file
-after first running the file through the C preprocessor.
-If this is not set, then $ASPPCOM (the command line) is displayed.
-
-.ES
-env = Environment(ASPPCOMSTR = "Assembling $TARGET")
-.EE
-
-.IP ASPPFLAGS
-General options when an assembling an assembly-language
-source file into an object file
-after first running the file through the C preprocessor.
-The default is to use the value of $ASFLAGS.
-
 .IP BIBTEX
 The bibliography generator for the TeX formatter and typesetter and the
 LaTeX structured formatter and typesetter.
@@ -4983,16 +4451,6 @@ The command line used to call the bibliography generator for the
 TeX formatter and typesetter and the LaTeX structured formatter and
 typesetter.
 
-.IP BIBTEXCOMSTR
-The string displayed when generating a bibliography
-for TeX or LaTeX.
-If this is not set, then $BIBTEXCOM (the command line) is displayed.
-
-.ES
-env = Environment(BIBTEXCOMSTR = "Generating bibliography $TARGET")
-.EE
-
-
 .IP BIBTEXFLAGS
 General options passed to the bibliography generator for the TeX formatter
 and typesetter and the LaTeX structured formatter and typesetter.
@@ -5002,13 +4460,7 @@ The BitKeeper executable.
 
 .IP BITKEEPERCOM
 The command line for
-fetching source files using BitKeeper.
-
-.IP BITKEEPERCOMSTR
-The string displayed when fetching
-a source file using BitKeeper.
-If this is not set, then $BITKEEPERCOM
-(the command line) is displayed.
+fetching source files using BitKEeper.
 
 .IP BITKEEPERGET
 The command ($BITKEEPER) and subcommand
@@ -5057,15 +4509,6 @@ The command line used to compile a C source file to a (static) object file.
 Any options specified in the $CCFLAGS and $CPPFLAGS construction variables
 are included on this command line.
 
-.IP CCCOMSTR
-The string displayed when a C source file
-is compiled to a (static) object file.
-If this is not set, then $CCCOM (the command line) is displayed.
-
-.ES
-env = Environment(CCCOMSTR = "Compiling static object $TARGET")
-.EE
-
 .IP CCFLAGS 
 General options that are passed to the C compiler.
 
@@ -5099,25 +4542,6 @@ called to transform the list before concatenation.
 env['_CPPINCFLAGS'] = '$( ${_concat(INCPREFIX, CPPPATH, INCSUFFIX, __env__, RDirs)} $)',
 .EE
 
-.IP CONFIGUREDIR
-The name of the directory in which
-Configure context test files are written.
-The default is
-.B .sconf_temp
-in the top-level directory
-containing the
-.B SConstruct
-file.
-
-.IP CONFIGURELOG
-The name of the Configure context log file.
-The default is
-.B config.log
-in the top-level directory
-containing the
-.B SConstruct
-file.
-
 .IP CPPDEFINES
 A platform independent specification of C preprocessor definitions.
 The definitions will be added to command lines
@@ -5289,7 +4713,6 @@ The default list is:
 [".c", ".C", ".cxx", ".cpp", ".c++", ".cc",
  ".h", ".H", ".hxx", ".hpp", ".hh",
  ".F", ".fpp", ".FPP",
- ".m", ".mm",
  ".S", ".spp", ".SPP"]
 .EE
 
@@ -5303,12 +4726,6 @@ Options that are passed to the CVS checkout subcommand.
 The command line used to
 fetch source files from a CVS repository.
 
-.IP CVSCOMSTR
-The string displayed when fetching
-a source file from a CVS repository.
-If this is not set, then $CVSCOM
-(the command line) is displayed.
-
 .IP CVSFLAGS
 General options that are passed to CVS.
 By default, this is set to
@@ -5335,10 +4752,7 @@ SCons also treats files with the suffixes
 .IR .c++ ,
 and
 .I .C++
-as C++ files,
-and files with
-.I .mm
-suffixes as Objective C++ files.
+as C++ files.
 On case-sensitive systems (Linux, UNIX, and other POSIX-alikes),
 SCons also treats
 .I .C
@@ -5350,21 +4764,8 @@ The command line used to compile a C++ source file to an object file.
 Any options specified in the $CXXFLAGS and $CPPFLAGS construction variables
 are included on this command line.
 
-.IP CXXCOMSTR
-The string displayed when a C++ source file
-is compiled to a (static) object file.
-If this is not set, then $CXXCOM (the command line) is displayed.
-
-.ES
-env = Environment(CXXCOMSTR = "Compiling static object $TARGET")
-.EE
-
 .IP CXXFLAGS 
 General options that are passed to the C++ compiler.
-By default, this includes the value of $CCFLAGS,
-so that setting $CCFLAGS affects both C and C++ compilation.
-If you want to add C++-specific flags,
-you must set or override the value of $CXXFLAGS.
 
 .IP CXXVERSION
 The version number of the C++ compiler.
@@ -5372,12 +4773,8 @@ This may or may not be set,
 depending on the specific C++ compiler being used.
 
 .IP Dir
-A function that converts a string
-into a Dir instance relative to the target being built. 
-
-.IP Dirs
-A function that converts a list of strings
-into a list of Dir instances relative to the target being built.
+A function that converts a file name into a Dir instance relative to the
+target being built. 
 
 .IP DSUFFIXES
 The list of suffixes of files that will be scanned
@@ -5397,11 +4794,6 @@ General options passed to the TeX DVI file to PDF file converter.
 .IP DVIPDFCOM
 The command line used to convert TeX DVI files into a PDF file.
 
-.IP DVIPDFCOMSTR
-The string displayed when a TeX DVI file
-is converted into a PDF file.
-If this is not set, then $DVIPDFCOM (the command line) is displayed.
-
 .IP DVIPS
 The TeX DVI file to PostScript converter.
 
@@ -5477,11 +4869,6 @@ You should normally set the $FORTRANCOM variable,
 which specifies the default command line
 for all Fortran versions.
 
-.IP F77COMSTR
-The string displayed when a Fortran 77 source file
-is compiled to an object file.
-If this is not set, then $F77COM or $FORTRANCOM (the command line) is displayed.
-
 .IP F77FLAGS
 General user-specified options that are passed to the Fortran 77 compiler.
 Note that this variable does
@@ -5587,12 +4974,6 @@ You should normally set the $FORTRANCOM variable,
 which specifies the default command line
 for all Fortran versions.
 
-.IP F90COMSTR
-The string displayed when a Fortran 90 source file
-is compiled to an object file.
-If this is not set, then $F90COM or $FORTRANCOM
-(the command line) is displayed.
-
 .IP F90FLAGS
 General user-specified options that are passed to the Fortran 90 compiler.
 Note that this variable does
@@ -5698,12 +5079,6 @@ You should normally set the $FORTRANCOM variable,
 which specifies the default command line
 for all Fortran versions.
 
-.IP F95COMSTR
-The string displayed when a Fortran 95 source file
-is compiled to an object file.
-If this is not set, then $F95COM or $FORTRANCOM
-(the command line) is displayed.
-
 .IP F95FLAGS
 General user-specified options that are passed to the Fortran 95 compiler.
 Note that this variable does
@@ -5804,12 +5179,6 @@ in the $FORTRANFLAGS, $CPPFLAGS, $_CPPDEFFLAGS,
 $_FORTRANMODFLAG, and $_FORTRANINCFLAGS construction variables
 are included on this command line.
 
-.IP FORTRANCOMSTR
-The string displayed when a Fortran source file
-is compiled to an object file.
-If this is not set, then $FORTRANCOM
-(the command line) is displayed.
-
 .IP FORTRANFLAGS
 General user-specified options that are passed to the Fortran compiler.
 Note that this variable does
@@ -5837,7 +5206,7 @@ of each directory in $FORTRANPATH.
 Directory location where the Fortran compiler should place
 any module files it generates.  This variable is empty, by default. Some 
 Fortran compilers will internally append this directory in the search path 
-for module files, as well.
+for module files, as well
 
 .IP FORTRANMODDIRPREFIX
 The prefix used to specify a module directory on the Fortran compiler command
@@ -5853,7 +5222,7 @@ This will be appended to the beginning of the directory
 in the $FORTRANMODDIR construction variables
 when the $_FORTRANMODFLAG variables is automatically generated.
 
-.IP _FORTRANMODFLAG
+.IP FORTRANMODFLAG
 An automatically-generated construction variable
 containing the Fortran compiler command-line option
 for specifying the directory location where the Fortran
@@ -5948,70 +5317,9 @@ The default list is:
 .EE
 
 .IP File
-A function that converts a string into a File instance relative to the
+A function that converts a file name into a File instance relative to the
 target being built. 
 
-.IP FRAMEWORKPATH
-On Mac OS X with gcc,
-a list containing the paths to search for frameworks.
-Used by the compiler to find framework-style includes like
-#include <Fmwk/Header.h>.
-Used by the linker to find user-specified frameworks when linking (see
-$FRAMEWORKS).
-For example:
-.ES
- env.AppendUnique(FRAMEWORKPATH='#myframeworkdir')
-.EE
-.IP
-will add
-.ES
-  ... -Fmyframeworkdir
-.EE
-.IP
-to the compiler and linker command lines.
-
-.IP _FRAMEWORKPATH
-On Mac OS X with gcc, an automatically-generated construction variable
-containing the linker command-line options corresponding to FRAMEWORKPATH.
-
-.IP FRAMEWORKPATHPREFIX
-On Mac OS X with gcc, the prefix to be used for the FRAMEWORKPATH entries.
-(see $FRAMEWORKPATH).
-The default value is
-.BR -F .
-
-.IP FRAMEWORKPREFIX
-On Mac OS X with gcc,
-the prefix to be used for linking in frameworks
-(see $FRAMEWORKS).
-The default value is
-.BR -framework .
-
-.IP FRAMEWORKS
-On Mac OS X with gcc, a list of the framework names to be linked into a
-program or shared library or bundle.
-The default value is the empty list.
-For example:
-.ES
- env.AppendUnique(FRAMEWORKS=Split('System Cocoa SystemConfiguration'))
-.EE
-
-.IP _FRAMEWORKS
-On Mac OS X with gcc,
-an automatically-generated construction variable
-containing the linker command-line options
-for linking with FRAMEWORKS.
-
-.IP FRAMEWORKSFLAGS
-On Mac OS X with gcc,
-general user-supplied frameworks options to be added at
-the end of a command
-line building a loadable module.
-(This has been largely superceded by
-the $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX,
-$FRAMWORKPREFIX and $FRAMEWORKS variables
-described above.)
-
 .IP GS
 The Ghostscript program used to convert PostScript to PDF files.
 
@@ -6022,12 +5330,6 @@ when converting PostScript to PDF files.
 .IP GSCOM
 The Ghostscript command line used to convert PostScript to PDF files.
 
-.IP GSCOMSTR
-The string displayed when
-Ghostscript is used to convert
-a PostScript file to a PDF file.
-If this is not set, then $GSCOM (the command line) is displayed.
-
 .IP IDLSUFFIXES
 The list of suffixes of files that will be scanned
 for IDL implicit dependencies
@@ -6075,19 +5377,6 @@ is the construction environment
 (a dictionary of construction values)
 in force for this file installation.
 
-.IP INSTALLSTR
-The string displayed when a file is
-installed into a destination file name.
-The default is:
-.ES
-Install file: "$SOURCE" as "$TARGET"
-.EE
-
-.IP INTEL_C_COMPILER_VERSION
-Set by the "intelc" Tool
-to the major version number of the Intel C compiler
-selected for use.
-
 .IP JAR
 The Java archive tool.
 
@@ -6100,15 +5389,6 @@ option).
 .IP JARCOM
 The command line used to call the Java archive tool.
 
-.IP JARCOMSTR
-The string displayed when the Java archive tool
-is called
-If this is not set, then $JARCOM (the command line) is displayed.
-
-.ES
-env = Environment(JARCOMSTR = "JARchiving $SOURCES into $TARGET")
-.EE
-
 .IP JARFLAGS
 General options passed to the Java archive tool.
 By default this is set to
@@ -6132,16 +5412,6 @@ corresponding Java class files.
 Any options specified in the $JAVACFLAGS construction variable
 are included on this command line.
 
-.IP JAVACCOMSTR
-The string displayed when compiling
-a directory tree of Java source files to
-corresponding Java class files.
-If this is not set, then $JAVACCOM (the command line) is displayed.
-
-.ES
-env = Environment(JAVACCOMSTR = "Compiling class files $TARGETS from $SOURCES")
-.EE
-
 .IP JAVACFLAGS
 General options that are passed to the Java compiler.
 
@@ -6166,15 +5436,6 @@ from Java classes.
 Any options specified in the $JAVAHFLAGS construction variable
 are included on this command line.
 
-.IP JAVAHCOMSTR
-The string displayed when C header and stub files
-are generated from Java classes.
-If this is not set, then $JAVAHCOM (the command line) is displayed.
-
-.ES
-env = Environment(JAVAHCOMSTR = "Generating header/stub file(s) $TARGETS from $SOURCES")
-.EE
-
 .IP JAVAHFLAGS
 General options passed to the C header and stub file generator
 for Java classes.
@@ -6190,66 +5451,9 @@ The LaTeX structured formatter and typesetter.
 .IP LATEXCOM
 The command line used to call the LaTeX structured formatter and typesetter.
 
-.IP LATEXCOMSTR
-The string displayed when calling
-the LaTeX structured formatter and typesetter.
-If this is not set, then $LATEXCOM (the command line) is displayed.
-
-.ES
-env = Environment(LATEXCOMSTR = "Building $TARGET from LaTeX input $SOURCES")
-.EE
-
 .IP LATEXFLAGS
 General options passed to the LaTeX structured formatter and typesetter.
 
-.IP LATEXRETRIES
-The maximum number of times that LaTeX
-will be re-run if the
-.B .log
-generated by the $LATEXCOM command
-indicates that there are undefined references.
-The default is to try to resolve undefined references
-by re-running LaTeX up to three times.
-
-.IP LATEXSUFFIXES
-The list of suffixes of files that will be scanned
-for LaTeX implicit dependencies
-(\\include or \\import files).
-The default list is:
-
-.ES
-[".tex", ".ltx", ".latex"]
-.EE
-
-.IP LDMODULE
-The linker for building loadable modules.
-By default, this is the same as $SHLINK.
-
-.IP LDMODULECOM
-The command line for building loadable modules.
-On Mac OS X, this uses the $LDMODULE,
-$LDMODULEFLAGS and $FRAMEWORKSFLAGS variables.
-On other systems, this is the same as $SHLINK.
-
-.IP LDMODULECOMSTR
-The string displayed when building loadable modules.
-If this is not set, then $LDMODULECOM (the command line) is displayed.
-
-.IP LDMODULEFLAGS
-General user options passed to the linker for building loadable modules.
-
-.IP LDMODULEPREFIX
-The prefix used for loadable module file names.
-On Mac OS X, this is null;
-on other systems, this is
-the same as $SHLIBPREFIX.
-
-.IP LDMODULESUFFIX
-The suffix used for loadable module file names.
-On Mac OS X, this is null;
-on other systems, this is
-the same as $SHLIBSUFFIX.
-
 .IP LEX
 The lexical analyzer generator.
 
@@ -6260,15 +5464,6 @@ General options passed to the lexical analyzer generator.
 The command line used to call the lexical analyzer generator
 to generate a source file.
 
-.IP LEXCOMSTR
-The string displayed when generating a source file
-using the lexical analyzer generator.
-If this is not set, then $LEXCOM (the command line) is displayed.
-
-.ES
-env = Environment(LEXCOMSTR = "Lex'ing $TARGET from $SOURCES")
-.EE
-
 .IP _LIBDIRFLAGS
 An automatically-generated construction variable
 containing the linker command-line options
@@ -6297,7 +5492,7 @@ for specifying libraries to be linked with the resulting target.
 The value of $_LIBFLAGS is created
 by appending $LIBLINKPREFIX and $LIBLINKSUFFIX
 to the beginning and end
-of each filename in $LIBS.
+of each directory in $LIBS.
 
 .IP LIBLINKPREFIX
 The prefix used to specify a library to link on the linker command line.
@@ -6383,7 +5578,7 @@ appending the values of the
 $LIBLINKPREFIX and $LIBLINKSUFFIX
 construction variables
 to the beginning and end
-of each filename in $LIBS.
+of each directory in $LIBS.
 Any command lines you define that need
 the LIBS library list should
 include $_LIBFLAGS:
@@ -6392,26 +5587,6 @@ include $_LIBFLAGS:
 env = Environment(LINKCOM="my_linker $_LIBDIRFLAGS $_LIBFLAGS -o $TARGET $SOURCE")
 .EE
 
-.IP
-If you add a
-File
-object to the
-LIBS
-list, the name of that file will be added to
-$_LIBFLAGS,
-and thus the link line, as is, without
-$LIBLINKPREFIX
-or
-$LIBLINKSUFFIX.
-For example:
-.ES
-env.Append(LIBS=File('/tmp/mylib.so'))
-.EE
-
-.IP
-In all cases, scons will add dependencies from the executable program to
-all the libraries in this list.
-
 .IP LIBSUFFIX 
 The suffix used for (static) library file names.
 A default value is set for each platform
@@ -6449,15 +5624,6 @@ for the variable that expands to library search path options.
 .IP LINKCOM
 The command line used to link object files into an executable.
 
-.IP LINKCOMSTR
-The string displayed when object files
-are linked into an executable.
-If this is not set, then $LINKCOM (the command line) is displayed.
-
-.ES
-env = Environment(LINKCOMSTR = "Linking $TARGET")
-.EE
-
 .IP M4
 The M4 macro preprocessor.
 
@@ -6465,31 +5631,7 @@ The M4 macro preprocessor.
 General options passed to the M4 macro preprocessor.
 
 .IP M4COM
-The command line used to pass files through the M4 macro preprocessor.
-
-.IP M4COMSTR
-The string displayed when
-a file is passed through the M4 macro preprocessor.
-If this is not set, then $M4COM (the command line) is displayed.
-
-.IP MAKEINDEX
-The makeindex generator for the TeX formatter and typesetter and the
-LaTeX structured formatter and typesetter.
-
-.IP MAKEINDEXCOM
-The command line used to call the makeindex generator for the
-TeX formatter and typesetter and the LaTeX structured formatter and
-typesetter.
-
-.IP MAKEINDEXCOMSTR
-The string displayed when calling the makeindex generator for the
-TeX formatter and typesetter
-and the LaTeX structured formatter and typesetter.
-If this is not set, then $MAKEINDEXCOM (the command line) is displayed.
-
-.IP MAKEINDEXFLAGS
-General options passed to the makeindex generator for the TeX formatter
-and typesetter and the LaTeX structured formatter and typesetter.
+The command line used to pass files through the macro preprocessor.
 
 .IP MAXLINELENGTH
 The maximum number of characters allowed on an external command line.
@@ -6564,81 +5706,6 @@ For VS7, it is:
 .IP
 Where '<VSDir>' is the installed location of Visual Studio.
 
-.IP MSVS_PROJECT_BASE_PATH
-The string
-placed in a generated Microsoft Visual Studio solution file
-as the value of the
-.B SccProjectFilePathRelativizedFromConnection0
-and 
-.B SccProjectFilePathRelativizedFromConnection1
-attributes of the
-.B GlobalSection(SourceCodeControl)
-section.
-There is no default value.
-
-.IP MSVS_PROJECT_GUID
-The string
-placed in a generated Microsoft Visual Studio project file
-as the value of the
-.B ProjectGUID
-attribute.
-The string is also placed in the
-.B SolutionUniqueID
-attribute of the
-.B GlobalSection(SourceCodeControl)
-section of the Microsoft Visual Studio solution file.
-There is no default value.
-
-.IP MSVS_SCC_AUX_PATH
-The path name
-placed in a generated Microsoft Visual Studio project file
-as the value of the
-.B SccAuxPath
-attribute
-if the
-.I MSVS_SCC_PROVIDER
-construction variable is also set.
-There is no default value.
-
-.IP MSVS_SCC_LOCAL_PATH
-The path name
-placed in a generated Microsoft Visual Studio project file
-as the value of the
-.B SccLocalPath
-attribute
-if the
-.I MSVS_SCC_PROVIDER
-construction variable is also set.
-The path name is also placed in the
-.B SccLocalPath0
-and
-.B SccLocalPath1
-attributes of the
-.B GlobalSection(SourceCodeControl)
-section of the Microsoft Visual Studio solution file.
-There is no default value.
-
-.IP MSVS_SCC_PROJECT_NAME
-The project name
-placed in a generated Microsoft Visual Studio project file
-as the value of the
-.B SccProjectName
-attribute.
-There is no default value.
-
-.IP MSVS_SCC_PROVIDER
-The string
-placed in a generated Microsoft Visual Studio project file
-as the value of the
-.B SccProvider
-attribute.
-The string is also placed in the
-.B SccProvider1
-attribute of the
-.B GlobalSection(SourceCodeControl)
-section of the Microsoft Visual Studio solution file.
-There is no default value.
-
 .IP MSVS_USE_MFC_DIRS
 Tells the MS Visual Studio tool(s) to use
 the MFC directories in its default paths
@@ -6697,36 +5764,17 @@ environment variables are set explictly.
 Sets the preferred version of MSVS to use.
 
 SCons will (by default) select the latest version of MSVS
-installed on your machine.
-So, if you have version 6 and version 7 (MSVS .NET) installed,
-it will prefer version 7.
-You can override this by
+installed on your machine.  So, if you have version 6 and version 7
+(MSVS .NET) installed, it will prefer version 7.  You can override this by
 specifying the 
 .B MSVS_VERSION
 variable in the Environment initialization, setting it to the
 appropriate version ('6.0' or '7.0', for example).
 If the given version isn't installed, tool initialization will fail.
 
-.IP MSVSBUILDCOM
-The build command line placed in
-a generated Microsoft Visual Studio project file.
-The default is to have Visual Studio invoke SCons with any specified
-build targets.
-
-.IP MSVSCLEANCOM
-The clean command line placed in
-a generated Microsoft Visual Studio project file.
-The default is to have Visual Studio invoke SCons with the -c option
-to remove any specified targets.
-
-.IP MSVSENCODING
-The encoding string placed in
-a generated Microsoft Visual Studio project file.
-The default is encoding
-.BR Windows-1252 .
-
 .IP MSVSPROJECTCOM
-The action used to generate Microsoft Visual Studio project files.
+The action used to generate Microsoft Visual Studio
+project and solution files.
 
 .IP MSVSPROJECTSUFFIX
 The suffix used for Microsoft Visual Studio project (DSP) files.
@@ -6737,45 +5785,6 @@ and
 .B .dsp
 when using earlier versions of Visual Studio.
 
-.IP MSVSREBUILDCOM
-The rebuild command line placed in
-a generated Microsoft Visual Studio project file.
-The default is to have Visual Studio invoke SCons with any specified
-rebuild targets.
-
-.IP MSVSSCONS
-The SCons used in generated Microsoft Visual Studio project files.
-The default is the version of SCons being
-used to generate the project file.
-
-.IP MSVSSCONSFLAGS
-The SCons flags used in generated Microsoft Visual Studio
-project files.
-
-.IP MSVSSCONSCOM
-The default SCons command used in generated Microsoft Visual Studio
-project files.
-
-.IP MSVSSCONSCRIPT
-The sconscript file
-(that is,
-.B SConstruct
-or
-.B SConscript
-file)
-that will be invoked by Visual Studio
-project files
-(through the
-.B MSVSSCONSCOM
-variable).
-The default is the same sconscript file
-that contains the call to
-.BR MSVSProject ()
-to build the project file.
-
-.IP MSVSSOLUTIONCOM
-The action used to generate Microsoft Visual Studio solution files.
-
 .IP MSVSSOLUTIONSUFFIX
 The suffix used for Microsoft Visual Studio solution (DSW) files.
 The default value is
@@ -6785,14 +5794,6 @@ and
 .B .dsw
 when using earlier versions of Visual Studio.
 
-.IP MWCW_VERSION
-The version number of the MetroWerks CodeWarrior C compiler
-to be used.
-
-.IP MWCW_VERSIONS
-A list of installed versions of the MetroWerks CodeWarrior C compiler
-on this system.
-
 .IP no_import_lib
 When set to non-zero,
 suppresses creation of a corresponding Win32 static import lib by the
@@ -6816,11 +5817,6 @@ The Perforce executable.
 The command line used to
 fetch source files from Perforce.
 
-.IP P4COMSTR
-The string displayed when
-fetching a source file from Perforce.
-If this is not set, then $P4COM (the command line) is displayed.
-
 .IP P4FLAGS
 General options that are passed to Perforce.
 
@@ -6836,15 +5832,6 @@ dependencies for the PCH file. Example:
 env['PCH'] = 'StdAfx.pch'
 .EE
 
-.IP PCHCOM
-The command line used by the
-.B PCH
-builder to generated a precompiled header.
-
-.IP PCHCOMSTR
-The string displayed when generating a precompiled header.
-If this is not set, then $PCHCOM (the command line) is displayed.
-
 .IP PCHSTOP
 This variable specifies how much of a source file is precompiled. This
 variable is ignored by tools other than Microsoft Visual C++, or when
@@ -6941,11 +5928,6 @@ The suffix used for executable file names.
 .IP PSCOM
 The command line used to convert TeX DVI files into a PostScript file.
 
-.IP PSCOMSTR
-The string displayed when a TeX DVI file
-is converted into a PostScript file.
-If this is not set, then $PSCOM (the command line) is displayed.
-
 .IP PSPREFIX
 The prefix used for PostScript file names.
 
@@ -7044,17 +6026,9 @@ cpp file.
 .IP QT_MOCFROMCXXCOM
 Command to generate a moc file from a cpp file.
 
-.IP QT_MOCFROMCXXCOMSTR
-The string displayed when generating a moc file from a cpp file.
-If this is not set, then $QT_MOCFROMCXXCOM (the command line) is displayed.
-
 .IP QT_MOCFROMHCOM
 Command to generate a moc file from a header.
 
-.IP QT_MOCFROMHCOMSTR
-The string displayed when generating a moc file from a cpp file.
-If this is not set, then $QT_MOCFROMHCOM (the command line) is displayed.
-
 .IP QT_MOCFROMHFLAGS
 Default value is ''. These flags are passed to moc, when moccing a header
 file.
@@ -7069,13 +6043,9 @@ a header.
 .IP QT_UIC
 Default value is '$QT_BINPATH/uic'.
 
-.IP QT_UICCOM
+.IP QT_UICDECLCOM
 Command to generate header files from .ui files.
 
-.IP QT_UICCOMSTR
-The string displayed when generating header files from .ui files.
-If this is not set, then $QT_UICCOM (the command line) is displayed.
-
 .IP QT_UICDECLFLAGS
 Default value is ''. These flags are passed to uic, when creating a a h
 file from a .ui file.
@@ -7086,6 +6056,9 @@ Default value is ''. Prefix for uic generated header files.
 .IP QT_UICDECLSUFFIX
 Default value is '.h'. Suffix for uic generated header files.
 
+.IP QT_UICIMPLCOM
+Command to generate cxx files from .ui files.
+
 .IP QT_UICIMPLFLAGS
 Default value is ''. These flags are passed to uic, when creating a cxx
 file from a .ui file.
@@ -7107,17 +6080,10 @@ The archive indexer.
 General options passed to the archive indexer.
 
 .IP RC
-The resource compiler used to build
-a Microsoft Visual C++ resource file.
+The resource compiler used by the RES builder.
 
 .IP RCCOM
-The command line used to build
-a Microsoft Visual C++ resource file.
-
-.IP RCCOMSTR
-The string displayed when invoking the resource compiler
-to build a Microsoft Visual C++ resource file.
-If this is not set, then $RCCOM (the command line) is displayed.
+The command line used by the RES builder.
 
 .IP RCFLAGS
 The flags passed to the resource compiler by the RES builder.
@@ -7138,32 +6104,11 @@ used to fetch source files from RCS.
 The command line used to
 fetch (checkout) source files from RCS.
 
-.IP RCS_COCOMSTR
-The string displayed when fetching
-a source file from RCS.
-If this is not set, then $RCS_COCOM
-(the command line) is displayed.
-
 .IP RCS_COFLAGS
 Options that are passed to the $RCS_CO command.
 
-.IP REGSVR
-The program used to register DLLs on Windows systems.
-
-.IP REGSVRCOM
-The command line used to register a newly-built DLL file
-on Windows systems.
-Invoked when the "register=1"
-keyword argument is passed to the
-.B SharedLibrary
-Builder.
-
-.IP REGSVRCOMSTR
-The string displayed when registering a newly-built DLL file.
-If this is not set, then $REGSVRCOM (the command line) is displayed.
-
 .IP RDirs
-A function that converts a string into a list of Dir instances by
+A function that converts a file name into a list of Dir instances by
 searching the repositories. 
 
 .IP RMIC
@@ -7176,16 +6121,6 @@ from Java classes that contain RMI implementations.
 Any options specified in the $RMICFLAGS construction variable
 are included on this command line.
 
-.IP RMICCOMSTR
-The string displayed when compiling
-stub and skeleton class files
-from Java classes that contain RMI implementations.
-If this is not set, then $RMICCOM (the command line) is displayed.
-
-.ES
-env = Environment(RMICCOMSTR = "Generating stub/skeleton class files $TARGETS from $SOURCES")
-.EE
-
 .IP RMICFLAGS
 General options passed to the Java RMI stub compiler.
 
@@ -7225,8 +6160,7 @@ construction variable.
 
 .IP RPATH
 A list of paths to search for shared libraries when running programs.
-Currently only used in the GNU (gnulink),
-IRIX (sgilink) and Sun (sunlink) linkers.
+Currently only used in the GNU linker (gnulink) and IRIX linker (sgilink).
 Ignored on platforms and toolchains that don't support it.
 Note that the paths added to RPATH
 are not transformed by
@@ -7252,12 +6186,6 @@ The SCCS executable.
 The command line used to
 fetch source files from SCCS.
 
-.IP SCCSCOMSTR
-The string displayed when fetching
-a source file from a CVS repository.
-If this is not set, then $SCCSCOM
-(the command line) is displayed.
-
 .IP SCCSFLAGS
 General options that are passed to SCCS.
 
@@ -7267,15 +6195,6 @@ This can be set, for example, to
 .I -e
 to check out editable files from SCCS.
 
-.IP SCONS_HOME
-The (optional) path to the SCons library directory,
-initialized from the external environment.
-If set, this is used to construct a shorter and more
-efficient search path in the
-.B MSVSSCONS
-command line executed
-from Microsoft Visual Studio project files.
-
 .IP SHCC
 The C compiler used for generating shared-library objects.
 
@@ -7285,15 +6204,6 @@ to a shared-library object file.
 Any options specified in the $SHCCFLAGS and $CPPFLAGS construction variables
 are included on this command line.
 
-.IP SHCCCOMSTR
-The string displayed when a C source file
-is compiled to a shared object file.
-If this is not set, then $SHCCCOM (the command line) is displayed.
-
-.ES
-env = Environment(SHCCCOMSTR = "Compiling shared object $TARGET")
-.EE
-
 .IP SHCCFLAGS
 Options that are passed to the C compiler
 to generate shared-library objects.
@@ -7307,15 +6217,6 @@ to a shared-library object file.
 Any options specified in the $SHCXXFLAGS and $CPPFLAGS construction variables
 are included on this command line.
 
-.IP SHCXXCOMSTR
-The string displayed when a C++ source file
-is compiled to a shared object file.
-If this is not set, then $SHCXXCOM (the command line) is displayed.
-
-.ES
-env = Environment(SHCXXCOMSTR = "Compiling shared object $TARGET")
-.EE
-
 .IP SHCXXFLAGS
 Options that are passed to the C++ compiler
 to generate shared-library objects.
@@ -7345,12 +6246,6 @@ You should normally set the $SHFORTRANCOM variable,
 which specifies the default command line
 for all Fortran versions.
 
-.IP SHF77COMSTR
-The string displayed when a Fortran 77 source file
-is compiled to a shared-library object file.
-If this is not set, then $SHF77COM or $SHFORTRANCOM
-(the command line) is displayed.
-
 .IP SHF77FLAGS
 Options that are passed to the Fortran 77 compiler
 to generated shared-library objects.
@@ -7390,12 +6285,6 @@ You should normally set the $SHFORTRANCOM variable,
 which specifies the default command line
 for all Fortran versions.
 
-.IP SHF90COMSTR
-The string displayed when a Fortran 90 source file
-is compiled to a shared-library object file.
-If this is not set, then $SHF90COM or $SHFORTRANCOM
-(the command line) is displayed.
-
 .IP SHF90FLAGS
 Options that are passed to the Fortran 90 compiler
 to generated shared-library objects.
@@ -7435,12 +6324,6 @@ You should normally set the $SHFORTRANCOM variable,
 which specifies the default command line
 for all Fortran versions.
 
-.IP SHF95COMSTR
-The string displayed when a Fortran 95 source file
-is compiled to a shared-library object file.
-If this is not set, then $SHF95COM or $SHFORTRANCOM
-(the command line) is displayed.
-
 .IP SHF95FLAGS
 Options that are passed to the Fortran 95 compiler
 to generated shared-library objects.
@@ -7470,12 +6353,6 @@ The default Fortran compiler used for generating shared-library objects.
 The command line used to compile a Fortran source file
 to a shared-library object file.
 
-.IP FORTRANCOMSTR
-The string displayed when a Fortran source file
-is compiled to a shared-library object file.
-If this is not set, then $SHFORTRANCOM
-(the command line) is displayed.
-
 .IP SHFORTRANFLAGS
 Options that are passed to the Fortran compiler
 to generate shared-library objects.
@@ -7497,17 +6374,6 @@ The suffix used for shared library file names.
 .IP SHLINK
 The linker for programs that use shared libraries.
 
-.IP SHLINKCOM
-The command line used to link programs using shared libaries.
-
-.IP SHLINKCOMSTR
-The string displayed when programs using shared libraries are linked.
-If this is not set, then $SHLINKCOM (the command line) is displayed.
-
-.ES
-env = Environment(SHLINKCOMSTR = "Linking shared $TARGET")
-.EE
-
 .IP SHLINKFLAGS
 General user options passed to the linker for programs using shared libraries.
 Note that this variable should
@@ -7597,11 +6463,6 @@ construction variable.
 The command line used to call
 the scripting language wrapper and interface generator.
 
-.IP SWIGCOMSTR
-The string displayed when calling
-the scripting language wrapper and interface generator.
-If this is not set, then $SWIGCOM (the command line) is displayed.
-
 .IP SWIGCXXFILESUFFIX
 The suffix that will be used for intermediate C++
 source files generated by
@@ -7638,15 +6499,6 @@ The tar archiver.
 .IP TARCOM
 The command line used to call the tar archiver.
 
-.IP TARCOMSTR
-The string displayed when archiving files
-using the tar archiver.
-If this is not set, then $TARCOM (the command line) is displayed.
-
-.ES
-env = Environment(TARCOMSTR = "Archiving $TARGET")
-.EE
-
 .IP TARFLAGS
 General options passed to the tar archiver.
 
@@ -7663,38 +6515,15 @@ that may not be set or used in a construction environment.
 .IP TARSUFFIX 
 The suffix used for tar file names.
 
-.IP TEMPFILEPREFIX
-The prefix for a temporary file used
-to execute lines longer than $MAXLINELENGTH.
-The default is '@'.
-This may be set for toolchains that use other values,
-such as '-@' for the diab compiler
-or '-via' for ARM toolchain.
-
 .IP TEX
 The TeX formatter and typesetter.
 
 .IP TEXCOM
 The command line used to call the TeX formatter and typesetter.
 
-.IP TEXCOMSTR
-The string displayed when calling
-the TeX formatter and typesetter.
-If this is not set, then $TEXCOM (the command line) is displayed.
-
-.ES
-env = Environment(TEXCOMSTR = "Building $TARGET from TeX input $SOURCES")
-.EE
-
 .IP TEXFLAGS
 General options passed to the TeX formatter and typesetter.
 
-.IP TEXINPUTS
-List of directories that the LaTeX programm will search
-for include directories.
-The LaTeX implicit dependency scanner will search these
-directories for \include and \import files.
-
 .IP TOOLS
 A list of the names of the Tool specifications
 that are part of this construction environment.
@@ -7719,15 +6548,6 @@ The parser generator.
 The command line used to call the parser generator
 to generate a source file.
 
-.IP YACCCOMSTR
-The string displayed when generating a source file
-using the parser generator.
-If this is not set, then $YACCCOM (the command line) is displayed.
-
-.ES
-env = Environment(YACCCOMSTR = "Yacc'ing $TARGET from $SOURCES")
-.EE
-
 .IP YACCFLAGS
 General options passed to the parser generator.
 If $YACCFLAGS contains a \-d option,
@@ -7736,34 +6556,6 @@ SCons assumes that the call will also create a .h file
 or a .hpp file
 (if the yacc source file ends in a .yy suffix)
 
-.IP YACCHFILESUFFIX
-The suffix of the C
-header file generated by the parser generator
-when the
-.B -d
-option is used.
-Note that setting this variable does not cause
-the parser generator to generate a header
-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
-.BR .h .
-
-.IP YACCHXXFILESUFFIX
-The suffix of the C++
-header file generated by the parser generator
-when the
-.B -d
-option is used.
-Note that setting this variable does not cause
-the parser generator to generate a header
-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
-.BR .hpp .
-
 .IP ZIP
 The zip compression and file packaging utility.
 
@@ -7772,16 +6564,6 @@ The command line used to call the zip utility,
 or the internal Python function used to create a
 zip archive.
 
-.IP ZIPCOMSTR
-The string displayed when archiving files
-using the zip utility.
-If this is not set, then $ZIPCOM
-(the command line or internal Python function) is displayed.
-
-.ES
-env = Environment(ZIPCOMSTR = "Zipping $TARGET")
-.EE
-
 .IP ZIPCOMPRESSION
 The
 .I compression
@@ -8032,7 +6814,7 @@ and selects the compiler to be used for the check;
 the default is "C".
 
 .TP 
-.RI Configure.CheckLib( self ", [" library ", " symbol ", " header ", " language ", " autoadd=1 ])
+.RI Configure.CheckLib( self ", [" library ", " symbol ", " header ", " language ", " autoadd ])
 Checks if 
 .I library 
 provides 
@@ -8322,13 +7104,7 @@ is the name of the variable.
 .I help 
 is the help text for the variable.
 .I default 
-is the default value of the variable;
-if the default value is
-.B None
-and there is no explicit value specified,
-the construction variable will
-.I not
-be added to the construction environment.
+is the default value of the variable.
 .I validator
 is called to validate the value of the variable, and should take three
 arguments: key, value, and environment
@@ -8373,18 +7149,6 @@ the Environment() function:
 env = Environment(options=opts)
 .EE
 
-.IP
-The text file(s) that were specified
-when the Options object was created
-are executed as Python scripts,
-and the values of (global) Python variables set in the file
-are added to the construction environment.
-Example:
-
-.ES
-CC = 'my_cc'
-.EE
-
 .TP
 .RI Save( filename ", " env )
 This saves the currently set options into a script file named  
@@ -8428,30 +7192,12 @@ Help(opts.GenerateHelpText(env))
 Help(opts.GenerateHelpText(env, sort=cmp))
 .EE
 
-.TP
-.RI FormatOptionHelpText( env ", " opt ", " help ", " default ", " actual )
-This method returns a formatted string
-containing the printable help text
-for one option.
-It is normally not called directly,
-but is called by the
-.IR GenerateHelpText ()
-method to create the returned help text.
-It may be overridden with your own
-function that takes the arguments specified above
-and returns a string of help text formatted to your liking.
-Note that the
-.IR GenerateHelpText ()
-will not put any blank lines or extra
-characters in between the entries,
-so you must add those characters to the returned
-string if you want the entries separated.
+The text based SConscript file is executed as a Python script, and the
+global variables are queried for customizable construction
+variables. Example:
 
 .ES
-def my_format(env, opt, help, default, actual):
-    fmt = "\n%s: default=%s actual=%s (%s)\n"
-    return fmt % (opt, default. actual, help)
-opts.FormatOptionHelpText = my_format
+CC = 'my_cc'
 .EE
 
 To make it more convenient to work with customizable Options,
@@ -8541,7 +7287,7 @@ and all input values will be
 converted to lower case.
 
 .TP
-.RI ListOption( key ", " help ", " default ", " names ", [", map ])
+.RI ListOption( key ", " help ", " default ", " names )
 Return a tuple of arguments
 to set up an option
 whose value may be one or more
@@ -8565,14 +7311,6 @@ with all values separated by commas.
 The default may be a string of
 comma-separated default values,
 or a list of the default values.
-The optional
-.I map
-argument is a dictionary
-that can be used to convert
-input values into specific legal values
-in the
-.I names
-list.
 
 .TP
 .RI PackageOption( key ", " help ", " default )
@@ -8643,7 +7381,7 @@ which verifies that the specified path is an existing directory;
 and
 .BR PathOption.PathIsDirCreate ,
 which verifies that the specified path is a directory,
-and will create the specified directory if the path does not exist.
+and will create the specified directory if the path exist.
 You may supply your own
 .I validator
 function,
@@ -8894,17 +7632,8 @@ specify a scanner to
 find things like
 .B #include
 lines in source files.
-The pre-built
-.B DirScanner
-Scanner object may be used to
-indicate that this Builder
-should scan directory trees
-for on-disk changes to files
-that
-.B scons
-does not know about from other Builder or function calls.
 (See the section "Scanner Objects," below,
-for information about creating your own Scanner objects.)
+for information about creating Scanner objects.)
 
 .IP target_factory
 A factory function that the Builder will use
@@ -8929,15 +7658,6 @@ env.Append(BUILDERS = {'MakeDirectory':MakeDirectoryBuilder})
 env.MakeDirectory('new_directory', [])
 .EE
 
-Note that the call to the MakeDirectory Builder
-needs to specify an empty source list
-to make the string represent the builder's target;
-without that, it would assume the argument is the source,
-and would try to deduce the target name from it,
-which in the absence of an automatically-added prefix or suffix
-would lead to a matching target and source name
-and a circular dependency.
-
 .IP source_factory
 A factory function that the Builder will use
 to turn any sources specified as strings into SCons Nodes.
@@ -9023,6 +7743,12 @@ b = Builder("my_build < $TARGET > $SOURCE",
             emitter = {'.suf1' : e_suf1,
                        '.suf2' : e_suf2})
 .EE
+.IP
+The 
+.I generator
+and
+.I action
+arguments must not both be used for the same Builder.
 
 .IP multi
 Specifies whether this builder is allowed to be called multiple times for
@@ -9072,13 +7798,6 @@ def g(source, target, env, for_signature):
 b = Builder(generator=g)
 .EE
 
-.IP
-The 
-.I generator
-and
-.I action
-arguments must not both be used for the same Builder.
-
 .IP src_builder
 Specifies a builder to use when a source file name suffix does not match
 any of the suffixes of the builder. Using this argument produces a
@@ -9222,27 +7941,9 @@ the object is simply returned.
 .IP String
 If the first argument is a string,
 a command-line Action is returned.
-Note that the command line string
-may be preceded by an
-.B @
-(at-sign)
-to suppress printing of the
-specified command line,
-or by a
-.B \-
-(hyphen)
-to ignore the exit status from
-the specified command.
-Examples:
 
 .ES
 Action('$CC -c -o $TARGET $SOURCES')
-
-# Doesn't print the line being executed.
-Action('@build $TARGET $SOURCES')
-
-# Ignores
-Action('-build $TARGET $SOURCES')
 .EE
 
 .\" XXX From Gary Ruben, 23 April 2002:
@@ -9432,32 +8133,6 @@ a = Action("build < ${SOURCE.file} > ${TARGET.file}",
            chdir=1)
 .EE
 
-The
-.BR Action ()
-global function
-also takes an
-.B exitstatfunc
-keyword argument
-which specifies a function
-that is passed the exit status
-(or return value)
-from the specified action
-and can return an arbitrary
-or modified value.
-This can be used, for example,
-to specify that an Action object's
-return value should be ignored
-and SCons should, therefore,
-consider that the action always suceeds:
-
-.ES
-def always_succeed(s):
-    # Always return 0, which indicates success.
-    return 0
-a = Action("build < ${SOURCE.file} > ${TARGET.file}",
-           exitstatfunc=always_succeed)
-.EE
-
 .SS Miscellaneous Action Functions
 
 .B scons
@@ -9516,7 +8191,7 @@ that
 env = Environment(TMPBUILD = '/tmp/builddir')
 env.Command('foo.out', 'foo.in',
             [Mkdir('$TMPBUILD'),
-             Copy('$TMPBUILD', '${SOURCE.dir}')
+             Copy('${SOURCE.dir}', '$TMPBUILD')
              "cd $TMPBUILD && make",
              Delete('$TMPBUILD')])
 .EE
@@ -9770,7 +8445,7 @@ ${TARGET.filebase}   => file
 ${TARGET.suffix}     => .x
 ${TARGET.abspath}    => /top/dir/sub/dir/file.x
 
-SConscript('src/SConscript', build_dir='sub/dir')
+BuildDir('sub/dir','src')
 $SOURCE              => sub/dir/file.x
 ${SOURCE.srcpath}    => src/file.x
 ${SOURCE.srcdir}     => src
@@ -10022,49 +8697,6 @@ only invoke the scanner on the file being scanned,
 and not (for example) also on the files
 specified by the #include lines
 in the file being scanned.
-.I recursive
-may be a callable function,
-in which case it will be called with a list of
-Nodes found and
-should return a list of Nodes
-that should be scanned recursively;
-this can be used to select a specific subset of
-Nodes for additional scanning.
-
-Note that
-.B scons
-has a global
-.B SourceFileScanner
-object that is used by
-the
-.BR Object (),
-.BR SharedObject (),
-and 
-.BR StaticObject ()
-builders to decide
-which scanner should be used
-for different file extensions.
-You can using the
-.BR SourceFileScanner.add_scanner ()
-method to add your own Scanner object
-to the
-.B scons
-infrastructure
-that builds target programs or
-libraries from a list of
-source files of different types:
-
-.ES
-def xyz_scan(node, env, path):
-    contents = node.get_contents()
-    # Scan the contents and return the included files.
-
-XYZScanner = Scanner(xyz_scan)
-
-SourceFileScanner.add_scanner('.xyx', XYZScanner)
-
-env.Program('my_prog', ['file1.c', 'file2.f', 'file3.xyz'])
-.EE
 
 .SH SYSTEM-SPECIFIC BEHAVIOR
 SCons and its configuration files are very portable,
@@ -10312,8 +8944,6 @@ env['BUILDERS]['PDFBuilder'] = bld
 .ES
 import re
 
-'\" Note:  the \\ in the following are for the benefit of nroff/troff,
-'\" not inappropriate doubled escape characters within the r'' raw string.
 include_re = re.compile(r'^include\\s+(\\S+)$', re.M)
 
 def kfile_scan(node, env, path, arg):
@@ -10386,32 +9016,36 @@ subdirectory/SConscript:
 
 .SS Building Multiple Variants From the Same Source
 
-Use the build_dir keyword argument to
-the SConscript function to establish
+Use the BuildDir() method to establish
 one or more separate build directories for
-a given source directory:
+a given source directory,
+then use the SConscript() method
+to specify the SConscript files
+in the build directories:
 
 .ES
 SConstruct:
 
-    cppdefines = ['FOO']
-    Export("cppdefines")
-    SConscript('src/SConscript', build_dir='foo')
+    ccflags = '-DFOO'
+    Export("ccflags")
+    BuildDir('foo', 'src')
+    SConscript('foo/SConscript')
 
-    cppdefines = ['BAR']
-    Export("cppdefines")
-    SConscript('src/SConscript', build_dir='bar')
+    ccflags = '-DBAR'
+    Export("ccflags")
+    BuildDir('bar', 'src')
+    SConscript('bar/SConscript')
 
 src/SConscript:
 
-    Import("cppdefines")
-    env = Environment(CPPDEFINES = cppdefines)
+    Import("ccflags")
+    env = Environment(CCFLAGS = ccflags)
     env.Program(target = 'src', source = 'src.c')
 .EE
 
 Note the use of the Export() method
-to set the "cppdefines" variable to a different
-value each time we call the SConscript function.
+to set the "ccflags" variable to a different
+value for each variant build.
 
 .SS Hierarchical Build of Two Libraries Linked With a Program
 
index cdf7e91756411b8bd5fd99d60f7db7ed5852a97d..5a4062fa111d5202428a5a561e206bbe12c26336 100644 (file)
 
 RELEASE 0.97 - XXX
 
-  From Anonymous:
-
-  - Fix Java parsing to avoid erroneously identifying a new array
-    of class instances as an anonymous inner class.
-
-  - Fix a typo in the man page description of PathIsDirCreate.
-
-  - Fix the intelc.py Tool module to not throw an exception if the
-    only installed version is something other than ia32.
-
   From Chad Austin:
 
   - Allow Help() to be called multiple times, appending to the help
@@ -28,37 +18,6 @@ RELEASE 0.97 - XXX
   - Allow Tools found on a toolpath to import Python modules from
     their local directory.
 
-  - Have the environment store the toolpath and re-use it to find Tools
-    modules during later Copy() or Tool() calls (unless overridden).
-
-  - Normalize the directory path names in SConsignFile() database
-    files so the same signature file can interoperate on Windows and
-    non-Windows systems.
-
-  - Make --debug=stacktrace print a stacktrace when a UserError is thrown.
-
-  - Remove an old, erroneous cut-and-paste comment in Scanner/Dir.py.
-
-  From Stanislav Baranov:
-
-  - Make it possible to support with custom Alias (sub-)classes.
-
-  - Allow Builders to take empty source lists when called.
-
-  - Allow access to both TARGET and SOURCE in $*PATH expansions.
-
-  - Allow SConscript files to modify BUILD_TARGETS.
-
-  - Add a separate MSVSSolution() Builder, with support for the
-    following new construction variables: $MSVSBUILDCOM, $MSVSCLEANCOM,
-    $MSVSENCODING, $MSVSREBUILDCOM, $MSVSSCONS, $MSVSSCONSCOM,
-    $MSVSSCONSFLAGS, $MSVSSCONSCRIPT and $MSVSSOLUTIONCOM.
-
-  From Timothee Besset:
-
-  - Add support for Objective C/C++ .m and .mm file suffixes (for
-    Mac OS X).
-
   From Steve Christensen:
 
   - Handle exceptions from Python functions as build actions.
@@ -66,22 +25,6 @@ RELEASE 0.97 - XXX
   - Add a set of canned PathOption validators:  PathExists (the default),
     PathIsFile, PathIsDir and PathIsDirCreate.
 
-  Charles Crain
-
-  - Fix the PharLap linkloc.py module to use target+source arguments
-    when calling env.subst().
-
-  From Matthew Doar:
-
-  - Add support for .lex and .yacc file suffixes for Lex and Yacc files.
-
-  From Bjorn Eriksson:
-
-  - Fix an incorrect Command() keyword argument in the man page.
-
-  - Add a $TEMPFILEPREFIX variable to control the prefix or flag used
-    to pass a long-command-line-execution tempfile to a command.
-
   From Eric Frias:
 
   - Huge performance improvement:  wrap the tuples representing an
@@ -94,25 +37,6 @@ RELEASE 0.97 - XXX
     check for the build engine in the parent directory of the Python
     library directory (/usr/lib64 instead of /usr/lib).
 
-  From Ralph W. Grosse-Kunstleve and Patrick Mezard:
-
-  - Remove unneceesary (and incorrect) SCons.Util strings on some function
-    calls in SCons.Util.
-
-  From August Hörandl:
-
-  - Add a scanner for \include and \import files, with support for
-    searching a directory list in $TEXINPUTS (imported from the external
-    environment).
-
-  - Support $MAKEINDEX, $MAKEINDEXCOM, $MAKEINDEXCOMSTR and
-    $MAKEINDEXFLAGS for generating indices from .idx files.
-
-  From Stephen Kennedy:
-
-  - Speed up writing the .sconsign file at the end of a run by only
-    calling sync() once at the end, not after every entry.
-
   From Steven Knight:
 
   - When compiling with Microsoft Visual Studio, don't include the ATL and
@@ -176,291 +100,16 @@ RELEASE 0.97 - XXX
   - Fix the ability to specify a target_factory of Dir() to a Builder,
     which the default create-a-directory Builder was interfering with.
 
-  - Mark a directory as built if it's created as part of the preparation
-    for another target, to avoid trying to build it again when it comes
-    up in the target list.
-
-  - Allow a function with the right calling signature to be put directly
-    in an Environment's BUILDERS dictionary, making for easier creation
-    and use of wrappers (pseudo-Builders) that call other Builders.
-
-  - On Python 2.x, wrap lists of Nodes returned by Builders in a UserList
-    object that adds a method that makes str() object return a string
-    with all of the Nodes expanded to their path names.  (Builders under
-    Python 1.5.2 still return lists to avoid TypeErrors when trying
-    to extend() list, so Python 1.5.2 doesn't get pretty-printing of Node
-    lists, but everything should still function.)
-
-  - Allow Aliases to have actions that will be executed whenever
-    any of the expanded Alias targets are out of date.
-
-  - Fix expansion of env.Command() overrides within target and
-    source file names.
-
-  - Support easier customization of what's displayed by various default
-    actions by adding lots of new construction variables: $ARCOMSTR,
-    $ASCOMSTR, $ASPPCOMSTR, $BIBTEXCOMSTR, $BITKEEPERCOMSTR, $CCCOMSTR,
-    $CVSCOMSTR, $CXXCOMSTR, $DCOMSTR, $DVIPDFCOMSTR, $F77COMSTR,
-    $F90COMSTR, $F95COMSTR, $FORTRANCOMSTR, $GSCOMSTR, $JARCOMSTR,
-    $JAVACCOMSTR, $JAVAHCOMSTR, $LATEXCOMSTR, $LEXCOMSTR, $LINKCOMSTR,
-    $M4COMSTR, $MIDLCOMSTR, $P4COMSTR, $PCHCOMSTR, $PDFLATEXCOMSTR,
-    $PDFTEXCOMSTR, $PSCOMSTR, $QT_MOCFROMCXXCOMSTR, $QT_MOCFROMHCOMSTR,
-    $QT_UICCOMSTR, $RCCOMSTR, $REGSVRCOMSTR, $RCS_COCOMSTR, $RMICCOMSTR,
-    $SCCSCOMSTR, $SHCCCOMSTR, $SHCXXCOMSTR, $SHF77COMSTR, $SHF90COMSTR,
-    $SHF95COMSTR, $SHFORTRANCOMSTR, $SHLINKCOMSTR, $SWIGCOMSTR,
-    $TARCOMSTR, $TEXCOMSTR, $YACCCOMSTR and $ZIPCOMSTR.
-
-  - Add an optional "map" keyword argument to ListOption() that takes a
-    dictionary to map user-specified values to legal values from the list
-    (like EnumOption() already doee).
-
-  - Add specific exceptions to try:-except: blocks without any listed,
-    so that they won't catch and mask keyboard interrupts.
-
-  - Make --debug={tree,dtree,stree} print something even when there's
-    a build failure.
-
-  - Fix how Scanners sort the found dependencies so that it doesn't
-    matter whether the dependency file is in a Repository or not.
-    This may cause recompilations upon upgrade to this version.
-
-  - Make AlwaysBuild() work with Alias and Python value Nodes (making
-    it much simpler to support aliases like "clean" that just invoke
-    an arbitrary action).
-
-  - Have env.ParseConfig() use AppendUnique() by default to suppress
-    duplicate entries from multiple calls.  Add a "unique" keyword
-    argument to allow the old behavior to be specified.
-
-  - Allow the library modules imported by an SConscript file to get at
-    all of the normally-available global functions and variables by saying
-    "from SCons.Script import *".
-
-  - Add a --debug=memoizer option to print Memoizer hit/mass statistics.
-
-  - Allow more than one --debug= option to be set at a time.
-
-  - Change --debug=count to report object counts before and after
-    reading SConscript files and before and after building targets.
-
-  - Change --debug=memory output to line up the numbers and to better
-    match (more or less) the headers on the --debug=count columns.
-
-  - Speed things up when there are lists of targets and/or sources by
-    getting rid of some N^2 walks of the lists involved.
-
-  - Cache evaluation of LazyActions so we don't create a new object
-    for each invocation.
-
-  - When scanning, don't create Nodes for include files that don't
-    actually exist on disk.
-
-  - Make supported global variables CScanner, DScanner, ProgramScanner and
-    SourceFileScanner.  Make SourceFileScanner.add_scanner() a supported
-    part of the public interface.  Keep the old SCons.Defaults.*Scan names
-    around for a while longer since some people were already using them.
-
-  - By default, don't scan directories for on-disk files.  Add a
-    DirScanner global scanner that can be used in Builders or Command()
-    calls that want source directory trees scanned for on-disk changes.
-    Have the Tar() and Zip() Builders use the new DirScanner to preserve
-    the behavior of rebuilding a .tar or .zip file if any file or
-    directory under a source tree changes.  Add Command() support for
-    a source_scanner keyword argument to Command() that can be set to
-    DirScanner to get this behavior.
-
-  - Documentation changes:  Explain that $CXXFLAGS contains $CCFLAGS
-    by default.  Fix a bad target_factory example in the man page.
-    Add appendices to the User's Guide to cover the available Tools,
-    Builders and construction variables.  Comment out the build of
-    the old Python 10 paper, which doesn't build on all systems and
-    is old enough at this point that it probably isn't worth the
-    effort to make it do so.
-
-  - Enhanced the SCons setup.py script to install man pages on
-    UNIX/Linux systems.
-
-  - Add support for an Options.FormatOptionHelpText() method that can
-    be overridden to customize the format of Options help text.
-
-  - Add a global name for the Entry class (which had already been
-    documented).
-
-  - Fix re-scanning of generated source files for implicit dependencies
-    when the -j option is used.
-
-  - Fix a dependency problem that caused $LIBS scans to not be added
-    to all of the targets in a multiple-target builder call, which
-    could cause out-of-order builds when the -j option is used.
-
-  - Store the paths of source files and dependencies in the .sconsign*
-    file(s) relative to the target's directory, not relative to the
-    top-level SConstruct directory.  This starts to make it possible to
-    subdivide the dependency tree arbitrarily by putting an SConstruct
-    file in every directory and using content signatures.
-
-  - Add support for $YACCHFILESUFFIX and $YACCHXXFILESUFFIX variables
-    that accomodate parser generators that write header files to a
-    different suffix than the hard-coded .hpp when the -d option is used.
-
-  - The default behavior is now to store signature information in a
-    single .sconsign.dblite file in the top-level SConstruct directory.
-    The old behavior of a separate .sconsign file in each directory can
-    be specified by calling SConsignFile(None).
-
-  - Remove line number byte codes within the signature calculation
-    of Python function actions, so that changing the location of an
-    otherwise unmodified Python function doesn't cause rebuilds.
-
-  - Fix AddPreAction() and AddPostAction() when an action has more than
-    one target file:  attach the actions to the Executor, not the Node.
-
-  - Allow the source directory of a BuildDir / build_dir to be outside
-    of the top-level SConstruct directory tree.
-
-  - Add a --debug=nomemoizer option that disables the Memoizer for clearer
-    looks at the counts and profiles of the underlying function calls,
-    not the Memoizer wrappers.
-
-  - Print various --debug= stats even if we exit early (e.g. using -h).
-
-  - Really only use the cached content signature value if the file
-    is older than --max-drift, not just if --max-drift is set.
-
-  - Remove support for conversion from old (pre 0.96) .sconsign formats.
-
-  - Add support for a --diskcheck option to enable or disable various
-    on-disk checks:  that File and Dir nodes match on-disk entries;
-    whether an RCS file exists for a missing source file; whether an
-    SCCS file exists for a missing source file.
-
-  - Add a --raw argument to the sconsign script, so it can print a
-    raw representation of each entry's NodeInfo dictionary.
-
-  - Add the 'f90' and 'f95' tools to the list of Fortran compilers
-    searched for by default.
-
-  - Add the +Z option by default when compiling shared objects on
-    HP-UX.
-
-  - Check for whether files exist on disk by listing the directory
-    contents, not calling os.path.exists() file by file.  This is
-    somewhat more efficient in general, and may be significantly
-    more efficient on Windows.
-
-  - Minor speedups in the internal is_Dict(), is_List() and is_String()
-    functions.
-
-  - Fix a signature refactoring bug that caused Qt header files to
-    get re-generated every time.
-
-  - Don't fail when writing signatures if the .sconsign.dblite file is
-    owned by a different user (e.g. root) from a previous run.
-
-  - When deleting variables from stacked OverrideEnvironments, don't
-    throw a KeyError if we were able to delte the variable from any
-    Environment in the stack.
-
-  - Get rid of the last indentation tabs in the SCons source files and
-    add -tt to the Python invocations in the packaging build and the
-    tests so they don't creep back in.
-
-  - In Visual Studio project files, put quotes around the -C directory
-    so everything works even if the path has spaces in it.
-
-  - The Intel Fortran compiler uses -object:$TARGET, not "-o $TARGET",
-    when building object files on Windows.  Have the the ifort Tool
-    modify the default command lines appropriately.
-
-  - Document the --debug=explain option in the man page.  (How did we
-    miss this?)
-
-  - Add a $LATEXRETRIES variable to allow configuration of the number of
-    times LaTex can be re-called to try to resolve undefined references.
-
-  From Chen Lee:
-
-  - Handle Visual Studio project and solution files in Unicode.
-
-  From Wayne Lee:
-
-  - Avoid "maximum recursion limit" errors when removing $(-$) pairs
-    from long command lines.
-
   From Clive Levinson:
 
   - Make ParseConfig() recognize and add -mno-cygwin to $LINKFLAGS and
     $CCFLAGS, and -mwindows to $LINKFLAGS.
 
-  From Sanjoy Mahajan:
-
-  - Correct TeX-related command lines to just $SOURCE, not $SOURCES
-
-  - Fix a bad use of Copy() in an example in the man page, and a
-    bad regular expression example in the man page and User's Guide.
-
-  From Shannon Mann:
-
-  - Have the Visual Studio project file(s) echo "Starting SCons" before
-    executing SCons, mainly to work around a quote-stripping bug in
-    (some versions of?) the Windows cmd command executor.
-
-  From Michael McCracken:
-
-  - Add a new "applelink" tool to handle the things like Frameworks and
-    bundles that Apple has added to gcc for linking.
-
-  - Use more appropriate default search lists of linkers, compilers and
-    and other tools for the 'darwin' platform.
-
-  - Add a LoadableModule Builder that builds a bundle on Mac OS X (Darwin)
-    and a shared library on other systems.
-
-  - Improve SWIG tests for use on Mac OS X (Darwin).
-
-  From Patrick Mezard:
-
-  - Execute build commands for a command-line target if any of the
-    files built along with the target is out of date or non-existent,
-    not just if the command-line target itself is out of date.
-
-  - Fix the -n option when used with -c to print all of the targets
-    that will be removed for a multi-target Builder call.
-
-  - If there's no file in the source directory, make sure there isn't
-    one in the build directory, too, to avoid dangling files left
-    over from previous runs when a source file is removed.
-
-  - Allow AppendUnique() and PrependUnique() to append strings (and
-    other atomic objects) to lists.
-
-  From Georg Mischler:
-
-  - Remove the space after the -o option when invoking the Borland
-    BCC compiler; some versions apparently require that the file name
-    argument be concatenated with the option.
-
-  From Joel B. Mohler:
-
-  - Extend latex.py, pdflatex.py, pdftex.py and tex.py so that building
-    from both TeX and LaTeX files uses the same logic to call $BIBTEX
-    when it's necessary, to call $MAKEINDEX when it's necessary, and to
-    call $TEX or $LATEX multiple times to handle undefined references.
-
   From Elliot Murphy:
 
   - Enhance the tests to guarantee persistence of ListOption
     values in saved options files.
 
-  - Supply the help text when -h is used with the -u, -U or -D options.
-
-  From Leanid Nazdrynau:
-
-  - Fix the Java parser's handling of backslashes in strings.
-
-  - Fix the Qt UIC scanner to work with generated .ui files (by using
-    the FindFile() function instead of checking by-hand for the file).
-
   From Christian Neeb:
 
   - Fix the Java parser's handling of string definitions to avoid ignoring
@@ -473,20 +122,6 @@ RELEASE 0.97 - XXX
     value directory; avoiding slowing substitution logic when there's no
     '$' in the string.
 
-  From Jan Nieuwenhuizen:
-
-  - Fix a problem with interpreting quoted argument lists on command lines.
-
-  From Greg Noel:
-
-  - Add construction variables to support frameworks on Mac OS X:
-    $FRAMEWORKS, $FRAMEWORKPREFIX, $FRAMEWORKPATH, $FRAMEWORKPATHPREFIX.
-
-  - Re-order link lines so the -o option always comes right after the
-    command name.
-
-  - Add /sw/bin to the default execution PATH on Mac OS X.
-
   From Gary Oberbrunner:
 
   - Add an Environment.Dump() method to print the contents of a
@@ -511,65 +146,11 @@ RELEASE 0.97 - XXX
   - Allow Tool specifications to be passed a dictionary of keyword
     arguments.
 
-  - Support an Options default value of None, in which case the variable
-    will not be added to the construction environment unless it's set
-    explicitly by the user or from an Options file.
-
-  - Avoid copying __builtin__ values into a construction environment's
-    dictionary when evaluating construction variables.
-
-  - Add a new cross-platform intelc.py Tool that can detect and
-    configure the Intel C++ v8 compiler on both Windows, where it's
-    named icl, and Linux, where it's named icc.  It also checks that
-    the directory specified in the Windows registry exists, and sets a
-    new $INTEL_C_COMPILER_VERSION construction variable to identify the
-    version being used.  (Niall Douglas contributed an early prototype
-    of parts of this module.)
-
-  - Fix the private Conftest._Have() function so it doesn't change
-    non-alphanumeric characters to underscores.
-
-  - Supply a better error message when a construction variable expansion
-    has an unknown attribute.
-
-  - Documentation changes:  Update the man page to describe use of
-    filenames or Nodes in $LIBS.
-
-  - Add support for Intel C++ beta 9.0 (both 32 and 64 bit versions).
-
-  - Document the new $FRAMEWORK* variables for Mac OS X.
-
-  From Kian Win Ong:
-
-  - When building a .jar file and there is a $JARCHDIR, put the -C
-    in front of each .class file on the command line.
-
-  - Recognize the Java 1.5 enum keyword.
-
   From Chris Pawling:
 
   - Have the linkloc tool use $MSVS_VERSION to select the Microsoft
     Visual Studio version to use.
 
-  From Karol Pietrzak:
-
-  - Add $RPATH (-R) support to the Sun linker Tool (sunlink).
-
-  - Add a description of env.subst() to the man page.
-
-  From Chris Prince:
-
-  - Look in the right directory, not always the local directory, for a
-    same-named file or directory conflict on disk.
-
-  - On Windows, preserve the external environment's %SYSTEMDRIVE%
-    variable, too.
-
-  From Asfand Yar Qazi:
-
-  - Add /opt/bin to the default execution PATH on all POSIX platforms
-    (between /usr/local/bin and /bin).
-
   From Kevin Quick:
 
   - Fix the Builder name returned from ListBuilders and other instances
@@ -637,122 +218,6 @@ RELEASE 0.97 - XXX
     Command() and Scanner test coverage.  Improved test infrastructure
     for -c output.
 
-  - Refactor the interface between Action and Executor objects to treat
-    Actions atomically.
-
-  - The --debug=presub option will now report the pre-substitution
-    each action seprately, instead of reporting the entire list before
-    executing the actions one by one.
-
-  - The --debug=explain option explaining a changed action will now
-    (more correctly) show pre-substitution action strings, instead of
-    the commands with substituted file names.
-
-  - A Node (file) will now be rebuilt if its PreAction or PostAction
-    actions change.
-
-  - Python Function actions now have their calling signature (target,
-    source, env) reported correctly when displayed.
-
-  - Fix BuildDir()/build_dir handling when the build_dir is underneath
-    the source directory and trying to use entries from the build_dir
-    as sources for other targets in the build-dir.
-
-  - Fix hard-coding of JDK path names in various Java tests.
-
-  - Handle Python stack traces consistently (stop at the SConscript stack
-    frame, by default) even if the Python source code isn't available.
-
-  - Improve the performance of the --debug={tree,dtree} options.
-
-  - Add --debug=objects logging of creation of OverrideWarner,
-    EnvironmentCopy and EnvironmentOverride objects.
-
-  - Fix command-line expansion of Python Value Nodes.
-
-  - Internal cleanups:  Remove an unnecessary scan argument.  Associate
-    Scanners only with Builders, not nodes.  Apply overrides once when
-    a Builder is called, not in multiple places.  Cache results from the
-    Node.FS.get_suffix() and Node.get_build_env() methods.  Use the Python
-    md5 modules' hexdigest() method, if there is one.  Have Taskmaster
-    call get_stat() once for each Node and re-use the value instead of
-    calling it each time it needs the value.  Have Node.depends_on()
-    re-use the list from the children() method instead of calling it
-    multiple times.
-
-  - Use the correct scanner if the same source file is used for targets in
-    two different environments with the same path but different scanners.
-
-  - Collect logic for caching values in memory in a Memoizer class,
-    which cleans up a lot of special-case code in various methods and
-    caches additional values to speed up most configurations.
-
-  - Add a PathAccept validator to the list of new canned PathOption
-    validators.
-
-  From Christoph Schulz:
-
-  - Add support for $CONFIGUREDIR and $CONFIGURELOG variables to control
-    the directory and logs for configuration tests.
-
-  - Add support for a $INSTALLSTR variable.
-
-  From Craig Scott:
-
-  - Have the Fortran module emitter look for Fortan modules to be created
-    relative to $FORTRANMODDIR, not the top-level directory.
-
-  - When saving Options to a file, run default values through the
-    converter before comparing them with the set values.  This correctly
-    suppresses Boolean Option values from getting written to the saved
-    file when they're one of the many synonyms for a default True or
-    False value.
-
-  - Fix the Fortran Scanner's ability to handle a module being used
-    in the same file in which it is defined.
-
-  From Jeff Squyres:
-
-  - Documentation changes:  Use $CPPDEFINES instead of $CCFLAGS in man
-    page examples.
-
-  From Levi Stephen:
-
-  - Allow $JARCHDIR to be expanded to other construction variables.
-
-  From Steve-o:
-
-  - Add the -KPIC option by default when compiling shared objects on
-    Solaris.
-
-  - Change the default suffix for Solaris objects to .o, to conform to
-    Sun WorkShop's expectations.  Change the profix to so_ so they can
-    still be differentiated from static objects in the same directory.
-
-  From Amir Szekely:
-
-  - When calling the resource compiler on MinGW, add --include-dir and
-    the source directory so it finds the source file.
-
-  - Update EnsureSConsVersion() to support revision numbers.
-
-  - Add use of $CPPDEFINES to $RCCOM (resource file compilation) on MinGW.
-
-  From Dobes Vandermeer:
-
-  - Add support for SCC and other settings in Microsoft Visual
-    Studio project and solution files:  $MSVS_PROJECT_BASE_PATH,
-    $MSVS_PROJECT_GUID, $MSVS_SCC_AUX_PATH, $MSVS_SCC_LOCAL_PATH,
-    $MSVS_SCC_PROJECT_NAME, $MSVS_SCC_PROVIDER,
-
-  - Add support for using a $SCONS_HOME variable (imported from the
-    external environment, or settable internally) to put a shortened
-    SCons execution line in the Visual Studio project file.
-
-  From Greg Ward:
-
-  - Fix a misplaced line in the man page.
-
   From Christoph Wiedemann:
 
   - Add an Environment.SetDefault() method that only sets values if
@@ -799,17 +264,6 @@ RELEASE 0.97 - XXX
   - Have the Qt Builder make uic-generated files dependent on the .ui.h
     file, if one exists.
 
-  - Add a test to make sure that SCons source code does not contain
-    try:-except: blocks that catch all errors, which potentially catch
-    and mask keyboard interrupts.
-
-  - Fix us of TargetSignatures('content') with the SConf subsystem.
-
-  From Russell Yanofsky:
-
-  - Add support for the Metrowerks Codewarrior compiler and linker
-    (mwcc and mwld).
-
 
 
 RELEASE 0.96.1 - XXX
index 90839855448690976d9c0959ff6ae34c71fb4099..6afff589788ce06025681995e2738281779dee3b 100644 (file)
@@ -126,7 +126,6 @@ import SCons.Action
 from SCons.Debug import logInstanceCreation
 from SCons.Errors import InternalError, UserError
 import SCons.Executor
-import SCons.Node
 import SCons.Node.FS
 import SCons.Util
 import SCons.Warnings
@@ -152,9 +151,6 @@ class DictCmdGenerator(SCons.Util.Selector):
         self[suffix] = action
 
     def __call__(self, target, source, env, for_signature):
-        if not source:
-            return []
-
         ext = None
         for src in map(str, source):
             my_ext = SCons.Util.splitext(src)[1]
@@ -225,7 +221,6 @@ class OverrideWarner(UserDict.UserDict):
     """
     def __init__(self, dict):
         UserDict.UserDict.__init__(self, dict)
-        if __debug__: logInstanceCreation(self, 'Builder.OverrideWarner')
         self.already_warned = None
     def warn(self):
         if self.already_warned:
@@ -246,15 +241,12 @@ def Builder(**kw):
     if kw.has_key('generator'):
         if kw.has_key('action'):
             raise UserError, "You must not specify both an action and a generator."
-        kw['action'] = SCons.Action.CommandGeneratorAction(kw['generator'])
+        kw['action'] = SCons.Action.CommandGenerator(kw['generator'])
         del kw['generator']
-    elif kw.has_key('action'):
-        if SCons.Util.is_Dict(kw['action']):
-            composite = DictCmdGenerator(kw['action'])
-            kw['action'] = SCons.Action.CommandGeneratorAction(composite)
-            kw['src_suffix'] = composite.src_suffixes()
-        else:
-            kw['action'] = SCons.Action.Action(kw['action'])
+    elif kw.has_key('action') and SCons.Util.is_Dict(kw['action']):
+        composite = DictCmdGenerator(kw['action'])
+        kw['action'] = SCons.Action.CommandGenerator(composite)
+        kw['src_suffix'] = composite.src_suffixes()
 
     if kw.has_key('emitter'):
         emitter = kw['emitter']
@@ -282,10 +274,9 @@ def Builder(**kw):
 
     return ret
 
-def _node_errors(builder, env, tlist, slist):
-    """Validate that the lists of target and source nodes are
-    legal for this builder and environment.  Raise errors or
-    issue warnings as appropriate.
+def _init_nodes(builder, env, overrides, executor_kw, tlist, slist):
+    """Initialize lists of target and source nodes with all of
+    the proper Builder information.
     """
 
     # First, figure out if there are any errors in the way the targets
@@ -295,17 +286,22 @@ def _node_errors(builder, env, tlist, slist):
             raise UserError, "Multiple ways to build the same target were specified for: %s" % str(t)
         if t.has_explicit_builder():
             if not t.env is None and not t.env is env:
-                action = t.builder.action
-                t_contents = action.get_contents(tlist, slist, t.env)
-                contents = action.get_contents(tlist, slist, env)
+                t_contents = t.builder.action.get_contents(tlist, slist, t.env)
+                contents = t.builder.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)))
+                                        "Two different environments were specified for target %s,\n\tbut they appear to have the same action: %s"%(str(t), t.builder.action.strfunction(tlist, slist, t.env)))
 
                 else:
                     raise UserError, "Two environments with different actions were specified for the same target: %s"%str(t)
 
+            elif t.overrides != overrides:
+                raise UserError, "Two different sets of overrides were specified for the same target: %s"%str(t)
+
+            elif builder.target_scanner and t.target_scanner and builder.target_scanner != t.target_scanner:
+                raise UserError, "Two different scanners were specified for the same target: %s"%str(t)
+
             if builder.multi:
                 if t.builder != builder:
                     if isinstance(t.builder, ListBuilder) and isinstance(builder, ListBuilder) and t.builder.builder == builder.builder:
@@ -321,6 +317,52 @@ def _node_errors(builder, env, tlist, slist):
         if len(slist) > 1:
             raise UserError, "More than one source given for single-source builder: targets=%s sources=%s" % (map(str,tlist), map(str,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.
+    executor = None
+    if builder.multi:
+        try:
+            executor = tlist[0].get_executor(create = 0)
+        except AttributeError:
+            pass
+        else:
+            executor.add_sources(slist)
+    if executor is None:
+        if not builder.action:
+            raise UserError, "Builder %s must have an action to build %s."%(builder.get_name(env or builder.env), map(str,tlist))
+        executor = SCons.Executor.Executor(builder.action,
+                                           env or builder.env,
+                                           [builder.overrides, overrides],
+                                           tlist,
+                                           slist,
+                                           executor_kw)
+
+    # Now set up the relevant information in the target Nodes themselves.
+    for t in tlist:
+        t.overrides = overrides
+        t.cwd = SCons.Node.FS.default_fs.getcwd()
+        t.builder_set(builder)
+        t.env_set(env)
+        t.add_source(slist)
+        t.set_executor(executor)
+        if builder.target_scanner:
+            t.target_scanner = builder.target_scanner
+        if t.source_scanner is None:
+            t.source_scanner = builder.source_scanner
+
+    # Add backup source scanners from the environment to the source
+    # nodes.  This may not be necessary if the node will have a real
+    # source scanner added later (which is why these are the "backup"
+    # source scanners, not the real ones), but because source nodes may
+    # be used multiple times for different targets, it ends up being
+    # more efficient to do this calculation once here, as opposed to
+    # delaying it until later when we potentially have to calculate it
+    # over and over and over.
+    for s in slist:
+        if s.source_scanner is None and s.backup_source_scanner is None:
+            s.backup_source_scanner = env.get_scanner(s.scanner_key())
+
 class EmitterProxy:
     """This is a callable class that can act as a
     Builder emitter.  It holds on to a string that
@@ -356,15 +398,12 @@ class BuilderBase:
     nodes (files) from input nodes (files).
     """
 
-    if SCons.Memoize.use_memoizer:
-        __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
     def __init__(self,  action = None,
                         prefix = '',
                         suffix = '',
                         src_suffix = '',
-                        target_factory = None,
-                        source_factory = None,
+                        target_factory = SCons.Node.FS.default_fs.File,
+                        source_factory = SCons.Node.FS.default_fs.File,
                         target_scanner = None,
                         source_scanner = None,
                         emitter = None,
@@ -375,14 +414,15 @@ class BuilderBase:
                         chdir = _null,
                         is_explicit = 1,
                         **overrides):
-        if __debug__: logInstanceCreation(self, 'Builder.BuilderBase')
-        self.action = action
+        if __debug__: logInstanceCreation(self, 'BuilderBase')
+        self.action = SCons.Action.Action(action)
         self.multi = multi
         if SCons.Util.is_Dict(prefix):
             prefix = CallableSelector(prefix)
         self.prefix = prefix
         if SCons.Util.is_Dict(suffix):
             suffix = CallableSelector(suffix)
+        self.suffix = suffix
         self.env = env
         self.single_source = single_source
         if overrides.has_key('overrides'):
@@ -398,7 +438,6 @@ class BuilderBase:
             del overrides['scanner']
         self.overrides = overrides
 
-        self.set_suffix(suffix)
         self.set_src_suffix(src_suffix)
 
         self.target_factory = target_factory
@@ -431,7 +470,7 @@ class BuilderBase:
         try:
             index = env['BUILDERS'].values().index(self)
             return env['BUILDERS'].keys()[index]
-        except (AttributeError, KeyError, TypeError, ValueError):
+        except (AttributeError, KeyError, ValueError):
             try:
                 return self.name
             except AttributeError:
@@ -451,25 +490,7 @@ class BuilderBase:
                 return [path[:-len(suf)], path[-len(suf):]]
         return SCons.Util.splitext(path)
 
-    def get_single_executor(self, env, tlist, slist, executor_kw):
-        if not self.action:
-            raise UserError, "Builder %s must have an action to build %s."%(self.get_name(env or self.env), map(str,tlist))
-        return self.action.get_executor(env or self.env,
-                                        [],  # env already has overrides
-                                        tlist,
-                                        slist,
-                                        executor_kw)
-
-    def get_multi_executor(self, env, tlist, slist, executor_kw):
-        try:
-            executor = tlist[0].get_executor(create = 0)
-        except (AttributeError, IndexError):
-            return self.get_single_executor(env, tlist, slist, executor_kw)
-        else:
-            executor.add_sources(slist)
-            return executor
-
-    def _create_nodes(self, env, target = None, source = None):
+    def _create_nodes(self, env, overwarn, target = None, source = None):
         """Create and return lists of target and source nodes.
         """
         def _adjustixes(files, pre, suf):
@@ -485,13 +506,14 @@ class BuilderBase:
                 result.append(f)
             return result
 
-        src_suf = self.get_src_suffix(env)
+        overwarn.warn()
 
-        target_factory = env.get_factory(self.target_factory)
-        source_factory = env.get_factory(self.source_factory)
+        env = env.Override(overwarn.data)
+
+        src_suf = self.get_src_suffix(env)
 
         source = _adjustixes(source, None, src_suf)
-        slist = env.arg2nodes(source, source_factory)
+        slist = env.arg2nodes(source, self.source_factory)
 
         pre = self.get_prefix(env, slist)
         suf = self.get_suffix(env, slist)
@@ -501,14 +523,11 @@ class BuilderBase:
                 t_from_s = slist[0].target_from_source
             except AttributeError:
                 raise UserError("Do not know how to create a target from source `%s'" % slist[0])
-            except IndexError:
-                tlist = []
-            else:
-                splitext = lambda S,self=self,env=env: self.splitext(S,env)
-                tlist = [ t_from_s(pre, suf, splitext) ]
+            splitext = lambda S,self=self,env=env: self.splitext(S,env)
+            tlist = [ t_from_s(pre, suf, splitext) ]
         else:
             target = _adjustixes(target, pre, suf)
-            tlist = env.arg2nodes(target, target_factory)
+            tlist = env.arg2nodes(target, self.target_factory)
 
         if self.emitter:
             # The emitter is going to do str(node), but because we're
@@ -519,88 +538,62 @@ class BuilderBase:
             new_targets = []
             for t in tlist:
                 if not t.is_derived():
-                    t.builder_set(self)
+                    t.builder = self
                     new_targets.append(t)
 
             target, source = self.emitter(target=tlist, source=slist, env=env)
 
             # Now delete the temporary builders that we attached to any
-            # new targets, so that _node_errors() doesn't do weird stuff
+            # new targets, so that _init_nodes() doesn't do weird stuff
             # to them because it thinks they already have builders.
             for t in new_targets:
                 if t.builder is self:
                     # Only delete the temporary builder if the emitter
                     # didn't change it on us.
-                    t.builder_set(None)
+                    t.builder = None
 
             # Have to call arg2nodes yet again, since it is legal for
             # emitters to spit out strings as well as Node instances.
-            slist = env.arg2nodes(source, source_factory)
-            tlist = env.arg2nodes(target, target_factory)
+            slist = env.arg2nodes(source, self.source_factory)
+            tlist = env.arg2nodes(target, self.target_factory)
 
         return tlist, slist
 
-    def _execute(self, env, target, source, overwarn={}, executor_kw={}):
-        # We now assume that target and source are lists or None.
-        if self.single_source and len(source) > 1 and target is None:
+    def _execute(self, env, target=None, source=_null, overwarn={}, executor_kw={}):
+        if source is _null:
+            source = target
+            target = None
+
+        if(self.single_source and
+           SCons.Util.is_List(source) and
+           len(source) > 1 and
+           target is None):
             result = []
             if target is None: target = [None]*len(source)
-            for tgt, src in zip(target, source):
-                if not tgt is None: tgt = [tgt]
-                if not src is None: src = [src]
-                result.extend(self._execute(env, tgt, src, overwarn))
+            for k in range(len(source)):
+                t = self._execute(env, target[k], source[k], overwarn)
+                if SCons.Util.is_List(t):
+                    result.extend(t)
+                else:
+                    result.append(t)
             return result
-
-        overwarn.warn()
         
-        tlist, slist = self._create_nodes(env, target, source)
+        tlist, slist = self._create_nodes(env, overwarn, target, source)
 
         if len(tlist) == 1:
             builder = self
         else:
             builder = ListBuilder(self, env, tlist)
+        _init_nodes(builder, env, overwarn.data, executor_kw, tlist, slist)
 
-        # Check for errors with the specified target/source lists.
-        _node_errors(builder, env, tlist, slist)
+        return tlist
 
-        # 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
-        else:
-            get_executor = builder.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.env_set(env)
-            t.add_source(slist)
-            t.set_executor(executor)
-            t.set_explicit(builder.is_explicit)
-
-        return SCons.Node.NodeList(tlist)
-
-    def __call__(self, env, target=None, source=None, chdir=_null, **kw):
-        # We now assume that target and source are lists or None.
-        # The caller (typically Environment.BuilderWrapper) is
-        # responsible for converting any scalar values to lists.
+    def __call__(self, env, target=None, source=_null, chdir=_null, **kw):
         if chdir is _null:
             ekw = self.executor_kw
         else:
             ekw = self.executor_kw.copy()
             ekw['chdir'] = chdir
-        if kw:
-            if self.overrides:
-                env_kw = self.overrides.copy()
-                env_kw.update(kw)
-            else:
-                env_kw = kw
-        else:
-            env_kw = self.overrides
-        env = env.Override(env_kw)
         return self._execute(env, target, source, OverrideWarner(kw), ekw)
 
     def adjust_suffix(self, suff):
@@ -614,29 +607,24 @@ class BuilderBase:
             prefix = prefix(env, sources)
         return env.subst(prefix)
 
-    def set_suffix(self, suffix):
-        if not callable(suffix):
-            suffix = self.adjust_suffix(suffix)
-        self.suffix = suffix
-
     def get_suffix(self, env, sources=[]):
         suffix = self.suffix
         if callable(suffix):
             suffix = suffix(env, sources)
+        else:
+            suffix = self.adjust_suffix(suffix)
         return env.subst(suffix)
 
     def src_suffixes(self, env):
-        "__cacheable__"
-        return map(lambda x, s=self, e=env: e.subst(x), self.src_suffix)
+        return map(lambda x, s=self, e=env: e.subst(s.adjust_suffix(x)),
+                   self.src_suffix)
 
     def set_src_suffix(self, src_suffix):
         if not src_suffix:
             src_suffix = []
         elif not SCons.Util.is_List(src_suffix):
             src_suffix = [ src_suffix ]
-        adjust = lambda suf, s=self: \
-                        callable(suf) and suf or s.adjust_suffix(suf)
-        self.src_suffix = map(adjust, src_suffix)
+        self.src_suffix = src_suffix
 
     def get_src_suffix(self, env):
         """Get the first src_suffix in the list of src_suffixes."""
@@ -662,21 +650,13 @@ class BuilderBase:
         """
         self.emitter[suffix] = emitter
 
-if SCons.Memoize.use_old_memoization():
-    _Base = BuilderBase
-    class BuilderBase(SCons.Memoize.Memoizer, _Base):
-        "Cache-backed version of BuilderBase"
-        def __init__(self, *args, **kw):
-            apply(_Base.__init__, (self,)+args, kw)
-            SCons.Memoize.Memoizer.__init__(self)
-
 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')
+        if __debug__: logInstanceCreation(self)
         SCons.Util.Proxy.__init__(self, builder)
         self.builder = builder
         self.target_scanner = builder.target_scanner
@@ -712,13 +692,13 @@ class MultiStepBuilder(BuilderBase):
                         prefix = '',
                         suffix = '',
                         src_suffix = '',
-                        target_factory = None,
-                        source_factory = None,
+                        target_factory = SCons.Node.FS.default_fs.File,
+                        source_factory = SCons.Node.FS.default_fs.File,
                         target_scanner = None,
                         source_scanner = None,
                         emitter=None,
                         single_source=0):
-        if __debug__: logInstanceCreation(self, 'Builder.MultiStepBuilder')
+        if __debug__: logInstanceCreation(self)
         BuilderBase.__init__(self, action, prefix, suffix, src_suffix,
                              target_factory, source_factory,
                              target_scanner, source_scanner, emitter,
@@ -726,60 +706,50 @@ class MultiStepBuilder(BuilderBase):
         if not SCons.Util.is_List(src_builder):
             src_builder = [ src_builder ]
         self.src_builder = src_builder
+        self.sdict = {}
+        self.cached_src_suffixes = {} # source suffixes keyed on id(env)
 
-    def _get_sdict(self, env):
-        "__cacheable__"
-        sdict = {}
-        for bld in self.src_builder:
-            if SCons.Util.is_String(bld):
-                try:
-                    bld = env['BUILDERS'][bld]
-                except KeyError:
-                    continue
-            for suf in bld.src_suffixes(env):
-                sdict[suf] = bld
-        return sdict
-        
-    def _execute(self, env, target, source, overwarn={}, executor_kw={}):
-        # We now assume that target and source are lists or None.
-        source_factory = env.get_factory(self.source_factory)
-        slist = env.arg2nodes(source, source_factory)
+    def _execute(self, env, target = None, source = _null, overwarn={}, executor_kw={}):
+        if source is _null:
+            source = target
+            target = None
+
+        slist = env.arg2nodes(source, self.source_factory)
         final_sources = []
 
-        sdict = self._get_sdict(env)
+        try:
+            sdict = self.sdict[id(env)]
+        except KeyError:
+            sdict = {}
+            self.sdict[id(env)] = sdict
+            for bld in self.src_builder:
+                if SCons.Util.is_String(bld):
+                    try:
+                        bld = env['BUILDERS'][bld]
+                    except KeyError:
+                        continue
+                for suf in bld.src_suffixes(env):
+                    sdict[suf] = bld
 
         src_suffixes = self.src_suffixes(env)
 
-        lengths_dict = {}
-        for l in map(len, src_suffixes):
-            lengths_dict[l] = None
-        lengths = lengths_dict.keys()
-
-        def match_src_suffix(node, src_suffixes=src_suffixes, lengths=lengths):
-            node_suffixes = map(lambda l, n=node: n.name[-l:], lengths)
-            for suf in src_suffixes:
-                if suf in node_suffixes:
-                    return suf
-            return None
-
         for snode in slist:
-            match_suffix = match_src_suffix(snode)
-            if match_suffix:
-                try:
-                    bld = sdict[match_suffix]
-                except KeyError:
-                    final_sources.append(snode)
-                else:
-                    tlist = bld._execute(env, None, [snode], overwarn)
-                    # If the subsidiary Builder returned more than one
-                    # target, then filter out any sources that this
-                    # Builder isn't capable of building.
-                    if len(tlist) > 1:
-                        tlist = filter(match_src_suffix, tlist)
-                    final_sources.extend(tlist)
-            else:
+            for srcsuf in src_suffixes:
+                if str(snode)[-len(srcsuf):] == srcsuf and sdict.has_key(srcsuf):
+                    tgt = sdict[srcsuf]._execute(env, None, snode, overwarn)
+                    # If the subsidiary Builder returned more than one target,
+                    # then filter out any sources that this Builder isn't
+                    # capable of building.
+                    if len(tgt) > 1:
+                        tgt = filter(lambda x, self=self, suf=src_suffixes, e=env:
+                                     self.splitext(SCons.Util.to_String(x),e)[1] in suf,
+                                     tgt)
+                    final_sources.extend(tgt)
+                    snode = None
+                    break
+            if snode:
                 final_sources.append(snode)
-
+                
         return BuilderBase._execute(self, env, target, final_sources, overwarn)
 
     def get_src_builders(self, env):
@@ -802,12 +772,15 @@ class MultiStepBuilder(BuilderBase):
     def src_suffixes(self, env):
         """Return a list of the src_suffix attributes for all
         src_builders of this Builder.
-        __cacheable__
         """
-        suffixes = BuilderBase.src_suffixes(self, env)
-        for builder in self.get_src_builders(env):
-            suffixes.extend(builder.src_suffixes(env))
-        return suffixes
+        try:
+            return self.cached_src_suffixes[id(env)]
+        except KeyError:
+            suffixes = BuilderBase.src_suffixes(self, env)
+            for builder in self.get_src_builders(env):
+                suffixes.extend(builder.src_suffixes(env))
+            self.cached_src_suffixes[id(env)] = suffixes
+            return suffixes
 
 class CompositeBuilder(SCons.Util.Proxy):
     """A Builder Proxy whose main purpose is to always have
@@ -816,7 +789,7 @@ class CompositeBuilder(SCons.Util.Proxy):
     """
 
     def __init__(self, builder, cmdgen):
-        if __debug__: logInstanceCreation(self, 'Builder.CompositeBuilder')
+        if __debug__: logInstanceCreation(self)
         SCons.Util.Proxy.__init__(self, builder)
 
         # cmdgen should always be an instance of DictCmdGenerator.
index 233336c731d2215ca2f04ccdcc15b0678d1b0e26..2075bf0e85d8bf51f0e4e480127a5a522714b8d5 100644 (file)
@@ -34,7 +34,6 @@ import os.path
 import sys
 import types
 import unittest
-import UserList
 
 import TestCmd
 
@@ -55,9 +54,6 @@ test = TestCmd.TestCmd(workdir = '')
 outfile = test.workpath('outfile')
 outfile2 = test.workpath('outfile2')
 
-infile = test.workpath('infile')
-test.write(infile, "infile\n")
-
 show_string = None
 
 scons_env = SCons.Environment.Environment()
@@ -75,7 +71,6 @@ class Environment:
         global env_arg2nodes_called
         env_arg2nodes_called = None
         self.scanner = None
-        self.fs = SCons.Node.FS.FS()
     def subst(self, s):
         if not SCons.Util.is_String(s):
             return s
@@ -87,14 +82,6 @@ class Environment:
         except IndexError:
             pass
         return self.d.get(s, s)
-    def subst_target_source(self, string, raw=0, target=None,
-                            source=None, dict=None, conv=None):
-        return SCons.Util.scons_subst(string, self, raw, target,
-                                      source, dict, conv)
-    def subst_list(self, string, raw=0, target=None,
-                   source=None, dict=None, conv=None):
-        return SCons.Util.scons_subst_list(string, self, raw, target,
-                                           source, dict, conv)
     def arg2nodes(self, args, factory):
         global env_arg2nodes_called
         env_arg2nodes_called = 1
@@ -106,8 +93,6 @@ class Environment:
                 a = factory(a)
             list.append(a)
         return list
-    def get_factory(self, factory):
-        return factory or self.fs.File
     def get_scanner(self, ext):
         return self.scanner
     def Dictionary(self):
@@ -127,7 +112,6 @@ class Environment:
     def Override(self, overrides):
         env = apply(Environment, (), self.d)
         env.d.update(overrides)
-        env.scanner = self.scanner
         return env
     def _update(self, dict):
         self.d.update(dict)
@@ -141,32 +125,23 @@ class Environment:
         d['SOURCES'] = ['__s1__', '__s2__', '__s3__', '__s4__', '__s5__', '__s6__']
         d['SOURCE'] = d['SOURCES'][0]
         return d
-    def __cmp__(self, other):
-        return cmp(self.scanner, other.scanner) or cmp(self.d, other.d)
-
-class MyAction:
-    def __init__(self, action):
-        self.action = action
-    def get_executor(self, env, overrides, tlist, slist, executor_kw):
-        return ['executor'] + [self.action]
 
 class MyNode_without_target_from_source:
     def __init__(self, name):
         self.name = name
         self.sources = []
         self.builder = None
-        self.is_explicit = None
         self.side_effect = 0
+        self.source_scanner = None
+        self.backup_source_scanner = None
     def __str__(self):
         return self.name
     def builder_set(self, builder):
         self.builder = builder
     def has_builder(self):
         return not self.builder is None
-    def set_explicit(self, is_explicit):
-        self.is_explicit = is_explicit
     def has_explicit_builder(self):
-        return self.is_explicit
+        return not self.builder is None and self.builder.is_explicit
     def env_set(self, env, safe=0):
         self.env = env
     def add_source(self, source):
@@ -231,11 +206,8 @@ class BuilderTestCase(unittest.TestCase):
                                         target_factory=MyNode,
                                         source_factory=MyNode)
 
-        tgt = builder(env, source=[])
-        assert tgt == [], tgt
-
-        n1 = MyNode("n1")
-        n2 = MyNode("n2")
+        n1 = MyNode("n1");
+        n2 = MyNode("n2");
         builder(env, target = n1, source = n2)
         assert env_arg2nodes_called
         assert n1.env == env, n1.env
@@ -244,48 +216,15 @@ class BuilderTestCase(unittest.TestCase):
         assert n1.executor, "no executor found"
         assert not hasattr(n2, 'env')
 
-        l = [1]
-        ul = UserList.UserList([2])
-        try:
-            l.extend(ul)
-        except TypeError:
-            def mystr(l):
-                return str(map(str, l))
-        else:
-            mystr = str
-
-        nnn1 = MyNode("nnn1")
-        nnn2 = MyNode("nnn2")
-        tlist = builder(env, target = [nnn1, nnn2], source = [])
-        s = mystr(tlist)
-        assert s == "['nnn1', 'nnn2']", s
-        l = map(str, tlist)
-        assert l == ['nnn1', 'nnn2'], l
-
-        tlist = builder(env, target = 'n3', source = 'n4')
-        s = mystr(tlist)
-        assert s == "['n3']", s
-        target = tlist[0]
-        l = map(str, tlist)
-        assert l == ['n3'], l
+        target = builder(env, target = 'n3', source = 'n4')[0]
         assert target.name == 'n3'
         assert target.sources[0].name == 'n4'
 
-        tlist = builder(env, target = 'n4 n5', source = ['n6 n7'])
-        s = mystr(tlist)
-        assert s == "['n4 n5']", s
-        l = map(str, tlist)
-        assert l == ['n4 n5'], l
-        target = tlist[0]
+        target = builder(env, target = 'n4 n5', source = ['n6 n7'])[0]
         assert target.name == 'n4 n5'
         assert target.sources[0].name == 'n6 n7'
 
-        tlist = builder(env, target = ['n8 n9'], source = 'n10 n11')
-        s = mystr(tlist)
-        assert s == "['n8 n9']", s
-        l = map(str, tlist)
-        assert l == ['n8 n9'], l
-        target = tlist[0]
+        target = builder(env, target = ['n8 n9'], source = 'n10 n11')[0]
         assert target.name == 'n8 n9'
         assert target.sources[0].name == 'n10 n11'
 
@@ -315,7 +254,7 @@ class BuilderTestCase(unittest.TestCase):
         n20 = MyNode_without_target_from_source('n20')
         flag = 0
         try:
-            target = builder(env, None, source=n20)
+            target = builder(env, source=n20)
         except SCons.Errors.UserError, e:
             flag = 1
         assert flag, "UserError should be thrown if a source node can't create a target."
@@ -325,7 +264,7 @@ class BuilderTestCase(unittest.TestCase):
                                         source_factory=MyNode,
                                         prefix='p-',
                                         suffix='.s')
-        target = builder(env, None, source='n21')[0]
+        target = builder(env, source='n21')[0]
         assert target.name == 'p-n21.s', target
 
         builder = SCons.Builder.Builder(misspelled_action="foo",
@@ -392,29 +331,6 @@ class BuilderTestCase(unittest.TestCase):
         builder = SCons.Builder.Builder(generator=generator)
         assert builder.action.generator == generator
 
-    def test_get_name(self):
-        """Test the get_name() method
-        """
-
-    def test_get_single_executor(self):
-        """Test the get_single_executor() method
-        """
-        b = SCons.Builder.Builder(action='foo')
-        x = b.get_single_executor({}, [], [], {})
-        assert not x is None, x
-
-    def test_get_multi_executor(self):
-        """Test the get_multi_executor() method
-        """
-        b = SCons.Builder.Builder(action='foo', multi=1)
-        t1 = MyNode('t1')
-        s1 = MyNode('s1')
-        s2 = MyNode('s2')
-        x1 = b.get_multi_executor({}, [t1], [s1], {})
-        t1.executor = x1
-        x2 = b.get_multi_executor({}, [t1], [s2], {})
-        assert x1 is x2, "%s is not %s" % (repr(x1), repr(x2))
-
     def test_cmp(self):
         """Test simple comparisons of Builder objects
         """
@@ -496,10 +412,10 @@ class BuilderTestCase(unittest.TestCase):
         tgt = builder(env, target = 'tgt2a tgt2b', source = 'src2')[0]
         assert tgt.path == 'libtgt2a tgt2b', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, target = None, source = 'src3')[0]
+        tgt = builder(env, source = 'src3')[0]
         assert tgt.path == 'libsrc3', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, target = None, source = 'lib/src4')[0]
+        tgt = builder(env, source = 'lib/src4')[0]
         assert tgt.path == os.path.join('lib', 'libsrc4'), \
                 "Target has unexpected name: %s" % tgt.path
         tgt = builder(env, target = 'lib/tgt5', source = 'lib/src5')[0]
@@ -523,35 +439,19 @@ class BuilderTestCase(unittest.TestCase):
                                                   '$FOO' : 'foo-',
                                                   '.zzz' : my_emit},
                                         action = '')
-        tgt = builder(my_env, target = None, source = 'f1')[0]
+        tgt = builder(my_env, source = 'f1')[0]
         assert tgt.path == 'default-f1', tgt.path
-        tgt = builder(my_env, target = None, source = 'f2.c')[0]
+        tgt = builder(my_env, source = 'f2.c')[0]
         assert tgt.path == 'default-f2', tgt.path
-        tgt = builder(my_env, target = None, source = 'f3.in')[0]
+        tgt = builder(my_env, source = 'f3.in')[0]
         assert tgt.path == 'out-f3', tgt.path
-        tgt = builder(my_env, target = None, source = 'f4.x')[0]
+        tgt = builder(my_env, source = 'f4.x')[0]
         assert tgt.path == 'y-f4', tgt.path
-        tgt = builder(my_env, target = None, source = 'f5.foo')[0]
+        tgt = builder(my_env, source = 'f5.foo')[0]
         assert tgt.path == 'foo-f5', tgt.path
-        tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
+        tgt = builder(my_env, source = 'f6.zzz')[0]
         assert tgt.path == 'emit-f6', tgt.path
 
-    def test_set_suffix(self):
-        """Test the set_suffix() method"""
-        b = SCons.Builder.Builder(action='')
-        env = Environment(XSUFFIX = '.x')
-
-        s = b.get_suffix(env)
-        assert s == '', s
-
-        b.set_suffix('.foo')
-        s = b.get_suffix(env)
-        assert s == '.foo', s
-
-        b.set_suffix('$XSUFFIX')
-        s = b.get_suffix(env)
-        assert s == '.x', s
-
     def test_src_suffix(self):
         """Test Builder creation with a specified source file suffix
         
@@ -592,7 +492,7 @@ class BuilderTestCase(unittest.TestCase):
         b6 = SCons.Builder.Builder(action = '',
                                    src_suffix='_src.a',
                                    suffix='.b')
-        tgt = b6(env, target=None, source='foo_src.a')
+        tgt = b6(env, source='foo_src.a')
         assert str(tgt[0]) == 'foo.b', str(tgt[0])
 
         b7 = SCons.Builder.Builder(action = '',
@@ -601,16 +501,16 @@ class BuilderTestCase(unittest.TestCase):
         b8 = SCons.Builder.Builder(action = '',
                                    src_builder=b7,
                                    suffix='.c')
-        tgt = b8(env, target=None, source='foo_source.a')
+        tgt = b8(env, source='foo_source.a')
         assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
-        src = env.fs.File('foo_source.a')
-        tgt = b8(env, target=None, source=src)
+        src = SCons.Node.FS.default_fs.File('foo_source.a')
+        tgt = b8(env, source=src)
         assert str(tgt[0]) == 'foo_obj.c', str(tgt[0])
 
         b9 = SCons.Builder.Builder(action={'_src.a' : 'srcaction'},
                                    suffix='.c')
         b9.add_action('_altsrc.b', 'altaction')
-        tgt = b9(env, target=None, source='foo_altsrc.b')
+        tgt = b9(env, source='foo_altsrc.b')
         assert str(tgt[0]) == 'foo.c', str(tgt[0])
 
     def test_suffix(self):
@@ -630,7 +530,7 @@ class BuilderTestCase(unittest.TestCase):
         tgt = builder(env, target = 'tgt4a tgt4b', source = 'src4')[0]
         assert tgt.path == 'tgt4a tgt4b.o', \
                 "Target has unexpected name: %s" % tgt.path
-        tgt = builder(env, target = None, source = 'src5')[0]
+        tgt = builder(env, source = 'src5')[0]
         assert tgt.path == 'src5.o', \
                 "Target has unexpected name: %s" % tgt.path
 
@@ -651,17 +551,17 @@ class BuilderTestCase(unittest.TestCase):
                                                   '$BAR' : '.new',
                                                   '.zzz' : my_emit},
                                         action='')
-        tgt = builder(my_env, target = None, source = 'f1')[0]
+        tgt = builder(my_env, source = 'f1')[0]
         assert tgt.path == 'f1.default', tgt.path
-        tgt = builder(my_env, target = None, source = 'f2.c')[0]
+        tgt = builder(my_env, source = 'f2.c')[0]
         assert tgt.path == 'f2.default', tgt.path
-        tgt = builder(my_env, target = None, source = 'f3.in')[0]
+        tgt = builder(my_env, source = 'f3.in')[0]
         assert tgt.path == 'f3.out', tgt.path
-        tgt = builder(my_env, target = None, source = 'f4.x')[0]
+        tgt = builder(my_env, source = 'f4.x')[0]
         assert tgt.path == 'f4.y', tgt.path
-        tgt = builder(my_env, target = None, source = 'f5.bar')[0]
+        tgt = builder(my_env, source = 'f5.bar')[0]
         assert tgt.path == 'f5.new', tgt.path
-        tgt = builder(my_env, target = None, source = 'f6.zzz')[0]
+        tgt = builder(my_env, source = 'f6.zzz')[0]
         assert tgt.path == 'f6.emit', tgt.path
 
     def test_single_source(self):
@@ -689,7 +589,7 @@ class BuilderTestCase(unittest.TestCase):
         tgt.prepare()
         tgt.build()
         assert env['CNT'][0] == 2
-        tgts = builder(env, None, infiles[2:4])
+        tgts = builder(env, infiles[2:4])
         for t in tgts: t.prepare()
         tgts[0].build()
         tgts[1].build()
@@ -721,11 +621,7 @@ class BuilderTestCase(unittest.TestCase):
 
         env = Environment()
         builder = SCons.Builder.Builder(action = function2)
-
-        tgts = builder(env, source=[])
-        assert tgts == [], tgts
-
-        tgts = builder(env, target = [outfile, outfile2], source = infile)
+        tgts = builder(env, target = [outfile, outfile2], source = 'foo')
         for t in tgts:
             t.prepare()
         try:
@@ -749,7 +645,7 @@ class BuilderTestCase(unittest.TestCase):
             return 1
 
         builder = SCons.Builder.Builder(action = function3)
-        tgts = builder(env, target = [sub1_out, sub2_out], source = infile)
+        tgts = builder(env, target = [sub1_out, sub2_out], source = 'foo')
         for t in tgts:
             t.prepare()
         try:
@@ -769,29 +665,23 @@ class BuilderTestCase(unittest.TestCase):
         builder1 = SCons.Builder.Builder(action='foo',
                                          src_suffix='.bar',
                                          suffix='.foo')
-        builder2 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
+        builder2 = SCons.Builder.MultiStepBuilder(action='bar',
                                                   src_builder = builder1,
                                                   src_suffix = '.foo')
 
-        tgt = builder2(env, source=[])
-        assert tgt == [], tgt
-
         tgt = builder2(env, target='baz',
                        source=['test.bar', 'test2.foo', 'test3.txt'])[0]
-        s = str(tgt)
-        assert s == 'baz', s
-        s = map(str, tgt.sources)
-        assert s == ['test.foo', 'test2.foo', 'test3.txt'], s
-        s = map(str, tgt.sources[0].sources)
-        assert s == ['test.bar'], s
-
-        tgt = builder2(env, None, 'aaa.bar')[0]
-        s = str(tgt)
-        assert s == 'aaa', s
-        s = map(str, tgt.sources)
-        assert s == ['aaa.foo'], s
-        s = map(str, tgt.sources[0].sources)
-        assert s == ['aaa.bar'], s
+        assert str(tgt.sources[0]) == 'test.foo', str(tgt.sources[0])
+        assert str(tgt.sources[0].sources[0]) == 'test.bar', \
+               str(tgt.sources[0].sources[0])
+        assert str(tgt.sources[1]) == 'test2.foo', str(tgt.sources[1])
+        assert str(tgt.sources[2]) == 'test3.txt', str(tgt.sources[2])
+
+        tgt = builder2(env, 'aaa.bar')[0]
+        assert str(tgt) == 'aaa', str(tgt)
+        assert str(tgt.sources[0]) == 'aaa.foo', str(tgt.sources[0])
+        assert str(tgt.sources[0].sources[0]) == 'aaa.bar', \
+               str(tgt.sources[0].sources[0])
 
         builder3 = SCons.Builder.MultiStepBuilder(action = 'foo',
                                                   src_builder = 'xyzzy',
@@ -801,23 +691,21 @@ class BuilderTestCase(unittest.TestCase):
         builder4 = SCons.Builder.Builder(action='bld4',
                                          src_suffix='.i',
                                          suffix='_wrap.c')
-        builder5 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
+        builder5 = SCons.Builder.MultiStepBuilder(action='bld5',
                                                   src_builder=builder4,
                                                   suffix='.obj',
                                                   src_suffix='.c')
-        builder6 = SCons.Builder.MultiStepBuilder(action=MyAction('act'),
+        builder6 = SCons.Builder.MultiStepBuilder(action='bld6',
                                                   src_builder=builder5,
                                                   suffix='.exe',
                                                   src_suffix='.obj')
         tgt = builder6(env, 'test', 'test.i')[0]
-        s = str(tgt)
-        assert s == 'test.exe', s
-        s = map(str, tgt.sources)
-        assert s == ['test_wrap.obj'], s
-        s = map(str, tgt.sources[0].sources)
-        assert s == ['test_wrap.c'], s
-        s = map(str, tgt.sources[0].sources[0].sources)
-        assert s == ['test.i'], s
+        assert str(tgt) == 'test.exe', str(tgt)
+        assert str(tgt.sources[0]) == 'test_wrap.obj', str(tgt.sources[0])
+        assert str(tgt.sources[0].sources[0]) == 'test_wrap.c', \
+               str(tgt.sources[0].sources[0])
+        assert str(tgt.sources[0].sources[0].sources[0]) == 'test.i', \
+               str(tgt.sources[0].sources[0].sources[0])
         
     def test_CompositeBuilder(self):
         """Testing CompositeBuilder class."""
@@ -829,21 +717,15 @@ class BuilderTestCase(unittest.TestCase):
                                                  '.bar' : func_action,
                                                  '$BAR_SUFFIX' : func_action,
                                                  '$FOO_SUFFIX' : func_action })
-
-        tgt = builder(env, source=[])
-        assert tgt == [], tgt
         
         assert isinstance(builder, SCons.Builder.CompositeBuilder)
         assert isinstance(builder.action, SCons.Action.CommandGeneratorAction)
-
         tgt = builder(env, target='test1', source='test1.foo')[0]
         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
         assert tgt.builder.action is builder.action
-
         tgt = builder(env, target='test2', source='test1.bar')[0]
         assert isinstance(tgt.builder, SCons.Builder.BuilderBase)
         assert tgt.builder.action is builder.action
-
         flag = 0
         tgt = builder(env, target='test3', source=['test2.bar', 'test1.foo'])[0]
         try:
@@ -970,8 +852,8 @@ class BuilderTestCase(unittest.TestCase):
                                         source_scanner=sscan,
                                         action='')
         tgt = builder(env, target='foo2', source='bar')[0]
-        assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
-        assert tgt.builder.source_scanner == sscan, tgt.builder.source_scanner
+        assert tgt.target_scanner == tscan, tgt.target_scanner
+        assert tgt.source_scanner == sscan, tgt.source_scanner
 
         builder1 = SCons.Builder.Builder(action='foo',
                                          src_suffix='.bar',
@@ -981,8 +863,8 @@ class BuilderTestCase(unittest.TestCase):
                                          target_scanner = tscan,
                                          source_scanner = tscan)
         tgt = builder2(env, target='baz2', source='test.bar test2.foo test3.txt')[0]
-        assert tgt.builder.target_scanner == tscan, tgt.builder.target_scanner
-        assert tgt.builder.source_scanner == tscan, tgt.builder.source_scanner
+        assert tgt.target_scanner == tscan, tgt.target_scanner
+        assert tgt.source_scanner == tscan, tgt.source_scanner
 
     def test_actual_scanner(self):
         """Test usage of actual Scanner objects."""
@@ -1008,84 +890,28 @@ class BuilderTestCase(unittest.TestCase):
                  return 'TestScannerkey'
             def instance(self, env):
                  return self
-            def select(self, node):
-                 return self
-            name = 'TestScanner'
-            def __str__(self):
-                return self.name
 
         scanner = TestScanner()
         builder = SCons.Builder.Builder(action='action')
 
         # With no scanner specified, source_scanner and
         # backup_source_scanner are None.
-        bar_y = MyNode('bar.y')
         env1 = Environment()
         tgt = builder(env1, target='foo1.x', source='bar.y')[0]
         src = tgt.sources[0]
-        assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
-        assert tgt.builder.source_scanner is None, tgt.builder.source_scanner
-        assert tgt.get_source_scanner(bar_y) is None, tgt.get_source_scanner(bar_y)
-        assert not src.has_builder(), src.has_builder()
-        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
-
-        # An Environment that has suffix-specified SCANNERS should
-        # provide a source scanner to the target.
-        class EnvTestScanner:
-            def key(self, env):
-                 return '.y'
-            def instance(self, env):
-                 return self
-            name = 'EnvTestScanner'
-            def __str__(self):
-                return self.name
-            def select(self, node):
-                return self
-            def path(self, env, dir=None):
-                return ()
-            def __call__(self, node, env, path):
-                return []
-        env3 = Environment(SCANNERS = [EnvTestScanner()])
-        env3.scanner = EnvTestScanner() # test env's version of SCANNERS
-        tgt = builder(env3, target='foo2.x', source='bar.y')[0]
+        assert tgt.target_scanner != scanner, tgt.target_scanner
+        assert src.source_scanner is None, src.source_scanner
+        assert src.backup_source_scanner is None, src.backup_source_scanner
+
+        # Later use of the same source file with an environment that
+        # has a scanner must still set the scanner.
+        env2 = Environment()
+        env2.scanner = scanner
+        tgt = builder(env2, target='foo2.x', source='bar.y')[0]
         src = tgt.sources[0]
-        assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
-        assert not tgt.builder.source_scanner, tgt.builder.source_scanner
-        assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
-        assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
-        assert not src.has_builder(), src.has_builder()
-        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
-
-        # Can't simply specify the scanner as a builder argument; it's
-        # global to all invocations of this builder.
-        tgt = builder(env3, target='foo3.x', source='bar.y', source_scanner = scanner)[0]
-        src = tgt.sources[0]
-        assert tgt.builder.target_scanner != scanner, tgt.builder.target_scanner
-        assert not tgt.builder.source_scanner, tgt.builder.source_scanner
-        assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
-        assert str(tgt.get_source_scanner(bar_y)) == 'EnvTestScanner', tgt.get_source_scanner(bar_y)
-        assert not src.has_builder(), src.has_builder()
-        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
-
-        # Now use a builder that actually has scanners and ensure that
-        # the target is set accordingly (using the specified scanner
-        # instead of the Environment's scanner)
-        builder = SCons.Builder.Builder(action='action',
-                                        source_scanner=scanner,
-                                        target_scanner=scanner)
-        tgt = builder(env3, target='foo4.x', source='bar.y')[0]
-        src = tgt.sources[0]
-        assert tgt.builder.target_scanner == scanner, tgt.builder.target_scanner
-        assert tgt.builder.source_scanner, tgt.builder.source_scanner
-        assert tgt.builder.source_scanner == scanner, tgt.builder.source_scanner
-        assert str(tgt.builder.source_scanner) == 'TestScanner', str(tgt.builder.source_scanner)
-        assert tgt.get_source_scanner(bar_y), tgt.get_source_scanner(bar_y)
-        assert tgt.get_source_scanner(bar_y) == scanner, tgt.get_source_scanner(bar_y)
-        assert str(tgt.get_source_scanner(bar_y)) == 'TestScanner', tgt.get_source_scanner(bar_y)
-        assert not src.has_builder(), src.has_builder()
-        assert src.get_source_scanner(bar_y) is None, src.get_source_scanner(bar_y)
-
-
+        assert tgt.target_scanner != scanner, tgt.target_scanner
+        assert src.source_scanner is None, src.source_scanner
+        assert src.backup_source_scanner == scanner, src.backup_source_scanner
 
     def test_Builder_API(self):
         """Test Builder interface.
@@ -1095,8 +921,8 @@ class BuilderTestCase(unittest.TestCase):
         forms of component specifications."""
 
         builder = SCons.Builder.Builder()
-        env = Environment(BUILDERS={'Bld':builder})
 
+        env = Environment(BUILDERS={'Bld':builder})
         r = builder.get_name(env)
         assert r == 'Bld', r
         r = builder.get_prefix(env)
@@ -1111,34 +937,23 @@ class BuilderTestCase(unittest.TestCase):
         assert r == ['foo'], r
 
         # src_suffix can be a single string or a list of strings
-        # src_suffixes() caches its return value, so we use a new
-        # Builder each time we do any of these tests
-
-        bld = SCons.Builder.Builder()
-        env = Environment(BUILDERS={'Bld':bld})
 
-        bld.set_src_suffix('.foo')
-        r = bld.get_src_suffix(env)
+        builder.set_src_suffix('.foo')
+        r = builder.get_src_suffix(env)
         assert r == '.foo', r
-        r = bld.src_suffixes(env)
+        r = builder.src_suffixes(env)
         assert r == ['.foo'], r
 
-        bld = SCons.Builder.Builder()
-        env = Environment(BUILDERS={'Bld':bld})
-
-        bld.set_src_suffix(['.foo', '.bar'])
-        r = bld.get_src_suffix(env)
+        builder.set_src_suffix(['.foo', '.bar'])
+        r = builder.get_src_suffix(env)
         assert r == '.foo', r
-        r = bld.src_suffixes(env)
+        r = builder.src_suffixes(env)
         assert r == ['.foo', '.bar'], r
 
-        bld = SCons.Builder.Builder()
-        env = Environment(BUILDERS={'Bld':bld})
-
-        bld.set_src_suffix(['.bar', '.foo'])
-        r = bld.get_src_suffix(env)
+        builder.set_src_suffix(['.bar', '.foo'])
+        r = builder.get_src_suffix(env)
         assert r == '.bar', r
-        r = bld.src_suffixes(env)
+        r = builder.src_suffixes(env)
         assert r == ['.bar', '.foo'], r
 
         # adjust_suffix normalizes the suffix, adding a `.' if needed
@@ -1211,8 +1026,8 @@ class BuilderTestCase(unittest.TestCase):
         assert r == '.D', r
 
         builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
-        env = Environment(BUILDERS={'Bld':builder})
 
+        env = Environment(BUILDERS={'Bld':builder})
         r = builder.get_name(env)
         assert r == 'Bld', r
         r = builder.get_prefix(env)
@@ -1228,10 +1043,7 @@ class BuilderTestCase(unittest.TestCase):
         # whose keys are the source suffix.  The add_action()
         # specifies a new source suffix/action binding.
 
-        builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
-        env = Environment(BUILDERS={'Bld':builder})
         builder.add_action('.src_sfx1', 'FOO')
-
         r = builder.get_name(env)
         assert r == 'Bld', r
         r = builder.get_prefix(env)
@@ -1245,9 +1057,6 @@ class BuilderTestCase(unittest.TestCase):
         r = builder.src_suffixes(env)
         assert r == ['.src_sfx1'], r
 
-        builder = SCons.Builder.Builder(prefix='A_', suffix={}, action={})
-        env = Environment(BUILDERS={'Bld':builder})
-        builder.add_action('.src_sfx1', 'FOO')
         builder.add_action('.src_sfx2', 'BAR')
 
         r = builder.get_name(env)
@@ -1375,11 +1184,11 @@ class BuilderTestCase(unittest.TestCase):
                                                   '.4b':emit4b},
                                          target_factory=MyNode,
                                          source_factory=MyNode)
-        tgt = builder4(env, None, source='aaa.4a')[0]
+        tgt = builder4(env, source='aaa.4a')[0]
         assert str(tgt) == 'emit4a-aaa', str(tgt)
-        tgt = builder4(env, None, source='bbb.4b')[0]
+        tgt = builder4(env, source='bbb.4b')[0]
         assert str(tgt) == 'emit4b-bbb', str(tgt)
-        tgt = builder4(env, None, source='ccc.4c')[0]
+        tgt = builder4(env, source='ccc.4c')[0]
         assert str(tgt) == 'ccc', str(tgt)
 
         def emit4c(target, source, env):
@@ -1387,7 +1196,7 @@ class BuilderTestCase(unittest.TestCase):
             target = map(lambda x: 'emit4c-' + x[:-3], source)
             return (target, source)
         builder4.add_emitter('.4c', emit4c)
-        tgt = builder4(env, None, source='ccc.4c')[0]
+        tgt = builder4(env, source='ccc.4c')[0]
         assert str(tgt) == 'emit4c-ccc', str(tgt)
 
         # Test a list of emitter functions.
@@ -1432,43 +1241,43 @@ class BuilderTestCase(unittest.TestCase):
         env = Environment()
         b = SCons.Builder.Builder(action='foo', suffix='.o')
 
-        tgt = b(env, None, 'aaa')[0]
+        tgt = b(env, 'aaa')[0]
         assert str(tgt) == 'aaa.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'aaa', map(str, tgt.sources)
 
-        tgt = b(env, None, 'bbb.c')[0]
+        tgt = b(env, 'bbb.c')[0]
         assert str(tgt) == 'bbb.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'bbb.c', map(str, tgt.sources)
 
-        tgt = b(env, None, 'ccc.x.c')[0]
+        tgt = b(env, 'ccc.x.c')[0]
         assert str(tgt) == 'ccc.x.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'ccc.x.c', map(str, tgt.sources)
 
-        tgt = b(env, None, ['d0.c', 'd1.c'])[0]
+        tgt = b(env, ['d0.c', 'd1.c'])[0]
         assert str(tgt) == 'd0.o', str(tgt)
         assert len(tgt.sources) == 2,  map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'd0.c', map(str, tgt.sources)
         assert str(tgt.sources[1]) == 'd1.c', map(str, tgt.sources)
 
-        tgt = b(env, target = None, source='eee')[0]
+        tgt = b(env, source='eee')[0]
         assert str(tgt) == 'eee.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'eee', map(str, tgt.sources)
 
-        tgt = b(env, target = None, source='fff.c')[0]
+        tgt = b(env, source='fff.c')[0]
         assert str(tgt) == 'fff.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'fff.c', map(str, tgt.sources)
 
-        tgt = b(env, target = None, source='ggg.x.c')[0]
+        tgt = b(env, source='ggg.x.c')[0]
         assert str(tgt) == 'ggg.x.o', str(tgt)
         assert len(tgt.sources) == 1, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'ggg.x.c', map(str, tgt.sources)
 
-        tgt = b(env, target = None, source=['h0.c', 'h1.c'])[0]
+        tgt = b(env, source=['h0.c', 'h1.c'])[0]
         assert str(tgt) == 'h0.o', str(tgt)
         assert len(tgt.sources) == 2,  map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'h0.c', map(str, tgt.sources)
@@ -1476,7 +1285,7 @@ class BuilderTestCase(unittest.TestCase):
 
         w = b(env, target='i0.w', source=['i0.x'])[0]
         y = b(env, target='i1.y', source=['i1.z'])[0]
-        tgt = b(env, None, source=[w, y])[0]
+        tgt = b(env, source=[w, y])[0]
         assert str(tgt) == 'i0.o', str(tgt)
         assert len(tgt.sources) == 2, map(str, tgt.sources)
         assert str(tgt.sources[0]) == 'i0.w', map(str, tgt.sources)
@@ -1507,31 +1316,18 @@ class BuilderTestCase(unittest.TestCase):
                                      'B2': b2,
                                      'B3': b3,
                                      'B4': b4})
-        # With no name, get_name will return the class.  Allow
-        # for caching...
-        b6_names = [
-            'SCons.Builder.BuilderBase',
-            "<class 'SCons.Builder.BuilderBase'>",
-            'SCons.Memoize.BuilderBase',
-            "<class 'SCons.Memoize.BuilderBase'>",
-        ]
-
         assert b1.get_name(env) == 'bldr1', b1.get_name(env)
         assert b2.get_name(env) == 'bldr2', b2.get_name(env)
         assert b3.get_name(env) == 'bldr3', b3.get_name(env)
         assert b4.get_name(env) == 'bldr4', b4.get_name(env)
         assert b5.get_name(env) == 'builder5', b5.get_name(env)
-        assert b6.get_name(env) in b6_names, b6.get_name(env)
-
+        assert b6.get_name(env) == 'SCons.Builder.BuilderBase', b6.get_name(env)
         assert b1.get_name(env2) == 'B1', b1.get_name(env2)
         assert b2.get_name(env2) == 'B2', b2.get_name(env2)
         assert b3.get_name(env2) == 'B3', b3.get_name(env2)
         assert b4.get_name(env2) == 'B4', b4.get_name(env2)
         assert b5.get_name(env2) == 'builder5', b5.get_name(env2)
-        assert b6.get_name(env2) in b6_names, b6.get_name(env2)
-
-        assert b5.get_name(None) == 'builder5', b5.get_name(None)
-        assert b6.get_name(None) in b6_names, b6.get_name(None)
+        assert b6.get_name(env2) == 'SCons.Builder.BuilderBase', b6.get_name(env2)
 
         for B in b3.get_src_builders(env):
             assert B.get_name(env) == 'bldr1'
@@ -1540,8 +1336,7 @@ class BuilderTestCase(unittest.TestCase):
 
         tgts = b1(env, target = [outfile, outfile2], source='moo')
         for t in tgts:
-            name = t.builder.get_name(env)
-            assert name == 'ListBuilder(bldr1)', name
+            assert t.builder.get_name(env) == 'ListBuilder(bldr1)'
             # 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
index 810ede73e2ce2535caab5572855b3115081984ae..883b82c9037a600d8f81ddeefd0746bbd7e7ebbd 100644 (file)
@@ -5,8 +5,9 @@ File system nodes.
 These Nodes represent the canonical external objects that people think
 of when they think of building software: files and directories.
 
-This holds a "default_fs" variable that should be initialized with an FS
-that can be used by scripts or modules looking for the canonical default.
+This initializes a "default_fs" Node with an FS at the current directory
+for its own purposes, and for use by scripts or modules looking for the
+canonical default.
 
 """
 
@@ -49,14 +50,9 @@ from SCons.Debug import logInstanceCreation
 import SCons.Errors
 import SCons.Node
 import SCons.Sig.MD5
-import SCons.Subst
 import SCons.Util
 import SCons.Warnings
 
-# The max_drift value:  by default, use a cached signature value for
-# any file that's been untouched for more than two days.
-default_max_drift = 2*24*60*60
-
 #
 # We stringify these file system Nodes a lot.  Turning a file system Node
 # into a string is non-trivial, because the final string representation
@@ -147,7 +143,7 @@ def LinkFunc(target, source, env):
     src = source[0].abspath
     dest = target[0].abspath
     dir, file = os.path.split(dest)
-    if dir and not target[0].fs.isdir(dir):
+    if dir and not os.path.isdir(dir):
         os.makedirs(dir)
     if not Link_Funcs:
         # Set a default order of link functions.
@@ -157,13 +153,7 @@ def LinkFunc(target, source, env):
         try:
             func(src,dest)
             break
-        except (IOError, OSError):
-            # An OSError indicates something happened like a permissions
-            # problem or an attempt to symlink across file-system
-            # boundaries.  An IOError indicates something like the file
-            # not existing.  In either case, keeping trying additional
-            # functions in the list and only raise an error if the last
-            # one failed.
+        except OSError:
             if func == Link_Funcs[-1]:
                 # exception of the last link method (copy) are fatal
                 raise
@@ -186,8 +176,9 @@ Unlink = SCons.Action.Action(UnlinkFunc, None)
 
 def MkdirFunc(target, source, env):
     t = target[0]
-    if not t.exists():
-        t.fs.mkdir(t.abspath)
+    p = t.abspath
+    if not t.fs.exists(p):
+        t.fs.mkdir(p)
     return 0
 
 Mkdir = SCons.Action.Action(MkdirFunc, None, presub=None)
@@ -247,12 +238,10 @@ def CachePushFunc(target, source, env):
         fs.rename(tempfile, cachefile)
         st = fs.stat(t.path)
         fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
-    except (IOError, OSError):
-        # It's possible someone else tried writing the file at the
-        # same time we did, or else that there was some problem like
-        # the CacheDir being on a separate file system that's full.
-        # In any case, inability to push a file to cache doesn't affect
-        # the correctness of the build, so just print a warning.
+    except OSError:
+        # It's possible someone else tried writing the file at the same
+        # time we did.  Print a warning but don't stop the build, since
+        # it doesn't affect the correctness of the build.
         SCons.Warnings.warn(SCons.Warnings.CacheWriteErrorWarning,
                             "Unable to copy %s to cache. Cache file is %s"
                                 % (str(target), cachefile))
@@ -274,8 +263,7 @@ def get_DefaultSCCSBuilder():
         import SCons.Builder
         # "env" will get filled in by Executor.get_build_env()
         # calling SCons.Defaults.DefaultEnvironment() when necessary.
-        act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR')
-        DefaultSCCSBuilder = SCons.Builder.Builder(action = act,
+        DefaultSCCSBuilder = SCons.Builder.Builder(action = '$SCCSCOM',
                                                    env = None,
                                                    name = "DefaultSCCSBuilder")
     return DefaultSCCSBuilder
@@ -286,12 +274,49 @@ def get_DefaultRCSBuilder():
         import SCons.Builder
         # "env" will get filled in by Executor.get_build_env()
         # calling SCons.Defaults.DefaultEnvironment() when necessary.
-        act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR')
-        DefaultRCSBuilder = SCons.Builder.Builder(action = act,
+        DefaultRCSBuilder = SCons.Builder.Builder(action = '$RCS_COCOM',
                                                   env = None,
                                                   name = "DefaultRCSBuilder")
     return DefaultRCSBuilder
 
+#
+class ParentOfRoot:
+    """
+    An instance of this class is used as the parent of the root of a
+    filesystem (POSIX) or drive (Win32). This isn't actually a node,
+    but it looks enough like one so that we don't have to have
+    special purpose code everywhere to deal with dir being None. 
+    This class is an instance of the Null object pattern.
+    """
+    def __init__(self):
+        self.abspath = ''
+        self.path = ''
+        self.name=''
+        self.duplicate=0
+        self.srcdir=None
+        self.build_dirs=[]
+        
+    def is_under(self, dir):
+        return 0
+
+    def up(self):
+        return None
+
+    def getRepositories(self):
+        return []
+
+    def get_dir(self):
+        return None
+
+    def src_builder(self):
+        return _null
+
+    def entry_abspath(self, name):
+        return name
+
+    def entry_path(self, name):
+        return name
+
 # Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem.
 _is_cygwin = sys.platform == "cygwin"
 if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin:
@@ -301,96 +326,31 @@ else:
     def _my_normcase(x):
         return string.upper(x)
 
-
-
-class DiskChecker:
-    def __init__(self, type, do, ignore):
-        self.type = type
-        self.do = do
-        self.ignore = ignore
-        self.set_do()
-    def set_do(self):
-        self.__call__ = self.do
-    def set_ignore(self):
-        self.__call__ = self.ignore
-    def set(self, list):
-        if self.type in list:
-            self.set_do()
-        else:
-            self.set_ignore()
-
-def do_diskcheck_match(node, predicate, errorfmt):
-    path = node.abspath
-    if predicate(path):
-        raise TypeError, errorfmt % path
-
-def ignore_diskcheck_match(node, predicate, errorfmt):
-    pass
-
-def do_diskcheck_rcs(node, name):
-    try:
-        rcs_dir = node.rcs_dir
-    except AttributeError:
-        rcs_dir = node.rcs_dir = node.Dir('RCS')
-    return rcs_dir.entry_exists_on_disk(name+',v')
-
-def ignore_diskcheck_rcs(node, name):
-    return None
-
-def do_diskcheck_sccs(node, name):
-    try:
-        sccs_dir = node.sccs_dir
-    except AttributeError:
-        sccs_dir = node.sccs_dir = node.Dir('SCCS')
-    return sccs_dir.entry_exists_on_disk('s.'+name)
-
-def ignore_diskcheck_sccs(node, name):
-    return None
-
-diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match)
-diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs)
-diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs)
-
-diskcheckers = [
-    diskcheck_match,
-    diskcheck_rcs,
-    diskcheck_sccs,
-]
-
-def set_diskcheck(list):
-    for dc in diskcheckers:
-        dc.set(list)
-
-def diskcheck_types():
-    return map(lambda dc: dc.type, diskcheckers)
-
-
-
 class EntryProxy(SCons.Util.Proxy):
     def __get_abspath(self):
         entry = self.get()
-        return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(),
+        return SCons.Util.SpecialAttrWrapper(entry.get_abspath(),
                                              entry.name + "_abspath")
 
     def __get_filebase(self):
         name = self.get().name
-        return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[0],
+        return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[0],
                                              name + "_filebase")
 
     def __get_suffix(self):
         name = self.get().name
-        return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(name)[1],
+        return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(name)[1],
                                              name + "_suffix")
 
     def __get_file(self):
         name = self.get().name
-        return SCons.Subst.SpecialAttrWrapper(name, name + "_file")
+        return SCons.Util.SpecialAttrWrapper(name, name + "_file")
 
     def __get_base_path(self):
         """Return the file's directory and file name, with the
         suffix stripped."""
         entry = self.get()
-        return SCons.Subst.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0],
+        return SCons.Util.SpecialAttrWrapper(SCons.Util.splitext(entry.get_path())[0],
                                              entry.name + "_base")
 
     def __get_posix_path(self):
@@ -401,7 +361,7 @@ class EntryProxy(SCons.Util.Proxy):
         else:
             entry = self.get()
             r = string.replace(entry.get_path(), os.sep, '/')
-            return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_posix")
+            return SCons.Util.SpecialAttrWrapper(r, entry.name + "_posix")
 
     def __get_win32_path(self):
         """Return the path with \ as the path separator,
@@ -411,7 +371,7 @@ class EntryProxy(SCons.Util.Proxy):
         else:
             entry = self.get()
             r = string.replace(entry.get_path(), os.sep, '\\')
-            return SCons.Subst.SpecialAttrWrapper(r, entry.name + "_win32")
+            return SCons.Util.SpecialAttrWrapper(r, entry.name + "_win32")
 
     def __get_srcnode(self):
         return EntryProxy(self.get().srcnode())
@@ -457,12 +417,6 @@ class EntryProxy(SCons.Util.Proxy):
             except AttributeError:
                 entry = self.get()
                 classname = string.split(str(entry.__class__), '.')[-1]
-                if classname[-2:] == "'>":
-                    # new-style classes report their name as:
-                    #   "<class 'something'>"
-                    # instead of the classic classes:
-                    #   "something"
-                    classname = classname[:-2]
                 raise AttributeError, "%s instance '%s' has no attribute '%s'" % (classname, entry.name, name)
             return attr
 
@@ -491,6 +445,7 @@ class Base(SCons.Node.Node):
 
         self.name = name
         self.fs = fs
+        self.relpath = {self : '.'}
 
         assert directory, "A directory must be provided"
 
@@ -499,11 +454,6 @@ class Base(SCons.Node.Node):
             self.path = name
         else:
             self.path = directory.entry_path(name)
-        if directory.tpath == '.':
-            self.tpath = name
-        else:
-            self.tpath = directory.entry_tpath(name)
-        self.path_elements = directory.path_elements + [self]
 
         self.dir = directory
         self.cwd = None # will hold the SConscript directory for target nodes
@@ -513,15 +463,26 @@ class Base(SCons.Node.Node):
         """Completely clear a Node.FS.Base object of all its cached
         state (so that it can be re-evaluated by interfaces that do
         continuous integration builds).
-        __cache_reset__
         """
         SCons.Node.Node.clear(self)
+        try:
+            delattr(self, '_exists')
+        except AttributeError:
+            pass
+        try:
+            delattr(self, '_rexists')
+        except AttributeError:
+            pass
+        try:
+            delattr(self, '_str_val')
+        except AttributeError:
+            pass
+        self.relpath = {self : '.'}
 
     def get_dir(self):
         return self.dir
 
     def get_suffix(self):
-        "__cacheable__"
         return SCons.Util.splitext(self.name)[1]
 
     def rfile(self):
@@ -530,61 +491,33 @@ class Base(SCons.Node.Node):
     def __str__(self):
         """A Node.FS.Base object's string representation is its path
         name."""
-        global Save_Strings
-        if Save_Strings:
-            return self._save_str()
-        return self._get_str()
-
-    def _save_str(self):
-        "__cacheable__"
-        return self._get_str()
-
-    def _get_str(self):
-        if self.duplicate or self.is_derived():
-            return self.get_path()
-        return self.srcnode().get_path()
+        try:
+            return self._str_val
+        except AttributeError:
+            global Save_Strings
+            if self.duplicate or self.is_derived():
+                str_val = self.get_path()
+            else:
+                str_val = self.srcnode().get_path()
+            if Save_Strings:
+                self._str_val = str_val
+            return str_val
 
     rstr = __str__
 
-    def stat(self):
-        "__cacheable__"
-        try: return self.fs.stat(self.abspath)
-        except os.error: return None
-
     def exists(self):
-        "__cacheable__"
-        return not self.stat() is None
+        try:
+            return self._exists
+        except AttributeError:
+            self._exists = self.fs.exists(self.abspath)
+            return self._exists
 
     def rexists(self):
-        "__cacheable__"
-        return self.rfile().exists()
-
-    def getmtime(self):
-        st = self.stat()
-        if st: return st[stat.ST_MTIME]
-        else: return None
-
-    def getsize(self):
-        st = self.stat()
-        if st: return st[stat.ST_SIZE]
-        else: return None
-
-    def isdir(self):
-        st = self.stat()
-        return not st is None and stat.S_ISDIR(st[stat.ST_MODE])
-
-    def isfile(self):
-        st = self.stat()
-        return not st is None and stat.S_ISREG(st[stat.ST_MODE])
-
-    if hasattr(os, 'symlink'):
-        def islink(self):
-            try: st = self.fs.lstat(self.abspath)
-            except os.error: return 0
-            return stat.S_ISLNK(st[stat.ST_MODE])
-    else:
-        def islink(self):
-            return 0                    # no symlinks
+        try:
+            return self._rexists
+        except AttributeError:
+            self._rexists = self.rfile().exists()
+            return self._rexists
 
     def is_under(self, dir):
         if self is dir:
@@ -598,33 +531,40 @@ class Base(SCons.Node.Node):
     def srcnode(self):
         """If this node is in a build path, return the node
         corresponding to its source file.  Otherwise, return
-        ourself.
-        __cacheable__"""
-        dir=self.dir
-        name=self.name
-        while dir:
-            if dir.srcdir:
-                srcnode = self.fs.Entry(name, dir.srcdir,
-                                        klass=self.__class__)
-                return srcnode
-            name = dir.name + os.sep + name
-            dir = dir.up()
-        return self
+        ourself."""
+        try:
+            return self._srcnode
+        except AttributeError:
+            dir=self.dir
+            name=self.name
+            while dir:
+                if dir.srcdir:
+                    self._srcnode = self.fs.Entry(name, dir.srcdir,
+                                                  klass=self.__class__)
+                    return self._srcnode
+                name = dir.name + os.sep + name
+                dir=dir.get_dir()
+            self._srcnode = self
+            return self._srcnode
 
     def get_path(self, dir=None):
         """Return path relative to the current working directory of the
         Node.FS.Base object that owns us."""
         if not dir:
             dir = self.fs.getcwd()
-        if self == dir:
-            return '.'
-        path_elems = self.path_elements
-        try: i = path_elems.index(dir)
-        except ValueError: pass
-        else: path_elems = path_elems[i+1:]
-        path_elems = map(lambda n: n.name, path_elems)
-        return string.join(path_elems, os.sep)
-
+        try:
+            return self.relpath[dir]
+        except KeyError:
+            path_elems = []
+            d = self
+            while d != dir and not isinstance(d, ParentOfRoot):
+                path_elems.append(d.name)
+                d = d.dir
+            path_elems.reverse()
+            ret = string.join(path_elems, os.sep)
+            self.relpath[dir] = ret
+            return ret
+            
     def set_src_builder(self, builder):
         """Set the source code builder for this node."""
         self.sbuilder = builder
@@ -671,19 +611,6 @@ class Entry(Base):
     time comes, and then call the same-named method in the transformed
     class."""
 
-    def diskcheck_match(self):
-        pass
-
-    def disambiguate(self):
-        if self.isdir():
-            self.__class__ = Dir
-            self._morph()
-        else:
-            self.__class__ = File
-            self._morph()
-            self.clear()
-        return self
-
     def rfile(self):
         """We're a generic Entry, but the caller is actually looking for
         a File at this point, so morph into one."""
@@ -692,10 +619,11 @@ class Entry(Base):
         self.clear()
         return File.rfile(self)
 
-    def get_found_includes(self, env, scanner, path):
+    def get_found_includes(self, env, scanner, target):
         """If we're looking for included files, it's because this Entry
         is really supposed to be a File itself."""
-        return self.disambiguate().get_found_includes(env, scanner, path)
+        node = self.rfile()
+        return node.get_found_includes(env, scanner, target)
 
     def scanner_key(self):
         return self.get_suffix()
@@ -706,39 +634,51 @@ 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():
+        if self.fs.isfile(self.abspath):
             self.__class__ = File
             self._morph()
-            return self.get_contents()
-        if self.isdir():
+            return File.get_contents(self)
+        if self.fs.isdir(self.abspath):
             self.__class__ = Dir
             self._morph()
-            return self.get_contents()
-        if self.islink():
+            return Dir.get_contents(self)
+        if self.fs.islink(self.abspath):
             return ''             # avoid errors for dangling symlinks
         raise AttributeError
 
-    def rel_path(self, other):
-        return self.disambiguate().rel_path(other)
-
     def exists(self):
         """Return if the Entry exists.  Check the file system to see
         what we should turn into first.  Assume a file if there's no
         directory."""
-        return self.disambiguate().exists()
+        if self.fs.isdir(self.abspath):
+            self.__class__ = Dir
+            self._morph()
+            return Dir.exists(self)
+        else:
+            self.__class__ = File
+            self._morph()
+            self.clear()
+            return File.exists(self)
 
     def calc_signature(self, calc=None):
         """Return the Entry's calculated signature.  Check the file
         system to see what we should turn into first.  Assume a file if
         there's no directory."""
-        return self.disambiguate().calc_signature(calc)
+        if self.fs.isdir(self.abspath):
+            self.__class__ = Dir
+            self._morph()
+            return Dir.calc_signature(self, calc)
+        else:
+            self.__class__ = File
+            self._morph()
+            self.clear()
+            return File.calc_signature(self, calc)
 
     def must_be_a_Dir(self):
         """Called to make sure a Node is a Dir.  Since we're an
         Entry, we can morph into one."""
         self.__class__ = Dir
         self._morph()
-        return self
 
 # This is for later so we can differentiate between Entry the class and Entry
 # the method of the FS class.
@@ -746,10 +686,6 @@ _classEntry = Entry
 
 
 class LocalFS:
-
-    if SCons.Memoize.use_memoizer:
-        __metaclass__ = SCons.Memoize.Memoized_Metaclass
-    
     # This class implements an abstraction layer for operations involving
     # a local file system.  Essentially, this wraps any function in
     # the os, os.path or shutil modules that we use to actually go do
@@ -774,16 +710,12 @@ class LocalFS:
         return os.path.exists(path)
     def getmtime(self, path):
         return os.path.getmtime(path)
-    def getsize(self, path):
-        return os.path.getsize(path)
     def isdir(self, path):
         return os.path.isdir(path)
     def isfile(self, path):
         return os.path.isfile(path)
     def link(self, src, dst):
         return os.link(src, dst)
-    def lstat(self, path):
-        return os.lstat(path)
     def listdir(self, path):
         return os.listdir(path)
     def makedirs(self, path):
@@ -804,17 +736,12 @@ class LocalFS:
     if hasattr(os, 'symlink'):
         def islink(self, path):
             return os.path.islink(path)
+        def exists_or_islink(self, path):
+            return os.path.exists(path) or os.path.islink(path)
     else:
         def islink(self, path):
             return 0                    # no symlinks
-
-if SCons.Memoize.use_old_memoization():
-    _FSBase = LocalFS
-    class LocalFS(SCons.Memoize.Memoizer, _FSBase):
-        def __init__(self, *args, **kw):
-            apply(_FSBase.__init__, (self,)+args, kw)
-            SCons.Memoize.Memoizer.__init__(self)
-
+        exists_or_islink = exists
 
 #class RemoteFS:
 #    # Skeleton for the obvious methods we might need from the
@@ -826,7 +753,6 @@ if SCons.Memoize.use_old_memoization():
 
 
 class FS(LocalFS):
-
     def __init__(self, path = None):
         """Initialize the Node.FS subsystem.
 
@@ -836,38 +762,33 @@ class FS(LocalFS):
 
         The path argument must be a valid absolute path.
         """
-        if __debug__: logInstanceCreation(self, 'Node.FS')
+        if __debug__: logInstanceCreation(self)
+        self.Top = None
+        if path == None:
+            self.pathTop = os.getcwd()
+        else:
+            self.pathTop = path
         self.Root = {}
         self.SConstruct_dir = None
         self.CachePath = None
         self.cache_force = None
         self.cache_show = None
-        self.max_drift = default_max_drift
 
-        if path is None:
-            self.pathTop = os.getcwd()
-        else:
-            self.pathTop = path
+    def set_toplevel_dir(self, path):
+        assert not self.Top, "You can only set the top-level path on an FS object that has not had its File, Dir, or Entry methods called yet."
+        self.pathTop = path
 
-        self.Top = self._doLookup(Dir, os.path.normpath(self.pathTop))
-        self.Top.path = '.'
-        self.Top.tpath = '.'
-        self._cwd = self.Top
-
-    def clear_cache(self):
-        "__cache_reset__"
-        pass
-    
     def set_SConstruct_dir(self, dir):
         self.SConstruct_dir = dir
-
-    def get_max_drift(self):
-        return self.max_drift
-
-    def set_max_drift(self, max_drift):
-        self.max_drift = max_drift
-
+        
+    def __setTopLevelDir(self):
+        if not self.Top:
+            self.Top = self.__doLookup(Dir, os.path.normpath(self.pathTop))
+            self.Top.path = '.'
+            self._cwd = self.Top
+        
     def getcwd(self):
+        self.__setTopLevelDir()
         return self._cwd
 
     def __checkClass(self, node, klass):
@@ -880,20 +801,18 @@ class FS(LocalFS):
         raise TypeError, "Tried to lookup %s '%s' as a %s." % \
               (node.__class__.__name__, node.path, klass.__name__)
         
-    def _doLookup(self, fsclass, name, directory = None, create = 1):
+    def __doLookup(self, fsclass, name, directory = None, create = 1):
         """This method differs from the File and Dir factory methods in
         one important way: the meaning of the directory parameter.
         In this method, if directory is None or not supplied, the supplied
         name is expected to be an absolute path.  If you try to look up a
         relative path with directory=None, then an AssertionError will be
-        raised.
-        __cacheable__"""
+        raised."""
 
         if not name:
-            # This is a stupid hack to compensate for the fact that
-            # the POSIX and Win32 versions of os.path.normpath() behave
-            # differently in older versions of Python.  In particular,
-            # in POSIX:
+            # This is a stupid hack to compensate for the fact
+            # that the POSIX and Win32 versions of os.path.normpath()
+            # behave differently.  In particular, in POSIX:
             #   os.path.normpath('./') == '.'
             # in Win32
             #   os.path.normpath('./') == ''
@@ -902,81 +821,76 @@ class FS(LocalFS):
             # This is a definite bug in the Python library, but we have
             # to live with it.
             name = '.'
-        path_orig = string.split(name, os.sep)
-        path_norm = string.split(_my_normcase(name), os.sep)
-
-        first_orig = path_orig.pop(0)   # strip first element
-        first_norm = path_norm.pop(0)   # strip first element
-
-        drive, path_first = os.path.splitdrive(first_orig)
-        if path_first:
-            path_orig = [ path_first, ] + path_orig
-            path_norm = [ _my_normcase(path_first), ] + path_norm
-        else:
-            drive = _my_normcase(drive)
+        path_comp = string.split(name, os.sep)
+        drive, path_first = os.path.splitdrive(path_comp[0])
+        if not path_first:
             # Absolute path
+            drive = _my_normcase(drive)
             try:
                 directory = self.Root[drive]
             except KeyError:
                 if not create:
                     raise SCons.Errors.UserError
-                directory = RootDir(drive, self)
+                directory = RootDir(drive, ParentOfRoot(), self)
                 self.Root[drive] = directory
+            path_comp = path_comp[1:]
+        else:
+            path_comp = [ path_first, ] + path_comp[1:]
 
-        if not path_orig:
-            return directory
-
-        last_orig = path_orig.pop()     # strip last element
-        last_norm = path_norm.pop()     # strip last element
+        if not path_comp:
+            path_comp = ['']
             
         # Lookup the directory
-        for orig, norm in map(None, path_orig, path_norm):
-            try:
-                entries = directory.entries
-            except AttributeError:
-                # We tried to look up the entry in either an Entry or
-                # a File.  Give whatever it is a chance to do what's
-                # appropriate: morph into a Dir or raise an exception.
-                directory.must_be_a_Dir()
-                entries = directory.entries
+        for path_name in path_comp[:-1]:
+            path_norm = _my_normcase(path_name)
             try:
-                directory = entries[norm]
+                d = directory.entries[path_norm]
             except KeyError:
                 if not create:
                     raise SCons.Errors.UserError
 
-                d = Dir(orig, directory, self)
-
-                # Check the file system (or not, as configured) to make
-                # sure there isn't already a file there.
-                d.diskcheck_match()
-
-                directory.entries[norm] = d
-                directory.add_wkid(d)
+                # look at the actual filesystem and make sure there isn't
+                # a file already there
+                path = directory.entry_path(path_name)
+                if self.isfile(path):
+                    raise TypeError, \
+                          "File %s found where directory expected." % path
+
+                dir_temp = Dir(path_name, directory, self)
+                directory.entries[path_norm] = dir_temp
+                directory.add_wkid(dir_temp)
+                directory = dir_temp
+            else:
+                d.must_be_a_Dir()
                 directory = d
 
-        directory.must_be_a_Dir()
-
+        entry_norm = _my_normcase(path_comp[-1])
         try:
-            e = directory.entries[last_norm]
+            e = directory.entries[entry_norm]
         except KeyError:
             if not create:
                 raise SCons.Errors.UserError
 
-            result = fsclass(last_orig, directory, self)
-
-            # Check the file system (or not, as configured) to make
-            # sure there isn't already a directory at the path on
-            # disk where we just created a File node, and vice versa.
-            result.diskcheck_match()
-
-            directory.entries[last_norm] = result 
+            # make sure we don't create File nodes when there is actually
+            # a directory at that path on the disk, and vice versa
+            path = directory.entry_path(path_comp[-1])
+            if fsclass == File:
+                if self.isdir(path):
+                    raise TypeError, \
+                          "Directory %s found where file expected." % path
+            elif fsclass == Dir:
+                if self.isfile(path):
+                    raise TypeError, \
+                          "File %s found where directory expected." % path
+            
+            result = fsclass(path_comp[-1], directory, self)
+            directory.entries[entry_norm] = result 
             directory.add_wkid(result)
         else:
             result = self.__checkClass(e, fsclass)
         return result 
 
-    def _transformPath(self, name, directory):
+    def __transformPath(self, name, directory):
         """Take care of setting up the correct top-level directory,
         usually in preparation for a call to doLookup().
 
@@ -986,6 +900,7 @@ class FS(LocalFS):
         If directory is None, and name is a relative path,
         then the same applies.
         """
+        self.__setTopLevelDir()
         if name and name[0] == '#':
             directory = self.Top
             name = name[1:]
@@ -1003,6 +918,7 @@ class FS(LocalFS):
         If change_os_dir is true, we will also change the "real" cwd
         to match.
         """
+        self.__setTopLevelDir()
         curr=self._cwd
         try:
             if not dir is None:
@@ -1029,8 +945,8 @@ class FS(LocalFS):
         else:
             if directory and not isinstance(directory, Dir):
                 directory = self.Dir(directory)
-            name, directory = self._transformPath(name, directory)
-            return self._doLookup(klass, name, directory, create)
+            name, directory = self.__transformPath(name, directory)
+            return self.__doLookup(klass, name, directory, create)
     
     def File(self, name, directory = None, create = 1):
         """Lookup or create a File node with the specified name.  If
@@ -1062,10 +978,13 @@ class FS(LocalFS):
         """Link the supplied build directory to the source directory
         for purposes of building files."""
         
+        self.__setTopLevelDir()
         if not isinstance(src_dir, SCons.Node.Node):
             src_dir = self.Dir(src_dir)
         if not isinstance(build_dir, SCons.Node.Node):
             build_dir = self.Dir(build_dir)
+        if not src_dir.is_under(self.Top):
+            raise SCons.Errors.UserError, "Source directory must be under top of build tree."
         if src_dir.is_under(build_dir):
             raise SCons.Errors.UserError, "Source directory cannot be under build directory."
         if build_dir.srcdir:
@@ -1079,51 +998,134 @@ class FS(LocalFS):
         for d in dirs:
             if not isinstance(d, SCons.Node.Node):
                 d = self.Dir(d)
+            self.__setTopLevelDir()
             self.Top.addRepository(d)
 
-    def Rfindalldirs(self, pathlist, cwd):
-        """__cacheable__"""
+    def Rsearch(self, path, clazz=_classEntry, cwd=None):
+        """Search for something in a Repository.  Returns the first
+        one found in the list, or None if there isn't one."""
+        if isinstance(path, SCons.Node.Node):
+            return path
+        else:
+            name, d = self.__transformPath(path, cwd)
+            n = self.__doLookup(clazz, name, d)
+            if n.exists():
+                return n
+            if isinstance(n, Dir):
+                # If n is a Directory that has Repositories directly
+                # attached to it, then any of those is a valid Repository
+                # path.  Return the first one that exists.
+                reps = filter(lambda x: x.exists(), n.getRepositories())
+                if len(reps):
+                    return reps[0]
+            d = n.get_dir()
+            name = n.name
+            # Search repositories of all directories that this file is under.
+            while d:
+                for rep in d.getRepositories():
+                    try:
+                        rnode = self.__doLookup(clazz, name, rep)
+                        # Only find the node if it exists and it is not
+                       # a derived file.  If for some reason, we are
+                       # explicitly building a file IN a Repository, we
+                       # don't want it to show up in the build tree.
+                       # This is usually the case with BuildDir().
+                       # We only want to find pre-existing files.
+                        if rnode.exists() and \
+                           (isinstance(rnode, Dir) or not rnode.is_derived()):
+                            return rnode
+                    except TypeError:
+                        pass # Wrong type of node.
+                # Prepend directory name
+                name = d.name + os.sep + name
+                # Go up one directory
+                d = d.get_dir()
+        return None
+
+    def Rsearchall(self, pathlist, must_exist=1, clazz=_classEntry, cwd=None):
+        """Search for a list of somethings in the Repository list."""
+        ret = []
         if SCons.Util.is_String(pathlist):
             pathlist = string.split(pathlist, os.pathsep)
         if not SCons.Util.is_List(pathlist):
             pathlist = [pathlist]
-        result = []
         for path in filter(None, pathlist):
             if isinstance(path, SCons.Node.Node):
-                result.append(path)
-                continue
-            path, dir = self._transformPath(path, cwd)
-            dir = dir.Dir(path)
-            result.extend(dir.get_all_rdirs())
-        return result
+                ret.append(path)
+            else:
+                name, d = self.__transformPath(path, cwd)
+                n = self.__doLookup(clazz, name, d)
+                if not must_exist or n.exists():
+                    ret.append(n)
+                if isinstance(n, Dir):
+                    # If this node is a directory, then any repositories
+                    # attached to this node can be repository paths.
+                    ret.extend(filter(lambda x, me=must_exist, clazz=clazz: isinstance(x, clazz) and (not me or x.exists()),
+                                      n.getRepositories()))
+                    
+                d = n.get_dir()
+                name = n.name
+                # Search repositories of all directories that this file
+                # is under.
+                while d:
+                    for rep in d.getRepositories():
+                        try:
+                            rnode = self.__doLookup(clazz, name, rep)
+                            # Only find the node if it exists (or
+                            # must_exist is zero) and it is not a
+                            # derived file.  If for some reason, we
+                            # are explicitly building a file IN a
+                            # Repository, we don't want it to show up in
+                            # the build tree.  This is usually the case
+                            # with BuildDir().  We only want to find
+                            # pre-existing files.
+                            if (not must_exist or rnode.exists()) and \
+                               (not rnode.is_derived() or isinstance(rnode, Dir)):
+                                ret.append(rnode)
+                        except TypeError:
+                            pass # Wrong type of node.
+                    # Prepend directory name
+                    name = d.name + os.sep + name
+                    # Go up one directory
+                    d = d.get_dir()
+        return ret
 
     def CacheDir(self, path):
         self.CachePath = path
 
-    def build_dir_target_climb(self, orig, dir, tail):
+    def build_dir_target_climb(self, dir, tail):
         """Create targets in corresponding build directories
 
         Climb the directory tree, and look up path names
         relative to any linked build directories we find.
-        __cacheable__
         """
         targets = []
         message = None
-        fmt = "building associated BuildDir targets: %s"
-        start_dir = dir
         while dir:
             for bd in dir.build_dirs:
-                if start_dir.is_under(bd):
-                    # If already in the build-dir location, don't reflect
-                    return [orig], fmt % str(orig)
                 p = apply(os.path.join, [bd.path] + tail)
                 targets.append(self.Entry(p))
             tail = [dir.name] + tail
             dir = dir.up()
         if targets:
-            message = fmt % string.join(map(str, targets))
+            message = "building associated BuildDir targets: %s" % string.join(map(str, targets))
         return targets, message
 
+class DummyExecutor:
+    """Dummy executor class returned by Dir nodes to bamboozle SCons
+    into thinking we are an actual derived node, where our sources are
+    our directory entries."""
+    def cleanup(self):
+        pass
+    def get_raw_contents(self):
+        return ''
+    def get_contents(self):
+        return ''
+    def get_timestamp(self):
+        return 0
+    def get_build_env(self):
+        return None
+
 class Dir(Base):
     """A class for directories in a file system.
     """
@@ -1139,8 +1141,7 @@ class Dir(Base):
 
         Set up this directory's entries and hook it into the file
         system tree.  Specify that directories (this Node) don't use
-        signatures for calculating whether they're current.
-        __cache_reset__"""
+        signatures for calculating whether they're current."""
 
         self.repositories = []
         self.srcdir = None
@@ -1149,23 +1150,11 @@ class Dir(Base):
         self.entries['.'] = self
         self.entries['..'] = self.dir
         self.cwd = self
+        self.builder = get_MkdirBuilder()
         self.searched = 0
         self._sconsign = None
         self.build_dirs = []
 
-        # Don't just reset the executor, replace its action list,
-        # because it might have some pre-or post-actions that need to
-        # be preserved.
-        self.builder = get_MkdirBuilder()
-        self.get_executor().set_action_list(self.builder.action)
-
-    def diskcheck_match(self):
-        diskcheck_match(self, self.fs.isfile,
-                           "File %s found where directory expected.")
-
-    def disambiguate(self):
-        return self
-
     def __clearRepositoryCache(self, duplicate=None):
         """Called when we change the repository(ies) for a directory.
         This clears any cached information that is invalidated by changing
@@ -1176,11 +1165,30 @@ class Dir(Base):
                 if node != self and isinstance(node, Dir):
                     node.__clearRepositoryCache(duplicate)
                 else:
-                    node.clear()
                     try:
                         del node._srcreps
                     except AttributeError:
                         pass
+                    try:
+                        del node._rfile
+                    except AttributeError:
+                        pass
+                    try:
+                        del node._rexists
+                    except AttributeError:
+                        pass
+                    try:
+                        del node._exists
+                    except AttributeError:
+                        pass
+                    try:
+                        del node._srcnode
+                    except AttributeError:
+                        pass
+                    try:
+                        del node._str_val
+                    except AttributeError:
+                        pass
                     if duplicate != None:
                         node.duplicate=duplicate
     
@@ -1209,56 +1217,32 @@ class Dir(Base):
         srcdir.build_dirs.append(self)
 
     def getRepositories(self):
-        """Returns a list of repositories for this directory.
-        __cacheable__"""
+        """Returns a list of repositories for this directory."""
         if self.srcdir and not self.duplicate:
-            return self.srcdir.get_all_rdirs() + self.repositories
+            try:
+                return self._srcreps
+            except AttributeError:
+                self._srcreps = self.fs.Rsearchall(self.srcdir.path,
+                                                   clazz=Dir,
+                                                   must_exist=0,
+                                                   cwd=self.fs.Top) \
+                                + self.repositories
+                return self._srcreps
         return self.repositories
 
-    def get_all_rdirs(self):
-        """__cacheable__"""
-        result = [self]
-        fname = '.'
-        dir = self
-        while dir:
-            for rep in dir.getRepositories():
-                result.append(rep.Dir(fname))
-            fname = dir.name + os.sep + fname
-            dir = dir.up()
-        return result
-
     def addRepository(self, dir):
-        if dir != self and not dir in self.repositories:
+        if not dir in self.repositories and dir != self:
             self.repositories.append(dir)
-            dir.tpath = '.'
             self.__clearRepositoryCache()
 
     def up(self):
         return self.entries['..']
 
-    def rel_path(self, other):
-        """Return a path to "other" relative to this directory.
-        __cacheable__"""
-        if isinstance(other, Dir):
-            name = []
+    def root(self):
+        if not self.entries['..']:
+            return self
         else:
-            try:
-                name = [other.name]
-                other = other.dir
-            except AttributeError:
-                return str(other)
-        if self is other:
-            return name and name[0] or '.'
-        i = 0
-        for x, y in map(None, self.path_elements, other.path_elements):
-            if not x is y:
-                break
-            i = i + 1
-        path_elems = ['..']*(len(self.path_elements)-i) \
-                   + map(lambda n: n.name, other.path_elements[i:]) \
-                   + name
-             
-        return string.join(path_elems, os.sep)
+            return self.entries['..'].root()
 
     def scan(self):
         if not self.implicit is None:
@@ -1266,33 +1250,19 @@ class Dir(Base):
         self.implicit = []
         self.implicit_dict = {}
         self._children_reset()
-
-        dont_scan = lambda k: k not in ['.', '..', '.sconsign']
-        deps = filter(dont_scan, self.entries.keys())
-        # keys() is going to give back the entries in an internal,
-        # unsorted order.  Sort 'em so the order is deterministic.
-        deps.sort()
-        entries = map(lambda n, e=self.entries: e[n], deps)
-
-        self._add_child(self.implicit, self.implicit_dict, entries)
-
-    def get_found_includes(self, env, scanner, path):
-        """Return the included implicit dependencies in this file.
-        Cache results so we only scan the file once per path
-        regardless of how many times this information is requested.
-        __cacheable__"""
-        if not scanner:
-            return []
-        # Clear cached info for this Node.  If we already visited this
-        # directory on our walk down the tree (because we didn't know at
-        # that point it was being used as the source for another Node)
-        # then we may have calculated build signature before realizing
-        # we had to scan the disk.  Now that we have to, though, we need
-        # to invalidate the old calculated signature so that any node
-        # dependent on our directory structure gets one that includes
-        # info about everything on disk.
-        self.clear()
-        return scanner(self, env, path)
+        try:
+            for filename in self.fs.listdir(self.abspath):
+                if filename != '.sconsign':
+                    self.Entry(filename)
+        except OSError:
+            # Directory does not exist.  No big deal
+            pass
+        keys = filter(lambda k: k != '.' and k != '..', self.entries.keys())
+        kids = map(lambda x, s=self: s.entries[x], keys)
+        def c(one, two):
+            return cmp(one.abspath, two.abspath)
+        kids.sort(c)
+        self._add_child(self.implicit, self.implicit_dict, kids)
 
     def build(self, **kw):
         """A null "builder" for directories."""
@@ -1300,36 +1270,6 @@ class Dir(Base):
         if not self.builder is MkdirBuilder:
             apply(SCons.Node.Node.build, [self,], kw)
 
-    def _create(self):
-        """Create this directory, silently and without worrying about
-        whether the builder is the default or not."""
-        listDirs = []
-        parent = self
-        while parent:
-            if parent.exists():
-                break
-            listDirs.append(parent)
-            p = parent.up()
-            if p is None:
-                raise SCons.Errors.StopError, parent.path
-            parent = p
-        listDirs.reverse()
-        for dirnode in listDirs:
-            try:
-                # Don't call dirnode.build(), call the base Node method
-                # directly because we definitely *must* create this
-                # directory.  The dirnode.build() method will suppress
-                # the build if it's the default builder.
-                SCons.Node.Node.build(dirnode)
-                dirnode.get_executor().nullify()
-                # The build() action may or may not have actually
-                # created the directory, depending on whether the -n
-                # option was used or not.  Delete the _exists and
-                # _rexists attributes so they can be reevaluated.
-                dirnode.clear()
-            except OSError:
-                pass
-
     def multiple_side_effect_has_builder(self):
         global MkdirBuilder
         return not self.builder is MkdirBuilder and self.has_builder()
@@ -1337,7 +1277,7 @@ class Dir(Base):
     def alter_targets(self):
         """Return any corresponding targets in a build directory.
         """
-        return self.fs.build_dir_target_climb(self, self, [])
+        return self.fs.build_dir_target_climb(self, [])
 
     def scanner_key(self):
         """A directory does not get scanned."""
@@ -1349,13 +1289,10 @@ class Dir(Base):
         for kid in self.children():
             contents.write(kid.get_contents())
         return contents.getvalue()
-
+    
     def prepare(self):
         pass
 
-    def do_duplicate(self, src):
-        pass
-
     def current(self, calc=None):
         """If all of our children were up-to-date, then this
         directory was up-to-date, too."""
@@ -1373,16 +1310,15 @@ class Dir(Base):
             return 0
 
     def rdir(self):
-        "__cacheable__"
-        if not self.exists():
-            norm_name = _my_normcase(self.name)
-            for dir in self.dir.get_all_rdirs():
-                try: node = dir.entries[norm_name]
-                except KeyError: node = dir.dir_on_disk(self.name)
-                if node and node.exists() and \
-                    (isinstance(dir, Dir) or isinstance(dir, Entry)):
-                        return node
-        return self
+        try:
+            return self._rdir
+        except AttributeError:
+            self._rdir = self
+            if not self.exists():
+                n = self.fs.Rsearch(self.path, clazz=Dir, cwd=self.fs.Top)
+                if n:
+                    self._rdir = n
+            return self._rdir
 
     def sconsign(self):
         """Return the .sconsign file info for this directory,
@@ -1413,102 +1349,10 @@ class Dir(Base):
     def entry_path(self, name):
         return self.path + os.sep + name
 
-    def entry_tpath(self, name):
-        return self.tpath + os.sep + name
-
     def must_be_a_Dir(self):
         """Called to make sure a Node is a Dir.  Since we're already
         one, this is a no-op for us."""
-        return self
-
-    def entry_exists_on_disk(self, name):
-        """__cacheable__"""
-        try:
-            d = self.on_disk_entries
-        except AttributeError:
-            d = {}
-            try:
-                entries = os.listdir(self.abspath)
-            except OSError:
-                pass
-            else:
-                for entry in map(_my_normcase, entries):
-                    d[entry] = 1
-            self.on_disk_entries = d
-        return d.has_key(_my_normcase(name))
-
-    def srcdir_list(self):
-        """__cacheable__"""
-        result = []
-
-        dirname = '.'
-        dir = self
-        while dir:
-            if dir.srcdir:
-                d = dir.srcdir.Dir(dirname)
-                if d.is_under(dir):
-                    # Shouldn't source from something in the build path:
-                    # build_dir is probably under src_dir, in which case
-                    # we are reflecting.
-                    break
-                result.append(d)
-            dirname = dir.name + os.sep + dirname
-            dir = dir.up()
-
-        return result
-
-    def srcdir_duplicate(self, name):
-        for dir in self.srcdir_list():
-            if dir.entry_exists_on_disk(name):
-                srcnode = dir.File(name)
-                if self.duplicate:
-                    node = self.File(name)
-                    node.do_duplicate(srcnode)
-                    return node
-                else:
-                    return srcnode
-        return None
-
-    def srcdir_find_file(self, filename):
-        """__cacheable__"""
-        def func(node):
-            if (isinstance(node, File) or isinstance(node, Entry)) and \
-               (node.is_derived() or node.is_pseudo_derived() or node.exists()):
-                    return node
-            return None
-
-        norm_name = _my_normcase(filename)
-
-        for rdir in self.get_all_rdirs():
-            try: node = rdir.entries[norm_name]
-            except KeyError: node = rdir.file_on_disk(filename)
-            else: node = func(node)
-            if node:
-                return node, self
-
-        for srcdir in self.srcdir_list():
-            for rdir in srcdir.get_all_rdirs():
-                try: node = rdir.entries[norm_name]
-                except KeyError: node = rdir.file_on_disk(filename)
-                else: node = func(node)
-                if node:
-                    return File(filename, self, self.fs), srcdir
-
-        return None, None
-
-    def dir_on_disk(self, name):
-        if self.entry_exists_on_disk(name):
-            try: return self.Dir(name)
-            except TypeError: pass
-        return None
-
-    def file_on_disk(self, name):
-        if self.entry_exists_on_disk(name) or \
-           diskcheck_rcs(self, name) or \
-           diskcheck_sccs(self, name):
-            try: return self.File(name)
-            except TypeError: pass
-        return self.srcdir_duplicate(name)
+        pass
 
 class RootDir(Dir):
     """A class for the root directory of a file system.
@@ -1518,108 +1362,30 @@ class RootDir(Dir):
     add a separator when creating the path names of entries within
     this directory.
     """
-    def __init__(self, name, fs):
+    def __init__(self, name, directory, fs):
         if __debug__: logInstanceCreation(self, 'Node.FS.RootDir')
-        # We're going to be our own parent directory (".." entry and .dir
-        # attribute) so we have to set up some values so Base.__init__()
-        # won't gag won't it calls some of our methods.
-        self.abspath = ''
-        self.path = ''
-        self.tpath = ''
-        self.path_elements = []
-        self.duplicate = 0
-        Base.__init__(self, name, self, fs)
-
-        # Now set our paths to what we really want them to be: the
-        # initial drive letter (the name) plus the directory separator.
-        self.abspath = name + os.sep
-        self.path = name + os.sep
-        self.tpath = name + os.sep
+        Base.__init__(self, name, directory, fs)
+        self.path = self.path + os.sep
+        self.abspath = self.abspath + os.sep
         self._morph()
 
-    def __str__(self):
-        return self.abspath
-
     def entry_abspath(self, name):
         return self.abspath + name
 
     def entry_path(self, name):
         return self.path + name
 
-    def entry_tpath(self, name):
-        return self.tpath + name
-
-    def is_under(self, dir):
-        if self is dir:
-            return 1
-        else:
-            return 0
-
-    def up(self):
-        return None
-
-    def get_dir(self):
-        return None
-
-    def src_builder(self):
-        return _null
-
-class NodeInfo(SCons.Node.NodeInfo):
+class BuildInfo:
+    bsig = None
     def __cmp__(self, other):
-        try: return cmp(self.bsig, other.bsig)
-        except AttributeError: return 1
-    def update(self, node):
-        self.timestamp = node.get_timestamp()
-        self.size = node.getsize()
-
-class BuildInfo(SCons.Node.BuildInfo):
-    def __init__(self, node):
-        SCons.Node.BuildInfo.__init__(self, node)
-        self.node = node
-    def convert_to_sconsign(self):
-        """Convert this BuildInfo object for writing to a .sconsign file
-
-        We hung onto the node that we refer to so that we can translate
-        the lists of bsources, bdepends and bimplicit Nodes into strings
-        relative to the node, but we don't want to write out that Node
-        itself to the .sconsign file, so we delete the attribute in
-        preparation.
-        """
-        rel_path = self.node.rel_path
-        delattr(self, 'node')
-        for attr in ['bsources', 'bdepends', 'bimplicit']:
-            try:
-                val = getattr(self, attr)
-            except AttributeError:
-                pass
-            else:
-                setattr(self, attr, map(rel_path, val))
-    def convert_from_sconsign(self, dir, name):
-        """Convert a newly-read BuildInfo object for in-SCons use
-
-        An on-disk BuildInfo comes without a reference to the node
-        for which it's intended, so we have to convert the arguments
-        and add back a self.node attribute.  The bsources, bdepends and
-        bimplicit lists all come from disk as paths relative to that node,
-        so convert them to actual Nodes for use by the rest of SCons.
-        """
-        self.node = dir.Entry(name)
-        Entry_func = self.node.dir.Entry
-        for attr in ['bsources', 'bdepends', 'bimplicit']:
-            try:
-                val = getattr(self, attr)
-            except AttributeError:
-                pass
-            else:
-                setattr(self, attr, map(Entry_func, val))
+        try:
+            return cmp(self.bsig, other.bsig)
+        except AttributeError:
+            return 1
 
 class File(Base):
     """A class for files in a file system.
     """
-    def diskcheck_match(self):
-        diskcheck_match(self, self.fs.isdir,
-                           "Directory %s found where file expected.")
-
     def __init__(self, name, directory, fs):
         if __debug__: logInstanceCreation(self, 'Node.FS.File')
         Base.__init__(self, name, directory, fs)
@@ -1635,11 +1401,6 @@ class File(Base):
         the SConscript directory of this file."""
         return self.fs.Dir(name, self.cwd)
 
-    def Dirs(self, pathlist):
-        """Create a list of directories relative to the SConscript
-        directory of this file."""
-        return map(lambda p, s=self: s.Dir(p), pathlist)
-
     def File(self, name):
         """Create a file node named 'name' relative to
         the SConscript directory of this file."""
@@ -1647,16 +1408,25 @@ class File(Base):
 
     def RDirs(self, pathlist):
         """Search for a list of directories in the Repository list."""
-        return self.fs.Rfindalldirs(pathlist, self.cwd)
+        return self.fs.Rsearchall(pathlist, clazz=Dir, must_exist=0,
+                                  cwd=self.cwd)
+
+    def generate_build_dict(self):
+        """Return an appropriate dictionary of values for building
+        this File."""
+        return {'Dir' : self.Dir,
+                'File' : self.File,
+                'RDirs' : self.RDirs}
 
     def _morph(self):
-        """Turn a file system node into a File object.  __cache_reset__"""
+        """Turn a file system node into a File object."""
         self.scanner_paths = {}
+        self.found_includes = {}
         if not hasattr(self, '_local'):
             self._local = 0
 
-    def disambiguate(self):
-        return self
+    def root(self):
+        return self.dir.root()
 
     def scanner_key(self):
         return self.get_suffix()
@@ -1668,7 +1438,7 @@ class File(Base):
 
     def get_timestamp(self):
         if self.rexists():
-            return self.rfile().getmtime()
+            return self.fs.getmtime(self.rfile().abspath)
         else:
             return 0
 
@@ -1678,49 +1448,101 @@ class File(Base):
         # in one build (SConstruct file) is a source in a different build.
         # See test/chained-build.py for the use case.
         entry = self.get_stored_info()
-        entry.merge(obj)
+        for key, val in obj.__dict__.items():
+            entry.__dict__[key] = val
         self.dir.sconsign().set_entry(self.name, entry)
 
     def get_stored_info(self):
-        "__cacheable__"
         try:
             stored = self.dir.sconsign().get_entry(self.name)
-        except (KeyError, OSError):
-            return self.new_binfo()
-        else:
-            if not hasattr(stored, 'ninfo'):
-                # Transition:  The .sconsign file entry has no NodeInfo
-                # object, which means it's a slightly older BuildInfo.
-                # Copy over the relevant attributes.
-                ninfo = stored.ninfo = self.new_ninfo()
-                for attr in ninfo.__dict__.keys():
-                    try:
-                        setattr(ninfo, attr, getattr(stored, attr))
-                    except AttributeError:
-                        pass
-            return stored
+            if isinstance(stored, BuildInfo):
+                return stored
+            # The stored build information isn't a BuildInfo object.
+            # This probably means it's an old SConsignEntry from SCons
+            # 0.95 or before.  The relevant attribute names are the same,
+            # though, so just copy the attributes over to an object of
+            # the correct type.
+            binfo = BuildInfo()
+            for key, val in stored.__dict__.items():
+                setattr(binfo, key, val)
+            return binfo
+        except:
+            return BuildInfo()
 
     def get_stored_implicit(self):
         binfo = self.get_stored_info()
-        try: return binfo.bimplicit
-        except AttributeError: return None
-
-    def rel_path(self, other):
-        return self.dir.rel_path(other)
+        try:
+            return binfo.bimplicit
+        except AttributeError:
+            return None
 
-    def get_found_includes(self, env, scanner, path):
+    def get_found_includes(self, env, scanner, target):
         """Return the included implicit dependencies in this file.
-        Cache results so we only scan the file once per path
-        regardless of how many times this information is requested.
-        __cacheable__"""
+        Cache results so we only scan the file once regardless of
+        how many times this information is requested."""
         if not scanner:
             return []
-        return scanner(self, env, path)
+
+        try:
+            path = target.scanner_paths[scanner]
+        except AttributeError:
+            # The target had no scanner_paths attribute, which means
+            # it's an Alias or some other node that's not actually a
+            # file.  In that case, back off and use the path for this
+            # node itself.
+            try:
+                path = self.scanner_paths[scanner]
+            except KeyError:
+                path = scanner.path(env, self.cwd)
+                self.scanner_paths[scanner] = path
+        except KeyError:
+            path = scanner.path(env, target.cwd)
+            target.scanner_paths[scanner] = path
+
+        try:
+            includes = self.found_includes[path]
+        except KeyError:
+            includes = scanner(self, env, path)
+            self.found_includes[path] = includes
+
+        return includes
 
     def _createDir(self):
         # ensure that the directories for this node are
         # created.
-        self.dir._create()
+
+        listDirs = []
+        parent=self.dir
+        while parent:
+            if parent.exists():
+                break
+            listDirs.append(parent)
+            p = parent.up()
+            if isinstance(p, ParentOfRoot):
+                raise SCons.Errors.StopError, parent.path
+            parent = p
+        listDirs.reverse()
+        for dirnode in listDirs:
+            try:
+                # Don't call dirnode.build(), call the base Node method
+                # directly because we definitely *must* create this
+                # directory.  The dirnode.build() method will suppress
+                # the build if it's the default builder.
+                SCons.Node.Node.build(dirnode)
+                # The build() action may or may not have actually
+                # created the directory, depending on whether the -n
+                # option was used or not.  Delete the _exists and
+                # _rexists attributes so they can be reevaluated.
+                try:
+                    delattr(dirnode, '_exists')
+                except AttributeError:
+                    pass
+                try:
+                    delattr(dirnode, '_rexists')
+                except AttributeError:
+                    pass
+            except OSError:
+                pass
 
     def retrieve_from_cache(self):
         """Try to retrieve the node's content from a cache
@@ -1763,18 +1585,25 @@ class File(Base):
         return None
 
     def built(self):
-        """Called just after this node is successfully built.
-        __cache_reset__"""
+        """Called just after this node is sucessfully built."""
         # Push this file out to cache before the superclass Node.built()
         # method has a chance to clear the build signature, which it
         # will do if this file has a source scanner.
-        if self.fs.CachePath and self.exists():
+        if self.fs.CachePath and self.fs.exists(self.path):
             CachePush(self, [], None)
-        self.fs.clear_cache()
         SCons.Node.Node.built(self)
+        self.found_includes = {}
+        try:
+            delattr(self, '_exists')
+        except AttributeError:
+            pass
+        try:
+            delattr(self, '_rexists')
+        except AttributeError:
+            pass
 
     def visited(self):
-        if self.fs.CachePath and self.fs.cache_force and self.exists():
+        if self.fs.CachePath and self.fs.cache_force and self.fs.exists(self.path):
             CachePush(self, None, None)
 
     def has_src_builder(self):
@@ -1796,12 +1625,19 @@ class File(Base):
             else:
                 scb = self.dir.src_builder()
                 if scb is _null:
-                    if diskcheck_sccs(self.dir, self.name):
+                    scb = None
+                    dir = self.dir.path
+                    sccspath = os.path.join('SCCS', 's.' + self.name)
+                    if dir != '.':
+                        sccspath = os.path.join(dir, sccspath)
+                    if self.fs.exists(sccspath):
                         scb = get_DefaultSCCSBuilder()
-                    elif diskcheck_rcs(self.dir, self.name):
-                        scb = get_DefaultRCSBuilder()
                     else:
-                        scb = None
+                        rcspath = os.path.join('RCS', self.name + ',v')
+                        if dir != '.':
+                            rcspath = os.path.join(dir, rcspath)
+                        if os.path.exists(rcspath):
+                            scb = get_DefaultRCSBuilder()
                 if scb is not None:
                     self.builder_set(scb)
             self.sbuilder = scb
@@ -1812,16 +1648,11 @@ class File(Base):
         """
         if self.is_derived():
             return [], None
-        return self.fs.build_dir_target_climb(self, self.dir, [self.name])
+        return self.fs.build_dir_target_climb(self.dir, [self.name])
 
     def is_pseudo_derived(self):
-        "__cacheable__"
         return self.has_src_builder()
-
-    def _rmv_existing(self):
-        '__cache_reset__'
-        Unlink(self, [], None)
-        
+    
     def prepare(self):
         """Prepare for this file to be created."""
         SCons.Node.Node.prepare(self)
@@ -1829,7 +1660,11 @@ class File(Base):
         if self.get_state() != SCons.Node.up_to_date:
             if self.exists():
                 if self.is_derived() and not self.precious:
-                    self._rmv_existing()
+                    Unlink(self, [], None)
+                    try:
+                        delattr(self, '_exists')
+                    except AttributeError:
+                        pass
             else:
                 try:
                     self._createDir()
@@ -1839,66 +1674,55 @@ class File(Base):
 
     def remove(self):
         """Remove this file."""
-        if self.exists() or self.islink():
+        if self.fs.exists_or_islink(self.path):
             self.fs.unlink(self.path)
             return 1
         return None
 
-    def do_duplicate(self, src):
-        self._createDir()
-        try:
-            Unlink(self, None, None)
-        except SCons.Errors.BuildError:
-            pass
-        try:
-            Link(self, src, None)
-        except SCons.Errors.BuildError, e:
-            desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
-            raise SCons.Errors.StopError, desc
-        self.linked = 1
-        # The Link() action may or may not have actually
-        # created the file, depending on whether the -n
-        # option was used or not.  Delete the _exists and
-        # _rexists attributes so they can be reevaluated.
-        self.clear()
-
     def exists(self):
-        "__cacheable__"
         # Duplicate from source path if we are set up to do this.
         if self.duplicate and not self.is_derived() and not self.linked:
-            src = self.srcnode()
-            if src is self:
-                return Base.exists(self)
-            # At this point, src is meant to be copied in a build directory.
-            src = src.rfile()
-            if src.abspath != self.abspath:
-                if src.exists():
-                    self.do_duplicate(src)
-                    # Can't return 1 here because the duplication might
-                    # not actually occur if the -n option is being used.
-                else:
-                    # The source file does not exist.  Make sure no old
-                    # copy remains in the build directory.
-                    if Base.exists(self) or self.islink():
-                        self.fs.unlink(self.path)
-                    # Return None explicitly because the Base.exists() call
-                    # above will have cached its value if the file existed.
-                    return None
+            src=self.srcnode().rfile()
+            if src.exists() and src.abspath != self.abspath:
+                self._createDir()
+                try:
+                    Unlink(self, None, None)
+                except SCons.Errors.BuildError:
+                    pass
+                try:
+                    Link(self, src, None)
+                except SCons.Errors.BuildError, e:
+                    desc = "Cannot duplicate `%s' in `%s': %s." % (src.path, self.dir.path, e.errstr)
+                    raise SCons.Errors.StopError, desc
+                self.linked = 1
+                # The Link() action may or may not have actually
+                # created the file, depending on whether the -n
+                # option was used or not.  Delete the _exists and
+                # _rexists attributes so they can be reevaluated.
+                try:
+                    delattr(self, '_exists')
+                except AttributeError:
+                    pass
+                try:
+                    delattr(self, '_rexists')
+                except AttributeError:
+                    pass
         return Base.exists(self)
 
-    #
-    # SIGNATURE SUBSYSTEM
-    #
-
     def new_binfo(self):
-        return BuildInfo(self)
+        return BuildInfo()
 
-    def new_ninfo(self):
-        ninfo = NodeInfo()
-        ninfo.update(self)
-        return ninfo
+    def del_cinfo(self):
+        try:
+            del self.binfo.csig
+        except AttributeError:
+            pass
+        try:
+            del self.binfo.timestamp
+        except AttributeError:
+            pass
 
-    def get_csig(self, calc=None):
+    def calc_csig(self, calc=None):
         """
         Generate a node's content signature, the digested signature
         of its content.
@@ -1907,48 +1731,47 @@ class File(Base):
         cache - alternate node to use for the signature cache
         returns - the content signature
         """
+        if calc is None:
+            calc = self.calculator()
+
         try:
-            return self.binfo.ninfo.csig
+            return self.binfo.csig
         except AttributeError:
             pass
+        
+        if calc.max_drift >= 0:
+            old = self.get_stored_info()
+        else:
+            old = BuildInfo()
 
-        if calc is None:
-            calc = self.calculator()
+        try:
+            mtime = self.get_timestamp()
+        except:
+            mtime = 0
+            raise SCons.Errors.UserError, "no such %s" % self
 
-        max_drift = self.fs.max_drift
-        mtime = self.get_timestamp()
-        use_stored = max_drift >= 0 and (time.time() - mtime) > max_drift
+        try:
+            if (old.timestamp and old.csig and old.timestamp == mtime):
+                # use the signature stored in the .sconsign file
+                csig = old.csig
+            else:
+                csig = calc.module.signature(self)
+        except AttributeError:
+            csig = calc.module.signature(self)
 
-        csig = None
-        if use_stored:
-            old = self.get_stored_info().ninfo
+        if calc.max_drift >= 0 and (time.time() - mtime) > calc.max_drift:
             try:
-                if old.timestamp and old.csig and old.timestamp == mtime:
-                    csig = old.csig
+                binfo = self.binfo
             except AttributeError:
-                pass
-        if csig is None:
-            csig = calc.module.signature(self)
-
-        binfo = self.get_binfo()
-        ninfo = binfo.ninfo
-        ninfo.csig = csig
-        ninfo.update(self)
-
-        if use_stored:
+                binfo = self.binfo = self.new_binfo()
+            binfo.csig = csig
+            binfo.timestamp = mtime
             self.store_info(binfo)
 
         return csig
 
-    #
-    #
-    #
-
-    def current(self, calc=None):
+    def current(self, calc=None, scan=1):
         self.binfo = self.gen_binfo(calc)
-        return self._cur2()
-    def _cur2(self):
-        "__cacheable__"
         if self.always_build:
             return None
         if not self.exists():
@@ -1957,32 +1780,30 @@ class File(Base):
             if r != self:
                 # ...but there is one in a Repository...
                 old = r.get_stored_info()
-                new = self.get_binfo()
-                if new == old:
+                if old == self.binfo:
                     # ...and it's even up-to-date...
                     if self._local:
                         # ...and they'd like a local copy.
                         LocalCopy(self, r, None)
-                        self.store_info(new)
+                        self.store_info(self.binfo)
                     return 1
+            self._rfile = self
             return None
         else:
             old = self.get_stored_info()
-            new = self.get_binfo()
-            return (new == old)
+            return (old == self.binfo)
 
     def rfile(self):
-        "__cacheable__"
-        if not self.exists():
-            norm_name = _my_normcase(self.name)
-            for dir in self.dir.get_all_rdirs():
-                try: node = dir.entries[norm_name]
-                except KeyError: node = dir.file_on_disk(self.name)
-                if node and node.exists() and \
-                   (isinstance(node, File) or isinstance(node, Entry) \
-                    or not node.is_derived()):
-                        return node
-        return self
+        try:
+            return self._rfile
+        except AttributeError:
+            self._rfile = self
+            if not self.exists():
+                n = self.fs.Rsearch(self.path, clazz=File,
+                                    cwd=self.fs.Top)
+                if n:
+                    self._rfile = n
+            return self._rfile
 
     def rstr(self):
         return str(self.rfile())
@@ -1990,15 +1811,12 @@ class File(Base):
     def cachepath(self):
         if not self.fs.CachePath:
             return None, None
-        ninfo = self.get_binfo().ninfo
-        if not hasattr(ninfo, 'bsig'):
-            raise SCons.Errors.InternalError, "cachepath(%s) found no bsig" % self.path
-        elif ninfo.bsig is None:
+        if self.binfo.bsig is None:
             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.
-        cache_sig = SCons.Sig.MD5.collect([ninfo.bsig, self.path])
+        cache_sig = SCons.Sig.MD5.collect([self.binfo.bsig, self.path])
         subdir = string.upper(cache_sig[0])
         dir = os.path.join(self.fs.CachePath, subdir)
         return dir, os.path.join(dir, cache_sig)
@@ -2011,16 +1829,14 @@ class File(Base):
         File, this is a TypeError..."""
         raise TypeError, "Tried to lookup File '%s' as a Dir." % self.path
 
-default_fs = None
+default_fs = FS()
 
-def find_file(filename, paths, verbose=None):
+def find_file(filename, paths, node_factory=default_fs.File, verbose=None):
     """
     find_file(str, [Dir()]) -> [nodes]
 
     filename - a filename to find
-    paths - a list of directory path *nodes* to search in.  Can be
-            represented as a list, a tuple, or a callable that is
-            called with no arguments and returns the list or tuple.
+    paths - a list of directory path *nodes* to search in
 
     returns - the node created from the found file.
 
@@ -2029,43 +1845,30 @@ def find_file(filename, paths, verbose=None):
 
     Only the first file found is returned, and none is returned
     if no file is found.
-    __cacheable__
     """
-    if verbose:
-        if not SCons.Util.is_String(verbose):
-            verbose = "find_file"
-        if not callable(verbose):
-            verbose = '  %s: ' % verbose
-            verbose = lambda s, v=verbose: sys.stdout.write(v + s)
-    else:
-        verbose = lambda x: x
-
-    if callable(paths):
-        paths = paths()
-
-    # Give Entries a chance to morph into Dirs.
-    paths = map(lambda p: p.must_be_a_Dir(), paths)
-
-    filedir, filename = os.path.split(filename)
-    if filedir:
-        def filedir_lookup(p, fd=filedir):
-            try:
-                return p.Dir(fd)
-            except TypeError:
-                # We tried to look up a Dir, but it seems there's already
-                # a File (or something else) there.  No big.
-                return None
-        paths = filter(None, map(filedir_lookup, paths))
-
+    if verbose and not SCons.Util.is_String(verbose):
+        verbose = "find_file"
+    retval = None
     for dir in paths:
-        verbose("looking for '%s' in '%s' ...\n" % (filename, dir))
-        node, d = dir.srcdir_find_file(filename)
-        if node:
-            verbose("... FOUND '%s' in '%s'\n" % (filename, d))
-            return node
-    return None
+        if verbose:
+            sys.stdout.write("  %s: looking for '%s' in '%s' ...\n" % (verbose, filename, dir))
+        try:
+            node = node_factory(filename, dir)
+            # Return true if the node exists or is a derived node.
+            if node.is_derived() or \
+               node.is_pseudo_derived() or \
+               (isinstance(node, SCons.Node.FS.Base) and node.exists()):
+                retval = node
+                if verbose:
+                    sys.stdout.write("  %s: ... FOUND '%s' in '%s'\n" % (verbose, filename, dir))
+                break
+        except TypeError:
+            # If we find a directory instead of a file, we don't care
+            pass
+
+    return retval
 
-def find_files(filenames, paths):
+def find_files(filenames, paths, node_factory = default_fs.File):
     """
     find_files([str], [Dir()]) -> [nodes]
 
@@ -2080,5 +1883,7 @@ def find_files(filenames, paths):
     Only the first file found is returned for each filename,
     and any files that aren't found are ignored.
     """
-    nodes = map(lambda x, paths=paths: find_file(x, paths), filenames)
-    return filter(None, nodes)
+    nodes = map(lambda x, paths=paths, node_factory=node_factory:
+                       find_file(x, paths, node_factory),
+                filenames)
+    return filter(lambda x: x != None, nodes)
index 8c2e6ea6934a0912f25c64b27d6b5636bcdb6f8e..ce677815c006c83d07d3de7344e528895e422268 100644 (file)
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os
-import re
-import string
 import sys
 import types
 import unittest
-import UserList
 
 import SCons.Errors
 import SCons.Node
@@ -42,27 +39,7 @@ built_source =  None
 cycle_detected = None
 built_order = 0
 
-def _actionAppend(a1, a2):
-    all = []
-    for curr_a in [a1, a2]:
-        if isinstance(curr_a, MyAction):
-            all.append(curr_a)
-        elif isinstance(curr_a, MyListAction):
-            all.extend(curr_a.list)
-        elif type(curr_a) == type([1,2]):
-            all.extend(curr_a)
-        else:
-            raise 'Cannot Combine Actions'
-    return MyListAction(all)
-
-class MyActionBase:
-    def __add__(self, other):
-        return _actionAppend(self, other)
-
-    def __radd__(self, other):
-        return _actionAppend(other, self)
-
-class MyAction(MyActionBase):
+class MyAction:
     def __init__(self):
         self.order = 0
 
@@ -76,36 +53,29 @@ class MyAction(MyActionBase):
         self.order = built_order
         return 0
 
-class MyExecutor:
-    def __init__(self, env=None, targets=[], sources=[]):
-        self.env = env
-        self.targets = targets
-        self.sources = sources
-    def get_build_env(self):
-        return self.env
-    def get_build_scanner_path(self, scanner):
-        return 'executor would call %s' % scanner
-    def cleanup(self):
-        self.cleaned_up = 1
-    def scan_targets(self, scanner):
-        if not scanner:
-            return
-        d = scanner(self.targets)
-        for t in self.targets:
-            t.implicit.extend(d)
-    def scan_sources(self, scanner):
-        if not scanner:
-            return
-        d = scanner(self.sources)
-        for t in self.targets:
-            t.implicit.extend(d)
-
-class MyListAction(MyActionBase):
-    def __init__(self, list):
-        self.list = list
+    def get_actions(self):
+        return [self]
+
+class MyNonGlobalAction:
+    def __init__(self):
+        self.order = 0
+        self.built_it = None
+        self.built_target =  None
+        self.built_source =  None
+
     def __call__(self, target, source, env, errfunc):
-        for A in self.list:
-            A(target, source, env, errfunc)
+        # Okay, so not ENTIRELY non-global...
+        global built_order
+        self.built_it = 1
+        self.built_target = target
+        self.built_source = source
+        self.built_args = env
+        built_order = built_order + 1
+        self.order = built_order
+        return 0
+
+    def get_actions(self):
+        return [self]
 
 class Environment:
     def __init__(self, **kw):
@@ -121,23 +91,13 @@ class Environment:
         return apply(Environment, (), d)
     def _update(self, dict):
         self._dict.update(dict)
-    def get_calculator(self):
-        return SCons.Sig.default_calc
-    def get_factory(self, factory):
-        return factory or MyNode
-    def get_scanner(self, scanner_key):
-        return self._dict['SCANNERS'][0]
 
 class Builder:
-    def __init__(self, env=None, is_explicit=1):
-        if env is None: env = Environment()
-        self.env = env
+    def __init__(self, is_explicit=1):
+        self.env = Environment()
         self.overrides = {}
         self.action = MyAction()
-        self.source_factory = MyNode
         self.is_explicit = is_explicit
-        self.target_scanner = None
-        self.source_scanner = None
     def targets(self, t):
         return [t]
     def get_actions(self):
@@ -179,12 +139,8 @@ class Scanner:
     def __call__(self, node):
         self.called = 1
         return node.found_includes
-    def path(self, env, dir, target=None, source=None):
-        return ()
     def select(self, node):
         return self
-    def recurse_nodes(self, nodes):
-        return nodes
 
 class MyNode(SCons.Node.Node):
     """The base Node class contains a number of do-nothing methods that
@@ -200,116 +156,6 @@ class MyNode(SCons.Node.Node):
     def get_found_includes(self, env, scanner, target):
         return scanner(self)
 
-class Calculator:
-    def __init__(self, val):
-        self.max_drift = 0
-        class M:
-            def __init__(self, val):
-                self.val = val
-            def signature(self, args):
-                return self.val
-            def collect(self, args):
-                return reduce(lambda x, y: x+y, args, self.val)
-        self.module = M(val)
-
-
-
-class NodeInfoTestCase(unittest.TestCase):
-
-    def test___cmp__(self):
-        """Test comparing NodeInfo objects"""
-        ni1 = SCons.Node.NodeInfo()
-        ni2 = SCons.Node.NodeInfo()
-
-        assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__)
-
-        ni1.foo = 777
-        assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__)
-
-        ni2.foo = 888
-        assert ni1 != ni2, "%s == %s" % (ni1.__dict__, ni2.__dict__)
-
-        ni1.foo = 888
-        assert ni1 == ni2, "%s != %s" % (ni1.__dict__, ni2.__dict__)
-
-    def test_merge(self):
-        """Test merging NodeInfo attributes"""
-        ni1 = SCons.Node.NodeInfo()
-        ni2 = SCons.Node.NodeInfo()
-
-        ni1.a1 = 1
-        ni1.a2 = 2
-
-        ni2.a2 = 222
-        ni2.a3 = 333
-
-        ni1.merge(ni2)
-        assert ni1.__dict__ == {'a1':1, 'a2':222, 'a3':333}, ni1.__dict__
-
-    def test_update(self):
-        """Test the update() method"""
-        ni = SCons.Node.NodeInfo()
-        ni.update(SCons.Node.Node())
-
-
-
-class BuildInfoTestCase(unittest.TestCase):
-
-    def test___init__(self):
-        """Test BuildInfo initialization"""
-        bi = SCons.Node.BuildInfo(SCons.Node.Node())
-        assert hasattr(bi, 'ninfo')
-
-        class MyNode(SCons.Node.Node):
-            def new_ninfo(self):
-                return 'ninfo initialization'
-        bi = SCons.Node.BuildInfo(MyNode())
-        assert bi.ninfo == 'ninfo initialization', bi.ninfo
-
-    def test___cmp__(self):
-        """Test comparing BuildInfo objects"""
-        bi1 = SCons.Node.BuildInfo(SCons.Node.Node())
-        bi2 = SCons.Node.BuildInfo(SCons.Node.Node())
-
-        assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__)
-
-        bi1.ninfo.foo = 777
-        assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__)
-
-        bi2.ninfo.foo = 888
-        assert bi1 != bi2, "%s == %s" % (bi1.__dict__, bi2.__dict__)
-
-        bi1.ninfo.foo = 888
-        assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__)
-
-        bi1.foo = 999
-        assert bi1 == bi2, "%s != %s" % (bi1.__dict__, bi2.__dict__)
-
-    def test_merge(self):
-        """Test merging BuildInfo attributes"""
-        bi1 = SCons.Node.BuildInfo(SCons.Node.Node())
-        bi2 = SCons.Node.BuildInfo(SCons.Node.Node())
-
-        bi1.a1 = 1
-        bi1.a2 = 2
-
-        bi2.a2 = 222
-        bi2.a3 = 333
-
-        bi1.ninfo.a4 = 4
-        bi1.ninfo.a5 = 5
-        bi2.ninfo.a5 = 555
-        bi2.ninfo.a6 = 666
-
-        bi1.merge(bi2)
-        assert bi1.a1 == 1, bi1.a1
-        assert bi1.a2 == 222, bi1.a2
-        assert bi1.a3 == 333, bi1.a3
-        assert bi1.ninfo.a4 == 4, bi1.ninfo.a4
-        assert bi1.ninfo.a5 == 555, bi1.ninfo.a5
-        assert bi1.ninfo.a6 == 666, bi1.ninfo.a6
-
-
 
 
 class NodeTestCase(unittest.TestCase):
@@ -390,62 +236,36 @@ class NodeTestCase(unittest.TestCase):
         assert built_args["on"] == 3, built_args
         assert built_args["off"] == 4, built_args
 
-    def test_get_build_scanner_path(self):
-        """Test the get_build_scanner_path() method"""
-        n = SCons.Node.Node()
-        x = MyExecutor()
-        n.set_executor(x)
-        p = n.get_build_scanner_path('fake_scanner')
-        assert p == "executor would call fake_scanner", p
-
-    def test_get_executor(self):
-        """Test the get_executor() method"""
-        n = SCons.Node.Node()
-
-        try:
-            n.get_executor(0)
-        except AttributeError:
-            pass
-        else:
-            self.fail("did not catch expected AttributeError")
-
-        class Builder:
-            action = 'act'
-            env = 'env1'
-            overrides = {}
-
-        n = SCons.Node.Node()
-        n.builder_set(Builder())
-        x = n.get_executor()
-        assert x.env == 'env1', x.env
-
-        n = SCons.Node.Node()
-        n.builder_set(Builder())
-        n.env_set('env2')
-        x = n.get_executor()
-        assert x.env == 'env2', x.env
-
-    def test_set_executor(self):
-        """Test the set_executor() method"""
-        n = SCons.Node.Node()
-        n.set_executor(1)
-        assert n.executor == 1, n.executor
-
-    def test_executor_cleanup(self):
-        """Test letting the executor cleanup its cache"""
-        n = SCons.Node.Node()
-        x = MyExecutor()
-        n.set_executor(x)
-        n.executor_cleanup()
-        assert x.cleaned_up
-
-    def test_reset_executor(self):
-        """Test the reset_executor() method"""
-        n = SCons.Node.Node()
-        n.set_executor(1)
-        assert n.executor == 1, n.executor
-        n.reset_executor()
-        assert not hasattr(n, 'executor'), "unexpected executor attribute"
+        built_it = None
+        built_order = 0
+        node = MyNode("xxx")
+        node.builder_set(Builder())
+        node.env_set(Environment())
+        node.sources = ["yyy", "zzz"]
+        pre1 = MyNonGlobalAction()
+        pre2 = MyNonGlobalAction()
+        post1 = MyNonGlobalAction()
+        post2 = MyNonGlobalAction()
+        node.add_pre_action(pre1)
+        node.add_pre_action(pre2)
+        node.add_post_action(post1)
+        node.add_post_action(post2)
+        node.build()
+        assert built_it
+        assert pre1.built_it
+        assert pre2.built_it
+        assert post1.built_it
+        assert post2.built_it
+        assert pre1.order == 1, pre1.order
+        assert pre2.order == 2, pre1.order
+        # The action of the builder itself is order 3...
+        assert post1.order == 4, pre1.order
+        assert post2.order == 5, pre1.order
+
+        for act in [ pre1, pre2, post1, post2 ]:
+            assert type(act.built_target[0]) == type(MyNode("bar")), type(act.built_target[0])
+            assert str(act.built_target[0]) == "xxx", str(act.built_target[0])
+            assert act.built_source == ["yyy", "zzz"], act.built_source
 
     def test_built(self):
         """Test the built() method"""
@@ -471,6 +291,14 @@ class NodeTestCase(unittest.TestCase):
         n = SCons.Node.Node()
         n.visited()
 
+    def test_depends_on(self):
+        """Test the depends_on() method
+        """
+        parent = SCons.Node.Node()
+        child = SCons.Node.Node()
+        parent.add_dependency([child])
+        assert parent.depends_on([child])
+
     def test_builder_set(self):
         """Test setting a Node's Builder
         """
@@ -492,24 +320,11 @@ class NodeTestCase(unittest.TestCase):
         """
         n1 = SCons.Node.Node()
         assert not n1.has_explicit_builder()
-        n1.set_explicit(1)
+        n1.builder_set(Builder(is_explicit=1))
         assert n1.has_explicit_builder()
-        n1.set_explicit(None)
+        n1.builder_set(Builder(is_explicit=None))
         assert not n1.has_explicit_builder()
 
-    def test_get_builder(self):
-        """Test the get_builder() method"""
-        n1 = SCons.Node.Node()
-        b = n1.get_builder()
-        assert b is None, b
-        b = n1.get_builder(777)
-        assert b == 777, b
-        n1.builder_set(888)
-        b = n1.get_builder()
-        assert b == 888, b
-        b = n1.get_builder(999)
-        assert b == 888, b
-
     def test_multiple_side_effect_has_builder(self):
         """Test the multiple_side_effect_has_builder() method
         """
@@ -546,23 +361,6 @@ class NodeTestCase(unittest.TestCase):
         node = SCons.Node.Node()
         assert node.current() is None
 
-    def test_children_are_up_to_date(self):
-        """Test the children_are_up_to_date() method used by subclasses
-        """
-        n1 = SCons.Node.Node()
-        n2 = SCons.Node.Node()
-
-        calc = Calculator(111)
-
-        n1.add_source(n2)
-        assert n1.children_are_up_to_date(calc), "expected up to date"
-        n2.set_state(SCons.Node.executed)
-        assert not n1.children_are_up_to_date(calc), "expected not up to date"
-        n2.set_state(SCons.Node.up_to_date)
-        assert n1.children_are_up_to_date(calc), "expected up to date"
-        n1.always_build = 1
-        assert not n1.children_are_up_to_date(calc), "expected not up to date"
-
     def test_env_set(self):
         """Test setting a Node's Environment
         """
@@ -579,80 +377,85 @@ class NodeTestCase(unittest.TestCase):
         a = node.builder.get_actions()
         assert isinstance(a[0], MyAction), a[0]
 
-    def test_get_bsig(self):
+    def test_calc_bsig(self):
         """Test generic build signature calculation
         """
+        class Calculator:
+            def __init__(self, val):
+                self.max_drift = 0
+                class M:
+                    def __init__(self, val):
+                        self.val = val
+                    def collect(self, args):
+                        return reduce(lambda x, y: x+y, args, self.val)
+                self.module = M(val)
         node = SCons.Node.Node()
-        result = node.get_bsig(Calculator(222))
+        result = node.calc_bsig(Calculator(222))
         assert result == 222, result
-        result = node.get_bsig(Calculator(333))
+        result = node.calc_bsig(Calculator(333))
         assert result == 222, result
 
-    def test_get_csig(self):
+    def test_calc_csig(self):
         """Test generic content signature calculation
         """
+        class Calculator:
+            def __init__(self, val):
+                self.max_drift = 0
+                class M:
+                    def __init__(self, val):
+                        self.val = val
+                    def signature(self, args):
+                        return self.val
+                self.module = M(val)
         node = SCons.Node.Node()
-        result = node.get_csig(Calculator(444))
+        result = node.calc_csig(Calculator(444))
         assert result == 444, result
-        result = node.get_csig(Calculator(555))
+        result = node.calc_csig(Calculator(555))
         assert result == 444, result
 
-    def test_get_binfo(self):
-        """Test fetching/creating a build information structure
-        """
-        node = SCons.Node.Node()
-
-        binfo = node.get_binfo()
-        assert isinstance(binfo, SCons.Node.BuildInfo), binfo
-
-        node.binfo = 777
-        binfo = node.get_binfo()
-        assert binfo == 777, binfo
-
     def test_gen_binfo(self):
         """Test generating a build information structure
         """
+        class Calculator:
+            def __init__(self, val):
+                self.max_drift = 0
+                class M:
+                    def __init__(self, val):
+                        self.val = val
+                    def collect(self, args):
+                        return reduce(lambda x, y: x+y, args, self.val)
+                self.module = M(val)
+
         node = SCons.Node.Node()
-        d = SCons.Node.Node()
-        i = SCons.Node.Node()
-        node.depends = [d]
-        node.implicit = [i]
-        node.gen_binfo(Calculator(666))
-        binfo = node.binfo
+        binfo = node.gen_binfo(Calculator(666))
         assert isinstance(binfo, SCons.Node.BuildInfo), binfo
         assert hasattr(binfo, 'bsources')
         assert hasattr(binfo, 'bsourcesigs')
-        assert binfo.bdepends == [d]
+        assert hasattr(binfo, 'bdepends')
         assert hasattr(binfo, 'bdependsigs')
-        assert binfo.bimplicit == [i]
+        assert hasattr(binfo, 'bimplicit')
         assert hasattr(binfo, 'bimplicitsigs')
-        assert binfo.ninfo.bsig == 1998, binfo.ninfo.bsig
+        assert binfo.bsig == 666, binfo.bsig
 
     def test_explain(self):
         """Test explaining why a Node must be rebuilt
         """
-        class testNode(SCons.Node.Node):
-            def __str__(self): return 'xyzzy'
-        node = testNode()
+        node = SCons.Node.Node()
         node.exists = lambda: None
-        # Can't do this with new-style classes (python bug #1066490)
-        #node.__str__ = lambda: 'xyzzy'
+        node.__str__ = lambda: 'xyzzy'
         result = node.explain()
         assert result == "building `xyzzy' because it doesn't exist\n", result
 
-        class testNode2(SCons.Node.Node):
-            def __str__(self): return 'null_binfo'
-        node = testNode2()
+        node = SCons.Node.Node()
         result = node.explain()
         assert result == None, result
 
-        def get_null_info():
-            class Null_BInfo:
+        class Null_BInfo:
+            def __init__(self):
                 pass
-            return Null_BInfo()
 
-        node.get_stored_info = get_null_info
-        #see above: node.__str__ = lambda: 'null_binfo'
+        node.get_stored_info = Null_BInfo
+        node.__str__ = lambda: 'null_binfo'
         result = node.explain()
         assert result == "Cannot explain why `null_binfo' is being rebuilt: No previous build information found\n", result
 
@@ -723,8 +526,6 @@ class NodeTestCase(unittest.TestCase):
 
     def test_prepare(self):
         """Test preparing a node to be built
-
-        By extension, this also tests the missing() method.
         """
         node = SCons.Node.Node()
 
@@ -881,55 +682,41 @@ class NodeTestCase(unittest.TestCase):
         assert deps == [], deps
 
         s = Scanner()
-        d1 = MyNode("d1")
-        d2 = MyNode("d2")
-        node.found_includes = [d1, d2]
+        d = MyNode("ddd")
+        node.found_includes = [d]
 
         # Simple return of the found includes
         deps = node.get_implicit_deps(env, s, target)
-        assert deps == [d1, d2], deps
+        assert deps == [d], deps
 
-        # By default, our fake scanner recurses
+        # No "recursive" attribute on scanner doesn't recurse
         e = MyNode("eee")
-        f = MyNode("fff")
-        g = MyNode("ggg")
-        d1.found_includes = [e, f]
-        d2.found_includes = [e, f]
-        f.found_includes = [g]
+        d.found_includes = [e]
         deps = node.get_implicit_deps(env, s, target)
-        assert deps == [d1, d2, e, f, g], map(str, deps)
+        assert deps == [d], map(str, deps)
 
-        # Recursive scanning eliminates duplicates
-        e.found_includes = [f]
+        # Explicit "recursive" attribute on scanner doesn't recurse
+        s.recursive = None
         deps = node.get_implicit_deps(env, s, target)
-        assert deps == [d1, d2, e, f, g], map(str, deps)
+        assert deps == [d], map(str, deps)
 
-        # Scanner method can select specific nodes to recurse
-        def no_fff(nodes):
-            return filter(lambda n: str(n)[0] != 'f', nodes)
-        s.recurse_nodes = no_fff
+        # Explicit "recursive" attribute on scanner which does recurse
+        s.recursive = 1
         deps = node.get_implicit_deps(env, s, target)
-        assert deps == [d1, d2, e, f], map(str, deps)
+        assert deps == [d, e], map(str, deps)
 
-        # Scanner method can short-circuit recursing entirely
-        s.recurse_nodes = lambda nodes: []
+        # Recursive scanning eliminates duplicates
+        f = MyNode("fff")
+        d.found_includes = [e, f]
+        e.found_includes = [f]
         deps = node.get_implicit_deps(env, s, target)
-        assert deps == [d1, d2], map(str, deps)
-
-    def test_get_scanner(self):
-        """Test fetching the environment scanner for a Node
-        """
-        node = SCons.Node.Node()
-        scanner = Scanner()
-        env = Environment(SCANNERS = [scanner])
-        s = node.get_scanner(env)
-        assert s == scanner, s
-        s = node.get_scanner(env, {'X':1})
-        assert s == scanner, s
+        assert deps == [d, e, f], map(str, deps)
 
     def test_get_source_scanner(self):
         """Test fetching the source scanner for a Node
         """
+        class Builder:
+            pass
         target = SCons.Node.Node()
         source = SCons.Node.Node()
         s = target.get_source_scanner(source)
@@ -939,48 +726,32 @@ class NodeTestCase(unittest.TestCase):
         ts2 = Scanner()
         ts3 = Scanner()
 
-        class Builder1(Builder):
-            def __call__(self, source):
-                r = SCons.Node.Node()
-                r.builder = self
-                return [r]
-        class Builder2(Builder1):
-            def __init__(self, scanner):
-                self.source_scanner = scanner
-
-        builder = Builder2(ts1)
-            
-        targets = builder([source])
-        s = targets[0].get_source_scanner(source)
+        source.backup_source_scanner = ts1
+        s = target.get_source_scanner(source)
         assert s is ts1, s
 
-        target.builder_set(Builder2(ts1))
+        target.builder = Builder()
         target.builder.source_scanner = ts2
         s = target.get_source_scanner(source)
         assert s is ts2, s
 
-        builder = Builder1(env=Environment(SCANNERS = [ts3]))
-
-        targets = builder([source])
-        
-        s = targets[0].get_source_scanner(source)
+        target.source_scanner = ts3
+        s = target.get_source_scanner(source)
         assert s is ts3, s
 
-
     def test_scan(self):
         """Test Scanner functionality
         """
-        env = Environment()
         node = MyNode("nnn")
         node.builder = Builder()
-        node.env_set(env)
-        x = MyExecutor(env, [node])
-
+        node.env_set(Environment())
         s = Scanner()
+
         d = MyNode("ddd")
         node.found_includes = [d]
 
-        node.builder.target_scanner = s
+        assert node.target_scanner == None, node.target_scanner
+        node.target_scanner = s
         assert node.implicit is None
 
         node.scan()
@@ -1012,14 +783,13 @@ class NodeTestCase(unittest.TestCase):
         SCons.Node.implicit_deps_unchanged = None
         try:
             sn = StoredNode("eee")
-            sn.builder_set(Builder())
-            sn.builder.target_scanner = s
+            sn._children = ['fake']
+            sn.target_scanner = s
 
             sn.scan()
 
             assert sn.implicit == [], sn.implicit
-            assert sn.children() == [], sn.children()
-
+            assert not hasattr(sn, '_children'), "unexpected _children attribute"
         finally:
             SCons.Sig.default_calc = save_default_calc
             SCons.Node.implicit_cache = save_implicit_cache
@@ -1094,7 +864,7 @@ class NodeTestCase(unittest.TestCase):
         """Test setting and getting the state of a node
         """
         node = SCons.Node.Node()
-        assert node.get_state() == SCons.Node.no_state
+        assert node.get_state() == None
         node.set_state(SCons.Node.executing)
         assert node.get_state() == SCons.Node.executing
         assert SCons.Node.pending < SCons.Node.executing
@@ -1229,17 +999,14 @@ class NodeTestCase(unittest.TestCase):
         n.implicit = 'testimplicit'
         n.waiting_parents = ['foo', 'bar']
 
-        x = MyExecutor()
-        n.set_executor(x)
-
         n.clear()
 
+        assert n.get_state() is None, n.get_state()
         assert not hasattr(n, 'binfo'), n.bsig
         assert n.includes is None, n.includes
         assert n.found_includes == {}, n.found_includes
         assert n.implicit is None, n.implicit
         assert n.waiting_parents == [], n.waiting_parents
-        assert x.cleaned_up
 
     def test_get_subst_proxy(self):
         """Test the get_subst_proxy method."""
@@ -1259,6 +1026,12 @@ class NodeTestCase(unittest.TestCase):
         s = n.get_suffix()
         assert s == '', s
 
+    def test_generate_build_dict(self):
+        """Test the base Node generate_build_dict() method"""
+        n = SCons.Node.Node()
+        dict = n.generate_build_dict()
+        assert dict == {}, dict
+
     def test_postprocess(self):
         """Test calling the base Node postprocess() method"""
         n = SCons.Node.Node()
@@ -1283,46 +1056,9 @@ class NodeTestCase(unittest.TestCase):
         n1.call_for_all_waiting_parents(func)
         assert result == [n1, n2], result
 
-class NodeListTestCase(unittest.TestCase):
-    def test___str__(self):
-        """Test"""
-        n1 = MyNode("n1")
-        n2 = MyNode("n2")
-        n3 = MyNode("n3")
-        nl = SCons.Node.NodeList([n3, n2, n1])
-
-        l = [1]
-        ul = UserList.UserList([2])
-        try:
-            l.extend(ul)
-        except TypeError:
-            # An older version of Python (*cough* 1.5.2 *cough*)
-            # that doesn't allow UserList objects to extend lists.
-            pass
-        else:
-            s = str(nl)
-            assert s == "['n3', 'n2', 'n1']", s
-
-        r = repr(nl)
-        r = re.sub('at (0[xX])?[0-9a-fA-F]+', 'at 0x', r)
-        # Don't care about ancestry: just leaf value of MyNode
-        r = re.sub('<.*?\.MyNode', '<MyNode', r)
-        # New-style classes report as "object"; classic classes report
-        # as "instance"...
-        r = re.sub("object", "instance", r)
-        l = string.join(["<MyNode instance at 0x>"]*3, ", ")
-        assert r == '[%s]' % l, r
-
 
 
 if __name__ == "__main__":
-    suite = unittest.TestSuite()
-    tclasses = [ BuildInfoTestCase,
-                 NodeInfoTestCase,
-                 NodeTestCase,
-                 NodeListTestCase ]
-    for tclass in tclasses:
-        names = unittest.getTestCaseNames(tclass, 'test_')
-        suite.addTests(map(tclass, names))
+    suite = unittest.makeSuite(NodeTestCase, 'test_')
     if not unittest.TextTestRunner().run(suite).wasSuccessful():
         sys.exit(1)
index e73e5f389b166912d99b8d5bec340be0a6dee994..38cff929751d8f17408859854cdbe49bcf80d6a3 100644 (file)
@@ -48,10 +48,8 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import copy
 import string
-import UserList
 
 from SCons.Debug import logInstanceCreation
-import SCons.Executor
 import SCons.SConsign
 import SCons.Util
 
@@ -62,7 +60,6 @@ import SCons.Util
 # it has no builder of its own.  The canonical example is a file
 # system directory, which is only up to date if all of its children
 # were up to date.
-no_state = 0
 pending = 1
 executing = 2
 up_to_date = 3
@@ -70,17 +67,7 @@ executed = 4
 failed = 5
 stack = 6 # nodes that are in the current Taskmaster execution stack
 
-StateString = {
-    0 : "0",
-    1 : "pending",
-    2 : "executing",
-    3 : "up_to_date",
-    4 : "executed",
-    5 : "failed",
-    6 : "stack",
-}
-
-# controls whether implicit dependencies are cached:
+# controls whether implicit depedencies are cached:
 implicit_cache = 0
 
 # controls whether implicit dep changes are ignored:
@@ -95,69 +82,20 @@ def do_nothing(node): pass
 
 Annotate = do_nothing
 
-# Classes for signature info for Nodes.
-
-class NodeInfo:
-    """
-    A generic class for signature information for a Node.
-
-    We actually expect that modules containing Node subclasses will also
-    subclass NodeInfo, to provide their own logic for dealing with their
-    own Node-specific signature information.
-    """
-    def __init__(self):
-        """A null initializer so that subclasses have a superclass
-        initialization method to call for future use.
-        """
-        pass
-    def __cmp__(self, other):
-        return cmp(self.__dict__, other.__dict__)
-    def update(self, node):
-        pass
-    def merge(self, other):
-        for key, val in other.__dict__.items():
-            self.__dict__[key] = val
-
 class BuildInfo:
-    """
-    The generic build information for a Node.
-
-    This is what gets stored in a .sconsign file for each target file.
-    It contains a NodeInfo instance for this node (signature information
-    that's specific to the type of Node) and direct attributes for the
-    generic build stuff we have to track:  sources, explicit dependencies,
-    implicit dependencies, and action information.
-    """
-    def __init__(self, node):
-        self.ninfo = node.new_ninfo()
-        self.bsourcesigs = []
-        self.bdependsigs = []
-        self.bimplicitsigs = []
-        self.bactsig = None
     def __cmp__(self, other):
-        return cmp(self.ninfo, other.ninfo)
-    def merge(self, other):
-        for key, val in other.__dict__.items():
-            try:
-                merge = self.__dict__[key].merge
-            except (AttributeError, KeyError):
-                self.__dict__[key] = val
-            else:
-                merge(val)
+        return cmp(self.__dict__, other.__dict__)
 
 class Node:
     """The base Node class, for entities that we know how to
     build, or use to build other Nodes.
     """
 
-    if SCons.Memoize.use_memoizer:
-        __metaclass__ = SCons.Memoize.Memoized_Metaclass
-
     class Attrs:
         pass
 
     def __init__(self):
-        if __debug__: logInstanceCreation(self, 'Node.Node')
+        if __debug__: logInstanceCreation(self, 'Node')
         # Note that we no longer explicitly initialize a self.builder
         # attribute to None here.  That's because the self.builder
         # attribute may be created on-the-fly later by a subclass (the
@@ -180,13 +118,17 @@ class Node:
         self.implicit = None    # implicit (scanned) dependencies (None means not scanned yet)
         self.waiting_parents = []
         self.wkids = None       # Kids yet to walk, when it's an array
+        self.target_scanner = None      # explicit scanner from this node's Builder
+        self.source_scanner = None
+        self.backup_source_scanner = None
 
         self.env = None
-        self.state = no_state
+        self.state = None
         self.precious = None
         self.always_build = None
         self.found_includes = {}
         self.includes = None
+        self.overrides = {}     # construction variable overrides for building this node
         self.attributes = self.Attrs() # Generic place to stick information about the Node.
         self.side_effect = 0 # true iff this node is a side effect
         self.side_effects = [] # the side effects of building this target
@@ -202,14 +144,15 @@ class Node:
     def get_suffix(self):
         return ''
 
-    def get_build_env(self):
-        """Fetch the appropriate Environment to build this node.
-        __cacheable__"""
-        return self.get_executor().get_build_env()
+    def generate_build_dict(self):
+        """Return an appropriate dictionary of values for building
+        this Node."""
+        return {}
 
-    def get_build_scanner_path(self, scanner):
-        """Fetch the appropriate scanner path for this node."""
-        return self.get_executor().get_build_scanner_path(scanner)
+    def get_build_env(self):
+        """Fetch the appropriate Environment to build this node."""
+        executor = self.get_executor()
+        return executor.get_build_env()
 
     def set_executor(self, executor):
         """Set the action executor for this node."""
@@ -223,35 +166,15 @@ class Node:
         except AttributeError:
             if not create:
                 raise
-            try:
-                act = self.builder.action
-            except AttributeError:
-                executor = SCons.Executor.Null(targets=[self])
-            else:
-                executor = SCons.Executor.Executor(act,
-                                                   self.env or self.builder.env,
-                                                   [self.builder.overrides],
-                                                   [self],
-                                                   self.sources)
+            import SCons.Executor
+            executor = SCons.Executor.Executor(self.builder.action,
+                                               self.builder.env,
+                                               [self.builder.overrides],
+                                               [self],
+                                               self.sources)
             self.executor = executor
         return executor
 
-    def executor_cleanup(self):
-        """Let the executor clean up any cached information."""
-        try:
-            executor = self.get_executor(create=None)
-        except AttributeError:
-            pass
-        else:
-            executor.cleanup()
-
-    def reset_executor(self):
-        "Remove cached executor; forces recompute when needed."
-        try:
-            delattr(self, 'executor')
-        except AttributeError:
-            pass
-
     def retrieve_from_cache(self):
         """Try to retrieve the node's content from a cache
 
@@ -270,15 +193,15 @@ class Node:
         so only do thread safe stuff here. Do thread unsafe stuff in
         built().
         """
-        def exitstatfunc(stat, node=self):
-            if stat:
-                msg = "Error %d" % stat
-                raise SCons.Errors.BuildError(node=node, errstr=msg)
+        if not self.has_builder():
+            return
+        def errfunc(stat, node=self):
+            raise SCons.Errors.BuildError(node=node, errstr="Error %d" % stat)
         executor = self.get_executor()
-        apply(executor, (self, exitstatfunc), kw)
+        apply(executor, (self, errfunc), kw)
 
     def built(self):
-        """Called just after this node is successfully built."""
+        """Called just after this node is sucessfully built."""
 
         # Clear the implicit dependency caches of any Nodes
         # waiting for this Node to be built.
@@ -287,27 +210,30 @@ class Node:
             parent.del_binfo()
         
         try:
-            new = self.binfo
+            new_binfo = self.binfo
         except AttributeError:
             # Node arrived here without build info; apparently it
             # doesn't need it, so don't bother calculating or storing
             # it.
-            new = None
+            new_binfo = None
 
         # Reset this Node's cached state since it was just built and
         # various state has changed.
+        save_state = self.get_state()
         self.clear()
+        self.set_state(save_state)
+
+        # Had build info, so it should be stored in the signature
+        # cache.  However, if the build info included a content
+        # signature then it should be recalculated before being
+        # stored.
         
-        if new:
-            # It had build info, so it should be stored in the signature
-            # cache.  However, if the build info included a content
-            # signature then it must be recalculated before being stored.
-            if hasattr(new.ninfo, 'csig'):
-                self.get_csig()
+        if new_binfo:
+            if hasattr(new_binfo, 'csig'):
+                new_binfo = self.gen_binfo()  # sets self.binfo
             else:
-                new.ninfo.update(self)
-                self.binfo = new
-            self.store_info(self.binfo)
+                self.binfo = new_binfo
+            self.store_info(new_binfo)
 
     def add_to_waiting_parents(self, node):
         self.waiting_parents.append(node)
@@ -320,16 +246,21 @@ class Node:
     def postprocess(self):
         """Clean up anything we don't need to hang onto after we've
         been built."""
-        self.executor_cleanup()
+        try:
+            executor = self.get_executor(create=None)
+        except AttributeError:
+            pass
+        else:
+            executor.cleanup()
 
     def clear(self):
         """Completely clear a Node of all its cached state (so that it
         can be re-evaluated by interfaces that do continuous integration
         builds).
-        __reset_cache__
         """
-        self.executor_cleanup()
+        self.set_state(None)
         self.del_binfo()
+        self.del_cinfo()
         try:
             delattr(self, '_calculated_sig')
         except AttributeError:
@@ -345,8 +276,15 @@ class Node:
         without requiring a build.."""
         pass
 
+    def depends_on(self, nodes):
+        """Does this node depend on any of 'nodes'?"""
+        for node in nodes:
+            if node in self.children():
+                return 1
+
+        return 0
+
     def builder_set(self, builder):
-        "__cache_reset__"
         self.builder = builder
 
     def has_builder(self):
@@ -369,9 +307,6 @@ class Node:
             b = self.builder
         return not b is None
 
-    def set_explicit(self, is_explicit):
-        self.is_explicit = is_explicit
-
     def has_explicit_builder(self):
         """Return whether this Node has an explicit builder
 
@@ -379,18 +314,7 @@ class Node:
         non-explicit, so that it can be overridden by an explicit
         builder that the user supplies (the canonical example being
         directories)."""
-        try:
-            return self.is_explicit
-        except AttributeError:
-            self.is_explicit = None
-            return self.is_explicit
-
-    def get_builder(self, default_builder=None):
-        """Return the set builder, or a specified default value"""
-        try:
-            return self.builder
-        except AttributeError:
-            return default_builder
+        return self.has_builder() and self.builder.is_explicit
 
     multiple_side_effect_has_builder = has_builder
 
@@ -403,7 +327,6 @@ class Node:
         signatures when they are used as source files to other derived files. For
         example: source with source builders are not derived in this sense,
         and hence should not return true.
-        __cacheable__
         """
         return self.has_builder() or self.side_effect
 
@@ -420,7 +343,7 @@ class Node:
         """
         return [], None
 
-    def get_found_includes(self, env, scanner, path):
+    def get_found_includes(self, env, scanner, target):
         """Return the scanned include lines (implicit dependencies)
         found in this node.
 
@@ -430,7 +353,7 @@ class Node:
         """
         return []
 
-    def get_implicit_deps(self, env, scanner, path):
+    def get_implicit_deps(self, env, scanner, target):
         """Return a list of implicit dependencies for this node.
 
         This method exists to handle recursive invocation of the scanner
@@ -444,57 +367,59 @@ class Node:
         # for this Node.
         scanner = scanner.select(self)
 
+        try:
+            recurse = scanner.recursive
+        except AttributeError:
+            recurse = None
+
         nodes = [self]
         seen = {}
         seen[self] = 1
         deps = []
         while nodes:
-            n = nodes.pop(0)
-            d = filter(lambda x, seen=seen: not seen.has_key(x),
-                       n.get_found_includes(env, scanner, path))
-            if d:
-                deps.extend(d)
-                for n in d:
-                    seen[n] = 1
-                nodes.extend(scanner.recurse_nodes(d))
+           n = nodes.pop(0)
+           d = filter(lambda x, seen=seen: not seen.has_key(x),
+                      n.get_found_includes(env, scanner, target))
+           if d:
+               deps.extend(d)
+               for n in d:
+                   seen[n] = 1
+               if recurse:
+                   nodes.extend(d)
 
         return deps
 
-    def get_scanner(self, env, kw={}):
-        return env.get_scanner(self.scanner_key())
+    # cache used to make implicit_factory fast.
+    implicit_factory_cache = {}
+    
+    def implicit_factory(self, path):
+        """
+        Turn a cache implicit dependency path into a node.
+        This is called so many times that doing caching
+        here is a significant performance boost.
+        """
+        try:
+            return self.implicit_factory_cache[path]
+        except KeyError:
+            n = self.builder.source_factory(path)
+            self.implicit_factory_cache[path] = n
+            return n
 
     def get_source_scanner(self, node):
         """Fetch the source scanner for the specified node
 
         NOTE:  "self" is the target being built, "node" is
         the source file for which we want to fetch the scanner.
-
-        Implies self.has_builder() is true; again, expect to only be
-        called from locations where this is already verified.
-
-        This function may be called very often; it attempts to cache
-        the scanner found to improve performance.
         """
-        scanner = None
+        if self.source_scanner:
+            return self.source_scanner
         try:
             scanner = self.builder.source_scanner
+            if scanner:
+                return scanner
         except AttributeError:
             pass
-        if not scanner:
-            # The builder didn't have an explicit scanner, so go look up
-            # a scanner from env['SCANNERS'] based on the node's scanner
-            # key (usually the file extension).
-            scanner = self.get_scanner(self.get_build_env())
-        if scanner:
-            scanner = scanner.select(node)
-        return scanner
-
-    def add_to_implicit(self, deps):
-        if not hasattr(self, 'implicit') or self.implicit is None:
-            self.implicit = []
-            self.implicit_dict = {}
-            self._children_reset()
-        self._add_child(self.implicit, self.implicit_dict, deps)
+        return node.backup_source_scanner or None
 
     def scan(self):
         """Scan this node's dependents for implicit dependencies."""
@@ -514,44 +439,34 @@ class Node:
         # Here's where we implement --implicit-cache.
         if implicit_cache and not implicit_deps_changed:
             implicit = self.get_stored_implicit()
-            if implicit:
-                factory = build_env.get_factory(self.builder.source_factory)
-                nodes = []
-                for i in implicit:
-                    try:
-                        n = factory(i)
-                    except TypeError:
-                        # The implicit dependency was cached as one type
-                        # of Node last time, but the configuration has
-                        # changed (probably) and it's a different type
-                        # this time.  Just ignore the mismatch and go
-                        # with what our current configuration says the
-                        # Node is.
-                        pass
-                    else:
-                        nodes.append(n)
-                self._add_child(self.implicit, self.implicit_dict, nodes)
+            if implicit is not None:
+                implicit = map(self.implicit_factory, implicit)
+                self._add_child(self.implicit, self.implicit_dict, implicit)
                 calc = build_env.get_calculator()
-                if implicit_deps_unchanged or self.current(calc):
+                if implicit_deps_unchanged or self.current(calc, scan=0):
                     return
-                # one of this node's sources has changed, so
-                # we need to recalculate the implicit deps,
-                # and the bsig:
-                self.implicit = []
-                self.implicit_dict = {}
-                self._children_reset()
-                self.del_binfo()
-
-        executor = self.get_executor()
-
-        # Have the executor scan the sources.
-        executor.scan_sources(self.builder.source_scanner)
+                else:
+                    # one of this node's sources has changed, so
+                    # we need to recalculate the implicit deps,
+                    # and the bsig:
+                    self.implicit = []
+                    self.implicit_dict = {}
+                    self._children_reset()
+                    self.del_binfo()
+
+        for child in self.children(scan=0):
+            scanner = self.get_source_scanner(child)
+            if scanner:
+                deps = child.get_implicit_deps(build_env, scanner, self)
+                self._add_child(self.implicit, self.implicit_dict, deps)
+
+        # scan this node itself for implicit dependencies
+        deps = self.get_implicit_deps(build_env, self.target_scanner, self)
+        self._add_child(self.implicit, self.implicit_dict, deps)
 
-        # If there's a target scanner, have the executor scan the target
-        # node itself and associated targets that might be built.
-        scanner = self.builder.target_scanner
-        if scanner:
-            executor.scan_targets(scanner)
+        # XXX See note above re: --implicit-cache.
+        #if implicit_cache:
+        #    self.store_implicit()
 
     def scanner_key(self):
         return None
@@ -561,10 +476,6 @@ class Node:
             return
         self.env = env
 
-    #
-    # SIGNATURE SUBSYSTEM
-    #
-
     def calculator(self):
         import SCons.Defaults
         
@@ -574,42 +485,46 @@ class Node:
     def calc_signature(self, calc=None):
         """
         Select and calculate the appropriate build signature for a node.
-        __cacheable__
 
         self - the node
         calc - the signature calculation module
         returns - the signature
         """
-        if self.is_derived():
-            import SCons.Defaults
-
-            env = self.env or SCons.Defaults.DefaultEnvironment()
-            if env.use_build_signature():
-                return self.get_bsig(calc)
-        elif not self.rexists():
-            return None
-        return self.get_csig(calc)
-
-    def new_ninfo(self):
-        return NodeInfo()
-
-    def new_binfo(self):
-        return BuildInfo(self)
-
-    def get_binfo(self):
         try:
-            return self.binfo
+            return self._calculated_sig
         except AttributeError:
-            self.binfo = self.new_binfo()
-            return self.binfo
+            if self.is_derived():
+                import SCons.Defaults
+                
+                env = self.env or SCons.Defaults.DefaultEnvironment()
+                if env.use_build_signature():
+                    sig = self.calc_bsig(calc)
+                else:
+                    sig = self.calc_csig(calc)
+            elif not self.rexists():
+                sig = None
+            else:
+                sig = self.calc_csig(calc)
+            self._calculated_sig = sig
+            return sig
+
+    def new_binfo(self):
+        return BuildInfo()
 
     def del_binfo(self):
-        """Delete the build info from this node."""
+        """Delete the bsig from this node."""
         try:
             delattr(self, 'binfo')
         except AttributeError:
             pass
 
+    def calc_bsig(self, calc=None):
+        try:
+            return self.binfo.bsig
+        except AttributeError:
+            self.binfo = self.gen_binfo(calc)
+            return self.binfo.bsig
+
     def gen_binfo(self, calc=None, scan=1):
         """
         Generate a node's build signature, the digested signatures
@@ -623,70 +538,68 @@ class Node:
         node's children's signatures.  We expect that they're
         already built and updated by someone else, if that's
         what's wanted.
-        __cacheable__
         """
 
         if calc is None:
             calc = self.calculator()
 
-        binfo = self.get_binfo()
+        binfo = self.new_binfo()
 
         if scan:
             self.scan()
 
-        executor = self.get_executor()
+        sources = self.filter_ignore(self.sources)
+        depends = self.filter_ignore(self.depends)
+        if self.implicit is None:
+            implicit = []
+        else:
+            implicit = self.filter_ignore(self.implicit)
+
         def calc_signature(node, calc=calc):
             return node.calc_signature(calc)
-
-        sources = executor.process_sources(None, self.ignore)
-        sourcesigs = executor.process_sources(calc_signature, self.ignore)
-
-        depends = self.depends
-        implicit = self.implicit or []
-
-        if self.ignore:
-            depends = filter(self.do_not_ignore, depends)
-            implicit = filter(self.do_not_ignore, implicit)
-
+        sourcesigs = map(calc_signature, sources)
         dependsigs = map(calc_signature, depends)
         implicitsigs = map(calc_signature, implicit)
 
         sigs = sourcesigs + dependsigs + implicitsigs
 
         if self.has_builder():
-            binfo.bact = str(executor)
+            executor = self.get_executor()
+            binfo.bact = executor.strfunction()
             binfo.bactsig = calc.module.signature(executor)
             sigs.append(binfo.bactsig)
 
-        binfo.bsources = sources
-        binfo.bdepends = depends
-        binfo.bimplicit = implicit
+        binfo.bsources = map(str, sources)
+        binfo.bdepends = map(str, depends)
+        binfo.bimplicit = map(str, implicit)
 
         binfo.bsourcesigs = sourcesigs
         binfo.bdependsigs = dependsigs
         binfo.bimplicitsigs = implicitsigs
 
-        binfo.ninfo.bsig = calc.module.collect(filter(None, sigs))
+        binfo.bsig = calc.module.collect(filter(None, sigs))
 
         return binfo
 
-    def get_bsig(self, calc=None):
-        binfo = self.get_binfo()
+    def del_cinfo(self):
         try:
-            return binfo.ninfo.bsig
+            del self.binfo.csig
         except AttributeError:
-            self.binfo = self.gen_binfo(calc)
-            return self.binfo.ninfo.bsig
+            pass
 
-    def get_csig(self, calc=None):
-        binfo = self.get_binfo()
+    def calc_csig(self, calc=None):
         try:
-            return binfo.ninfo.csig
+            binfo = self.binfo
+        except AttributeError:
+            binfo = self.binfo = self.new_binfo()
+        try:
+            return binfo.csig
         except AttributeError:
             if calc is None:
                 calc = self.calculator()
-            csig = binfo.ninfo.csig = calc.module.signature(self)
-            return csig
+            binfo.csig = calc.module.signature(self)
+            self.store_info(binfo)
+            return binfo.csig
 
     def store_info(self, obj):
         """Make the build signature permanent (that is, store it in the
@@ -700,10 +613,6 @@ class Node:
         """Fetch the stored implicit dependencies"""
         return None
 
-    #
-    #
-    #
-
     def set_precious(self, precious = 1):
         """Set the Node's precious value."""
         self.precious = precious
@@ -721,24 +630,18 @@ class Node:
         """Does this node exist locally or in a repositiory?"""
         # There are no repositories by default:
         return self.exists()
-
-    def missing(self):
-        """__cacheable__"""
-        return not self.is_derived() and \
-               not self.is_pseudo_derived() and \
-               not self.linked and \
-               not self.rexists()
     
     def prepare(self):
         """Prepare for this Node to be created.
         The default implemenation checks that all children either exist
         or are derived.
         """
-        l = self.depends
-        if not self.implicit is None:
-            l = l + self.implicit
-        missing_sources = self.get_executor().get_missing_sources() \
-                          + filter(lambda c: c.missing(), l)
+        def missing(node):
+            return not node.is_derived() and \
+                   not node.is_pseudo_derived() and \
+                   not node.linked and \
+                   not node.rexists()
+        missing_sources = filter(missing, self.children())
         if missing_sources:
             desc = "Source `%s' not found, needed by target `%s'." % (missing_sources[0], self)
             raise SCons.Errors.StopError, desc
@@ -806,15 +709,33 @@ class Node:
             self.wkids.append(wkid)
 
     def _children_reset(self):
-        "__cache_reset__"
-        # We need to let the Executor clear out any calculated
-        # bsig info that it's cached so we can re-calculate it.
-        self.executor_cleanup()
+        try:
+            delattr(self, '_children')
+        except AttributeError:
+            pass
 
-    def do_not_ignore(self, node):
-        return node not in self.ignore
+    def filter_ignore(self, nodelist):
+        ignore = self.ignore
+        result = []
+        for node in nodelist:
+            if node not in ignore:
+                result.append(node)
+        return result
 
-    def _all_children_get(self):
+    def children(self, scan=1):
+        """Return a list of the node's direct children, minus those
+        that are ignored by this node."""
+        if scan:
+            self.scan()
+        try:
+            return self._children
+        except AttributeError:
+            c = self.all_children(scan=0)
+            self._children = self.filter_ignore(c)
+            return self._children
+
+    def all_children(self, scan=1):
+        """Return a list of all the node's direct children."""
         # The return list may contain duplicate Nodes, especially in
         # source trees where there are a lot of repeated #includes
         # of a tangle of .h files.  Profiling shows, however, that
@@ -832,31 +753,13 @@ class Node:
         # using dictionary keys, lose the order, and the only ordered
         # dictionary patterns I found all ended up using "not in"
         # internally anyway...)
+        if scan:
+            self.scan()
         if self.implicit is None:
             return self.sources + self.depends
         else:
             return self.sources + self.depends + self.implicit
 
-    def _children_get(self):
-        "__cacheable__"
-        children = self._all_children_get()
-        if self.ignore:
-            children = filter(self.do_not_ignore, children)
-        return children
-
-    def all_children(self, scan=1):
-        """Return a list of all the node's direct children."""
-        if scan:
-            self.scan()
-        return self._all_children_get()
-
-    def children(self, scan=1):
-        """Return a list of the node's direct children, minus those
-        that are ignored by this node."""
-        if scan:
-            self.scan()
-        return self._children_get()
-
     def set_state(self, state):
         self.state = state
 
@@ -876,8 +779,6 @@ class Node:
         rebind their current() method to this method."""
         # Allow the children to calculate their signatures.
         self.binfo = self.gen_binfo(calc)
-        if self.always_build:
-            return None
         state = 0
         for kid in self.children(None):
             s = kid.get_state()
@@ -890,6 +791,16 @@ class Node:
         the command interpreter literally."""
         return 1
 
+    def add_pre_action(self, act):
+        """Adds an Action performed on this Node only before
+        building it."""
+        self.pre_actions.append(act)
+
+    def add_post_action(self, act):
+        """Adds and Action performed on this Node only after
+        building it."""
+        self.post_actions.append(act)
+
     def render_include_tree(self):
         """
         Return a text representation, suitable for displaying to the
@@ -899,9 +810,8 @@ class Node:
             env = self.get_build_env()
             for s in self.sources:
                 scanner = self.get_source_scanner(s)
-                path = self.get_build_scanner_path(scanner)
-                def f(node, env=env, scanner=scanner, path=path):
-                    return node.get_found_includes(env, scanner, path)
+                def f(node, env=env, scanner=scanner, target=self):
+                    return node.get_found_includes(env, scanner, target)
                 return SCons.Util.render_tree(s, f, 1)
         else:
             return None
@@ -974,63 +884,53 @@ class Node:
                 result[k] = s
 
         try:
-            osig = {}
-            dictify(osig, old.bsources, old.bsourcesigs)
-            dictify(osig, old.bdepends, old.bdependsigs)
-            dictify(osig, old.bimplicit, old.bimplicitsigs)
+            old_bkids = old.bsources + old.bdepends + old.bimplicit
         except AttributeError:
             return "Cannot explain why `%s' is being rebuilt: No previous build information found\n" % self
 
-        new = self.get_binfo()
-
-        nsig = {}
-        dictify(nsig, new.bsources, new.bsourcesigs)
-        dictify(nsig, new.bdepends, new.bdependsigs)
-        dictify(nsig, new.bimplicit, new.bimplicitsigs)
-
-        old_bkids = old.bsources + old.bdepends + old.bimplicit
-        new_bkids = new.bsources + new.bdepends + new.bimplicit
+        osig = {}
+        dictify(osig, old.bsources, old.bsourcesigs)
+        dictify(osig, old.bdepends, old.bdependsigs)
+        dictify(osig, old.bimplicit, old.bimplicitsigs)
 
-        # The sources and dependencies we'll want to report are all stored
-        # as relative paths to this target's directory, but we want to
-        # report them relative to the top-level SConstruct directory,
-        # so we only print them after running them through this lambda
-        # to turn them into the right relative Node and then return
-        # its string.
-        stringify = lambda s, E=self.dir.Entry: str(E(s))
+        new_bsources = map(str, self.binfo.bsources)
+        new_bdepends = map(str, self.binfo.bdepends)
+        new_bimplicit = map(str, self.binfo.bimplicit)
 
-        lines = []
+        nsig = {}
+        dictify(nsig, new_bsources, self.binfo.bsourcesigs)
+        dictify(nsig, new_bdepends, self.binfo.bdependsigs)
+        dictify(nsig, new_bimplicit, self.binfo.bimplicitsigs)
 
-        removed = filter(lambda x, nk=new_bkids: not x in nk, old_bkids)
-        if removed:
-            removed = map(stringify, removed)
-            fmt = "`%s' is no longer a dependency\n"
-            lines.extend(map(lambda s, fmt=fmt: fmt % s, removed))
+        new_bkids = new_bsources + new_bdepends + new_bimplicit
+        lines = map(lambda x: "`%s' is no longer a dependency\n" % x,
+                    filter(lambda x, nk=new_bkids: not x in nk, old_bkids))
 
         for k in new_bkids:
             if not k in old_bkids:
-                lines.append("`%s' is a new dependency\n" % stringify(k))
+                lines.append("`%s' is a new dependency\n" % k)
             elif osig[k] != nsig[k]:
-                lines.append("`%s' changed\n" % stringify(k))
+                lines.append("`%s' changed\n" % k)
 
         if len(lines) == 0 and old_bkids != new_bkids:
             lines.append("the dependency order changed:\n" +
-                         "%sold: %s\n" % (' '*15, map(stringify, old_bkids)) +
-                         "%snew: %s\n" % (' '*15, map(stringify, new_bkids)))
+                         "%sold: %s\n" % (' '*15, old_bkids) +
+                         "%snew: %s\n" % (' '*15, new_bkids))
 
         if len(lines) == 0:
+            newact, newactsig = self.binfo.bact, self.binfo.bactsig
             def fmt_with_title(title, strlines):
                 lines = string.split(strlines, '\n')
                 sep = '\n' + ' '*(15 + len(title))
                 return ' '*15 + title + string.join(lines, sep) + '\n'
-            if old.bactsig != new.bactsig:
-                if old.bact == new.bact:
+            if old.bactsig != newactsig:
+                if old.bact == newact:
                     lines.append("the contents of the build action changed\n" +
-                                 fmt_with_title('action: ', new.bact))
+                                 fmt_with_title('action: ', newact))
                 else:
                     lines.append("the build action changed:\n" +
                                  fmt_with_title('old: ', old.bact) +
-                                 fmt_with_title('new: ', new.bact))
+                                 fmt_with_title('new: ', newact))
 
         if len(lines) == 0:
             return "rebuilding `%s' for unknown reasons\n" % self
@@ -1042,28 +942,6 @@ class Node:
             lines = ["%s:\n" % preamble] + lines
             return string.join(lines, ' '*11)
 
-l = [1]
-ul = UserList.UserList([2])
-try:
-    l.extend(ul)
-except TypeError:
-    def NodeList(l):
-        return l
-else:
-    class NodeList(UserList.UserList):
-        def __str__(self):
-            return str(map(str, self.data))
-del l
-del ul
-
-if SCons.Memoize.use_old_memoization():
-    _Base = Node
-    class Node(SCons.Memoize.Memoizer, _Base):
-        def __init__(self, *args, **kw):
-            apply(_Base.__init__, (self,)+args, kw)
-            SCons.Memoize.Memoizer.__init__(self)
-
-
 def get_children(node, parent): return node.children()
 def ignore_cycle(node, stack): pass
 def do_nothing(node, parent): pass