Commits

Anonymous committed a055804

Merged revisions 3788-3853 via svnmerge from
http://scons.tigris.org/svn/scons/trunk

................
r3791 | garyo | 2008-11-18 17:29:38 -0800 (Tue, 18 Nov 2008) | 1 line

Update Users Guide to clarify usage of site_scons and site_init.py.
................
r3792 | GregNoel | 2008-11-19 15:51:58 -0800 (Wed, 19 Nov 2008) | 1 line

Add reference to LoadableModule in SharedLibrary description
................
r3793 | managan | 2008-11-25 10:32:19 -0800 (Tue, 25 Nov 2008) | 6 lines

Add a test for the case of a .DVI build using a .eps graphics.
Test that we don't make a .pdf file from the graphic.
The patch also covers the case of requiring the graphics files
I search for to have an extension so we don't try to build fig.eps from fig.eps...
................
r3794 | managan | 2008-11-25 10:37:22 -0800 (Tue, 25 Nov 2008) | 2 lines

Accidently left a debug print in.
................
r3802 | stevenknight | 2008-11-26 12:16:14 -0800 (Wed, 26 Nov 2008) | 2 lines

Highlights and roadmap update for checkpoing d20081125.
................
r3803 | stevenknight | 2008-12-02 10:58:29 -0800 (Tue, 02 Dec 2008) | 4 lines

Fix a Glob() exception (with stack trace) when an explicit Node
exists in a repository directory without a corresponding on-disk
file or directory.
................
r3804 | garyo | 2008-12-04 19:22:28 -0800 (Thu, 04 Dec 2008) | 1 line

Fix issue #3: make Append/PrependUnique uniquify the appended/prepended list first.
................
r3805 | stevenknight | 2008-12-05 00:16:58 -0800 (Fri, 05 Dec 2008) | 3 lines

Issue 2265: Add additional --taskmastertrace= messages in the Task class.
Refactor messages in the Taskmaster class to use new, common methods.
................
r3806 | stevenknight | 2008-12-05 06:14:58 -0800 (Fri, 05 Dec 2008) | 3 lines

Give proper credit to Benoit Belley for the --taskmastertrace=
enhancements in the previous commit.
................
r3807 | GregNoel | 2008-12-05 17:13:18 -0800 (Fri, 05 Dec 2008) | 1 line

Issue 2267, fix use of uninitialized variable
................
r3808 | stevenknight | 2008-12-06 09:42:03 -0800 (Sat, 06 Dec 2008) | 4 lines

Issue 2116: Eliminate some spurious dependency cycles by being more
aggressive about pruning pending children from the Taskmaster walk.
(Benoit Belley)
................
r3809 | stevenknight | 2008-12-06 20:15:22 -0800 (Sat, 06 Dec 2008) | 3 lines

Issue 2265: Suppress messages about spurious dependency cycles.
(Jason Kenny)
................
r3810 | stevenknight | 2008-12-07 08:31:39 -0800 (Sun, 07 Dec 2008) | 2 lines

src/CHANGES.txt updates for changes since 4 November.
................
r3811 | stevenknight | 2008-12-07 10:04:59 -0800 (Sun, 07 Dec 2008) | 3 lines

Make ${,UN}CHANGED_{SOURCES,TARGETS} into future reserved construction
variable names, with an appropriate warning.
................
r3817 | stevenknight | 2008-12-08 11:04:22 -0800 (Mon, 08 Dec 2008) | 3 lines

Create Taskmaster.{Always,OutOfDate}Task subclasses of Taskmaster.Task
to hold different implementations of the .needs_execute() method.
................
r3819 | stevenknight | 2008-12-09 23:54:38 -0800 (Tue, 09 Dec 2008) | 2 lines

Delete empty directories.
................
r3821 | stevenknight | 2008-12-10 06:25:18 -0800 (Wed, 10 Dec 2008) | 4 lines

Issue 1287: copy File attributes from the local Node to a Repository
Node so we identify shared object files in a Repository and can link
them into a local shared library. (Matthew Wesley)
................
r3822 | stevenknight | 2008-12-10 08:20:12 -0800 (Wed, 10 Dec 2008) | 2 lines

Print "scons: Build interrupted." on stderr, not stdout.
................
r3823 | garyo | 2008-12-10 18:59:40 -0800 (Wed, 10 Dec 2008) | 1 line

Fix issue #1249 by making Mkdir action (actually mkdir_func) succeed if the underlying mkdir throws EEXIST and there is an existing dir there already.
................
r3824 | garyo | 2008-12-10 19:12:25 -0800 (Wed, 10 Dec 2008) | 1 line

Added regression test for issue #1249
................
r3825 | garyo | 2008-12-10 19:16:12 -0800 (Wed, 10 Dec 2008) | 1 line

Fixed syntax and semantic errors in Mkdir example in man page, to fix issue #1755.
................
r3826 | stevenknight | 2008-12-10 21:27:02 -0800 (Wed, 10 Dec 2008) | 3 lines

Fix test breakage from the change to "scons: Build interrupted".
(Overlooked checking this in with r3822.)
................
r3827 | stevenknight | 2008-12-10 21:37:35 -0800 (Wed, 10 Dec 2008) | 6 lines

Issue 2231: Add a --warn=future-deprecated option, along with
FutureDeprecatedWarning and MandatoryDeprecatedWarning subclasses.
Use these to future-deprecate the Taskmaster.Task class, which we intend
to turn into an abstract base class by requiring subclasses to implement
the .needs_execute() method.
................
r3828 | stevenknight | 2008-12-11 05:42:43 -0800 (Thu, 11 Dec 2008) | 2 lines

Issue 2102: Add support for Microsoft Visual Studio 9. (Dmitri Rubinstein)
................
r3829 | stevenknight | 2008-12-11 22:16:31 -0800 (Thu, 11 Dec 2008) | 2 lines

Issue 2255: Handle scanning of UTF-8 and UTF-16 files. (Greg Spencer)
................
r3830 | stevenknight | 2008-12-12 06:32:49 -0800 (Fri, 12 Dec 2008) | 2 lines

Revert r3828 (vs9 support) in favor of the vs_revamp integration.
................
r3836 | stevenknight | 2008-12-20 06:01:33 -0800 (Sat, 20 Dec 2008) | 2 lines

Issue 2276: Fix use of codecs module in pre-2.3 Python versions.
................
r3837 | stevenknight | 2008-12-20 06:19:09 -0800 (Sat, 20 Dec 2008) | 2 lines

Issue 2247: Don't fail if we can't import a _subprocess module on Windows.
................
r3838 | stevenknight | 2008-12-20 08:15:01 -0800 (Sat, 20 Dec 2008) | 2 lines

Fix left-over deprecated use of the Options object.
................
r3839 | stevenknight | 2008-12-20 08:23:36 -0800 (Sat, 20 Dec 2008) | 3 lines

Add warnings for use of the (already) deprecated Options object
and its related functions.
................
r3846 | stevenknight | 2008-12-21 07:35:09 -0800 (Sun, 21 Dec 2008) | 2 lines

Updates for release 1.2.0.
................
r3848 | stevenknight | 2008-12-21 08:01:54 -0800 (Sun, 21 Dec 2008) | 36 lines

Merged revisions 3611-3763,3765-3794,3796-3811,3813-3839,3841-3847 via svnmerge from
http://scons.tigris.org/svn/scons/checkpoint

................
r3765 | stevenknight | 2008-11-04 07:57:03 -0800 (Tue, 04 Nov 2008) | 2 lines

Update release info for checkpoint.
................
r3796 | stevenknight | 2008-11-25 21:56:26 -0800 (Tue, 25 Nov 2008) | 2 lines

Updates for 20081125 checkpoint release.
................
r3797 | stevenknight | 2008-11-26 08:21:04 -0800 (Wed, 26 Nov 2008) | 2 lines

User's Guide updates for changes to error messages.
................
r3813 | stevenknight | 2008-12-07 19:13:17 -0800 (Sun, 07 Dec 2008) | 2 lines

Update lines for the 20081207 checkpoint release (candidate for 1.2.0).
................
r3814 | stevenknight | 2008-12-07 19:35:09 -0800 (Sun, 07 Dec 2008) | 2 lines

Update troubleshoot.xml for the changes to taskmastertrace output.
................
r3847 | stevenknight | 2008-12-21 07:57:50 -0800 (Sun, 21 Dec 2008) | 9 lines

Merged revisions 3610-3840,3842-3846 via svnmerge from
http://scons.tigris.org/svn/scons/release

........
r3842 | stevenknight | 2008-12-20 22:48:14 -0800 (Sat, 20 Dec 2008) | 2 lines

Update files for 1.2.0.
........
................
................
r3849 | stevenknight | 2008-12-21 19:26:03 -0800 (Sun, 21 Dec 2008) | 3 lines

Add a release note about the mandatory warnings (i.e. warnings
which cannot be disabled) in 1.3.0 for deprecated features.
................
r3850 | stevenknight | 2008-12-21 19:58:03 -0800 (Sun, 21 Dec 2008) | 3 lines

Add the Options object and *Option() function to the list of features
getting mandatory deprecation warnings in 1.3.0.
................
r3851 | stevenknight | 2008-12-21 22:19:38 -0800 (Sun, 21 Dec 2008) | 3 lines

Initialized merge tracking via "svnmerge" with revisions "1-3818" from
http://scons.tigris.org/svn/scons/branches/sgk_batch
................

  • Participants
  • Parent commits 7674edc
  • Branches sgk_PathList

Comments (0)

Files changed (77)

File QMTest/TestSCons.py

 # here provides some independent verification that what we packaged
 # conforms to what we expect.
 
-default_version = '1.1.0'
+default_version = '1.2.0'
 
 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
 
     --  (Optional.)  Install from a pre-packaged SCons package that
         does not require distutils:
 
-            Red Hat Linux       scons-1.1.0.noarch.rpm
+            Red Hat Linux       scons-1.2.0.noarch.rpm
 
             Debian GNU/Linux    use apt-get to get the official package
 
-            Windows             scons-1.1.0.win32.exe
+            Windows             scons-1.2.0.win32.exe
 
     --  (Recommended.)  Download the latest distutils package from the
         following URL:
 
 By default, the above commands will do the following:
 
-    --  Install the version-numbered "scons-1.1.0" and "sconsign-1.1.0"
+    --  Install the version-numbered "scons-1.2.0" and "sconsign-1.2.0"
         scripts in the default system script directory (/usr/bin or
         C:\Python*\Scripts, for example).  This can be disabled by
         specifying the "--no-version-script" option on the command
         making it the default on your system.
 
         On UNIX or Linux systems, you can have the "scons" and "sconsign"
-        scripts be hard links or symbolic links to the "scons-1.1.0" and
-        "sconsign-1.1.0" scripts by specifying the "--hardlink-scons" or
+        scripts be hard links or symbolic links to the "scons-1.2.0" and
+        "sconsign-1.2.0" scripts by specifying the "--hardlink-scons" or
         "--symlink-scons" options on the command line.
 
-    --  Install "scons-1.1.0.bat" and "scons.bat" wrapper scripts in the
+    --  Install "scons-1.2.0.bat" and "scons.bat" wrapper scripts in the
         Python prefix directory on Windows (C:\Python*, for example).
         This can be disabled by specifying the "--no-install-bat" option
         on the command line.
 
         On UNIX or Linux systems, the "--install-bat" option may be
-        specified to have "scons-1.1.0.bat" and "scons.bat" files installed
+        specified to have "scons-1.2.0.bat" and "scons.bat" files installed
         in the default system script directory, which is useful if you
         want to install SCons in a shared file system directory that can
         be used to execute SCons from both UNIX/Linux and Windows systems.
 
     --  Install the SCons build engine (a Python module) in an
         appropriate version-numbered SCons library directory
-        (/usr/lib/scons-1.1.0 or C:\Python*\scons-1.1.0, for example).
+        (/usr/lib/scons-1.2.0 or C:\Python*\scons-1.2.0, for example).
         See below for more options related to installing the build
         engine library.
 
 Depending on the utilities installed on your system, any or all of the
 following packages will be built:
 
-        build/dist/scons-1.1.0-1.noarch.rpm
-        build/dist/scons-1.1.0-1.src.rpm
-        build/dist/scons-1.1.0.linux-i686.tar.gz
-        build/dist/scons-1.1.0.tar.gz
-        build/dist/scons-1.1.0.win32.exe
-        build/dist/scons-1.1.0.zip
-        build/dist/scons-doc-1.1.0.tar.gz
-        build/dist/scons-local-1.1.0.tar.gz
-        build/dist/scons-local-1.1.0.zip
-        build/dist/scons-src-1.1.0.tar.gz
-        build/dist/scons-src-1.1.0.zip
-        build/dist/scons_1.1.0-1_all.deb
+        build/dist/scons-1.2.0-1.noarch.rpm
+        build/dist/scons-1.2.0-1.src.rpm
+        build/dist/scons-1.2.0.linux-i686.tar.gz
+        build/dist/scons-1.2.0.tar.gz
+        build/dist/scons-1.2.0.win32.exe
+        build/dist/scons-1.2.0.zip
+        build/dist/scons-doc-1.2.0.tar.gz
+        build/dist/scons-local-1.2.0.tar.gz
+        build/dist/scons-local-1.2.0.zip
+        build/dist/scons-src-1.2.0.tar.gz
+        build/dist/scons-src-1.2.0.zip
+        build/dist/scons_1.2.0-1_all.deb
 
 The SConstruct file is supposed to be smart enough to avoid trying to
 build packages for which you don't have the proper utilities installed.
 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008'
 
 # This gets inserted into the man pages to reflect the month of release.
-month_year = 'October 2008'
+month_year = 'December 2008'
 
 #
 # __COPYRIGHT__
 import tempfile
 
 project = 'scons'
-default_version = '1.1.0'
+default_version = '1.2.0'
 copyright = "Copyright (c) %s The SCons Foundation" % copyright_years
 
 SConsignFile()
     tfp.close()
 
 def soscan(node, env, path):
-    c = node.get_contents()
+    c = node.get_text_contents()
     return re.compile(r"^[\.']so\s+(\S+)", re.M).findall(c)
 
 soelimbuilder = Builder(action = Action(soelim),

File doc/SConscript

 def scanxml(node, env, target):
     includes = []
 
-    contents = node.get_contents()
+    contents = node.get_text_contents()
 
     includes.extend(entity_re.findall(contents))
 

File doc/man/scons.1

 
 .TP
 --warn=deprecated, --warn=no-deprecated
-Enables or disables all warnings about use of deprecated features.
+Enables or disables all warnings about use of
+currently deprecated features.
 These warnings are enabled by default.
+Note that the
+.b --warn=no-deprecated
+option does not disable warnings about absolutely all deprecated features.
+Warnings for some deprecated features that have already been through
+several releases with deprecation warnings
+may be mandatory for a release or two
+before they are officially no longer supported by SCons.
 Warnings for some specific deprecated features
 may be enabled or disabled individually;
 see below.
 which can yield unpredictable behavior with some compilers.
 
 .TP
+--warn=future-deprecated, --warn=no-future-deprecated
+Enables or disables warnings about features
+that will be deprecated in the future.
+These warnings are disabled by default.
+Enabling this warning is especially
+recommended for projects that redistribute
+SCons configurations for other users to build,
+so that the project can be warned as soon as possible
+about to-be-deprecated features
+that may require changes to the configuration.
+
+.TP
 --warn=link, --warn=no-link
 Enables or disables warnings about link steps.
 
 Execute(Mkdir('/tmp/outputdir'))
 
 env.Command('foo.out', 'foo.in',
-            [Mkdir('/tmp/builddir',
-             Copy('$SOURCE', '/tmp/builddir/foo.in')
+            [Mkdir('/tmp/builddir'),
+             Copy('/tmp/builddir/foo.in', '$SOURCE'),
              "cd /tmp/builddir && make",
-             Copy('/tmp/builddir/foo.out', '$TARGET')])
+             Copy('$TARGET', '/tmp/builddir/foo.out')])
 .EE
 
 .TP
 
 .ES
 def xyz_scan(node, env, path):
-    contents = node.get_contents()
+    contents = node.get_text_contents()
     # Scan the contents and return the included files.
 
 XYZScanner = Scanner(xyz_scan)
 include_re = re.compile(r'^include\\s+(\\S+)$', re.M)
 
 def kfile_scan(node, env, path, arg):
-    contents = node.get_contents()
+    contents = node.get_text_contents()
     includes = include_re.findall(contents)
     return includes
 
 include_re = re.compile(r'^include\\s+(\\S+)$', re.M)
 
 def my_scan(node, env, path, arg):
-   contents = node.get_contents()
+   contents = node.get_text_contents()
    includes = include_re.findall(contents)
    if includes == []:
         return []

File doc/user/README

 When adding a new file, add it to main.xml and MANIFEST.
 
 To build the .xml files from the .in files:
-  scons -D . BUILDDOC=1
+  scons -D BUILDDOC=1 foo.xml
 To build the whole PDF doc from this dir, for testing:
   scons -D ../../build/doc/PDF/scons-user.pdf 
 

File doc/user/builders-writing.in

     to include in &SConscript;s; just put that module in
     <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
     choice.  For instance you can do something like this in
-    <filename>site_scons/my_utils.py</filename> to add a build_id method:
+    <filename>site_scons/my_utils.py</filename> to add build_id and MakeWorkDir functions:
   </para>
     
   <scons_example name="site2">
     <file name="site_scons/my_utils.py" printme=1>
+      from SCons.Script import *   # for Execute and Mkdir
       def build_id():
          """Return a build ID (stub version)"""
          return "100"
+      def MakeWorkDir(workdir):
+         """Create the specified dir immediately"""
+         Execute(Mkdir(workdir))
     </file>
     <file name="SConscript">
       import my_utils
+      MakeWorkDir('/tmp/work')
       print "build_id=" + my_utils.build_id()
     </file>
   </scons_example>
   <sconstruct>
       import my_utils
       print "build_id=" + my_utils.build_id()
+      my_utils.MakeWorkDir('/tmp/work')
   </sconstruct>
 
   <para>
+    Note that although you can put this library in
+    <filename>site_scons/site_init.py</filename>,
+    it is no better there than <filename>site_scons/my_utils.py</filename>
+    since you still have to import that module into your &SConscript;.
+    Also note that in order to refer to objects in the SCons namespace
+    such as &Environment; or &Mkdir; or &Execute; in any file other
+    than a &SConstruct; or &SConscript; you always need to do
+  </para>
+  <sconstruct>
+      from SCons.Script import *
+  </sconstruct>
+
+  <para>
+    This is true in modules in <filename>site_scons</filename> such as
+    <filename>site_scons/site_init.py</filename> as well.
+  </para>
+
+  <para>
 
     If you have a machine-wide site dir you'd like to use instead of
     <filename>./site_scons</filename>, use the

File doc/user/builders-writing.xml

     to include in &SConscript;s; just put that module in
     <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
     choice.  For instance you can do something like this in
-    <filename>site_scons/my_utils.py</filename> to add a build_id method:
+    <filename>site_scons/my_utils.py</filename> to add build_id and MakeWorkDir functions:
   </para>
     
   <programlisting>
+      from SCons.Script import *   # for Execute and Mkdir
       def build_id():
          """Return a build ID (stub version)"""
          return "100"
+      def MakeWorkDir(workdir):
+         """Create the specified dir immediately"""
+         Execute(Mkdir(workdir))
   </programlisting>
 
   <para>
   <programlisting>
       import my_utils
       print "build_id=" + my_utils.build_id()
+      my_utils.MakeWorkDir('/tmp/work')
   </programlisting>
 
   <para>
+    Note that although you can put this library in
+    <filename>site_scons/site_init.py</filename>,
+    it is no better there than <filename>site_scons/my_utils.py</filename>
+    since you still have to import that module into your &SConscript;.
+    Also note that in order to refer to objects in the SCons namespace
+    such as &Environment; or &Mkdir; or &Execute; in any file other
+    than a &SConstruct; or &SConscript; you always need to do
+  </para>
+  <programlisting>
+      from SCons.Script import *
+  </programlisting>
+
+  <para>
+    This is true in modules in <filename>site_scons</filename> such as
+    <filename>site_scons/site_init.py</filename> as well.
+  </para>
+
+  <para>
 
     If you have a machine-wide site dir you'd like to use instead of
     <filename>./site_scons</filename>, use the

File doc/user/misc.xml

        % <userinput>scons -Q</userinput>
        None
        derived
-       scons: *** Source `leaf' not found, needed by target `derived'.  Stop.
+       scons: *** [derived] Source `leaf' not found, needed by target `derived'.
     </screen>
 
     <programlisting>

File doc/user/output.xml

           scons: `.' is up to date.
           Build succeeded.
           % <userinput>scons -Q fail=1</userinput>
-          scons: *** Source `source' not found, needed by target `target'.  Stop.
+          scons: *** [target] Source `source' not found, needed by target `target'.
           FAILED!!!!
-          Failed building Source `source' not found, needed by target `target'.
+          Failed building target: Source `source' not found, needed by target `target'.
     </screen>
 
   </section>

File doc/user/scanners.in

       include_re = re.compile(r'^include\s+(\S+)$', re.M)
       
       def kfile_scan(node, env, path, arg):
-          contents = node.get_contents()
+          contents = node.get_text_contents()
           return include_re.findall(contents)
     </programlisting>
 
       The path name to the file can be
       used by converting the node to a string
       using the <literal>str()</literal> function,
-      or an internal &SCons; <literal>get_contents()</literal>
+      or an internal &SCons; <literal>get_text_contents()</literal>
       object method can be used to fetch the contents.
 
       </para>
         include_re = re.compile(r'^include\s+(\S+)$', re.M)
 
         def kfile_scan(node, env, path):
-            contents = node.get_contents()
+            contents = node.get_text_contents()
             includes = include_re.findall(contents)
             return includes
 

File doc/user/scanners.xml

       include_re = re.compile(r'^include\s+(\S+)$', re.M)
       
       def kfile_scan(node, env, path, arg):
-          contents = node.get_contents()
+          contents = node.get_text_contents()
           return include_re.findall(contents)
     </programlisting>
 
       The path name to the file can be
       used by converting the node to a string
       using the <literal>str()</literal> function,
-      or an internal &SCons; <literal>get_contents()</literal>
+      or an internal &SCons; <literal>get_text_contents()</literal>
       object method can be used to fetch the contents.
 
       </para>
         include_re = re.compile(r'^include\s+(\S+)$', re.M)
 
         def kfile_scan(node, env, path):
-            contents = node.get_contents()
+            contents = node.get_text_contents()
             includes = include_re.findall(contents)
             return includes
 

File doc/user/troubleshoot.xml

 
     <screen>
       % <userinput>scons -Q</userinput>
-      scons: *** Source `prog.c' not found, needed by target `prog.o'.  Stop.
+      scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'.
     </screen>
 
     <para>
 
     <screen>
       % <userinput>scons -Q --debug=stacktrace</userinput>
-      scons: *** Source `prog.c' not found, needed by target `prog.o'.  Stop.
+      scons: *** [prog.o] Source `prog.c' not found, needed by target `prog.o'.
       scons: internal stack trace:
         File "bootstrap/src/engine/SCons/Job.py", line 197, in start
         File "bootstrap/src/engine/SCons/Script/Main.py", line 167, in prepare
-        File "bootstrap/src/engine/SCons/Taskmaster.py", line 182, in prepare
-        File "bootstrap/src/engine/SCons/Executor.py", line 171, in prepare
+        File "bootstrap/src/engine/SCons/Taskmaster.py", line 188, in prepare
+        File "bootstrap/src/engine/SCons/Executor.py", line 175, in prepare
     </screen>
 
     <para>
       Taskmaster: Looking for a node to evaluate
       Taskmaster:     Considering node &lt;no_state   0   'prog'&gt; and its children:
       Taskmaster:        &lt;no_state   0   'prog.o'&gt;
-      Taskmaster:      adjusting ref count: &lt;pending    1   'prog'&gt;
+      Taskmaster:      adjusted ref count: &lt;pending    1   'prog'&gt;, child 'prog.o'
       Taskmaster:     Considering node &lt;no_state   0   'prog.o'&gt; and its children:
       Taskmaster:        &lt;no_state   0   'prog.c'&gt;
       Taskmaster:        &lt;no_state   0   'inc.h'&gt;
-      Taskmaster:      adjusting ref count: &lt;pending    1   'prog.o'&gt;
-      Taskmaster:      adjusting ref count: &lt;pending    2   'prog.o'&gt;
+      Taskmaster:      adjusted ref count: &lt;pending    1   'prog.o'&gt;, child 'prog.c'
+      Taskmaster:      adjusted ref count: &lt;pending    2   'prog.o'&gt;, child 'inc.h'
       Taskmaster:     Considering node &lt;no_state   0   'prog.c'&gt; and its children:
       Taskmaster: Evaluating &lt;pending    0   'prog.c'&gt;
       
+      Task.make_ready_current(): node &lt;pending    0   'prog.c'&gt;
+      Task.prepare():      node &lt;up_to_date 0   'prog.c'&gt;
+      Task.executed_with_callbacks(): node &lt;up_to_date 0   'prog.c'&gt;
+      Task.postprocess():  node &lt;up_to_date 0   'prog.c'&gt;
+      Task.postprocess():  removing &lt;up_to_date 0   'prog.c'&gt;
+      Task.postprocess():  adjusted parent ref count &lt;pending    1   'prog.o'&gt;
+      
       Taskmaster: Looking for a node to evaluate
       Taskmaster:     Considering node &lt;no_state   0   'inc.h'&gt; and its children:
       Taskmaster: Evaluating &lt;pending    0   'inc.h'&gt;
       
+      Task.make_ready_current(): node &lt;pending    0   'inc.h'&gt;
+      Task.prepare():      node &lt;up_to_date 0   'inc.h'&gt;
+      Task.executed_with_callbacks(): node &lt;up_to_date 0   'inc.h'&gt;
+      Task.postprocess():  node &lt;up_to_date 0   'inc.h'&gt;
+      Task.postprocess():  removing &lt;up_to_date 0   'inc.h'&gt;
+      Task.postprocess():  adjusted parent ref count &lt;pending    0   'prog.o'&gt;
+      
       Taskmaster: Looking for a node to evaluate
       Taskmaster:     Considering node &lt;pending    0   'prog.o'&gt; and its children:
       Taskmaster:        &lt;up_to_date 0   'prog.c'&gt;
       Taskmaster:        &lt;up_to_date 0   'inc.h'&gt;
       Taskmaster: Evaluating &lt;pending    0   'prog.o'&gt;
+      
+      Task.make_ready_current(): node &lt;pending    0   'prog.o'&gt;
+      Task.prepare():      node &lt;executing  0   'prog.o'&gt;
+      Task.execute():      node &lt;executing  0   'prog.o'&gt;
       cc -o prog.o -c -I. prog.c
+      Task.executed_with_callbacks(): node &lt;executing  0   'prog.o'&gt;
+      Task.postprocess():  node &lt;executed   0   'prog.o'&gt;
+      Task.postprocess():  removing &lt;executed   0   'prog.o'&gt;
+      Task.postprocess():  adjusted parent ref count &lt;pending    0   'prog'&gt;
       
       Taskmaster: Looking for a node to evaluate
       Taskmaster:     Considering node &lt;pending    0   'prog'&gt; and its children:
       Taskmaster:        &lt;executed   0   'prog.o'&gt;
       Taskmaster: Evaluating &lt;pending    0   'prog'&gt;
+      
+      Task.make_ready_current(): node &lt;pending    0   'prog'&gt;
+      Task.prepare():      node &lt;executing  0   'prog'&gt;
+      Task.execute():      node &lt;executing  0   'prog'&gt;
       cc -o prog prog.o
+      Task.executed_with_callbacks(): node &lt;executing  0   'prog'&gt;
+      Task.postprocess():  node &lt;executed   0   'prog'&gt;
       
       Taskmaster: Looking for a node to evaluate
       Taskmaster: No candidate anymore.

File src/CHANGES.txt

 
 RELEASE 1.X - XXX
 
+  From Steven Knight:
+
+    - Print the message, "scons: Build interrupted." on error output,
+      not standard output.
+
+    - Add a --warn=future-deprecated option for advance warnings about
+      deprecated features that still have warnings hidden by default.
+
+  From Greg Spencer:
+
+    - Support implicit dependency scanning of files encoded in utf-8
+      and utf-16.
+
+  From Matthew Wesley:
+
+    - Copy file attributes so we identify, and can link a shared library
+      from, shared object files in a Repository.
+
+
+
+RELEASE 1.2.0 - Sat, 20 Dec 2008 22:47:29 -0800
+
+  From Steven Knight:
+
+    - Don't fail if can't import a _subprocess module on Windows.
+
+    - Add warnings for use of the deprecated Options object.
+
+
+
+RELEASE 1.1.0.d20081207 - Sun, 07 Dec 2008 19:17:23 -0800
+
   From Benoit Belley:
 
     - Improve the robustness of GetBuildFailures() by refactoring
       SCons exception handling (especially BuildError exceptions).
 
+    - Have the --taskmastertrace= option print information about
+      individual Task methods, not just the Taskmaster control flow.
+
+    - Eliminate some spurious dependency cycles by being more aggressive
+      about pruning pending children from the Taskmaster walk.
+
   From David Cournapeau:
 
     - Fix $FORTRANMODDIRPREFIX for the ifort (Intel Fortran) tool.
     - Don't pre-generate an exception message (which will likely be
       ignored anyway) when an EntryProxy re-raises an AttributeError.
 
+  From Jared Grubb:
+
+    - Clean up coding style and white space in Node/FS.py.
+
+    - Fix a typo in the documentation for $_CPPDEFFLAGS.
+
   From Ludwig H�hne:
 
     - Handle Java inner classes declared within a method.
 
+  From Jason Kenny:
+
+    - Suppress mistaken reports of a dependency cycle when a child
+      left on the pending list is a single Node in EXECUTED state.
+
   From Steven Knight:
 
     - Fix label placement by the "scons-time.py func" subcommand
     - Add support for using the Python "in" keyword on construction
       environments (for example, if "CPPPATH" in env: ...).
 
+    - Fix use of Glob() when a repository or source directory contains
+      an in-memory Node without a corresponding on-disk file or directory.
+
+    - Add a warning about future reservation of $CHANGED_SOURCES,
+      $CHANGED_TARGETS, $UNCHANGED_SOURCES and $UNCHANGED_TARGETS.
+
+    - Enable by default the existing warnings about setting the resource
+      $SOURCE, $SOURCES, $TARGET and $TARGETS variable.
+
   From Rob Managan:
 
     - Scan for TeX files in the paths specified in the $TEXINPUTS
 
     - Recursive scan included input TeX files.
 
+    - Handle requiring searched-for TeX input graphics files to have
+      extensions (to avoid trying to build a .eps from itself, e.g.).
+
   From Greg Noel:
 
     - Make the Action() function handle positional parameters consistently.
 
+    - Clarify use of Configure.CheckType().
+
+    - Make the File.{Dir,Entry,File}() methods create their entries
+      relative to the calling File's directory, not the SConscript
+      directory.
+
+    - Use the Python os.devnull variable to discard error output when
+      looking for the $CC or $CXX version.
+
+    - Mention LoadableModule() in the SharedLibrary() documentation.
+
+  From Gary Oberbrunner:
+
+    - Update the User's Guide to clarify use of the site_scons/
+      directory and the site_init.py module.
+
+    - Make env.AppendUnique() and env.PrependUnique remove duplicates
+      within a passed-in list being added, too.
+
   From Randall Spangler:
 
     - Fix Glob() so an on-disk file or directory beginning with '#'

File src/RELEASE.txt

 
 
 
-RELEASE 1.1.0 - Thu, 09 Oct 2008 08:33:47 -0700
+RELEASE 1.2.0 - Sat, 20 Dec 2008 22:47:29 -0800
 
   Please consult the CHANGES.txt file for a list of specific changes
   since last release.
 
+  Please note the following important changes scheduled for the next
+  minor release (1.3.0):
+
+    --  DEPRECATED FEATURES WILL GENERATE MANDATORY WARNINGS IN 1.3.0
+
+        In keeping with our deprecation cycle, the following deprecated
+        features will still be supported in 1.3.0 but will generate
+        mandatory, non-disableable warnings:
+
+            --  Support for Python versions 1.5, 1.6, 2.0 and 2.1.
+            --  The overrides= keyword argument to the Builder() call.
+            --  The scanner= keyword argument to the Builder() call.
+            --  The BuildDir() function and env.BuildDir() method.
+            --  The env.Copy() method.
+            --  The SourceSignatures() function and
+                env.SourceSignatures() method.
+            --  The TargetSignatures() function and
+                env.TargetSignatures() method.
+            --  The Sig module (now an unnused stub).
+            --  The --debug=dtree, --debug=stree and --debug=tree options.
+            --  The --debug=nomemoizer option.
+            --  The Options object and the related BoolOption(),
+                EnumOption(), ListOption(), PackageOption() and
+                PathOption() functions.
+
+        The mandatory warnings will be issued in order to make sure
+        users of 1.3.0 notice *prior* to the release of SCons 2.0.0, that
+        these features will be removed.  In SCons 2.0.0 these features
+        will no longer work at all, and will instead generate specific
+        fatal errors when anyone tries to use them.
+
+  Please note the following important changes since release 1.1.0:
+
+    --  THE $CHANGED_SOURCES, $CHANGED_TARGETS, $UNCHANGED_SOURCES
+        AND $UNCHANGED_TARGETS VARIABLES WILL BECOME RESERVED
+
+        A future release (probably 1.3.0) will make the construction
+        variable names $CHANGED_SOURCES, $CHANGED_TARGETS,
+        $UNCHANGED_SOURCES and $UNCHANGED_TARGETS into reserved
+        construction variable names controlled by SCons itself (like
+        the current $SOURCE, $TARGETS, etc.).
+
+        Setting these variable names in the current release will generate
+        a warning but still set the variables.  When they become reserved
+        variable names, they will generate a different warning message
+        and attempts to set these variables will be ignored.
+
+        SCons configurations that happen to use these variable names
+        should be changed to use different variable names, in order
+        to ensure that the configuration continues to work with future
+        versions of SCons.
+
+    --  THE Options OBJECT AND RELATED FUNCTIONS NOW GENERATE WARNINGS
+
+	Use of the Options object, and related functions BoolOption(),
+	EnumOption(), ListOption(), PackageOption() and PathOption()
+	were announced as deprecated in release 0.98.1.  Since then,
+	however, no warning messages were ever implemented for the
+        use of these deprecated functions.
+
+        By default, release 1.2.0 prints warning messages when these
+        deprecated features are used.  Warnings about all deprecated
+        features may be suppressed by using the --warn=no-deprecated
+        command-line option:
+
+            $ scons --warn=no-deprecated
+
+        Or by using the appropriate SetOption() call in any SConscript
+        file:
+
+            SetOption('warn', 'no-deprecated')
+
+        You may optionally disable just warnings about the deprecation
+        of the Options object and its related functions as follows:
+
+            SetOption('warn', 'no-deprecated-options')
+
+        The current plan is for these warnings to become mandatory
+        (non-suppressible) in release 1.3.0, and for the use of Options
+        and its related functions to generate errors in release 2.0.
+
   Please note the following important changes since release 0.98.4:
 
     --  scons.bat NOW RETURNS THE REAL SCONS EXIT STATUS

File src/engine/SCons/Action.py

             l = string.join(self.presub_lines(env), '\n  ')
             out = "Building %s with action:\n  %s\n" % (t, l)
             sys.stdout.write(out)
-        s = None
+        cmd = None
         if show and self.strfunction:
-            s = self.strfunction(target, source, env)
-            if s:
+            cmd = self.strfunction(target, source, env)
+            if cmd:
                 if chdir:
-                    s = ('os.chdir(%s)\n' % repr(chdir)) + s
+                    cmd = ('os.chdir(%s)\n' % repr(chdir)) + cmd
                 try:
                     get = env.get
                 except AttributeError:
                     print_func = get('PRINT_CMD_LINE_FUNC')
                     if not print_func:
                         print_func = self.print_cmd_line
-                print_func(s, target, source, env)
+                print_func(cmd, target, source, env)
         stat = 0
         if execute:
             if chdir:
             finally:
                 if save_cwd:
                     os.chdir(save_cwd)
-        if s and save_cwd:
+        if cmd and save_cwd:
             print_func('os.chdir(%s)' % repr(save_cwd), target, source, env)
 
         return stat

File src/engine/SCons/Defaults.py

 
 import os
 import os.path
+import errno
 import shutil
 import stat
 import string
     if not SCons.Util.is_List(dest):
         dest = [dest]
     for entry in dest:
-        os.makedirs(str(entry))
+        try:
+            os.makedirs(str(entry))
+        except os.error, e:
+            p = str(entry)
+            if e[0] == errno.EEXIST and os.path.isdir(str(entry)):
+                pass            # not an error if already exists
+            else:
+                raise
 
 Mkdir = ActionFactory(mkdir_func,
                       lambda dir: 'Mkdir(%s)' % get_paths_str(dir))

File src/engine/SCons/DefaultsTests.py

+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import os.path
+import string
+import StringIO
+import sys
+import types
+import unittest
+
+from UserDict import UserDict
+
+import TestCmd
+
+import SCons.Errors
+
+from SCons.Defaults import *
+
+class DefaultsTestCase(unittest.TestCase):
+    def test_mkdir_func0(self):
+        test = TestCmd.TestCmd(workdir = '')
+        test.subdir('sub')
+        subdir2 = test.workpath('sub', 'dir1', 'dir2')
+        # Simple smoke test
+        mkdir_func(subdir2)
+        mkdir_func(subdir2)     # 2nd time should be OK too
+
+    def test_mkdir_func1(self):
+        test = TestCmd.TestCmd(workdir = '')
+        test.subdir('sub')
+        subdir1 = test.workpath('sub', 'dir1')
+        subdir2 = test.workpath('sub', 'dir1', 'dir2')
+        # No error if asked to create existing dir
+        os.makedirs(subdir2)
+        mkdir_func(subdir2)
+        mkdir_func(subdir1)
+
+    def test_mkdir_func2(self):
+        test = TestCmd.TestCmd(workdir = '')
+        test.subdir('sub')
+        subdir1 = test.workpath('sub', 'dir1')
+        subdir2 = test.workpath('sub', 'dir1', 'dir2')
+        file = test.workpath('sub', 'dir1', 'dir2', 'file')
+
+        # make sure it does error if asked to create a dir
+        # where there's already a file
+        os.makedirs(subdir2)
+        test.write(file, "test\n")
+        try:
+            mkdir_func(file)
+        except os.error, e:
+            pass
+        else:
+            fail("expected os.error")
+        
+
+if __name__ == "__main__":
+    suite = unittest.TestSuite()
+    tclasses = [ DefaultsTestCase,
+               ]
+    for tclass in tclasses:
+        names = unittest.getTestCaseNames(tclass, 'test_')
+        suite.addTests(map(tclass, names))
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+        sys.exit(1)

File src/engine/SCons/Environment.py

         else:
             env.Tool(tool)
 
-# These names are controlled by SCons; users should never set or override
-# them.  This warning can optionally be turned off, but scons will still
-# ignore the illegal variable names even if it's off.
-reserved_construction_var_names = \
-    ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']
+# These names are (or will be) controlled by SCons; users should never
+# set or override them.  This warning can optionally be turned off,
+# but scons will still ignore the illegal variable names even if it's off.
+reserved_construction_var_names = [
+    'SOURCE',
+    'SOURCES',
+    'TARGET',
+    'TARGETS',
+]
+
+future_reserved_construction_var_names = [
+    'CHANGED_SOURCES',
+    'CHANGED_TARGETS',
+    'UNCHANGED_SOURCES',
+    'UNCHANGED_TARGETS',
+]
 
 def copy_non_reserved_keywords(dict):
     result = semi_deepcopy(dict)
     for k in result.keys():
         if k in reserved_construction_var_names:
-            SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning,
-                                "Ignoring attempt to set reserved variable `%s'" % k)
+            msg = "Ignoring attempt to set reserved variable `$%s'"
+            SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % k)
             del result[k]
     return result
 
 def _set_reserved(env, key, value):
-    msg = "Ignoring attempt to set reserved variable `%s'" % key
-    SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg)
+    msg = "Ignoring attempt to set reserved variable `$%s'"
+    SCons.Warnings.warn(SCons.Warnings.ReservedVariableWarning, msg % key)
+
+def _set_future_reserved(env, key, value):
+    env._dict[key] = value
+    msg = "`$%s' will be reserved in a future release and setting it will become ignored"
+    SCons.Warnings.warn(SCons.Warnings.FutureReservedVariableWarning, msg % key)
 
 def _set_BUILDERS(env, key, value):
     try:
     env._dict[key] = value
     env.scanner_map_delete()
 
+def _delete_duplicates(l, keep_last):
+    """Delete duplicates from a sequence, keeping the first or last."""
+    seen={}
+    result=[]
+    if keep_last:           # reverse in & out, then keep first
+        l.reverse()
+    for i in l:
+        try:
+            if not seen.has_key(i):
+                result.append(i)
+                seen[i]=1
+        except TypeError:
+            # probably unhashable.  Just keep it.
+            result.append(i)
+    if keep_last:
+        result.reverse()
+    return result
+
 
 
 # The following is partly based on code in a comment added by Peter
         self._special_set = {}
         for key in reserved_construction_var_names:
             self._special_set[key] = _set_reserved
+        for key in future_reserved_construction_var_names:
+            self._special_set[key] = _set_future_reserved
         self._special_set['BUILDERS'] = _set_BUILDERS
         self._special_set['SCANNERS'] = _set_SCANNERS
 
             self._dict[key] = value
 
     def get(self, key, default=None):
-        "Emulates the get() method of dictionaries."""
+        """Emulates the get() method of dictionaries."""
         return self._dict.get(key, default)
 
     def has_key(self, key):
         """
         kw = copy_non_reserved_keywords(kw)
         for key, val in kw.items():
+            if SCons.Util.is_List(val):
+                val = _delete_duplicates(val, delete_existing)
             if not self._dict.has_key(key) or self._dict[key] in ('', None):
                 self._dict[key] = val
             elif SCons.Util.is_Dict(self._dict[key]) and \
         """
         kw = copy_non_reserved_keywords(kw)
         for key, val in kw.items():
+            if SCons.Util.is_List(val):
+                val = _delete_duplicates(val, not delete_existing)
             if not self._dict.has_key(key) or self._dict[key] in ('', None):
                 self._dict[key] = val
             elif SCons.Util.is_Dict(self._dict[key]) and \

File src/engine/SCons/EnvironmentTests.py

         assert env.Dictionary('ENV')['PATH'] == '/foo:/bar'
 
     def test_ReservedVariables(self):
-        """Test generation of warnings when reserved variable names
-        are set in an environment."""
-
-        SCons.Warnings.enableWarningClass(SCons.Warnings.ReservedVariableWarning)
+        """Test warning generation when reserved variable names are set"""
+
+        reserved_variables = [
+            'SOURCE',
+            'SOURCES',
+            'TARGET',
+            'TARGETS',
+        ]
+
+        warning = SCons.Warnings.ReservedVariableWarning
+        SCons.Warnings.enableWarningClass(warning)
         old = SCons.Warnings.warningAsException(1)
 
         try:
             env4 = Environment()
-            for kw in ['TARGET', 'TARGETS', 'SOURCE', 'SOURCES']:
+            for kw in reserved_variables:
                 exc_caught = None
                 try:
                     env4[kw] = 'xyzzy'
-                except SCons.Warnings.ReservedVariableWarning:
+                except warning:
                     exc_caught = 1
                 assert exc_caught, "Did not catch ReservedVariableWarning for `%s'" % kw
                 assert not env4.has_key(kw), "`%s' variable was incorrectly set" % kw
         finally:
             SCons.Warnings.warningAsException(old)
 
+    def test_FutureReservedVariables(self):
+        """Test warning generation when future reserved variable names are set"""
+
+        future_reserved_variables = [
+            'CHANGED_SOURCES',
+            'CHANGED_TARGETS',
+            'UNCHANGED_SOURCES',
+            'UNCHANGED_TARGETS',
+        ]
+
+        warning = SCons.Warnings.FutureReservedVariableWarning
+        SCons.Warnings.enableWarningClass(warning)
+        old = SCons.Warnings.warningAsException(1)
+
+        try:
+            env4 = Environment()
+            for kw in future_reserved_variables:
+                exc_caught = None
+                try:
+                    env4[kw] = 'xyzzy'
+                except warning:
+                    exc_caught = 1
+                assert exc_caught, "Did not catch FutureReservedVariableWarning for `%s'" % kw
+                assert env4.has_key(kw), "`%s' variable was not set" % kw
+        finally:
+            SCons.Warnings.warningAsException(old)
+
     def test_IllegalVariables(self):
         """Test that use of illegal variables raises an exception"""
         env = Environment()
                           DDD1 = ['a', 'b', 'c'])
         env.AppendUnique(AAA1 = 'a1',
                          AAA2 = ['a2'],
-                         AAA3 = ['a3', 'b', 'c', 'a3'],
+                         AAA3 = ['a3', 'b', 'c', 'c', 'b', 'a3'], # ignore dups
                          AAA4 = 'a4.new',
                          AAA5 = ['a5.new'],
                          BBB1 = 'b1',
                          BBB2 = ['b2'],
-                         BBB3 = ['b3', 'c', 'd', 'b3'],
+                         BBB3 = ['b3', 'c', 'd', 'c', 'b3'],
                          BBB4 = 'b4.new',
                          BBB5 = ['b5.new'],
                          CCC1 = 'c1',
         assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # b moves to end
         env.AppendUnique(DDD1 = ['a','b'], delete_existing=1)
         assert env['DDD1'] == ['c', 'a', 'b'], env['DDD1'] # a & b move to end
+        env.AppendUnique(DDD1 = ['e','f', 'e'], delete_existing=1)
+        assert env['DDD1'] == ['c', 'a', 'b', 'f', 'e'], env['DDD1'] # add last
         
         env['CLVar'] = CLVar([])
         env.AppendUnique(CLVar = 'bar')
                           DDD1 = ['a', 'b', 'c'])
         env.PrependUnique(AAA1 = 'a1',
                           AAA2 = ['a2'],
-                          AAA3 = ['a3', 'b', 'c', 'a3'],
+                          AAA3 = ['a3', 'b', 'c', 'b', 'a3'], # ignore dups
                           AAA4 = 'a4.new',
                           AAA5 = ['a5.new'],
                           BBB1 = 'b1',
                           DDD1 = 'b')
         assert env['AAA1'] == 'a1a1', env['AAA1']
         assert env['AAA2'] == ['a2'], env['AAA2']
-        assert env['AAA3'] == ['b', 'c', 'a3'], env['AAA3']
+        assert env['AAA3'] == ['c', 'b', 'a3'], env['AAA3']
         assert env['AAA4'] == 'a4.newa4', env['AAA4']
         assert env['AAA5'] == ['a5.new', 'a5'], env['AAA5']
         assert env['BBB1'] == ['b1'], env['BBB1']
         assert env['DDD1'] == ['b', 'a', 'c'], env['DDD1'] # b moves to front
         env.PrependUnique(DDD1 = ['a','c'], delete_existing=1)
         assert env['DDD1'] == ['a', 'c', 'b'], env['DDD1'] # a & c move to front
+        env.PrependUnique(DDD1 = ['d','e','d'], delete_existing=1)
+        assert env['DDD1'] == ['d', 'e', 'a', 'c', 'b'], env['DDD1'] 
 
 
         env['CLVar'] = CLVar([])

File src/engine/SCons/JobTests.py

         for tnum in range(num_tasks):
             testnodes.append(node_seq[tnum % len(node_seq)]())
 
-        taskmaster = SCons.Taskmaster.Taskmaster(testnodes)
+        taskmaster = SCons.Taskmaster.Taskmaster(testnodes,
+                                                 tasker=SCons.Taskmaster.AlwaysTask)
+
         jobs = SCons.Job.Jobs(num_jobs, taskmaster)
 
         # Exceptions thrown by tasks are not actually propagated to

File src/engine/SCons/Node/FS.py

 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+from itertools import izip
+import cStringIO
 import fnmatch
-from itertools import izip
 import os
 import os.path
 import re
 import string
 import sys
 import time
-import cStringIO
+
+try:
+    import codecs
+except ImportError:
+    pass
+else:
+    # TODO(2.2):  Remove when 2.3 becomes the minimal supported version.
+    try:
+        codecs.BOM_UTF8
+    except AttributeError:
+        codecs.BOM_UTF8 = '\xef\xbb\xbf'
+    try:
+        codecs.BOM_UTF16
+    except AttributeError:
+        if sys.byteorder == 'little':
+            codecs.BOM_UTF16 = '\xff\xfe'
+        else:
+            codecs.BOM_UTF16 = '\xfe\xff'
 
 import SCons.Action
 from SCons.Debug import logInstanceCreation
         return self.get_suffix()
 
     def get_contents(self):
-        """Fetch the contents of the entry.
-
-        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."""
+        """Fetch the contents of the entry.  Returns the exact binary
+        contents of the file."""
         try:
             self = self.disambiguate(must_exist=1)
         except SCons.Errors.UserError:
         else:
             return self.get_contents()
 
+    def get_text_contents(self):
+        """Fetch the decoded text contents of a Unicode encoded Entry.
+
+        Since this should return the text contents from the file
+        system, we check to see into what sort of subclass we should
+        morph this Entry."""
+        try:
+            self = self.disambiguate(must_exist=1)
+        except SCons.Errors.UserError:
+            # There was nothing on disk with which to disambiguate
+            # this entry.  Leave it as an Entry, but return a null
+            # string so calls to get_text_contents() in emitters and
+            # the like (e.g. in qt.py) don't have to disambiguate by
+            # hand or catch the exception.
+            return ''
+        else:
+            return self.get_text_contents()
+
     def must_be_same(self, klass):
         """Called to make sure a Node is a Dir.  Since we're an
         Entry, we can morph into one."""
         """A directory does not get scanned."""
         return None
 
+    def get_text_contents(self):
+        """We already emit things in text, so just return the binary
+        version."""
+        return self.get_contents()
+
     def get_contents(self):
         """Return content signatures and names of all our children
         separated by new-lines. Ensure that the nodes are sorted."""
         contents = []
         name_cmp = lambda a, b: cmp(a.name, b.name)
         sorted_children = self.children()[:]
-        sorted_children.sort(name_cmp)        
+        sorted_children.sort(name_cmp)
         for node in sorted_children:
             contents.append('%s %s\n' % (node.get_csig(), node.name))
         return string.join(contents, '')
         for srcdir in self.srcdir_list():
             search_dir_list.extend(srcdir.get_all_rdirs())
 
+        selfEntry = self.Entry
         names = []
         for dir in search_dir_list:
             # We use the .name attribute from the Node because the keys of
             entry_names = filter(lambda n: n not in ('.', '..'), dir.entries.keys())
             node_names = map(lambda n, e=dir.entries: e[n].name, entry_names)
             names.extend(node_names)
+            if not strings:
+                # Make sure the working directory (self) actually has
+                # entries for all Nodes in repositories or variant dirs.
+                map(selfEntry, node_names)
             if ondisk:
                 try:
                     disk_names = os.listdir(dir.abspath)
                         disk_names = filter(lambda x: x[0] != '.', disk_names)
                     disk_names = fnmatch.filter(disk_names, pattern)
                     dirEntry = dir.Entry
-                    selfEntry = self.Entry
                     for name in disk_names:
                         # Add './' before disk filename so that '#' at
                         # beginning of filename isn't interpreted.
             return ''
         fname = self.rfile().abspath
         try:
-            r = open(fname, "rb").read()
+            contents = open(fname, "rb").read()
         except EnvironmentError, e:
             if not e.filename:
                 e.filename = fname
             raise
-        return r
+        return contents
+
+    try:
+        import codecs
+    except ImportError:
+        get_text_contents = get_contents
+    else:
+        # This attempts to figure out what the encoding of the text is
+        # based upon the BOM bytes, and then decodes the contents so that
+        # it's a valid python string.
+        def get_text_contents(self):
+            contents = self.get_contents()
+            if contents.startswith(codecs.BOM_UTF8):
+                contents = contents.decode('utf-8')
+            elif contents.startswith(codecs.BOM_UTF16):
+                contents = contents.decode('utf-16')
+            return contents
 
     def get_content_hash(self):
         """
                    (isinstance(node, File) or isinstance(node, Entry) \
                     or not node.is_derived()):
                         result = node
+                        # Copy over our local attributes to the repository
+                        # Node so we identify shared object files in the
+                        # repository and don't assume they're static.
+                        #
+                        # This isn't perfect; the attribute would ideally
+                        # be attached to the object in the repository in
+                        # case it was built statically in the repository
+                        # and we changed it to shared locally, but that's
+                        # rarely the case and would only occur if you
+                        # intentionally used the same suffix for both
+                        # shared and static objects anyway.  So this
+                        # should work well in practice.
+                        result.attributes = self.attributes
                         break
         self._memo['rfile'] = result
         return result

File src/engine/SCons/Node/FSTests.py

         f1 = fs.File(test.workpath("binary_file"))
         assert f1.get_contents() == "Foo\x1aBar", f1.get_contents()
 
+        try:
+            # TODO(1.5)
+            eval('test_string = u"Foo\x1aBar"')
+        except SyntaxError:
+            pass
+        else:
+            # This tests to make sure we can decode UTF-8 text files.
+            test.write("utf8_file", test_string.encode('utf-8'))
+            f1 = fs.File(test.workpath("utf8_file"))
+            assert eval('f1.get_text_contents() == u"Foo\x1aBar"'), \
+                   f1.get_text_contents()
+
         def nonexistent(method, s):
             try:
                 x = method(s, create = 0)
         finally:
             test.unlink("file")
 
+        # test Entry.get_text_contents()
+        e = fs.Entry('does_not_exist')
+        c = e.get_text_contents()
+        assert c == "", c
+        assert e.__class__ == SCons.Node.FS.Entry
+
+        test.write("file", "file\n")
+        try:
+            e = fs.Entry('file')
+            c = e.get_text_contents()
+            assert c == "file\n", c
+            assert e.__class__ == SCons.Node.FS.File
+        finally:
+            test.unlink("file")
+
         test.subdir("dir")
         e = fs.Entry('dir')
         c = e.get_contents()
         assert c == "", c
         assert e.__class__ == SCons.Node.FS.Dir
 
+        c = e.get_text_contents()
+        try:
+            eval('assert c == u"", c')
+        except SyntaxError:
+            assert c == ""
+
         if hasattr(os, 'symlink'):
             os.symlink('nonexistent', test.workpath('dangling_symlink'))
             e = fs.Entry('dangling_symlink')
             c = e.get_contents()
             assert e.__class__ == SCons.Node.FS.Entry, e.__class__
             assert c == "", c
+            c = e.get_text_contents()
+            try:
+                eval('assert c == u"", c')
+            except SyntaxError:
+                assert c == "", c
 
         test.write("tstamp", "tstamp\n")
         try:
         files = string.split(d.get_contents(), '\n')
 
         assert e.get_contents() == '', e.get_contents()
+        assert e.get_text_contents() == '', e.get_text_contents()
         assert e.get_csig()+" empty" == files[0], files
         assert f.get_csig()+" f" == files[1], files
         assert g.get_csig()+" g" == files[2], files
             r = apply(self.fs.Glob, (input,), kwargs)
             if node_expect:
                 r.sort(lambda a,b: cmp(a.path, b.path))
-                result = node_expect
+                result = []
+                for n in node_expect:
+                    if type(n) == type(''):
+                        n = self.fs.Entry(n)
+                    result.append(n)
+                fmt = lambda n: "%s %s" % (repr(n), repr(str(n)))
             else:
                 r = map(str, r)
                 r.sort()
                 result = string_expect
-            assert r == result, "Glob(%s) expected %s, got %s" % (input, map(str, result), map(str, r))
+                fmt = lambda n: n
+            if r != result:
+                import pprint
+                print "Glob(%s) expected:" % repr(input)
+                pprint.pprint(map(fmt, result))
+                print "Glob(%s) got:" % repr(input)
+                pprint.pprint(map(fmt, r))
+                self.fail()
 
     def test_exact_match(self):
         """Test globbing for exact Node matches"""
 
         self.do_cases(cases)
 
-    def test_asterisk(self):
-        """Test globbing for simple asterisk Node matches"""
+    def test_asterisk1(self):
+        """Test globbing for simple asterisk Node matches (1)"""
         cases = (
             ('h*',
              ['hhh'],
               'ggg', 'hhh', 'iii',
               'sub', 'subdir1', 'subdir2'],
              [self._both_hash, self._hash,
-              self.both_aaa, self.both_bbb, self.both_ccc,
+              self.both_aaa, self.both_bbb, self.both_ccc, 'both-hash',
               self.both_sub1, self.both_sub2,
-              self.ggg, self.hhh, self.iii,
+              self.ggg, 'hash', self.hhh, self.iii,
               self.sub, self.subdir1, self.subdir2]),
         )
 
         self.do_cases(cases, ondisk=False)
 
+    def test_asterisk2(self):
+        """Test globbing for simple asterisk Node matches (2)"""
         cases = (
             ('disk-b*',
              ['disk-bbb'],
               'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub',
               'ggg', 'hhh', 'iii',
               'sub', 'subdir1', 'subdir2'],
-             None),
+             ['./#both-hash', './#disk-hash', './#hash',
+              'both-aaa', 'both-bbb', 'both-ccc', 'both-hash',
+              'both-sub1', 'both-sub2',
+              'disk-aaa', 'disk-bbb', 'disk-ccc', 'disk-sub',
+              'ggg', 'hash', 'hhh', 'iii',
+              'sub', 'subdir1', 'subdir2']),
         )
 
         self.do_cases(cases)
         finally:
             test.unlink(["rep3", "contents"])
 
+    def test_get_text_contents(self):
+        """Ensure get_text_contents() returns text contents from
+        Repositories"""
+        fs = self.fs
+        test = self.test
+
+        # Use a test string that has a file terminator in it to make
+        # sure we read the entire file, regardless of its contents.
+        try:
+            eval('test_string = u"Con\x1aTents\n"')
+        except SyntaxError:
+            import UserString
+            class FakeUnicodeString(UserString.UserString):
+                def encode(self, encoding):
+                    return str(self)
+            test_string = FakeUnicodeString("Con\x1aTents\n")
+
+
+        # Test with ASCII.
+        test.write(["rep3", "contents"], test_string.encode('ascii'))
+        try:
+            c = fs.File("contents").get_text_contents()
+            assert test_string == c, "got %s" % repr(c)
+        finally:
+            test.unlink(["rep3", "contents"])
+
+        # Test with utf-8
+        test.write(["rep3", "contents"], test_string.encode('utf-8'))
+        try:
+            c = fs.File("contents").get_text_contents()
+            assert test_string == c, "got %s" % repr(c)
+        finally:
+            test.unlink(["rep3", "contents"])
+
+        # Test with utf-16
+        test.write(["rep3", "contents"], test_string.encode('utf-16'))
+        try:
+            c = fs.File("contents").get_text_contents()
+            assert test_string == c, "got %s" % repr(c)
+        finally:
+            test.unlink(["rep3", "contents"])
+
     #def test_is_up_to_date(self):
 
 

File src/engine/SCons/Options/BoolOption.py

 """
 
 import SCons.Variables
+import SCons.Warnings
 
-BoolOption = SCons.Variables.BoolVariable
+warned = False
+
+def BoolOption(*args, **kw):
+    global warned
+    if not warned:
+        msg = "The BoolOption() function is deprecated; use the BoolVariable() function instead."
+        SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+        warned = True
+    return apply(SCons.Variables.BoolVariable, args, kw)

File src/engine/SCons/Options/EnumOption.py

 """
 
 import SCons.Variables
+import SCons.Warnings
 
-EnumOption = SCons.Variables.EnumVariable
+warned = False
+
+def EnumOption(*args, **kw):
+    global warned
+    if not warned:
+        msg = "The EnumOption() function is deprecated; use the EnumVariable() function instead."
+        SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+        warned = True
+    return apply(SCons.Variables.EnumVariable, args, kw)

File src/engine/SCons/Options/ListOption.py

 """
 
 import SCons.Variables
+import SCons.Warnings
 
-ListOption = SCons.Variables.ListVariable
+warned = False
+
+def ListOption(*args, **kw):
+    global warned
+    if not warned:
+        msg = "The ListOption() function is deprecated; use the ListVariable() function instead."
+        SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+        warned = True
+    return apply(SCons.Variables.ListVariable, args, kw)

File src/engine/SCons/Options/PackageOption.py

 """
 
 import SCons.Variables
+import SCons.Warnings
 
-PackageOption = SCons.Variables.PackageVariable
+warned = False
+
+def PackageOption(*args, **kw):
+    global warned
+    if not warned:
+        msg = "The PackageOption() function is deprecated; use the PackageVariable() function instead."
+        SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+        warned = True
+    return apply(SCons.Variables.PackageVariable, args, kw)

File src/engine/SCons/Options/PathOption.py

 """
 
 import SCons.Variables
+import SCons.Warnings
 
-PathOption = SCons.Variables.PathVariable
+warned = False
+
+class _PathOptionClass:
+    def warn(self):
+        global warned
+        if not warned:
+            msg = "The PathOption() function is deprecated; use the PathVariable() function instead."
+            SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+            warned = True
+
+    def __call__(self, *args, **kw):
+        self.warn()
+        return apply(SCons.Variables.PathVariable, args, kw)
+
+    def PathAccept(self, *args, **kw):
+        self.warn()
+        return apply(SCons.Variables.PathVariable.PathAccept, args, kw)
+
+    def PathIsDir(self, *args, **kw):
+        self.warn()
+        return apply(SCons.Variables.PathVariable.PathIsDir, args, kw)
+
+    def PathIsDirCreate(self, *args, **kw):
+        self.warn()
+        return apply(SCons.Variables.PathVariable.PathIsDirCreate, args, kw)
+
+    def PathIsFile(self, *args, **kw):
+        self.warn()
+        return apply(SCons.Variables.PathVariable.PathIsFile, args, kw)
+
+    def PathExists(self, *args, **kw):
+        self.warn()
+        return apply(SCons.Variables.PathVariable.PathExists, args, kw)
+
+PathOption = _PathOptionClass()

File src/engine/SCons/Options/__init__.py

 """
 
 import SCons.Variables
+import SCons.Warnings
 
 from BoolOption import BoolOption  # okay
 from EnumOption import EnumOption  # okay
 from PackageOption import PackageOption # naja
 from PathOption import PathOption # okay
 
+warned = False
+
 class Options(SCons.Variables.Variables):
+    def __init__(self, *args, **kw):
+        global warned
+        if not warned:
+            msg = "The Options class is deprecated; use the Variables class instead."
+            SCons.Warnings.warn(SCons.Warnings.DeprecatedOptionsWarning, msg)
+            warned = True
+        apply(SCons.Variables.Variables.__init__,
+              (self,) + args,
+              kw)
 
     def AddOptions(self, *args, **kw):
         return apply(SCons.Variables.Variables.AddVariables,

File src/engine/SCons/SConf.py

         self.s.flush()
         
 
-class SConfBuildTask(SCons.Taskmaster.Task):
+class SConfBuildTask(SCons.Taskmaster.AlwaysTask):
     """
     This is almost the same as SCons.Script.BuildTask. Handles SConfErrors
     correctly and knows about the current cache_mode.

File src/engine/SCons/SConfTests.py

         self.scons_env[comp] = oldcomp
         self.scons_env['%sFLAGS' % comp] = 'qwertyuiop'
         r = func()
-        assert not r, "%s worked with %sFLAGS = qwertyuiop ?" % name
+        assert not r, "%s worked with %sFLAGS = qwertyuiop ?" % (name, comp)
 
     def test_CheckCC(self):
         """Test SConf.CheckCC()

File src/engine/SCons/Scanner/D.py

 
     def find_include_names(self, node):
         includes = []
-        for i in self.cre.findall(node.get_contents()):
+        for i in self.cre.findall(node.get_text_contents()):
             includes = includes + self.cre2.findall(i)
         return includes

File src/engine/SCons/Scanner/Fortran.py

             mods_and_includes = node.includes
         else:
             # retrieve all included filenames
-            includes = self.cre_incl.findall(node.get_contents())
+            includes = self.cre_incl.findall(node.get_text_contents())
             # retrieve all USE'd module names
-            modules = self.cre_use.findall(node.get_contents())
+            modules = self.cre_use.findall(node.get_text_contents())
             # retrieve all defined module names
-            defmodules = self.cre_def.findall(node.get_contents())
+            defmodules = self.cre_def.findall(node.get_text_contents())
 
             # Remove all USE'd module names that are defined in the same file
             d = {}

File src/engine/SCons/Scanner/LaTeX.py

         if node.includes != None:
             includes = node.includes
         else:
-            includes = self.cre.findall(node.get_contents())
+            includes = self.cre.findall(node.get_text_contents())
             # 1. Split comma-separated lines, e.g.
             #      ('bibliography', 'phys,comp')
             #    should become two entries

File src/engine/SCons/Scanner/ScannerTests.py

                 return self._exists
             def get_contents(self):
                 return self._contents
+            def get_text_contents(self):
+                return self._contents
             def get_dir(self):
                 return self._dir
 

File src/engine/SCons/Scanner/__init__.py

         return SCons.Node.FS._my_normcase(include)
 
     def find_include_names(self, node):
-        return self.cre.findall(node.get_contents())
+        return self.cre.findall(node.get_text_contents())
 
     def scan(self, node, path=()):
 

File src/engine/SCons/Script/Interactive.py

             node.set_state(SCons.Node.no_state)
             node.implicit = None
 
+            # Debug:  Uncomment to verify that all Taskmaster reference
+            # counts have been reset to zero.
+            #if node.ref_count != 0:
+            #    from SCons.Debug import Trace
+            #    Trace('node %s, ref_count %s !!!\n' % (node, node.ref_count))
+
         SCons.SConsign.Reset()
         SCons.Script.Main.progress_display("scons: done clearing node information.")
 

File src/engine/SCons/Script/Main.py

 def GetBuildFailures():
     return _BuildFailures
 
-class BuildTask(SCons.Taskmaster.Task):
+class BuildTask(SCons.Taskmaster.OutOfDateTask):
     """An SCons build task."""
     progress = ProgressObject
 
 
     def prepare(self):
         self.progress(self.targets[0])
-        return SCons.Taskmaster.Task.prepare(self)
+        return SCons.Taskmaster.OutOfDateTask.prepare(self)
 
     def needs_execute(self):
-        target = self.targets[0]
-        if target.get_state() == SCons.Node.executing:
+        if SCons.Taskmaster.OutOfDateTask.needs_execute(self):
             return True
-        else:
-            if self.top and target.has_builder():
-                display("scons: `%s' is up to date." % str(self.node))
-            return False
+        if self.top and self.targets[0].has_builder():
+            display("scons: `%s' is up to date." % str(self.node))
+        return False
 
     def execute(self):
         if print_time:
             global first_command_start
             if first_command_start is None:
                 first_command_start = start_time
-        SCons.Taskmaster.Task.execute(self)
+        SCons.Taskmaster.OutOfDateTask.execute(self)
         if print_time:
             global cumulative_command_time
             global last_command_end
         global exit_status
         global this_build_status
         if self.options.ignore_errors:
-            SCons.Taskmaster.Task.executed(self)
+            SCons.Taskmaster.OutOfDateTask.executed(self)
         elif self.options.keep_going:
-            SCons.Taskmaster.Task.fail_continue(self)
+            SCons.Taskmaster.OutOfDateTask.fail_continue(self)
             exit_status = status
             this_build_status = status
         else:
-            SCons.Taskmaster.Task.fail_stop(self)
+            SCons.Taskmaster.OutOfDateTask.fail_stop(self)
             exit_status = status
             this_build_status = status
             
                 self.do_failed()
             else:
                 print "scons: Nothing to be done for `%s'." % t
-                SCons.Taskmaster.Task.executed(self)
+                SCons.Taskmaster.OutOfDateTask.executed(self)
         else:
-            SCons.Taskmaster.Task.executed(self)
+            SCons.Taskmaster.OutOfDateTask.executed(self)
 
     def failed(self):
         # Handle the failure of a build task.  The primary purpose here
                 if tree:
                     print
                     print tree
-        SCons.Taskmaster.Task.postprocess(self)
+        SCons.Taskmaster.OutOfDateTask.postprocess(self)
 
     def make_ready(self):
         """Make a task ready for execution"""
-        SCons.Taskmaster.Task.make_ready(self)
+        SCons.Taskmaster.OutOfDateTask.make_ready(self)
         if self.out_of_date and self.options.debug_explain:
             explanation = self.out_of_date[0].explain()
             if explanation:
                 sys.stdout.write("scons: " + explanation)
 
-class CleanTask(SCons.Taskmaster.Task):
+class CleanTask(SCons.Taskmaster.AlwaysTask):
     """An SCons clean task."""
     def fs_delete(self, path, pathstr, remove=1):
         try:
     def prepare(self):
         pass
 
-class QuestionTask(SCons.Taskmaster.Task):
+class QuestionTask(SCons.Taskmaster.AlwaysTask):
     """An SCons task for the -q (question) option."""
     def prepare(self):
         pass
-    
+
     def execute(self):
         if self.targets[0].get_state() != SCons.Node.up_to_date or \
            (self.top and not self.targets[0].exists()):
     default_warnings = [ SCons.Warnings.CorruptSConsignWarning,
                          SCons.Warnings.DeprecatedWarning,
                          SCons.Warnings.DuplicateEnvironmentWarning,
+                         SCons.Warnings.FutureReservedVariableWarning,
                          SCons.Warnings.LinkWarning,
                          SCons.Warnings.MissingSConscriptWarning,
                          SCons.Warnings.NoMD5ModuleWarning,
                          SCons.Warnings.NoObjectCountWarning,
                          SCons.Warnings.NoParallelSupportWarning,
                          SCons.Warnings.MisleadingKeywordsWarning,
-                         SCons.Warnings.StackSizeWarning, ]
+                         SCons.Warnings.ReservedVariableWarning,