Commits

Steven Knight  committed 034a178

Merged revisions 4024-4028,4031-4063 via svnmerge from
http://scons.tigris.org/svn/scons/trunk

................
r4031 | stevenknight | 2009-02-21 17:51:36 -0800 (Sat, 21 Feb 2009) | 4 lines

Disable the cut-and-paste tests for executing from generated Visual
Studio 9.0 and 9.0Exp project+solution files until we actually support
generating those versions...
................
r4032 | stevenknight | 2009-02-21 18:03:06 -0800 (Sat, 21 Feb 2009) | 2 lines

Fix line endings.
................
r4033 | stevenknight | 2009-02-21 22:00:03 -0800 (Sat, 21 Feb 2009) | 2 lines

Replace embedded carriage returns with \r.
................
r4034 | stevenknight | 2009-02-21 22:01:10 -0800 (Sat, 21 Feb 2009) | 2 lines

Set explicit "svn:eol-style native" on all *.py files.
................
r4035 | stevenknight | 2009-02-21 22:34:14 -0800 (Sat, 21 Feb 2009) | 5 lines

Move the set_state(EXECUTED) call associated with pulling things
out of a CacheDir from CacheDir.py to Taskmaster.py. Ripple effects
include adding a Node.FS.push_to_cache() method, so that logic can
be invoked separately from the Node.built() method.
................
r4036 | stevenknight | 2009-02-22 00:17:49 -0800 (Sun, 22 Feb 2009) | 3 lines

Set IncludeSearchPath and PreprocessorDefinitions in the generation
project files for Visual Studio 8.0. (Allan Erskine)
................
r4037 | stevenknight | 2009-02-22 00:19:45 -0800 (Sun, 22 Feb 2009) | 2 lines

Undo changes to unrelated files accidentally included in r4036.
................
r4038 | stevenknight | 2009-02-22 07:33:05 -0800 (Sun, 22 Feb 2009) | 2 lines

Python 1.5 portability in last patch.
................
r4039 | stevenknight | 2009-02-22 07:33:23 -0800 (Sun, 22 Feb 2009) | 2 lines

Remove left-over work-in-progress files from the vs_revamp branch.
................
r4040 | stevenknight | 2009-02-23 06:55:04 -0800 (Mon, 23 Feb 2009) | 3 lines

Issue 2152: Fix the ability of --clean to handle / delete broken
symlinks, as well as named pipes. (Mateusz Gruca)
................
r4048 | stevenknight | 2009-02-23 13:19:27 -0800 (Mon, 23 Feb 2009) | 2 lines

Add project highlight for 1.2.0.d20090223 checkpoint release.
................
r4049 | GregNoel | 2009-02-24 14:47:43 -0800 (Tue, 24 Feb 2009) | 1 line

Issue 2260, document Clean() pattern for intermediate directories
................
r4050 | stevenknight | 2009-02-25 06:35:17 -0800 (Wed, 25 Feb 2009) | 17 lines

Merged revisions 3909-4041,4043-4049 via svnmerge from
http://scons.tigris.org/svn/scons/checkpoint

........
r4041 | stevenknight | 2009-02-23 07:06:15 -0800 (Mon, 23 Feb 2009) | 2 lines

Fix embedded carriage returns so merge will apply cleanly.
........
r4043 | stevenknight | 2009-02-23 08:47:32 -0800 (Mon, 23 Feb 2009) | 2 lines

Update release identification for the checkpoint.
........
r4044 | stevenknight | 2009-02-23 09:04:28 -0800 (Mon, 23 Feb 2009) | 2 lines

Add files that conflicted when re-merging.
........
................
r4051 | stevenknight | 2009-02-25 06:36:00 -0800 (Wed, 25 Feb 2009) | 3 lines

Add the new checkpoint to the list of all SCons versions that can be
installed.
................
r4052 | stevenknight | 2009-02-25 07:15:43 -0800 (Wed, 25 Feb 2009) | 3 lines

Issue 1059: Fix the -n option when VariantDir(duplicate=1) is used
and the variant directory doesn't already exist.
................
r4053 | stevenknight | 2009-02-26 06:41:05 -0800 (Thu, 26 Feb 2009) | 3 lines

Capture some minor updates while investigating possible patches to enable
use of pychecker on our code base. The test itself is still disabled.
................
r4057 | GregNoel | 2009-03-01 19:19:29 -0800 (Sun, 01 Mar 2009) | 1 line

replace execfile() by equivalent exec statement
................
r4060 | stevenknight | 2009-03-02 11:09:47 -0800 (Mon, 02 Mar 2009) | 2 lines

Fix a nested scope issue.
................
r4061 | stevenknight | 2009-03-02 11:11:09 -0800 (Mon, 02 Mar 2009) | 4 lines

Fix handling of both UTF_16_LE and UTF_16_BE. Add an actual test for
scanning Unicode files for implicit dependencies. Clean up how we handle
decoding, and wrap it for earlier Python versions.
................
r4062 | stevenknight | 2009-03-02 15:10:32 -0800 (Mon, 02 Mar 2009) | 3 lines

Issue 2360: fix a TypeError from attempts to intern() unicode objects
returned to the ClassicCPPScanner.
................

  • Participants
  • Parent commits d46f1f7
  • Branches vs_revamp

Comments (0)

Files changed (53)

File QMTest/TestSConsMSVS.py

 \t\t\t\tReBuildCommandLine="echo Starting SCons &amp;&amp; &quot;<PYTHON>&quot; -c &quot;<SCONS_SCRIPT_MAIN_XML>&quot; -C &quot;<WORKPATH>&quot; -f SConstruct &quot;Test.exe&quot;"
 \t\t\t\tCleanCommandLine="echo Starting SCons &amp;&amp; &quot;<PYTHON>&quot; -c &quot;<SCONS_SCRIPT_MAIN_XML>&quot; -C &quot;<WORKPATH>&quot; -f SConstruct -c &quot;Test.exe&quot;"
 \t\t\t\tOutput="Test.exe"
-\t\t\t\tPreprocessorDefinitions=""
-\t\t\t\tIncludeSearchPath=""
+\t\t\t\tPreprocessorDefinitions="DEF1;DEF2"
+\t\t\t\tIncludeSearchPath="inc1;inc2"
 \t\t\t\tForcedIncludes=""
 \t\t\t\tAssemblySearchPath=""
 \t\t\t\tForcedUsingAssemblies=""
 """
 
 SConscript_contents_8_0 = """\
-env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0')
+env=Environment(platform='win32', tools=['msvs'], MSVS_VERSION='8.0',
+                CPPDEFINES=['DEF1', 'DEF2'],
+                CPPPATH=['inc1', 'inc2'])
 
 testsrc = ['test1.cpp', 'test2.cpp']
 testincs = ['sdk.h']

File QMTest/TestSCons_time.py

 #!/usr/bin/env python
 import os
 import sys
+import string
 def write_args(fp, args):
     fp.write(args[0] + '\\n')
     for arg in args[1:]:
         write_args(profile, sys.argv)
         break
 sys.stdout.write('SCONS_LIB_DIR = ' + os.environ['SCONS_LIB_DIR'] + '\\n')
-execfile('SConstruct')
+exec(string.replace(open('SConstruct').read(), '\\r', '\\n'))
 """
 
 aegis_py = """\
 copyright_years = '2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009'
 
 # This gets inserted into the man pages to reflect the month of release.
-month_year = 'January 2009'
+month_year = 'February 2009'
 
 #
 # __COPYRIGHT__

File bench/bench.py

 import sys
 import time
 import types
+import string
 
 Usage = """\
 Usage:  bench.py OPTIONS file.py
     sys.exit(1)
 
 
-execfile(args[0])
+exec(string.replace(open(args[0]).read(), '\r', '\n'))
 
 
 try:

File bin/install_scons.py

     '1.1.0.d20081207',
     '1.2.0',
     '1.2.0.d20090113',
+    '1.2.0.d20090223',
 ]
 
 def main(argv=None):

File doc/man/scons.1

 Clean(['foo', 'bar'], 'something_else_to_clean')
 .EE
 
+In this example,
+installing the project creates a subdirectory for the documentation.
+This statement causes the subdirectory to be removed
+if the project is deinstalled.
+.ES
+Clean(docdir, os.path.join(docdir, projectname))
+.EE
+
 '\"""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
 .TP
 .RI Command( target ", " source ", " action ", [" key = val ", ...])"
 .IP
 will print:
 .ES
-\&'$CC $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS -c -o $TARGET $SOURCES'
+\&'$CC -c -o $TARGET $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $SOURCES'
 .EE
 
 .ES

File src/CHANGES.txt

 
 RELEASE X.X.X - XXX
 
+  From Steven Knight:
+
+    - Fix the -n option when used with VariantDir(duplicate=1)
+      and the variant directory doesn't already exist.
+
+    - Fix scanning of Unicode files for both UTF-16 endian flavors.
+
+    - Fix a TypeError on #include of file names with Unicode characters.
+
+
+
+RELEASE 1.2.0.d20090223 - Mon, 23 Feb 2009 08:41:06 -0800
+
   From Stanislav Baranov:
 
     - Make suffix-matching for scanners case-insensitive on Windows.
 
     - Fix generation of Visual Studio 8 project files on x64 platforms.
 
+  From Allan Erskine:
+
+    - Set IncludeSearchPath and PreprocessorDefinitions in generated
+      Visual Studio 8 project files, to help IntelliSense work.
+
+  From Mateusz Gruca:
+
+    - Fix deletion of broken symlinks by the --clean option.
+
   From Steven Knight:
 
     - Fix the error message when use of a non-existent drive on Windows

File src/RELEASE.txt

 
 
 
-RELEASE 1.2.0.d20090113 - Tue, 13 Jan 2009 02:50:30 -0800
+RELEASE 1.2.0.d20090223 - Mon, 23 Feb 2009 08:41:06 -0800
 
   Please consult the CHANGES.txt file for a list of specific changes
   since last release.

File src/engine/SCons/CacheDir.py

         if not self.is_enabled():
             return False
 
-        retrieved = False
+        env = node.get_build_env()
+        if cache_show:
+            if CacheRetrieveSilent(node, [], env, execute=1) == 0:
+                node.build(presub=0, execute=0)
+                return True
+        else:
+            if CacheRetrieve(node, [], env, execute=1) == 0:
+                return True
 
-        if cache_show:
-            if CacheRetrieveSilent(node, [], node.get_build_env(), execute=1) == 0:
-                node.build(presub=0, execute=0)
-                retrieved = 1
-        else:
-            if CacheRetrieve(node, [], node.get_build_env(), execute=1) == 0:
-                retrieved = 1
-        if retrieved:
-            # Record build signature information, but don't
-            # push it out to cache.  (We just got it from there!)
-            node.set_state(SCons.Node.executed)
-            SCons.Node.Node.built(node)
-
-        return retrieved
+        return False
 
     def push(self, node):
         if not self.is_enabled():

File src/engine/SCons/CacheDirTests.py

 
             cd_f3 = self.test.workpath("cd.f3")
             f3 = self.File(cd_f3)
-            f3.built()
+            f3.push_to_cache()
             assert self.pushed == [], self.pushed
             self.test.write(cd_f3, "cd.f3\n")
-            f3.built()
+            f3.push_to_cache()
             assert self.pushed == [f3], self.pushed
 
             self.pushed = []
 
             warn_caught = 0
             try:
-                f7.built()
+                f7.push_to_cache()
             except SCons.Errors.BuildError, e:
                 assert e.exc_info[0] == SCons.Warnings.CacheWriteErrorWarning
                 warn_caught = 1

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

     except AttributeError:
         codecs.BOM_UTF8 = '\xef\xbb\xbf'
     try:
-        codecs.BOM_UTF16
+        codecs.BOM_UTF16_LE
+        codecs.BOM_UTF16_BE
     except AttributeError:
-        if sys.byteorder == 'little':
-            codecs.BOM_UTF16 = '\xff\xfe'
+        codecs.BOM_UTF16_LE = '\xff\xfe'
+        codecs.BOM_UTF16_BE = '\xfe\xff'
+
+    # Provide a wrapper function to handle decoding differences in
+    # different versions of Python.  Normally, we'd try to do this in the
+    # compat layer (and maybe it still makes sense to move there?) but
+    # that doesn't provide a way to supply the string class used in
+    # pre-2.3 Python versions with a .decode() method that all strings
+    # naturally have.  Plus, the 2.[01] encodings behave differently
+    # enough that we have to settle for a lowest-common-denominator
+    # wrapper approach.
+    #
+    # Note that the 2.[012] implementations below may be inefficient
+    # because they perform an explicit look up of the encoding for every
+    # decode, but they're old enough (and we want to stop supporting
+    # them soon enough) that it's not worth complicating the interface.
+    # Think of it as additional incentive for people to upgrade...
+    try:
+        ''.decode
+    except AttributeError:
+        # 2.0 through 2.2:  strings have no .decode() method
+        try:
+            codecs.lookup('ascii').decode
+        except AttributeError:
+            # 2.0 and 2.1:  encodings are a tuple of functions, and the
+            # decode() function returns a (result, length) tuple.
+            def my_decode(contents, encoding):
+                return codecs.lookup(encoding)[1](contents)[0]
         else:
-            codecs.BOM_UTF16 = '\xfe\xff'
+            # 2.2:  encodings are an object with methods, and the
+            # .decode() method returns just the decoded bytes.
+            def my_decode(contents, encoding):
+                return codecs.lookup(encoding).decode(contents)
+    else:
+        # 2.3 or later:  use the .decode() string method
+        def my_decode(contents, encoding):
+            return contents.decode(encoding)
 
 import SCons.Action
 from SCons.Debug import logInstanceCreation
         # it's a valid python string.
         def get_text_contents(self):
             contents = self.get_contents()
+            # The behavior of various decode() methods and functions
+            # w.r.t. the initial BOM bytes is different for different
+            # encodings and/or Python versions.  ('utf-8' does not strip
+            # them, but has a 'utf-8-sig' which does; 'utf-16' seems to
+            # strip them; etc.)  Just side step all the complication by
+            # explicitly stripping the BOM before we decode().
             if contents.startswith(codecs.BOM_UTF8):
-                contents = contents.decode('utf-8')
-            elif contents.startswith(codecs.BOM_UTF16):
-                contents = contents.decode('utf-16')
+                contents = contents[len(codecs.BOM_UTF8):]
+                # TODO(2.2):  Remove when 2.3 becomes floor.
+                #contents = contents.decode('utf-8')
+                contents = my_decode(contents, 'utf-8')
+            elif contents.startswith(codecs.BOM_UTF16_LE):
+                contents = contents[len(codecs.BOM_UTF16_LE):]
+                # TODO(2.2):  Remove when 2.3 becomes floor.
+                #contents = contents.decode('utf-16-le')
+                contents = my_decode(contents, 'utf-16-le')
+            elif contents.startswith(codecs.BOM_UTF16_BE):
+                contents = contents[len(codecs.BOM_UTF16_BE):]
+                # TODO(2.2):  Remove when 2.3 becomes floor.
+                #contents = contents.decode('utf-16-be')
+                contents = my_decode(contents, 'utf-16-be')
             return contents
 
     def get_content_hash(self):
         # created.
         self.dir._create()
 
+    def push_to_cache(self):
+        """Try to push the node into a cache
+        """
+        # This should get called before the Nodes' .built() method is
+        # called, which would clear the build signature if the file has
+        # a source scanner.
+        #
+        # We have to clear the local memoized values *before* we push
+        # the node to cache so that the memoization of the self.exists()
+        # return value doesn't interfere.
+        if self.nocache:
+            return
+        self.clear_memoized_values()
+        if self.exists():
+            self.get_build_env().get_CacheDir().push(self)
+
     def retrieve_from_cache(self):
         """Try to retrieve the node's content from a cache
 
             return None
         return self.get_build_env().get_CacheDir().retrieve(self)
 
-    def built(self):
-        """
-        Called just after this node is successfully built.
-        """
-        # Push this file out to cache before the superclass Node.built()
-        # method has a chance to clear the build signature, which it
-        # will do if this file has a source scanner.
-        #
-        # We have to clear the memoized values *before* we push it to
-        # cache so that the memoization of the self.exists() return
-        # value doesn't interfere.
-        self.clear_memoized_values()
-        if self.exists():
-            self.get_build_env().get_CacheDir().push(self)
-        SCons.Node.Node.built(self)
-
     def visited(self):
         if self.exists():
             self.get_build_env().get_CacheDir().push_if_forced(self)

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

         assert n.cleared, n.cleared
         assert n.ninfo.updated, n.ninfo.cleared
 
+    def test_push_to_cache(self):
+        """Test the base push_to_cache() method"""
+        n = SCons.Node.Node()
+        r = n.push_to_cache()
+        assert r is None, r
+
     def test_retrieve_from_cache(self):
         """Test the base retrieve_from_cache() method"""
         n = SCons.Node.Node()

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

         except AttributeError:
             pass
 
+    def push_to_cache(self):
+        """Try to push a node into a cache
+        """
+        pass
+
     def retrieve_from_cache(self):
         """Try to retrieve the node's content from a cache
 

File src/engine/SCons/SConfTests.py

                         return None
                     def prepare(self):
                         pass
+                    def push_to_cache(self):
+                        pass
                     def retrieve_from_cache(self):
                         return 0
                     def build(self, **kw):

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

             assert n == 'path/bbb', n
             assert i == 'bbb', i
 
+            # TODO(1.5):  remove when 2.2 is minimal; replace ccc
+            # variable in find_include() call below with in-line u'ccc'.
+            try:
+                ccc = eval("u'ccc'")
+            except SyntaxError:
+                ccc = 'ccc'
+
+            n, i = s.find_include(('<', ccc), 'foo', ('path',))
+            assert n == 'path/ccc', n
+            assert i == 'ccc', i
+
         finally:
             SCons.Node.FS.find_file = save
 

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

 
         n = SCons.Node.FS.find_file(include[1], paths)
 
-        return n, intern(include[1])
+        i = include[1]
+        try:
+            i = intern(i)
+        except TypeError:
+            # Probably a unicode object; just don't worry about intern().
+            pass
+        return n, i
 
     def sort_key(self, include):
         return SCons.Node.FS._my_normcase(string.join(include))

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

     """An SCons clean task."""
     def fs_delete(self, path, pathstr, remove=1):
         try:
-            if os.path.exists(path):
-                if os.path.isfile(path):
+            if os.path.lexists(path):
+                if os.path.isfile(path) or os.path.islink(path):
                     if remove: os.unlink(path)
                     display("Removed " + pathstr)
                 elif os.path.isdir(path) and not os.path.islink(path):
                     # then delete dir itself
                     if remove: os.rmdir(path)
                     display("Removed directory " + pathstr)
+                else:
+                    errstr = "Path '%s' exists but isn't a file or directory."
+                    raise SCons.Errors.UserError(errstr % (pathstr))
+        except SCons.Errors.UserError, e:
+            print e
         except (IOError, OSError), e:
             print "scons: Could not remove '%s':" % pathstr, e.strerror
 

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

                 # fs match so we can open the SConscript.
                 fs.chdir(top, change_os_dir=1)
                 if f.rexists():
-                    _file_ = open(f.rfile().get_abspath(), "r")
+                    actual = f.rfile()
+                    _file_ = open(actual.get_abspath(), "r")
+                elif f.srcnode().rexists():
+                    actual = f.srcnode().rfile()
+                    _file_ = open(actual.get_abspath(), "r")
                 elif f.has_src_builder():
                     # The SConscript file apparently exists in a source
                     # code management system.  Build it, but then clear
                         # interpret the stuff within the SConscript file
                         # relative to where we are logically.
                         fs.chdir(ldir, change_os_dir=0)
-                        # TODO Not sure how to handle src_dir here
-                        os.chdir(f.rfile().dir.get_abspath())
+                        os.chdir(actual.dir.get_abspath())
 
                     # Append the SConscript directory to the beginning
                     # of sys.path so Python modules in the SConscript

File src/engine/SCons/Taskmaster.py

         try:
             everything_was_cached = 1
             for t in self.targets:
-                if not t.retrieve_from_cache():
+                if t.retrieve_from_cache():
+                    # Call the .built() method without calling the
+                    # .push_to_cache() method, since we just got the
+                    # target from the cache and don't need to push
+                    # it back there.
+                    t.set_state(NODE_EXECUTED)
+                    t.built()
+                else:
                     everything_was_cached = 0
                     break
             if not everything_was_cached:
                 for side_effect in t.side_effects:
                     side_effect.set_state(NODE_NO_STATE)
                 t.set_state(NODE_EXECUTED)
+                t.push_to_cache()
                 t.built()
             t.visited()
 

File src/engine/SCons/TaskmasterTests.py

     def disambiguate(self):
         return self
 
+    def push_to_cache(self):
+        pass
+
     def retrieve_from_cache(self):
         global cache_text
         if self.cached:
 
     def built(self):
         global built_text
-        built_text = built_text + " really"
+        if not self.cached:
+            built_text = built_text + " really"
 
     def has_builder(self):
         return not self.builder is None

File src/engine/SCons/Tool/MSCommon/TODO

-Last Change: Fri Oct 17 04:00 PM 2008 J
-
-- Make sure VS 6 and VS 2003 .Net work (with their own SDK)
-- See whether current unit tests can be updated and need to be rewritten from
-  scratch

File src/engine/SCons/Tool/MSCommon/common.py

     for i in keep:
         rdk[i] = re.compile('%s=(.*)' % i, re.I)
 
-    def add_env(rmatch, key):
+    def add_env(rmatch, key, dkeep=dkeep):
         plist = rmatch.group(1).split(os.pathsep)
         for p in plist:
             # Do not add empty paths (when a var ends with ;)

File src/engine/SCons/Tool/MSCommon/msvc_changes.txt

-The Visual Studio support has been totally revamped. Instead of using registry
-magic, we use the .bat files available for each version of visual studio. This
-is simpler (does not depend on the version of the compiler), more reliable, and
-just plain better.
-
-Specification
-=============
-
-Tested versions
----------------
-
-The following versions have been succesfully tested:
-  - VS 2008 (express), 32 bits
-  - VS 2005 (express), 32 bits
-  - VS 2003 (.Net, pro), 32 bits
-
-Detection
----------
-
-All tools related to the ms toolchain are detected through the same method:
-  - detect the .bat configuration file (vsvarsall.bat/vsvars32.bat
-    depending on the version) from the registry
-  - if registry does not return anything useful, use the VS*COMNTOOLS env
-    variable.
-
-A version is detected only when the .bat file actually exists on the
-filesystem.  Once the .bat file is found, it is executed through a clean
-environment, and its output is parsed to get the variables
-PATH/LIB/LIBPATH/INCLUDE. Those variables are then added to env['ENV']
-
-By default, the most recent detected version is set, and can be queried in
-env['MSVS_VERSION'] *after* the tool initialization. The version can be forced
-by setting the MSVS_VERSION variable *before* initializing the tool.
-
-SDK
----
-
-Separate SDKs are only supported for the following versions:
-        - Platform SDK 2003 R1 and R2
-        - Windows SDK. I tried the following versions: 6.0, 6.0A (SDK delivered
-          with VS 2008 express) and 6.1 (Windows SDK 2008).
-
-Previous SDKs are not available anymore on MS website, so I could not test
-them. I believe VS 6 has its own SDK included, as well as VS 2003 .Net. The SDK
-is set *after* the msvs tool.
-
-The version of the SDK can be controlled by the scons variable:
-
-    MSSDK_DIR:      If set, specifies the directory location of the
-                    SDK to be used.
-
-    MSSDK_VERSION:  If set, specifies the version of the SDK to be used.
-
-If neither of these is set, MSVS_VERSION is used to pick an appropriate
-default.
-
-Architecture
-------------
-
-env['MSVS_ARCH'] = 'x86'
-                   'amd64'
-
-If not set, the selection logic defaults to x86. Cross compiling has been
-disabled, filtering out values not included in version.SUPPORTED_ARCH.
-(No tests made with cross compiling.)
-
-Fundamental changes
-===================
-
-env["ENV"] has been expanded a bit on windows:
-  - add %SystemRoot%\system32 in the path for windows
-  - add COMSPEC (needed by the .bat file execution)
-
-Internals
-=========
-
-The code can be found in the MSVCCommon submodule:
-  - findloc: find the product dir from the registry or the shell environment
-        - versions: query the system for available versions of the VS suite on
-          the system
-        - misc: high level functions, *candidates* for the public API.
-        - sdk: specifics to the SDK detection.
-        - defaults: default values to use for the paths, to use instead of the
-          whole env parsing which can be quite slow, but less reliable. Still
-          experimental, may be removed
-  - envhelpers: functions to execute a VS .bat file, parse its output,
-    and get the variables with modified values.
-
-At this point, no function should be considered public, the exact API is not
-good yet.

File src/engine/SCons/Tool/msvs.py

 \t\t\t\tReBuildCommandLine="%(rebuildcmd)s"
 \t\t\t\tCleanCommandLine="%(cleancmd)s"
 \t\t\t\tOutput="%(runfile)s"
-\t\t\t\tPreprocessorDefinitions=""
-\t\t\t\tIncludeSearchPath=""
+\t\t\t\tPreprocessorDefinitions="%(preprocdefs)s"
+\t\t\t\tIncludeSearchPath="%(includepath)s"
 \t\t\t\tForcedIncludes=""
 \t\t\t\tAssemblySearchPath=""
 \t\t\t\tForcedUsingAssemblies=""
             rebuildcmd  = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs)
             cleancmd    = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs)
 
+            # TODO(1.5)
+            #preprocdefs = xmlify(';'.join(self.env.get('CPPDEFINES', [])))
+            #includepath = xmlify(';'.join(self.env.get('CPPPATH', [])))
+            preprocdefs = xmlify(string.join(self.env.get('CPPDEFINES', []), ';'))
+            includepath = xmlify(string.join(self.env.get('CPPPATH', []), ';'))
+
             if not env_has_buildtarget:
                 del self.env['MSVSBUILDTARGET']
 

File src/engine/SCons/Variables/VariablesTests.py

 def checkSave(file, expected):
     gdict = {}
     ldict = {}
-    execfile(file, gdict, ldict)
+    exec string.replace(open(file).read(), '\r', '\n') in gdict, ldict
     assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
 
 class VariablesTestCase(unittest.TestCase):

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

                     sys.path.insert(0, dir)
                 try:
                     values['__name__'] = filename
-                    execfile(filename, {}, values)
+                    exec string.replace(open(filename).read(), '\r', '\n') in {}, values
                 finally:
                     if dir:
                         del sys.path[0]

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

     elif 'nt' in _names:
         os.devnull = 'nul'
     os.path.devnull = os.devnull
+try:
+    os.path.lexists
+except AttributeError:
+    # Pre-2.4 Python has no os.path.lexists function
+    def lexists(path):
+        return os.path.exists(path) or os.path.islink(path)
+    os.path.lexists = lexists
 
 import shlex
 try:

File src/engine/SCons/compat/_scons_optparse.py

 
     def read_file(self, filename, mode="careful"):
         vars = {}
-        execfile(filename, vars)
+        exec string.replace(open(filename).read(), '\r', '\n') in vars
         self._update(vars, mode)
 
     def ensure_value(self, attr, value):

File src/script/scons-time.py

             tempfile.template = save_template
     return result
 
+def HACK_for_exec(cmd, *args):
+    '''
+    For some reason, Python won't allow an exec() within a function
+    that also declares an internal function (including lambda functions).
+    This function is a hack that calls exec() in a function with no
+    internal functions.
+    '''
+    if not args:          exec(cmd)
+    elif len(args) == 1:  exec cmd in args[0]
+    else:                 exec cmd in args[0], args[1]
+
 class Plotter:
     def increment_size(self, largest):
         """
                 self.title = a
 
         if self.config_file:
-            execfile(self.config_file, self.__dict__)
+            exec string.replace(open(self.config_file).read(), '\r', '\n') in self.__dict__
 
         if self.chdir:
             os.chdir(self.chdir)
 
         if format == 'ascii':
 
-            def print_function_timing(file, func):
+            for file in args:
                 try:
-                    f, line, func, time = self.get_function_profile(file, func)
+                    f, line, func, time = \
+                            self.get_function_profile(file, function_name)
                 except ValueError, e:
-                    sys.stderr.write("%s: func: %s: %s\n" % (self.name, file, e))
+                    sys.stderr.write("%s: func: %s: %s\n" %
+                                     (self.name, file, e))
                 else:
                     if f.startswith(cwd_):
                         f = f[len(cwd_):]
                     print "%.3f %s:%d(%s)" % (time, f, line, func)
 
-            for file in args:
-                print_function_timing(file, function_name)
-
         elif format == 'gnuplot':
 
             results = self.collect_results(args, self.get_function_time,
                 self.title = a
 
         if self.config_file:
-            execfile(self.config_file, self.__dict__)
+            HACK_for_exec(string.replace(open(self.config_file).read(), '\r', '\n'), self.__dict__)
 
         if self.chdir:
             os.chdir(self.chdir)
         object_name = args.pop(0)
 
         if self.config_file:
-            execfile(self.config_file, self.__dict__)
+            HACK_for_exec(string.replace(open(self.config_file).read(), '\r', '\n'), self.__dict__)
 
         if self.chdir:
             os.chdir(self.chdir)
             sys.exit(1)
 
         if self.config_file:
-            execfile(self.config_file, self.__dict__)
+            exec string.replace(open(self.config_file).read(), '\r', '\n') in self.__dict__
 
         if args:
             self.archive_list = args
                 which = a
 
         if self.config_file:
-            execfile(self.config_file, self.__dict__)
+            HACK_for_exec(string.replace(open(self.config_file).read(), '\r', '\n'), self.__dict__)
 
         if self.chdir:
             os.chdir(self.chdir)

File src/test_pychecker.py

 
 ignore = [
     'SCons/compat/__init__.py',
+    'SCons/compat/_scons_UserString.py',
+    'SCons/compat/_scons_hashlib.py',
+    'SCons/compat/_scons_itertools.py',
+    'SCons/compat/_scons_optparse.py',
+    'SCons/compat/_scons_sets.py',
+    'SCons/compat/_scons_sets15.py',
+    'SCons/compat/_scons_shlex.py',
+    'SCons/compat/_scons_subprocess.py',
+    'SCons/compat/_scons_textwrap.py',
     'SCons/compat/builtins.py',
-    'SCons/compat/_subprocess.py',
 ]
 
 u = {}
 default_arguments.extend([
     '--quiet',
     '--limit=1000',
+    # Suppress warnings about unused arguments to functions and methods.
+    # We have too many wrapper functions that intentionally only use some
+    # of their arguments.
+    '--no-argsused',
 ])
 
 if sys.platform == 'win32':
     ])
 
 per_file_arguments = {
-    'SCons/__init__.py' : [
-        '--varlist', '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"',
-    ],
+    #'SCons/__init__.py' : [
+    #    '--varlist',
+    #    '"__revision__,__version__,__build__,__buildsys__,__date__,__developer__"',
+    #],
 }
 
 pywintypes_warning = "warning: couldn't find real module for class pywintypes.error (module name: pywintypes)\n"
 
 for file in files:
 
-    file = os.path.join(src_engine, file)
-    args = default_arguments + per_file_arguments.get(file, []) + [file]
+    args = (default_arguments + 
+            per_file_arguments.get(file, []) +
+            [os.path.join(src_engine, file)])
 
     test.run(program=program, arguments=args, status=None, stderr=None)
 

File test/Clean/Option.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 {Set,Get}Option('clean') works correctly to control
+cleaning behavior.
+"""
+
+import os
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'rb').read()
+file = open(sys.argv[1], 'wb')
+file.write(contents)
+file.close()
+""")
+
+test.write('SConstruct', """
+B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'foo.out', source = 'foo.in')
+
+mode = ARGUMENTS.get('MODE')
+if mode == 'not':
+    assert not GetOption('clean')
+if mode == 'set-zero':
+    assert GetOption('clean')
+    SetOption('clean', 0)
+    assert GetOption('clean')
+if mode == 'set-one':
+    assert not GetOption('clean')
+    SetOption('clean', 1)
+    assert GetOption('clean')
+""" % locals())
+
+test.write('foo.in', '"Foo", I say!\n')
+
+test.run(arguments='foo.out MODE=not')
+test.must_match(test.workpath('foo.out'), '"Foo", I say!\n')
+
+test.run(arguments='-c foo.out MODE=set-zero')
+test.must_not_exist(test.workpath('foo.out'))
+
+test.run(arguments='foo.out MODE=none')
+test.must_match(test.workpath('foo.out'), '"Foo", I say!\n')
+
+test.run(arguments='foo.out MODE=set-one')
+test.must_not_exist(test.workpath('foo.out'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:

File test/Clean/basic.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__"
+
+"""
+Test various basic uses of the -c (clean) option.
+"""
+
+import os
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'rb').read()
+file = open(sys.argv[1], 'wb')
+file.write(contents)
+file.close()
+""")
+
+test.write('SConstruct', """
+B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B })
+env.B(target = 'foo1.out', source = 'foo1.in')
+env.B(target = 'foo2.out', source = 'foo2.xxx')
+env.B(target = 'foo2.xxx', source = 'foo2.in')
+env.B(target = 'foo3.out', source = 'foo3.in')
+env.B(target = 'foo4.out', source = 'foo4.in')
+env.NoClean('foo4.out')
+import os
+if hasattr(os, 'symlink'):
+    def symlink1(env, target, source):
+        # symlink to a file that exists
+        os.symlink(str(source[0]), str(target[0]))
+    env.Command(target = 'symlink1', source = 'foo1.in', action = symlink1)
+    def symlink2(env, target, source):
+        # force symlink to a file that doesn't exist
+        os.symlink('does_not_exist', str(target[0]))
+    env.Command(target = 'symlink2', source = 'foo1.in', action = symlink2)
+# Test handling of Builder calls that have multiple targets.
+env.Command(['touch1.out', 'touch2.out'],
+            [],
+            [Touch('${TARGETS[0]}'), Touch('${TARGETS[1]}')])
+""" % locals())
+
+test.write('foo1.in', "foo1.in\n")
+
+test.write('foo2.in', "foo2.in\n")
+
+test.write('foo3.in', "foo3.in\n")
+
+test.write('foo4.in', "foo4.in\n")
+
+test.run(arguments = 'foo1.out foo2.out foo3.out foo4.out')
+
+test.must_match(test.workpath('foo1.out'), "foo1.in\n")
+test.must_match(test.workpath('foo2.xxx'), "foo2.in\n")
+test.must_match(test.workpath('foo2.out'), "foo2.in\n")
+test.must_match(test.workpath('foo3.out'), "foo3.in\n")
+test.must_match(test.workpath('foo4.out'), "foo4.in\n")
+
+test.run(arguments = '-c foo1.out',
+         stdout = test.wrap_stdout("Removed foo1.out\n", cleaning=1))
+
+test.must_not_exist(test.workpath('foo1.out'))
+test.must_exist(test.workpath('foo2.xxx'))
+test.must_exist(test.workpath('foo2.out'))
+test.must_exist(test.workpath('foo3.out'))
+test.must_exist(test.workpath('foo4.out'))
+
+test.run(arguments = '--clean foo2.out foo2.xxx',
+         stdout = test.wrap_stdout("Removed foo2.xxx\nRemoved foo2.out\n",
+                                   cleaning=1))
+
+test.must_not_exist(test.workpath('foo1.out'))
+test.must_not_exist(test.workpath('foo2.xxx'))
+test.must_not_exist(test.workpath('foo2.out'))
+test.must_exist(test.workpath('foo3.out'))
+test.must_exist(test.workpath('foo4.out'))
+
+test.run(arguments = '--remove foo3.out',
+         stdout = test.wrap_stdout("Removed foo3.out\n", cleaning=1))
+
+test.must_not_exist(test.workpath('foo1.out'))
+test.must_not_exist(test.workpath('foo2.xxx'))
+test.must_not_exist(test.workpath('foo2.out'))
+test.must_not_exist(test.workpath('foo3.out'))
+test.must_exist(test.workpath('foo4.out'))
+
+test.run(arguments = '.')
+
+test.must_match(test.workpath('foo1.out'), "foo1.in\n")
+test.must_match(test.workpath('foo2.xxx'), "foo2.in\n")
+test.must_match(test.workpath('foo2.out'), "foo2.in\n")
+test.must_match(test.workpath('foo3.out'), "foo3.in\n")
+test.must_match(test.workpath('foo3.out'), "foo3.in\n")
+test.must_match(test.workpath('foo4.out'), "foo4.in\n")
+test.must_exist(test.workpath('touch1.out'))
+test.must_exist(test.workpath('touch2.out'))
+
+if hasattr(os, 'symlink'):
+    test.fail_test(not os.path.islink(test.workpath('symlink1')))
+    test.fail_test(not os.path.islink(test.workpath('symlink2')))
+
+test.run(arguments = '-c foo2.xxx',
+         stdout = test.wrap_stdout("Removed foo2.xxx\n", cleaning=1))
+
+test.must_match(test.workpath('foo1.out'), "foo1.in\n")
+test.must_not_exist(test.workpath('foo2.xxx'))
+test.must_match(test.workpath('foo2.out'), "foo2.in\n")
+test.must_match(test.workpath('foo3.out'), "foo3.in\n")
+test.must_match(test.workpath('foo4.out'), "foo4.in\n")
+test.must_exist(test.workpath('touch1.out'))
+test.must_exist(test.workpath('touch2.out'))
+
+test.run(arguments = '-c .')
+
+test.must_not_exist(test.workpath('foo1.out'))
+test.must_not_exist(test.workpath('foo2.out'))
+test.must_not_exist(test.workpath('foo3.out'))
+test.must_exist(test.workpath('foo4.out'))
+test.must_not_exist(test.workpath('touch1.out'))
+test.must_not_exist(test.workpath('touch2.out'))
+
+if hasattr(os, 'symlink'):
+    test.fail_test(os.path.islink(test.workpath('symlink1')))
+    test.fail_test(os.path.islink(test.workpath('symlink2')))
+
+args = 'foo1.out foo2.out foo3.out touch1.out'
+
+expect = test.wrap_stdout("""\
+Removed foo1.out
+Removed foo2.xxx
+Removed foo2.out
+Removed foo3.out
+Removed touch1.out
+Removed touch2.out
+""", cleaning=1)
+
+test.run(arguments = args)
+
+test.run(arguments = '-c -n ' + args, stdout = expect)
+
+test.run(arguments = '-n -c ' + args, stdout = expect)
+
+test.must_match(test.workpath('foo1.out'), "foo1.in\n")
+test.must_match(test.workpath('foo2.xxx'), "foo2.in\n")
+test.must_match(test.workpath('foo2.out'), "foo2.in\n")
+test.must_match(test.workpath('foo3.out'), "foo3.in\n")
+test.must_match(test.workpath('foo4.out'), "foo4.in\n")
+test.must_exist(test.workpath('touch1.out'))
+test.must_exist(test.workpath('touch2.out'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:

File test/Clean/function.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 Clean() function.
+"""
+
+import os
+
+import TestSCons
+
+_python_ = TestSCons._python_
+
+test = TestSCons.TestSCons()
+
+test.write('build.py', r"""
+import sys
+contents = open(sys.argv[2], 'rb').read()
+file = open(sys.argv[1], 'wb')
+file.write(contents)
+file.close()
+""")
+
+test.subdir('subd')
+
+subd_SConscript = os.path.join('subd', 'SConscript')
+subd_foon_in = os.path.join('subd', 'foon.in')
+subd_foox_in = os.path.join('subd', 'foox.in')
+
+test.write('SConstruct', """
+B = Builder(action = r'%(_python_)s build.py $TARGETS $SOURCES')
+env = Environment(BUILDERS = { 'B' : B }, FOO = 'foo2')
+env.B(target = 'foo1.out', source = 'foo1.in')
+env.B(target = 'foo2.out', source = 'foo2.xxx')
+foo2_xxx = env.B(target = 'foo2.xxx', source = 'foo2.in')
+env.B(target = 'foo3.out', source = 'foo3.in')
+SConscript('subd/SConscript')
+Clean(foo2_xxx, ['aux1.x'])
+env.Clean(['${FOO}.xxx'], ['aux2.x'])
+Clean('.', ['subd'])
+""" % locals())
+
+test.write(['subd', 'SConscript'], """
+Clean('.', 'foox.in')
+""")
+
+test.write('foo1.in', "foo1.in\n")
+test.write('foo2.in', "foo2.in\n")
+test.write('foo3.in', "foo3.in\n")
+test.write(['subd', 'foon.in'], "foon.in\n")
+test.write(['subd', 'foox.in'], "foox.in\n")
+test.write('aux1.x', "aux1.x\n")
+test.write('aux2.x', "aux2.x\n")
+
+test.run()
+
+expect = test.wrap_stdout("""Removed foo2.xxx
+Removed aux1.x
+Removed aux2.x
+""", cleaning=1)
+test.run(arguments = '-c foo2.xxx', stdout=expect)
+test.must_match(test.workpath('foo1.out'), "foo1.in\n")
+test.must_not_exist(test.workpath('foo2.xxx'))
+test.must_match(test.workpath('foo2.out'), "foo2.in\n")
+test.must_match(test.workpath('foo3.out'), "foo3.in\n")
+
+expect = test.wrap_stdout("Removed %s\n" % subd_foox_in, cleaning = 1)
+test.run(arguments = '-c subd', stdout=expect)
+test.must_not_exist(test.workpath('foox.in'))
+
+expect = test.wrap_stdout("""Removed foo1.out
+Removed foo2.xxx
+Removed foo2.out
+Removed foo3.out
+Removed %(subd_SConscript)s
+Removed %(subd_foon_in)s
+Removed directory subd
+""" % locals(), cleaning = 1)
+test.run(arguments = '-c -n .', stdout=expect)
+
+expect = test.wrap_stdout("""Removed foo1.out
+Removed foo2.out
+Removed foo3.out
+Removed %(subd_SConscript)s
+Removed %(subd_foon_in)s
+Removed directory subd
+""" % locals(), cleaning = 1)
+test.run(arguments = '-c .', stdout=expect)
+test.must_not_exist(test.workpath('subdir', 'foon.in'))
+test.must_not_exist(test.workpath('subdir'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:

File test/Clean/mkfifo.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 SCons reports an error when cleaning up a target directory
+containing a named pipe created with o.mkfifo().
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+if not hasattr(os, 'mkfifo'):
+    test.skip_test('No os.mkfifo() function; skipping test\n')
+
+test.write('SConstruct', """\
+Execute(Mkdir("testdir"))
+dir = Dir("testdir")
+Clean(dir, 'testdir')
+""")
+
+test.run(arguments='-Q -q', stdout='Mkdir("testdir")\n')
+
+os.mkfifo('testdir/namedpipe')
+
+expect = """\
+Mkdir("testdir")
+Path '%s' exists but isn't a file or directory.
+scons: Could not remove 'testdir': Directory not empty
+""" % os.path.join('testdir', 'namedpipe')
+
+test.run(arguments='-c -Q -q', stdout=expect)
+ 
+test.must_exist(test.workpath('testdir/namedpipe'))
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:

File test/Clean/symlinks.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 correct deletion of broken symlinks.
+"""
+
+import os
+
+import TestSCons
+
+test = TestSCons.TestSCons()
+
+if not hasattr(os, 'symlink'):
+    test.skip_test('No os.symlink() function; skipping test\n')
+
+test.write('SConstruct', """\
+Execute(Mkdir("testdir"))
+dir = Dir("testdir")
+Clean(dir, 'testdir')
+""")
+
+test.run(arguments = '-Q -q', stdout='Mkdir("testdir")\n')
+
+os.symlink('testdir/symlinksrc', 'testdir/symlinkdst')
+
+expect = """\
+Mkdir("testdir")
+Removed %s
+Removed directory testdir
+""" % os.path.join('testdir', 'symlinkdst')
+
+test.run(arguments = '-c -Q -q', stdout=expect)
+
+test.pass_test()
+
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4:

File test/Decider/unknown.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 the error when the Decider() function is handed an unknown
-function string.
-"""
-
-import TestSCons
-
-test = TestSCons.TestSCons(match = TestSCons.match_re_dotall)
-
-test.write('SConstruct', """\
-Decider('fiddle-dee-dee')
-""")
-
-expect = r"""
-scons: \*\*\* Unknown Decider value 'fiddle-dee-dee'
-""" + TestSCons.file_expr
-
-test.run(arguments = '.', status = 2, stderr = expect)
-
-test.pass_test()
+#!/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 the error when the Decider() function is handed an unknown
+function string.
+"""
+
+import TestSCons
+
+test = TestSCons.TestSCons(match = TestSCons.match_re_dotall)
+
+test.write('SConstruct', """\
+Decider('fiddle-dee-dee')
+""")
+
+expect = r"""
+scons: \*\*\* Unknown Decider value 'fiddle-dee-dee'
+""" + TestSCons.file_expr
+
+test.run(arguments = '.', status = 2, stderr = expect)
+
+test.pass_test()
 
 # Local Variables:
 # tab-width:4

File test/Deprecated/Options/Options.py

 def checkSave(file, expected):
     gdict = {}
     ldict = {}
-    execfile(file, gdict, ldict)
+    exec string.replace(open(file).read(), '\r', '\n') in gdict, ldict
     assert expected == ldict, "%s\n...not equal to...\n%s" % (expected, ldict)
 
 # First test with no command line options

File test/Deprecated/Options/chdir.py

 test.write(['bin', 'opts.cfg'], """\
 import os
 os.chdir(os.path.split(__name__)[0])
-execfile('opts2.cfg')
+import string
+exec(string.replace(open('opts2.cfg').read(), '\\r', '\\n'))
 """)
 
 test.write(['bin', 'opts2.cfg'], """\

File test/MSVC/hierarchical.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 Visual Studio with a hierarchical build.
-"""
-
-import sys
-
-import TestSCons
-
-test = TestSCons.TestSCons(match = TestSCons.match_re)
-
-if sys.platform != 'win32':
-    msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
-    test.skip_test(msg)
-
-
-
-test.subdir('src', 'build', 'out')
-
-test.write('SConstruct', """
-VariantDir('build', 'src', duplicate=0)
-SConscript('build/SConscript')
-""")
-
-test.write('src/SConscript',"""
-import os
-env = Environment()
-env.Append(CPPPATH=os.environ.get('INCLUDE', ''),
-           LIBPATH=os.environ.get('LIB', ''))
-env['PCH'] = 'StdAfx.pch'
-env['PDB'] = '#out/test.pdb'
-env['PCHSTOP'] = 'StdAfx.h'
-env.PCH('StdAfx.cpp')
-env.Program('#out/test.exe', 'test.cpp')
-""")
-
-test.write('src/test.cpp', '''
-#include "StdAfx.h"
-
-int main(void) 
-{ 
-    return 1;
-}
-''')
-
-test.write('src/StdAfx.h', '''
-#include <windows.h>
-''')
-
-test.write('src/StdAfx.cpp', '''
-#include "StdAfx.h"
-''')
-
-test.run(arguments='out', stderr=None)
-
-test.must_exist(test.workpath('out/test.pdb'))
-test.must_exist(test.workpath('build/StdAfx.pch'))
-test.must_exist(test.workpath('build/StdAfx.obj'))
-
-test.run(arguments='-c out')
-
-test.must_not_exist(test.workpath('out/test.pdb'))
-test.must_not_exist(test.workpath('build/StdAfx.pch'))
-test.must_not_exist(test.workpath('build/StdAfx.obj'))
-
-
-
-test.pass_test()
+#!/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 Visual Studio with a hierarchical build.
+"""
+
+import sys
+
+import TestSCons
+
+test = TestSCons.TestSCons(match = TestSCons.match_re)
+
+if sys.platform != 'win32':
+    msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+    test.skip_test(msg)
+
+
+
+test.subdir('src', 'build', 'out')
+
+test.write('SConstruct', """
+VariantDir('build', 'src', duplicate=0)
+SConscript('build/SConscript')
+""")
+
+test.write('src/SConscript',"""
+import os
+env = Environment()
+env.Append(CPPPATH=os.environ.get('INCLUDE', ''),
+           LIBPATH=os.environ.get('LIB', ''))
+env['PCH'] = 'StdAfx.pch'
+env['PDB'] = '#out/test.pdb'
+env['PCHSTOP'] = 'StdAfx.h'
+env.PCH('StdAfx.cpp')
+env.Program('#out/test.exe', 'test.cpp')
+""")
+
+test.write('src/test.cpp', '''
+#include "StdAfx.h"
+
+int main(void) 
+{ 
+    return 1;
+}
+''')
+
+test.write('src/StdAfx.h', '''
+#include <windows.h>
+''')
+
+test.write('src/StdAfx.cpp', '''
+#include "StdAfx.h"
+''')
+
+test.run(arguments='out', stderr=None)
+
+test.must_exist(test.workpath('out/test.pdb'))
+test.must_exist(test.workpath('build/StdAfx.pch'))
+test.must_exist(test.workpath('build/StdAfx.obj'))
+
+test.run(arguments='-c out')
+
+test.must_not_exist(test.workpath('out/test.pdb'))
+test.must_not_exist(test.workpath('build/StdAfx.pch'))
+test.must_not_exist(test.workpath('build/StdAfx.obj'))
+
+
+
+test.pass_test()
 
 # Local Variables:
 # tab-width:4

File test/MSVC/multiple-pdb.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 setting $PDB to '${TARGET}.pdb allows us to build multiple
-programs with separate .pdb files from the same environment.
-
-Under the covers, this verifies that emitters support expansion of the
-$TARGET variable (and implicitly $SOURCE), using the original specified
-list(s).
-"""
-
-import sys
-
-import TestSCons
-
-_exe = TestSCons._exe
-
-test = TestSCons.TestSCons()
-
-if sys.platform != 'win32':
-    msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
-    test.skip_test(msg)
-
-test.write('SConstruct', """\
-env = Environment(PDB = '${TARGET.base}.pdb')
-env.Program('test1.cpp')
-env.Program('test2.cpp')
-""")
-
-test.write('test1.cpp', """\
-#include <stdio.h>
-#include <stdlib.h>
-int
-main(int argc, char *argv)
-{
-    printf("test1.cpp\\n");
-    exit (0);
-}
-""")
-
-test.write('test2.cpp', """\
-#include <stdio.h>
-#include <stdlib.h>
-int
-main(int argc, char *argv)
-{
-    printf("test2.cpp\\n");
-    exit (0);
-}
-""")
-
-test.run(arguments = '.')
-
-test.must_exist('test1%s' % _exe)
-test.must_exist('test1.pdb')
-test.must_exist('test2%s' % _exe)
-test.must_exist('test2.pdb')
-
-test.pass_test()
+#!/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 setting $PDB to '${TARGET}.pdb allows us to build multiple
+programs with separate .pdb files from the same environment.
+
+Under the covers, this verifies that emitters support expansion of the
+$TARGET variable (and implicitly $SOURCE), using the original specified
+list(s).
+"""
+
+import sys
+
+import TestSCons
+
+_exe = TestSCons._exe
+
+test = TestSCons.TestSCons()
+
+if sys.platform != 'win32':
+    msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
+    test.skip_test(msg)
+
+test.write('SConstruct', """\
+env = Environment(PDB = '${TARGET.base}.pdb')
+env.Program('test1.cpp')
+env.Program('test2.cpp')
+""")
+
+test.write('test1.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+    printf("test1.cpp\\n");
+    exit (0);
+}
+""")
+
+test.write('test2.cpp', """\
+#include <stdio.h>
+#include <stdlib.h>
+int
+main(int argc, char *argv)
+{
+    printf("test2.cpp\\n");
+    exit (0);
+}
+""")
+
+test.run(arguments = '.')
+
+test.must_exist('test1%s' % _exe)
+test.must_exist('test1.pdb')
+test.must_exist('test2%s' % _exe)
+test.must_exist('test2.pdb')
+
+test.pass_test()
 
 # Local Variables:
 # tab-width:4

File test/MSVC/pdb-VariantDir-path.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 .pdb files get put in a variant_dir correctly.
-"""
-
-import sys
-
-import TestSCons
-
-_exe = TestSCons._exe
-
-test = TestSCons.TestSCons()
-
-if sys.platform != 'win32':
-    msg = "Skipping Visual C/C++ test on non-Windows platform '%s'\n" % sys.platform
-    test.skip_test(msg)
-
-test.subdir('src')
-
-test.write('SConstruct', """\
-env = Environment()
-env.Append(BINDIR = '#bin')
-
-Export('env')
-SConscript('#src/SConscript', duplicate = 0, variant_dir = '#.build')
-""")
-
-test.write(['src', 'SConscript'], """\