Anonymous avatar Anonymous committed e4b07e2

Merged revisions 1907-1940,1942-1967 via svnmerge from
http://scons.tigris.org/svn/scons/branches/core

........
r1914 | stevenknight | 2007-05-19 20:18:45 -0700 (Sat, 19 May 2007) | 4 lines

Use the dict() builtin, not a by-hand function, to assemble --debug=explain
info. Update our backwards-compatibility dict() function so its calling
signature matches the later SCons dict() builtin, and so it actually works.
........
r1920 | stevenknight | 2007-05-21 19:59:00 -0700 (Mon, 21 May 2007) | 4 lines

Issue 1652: only add the {base}_p.c and {base}_data.c files if the
/proxy or /dlldata arguments are present (respectively).
(Allan Erskine)
........
r1921 | stevenknight | 2007-05-21 20:09:54 -0700 (Mon, 21 May 2007) | 2 lines

Add initial .svnt/conf file for experimental use.
........
r1924 | stevenknight | 2007-05-22 17:51:34 -0700 (Tue, 22 May 2007) | 3 lines

Don't let tool initialization overwrite ${C,CXX}FILESUFFIX if they're
already set.
........
r1925 | stevenknight | 2007-05-22 19:14:27 -0700 (Tue, 22 May 2007) | 3 lines

Change the --debug=explain message when AlwaysBuild() is set from
the default "unknown reasons" to "because AlwaysBuild() is specified."
........
r1927 | stevenknight | 2007-05-23 05:20:32 -0700 (Wed, 23 May 2007) | 3 lines

Issue 1658: Find Java anonymous classes when the next token after
the name is an open parenthesis. (Jan Nijtmans)
........
r1928 | stevenknight | 2007-05-23 11:50:20 -0700 (Wed, 23 May 2007) | 3 lines

Issue 1313: add support for {Get,Set}Option('help') and
{Get,Set}Option('random') .
........
r1929 | stevenknight | 2007-05-23 11:56:18 -0700 (Wed, 23 May 2007) | 3 lines

Issue 1362: Push/retrieve built symlinks to/from a CacheDir() as symlinks,
not by copying file contents.
........
r1930 | stevenknight | 2007-05-23 12:55:12 -0700 (Wed, 23 May 2007) | 3 lines

Issue 1638: add LaTeX scanner support for finding dependencies from
\usepackage{} directives. (Sohail Somani)
........
r1932 | stevenknight | 2007-05-24 05:06:45 -0700 (Thu, 24 May 2007) | 3 lines

Add a section mentioning that libraries can be built from mixed lists
of source code and object files. (Follow-up from IRC chat.)
........
r1933 | stevenknight | 2007-05-24 11:18:30 -0700 (Thu, 24 May 2007) | 2 lines

Issue 1467: add /opt/SUNWspro/bin to the default execution $PATH on Solaris.
........
r1934 | stevenknight | 2007-05-24 13:10:01 -0700 (Thu, 24 May 2007) | 3 lines

Support running tests when Python is in a path that contains
spaces (like "C:\Program Files\Python...").
........
r1935 | stevenknight | 2007-05-24 13:11:52 -0700 (Thu, 24 May 2007) | 4 lines

Issue 1479: print multiple projects in a Visual Studio 7.[01] solution
file, generating a separate GUID for each instead of re-using the
solution GUID. (Mark Bertoglio)
........
r1937 | stevenknight | 2007-05-26 14:35:17 -0700 (Sat, 26 May 2007) | 4 lines

Issue 1659: when stringifying a generator action, fall back to using
the default construction environment, not an empty dictionary, if not
environment was supplied.
........
r1938 | stevenknight | 2007-05-29 05:14:37 -0700 (Tue, 29 May 2007) | 2 lines

Re-run tests if any of the test infrastructure changed, too.
........
r1940 | stevenknight | 2007-05-29 06:58:42 -0700 (Tue, 29 May 2007) | 2 lines

Issue 1634: "define HAVE_FEATURE 1" lines in generated config.h files.
........
r1943 | stevenknight | 2007-05-29 13:24:34 -0700 (Tue, 29 May 2007) | 2 lines

Issue 1426: Generalize AlwaysBuild() to non-File Nodes.
........
r1945 | stevenknight | 2007-05-30 08:15:25 -0700 (Wed, 30 May 2007) | 3 lines

Clean up emitter unit tests: subdivide one large test case into
separate tests, code renaming and reorganizing for readability.
........
r1947 | stevenknight | 2007-05-30 10:22:49 -0700 (Wed, 30 May 2007) | 4 lines

Issue 1656: two PDB fixes:
1) support expansion of $TARGET, etc. in emitted file names
2) put the output PDB file in the target's build_dir
........
r1951 | stevenknight | 2007-05-31 12:51:42 -0700 (Thu, 31 May 2007) | 3 lines

Refactor the checkLogAndStdout() function into a TestSCons method
so we can split the sub-tests into their own scripts.
........
r1952 | stevenknight | 2007-05-31 13:00:56 -0700 (Thu, 31 May 2007) | 3 lines

Also move the definition of what system library we need to use for
Configure tests into the TestSCons class.
........
r1953 | stevenknight | 2007-05-31 14:12:16 -0700 (Thu, 31 May 2007) | 2 lines

Refactor the Configure context subtests into separate scripts.
........
r1954 | stevenknight | 2007-05-31 14:18:11 -0700 (Thu, 31 May 2007) | 3 lines

Get rid of the work_dir argument to checkLogAndStdout(), since we're
no longer executing sub-tests from separate subdirectories.
........
r1955 | stevenknight | 2007-05-31 14:35:20 -0700 (Thu, 31 May 2007) | 2 lines

Move the test to its correct subdirectory. Oops.
........
r1958 | stevenknight | 2007-06-01 08:35:50 -0700 (Fri, 01 Jun 2007) | 2 lines

Skip the Visual C/C++ PDB + BuildDir test when not run on Windows.
........
r1959 | stevenknight | 2007-06-01 09:35:09 -0700 (Fri, 01 Jun 2007) | 2 lines

Support an AddMethod() global function and construction environment method.
........
r1960 | stevenknight | 2007-06-01 09:52:59 -0700 (Fri, 01 Jun 2007) | 3 lines

Python 1.5 compatibility in the test infrastructure added to support
the refactored Configure tests.
........
r1963 | stevenknight | 2007-06-01 12:29:48 -0700 (Fri, 01 Jun 2007) | 2 lines

Python 1.5 compatibility fix for AddMethod().
........

Comments (0)

Files changed (60)

+import os
+import sys
+
+python = os.environ.get('PYTHON', sys.executable)
+
+cmd = '"%(python)s" runtest.py -q --noqmtest %%s' % locals()
+
+test_inputs = [
+    'src/*.py',
+    'QMTest/*.py',
+]
+
+tests = (
+    ('src/*Tests.py', cmd),
+    ('test/*.py', cmd),
+)
+
+valid_regression_results = [0, 2]

QMTest/TestSCons.py

 
 import os
 import os.path
+import re
 import string
 import sys
 
+import __builtin__
+try:
+    __builtin__.zip
+except AttributeError:
+    def zip(*lists):
+        result = []
+        for i in xrange(len(lists[0])):
+            result.append(tuple(map(lambda l, i=i: l[i], lists)))
+        return result
+    __builtin__.zip = zip
+
 from TestCommon import *
 from TestCommon import __all__
 
                 return p
         return apply(os.path.join, [vs_path] + sub_paths[version][0])
 
+
+    NCR = 0 # non-cached rebuild
+    CR  = 1 # cached rebuild (up to date)
+    NCF = 2 # non-cached build failure
+    CF  = 3 # cached build failure
+
+    if sys.platform == 'win32':
+        Configure_lib = 'msvcrt'
+    else:
+        Configure_lib = 'm'
+
+    # to use cygwin compilers on cmd.exe -> uncomment following line
+    #Configure_lib = 'm'
+
+    def checkLogAndStdout(self, checks, results, cached,
+                          logfile, sconf_dir, sconstruct,
+                          doCheckLog=1, doCheckStdout=1):
+
+        class NoMatch:
+            def __init__(self, p):
+                self.pos = p
+
+        def matchPart(log, logfile, lastEnd):
+            m = re.match(log, logfile[lastEnd:])
+            if not m:
+                raise NoMatch, lastEnd
+            return m.end() + lastEnd
+        try:
+            #print len(os.linesep)
+            ls = os.linesep
+            nols = "("
+            for i in range(len(ls)):
+                nols = nols + "("
+                for j in range(i):
+                    nols = nols + ls[j]
+                nols = nols + "[^" + ls[i] + "])"
+                if i < len(ls)-1:
+                    nols = nols + "|"
+            nols = nols + ")"
+            lastEnd = 0
+            logfile = self.read(self.workpath(logfile))
+            if (doCheckLog and
+                string.find( logfile, "scons: warning: The stored build "
+                             "information has an unexpected class." ) >= 0):
+                self.fail_test()
+            sconf_dir = sconf_dir
+            sconstruct = sconstruct
+
+            log = r'file\ \S*%s\,line \d+:' % re.escape(sconstruct) + ls
+            if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
+            log = "\t" + re.escape("Configure(confdir = %s)" % sconf_dir) + ls
+            if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
+            rdstr = ""
+            cnt = 0
+            for check,result,cache_desc in zip(checks, results, cached):
+                log   = re.escape("scons: Configure: " + check) + ls
+                if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
+                log = ""
+                result_cached = 1
+                for bld_desc in cache_desc: # each TryXXX
+                    for ext, flag in bld_desc: # each file in TryBuild
+                        file = os.path.join(sconf_dir,"conftest_%d%s" % (cnt, ext))
+                        if flag == self.NCR:
+                            # rebuild will pass
+                            if ext in ['.c', '.cpp']:
+                                log=log + re.escape(file + " <-") + ls
+                                log=log + r"(  \|" + nols + "*" + ls + ")+?"
+                            else:
+                                log=log + "(" + nols + "*" + ls +")*?"
+                            result_cached = 0
+                        if flag == self.CR:
+                            # up to date
+                            log=log + \
+                                 re.escape("scons: Configure: \"%s\" is up to date." 
+                                           % file) + ls
+                            log=log+re.escape("scons: Configure: The original builder "
+                                              "output was:") + ls
+                            log=log+r"(  \|.*"+ls+")+"
+                        if flag == self.NCF:
+                            # non-cached rebuild failure
+                            log=log + "(" + nols + "*" + ls + ")*?"
+                            result_cached = 0
+                        if flag == self.CF:
+                            # cached rebuild failure
+                            log=log + \
+                                 re.escape("scons: Configure: Building \"%s\" failed "
+                                           "in a previous run and all its sources are"
+                                           " up to date." % file) + ls
+                            log=log+re.escape("scons: Configure: The original builder "
+                                              "output was:") + ls
+                            log=log+r"(  \|.*"+ls+")+"
+                    cnt = cnt + 1
+                if result_cached:
+                    result = "(cached) " + result
+                rdstr = rdstr + re.escape(check) + re.escape(result) + "\n"
+                log=log + re.escape("scons: Configure: " + result) + ls + ls
+                if doCheckLog: lastEnd = matchPart(log, logfile, lastEnd)
+                log = ""
+            if doCheckLog: lastEnd = matchPart(ls, logfile, lastEnd)
+            if doCheckLog and lastEnd != len(logfile):
+                raise NoMatch, lastEnd
+            
+        except NoMatch, m:
+            print "Cannot match log file against log regexp."
+            print "log file: "
+            print "------------------------------------------------------"
+            print logfile[m.pos:]
+            print "------------------------------------------------------"
+            print "log regexp: "
+            print "------------------------------------------------------"
+            print log
+            print "------------------------------------------------------"
+            self.fail_test()
+
+        if doCheckStdout:
+            exp_stdout = self.wrap_stdout(".*", rdstr)
+            if not self.match_re_dotall(self.stdout(), exp_stdout):
+                print "Unexpected stdout: "
+                print "-----------------------------------------------------"
+                print repr(self.stdout())
+                print "-----------------------------------------------------"
+                print repr(exp_stdout)
+                print "-----------------------------------------------------"
+                self.fail_test()
+
 # In some environments, $AR will generate a warning message to stderr
 # if the library doesn't previously exist and is being created.  One
 # way to fix this is to tell AR to be quiet (sometimes the 'c' flag),
 
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP 
+.RI AddMethod( object, function ", [" name ])
+.TP
+.RI env.AddMethod( function ", [" name ])
+When called with the
+.BR AddMethod ()
+form,
+adds the specified
+.I function
+to the specified
+.I object
+as the specified method
+.IR name .
+When called with the
+.BR env.AddMethod ()
+form,
+adds the specified
+.I function
+to the construction environment
+.I env
+as the specified method
+.IR name .
+In both cases, if
+.I name
+is omitted or
+.BR None ,
+the name of the
+specified
+.I function
+itself is used for the method name.
+
+.ES
+# Note that the first argument to the function to
+# be attached as a method must be the object through
+# which the method will be called; the Python
+# convention is to call it 'self'.
+def my_method(self, arg):
+    print "my_method() got", arg
+
+# Use the global AddMethod() function to add a method
+# to the Environment class.  This
+AddMethod(Environment, my_method)
+env = Environment()
+env.my_method('arg')
+
+# Add the function as a method, using the function
+# name for the method call.
+env = Environment()
+env.AddMethod(my_method, 'other_method_name')
+env.other_method_name('another arg')
+.EE
+
+'\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
+.TP 
 .RI AddPostAction( target ", " action )
 .TP
 .RI env.AddPostAction( target ", " action )
 This function provides a way to set a select subset of the scons command
 line options from a SConscript file. The options supported are:
 .B clean
-which corresponds to -c, --clean, and --remove;
+which corresponds to -c, --clean and --remove;
 .B duplicate
-which 
-corresponds to --duplicate;
+which corresponds to --duplicate;
+.B help
+which corresponds to -h and --help;
 .B implicit_cache
 which corresponds to --implicit-cache;
 .B max_drift
 which corresponds to --max-drift;
 .B num_jobs
 which corresponds to -j and --jobs.
+.B random
+which corresponds to --random.
 See the documentation for the
 corresponding command line object for information about each specific
 option. Example:

doc/user/caching.in

 
     </para>
 
+    <para>
+
+    If you want to make sure dependencies will be built
+    in a random order without having to specify
+    the <literal>--random</literal> on very command line,
+    you can use the &SetOption; function to
+    set the <literal>random</litera> option
+    within any &SConscript; file:
+
+    </para>
+
+    <scons_example name="ex-random">
+       <file name="SConstruct" printme="1">
+       SetOption('random', 1)
+       Program('prog',
+               ['f1.c', 'f2.c', 'f3.c', 'f4.c', 'f5.c'])
+       </file>
+       <file name="f1.c">f1.c</file>
+       <file name="f2.c">f2.c</file>
+       <file name="f3.c">f3.c</file>
+       <file name="f4.c">f4.c</file>
+       <file name="f5.c">f5.c</file>
+       <file name="f6.c">f6.c</file>
+    </scons_example>
+
   </section>
 
   <!--

doc/user/libraries.in

     </para>
 
     <section>
+    <title>Building Libraries From Source Code or Object Files</title>
+
+      <para>
+
+      The previous example shows building a library from a
+      list of source files.
+      You can, however, also give the &b-link-Library; call
+      object files,
+      and it will correctly realize
+      In fact, you can arbitrarily mix source code files
+      and object files in the source list:
+
+      </para>
+
+      <para>
+
+      <scons_example name="objects" printme="1">
+        <file name="SConstruct" printme="1">
+        Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
+        </file>
+        <file name="f1.c">
+        void f1() { printf("f1.c\n"); }
+        </file>
+        <file name="f2.o">
+        object file
+        </file>
+        <file name="f3.c">
+        void f3() { printf("f3.c\n"); }
+        </file>
+        <file name="f4.o">
+        object file
+        </file>
+      </scons_example>
+
+      <para>
+
+      And SCons realizes that only the source code files
+      must be compiled into object files
+      before creating the final library:
+
+      </para>
+
+      <scons_output example="objects" os="posix">
+        <scons_output_command>scons -Q</scons_output_command>
+      </scons_output>
+
+      <para>
+
+      Of course, in this example, the object files
+      must already exist for the build to succeed.
+      See <xref linkend="chap-nodes">, below,
+      for information about how you can
+      build object files explicitly
+      and include the built files in a library.
+
+      </para>
+
+    </section>
+
+    <section>
     <title>Building Static Libraries Explicitly:  the &b-StaticLibrary; Builder</title>
 
       <para>

doc/user/libraries.sgml

     </para>
 
     <section>
+    <title>Building Libraries From Source Code or Object Files</title>
+
+      <para>
+
+      The previous example shows building a library from a
+      list of source files.
+      You can, however, also give the &b-link-Library; call
+      object files,
+      and it will correctly realize
+      In fact, you can arbitrarily mix source code files
+      and object files in the source list:
+
+      </para>
+
+      <para>
+
+      <programlisting>
+        Library('foo', ['f1.c', 'f2.o', 'f3.c', 'f4.o'])
+      </programlisting>
+
+      <para>
+
+      And SCons realizes that only the source code files
+      must be compiled into object files
+      before creating the final library:
+
+      </para>
+
+      <screen>
+        % <userinput>scons -Q</userinput>
+        cc -o f1.o -c f1.c
+        cc -o f3.o -c f3.c
+        ar rc libfoo.a f1.o f2.o f3.o f4.o
+        ranlib libfoo.a
+      </screen>
+
+      <para>
+
+      Of course, in this example, the object files
+      must already exist for the build to succeed.
+      See <xref linkend="chap-nodes">, below,
+      for information about how you can
+      build object files explicitly
+      and include the built files in a library.
+
+      </para>
+
+    </section>
+
+    <section>
     <title>Building Static Libraries Explicitly:  the &b-StaticLibrary; Builder</title>
 
       <para>
   XXX SetOption('duplicate')
   XXX - - duplicate=
 
+  XXX GetOption('help')
+  XXX SetOption('help')
+
   XXX GetOption('num_jobs')
   XXX SetOption('num_jobs')
 
 
 
 
+RELEASE 0.97.X - XXX
+
+  From Mark Bertoglio:
+
+  - Fix listing multiple projects in Visual Studio 7.[01] solution files,
+    including generating individual project GUIDs instead of re-using
+    the solution GUID.
+
+  From Jean Brouwers:
+
+  - Add /opt/SUNWspro/bin to the default execution PATH on Solaris.
+
+  From Allan Erskine:
+
+  - Only expect the Microsoft IDL compiler to emit *_p.c and *_data.c
+    files if the /proxy and /dlldata switches are used (respectively).
+
+  From Steven Knight:
+
+  - Have --debug=explain report if a target is being rebuilt because
+    AlwaysBuild() is specified (instead of "unknown reasons").
+
+  - Support {Get,Set}Option('help') to make it easier for SConscript
+    files to tell if a help option (-h, --help, etc.) has been specified.
+
+  - Support {Get,Set}Option('random') so random-dependency interaction
+    with CacheDir() is controllable from SConscript files.
+
+  - Push and retrieve built symlinks to/from a CacheDir() as actual
+    symlinks, not by copying the file contents.
+
+  - Fix how the Action module handles stringifying the shared library
+    generator in the Tool/mingw.py module.
+
+  - When generating a config.h file, print "#define HAVE_{FEATURE} 1"
+    instad of just "#define HAVE_{FEATURE}", for more compatibility
+    with Autoconf-style projects.
+
+  - Fix expansion of $TARGET, $TARGETS, $SOURCE and $SOURCES keywords in
+    Visual C/C++ PDB file names.
+    
+  - Fix locating Visual C/C++ PDB files in build directories.
+
+  - Support an env.AddMethod() method and an AddMethod() global function
+    for adding a new method, respectively, to a construction environment
+    or an arbitrary object (such as a class).
+
+  From Leanid Nazdrynau:
+
+  - When applying Tool modules after a construction environment has
+    already been created, don't overwrite existing $CFILESUFFIX and
+    $CXXFILESUFFIX value.
+
+  From Jan Nijtmans:
+
+  - Find Java anonymous classes when the next token after the name is
+    an open parenthesis.
+
+  From Sohail Somani:
+
+  - Add LaTeX scanner support for finding dependencies specified with
+    the \usepackage{} directive.
+
+
+
 RELEASE 0.97 - Thu, 17 May 2007 08:59:41 -0500
 
   From Steven Knight:
   This is the eighth beta release of SCons.  Please consult the
   CHANGES.txt file for a list of specific changes since last release.
 
+  Please note the following important changes since release 0.97:
+
+    --  THE DEFAULT EXECUTION PATH FOR Solaris HAS CHANGED
+
+        On Solaris systems, SCons now adds the "/opt/SUNWspro/bin"
+        directory to the default execution $PATH variable before the
+        "/usr/ccs/bin" directory.  This was done to reflect the fact
+        that /opt/SUNWspro/ is the default for SUN tools, but it may
+        cause a different compiler to be used if you have compilers
+        installed in both directories.
+
+    --  GENERATED config.h FILES NOW SAY "#define HAVE_{FEATURE} 1"
+
+        When generating a "config.h" file, SCons now defines values that
+        record the existence of a feature with a "1" value:
+
+            #define HAVE_FEATURE 1
+
+        Instead of printing the line without a "1", as it used to:
+
+            #define HAVE_FEATURE
+
+        This should not cause any problems in the normal use of "#ifdef
+        HAVE_{FEATURE}" statements interpreted by a C preprocessor, but
+        might cause a compatibility issue if a script or other utility
+        was looking for an exact match of the previous text.
+
   Please note the following important changes since release 0.96.93:
 
     --  THE --debug=memoizer OPTION NOW REQUIRES PYTHON 2.2 OR LATER

src/engine/SCons/Action.py

 
     def __str__(self):
         try:
-            env = self.presub_env or {}
+            env = self.presub_env
         except AttributeError:
-            env = {}
+            env = None
+        if env is None:
+            env = SCons.Defaults.DefaultEnvironment()
         act = self._generate([], [], env, 1)
         return str(act)
 

src/engine/SCons/ActionTests.py

         """Test the pre-substitution strings for command generator Actions
         """
         def f(target, source, env, for_signature, self=self):
+
+            # See if "env" is really a construction environment (or
+            # looks like one) by accessing the FindIxes attribute.
+            # (The Tool/mingw.py module has a generator that uses this,
+            # and the __str__() method used to cause problems by passing
+            # us a regular dictionary as a fallback.)
+
+            env.FindIxes
             return "FOO"
         a = SCons.Action.CommandGeneratorAction(f)
         s = str(a)

src/engine/SCons/Builder.py

                     t.builder_set(self)
                     new_targets.append(t)
 
+            orig_tlist = tlist[:]
+            orig_slist = slist[:]
+
             target, source = self.emitter(target=tlist, source=slist, env=env)
 
             # Now delete the temporary builders that we attached to any
 
             # Have to call arg2nodes yet again, since it is legal for
             # emitters to spit out strings as well as Node instances.
-            tlist = env.arg2nodes(target, target_factory)
-            slist = env.arg2nodes(source, source_factory)
+            tlist = env.arg2nodes(target, target_factory,
+                                  target=orig_tlist, source=orig_slist)
+            slist = env.arg2nodes(source, source_factory,
+                                  target=orig_tlist, source=orig_slist)
 
         return tlist, slist
 

src/engine/SCons/BuilderTests.py

 import os.path
 import sys
 import types
+import StringIO
 import unittest
 import UserList
 
 import SCons.Environment
 import SCons.Errors
 
+sys.stdout = StringIO.StringIO()
+
 # Initial setup of the common environment for all tests,
 # a temporary working directory containing a
 # script for writing arguments to an output file.
                    source=None, dict=None, conv=None):
         return SCons.Util.scons_subst_list(string, self, raw, target,
                                            source, dict, conv)
-    def arg2nodes(self, args, factory):
+    def arg2nodes(self, args, factory, **kw):
         global env_arg2nodes_called
         env_arg2nodes_called = 1
         if not SCons.Util.is_List(args):
         assert 'baz' in map(str, tgt.sources), map(str, tgt.sources)
         assert 'bar' in map(str, tgt.sources), map(str, tgt.sources)
 
-        # Test that, if an emitter sets a builder on the passed-in
-        # targets and passes back new targets, the new builder doesn't
-        # get overwritten.
+    def test_emitter_preserve_builder(self):
+        """Test an emitter not overwriting a newly-set builder"""
+        env = Environment()
+
         new_builder = SCons.Builder.Builder(action='new')
         node = MyNode('foo8')
         new_node = MyNode('foo8.new')
-        def emit3(target, source, env, nb=new_builder, nn=new_node):
+
+        def emit(target, source, env, nb=new_builder, nn=new_node):
             for t in target:
                 t.builder = nb
             return [nn], source
             
-        builder3=SCons.Builder.Builder(action='foo',
-                                       emitter=emit3,
-                                       target_factory=MyNode,
-                                       source_factory=MyNode)
-        tgt = builder3(env, target=node, source='bar')[0]
+        builder=SCons.Builder.Builder(action='foo',
+                                      emitter=emit,
+                                      target_factory=MyNode,
+                                      source_factory=MyNode)
+        tgt = builder(env, target=node, source='bar')[0]
         assert tgt is new_node, tgt
-        assert tgt.builder is builder3, tgt.builder
+        assert tgt.builder is builder, tgt.builder
         assert node.builder is new_builder, node.builder
 
-        # Test use of a dictionary mapping file suffixes to
-        # emitter functions
+    def test_emitter_suffix_map(self):
+        """Test mapping file suffixes to emitter functions"""
+        env = Environment()
+
         def emit4a(target, source, env):
             source = map(str, source)
             target = map(lambda x: 'emit4a-' + x[:-3], source)
             source = map(str, source)
             target = map(lambda x: 'emit4b-' + x[:-3], source)
             return (target, source)
-        builder4 = SCons.Builder.Builder(action='foo',
-                                         emitter={'.4a':emit4a,
-                                                  '.4b':emit4b},
-                                         target_factory=MyNode,
-                                         source_factory=MyNode)
-        tgt = builder4(env, None, source='aaa.4a')[0]
+
+        builder = SCons.Builder.Builder(action='foo',
+                                        emitter={'.4a':emit4a,
+                                                 '.4b':emit4b},
+                                        target_factory=MyNode,
+                                        source_factory=MyNode)
+        tgt = builder(env, None, source='aaa.4a')[0]
         assert str(tgt) == 'emit4a-aaa', str(tgt)
-        tgt = builder4(env, None, source='bbb.4b')[0]
+        tgt = builder(env, None, source='bbb.4b')[0]
         assert str(tgt) == 'emit4b-bbb', str(tgt)
-        tgt = builder4(env, None, source='ccc.4c')[0]
+        tgt = builder(env, None, source='ccc.4c')[0]
         assert str(tgt) == 'ccc', str(tgt)
 
         def emit4c(target, source, env):
             source = map(str, source)
             target = map(lambda x: 'emit4c-' + x[:-3], source)
             return (target, source)
-        builder4.add_emitter('.4c', emit4c)
-        tgt = builder4(env, None, source='ccc.4c')[0]
+
+        builder.add_emitter('.4c', emit4c)
+        tgt = builder(env, None, source='ccc.4c')[0]
         assert str(tgt) == 'emit4c-ccc', str(tgt)
 
-        # Test a list of emitter functions.
-        def emit5a(target, source, env):
+    def test_emitter_function_list(self):
+        """Test lists of emitter functions"""
+        env = Environment()
+
+        def emit1a(target, source, env):
             source = map(str, source)
-            target = target + map(lambda x: 'emit5a-' + x[:-2], source)
+            target = target + map(lambda x: 'emit1a-' + x[:-2], source)
             return (target, source)
-        def emit5b(target, source, env):
+        def emit1b(target, source, env):
             source = map(str, source)
-            target = target + map(lambda x: 'emit5b-' + x[:-2], source)
+            target = target + map(lambda x: 'emit1b-' + x[:-2], source)
             return (target, source)
-        builder5 = SCons.Builder.Builder(action='foo',
-                                         emitter=[emit5a, emit5b],
+        builder1 = SCons.Builder.Builder(action='foo',
+                                         emitter=[emit1a, emit1b],
                                          node_factory=MyNode)
 
-        tgts = builder5(env, target='target-5', source='aaa.5')
+        tgts = builder1(env, target='target-1', source='aaa.1')
         tgts = map(str, tgts)
-        assert tgts == ['target-5', 'emit5a-aaa', 'emit5b-aaa'], tgts
+        assert tgts == ['target-1', 'emit1a-aaa', 'emit1b-aaa'], tgts
 
         # Test a list of emitter functions through the environment.
-        def emit6a(target, source, env):
+        def emit2a(target, source, env):
             source = map(str, source)
-            target = target + map(lambda x: 'emit6a-' + x[:-2], source)
+            target = target + map(lambda x: 'emit2a-' + x[:-2], source)
             return (target, source)
-        def emit6b(target, source, env):
+        def emit2b(target, source, env):
             source = map(str, source)
-            target = target + map(lambda x: 'emit6b-' + x[:-2], source)
+            target = target + map(lambda x: 'emit2b-' + x[:-2], source)
             return (target, source)
-        builder6 = SCons.Builder.Builder(action='foo',
+        builder2 = SCons.Builder.Builder(action='foo',
                                          emitter='$EMITTERLIST',
                                          node_factory=MyNode)
                                          
-        env = Environment(EMITTERLIST = [emit6a, emit6b])
+        env = Environment(EMITTERLIST = [emit2a, emit2b])
 
-        tgts = builder6(env, target='target-6', source='aaa.6')
+        tgts = builder2(env, target='target-2', source='aaa.2')
         tgts = map(str, tgts)
-        assert tgts == ['target-6', 'emit6a-aaa', 'emit6b-aaa'], tgts
+        assert tgts == ['target-2', 'emit2a-aaa', 'emit2b-aaa'], tgts
+
+    def test_emitter_TARGET_SOURCE(self):
+        """Test use of $TARGET and $SOURCE in emitter results"""
+
+        env = SCons.Environment.Environment()
+
+        def emit(target, source, env):
+            return (target + ['${SOURCE}.s1', '${TARGET}.t1'],
+                    source + ['${TARGET}.t2', '${SOURCE}.s2'])
+
+        builder = SCons.Builder.Builder(action='foo',
+                                        emitter = emit,
+                                        node_factory = MyNode)
+
+        targets = builder(env, target = 'TTT', source ='SSS')
+        sources = targets[0].sources
+        targets = map(str, targets)
+        sources = map(str, sources)
+        assert targets == ['TTT', 'SSS.s1', 'TTT.t1'], targets
+        assert sources == ['SSS', 'TTT.t2', 'SSS.s2'], targets
 
     def test_no_target(self):
         """Test deducing the target from the source."""

src/engine/SCons/Conftest.py

     key_up = re.sub('[^A-Z0-9_]', '_', key_up)
     context.havedict[key_up] = have
     if have == 1:
-        line = "#define %s\n" % key_up
+        line = "#define %s 1\n" % key_up
     elif have == 0:
         line = "/* #undef %s */\n" % key_up
     elif type(have) == IntType:

src/engine/SCons/Environment.py

     def items(self):
         return self._dict.items()
 
-    def arg2nodes(self, args, node_factory=_null, lookup_list=_null):
+    def arg2nodes(self, args, node_factory=_null, lookup_list=_null, **kw):
         if node_factory is _null:
             node_factory = self.fs.File
         if lookup_list is _null:
                         break
                 if not n is None:
                     if SCons.Util.is_String(n):
-                        n = self.subst(n, raw=1)
+                        # n = self.subst(n, raw=1, **kw)
+                        kw['raw'] = 1
+                        n = apply(self.subst, (n,), kw)
                         if node_factory:
                             n = node_factory(n)
                     if SCons.Util.is_List(n):
                     else:
                         nodes.append(n)
                 elif node_factory:
-                    v = node_factory(self.subst(v, raw=1))
+                    # v = node_factory(self.subst(v, raw=1, **kw))
+                    kw['raw'] = 1
+                    v = node_factory(apply(self.subst, (v,), kw))
                     if SCons.Util.is_List(v):
                         nodes.extend(v)
                     else:
             raise OSError("'%s' exited %d" % (command, status))
         return out
 
+    def AddMethod(self, function, name=None):
+        """
+        Adds the specified function as a method of this construction
+        environment with the specified name.  If the name is omitted,
+        the default name is the name of the function itself.
+        """
+        SCons.Util.AddMethod(self, function, name)
+
     def Override(self, overrides):
         """
         Produce a modified environment whose variables are overriden by
     def AlwaysBuild(self, *targets):
         tlist = []
         for t in targets:
-            tlist.extend(self.arg2nodes(t, self.fs.File))
+            tlist.extend(self.arg2nodes(t, self.fs.Entry))
         for t in tlist:
             t.set_always_build()
         return tlist

src/engine/SCons/EnvironmentTests.py

         assert not hasattr(nodes[1], 'bbbb'), nodes[0]
         assert nodes[1].c == 1, nodes[1]
 
+    def test_arg2nodes_target_source(self):
+        """Test the arg2nodes method with target= and source= keywords
+        """
+        targets = [DummyNode('t1'), DummyNode('t2')]
+        sources = [DummyNode('s1'), DummyNode('s2')]
+        env = SubstitutionEnvironment()
+        nodes = env.arg2nodes(['${TARGET}-a',
+                               '${SOURCE}-b',
+                               '${TARGETS[1]}-c',
+                               '${SOURCES[1]}-d'],
+                              DummyNode,
+                              target=targets,
+                              source=sources)
+        names = map(lambda n: n.name, nodes)
+        assert names == ['t1-a', 's1-b', 't2-c', 's2-d'], names
+
     def test_gvars(self):
         """Test the base class gvars() method"""
         env = SubstitutionEnvironment()
         finally:
             sys.stderr = save_stderr
 
+    def test_AddMethod(self):
+        """Test the AddMethod() method"""
+        env = SubstitutionEnvironment(FOO = 'foo')
+
+        def func(self):
+            return 'func-' + self['FOO']
+
+        assert not hasattr(env, 'func')
+        env.AddMethod(func)
+        r = env.func()
+        assert r == 'func-foo', r
+
+        assert not hasattr(env, 'bar')
+        env.AddMethod(func, 'bar')
+        r = env.bar()
+        assert r == 'func-foo', r
+
+        def func2(self, arg=''):
+            return 'func2-' + self['FOO'] + arg
+
+        env.AddMethod(func2)
+        r = env.func2()
+        assert r == 'func2-foo', r
+        r = env.func2('-xxx')
+        assert r == 'func2-foo-xxx', r
+
+        env.AddMethod(func2, 'func')
+        r = env.func()
+        assert r == 'func2-foo', r
+        r = env.func('-yyy')
+        assert r == 'func2-foo-yyy', r
+
     def test_Override(self):
         "Test overriding construction variables"
         env = SubstitutionEnvironment(ONE=1, TWO=2, THREE=3, FOUR=4)
     def test_AlwaysBuild(self):
         """Test the AlwaysBuild() method"""
         env = self.TestEnvironment(FOO='fff', BAR='bbb')
-        t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR')
-        assert t[0].__class__.__name__ == 'File'
+        t = env.AlwaysBuild('a', 'b$FOO', ['c', 'd'], '$BAR',
+                            env.fs.Dir('dir'), env.fs.File('file'))
+        assert t[0].__class__.__name__ == 'Entry'
         assert t[0].path == 'a'
         assert t[0].always_build
-        assert t[1].__class__.__name__ == 'File'
+        assert t[1].__class__.__name__ == 'Entry'
         assert t[1].path == 'bfff'
         assert t[1].always_build
-        assert t[2].__class__.__name__ == 'File'
+        assert t[2].__class__.__name__ == 'Entry'
         assert t[2].path == 'c'
         assert t[2].always_build
-        assert t[3].__class__.__name__ == 'File'
+        assert t[3].__class__.__name__ == 'Entry'
         assert t[3].path == 'd'
         assert t[3].always_build
-        assert t[4].__class__.__name__ == 'File'
+        assert t[4].__class__.__name__ == 'Entry'
         assert t[4].path == 'bbb'
         assert t[4].always_build
+        assert t[5].__class__.__name__ == 'Dir'
+        assert t[5].path == 'dir'
+        assert t[5].always_build
+        assert t[6].__class__.__name__ == 'File'
+        assert t[6].path == 'file'
+        assert t[6].always_build
 
     def test_BuildDir(self):
         """Test the BuildDir() method"""

src/engine/SCons/Node/FS.py

         return 1
     fs.CacheDebug('CacheRetrieve(%s):  retrieving from %s\n', t, cachefile)
     if SCons.Action.execute_actions:
-        fs.copy2(cachefile, t.path)
+        if fs.islink(cachefile):
+            fs.symlink(fs.readlink(cachefile), t.path)
+        else:
+            fs.copy2(cachefile, t.path)
         st = fs.stat(cachefile)
         fs.chmod(t.path, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
     return 0
 
     tempfile = cachefile+'.tmp'
     try:
-        fs.copy2(t.path, tempfile)
+        if fs.islink(t.path):
+            fs.symlink(fs.readlink(t.path), tempfile)
+        else:
+            fs.copy2(t.path, tempfile)
         fs.rename(tempfile, cachefile)
         st = fs.stat(t.path)
         fs.chmod(cachefile, stat.S_IMODE(st[stat.ST_MODE]) | stat.S_IWRITE)
         self.binfo = self.gen_binfo(calc)
         return self._cur2()
     def _cur2(self):
-        if self.always_build:
-            return None
         if not self.exists():
             # The file doesn't exist locally...
             r = self.rfile()

src/engine/SCons/Node/__init__.py

         if not self.exists():
             return "building `%s' because it doesn't exist\n" % self
 
+        if self.always_build:
+            return "rebuilding `%s' because AlwaysBuild() is specified\n" % self
+
         old = self.get_stored_info()
         if old is None:
             return None
         old.prepare_dependencies()
 
-        def dictify(result, kids, sigs):
-            for k, s in zip(kids, sigs):
-                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
+            old_bkidsigs = old.bsourcesigs + old.bdependsigs + old.bimplicitsigs
         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)
+        new_bkids    = new.bsources    + new.bdepends    + new.bimplicit
+        new_bkidsigs = new.bsourcesigs + new.bdependsigs + new.bimplicitsigs
 
-        old_bkids = old.bsources + old.bdepends + old.bimplicit
-        new_bkids = new.bsources + new.bdepends + new.bimplicit
+        osig = dict(zip(old_bkids, old_bkidsigs))
+        nsig = dict(zip(new_bkids, new_bkidsigs))
 
         # The sources and dependencies we'll want to report are all stored
         # as relative paths to this target's directory, but we want to

src/engine/SCons/Platform/sunos.py

     env['MAXLINELENGTH']  = 1045320
     env['PKGINFO'] = 'pkginfo'
     env['PKGCHK'] = '/usr/sbin/pkgchk'
-    env['ENV']['PATH'] = env['ENV']['PATH'] + ':/usr/ccs/bin'
+    env['ENV']['PATH'] = env['ENV']['PATH'] + ':/opt/SUNWspro/bin:/usr/ccs/bin'

src/engine/SCons/Scanner/LaTeX.py

     ds = LaTeX(name = "LaTeXScanner",
                suffixes =  '$LATEXSUFFIXES',
                path_variable = 'TEXINPUTS',
-               regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography){([^}]*)}',
+               regex = '\\\\(include|includegraphics(?:\[[^\]]+\])?|input|bibliography|usepackage){([^}]*)}',
                recursive = 0)
     return ds
 
             base, ext = os.path.splitext( filename )
             if ext == "":
                 filename = filename + '.bib'
+        if include[0] == 'usepackage':
+            base, ext = os.path.splitext( filename )
+            if ext == "":
+                filename = filename + '.sty'
         return filename
     def sort_key(self, include):
         return SCons.Node.FS._my_normcase(self.latex_name(include))

src/engine/SCons/Script/Main.py

 
 import os
 import os.path
-import random
 import string
 import sys
 import time
                         help="Read FILE as the top-level SConstruct file.")
 
         self.add_option('-h', '--help', action="store_true", default=0,
-                        dest="help_msg",
+                        dest="help",
                         help="Print defined help message, or this one.")
 
         self.add_option("-H", "--help-options",
 
         # This dictionary stores the defaults for all the SConscript
         # settable options, as well as indicating which options
-        # are SConscript settable. 
-        self.settable = {'num_jobs':1,
-                         'max_drift':SCons.Node.FS.default_max_drift,
-                         'implicit_cache':0,
-                         'clean':0,
-                         'duplicate':'hard-soft-copy',
-                         'diskcheck':diskcheck_all}
+        # are SConscript settable (and gettable, which for options
+        # like 'help' is far more important than being settable). 
+        self.settable = {
+            'clean'             : 0,
+            'diskcheck'         : diskcheck_all,
+            'duplicate'         : 'hard-soft-copy',
+            'help'              : 0,
+            'implicit_cache'    : 0,
+            'max_drift'         : SCons.Node.FS.default_max_drift,
+            'num_jobs'          : 1,
+            'random'            : 0,
+        }
 
     def get(self, name):
         if not self.settable.has_key(name):
             scripts.append(sfile)
 
     if not scripts:
-        if options.help_msg:
+        if options.help:
             # There's no SConstruct, but they specified -h.
             # Give them the options usage now, before we fail
             # trying to read a non-existent SConstruct file.
 
     fs.chdir(fs.Top)
 
-    if options.help_msg:
+    if ssoptions.get('help'):
         help_text = SCons.Script.help_text
         if help_text is None:
             # They specified -h, but there was no Help() inside the
     if options.random:
         def order(dependencies):
             """Randomize the dependencies."""
+            import random
             # This is cribbed from the implementation of
             # random.shuffle() in Python 2.X.
             d = dependencies

src/engine/SCons/Script/__init__.py

 
 #
 Action                  = SCons.Action.Action
+AddMethod               = SCons.Util.AddMethod
 AllowSubstExceptions    = SCons.Subst.SetAllowableExceptions
 BoolOption              = SCons.Options.BoolOption
 Builder                 = SCons.Builder.Builder

src/engine/SCons/Taskmaster.py

         """
         self.out_of_date = []
         for t in self.targets:
+            t.disambiguate()
             try:
-                is_up_to_date = t.disambiguate().current()
+                is_up_to_date = not t.always_build and t.current()
             except EnvironmentError, e:
                 raise SCons.Errors.BuildError(node=t, errstr=e.strerror, filename=e.filename)
             if is_up_to_date:

src/engine/SCons/TaskmasterTests.py

         self.postprocessed = None
         self._bsig_val = None
         self._current_val = 0
+        self.always_build = None
 
     def disambiguate(self):
         return self
         n3 = Node("n3")
         c4 = Node("c4")
         c4._current_val = 1
-        tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4],
+        a5 = Node("a5")
+        a5._current_val = 1
+        a5.always_build = 1
+        tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5],
                                          tasker = TaskGen)
 
         del ood[:]
         t = tm.next_task()
         assert ood == [], ood
 
+        del ood[:]
+        t = tm.next_task()
+        assert ood == [a5], ood
 
     def test_make_ready_exception(self):
         """Test handling exceptions from Task.make_ready()

src/engine/SCons/Tool/JavaCommon.py

     #     array declarations "[]";
     #     semi-colons;
     #     periods.
-    _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.]|' +
+    _reToken = re.compile(r'(\n|\\\\|//|\\[\'"]|[\'"\{\}\;\.\(\)]|' +
                           r'[A-Za-z_][\w\.]*|/\*|\*/|\[\])')
 
     class OuterState:
             try:
                 return self.anonState
             except AttributeError:
+                self.outer_state = self
                 ret = SkipState(1, AnonClassState(self))
                 self.anonState = ret
                 return ret
 
     class AnonClassState:
         """A state that looks for anonymous inner classes."""
-        def __init__(self, outer_state):
+        def __init__(self, old_state):
             # outer_state is always an instance of OuterState
-            self.outer_state = outer_state
-            self.tokens_to_find = 2
+            self.outer_state = old_state.outer_state
+            self.old_state = old_state
+            self.brace_level = 0
         def parseToken(self, token):
-            # This is an anonymous class if and only if the next  
-            # non-whitespace token is a bracket            
-            if token == '\n':
+            # This is an anonymous class if and only if the next
+            # non-whitespace token is a bracket. Everything between
+            # braces should be parsed as normal java code.
+            if token[:2] == '//':
+                return IgnoreState('\n', self)
+            elif token == '/*':
+                return IgnoreState('*/', self)
+            elif token == '\n':
+                return self
+            elif token == '(':
+                self.brace_level = self.brace_level + 1
+                return self
+            if self.brace_level > 0:
+                if token == 'new':
+                    # look further for anonymous inner class
+                    return SkipState(1, AnonClassState(self))
+                elif token in [ '"', "'" ]:
+                    return IgnoreState(token, self)
+                elif token == ')':
+                    self.brace_level = self.brace_level - 1
                 return self
             if token == '{':
-                self.outer_state.openBracket()
                 self.outer_state.addAnonClass()
-            elif token == '}':
-                self.outer_state.closeBracket()
-            elif token in ['"', "'"]:
-                return IgnoreState(token, self)
-            return self.outer_state
+            return self.old_state.parseToken(token)
 
     class SkipState:
         """A state that will skip a specified number of tokens before

src/engine/SCons/Tool/JavaCommonTests.py

         assert pkg_dir == None, pkg_dir
         assert classes == ['A$B', 'A'], classes
 
+    def test_anonymous_classes_with_parentheses(self):
+        """Test finding anonymous classes marked by parentheses"""
+        pkg_dir, classes = SCons.Tool.JavaCommon.parse_java("""\
+import java.io.File;
+
+public class Foo {
+    public static void main(String[] args) {
+        File f = new File(
+            new File("a") {
+                public String toString() {
+                    return "b";
+                }
+            } to String()
+        ) {
+            public String toString() {
+                return "c";
+            }
+        };
+    }
+}
+""")
+        assert classes == ['Foo$1', 'Foo$2', 'Foo'], classes
+
+
 
 
 if __name__ == "__main__":

src/engine/SCons/Tool/__init__.py

                                        emitter = {},
                                        suffix = {None:'$CFILESUFFIX'})
         env['BUILDERS']['CFile'] = c_file
-        env['CFILESUFFIX'] = '.c'
+
+        env.SetDefault(CFILESUFFIX = '.c')
 
     try:
         cxx_file = env['BUILDERS']['CXXFile']
                                          emitter = {},
                                          suffix = {None:'$CXXFILESUFFIX'})
         env['BUILDERS']['CXXFile'] = cxx_file
-        env['CXXFILESUFFIX'] = '.cc'
+        env.SetDefault(CXXFILESUFFIX = '.cc')
 
     return (c_file, cxx_file)
 

src/engine/SCons/Tool/midl.py

 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import string
+
 import SCons.Action
 import SCons.Builder
 import SCons.Defaults
     tlb = target[0]
     incl = base + '.h'
     interface = base + '_i.c'
-    proxy = base + '_p.c'
-    dlldata = base + '_data.c'
+    t = [tlb, incl, interface]
 
-    t = [tlb, incl, interface, proxy, dlldata]
+    midlcom = env['MIDLCOM']
 
+    if string.find(midlcom, '/proxy') != -1:
+        proxy = base + '_p.c'
+        t.append(proxy)
+    if string.find(midlcom, '/dlldata') != -1:
+        dlldata = base + '_data.c'
+        t.append(dlldata)
+    
     return (t,source)
 
 idl_scanner = SCons.Scanner.IDL.IDLScan()

src/engine/SCons/Tool/mslink.py

 import SCons.Util
 
 def pdbGenerator(env, target, source, for_signature):
-    if target and env.has_key('PDB') and env['PDB']:
-        return ['/PDB:%s'%target[0].File(env['PDB']).get_string(for_signature),
-                '/DEBUG']
-    return None
+    try:
+        return ['/PDB:%s' % target[0].attributes.pdb, '/DEBUG']
+    except (AttributeError, IndexError):
+        return None
 
 def windowsShlinkTargets(target, source, env, for_signature):
     listCmd = []
                                       "WINDOWSSHLIBMANIFESTPREFIX", "WINDOWSSHLIBMANIFESTSUFFIX"))
 
     if env.has_key('PDB') and env['PDB']:
-        target.append(env['PDB'])
+        pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
+        target.append(pdb)
+        target[0].attributes.pdb = pdb
 
     if not no_import_lib and \
        not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"):
                                       "WINDOWSPROGMANIFESTPREFIX", "WINDOWSPROGMANIFESTSUFFIX"))
 
     if env.has_key('PDB') and env['PDB']:
-        target.append(env['PDB'])
+        pdb = env.arg2nodes('$PDB', target=target, source=source)[0]
+        target.append(pdb)
+        target[0].attributes.pdb = pdb
 
     return (target,source)
 

src/engine/SCons/Tool/msvs.py

                     self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n'
                                     '\t\t%s.%s|%s.Build.0 = %s|%s\n'  % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform))
             else:
-                self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
-                                '\t\t%s.%s.Build.0 = %s|%s\n'  %(self.slnguid,variant,variant,platform,self.slnguid,variant,variant,platform))
+                for p in self.dspfiles:
+                    guid = _generateGUID(p, '')
+                    self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
+                                    '\t\t%s.%s.Build.0 = %s|%s\n'  %(guid,variant,variant,platform,guid,variant,variant,platform))
 
         self.file.write('\tEndGlobalSection\n')
 

src/engine/SCons/Util.py

         self.file.flush()
     def __getattr__(self, attr):
         return getattr(self.file, attr)
+
+# The original idea for AddMethod() and RenameFunction() come from the
+# following post to the ActiveState Python Cookbook:
+#
+#	ASPN: Python Cookbook : Install bound methods in an instance
+#	http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/223613
+#
+# That code was a little fragile, though, so the following changes
+# have been wrung on it:
+#
+# * Switched the installmethod() "object" and "function" arguments,
+#   so the order reflects that the left-hand side is the thing being
+#   "assigned to" and the right-hand side is the value being assigned.
+#
+# * Changed explicit type-checking to the "try: klass = object.__class__"
+#   block in installmethod() below so that it still works with the
+#   old-style classes that SCons uses.
+#
+# * Replaced the by-hand creation of methods and functions with use of
+#   the "new" module, as alluded to in Alex Martelli's response to the
+#   following Cookbook post:
+#
+#	ASPN: Python Cookbook : Dynamically added methods to a class
+#	http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81732
+
+def AddMethod(object, function, name = None):
+    """
+    Adds either a bound method to an instance or an unbound method to
+    a class. If name is ommited the name of the specified function
+    is used by default.
+    Example:
+      a = A()
+      def f(self, x, y):
+        self.z = x + y
+      AddMethod(f, A, "add")
+      a.add(2, 4)
+      print a.z
+      AddMethod(lambda self, i: self.l[i], a, "listIndex")
+      print a.listIndex(5)
+    """
+    import new
+
+    if name is None:
+        name = function.func_name
+    else:
+        function = RenameFunction(function, name)
+
+    try:
+        klass = object.__class__
+    except AttributeError:
+        # "object" is really a class, so it gets an unbound method.
+        object.__dict__[name] = new.instancemethod(function, None, object)
+    else:
+        # "object" is really an instance, so it gets a bound method.
+        object.__dict__[name] = new.instancemethod(function, object, klass)
+
+def RenameFunction(function, name):
+    """
+    Returns a function identical to the specified function, but with
+    the specified name.
+    """
+    import new
+
+    # Compatibility for Python 1.5 and 2.1.  Can be removed in favor of
+    # passing function.func_defaults directly to new.function() once
+    # we base on Python 2.2 or later.
+    func_defaults = function.func_defaults
+    if func_defaults is None:
+        func_defaults = ()
+
+    return new.function(function.func_code,
+                        function.func_globals,
+                        name,
+                        func_defaults)
+
+del __revision__

src/engine/SCons/compat/builtins.py

     dict
 except NameError:
     # Pre-2.2 Python has no dict() keyword.
-    def dict(*arg, **kwargs):
+    def dict(seq=[], **kwargs):
         """
         New dictionary initialization.
         """
-        d = apply(types.DictType, arg)
+        d = {}
+        for k, v in seq:
+            d[k] = v
         d.update(kwargs)
         return d
     __builtin__.dict = dict

test/AddMethod.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify use of the AddMethod() global function (specifically, to add
+an unbound method to the Environment class) and its counterpart
+construction environment method.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """
+def foo(self):
+    return 'foo-' + env['FOO']
+
+AddMethod(Environment, foo)
+env = Environment(FOO = '111')
+print env.foo()
+
+env = Environment(FOO = '222')
+print env.foo()
+
+env.AddMethod(foo, 'bar')
+print env.bar()
+
+""")
+
+expect = """\
+foo-111
+foo-222
+foo-222
+"""
+
+test.run(arguments = '-Q -q', stdout = expect)
+
+test.pass_test()

test/AlwaysBuild.py

 
 test.subdir('sub')
 
+sub_f3_out = os.path.join('sub', 'f3.out')
+_SUBDIR_f3_out = os.path.join('$SUBDIR', 'f3.out')
+
 test.write('SConstruct', """\
 def bfunc(target, source, env):
     import shutil
 env.B('f1.out', source='f1.in')
 AlwaysBuild('f1.out')
 
-env.B(r'%s', source='f3.in')
-env.AlwaysBuild(r'%s')
+env.B(r'%(sub_f3_out)s', source='f3.in')
+env.AlwaysBuild(r'%(_SUBDIR_f3_out)s')
 
 env.Alias('clean1', [], Delete('clean1-target'))
 env.AlwaysBuild('clean1')
 c2 = env.Alias('clean2', [], [Delete('clean2-t1'), Delete('clean2-t2')])
 env.AlwaysBuild(c2)
-""" % (os.path.join('sub', 'f3.out'),
-       os.path.join('$SUBDIR', 'f3.out')
-      ))
+
+def dir_build(target, source, env):
+    open('dir_build.txt', 'ab').write('dir_build()\\n')
+env.Command(Dir('dir'), None, dir_build)
+env.AlwaysBuild('dir')
+""" % locals())
 
 test.write('f1.in', "f1.in\n")
 test.write('f2.in', "1")
 test.write('f3.in', "f3.in\n")
 
+test.subdir('dir')
+
 test.run(arguments = ".")
-test.fail_test(test.read('f1.out') != '1')
-test.fail_test(test.read(['sub', 'f3.out']) != '1')
+test.must_match('f1.out', '1')
+test.must_match(['sub', 'f3.out'], '1')
+test.must_match('dir_build.txt', "dir_build()\n")
 
 test.write('f2.in', "2")
 
 test.run(arguments = ".")
-test.fail_test(test.read('f1.out') != '2')
-test.fail_test(test.read(['sub', 'f3.out']) != '2')
+test.must_match('f1.out', '2')
+test.must_match(['sub', 'f3.out'], '2')
+test.must_match('dir_build.txt', "dir_build()\ndir_build()\n")
 
 test.run(arguments = 'clean1', stdout=test.wrap_stdout("""\
 Delete("clean1-target")
 Delete("clean2-t2")
 """))
 
+test.not_up_to_date(arguments = 'dir')
+
+test.must_match('dir_build.txt', "dir_build()\ndir_build()\ndir_build()\n")
+
 test.pass_test()

test/CFILESUFFIX.py

 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+"""
+Verify that we can set CFILESUFFIX to arbitrary values.
+"""
+
 import os
 import os.path
 import string
 env = Environment(LEX = r'%(_python_)s mylex.py', tools = ['lex'])
 env.CFile(target = 'foo', source = 'foo.l')
 env.Clone(CFILESUFFIX = '.xyz').CFile(target = 'bar', source = 'bar.l')
+
+# Make sure that calling a Tool on a construction environment *after*
+# we've set CFILESUFFIX doesn't overwrite the value.
+env2 = Environment(tools = [], CFILESUFFIX = '.env2')
+env2.Tool('lex')
+env2['LEX'] = r'%(_python_)s mylex.py'
+env2.CFile(target = 'f3', source = 'f3.l')
 """ % locals())
 
 input = r"""
 
 test.write('bar.l', input % 'bar.l')
 
+test.write('f3.l', input % 'f3.l')
+
 test.run(arguments = '.')
 
-test.fail_test(not os.path.exists(test.workpath('foo.c')))
+test.must_exist(test.workpath('foo.c'))
 
-test.fail_test(not os.path.exists(test.workpath('bar.xyz')))
+test.must_exist(test.workpath('bar.xyz'))
+
+test.must_exist(test.workpath('f3.env2'))
 
 test.pass_test()

test/CXX/CXXFILESUFFIX.py

 env = Environment(LEX = r'%(_python_)s mylex.py', tools = ['lex'])
 env.CXXFile(target = 'foo', source = 'foo.ll')
 env.Clone(CXXFILESUFFIX = '.xyz').CXXFile(target = 'bar', source = 'bar.ll')
+
+# Make sure that calling a Tool on a construction environment *after*
+# we've set CXXFILESUFFIX doesn't overwrite the value.
+env2 = Environment(tools = [], CXXFILESUFFIX = '.env2')
+env2.Tool('lex')
+env2['LEX'] = r'%(_python_)s mylex.py'
+env2.CXXFile(target = 'f3', source = 'f3.ll')
 """ % locals())
 
 input = r"""
 
 test.write('bar.ll', input % 'bar.ll')
 
+test.write('f3.ll', input % 'f3.ll')
+
 test.run(arguments = '.')
 
-test.fail_test(not os.path.exists(test.workpath('foo.cc')))
+test.must_exist(test.workpath('foo.cc'))
 
-test.fail_test(not os.path.exists(test.workpath('bar.xyz')))
+test.must_exist(test.workpath('bar.xyz'))
+
+test.must_exist(test.workpath('f3.env2'))
 
 test.pass_test()

test/CacheDir/symlink.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that we push and retrieve a built symlink to/from a CacheDir()
+as an actualy symlink, not by copying the file contents.
+"""
+
+import os
+import os.path
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+if not hasattr(os, 'symlink'):
+    import sys
+    test.skip_test('%s has no os.symlink() method; skipping test\n' % sys.executable)
+
+test.write('SConstruct', """\
+CacheDir('cache')
+import os
+Symlink = Action(lambda target, source, env:
+                        os.symlink(str(source[0]), str(target[0])),
+                 "os.symlink($SOURCE, $TARGET)")
+Command('file.symlink', 'file.txt', Symlink)
+""")
+
+test.write('file.txt', "file.txt\n")
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.islink('file.symlink'))
+test.must_match('file.symlink', "file.txt\n")
+
+test.run(arguments = '-c .')
+
+test.must_not_exist('file.symlink')
+
+test.run(arguments = '.')
+
+test.fail_test(not os.path.islink('file.symlink'))
+test.must_match('file.symlink', "file.txt\n")
+
+test.pass_test()

test/Configure/Action-error.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that calling Configure from an Action results in a readable error.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+test.write('SConstruct', """\
+def ConfigureAction(target, source, env):
+    env.Configure()
+    return 0
+env = Environment(BUILDERS = {'MyAction' :
+                          Builder(action=Action(ConfigureAction))})
+env.MyAction('target', [])
+""")
+
+expect = "scons: *** Calling Configure from Builders is not supported.\n"
+
+test.run(status=2, stderr=expect)
+
+test.pass_test()

test/Configure/BuildDir-SConscript.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+"""
+Verify that Configure calls in SConscript files work when used
+with BuildDir.
+"""
+
+import os.path
+
+import TestSCons
+
+_obj = TestSCons._obj
+
+test = TestSCons.TestSCons(match = TestSCons.match_re_dotall)
+
+test.subdir( 'sub', ['sub', 'local'] )
+
+NCR = test.NCR  # non-cached rebuild
+CR  = test.CR   # cached rebuild (up to date)
+NCF = test.NCF  # non-cached build failure
+CF  = test.CF   # cached build failure
+
+test.write('SConstruct', """\
+opts = Options()
+opts.Add('chdir')
+env = Environment(options=opts)
+if env['chdir'] == 'yes':
+  SConscriptChdir(1)
+else:
+  SConscriptChdir(0)
+BuildDir( 'build', '.' )
+SConscript( 'build/SConscript' )
+""")
+
+test.write(['sub', 'local', 'local_header.h'], "/* Hello World */" )
+
+test.write('SConscript', """\
+SConscript( 'sub/SConscript' )
+""")
+
+test.write(['sub', 'SConscript'], """\
+def CustomTest(context):
+  context.Message('Executing Custom Test ... ')
+  ret = context.TryCompile('#include "local_header.h"', '.c')
+  context.Result(ret)
+  return ret
+
+env = Environment(FOO='fff')
+env.Append( CPPPATH='local' )
+import os
+env.AppendENVPath('PATH', os.environ['PATH'])
+conf = Configure( env, custom_tests = {'CustomTest' : CustomTest,
+                                       '$FOO' : CustomTest} )
+if hasattr(conf, 'fff'):
+  conf.Message('$FOO should not have been expanded!')
+  Exit(1)
+if not conf.CheckCHeader( 'math.h' ):
+  Exit(1)
+if conf.CheckCHeader( 'no_std_c_header.h' ):
+  Exit(1)
+if not conf.CustomTest():
+  Exit(1)
+env = conf.Finish()
+env.Program( 'TestProgram', 'TestProgram.c' )
+""")
+
+test.write(['sub', 'TestProgram.h'], """\
+/* Just a test header */
+""")
+
+test.write(['sub', 'TestProgram.c'], """\
+#include "TestProgram.h"
+#include <stdio.h>
+
+int main() {
+  printf( "Hello\\n" );
+}
+""")
+
+# first with SConscriptChdir(0)
+test.run(arguments='chdir=no')
+test.checkLogAndStdout( ["Checking for C header file math.h... ",
+                        "Checking for C header file no_std_c_header.h... ",
+                        "Executing Custom Test ... "],
+                        ["yes", "no", "yes"],
+                        [[((".c", NCR), (_obj, NCR))],
+                         [((".c", NCR), (_obj, NCF))],
+                         [((".c", NCR), (_obj, NCR))]],
+                        "config.log",
+                        ".sconf_temp",
+                        os.path.join("build", "sub", "SConscript"))
+
+test.run(arguments='chdir=no')
+test.checkLogAndStdout( ["Checking for C header file math.h... ",
+                        "Checking for C header file no_std_c_header.h... ",
+                        "Executing Custom Test ... "],
+                        ["yes", "no", "yes"],
+                        [[((".c", CR), (_obj, CR))],
+                         [((".c", CR), (_obj, CF))],
+                         [((".c", CR), (_obj, CR))]],
+                        "config.log",
+                        ".sconf_temp",
+                        os.path.join("build", "sub", "SConscript"))
+
+import shutil
+shutil.rmtree(test.workpath(".sconf_temp"))
+test.unlink(".sconsign.dblite")
+
+# now with SConscriptChdir(1)
+test.run(arguments='chdir=yes')
+test.checkLogAndStdout( ["Checking for C header file math.h... ",
+                        "Checking for C header file no_std_c_header.h... ",
+                        "Executing Custom Test ... "],
+                        ["yes", "no", "yes"],
+                        [[((".c", NCR), (_obj, NCR))],
+                         [((".c", NCR), (_obj, NCF))],
+                         [((".c", NCR), (_obj, NCR))]],
+                        "config.log",
+                        ".sconf_temp",
+                        os.path.join("build", "sub", "SConscript"))
+
+test.run(arguments='chdir=yes')
+test.checkLogAndStdout( ["Checking for C header file math.h... ",
+                        "Checking for C header file no_std_c_header.h... ",
+                        "Executing Custom Test ... "],
+                        ["yes", "no", "yes"],
+                        [[((".c", CR), (_obj, CR))],
+                         [((".c", CR), (_obj, CF))],
+                         [((".c", CR), (_obj, CR))]],
+                        "config.log",
+                        ".sconf_temp",
+                        os.path.join("build", "sub", "SConscript"))
+
+
+test.pass_test()

test/Configure/BuildDir.py

+#!/usr/bin/env python
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#