Add MSVS Project file support. (Greg Spencer)
authorstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 8 Jun 2003 13:22:57 +0000 (13:22 +0000)
committerstevenknight <stevenknight@fdb21ef1-2011-0410-befe-b5e4ea1792b1>
Sun, 8 Jun 2003 13:22:57 +0000 (13:22 +0000)
git-svn-id: http://scons.tigris.org/svn/scons/trunk@703 fdb21ef1-2011-0410-befe-b5e4ea1792b1

21 files changed:
bin/files
doc/man/scons.1
runtest.py
src/CHANGES.txt
src/RELEASE.txt
src/engine/MANIFEST.in
src/engine/SCons/Environment.py
src/engine/SCons/EnvironmentTests.py
src/engine/SCons/Platform/win32.py
src/engine/SCons/Script/SConscript.py
src/engine/SCons/Tool/__init__.py
src/engine/SCons/Tool/mslib.py
src/engine/SCons/Tool/mslink.py
src/engine/SCons/Tool/msvc.py
src/engine/SCons/Tool/msvs.py [new file with mode: 0644]
src/engine/SCons/Tool/msvsTests.py [new file with mode: 0644]
src/engine/SCons/Util.py
src/engine/SCons/UtilTests.py
test/import.py
test/midl.py
test/msvs.py [new file with mode: 0644]

index 86f297c5d12aa8e8fb021db0879650f6a120ac79..4e3a8631d6e9d31a0813d0e215cd9fadb4b01343 100644 (file)
--- a/bin/files
+++ b/bin/files
@@ -64,6 +64,7 @@
 ./SCons/Tool/mslib.py
 ./SCons/Tool/mslink.py
 ./SCons/Tool/msvc.py
+./SCons/Tool/msvs.py
 ./SCons/Tool/nasm.py
 ./SCons/Tool/pdflatex.py
 ./SCons/Tool/pdftex.py
index 1a1b81f48d325068f26b47bddc67d9cc6054d53f..32546ed9c553840b8ba51e319701055e4ddc2fce 100644 (file)
@@ -31,7 +31,7 @@
 .RE
 .fi
 ..
-.TH SCONS 1 "May 2003"
+.TH SCONS 1 "June 2003"
 .SH NAME
 scons \- a software construction tool
 .SH SYNOPSIS
@@ -887,6 +887,7 @@ mingw
 mslib
 mslink
 msvc
+msvs
 nasm
 pdflatex
 pdftex
@@ -1150,6 +1151,49 @@ Example:
 env.Program(target = 'foo', source = ['foo.o', 'bar.c', 'baz.f'])
 .EE
 
+.IP MSVSProject
+Builds Microsoft Visual Studio project files.
+.B scons
+will detect installed versions of Visual Studio
+up to and including versions 7.x (.NET).
+When one is detected,
+this builder will generate the correct
+project file
+.RI ( .vcproj
+or
+.IR .dsp )
+and solution file
+.RI ( .sln
+or
+.IR .dsw ).
+This builder takes a number of additional
+keyword arguments that supply information
+necessary to build the proper project files.
+Examples:
+
+.ES
+# For Visual Studio 7.0 or later (.NET).
+env.MSVSProject(target = 'Foo.vcproj',
+                slnguid = '{SLNGUID}',
+                srcs = ['foo.cpp'],
+                incs = ['sdk.h'],
+                localincs = ['foo.h'],
+                resources = ['foo.rc'],
+                misc = ['readme.txt'],
+                buildtarget = 'Foo.exe',
+                variant = 'Release')
+
+# For earlier Visual Studio versions.
+env.MSVSProject(target = 'Foo.dsp',
+                srcs = ['foo.cpp'],
+                incs = ['sdk.h'],
+                localincs = ['foo.h'],
+                resources = ['foo.rc'],
+                misc = ['readme.txt'],
+                buildtarget = 'Foo.exe',
+                variant = 'Release')
+.EE
+
 .IP RES
 Builds a Microsoft Visual C++ resource file.
 This builder is only provided
@@ -2646,6 +2690,28 @@ General options passed to the linker.
 .IP LINKCOM
 The command line used to link object files into an executable.
 
+.IP MSVSPROJECTCOM
+The action used to generate Microsoft Visual Studio
+project and solution files.
+
+.IP MSVSPROJECTSUFFIX
+The suffix used for Microsoft Visual Studio project (DSP) files.
+The default value is
+.B .vcproj
+when using Visual Studio version 7.x (.NET),
+and
+.B .dsp
+when using earlier versions of Visual Studio.
+
+.IP MSVSSOLUTIONSUFFIX
+The suffix used for Microsoft Visual Studio solution (DSW) files.
+The default value is
+.B .sln
+when using Visual Studio version 7.x (.NET),
+and
+.B .dsw
+when using earlier versions of Visual Studio.
+
 .IP no_import_lib
 When set to non-zero,
 suppresses creation of a corresponding Win32 static import lib by the
index b2176e9c660c9009cd9821503fa9523c7fb2d242..049643a12522a35d8a86a46c7836113d349a5fad 100644 (file)
@@ -309,6 +309,7 @@ elif scons_lib_dir:
 if scons_exec:
     os.environ['SCONS_EXEC'] = '1'
 
+os.environ['SCONS_SCRIPT_DIR'] = scons_script_dir
 os.environ['SCONS_CWD'] = cwd
 
 os.environ['SCONS_VERSION'] = version
index 1222d4415c03ddfac04af95c8f876d9fa8efc7f6..dd976ef5dedd26a5e4fc78864464226e69ebf54e 100644 (file)
@@ -39,6 +39,16 @@ RELEASE 0.15 - XXX
 
   - Fix use of the SConf subsystem with SConscriptChdir().
 
+  From Greg Spencer
+
+  - Check for the existence of MS Visual Studio on disk before using it,
+    to avoid getting fooled by leftover junk in the registry.
+
+  - Add support for MSVC++ .NET.
+
+  - Add support for MS Visual Studio project files (DSP, DSW,
+    SLN and VCPROJ files).
+
 
 
 RELEASE 0.14 - Wed, 21 May 2003 05:16:32 -0500
index 6d7916c39fecd44375750dfbf5141e5c9d0f8f04..467633cad7b88e5cabc3125ee8a2ef01c5880af1 100644 (file)
@@ -27,7 +27,12 @@ RELEASE 0.15 - XXX
 
   Please note the following important changes since release 0.14:
 
-  -
+  - SCons now tries to verify that Microsoft Visual Studio (including
+    Visual C++) is actually installed before using it, by checking that
+    the program directory exists.  If SCons cannot find your copy of
+    Visual Studio, it is probably because it installed itself itself in
+    a default directory that we have not seen before.  If this is the
+    case, please let us know so that we can update future versions.
 
   Please note the following important changes since release 0.13:
 
index 65d6886ee4b344dba241b2d13e9c24ccc1135e9a..f1987e750307d0cb60ae649e71d0aea4174e0203 100644 (file)
@@ -76,6 +76,7 @@ SCons/Tool/mingw.py
 SCons/Tool/mslib.py
 SCons/Tool/mslink.py
 SCons/Tool/msvc.py
+SCons/Tool/msvs.py
 SCons/Tool/nasm.py
 SCons/Tool/pdflatex.py
 SCons/Tool/pdftex.py
index ba5e279aa788cb62074efe9823d5ca6436cc9b79..0f5cb30fdde75126382dd956cd5bbb45718e8b80 100644 (file)
@@ -1,7 +1,12 @@
 """SCons.Environment
 
-XXX
+Base class for construction Environments.  These are
+the primary objects used to communicate dependency and
+construction information to the build engine.
 
+Keyword arguments supplied when the construction Environment
+is created are construction variables used to initialize the
+Environment 
 """
 
 #
@@ -33,6 +38,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 import copy
 import os
 import os.path
+import string
 import re
 import shutil
 from UserDict import UserDict
@@ -271,6 +277,45 @@ class Environment:
             else:
                 self._dict[key] = kw[key] + self._dict[key]
 
+    def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
+        """Prepend path elements to the path 'name' in the 'ENV'
+        dictionary for this environment.  Will only add any particular
+        path once, and will normpath and normcase all paths to help
+        assure this.  This can also handle the case where the env
+        variable is a list instead of a string.
+        """
+
+        orig = ''
+        if self._dict.has_key(envname) and self._dict[envname].has_key(name):
+            orig = self._dict[envname][name]
+
+        nv = SCons.Util.PrependPath(orig, newpath, sep)
+            
+        if not self._dict.has_key(envname):
+            self._dict[envname] = {}
+
+        self._dict[envname][name] = nv
+
+    def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep):
+        """Append path elements to the path 'name' in the 'ENV'
+        dictionary for this environment.  Will only add any particular
+        path once, and will normpath and normcase all paths to help
+        assure this.  This can also handle the case where the env
+        variable is a list instead of a string.
+        """
+
+        orig = ''
+        if self._dict.has_key(envname) and self._dict[envname].has_key(name):
+            orig = self._dict[envname][name]
+
+        nv = SCons.Util.AppendPath(orig, newpath, sep)
+            
+        if not self._dict.has_key(envname):
+            self._dict[envname] = {}
+
+        self._dict[envname][name] = nv
+
+
     def        Depends(self, target, dependency):
        """Explicity specify that 'target's depend on 'dependency'."""
         tlist = SCons.Node.arg2nodes(target, self.fs.File)
index aed3a9ef2def2cff9e5ba350ab4d5eb02ea59ba0..d8b4f9dc7ed82a086e7801fc85b68a7f9939a403 100644 (file)
@@ -474,6 +474,54 @@ class EnvironmentTestCase(unittest.TestCase):
         assert hasattr(env4, 'z1')
         assert hasattr(env4, 'z2')
 
+    def test_PrependENVPath(self):
+        """Test prepending to an ENV path."""
+        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+        # have to include the pathsep here so that the test will work on UNIX too.
+        env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') 
+        env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
+        assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+
+    def test_AppendENVPath(self):
+        """Test appending to an ENV path."""
+        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+        # have to include the pathsep here so that the test will work on UNIX too.
+        env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
+        env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
+        env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
+        env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
+        assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
+
+    def test_AppendENVPath(self):
+        """Test prepending to an ENV path."""
+        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+        # have to include the pathsep here so that the test will work on UNIX too.
+        env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') 
+        env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';')
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';')
+        env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';')
+        assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+
+    def test_AppendENVPath(self):
+        """Test appending to an ENV path."""
+        env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'},
+                           MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'})
+        # have to include the pathsep here so that the test will work on UNIX too.
+        env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';')
+        env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';')
+        env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';')
+        env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';')
+        assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+        assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
+
     def test_Depends(self):
        """Test the explicit Depends method."""
        env = Environment()
index 6874d0156eb09e16ab194da70caeea3bf1828a8d..080300408ee5304e02cb74702b6504df8110cbbc 100644 (file)
@@ -68,11 +68,7 @@ class TempFileMunge:
             # a bug in Win32 that will use a forward slash as a path
             # delimiter.  Win32's link mistakes that for a command line
             # switch and barfs.
-            #
-            # We use the .lnk suffix for the benefit of the Phar Lap
-            # linkloc linker, which likes to append an .lnk suffix if
-            # none is given.
-            tmp = os.path.normpath(tempfile.mktemp('.lnk'))
+            tmp = os.path.normpath(tempfile.mktemp())
             native_tmp = SCons.Util.get_native_path(tmp)
 
             # The sh shell will try to escape the backslashes in the
@@ -160,10 +156,18 @@ def spawn(sh, escape, cmd, args, env):
             sys.stderr.write("scons: %s: %s\n" % (cmd, e[1]))
         return ret
 
-# Windows does not allow special characters in file names
-# anyway, so no need for an escape function, we will just quote
-# the arg.
-escape = lambda x: '"' + x + '"'
+# We just quote the arg here, but since the escape for a double
+# quote in the command processor (I hesitate to call it a shell :-) is
+# to double it (i.e. '""' => '"' in the command processor), we have to
+# make sure not to double any double quotes on the ends.
+def escape(x):
+    first = '"'
+    last = '"'
+    if x and x[0] == '"':
+        first = '" '
+    if x and x[-1] == '"':
+        last = ' "'
+    return first + x + last
 
 # Get the windows system directory name
 def get_system_root():
@@ -233,10 +237,30 @@ def generate(env):
                 cmd_interp = os.path.join(val, 'command.com')
             except:
                 pass
+
+    # For the special case of not having access to the registry, we
+    # use a temporary path and pathext to attempt to find the command
+    # interpreter.  If we fail, we try to find the interpreter through
+    # the env's PATH.  The problem with that is that it might not
+    # contain an ENV and a PATH.
+    if not cmd_interp:
+        systemroot = r'C:\Windows'
+        if os.environ.has_key('SYSTEMROOT'):
+            systemroot = os.environ['SYSTEMROOT']
+        tmp_path = systemroot + os.pathsep + \
+                   os.path.join(systemroot,'System32')
+        tmp_pathext = '.com;.exe;.bat;.cmd'
+        if os.environ.has_key('PATHEXT'):
+            tmp_pathext = os.environ['PATHEXT'] 
+        cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext)
+        if not cmd_interp:
+            cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext)
+
     if not cmd_interp:
         cmd_interp = env.Detect('cmd')
         if not cmd_interp:
             cmd_interp = env.Detect('command')
+
     
     if not env.has_key('ENV'):
         env['ENV']        = {}
index 5258d9027e826e56b6363e3dcb661dec76d20919..1e14658d74bc94f7fd8de0f9eab1998974b9af06 100644 (file)
@@ -112,11 +112,16 @@ def compute_exports(exports):
 
 class Frame:
     """A frame on the SConstruct/SConscript call stack"""
-    def __init__(self, exports):
+    def __init__(self, exports, sconscript):
         self.globals = BuildDefaultGlobals()
         self.retval = None 
         self.prev_dir = SCons.Node.FS.default_fs.getcwd()
         self.exports = compute_exports(exports)  # exports from the calling SConscript
+        # make sure the sconscript attr is a Node.
+        if isinstance(sconscript, SCons.Node.Node):
+            self.sconscript = sconscript
+        else:
+            self.sconscript = SCons.Node.FS.default_fs.File(str(sconscript))
         
 # the SConstruct/SConscript call stack:
 stack = []
@@ -215,7 +220,7 @@ def SConscript(*ls, **kw):
     # evaluate each SConscript file
     results = []
     for fn in files:
-        stack.append(Frame(exports))
+        stack.append(Frame(exports,fn))
         old_sys_path = sys.path
         try:
             if fn == "-":
index 7cf9b292b0fed774b745a11b7824337f3a37d1a5..b03946046974be16ca17b38da1716c630fe14212 100644 (file)
@@ -228,7 +228,7 @@ def tool_list(platform, env):
     other_tools = FindAllTools(['BitKeeper', 'CVS',
                                 'dvipdf', 'dvips', 'gs',
                                 'jar', 'javac', 'javah',
-                                'latex', 'lex', 'midl',
+                                'latex', 'lex', 'midl', 'msvs',
                                 'pdflatex', 'pdftex', 'Perforce',
                                 'RCS', 'rmic', 'SCCS',
                                 # 'Subversion',
index 4180e05c3eee5c66635b08205863c54c3cc24ce8..93a36cc4b55c5843609a0b65038787fae5c10aae 100644 (file)
@@ -34,15 +34,32 @@ selection method.
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import SCons.Defaults
+import SCons.Tool.msvs
+import SCons.Tool.msvc
 
 def generate(env):
     """Add Builders and construction variables for lib to an Environment."""
     env['BUILDERS']['Library'] = SCons.Defaults.StaticLibrary
     env['BUILDERS']['StaticLibrary'] = SCons.Defaults.StaticLibrary
-    
+
+    version = SCons.Tool.msvs.get_default_visualstudio_version(env)
+
+    if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
+        include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(version)
+    else:
+        include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(version)
+
+    # since other tools can set this, we just make sure that the
+    # relevant stuff from MSVS is in there somewhere.
+    env.PrependENVPath('PATH', exe_path)
+
     env['AR']          = 'lib'
     env['ARFLAGS']     = '/nologo'
     env['ARCOM']       = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}"
 
 def exists(env):
-    return env.Detect('lib')
+    if not SCons.Util.can_read_reg or not SCons.Tool.msvs.get_visualstudio_versions():
+        return env.Detect('lib')
+    else:
+        # there's at least one version of MSVS installed.
+        return True
index 8aa6f09da2bd4d216db2727fa87f1f2c1f03e4c7..5a8a729f02961f7b0c9843b203912d5451937a59 100644 (file)
@@ -40,9 +40,9 @@ import SCons.Action
 import SCons.Defaults
 import SCons.Errors
 import SCons.Util
-import msvc
-
-from SCons.Tool.msvc import get_msdev_paths
+import SCons.Tool.msvs
+import SCons.Tool.msvc
+import SCons.Platform.win32
 
 def pdbGenerator(env, target, source, for_signature):
     if target and env.has_key('PDB') and env['PDB']:
@@ -73,7 +73,7 @@ def win32ShlinkSources(target, source, env, for_signature):
     return listCmd
     
 def win32LibEmitter(target, source, env):
-    msvc.validate_vars(env)
+    SCons.Tool.msvc.validate_vars(env)
     
     dll = env.FindIxes(target, "SHLIBPREFIX", "SHLIBSUFFIX")
     no_import_lib = env.get('no_import_lib', 0)
@@ -107,7 +107,7 @@ def win32LibEmitter(target, source, env):
     return (target, source)
 
 def prog_emitter(target, source, env):
-    msvc.validate_vars(env)
+    SCons.Tool.msvc.validate_vars(env)
     
     if env.has_key('PDB') and env['PDB']:
         env.SideEffect(env['PDB'], target)
@@ -159,14 +159,26 @@ def generate(env):
     env['WIN32EXPSUFFIX']        = '.exp'
 
     env['REGSVRACTION'] = regServerCheck
-    env['REGSVR'] = 'regsvr32'
+    env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32')
     env['REGSVRFLAGS'] = '/s '
     env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS $TARGET'
 
-    if SCons.Util.can_read_reg:
-        include_path, lib_path, exe_path = get_msdev_paths()
-        env['ENV']['LIB']            = lib_path
-        env['ENV']['PATH']           = exe_path
+    version = SCons.Tool.msvs.get_default_visualstudio_version(env)
+    
+    if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
+        include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(version)
+    else:
+        include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(version)
+
+    # since other tools can set these, we just make sure that the
+    # relevant stuff from MSVS is in there somewhere.
+    env.PrependENVPath('INCLUDE', include_path)
+    env.PrependENVPath('LIB', lib_path)
+    env.PrependENVPath('PATH', exe_path)
 
 def exists(env):
-    return env.Detect('link')
+    if not SCons.Util.can_read_reg or not SCons.Tool.msvs.get_visualstudio_versions():
+        return env.Detect('link')
+    else:
+        # there's at least one version of MSVS installed.
+        return True
index 6f0c516379aa1c287d398d77aa6a84122605b19d..f936535e845a4daf2cfd680ef8bcbb7cb995d232 100644 (file)
@@ -35,59 +35,121 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
 import os.path
 import string
+import types
+import re
 
 import SCons.Action
 import SCons.Tool
 import SCons.Errors
+import SCons.Warnings
 import SCons.Builder
 import SCons.Util
 import SCons.Platform.win32
+import SCons.Tool.msvs
 
 CSuffixes = ['.c', '.C']
 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
 
-def get_devstudio_versions():
-    """
-    Get list of devstudio versions from the Windows registry.  Return a
-    list of strings containing version numbers; an exception will be raised
-    if we were unable to access the registry (eg. couldn't import
-    a registry-access module) or the appropriate registry keys weren't
-    found.
-    """
-
+def _parse_msvc7_overrides(version):
+    """ Parse any overridden defaults for MSVS directory locations in MSVS .NET. """
+    
+    # First, we get the shell folder for this user:
     if not SCons.Util.can_read_reg:
         raise SCons.Errors.InternalError, "No Windows registry module was found"
 
-    K = 'Software\\Microsoft\\Devstudio'
-    L = []
-    for base in (SCons.Util.HKEY_CLASSES_ROOT,
-                 SCons.Util.HKEY_LOCAL_MACHINE,
-                 SCons.Util.HKEY_CURRENT_USER,
-                 SCons.Util.HKEY_USERS):
+    comps = ""
+    try:
+        (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
+                                            r'Software\Microsoft\Windows\CurrentVersion' +\
+                                            r'\Explorer\Shell Folders\Local AppData')
+    except SCons.Util.RegError:
+        raise SCons.Errors.InternalError, "The Local AppData directory was not found in the registry."
+
+    comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VSComponents.dat'
+    dirs = {}
+
+    if os.path.exists(comps):
+        # now we parse the directories from this file, if it exists.
+        # We only look for entries after: [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories],
+        # since this file could contain a number of things...
+        f = open(comps,'r')
+        line = f.readline()
+        found = 0
+        while line:
+            line.strip()
+            if found == 1:
+                (key, val) = line.split('=',1)
+                key = key.replace(' Dirs','')
+                dirs[key.upper()] = val
+            if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0:
+                found = 1
+            if line == '':
+                found = 0
+            line = f.readline()
+        f.close()
+    else:
+        # since the file didn't exist, we have only the defaults in
+        # the registry to work with.
         try:
-            k = SCons.Util.RegOpenKeyEx(base,K)
+            K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
+            K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories'
+            k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
             i = 0
             while 1:
                 try:
-                    p = SCons.Util.RegEnumKey(k,i)
-                    if p[0] in '123456789' and p not in L:
-                        L.append(p)
+                    (key,val,t) = SCons.Util.RegEnumValue(k,i)
+                    key = key.replace(' Dirs','')
+                    dirs[key.upper()] = val
+                    i = i + 1
                 except SCons.Util.RegError:
                     break
-                i = i + 1
         except SCons.Util.RegError:
-            pass
+            # if we got here, then we didn't find the registry entries:
+            raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
+    return dirs
+
+def _get_msvc7_path(path, version, platform):
+    """
+    Get Visual Studio directories from version 7 (MSVS .NET)
+    (it has a different registry structure than versions before it)
+    """
+    # first, look for a customization of the default values in the
+    # registry: These are sometimes stored in the Local Settings area
+    # for Visual Studio, in a file, so we have to parse it.
+    dirs = _parse_msvc7_overrides(version)
+
+    if dirs.has_key(path):
+        p = dirs[path]
+    else:
+        raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path
+
+    # collect some useful information for later expansions...
+    paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
+
+    # expand the directory path variables that we support.  If there
+    # is a variable we don't support, then replace that entry with
+    # "---Unknown Location VSInstallDir---" or something similar, to clue
+    # people in that we didn't find something, and so env expansion doesn't
+    # do weird things with the $(xxx)'s
+    s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
+
+    def repl(match):
+        key = string.upper(match.group(1))
+        if paths.has_key(key):
+            return paths[key]
+        else:
+            return '---Unknown Location %s---' % match.group()
 
-    if not L:
-        raise SCons.Errors.InternalError, "DevStudio was not found."
+    rv = []
+    for entry in p.split(os.pathsep):
+        entry = s.sub(repl,entry)
+        rv.append(entry)
 
-    L.sort()
-    L.reverse()
-    return L
+    return string.join(rv,os.pathsep)
 
 def get_msvc_path (path, version, platform='x86'):
     """
-    Get a list of devstudio directories (include, lib or path).  Return
+    Get a list of visualstudio directories (include, lib or path).  Return
     a string delimited by ';'. An exception will be raised if unable to
     access the registry or appropriate registry keys not found.
     """
@@ -95,16 +157,22 @@ def get_msvc_path (path, version, platform='x86'):
     if not SCons.Util.can_read_reg:
         raise SCons.Errors.InternalError, "No Windows registry module was found"
 
-    if path=='lib':
-        path= 'Library'
+    # normalize the case for comparisons (since the registry is case
+    # insensitive)
+    path = string.upper(path)
+
+    if path=='LIB':
+        path= 'LIBRARY'
+
+    if float(version) >= 7.0:
+        return _get_msvc7_path(path, version, platform)
+
     path = string.upper(path + ' Dirs')
     K = ('Software\\Microsoft\\Devstudio\\%s\\' +
          'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
         (version,platform)
-    for base in (SCons.Util.HKEY_CLASSES_ROOT,
-                 SCons.Util.HKEY_LOCAL_MACHINE,
-                 SCons.Util.HKEY_CURRENT_USER,
-                 SCons.Util.HKEY_USERS):
+    for base in (SCons.Util.HKEY_CURRENT_USER,
+                 SCons.Util.HKEY_LOCAL_MACHINE):
         try:
             k = SCons.Util.RegOpenKeyEx(base,K)
             i = 0
@@ -120,77 +188,127 @@ def get_msvc_path (path, version, platform='x86'):
             pass
 
     # if we got here, then we didn't find the registry entries:
-    raise SCons.Errors.InternalError, "%s was not found in the registry."%path
-
-def get_msdev_dir(version):
-    """Returns the root directory of the MSDev installation from the
-    registry if it can be found, otherwise we guess."""
-    if SCons.Util.can_read_reg:
-        K = ('Software\\Microsoft\\Devstudio\\%s\\' +
-             'Products\\Microsoft Visual C++') % \
-             version
-        for base in (SCons.Util.HKEY_LOCAL_MACHINE,
-                     SCons.Util.HKEY_CURRENT_USER):
-            try:
-                k = SCons.Util.RegOpenKeyEx(base,K)
-                val, tok = SCons.Util.RegQueryValueEx(k, 'ProductDir')
-                return os.path.split(val)[0]
-            except SCons.Util.RegError:
-                pass
-
-def get_msdev_paths(version=None):
+    raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
+
+def _get_msvc6_default_paths(version):
+    """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
+    three environment variables that should be set in order to execute
+    the MSVC 6.0 tools properly, if the information wasn't available
+    from the registry."""
+    MVSdir = None
+    paths = {}
+    exe_path = ''
+    lib_path = ''
+    include_path = ''
+    try:
+        paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
+        MVSdir = paths['VSINSTALLDIR']
+    except (SCons.Util.RegError, SCons.Errors.InternalError):
+        if os.environ.has_key('MSDEVDIR'):
+            MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
+        else:
+            MVSdir = r'C:\Program Files\Microsoft Visual Studio'
+    if MVSdir:
+        if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
+            MVSVCdir = paths['VCINSTALLDIR']
+        else:
+            MVSVCdir = os.path.join(MVSdir,'VC98')
+
+        MVSCommondir = r'%s\Common' % MVSdir
+        include_path = r'%s\ATL\include;%s\MFC\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
+        lib_path = r'%s\MFC\lib;%s\lib' % (MVSVCdir, MVSVCdir)
+        exe_path = r'%s\MSDev98\bin;%s\bin' % (MVSCommondir, MVSVCdir)
+    return (include_path, lib_path, exe_path)
+
+def _get_msvc7_default_paths(version):
+    """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
+    three environment variables that should be set in order to execute
+    the MSVC .NET tools properly, if the information wasn't available
+    from the registry."""
+            
+    MVSdir = None
+    paths = {}
+    exe_path = ''
+    lib_path = ''
+    include_path = ''
+    try:
+        paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
+        MVSdir = paths['VSINSTALLDIR']
+    except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
+        if os.environ.has_key('VSCOMNTOOLS'):
+            MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
+        else:
+            # last resort -- default install location
+            MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
+
+    if not MVSdir:
+        if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
+            MVSVCdir = paths['VCINSTALLDIR']
+        else:
+            MVSVCdir = os.path.join(MVSdir,'Vc7')
+
+        MVSCommondir = r'%s\Common7' % MVSdir
+        include_path = r'%s\atlmfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
+        lib_path = r'%s\atlmfc\lib;%s\lib' % (MVSVCdir, MVSVCdir)
+        exe_path = r'%s\Tools\bin;%s\Tools;%s\bin' % (MVSCommondir, MVSCommondir, MVSVCdir)
+    return (include_path, lib_path, exe_path)
+
+def get_msvc_paths(version=None):
     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
     of those three environment variables that should be set
     in order to execute the MSVC tools properly."""
     exe_path = ''
     lib_path = ''
     include_path = ''
+
+    if not version and not SCons.Util.can_read_reg:
+        version = '6.0'
+            
     try:
         if not version:
-            version = get_devstudio_versions()[0] #use highest version
+            version = get_visualstudio_versions()[0] #use highest version
+
         include_path = get_msvc_path("include", version)
         lib_path = get_msvc_path("lib", version)
-        exe_path = get_msvc_path("path", version) + ";" + os.environ['PATH']
+        exe_path = get_msvc_path("path", version)
+
     except (SCons.Util.RegError, SCons.Errors.InternalError):
-        # Could not get the configured directories from the registry.
-        # However, the configured directories only appear if the user
-        # changes them from the default.  Therefore, we'll see if
-        # we can get the path to the MSDev base installation from
-        # the registry and deduce the default directories.
-        MVSdir = None
-        if version:
-            MVSdir = get_msdev_dir(version)
-        if MVSdir:
-            MVSVCdir = r'%s\VC98' % MVSdir
-            MVSCommondir = r'%s\Common' % MVSdir
-            include_path = r'%s\atl\include;%s\mfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
-            lib_path = r'%s\mfc\lib;%s\lib' % (MVSVCdir, MVSVCdir)
-            try:
-                extra_path = os.pathsep + os.environ['PATH']
-            except KeyError:
-                extra_path = ''
-            exe_path = (r'%s\MSDev98\Bin;%s\Bin' % (MVSCommondir, MVSVCdir)) + extra_path
+        # Could not get all the configured directories from the
+        # registry.  However, some of the configured directories only
+        # appear if the user changes them from the default.
+        # Therefore, we'll see if we can get the path to the MSDev
+        # base installation from the registry and deduce the default
+        # directories.
+        if float(version) >= 7.0:
+            return _get_msvc7_default_paths(version)
         else:
-            # The DevStudio environment variables don't exist,
-            # so just use the variables from the source environment.
-            progfiles = SCons.Platform.win32.get_program_files_dir()
-            MVSdir = os.path.join(progfiles,r'Microsoft Visual Studio')
-            MVSVCdir = r'%s\VC98' % MVSdir
-            MVSCommondir = r'%s\Common' % MVSdir
-            try:
-                include_path = os.environ['INCLUDE']
-            except KeyError:
-                include_path = ''
-            try:
-                lib_path = os.environ['LIB']
-            except KeyError:
-                lib_path = ''
-            try:
-                exe_path = os.environ['PATH']
-            except KeyError:
-                exe_path = ''
+            return _get_msvc6_default_paths(version)
+            
     return (include_path, lib_path, exe_path)
 
+def get_msvc_default_paths(version = None):
+    """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
+    three environment variables that should be set in order to execute
+    the MSVC tools properly.  This will only return the default
+    locations for the tools, not the values used by MSVS in their
+    directory setup area.  This can help avoid problems with different
+    developers having different settings, and should allow the tools
+    to run in most cases."""
+
+    if not version and not SCons.Util.can_read_reg:
+        version = '6.0'
+
+    try:
+        if not version:
+            version = get_visualstudio_versions()[0] #use highest version
+    except:
+        pass
+
+    if float(version) >= 7.0:
+        return _get_msvc7_default_paths(version)
+    else:
+        return _get_msvc6_default_paths(version)
+
 def validate_vars(env):
     """Validate the PDB, PCH, and PCHSTOP construction variables."""
     if env.has_key('PCH') and env['PCH']:
@@ -282,10 +400,18 @@ def generate(env):
         CScan.add_skey('.rc')
     env['BUILDERS']['RES'] = res_builder
 
-    if SCons.Util.can_read_reg:
-        include_path, lib_path, exe_path = get_msdev_paths()
-        env['ENV']['INCLUDE'] = include_path
-        env['ENV']['PATH']    = exe_path
+    version = SCons.Tool.msvs.get_default_visualstudio_version(env)
+
+    if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
+        include_path, lib_path, exe_path = get_msvc_default_paths(version)
+    else:
+        include_path, lib_path, exe_path = get_msvc_paths(version)
+
+    # since other tools can set these, we just make sure that the
+    # relevant stuff from MSVS is in there somewhere.
+    env.PrependENVPath('INCLUDE', include_path)
+    env.PrependENVPath('LIB', lib_path)
+    env.PrependENVPath('PATH', exe_path)
 
     env['CFILESUFFIX'] = '.c'
     env['CXXFILESUFFIX'] = '.cc'
@@ -294,4 +420,8 @@ def generate(env):
     env['BUILDERS']['PCH'] = pch_builder
 
 def exists(env):
-    return env.Detect('cl')
+    if not SCons.Util.can_read_reg or not SCons.Tool.msvs.get_visualstudio_versions():
+        return env.Detect('cl')
+    else:
+        # there's at least one version of MSVS installed.
+        return True
diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py
new file mode 100644 (file)
index 0000000..9b8eb41
--- /dev/null
@@ -0,0 +1,1030 @@
+"""SCons.Tool.msvs
+
+Tool-specific initialization for Microsoft Visual Studio project files.
+
+There normally shouldn't be any need to import this module directly.
+It will usually be imported through the generic SCons.Tool.Tool()
+selection method.
+
+"""
+
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import base64
+import md5
+import os.path
+import pickle
+import re
+import string
+import sys
+import types
+
+import SCons.Builder
+import SCons.Node.FS
+import SCons.Platform.win32
+import SCons.Script.SConscript
+import SCons.Util
+import SCons.Warnings
+
+##############################################################################
+# Below here are the classes and functions for generation of
+# DSP/DSW/SLN/VCPROJ files.
+##############################################################################
+
+def _hexdigest(s):
+    """Return a string as a string of hex characters.
+    """
+    # NOTE:  This routine is a method in the Python 2.0 interface
+    # of the native md5 module, but we want SCons to operate all
+    # the way back to at least Python 1.5.2, which doesn't have it.
+    h = string.hexdigits
+    r = ''
+    for c in s:
+        i = ord(c)
+        r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
+    return r
+
+def _generateGUID(slnfile, name):
+    """This generates a dummy GUID for the sln file to use.  It is
+    based on the MD5 signatures of the sln filename plus the name of
+    the project.  It basically just needs to be unique, and not
+    change with each invocation."""
+    solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper()
+    # convert most of the signature to GUID form (discard the rest)
+    solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:28] + "}"
+    return solution
+
+class Config:
+    pass
+
+class _DSPGenerator:
+    """ Base class for DSP generators """
+    def __init__(self, dspfile, source, env):
+        if type(dspfile) == types.StringType:
+            self.dspfile = os.path.abspath(dspfile)
+        else:
+            self.dspfile = dspfile.get_abspath()
+
+        try:
+            self.conspath = source[0].attributes.sconstruct.get_abspath()
+        except KeyError:
+            raise SCons.Errors.InternalError, \
+                  "Unable to determine where the SConstruct is"
+
+        self.config = Config()
+        if env.has_key('variant'):
+            self.config.variant = env['variant'].capitalize()
+        else:
+            raise SCons.Errors.InternalError, \
+                  "You must specify a 'variant' argument (i.e. 'Debug' or " +\
+                  "'Release') to create an MSVSProject."
+
+        if env.has_key('buildtarget'):
+            if type(env['buildtarget']) == types.StringType:
+                self.config.buildtarget = os.path.abspath(env['buildtarget'])
+            elif type(env['buildtarget']) == types.ListType:
+                self.config.buildtarget = env['buildtarget'][0].get_abspath()
+            else:
+                self.config.buildtarget = env['buildtarget'].get_abspath()
+        else:
+            raise SCons.Errors.InternalError, \
+                  "You must specify a target 'buildtarget' file argument (such as the target" +\
+                  " executable) to create an MSVSProject."
+
+        self.config.outdir = os.path.dirname(self.config.buildtarget)
+
+        if type(source[0]) == types.StringType:
+            self.source = os.path.abspath(source[0])
+        else:
+            self.source = source[0].get_abspath()
+            
+        self.env = env
+
+        if self.env.has_key('name'):
+            self.name = self.env['name']
+        else:
+            self.name = os.path.basename(os.path.splitext(self.dspfile)[0])
+
+        print "Adding '" + self.name + ' - ' + self.config.variant + "' to Visual Studio Project '" + str(dspfile) + "'"
+
+        sourcenames = [
+            ' Source Files',
+            'Header Files',
+            'Local Headers',
+            'Resource Files',
+            'Other Files']
+
+        srcargs = [
+            'srcs',
+            'incs',
+            'localincs',
+            'resources',
+            'misc']
+
+        self.sources = {}
+        for n in sourcenames:
+            self.sources[n] = []
+
+        self.configs = {}
+
+        if os.path.exists(self.dspfile):
+            self.Parse()
+
+        for t in zip(sourcenames,srcargs):
+            if self.env.has_key(t[1]):
+                if type(self.env[t[1]]) == types.ListType:
+                    for i in self.env[t[1]]:
+                        if not i in self.sources[t[0]]:
+                            self.sources[t[0]].append(i)
+                else:
+                    if not self.env[t[1]] in self.sources[t[0]]:
+                        self.sources[t[0]].append(self.env[t[1]])
+
+        for n in sourcenames:
+            self.sources[n].sort()
+
+        self.configs[self.config.variant] = self.config
+
+    def Build(self):
+        pass
+        
+class _GenerateV6DSP(_DSPGenerator):
+    """Generates a Project file for MSVS 6.0"""
+
+    def PrintHeader(self):
+        name = self.name
+        # pick a default config
+        confkeys = self.configs.keys()
+        confkeys.sort()
+
+        self.file.write('# Microsoft Developer Studio Project File - Name="%s" - Package Owner=<4>\n'
+                        '# Microsoft Developer Studio Generated Build File, Format Version 6.00\n'
+                        '# ** DO NOT EDIT **\n\n'
+                        '# TARGTYPE "Win32 (x86) External Target" 0x0106\n\n'
+                        'CFG=%s - Win32 %s\n'
+                        '!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n'
+                        '!MESSAGE use the Export Makefile command and run\n'
+                        '!MESSAGE \n'
+                        '!MESSAGE NMAKE /f "%s.mak".\n'
+                        '!MESSAGE \n'
+                        '!MESSAGE You can specify a configuration when running NMAKE\n'
+                        '!MESSAGE by defining the macro CFG on the command line. For example:\n'
+                        '!MESSAGE \n'
+                        '!MESSAGE NMAKE /f "%s.mak" CFG="%s - Win32 %s"\n'
+                        '!MESSAGE \n'
+                        '!MESSAGE Possible choices for configuration are:\n'
+                        '!MESSAGE \n' % (name,name,confkeys[0],name,name,name,confkeys[0]))
+
+        for kind in confkeys:
+            self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind))
+            
+        self.file.write('!MESSAGE \n\n')
+
+    def PrintProject(self):
+        name = self.name
+        self.file.write('# Begin Project\n'
+                        '# PROP AllowPerConfigDependencies 0\n'
+                        '# PROP Scc_ProjName ""\n'
+                        '# PROP Scc_LocalPath ""\n\n')
+
+        first = 1
+        confkeys = self.configs.keys()
+        confkeys.sort()
+        for kind in confkeys:
+            outdir = self.configs[kind].outdir
+            buildtarget = self.configs[kind].buildtarget
+            if first == 1:
+                self.file.write('!IF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
+                first = 0
+            else:
+                self.file.write('\n!ELSEIF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
+
+            # have to write this twice, once with the BASE settings, and once without
+            for base in ("BASE ",""):
+                self.file.write('# PROP %sUse_MFC 0\n'
+                                '# PROP %sUse_Debug_Libraries ' % (base, base))
+                if kind.lower().find('debug') < 0:
+                    self.file.write('0\n')
+                else:
+                    self.file.write('1\n')
+                self.file.write('# PROP %sOutput_Dir "%s"\n'
+                                '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir))
+                (d,c) = os.path.split(str(self.conspath))
+                cmd = '%s %s -C %s -f %s %s' % (sys.executable, os.path.normpath(sys.argv[0]), d, c, buildtarget)
+                self.file.write('# PROP %sCmd_Line "%s"\n' 
+                                '# PROP %sRebuild_Opt "-c && %s"\n'
+                                '# PROP %sTarget_File "%s"\n'
+                                '# PROP %sBsc_Name ""\n'
+                                '# PROP %sTarget_Dir ""\n'\
+                                %(base,cmd,base,cmd,base,buildtarget,base,base))
+            
+        self.file.write('\n!ENDIF\n\n'
+                        '# Begin Target\n\n')
+        for kind in confkeys:
+            self.file.write('# Name "%s - Win32 %s"\n' % (name,kind))
+        self.file.write('\n')
+        first = 0
+        for kind in confkeys:
+            if first == 0:
+                self.file.write('!IF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
+                first = 1
+            else:
+                self.file.write('!ELSEIF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
+        self.file.write('!ENDIF \n\n')
+        self.PrintSourceFiles()
+        self.file.write('# End Target\n'
+                        '# End Project\n')
+        
+        # now we pickle some data and add it to the file -- MSDEV will ignore it.
+        pdata = pickle.dumps(self.configs,True)
+        pdata = base64.encodestring(pdata)
+        self.file.write(pdata + '\n')
+        pdata = pickle.dumps(self.sources,True)
+        pdata = base64.encodestring(pdata)
+        self.file.write(pdata + '\n')
+  
+    def PrintSourceFiles(self):
+        categories = {' Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat',
+                      'Header Files': 'h|hpp|hxx|hm|inl',
+                      'Local Headers': 'h|hpp|hxx|hm|inl',
+                      'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe',
+                      'Other Files': ''}
+
+        cats = categories.keys()
+        cats.sort()
+        for kind in cats:
+            if not self.sources[kind]:
+                continue # skip empty groups
+            
+            self.file.write('# Begin Group "' + kind + '"\n\n')
+            typelist = categories[kind].replace('|',';')
+            self.file.write('# PROP Default_Filter "' + typelist + '"\n')
+            
+            for file in self.sources[kind]:
+                file = os.path.normpath(file)
+                self.file.write('# Begin Source File\n\n'
+                                'SOURCE="' + file + '"\n'
+                                '# End Source File\n')
+            self.file.write('# End Group\n')
+
+        # add the Conscript file outside of the groups
+        self.file.write('# Begin Source File\n\n'
+                        'SOURCE="' + str(self.source) + '"\n'
+                        '# End Source File\n')
+
+    def Parse(self):
+        try:
+            dspfile = file(self.dspfile,'r')
+        except IOError:
+            return # doesn't exist yet, so can't add anything to configs.
+
+        line = dspfile.readline()
+        while line:
+            if line.find("# End Project") > -1:
+                break
+            line = dspfile.readline()
+
+        line = dspfile.readline()
+        datas = line
+        while line and line != '\n':
+            line = dspfile.readline()
+            datas = datas + line
+
+        # OK, we've found our little pickled cache of data.
+        try:
+            datas = base64.decodestring(datas)
+            data = pickle.loads(datas)
+        except:
+            return # unable to unpickle any data for some reason
+
+        self.configs.update(data)
+
+        data = None
+        line = dspfile.readline()
+        datas = line
+        while line and line != '\n':
+            line = dspfile.readline()
+            datas = datas + line
+
+        # OK, we've found our little pickled cache of data.
+        # it has a "# " in front of it, so we strip that.
+        try:
+            datas = base64.decodestring(datas)
+            data = pickle.loads(datas)
+        except:
+            return # unable to unpickle any data for some reason
+
+        self.sources.update(data)
+    
+    def Build(self):
+        try:
+            self.file = file(self.dspfile,'w')
+        except IOError, detail:
+            raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail)
+        else:
+            self.PrintHeader()
+            self.PrintProject()
+            self.file.close()
+
+class _GenerateV7DSP(_DSPGenerator):
+    """Generates a Project file for MSVS .NET"""
+
+    def PrintHeader(self):
+        self.file.write('<?xml version="1.0" encoding = "Windows-1252"?>\n'
+                        '<VisualStudioProject\n'
+                        '      ProjectType="Visual C++"\n'
+                        '      Version="7.00"\n'
+                        '      Name="%s"\n'
+                        '      SccProjectName=""\n'
+                        '      SccLocalPath=""\n'
+                        '      Keyword="MakeFileProj">\n'
+                        '      <Platforms>\n'
+                        '              <Platform\n'
+                        '                      Name="Win32"/>\n'
+                        '      </Platforms>\n' % self.name)
+
+    def PrintProject(self):
+        
+
+        self.file.write('      <Configurations>\n')
+
+        first = 1
+        confkeys = self.configs.keys()
+        confkeys.sort()
+        for kind in confkeys:
+            outdir = self.configs[kind].outdir
+            buildtarget = self.configs[kind].buildtarget
+
+            (d,c) = os.path.split(str(self.conspath))
+            cmd = '%s %s -C %s -f %s %s\n' % (sys.executable,\
+                                              os.path.normpath(sys.argv[0]),\
+                                              d,c,buildtarget)
+
+            cleancmd = '%s %s -C %s -f %s -c %s' % (sys.executable,\
+                                                    os.path.normpath(sys.argv[0]),\
+                                                    d,c,buildtarget)
+
+            self.file.write('          <Configuration\n'
+                            '                  Name="%s|Win32"\n'
+                            '                  OutputDirectory="%s"\n'
+                            '                  IntermediateDirectory="%s"\n'
+                            '                  ConfigurationType="0"\n'
+                            '                  UseOfMFC="0"\n'
+                            '                  ATLMinimizesCRunTimeLibraryUsage="FALSE">\n'
+                            '                  <Tool\n'
+                            '                          Name="VCNMakeTool"\n'
+                            '                          BuildCommandLine="%s"\n'
+                            '                          CleanCommandLine="%s"\n'
+                            '                          RebuildCommandLine="%s"\n'
+                            '                          Output="%s"/>\n'
+                            '          </Configuration>\n' % (kind.capitalize(),outdir,outdir,\
+                                                               cmd,cleancmd,cmd,buildtarget))
+            
+        self.file.write('      </Configurations>\n')
+
+        self.PrintSourceFiles()
+
+        self.file.write('</VisualStudioProject>\n')
+
+        # now we pickle some data and add it to the file -- MSDEV will ignore it.
+        pdata = pickle.dumps(self.configs,True)
+        pdata = base64.encodestring(pdata)
+        self.file.write('<!-- SCons Data:\n' + pdata + '\n')
+        pdata = pickle.dumps(self.sources,True)
+        pdata = base64.encodestring(pdata)
+        self.file.write(pdata + '-->\n')
+  
+    def PrintSourceFiles(self):
+        categories = {' Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
+                      'Header Files': 'h;hpp;hxx;hm;inl',
+                      'Local Headers': 'h;hpp;hxx;hm;inl',
+                      'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe',
+                      'Other Files': ''}
+
+        self.file.write('      <Files>\n')
+        
+        cats = categories.keys()
+        cats.sort()
+        for kind in cats:
+            if not self.sources[kind]:
+                continue # skip empty groups
+
+            self.file.write('          <Filter\n'
+                            '                  Name="%s"\n'
+                            '                  Filter="%s">\n' % (kind, categories[kind]))
+            
+            for file in self.sources[kind]:
+                file = os.path.normpath(file)
+                self.file.write('                      <File\n'
+                                '                              RelativePath="%s">\n'
+                                '                      </File>\n' % file)
+
+            self.file.write('          </Filter>\n')
+
+        # add the Conscript file outside of the groups
+        self.file.write('              <File\n'
+                        '                      RelativePath="%s">\n'
+                        '              </File>\n'
+                        '      </Files>\n'
+                        '      <Globals>\n'
+                        '      </Globals>\n' % str(self.source))
+
+    def Parse(self):
+        try:
+            dspfile = file(self.dspfile,'r')
+        except IOError:
+            return # doesn't exist yet, so can't add anything to configs.
+
+        line = dspfile.readline()
+        while line:
+            if line.find('<!-- SCons Data:') > -1:
+                break
+            line = dspfile.readline()
+
+        line = dspfile.readline()
+        datas = line
+        while line and line != '\n':
+            line = dspfile.readline()
+            datas = datas + line
+
+        # OK, we've found our little pickled cache of data.
+        try:
+            datas = base64.decodestring(datas)
+            data = pickle.loads(datas)
+        except:
+            return # unable to unpickle any data for some reason
+
+        self.configs.update(data)
+
+        data = None
+        line = dspfile.readline()
+        datas = line
+        while line and line != '\n':
+            line = dspfile.readline()
+            datas = datas + line
+
+        # OK, we've found our little pickled cache of data.
+        try:
+            datas = base64.decodestring(datas)
+            data = pickle.loads(datas)
+        except:
+            return # unable to unpickle any data for some reason
+
+        self.sources.update(data)
+    
+    def Build(self):
+        try:
+            self.file = file(self.dspfile,'w')
+        except IOError, detail:
+            raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail)
+        else:
+            self.PrintHeader()
+            self.PrintProject()
+            self.file.close()
+
+class _DSWGenerator:
+    """ Base class for DSW generators """
+    def __init__(self, dswfile, dspfile, source, env):
+        self.dswfile = os.path.normpath(str(dswfile))
+        self.dspfile = os.path.abspath(str(dspfile))
+        self.env = env
+
+        if self.env.has_key('name'):
+            self.name = self.env['name']
+        else:
+            self.name = os.path.basename(os.path.splitext(self.dspfile)[0])
+
+    def Build(self):
+        pass
+
+class _GenerateV7DSW(_DSWGenerator):
+    """Generates a Solution file for MSVS .NET"""
+    def __init__(self, dswfile, dspfile, source, env):
+        _DSWGenerator.__init__(self, dswfile,dspfile,source,env)
+
+        if env.has_key('slnguid') and env['slnguid']:
+            self.slnguid = env['slnguid']
+        else:
+            self.slnguid = _generateGUID(dswfile, self.name)
+
+        self.config = Config()
+        if env.has_key('variant'):
+            self.config.variant = env['variant'].capitalize()
+        else:
+            raise SCons.Errors.InternalError, \
+                  "You must specify a 'variant' argument (i.e. 'Debug' or " +\
+                  "'Release') to create an MSVS Solution File."
+
+        self.configs = {}
+
+        if os.path.exists(self.dswfile):
+            self.Parse()
+
+        self.configs[self.config.variant] = self.config
+
+    def Parse(self):
+        try:
+            dswfile = file(self.dswfile,'r')
+        except IOError:
+            return # doesn't exist yet, so can't add anything to configs.
+
+        line = dswfile.readline()
+        while line:
+            if line[:9] == "EndGlobal":
+                break
+            line = dswfile.readline()
+
+        line = dswfile.readline()
+        datas = line
+        while line:
+            line = dswfile.readline()
+            datas = datas + line
+
+        # OK, we've found our little pickled cache of data.
+        try:
+            datas = base64.decodestring(datas)
+            data = pickle.loads(datas)
+        except:
+            return # unable to unpickle any data for some reason
+
+        self.configs.update(data)
+
+    def PrintSolution(self):
+        """Writes a solution file"""
+        self.file.write('Microsoft Visual Studio Solution File, Format Version 7.00\n'
+        # the next line has the GUID for an external makefile project.
+                        'Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%s", "%s", "%s"\n'
+                        'EndProject\n'
+                        'Global\n'
+                        '      GlobalSection(SolutionConfiguration) = preSolution\n'\
+                         % (self.name, os.path.basename(self.dspfile), self.slnguid))
+        confkeys = self.configs.keys()
+        confkeys.sort()
+        cnt = 0
+        for name in confkeys:
+            self.file.write('          ConfigName.%d = %s\n' % (cnt, name.capitalize()))
+            cnt = cnt + 1
+        self.file.write('      EndGlobalSection\n'
+                        '      GlobalSection(ProjectDependencies) = postSolution\n'
+                        '      EndGlobalSection\n'
+                        '      GlobalSection(ProjectConfiguration) = postSolution\n')
+        for name in confkeys:
+            name = name.capitalize()
+            self.file.write('          %s.%s.ActiveCfg = %s|Win32\n'
+                            '          %s.%s.Build.0 = %s|Win32\n'  %(self.slnguid,name,name,self.slnguid,name,name))
+        self.file.write('      EndGlobalSection\n'
+                        '      GlobalSection(ExtensibilityGlobals) = postSolution\n'
+                        '      EndGlobalSection\n'
+                        '      GlobalSection(ExtensibilityAddIns) = postSolution\n'
+                        '      EndGlobalSection\n'
+                        'EndGlobal\n')
+        pdata = pickle.dumps(self.configs,True)
+        pdata = base64.encodestring(pdata)
+        self.file.write(pdata + '\n')
+
+    def Build(self):
+        try:
+            self.file = file(self.dswfile,'w')
+        except IOError, detail:
+            raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
+        else:
+            self.PrintSolution()
+            self.file.close()
+
+class _GenerateV6DSW(_DSWGenerator):
+    """Generates a Workspace file for MSVS 6.0"""
+
+    def PrintWorkspace(self):
+        """ writes a DSW file """
+        self.file.write('Microsoft Developer Studio Workspace File, Format Version 6.00\n'
+                        '# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n'
+                        '\n'
+                        '###############################################################################\n'
+                        '\n'
+                        'Project: "%s"="%s" - Package Owner=<4>\n'
+                        '\n'
+                        'Package=<5>\n'
+                        '{{{\n'
+                        '}}}\n'
+                        '\n'
+                        'Package=<4>\n'
+                        '{{{\n'
+                        '}}}\n'
+                        '\n'
+                        '###############################################################################\n'
+                        '\n'
+                        'Global:\n'
+                        '\n'
+                        'Package=<5>\n'
+                        '{{{\n'
+                        '}}}\n'
+                        '\n'
+                        'Package=<3>\n'
+                        '{{{\n'
+                        '}}}\n'
+                        '\n'
+                        '###############################################################################\n'\
+                         %(self.name,self.dspfile))
+
+    def Build(self):
+        try:
+            self.file = file(self.dswfile,'w')
+        except IOError, detail:
+            raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
+        else:
+            self.PrintWorkspace()
+            self.file.close()
+
+
+def GenerateDSP(dspfile, source, env):
+    """Generates a Project file based on the version of MSVS that is being used"""
+
+    if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0:
+        g = _GenerateV7DSP(dspfile, source, env)
+        g.Build()
+    else:
+        g = _GenerateV6DSP(dspfile, source, env)
+        g.Build()
+
+def GenerateDSW(dswfile, dspfile, source, env):
+    """Generates a Solution/Workspace file based on the version of MSVS that is being used"""
+    
+    if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0:
+        g = _GenerateV7DSW(dswfile, dspfile, source, env)
+        g.Build()
+    else:
+        g = _GenerateV6DSW(dswfile, dspfile, source, env)
+        g.Build()
+
+
+##############################################################################
+# Above here are the classes and functions for generation of
+# DSP/DSW/SLN/VCPROJ files.
+##############################################################################
+
+def get_default_visualstudio_version(env):
+    """Returns the version set in the env, or the latest version
+    installed, if it can find it, or '6.0' if all else fails.  Also
+    updated the environment with what it found."""
+
+    version = '6.0'
+    versions = [version]
+    if not env.has_key('MSVS') or type(env['MSVS']) != types.DictType:
+        env['MSVS'] = {}
+
+    if env.has_key('MSVS_VERSION'):
+        version = env['MSVS_VERSION']
+        versions = [version]
+    else:
+        if SCons.Util.can_read_reg:
+            versions = get_visualstudio_versions()
+            version = versions[0] #use highest version by default
+
+    env['MSVS_VERSION'] = version
+    env['MSVS']['VERSIONS'] = versions
+    env['MSVS']['VERSION'] = version
+    
+    return version
+
+def get_visualstudio_versions():
+    """
+    Get list of visualstudio versions from the Windows registry.  Return a
+    list of strings containing version numbers; an exception will be raised
+    if we were unable to access the registry (eg. couldn't import
+    a registry-access module) or the appropriate registry keys weren't
+    found.
+    """
+
+    if not SCons.Util.can_read_reg:
+        raise SCons.Errors.InternalError, "No Windows registry module was found"
+
+    HLM = SCons.Util.HKEY_LOCAL_MACHINE
+    K = r'Software\Microsoft\VisualStudio'
+    L = []
+    try:
+        k = SCons.Util.RegOpenKeyEx(HLM, K)
+        i = 0
+        while 1:
+            try:
+                p = SCons.Util.RegEnumKey(k,i)
+            except SCons.Util.RegError:
+                break
+            i = i + 1
+            if not p[0] in '123456789' or p in L:
+                continue
+            # Only add this version number if there is a valid
+            # registry structure (includes the "Setup" key),
+            # and at least some of the correct directories
+            # exist.  Sometimes VS uninstall leaves around
+            # some registry/filesystem turds that we don't
+            # want to trip over.  Also, some valid registry
+            # entries are MSDN entries, not MSVS ('7.1',
+            # notably), and we want to skip those too.
+            try:
+                tst = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p + '\\Setup')
+            except SCons.Util.RegError:
+                continue
+
+            id = []
+            idk = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p)
+            # This is not always here -- it only exists if the
+            # user installed into a non-standard location (at
+            # least in VS6 it works that way -- VS7 seems to
+            # always write it)
+            try:
+                id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
+            except SCons.Util.RegError:
+                pass
+
+            # If the InstallDir key doesn't exist,
+            # then we check the default locations.
+            if not id or not id[0]:
+                files_dir = SCons.Platform.win32.get_program_files_dir()
+                if float(p) < 7.0:
+                    vs = r'Microsoft Visual Studio\Common\MSDev98'
+                else:
+                    vs = r'Microsoft Visual Studio .NET\Common7\IDE'
+                id = [ os.path.join(files_dir, vs) ]
+            if os.path.exists(id[0]):
+                L.append(p)
+    except SCons.Util.RegError:
+        pass
+
+    if not L:
+        raise SCons.Errors.InternalError, "Microsoft Visual Studio was not found."
+
+    L.sort()
+    L.reverse()
+
+    return L
+
+def get_msvs_install_dirs(version = None):
+    """
+    Get installed locations for various msvc-related products, like the .NET SDK
+    and the Platform SDK.
+    """
+
+    if not SCons.Util.can_read_reg:
+        raise SCons.Errors.InternalError, "No Windows registry module was found"
+
+    if not version:
+        version = get_visualstudio_versions()[0] #use highest version by default
+
+    K = 'Software\\Microsoft\\VisualStudio\\' + version
+
+    # vc++ install dir
+    rv = {}
+    try:
+        if (float(version) < 7.0):
+            (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+                                                             K + r'\Setup\Microsoft Visual C++\ProductDir')
+        else:
+            (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+                                                             K + r'\Setup\VC\ProductDir')
+    except SCons.Util.RegError:
+        pass
+
+    # visual studio install dir
+    if (float(version) < 7.0):
+        try:
+            (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+                                                             K + r'\Setup\Microsoft Visual Studio\ProductDir')
+        except SCons.Util.RegError:
+            pass
+
+        if not rv.has_key('VSINSTALLDIR') or not rv['VSINSTALLDIR']:
+            if rv.has_key('VCINSTALLDIR') and rv['VCINSTALLDIR']:
+                rv['VSINSTALLDIR'] = os.path.dirname(rv['VCINSTALLDIR'])
+            else:
+                rv['VSINSTALLDIR'] = os.path.join(SCons.Platform.win32.get_program_files_dir(),'Microsoft Visual Studio')
+    else:
+        try:
+            (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+                                                             K + r'\Setup\VS\ProductDir')
+        except SCons.Util.RegError:
+            pass
+
+    # .NET framework install dir
+    try:
+        (rv['FRAMEWORKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+            r'Software\Microsoft\.NETFramework\InstallRoot')
+    except SCons.Util.RegError:
+        pass
+
+    if rv.has_key('FRAMEWORKDIR'):
+        # try and enumerate the installed versions of the .NET framework.
+        contents = os.listdir(rv['FRAMEWORKDIR'])
+        l = re.compile('v[0-9]+.*')
+        versions = []
+        for entry in contents:
+            if l.match(entry):
+                versions.append(entry)
+
+        def versrt(a,b):
+            # since version numbers aren't really floats...
+            aa = a[1:]
+            bb = b[1:]
+            aal = aa.split('.')
+            bbl = bb.split('.')
+            c = int(bbl[0]) - int(aal[0])
+            if c == 0:
+                c = int(bbl[1]) - int(aal[1])
+                if c == 0:
+                    c = int(bbl[2]) - int(aal[2])
+            return c
+        
+        versions.sort(versrt)
+
+        rv['FRAMEWORKVERSIONS'] = versions
+        # assume that the highest version is the latest version installed
+        rv['FRAMEWORKVERSION'] = versions[0]
+
+    # .NET framework SDK install dir
+    try:
+        (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+            r'Software\Microsoft\.NETFramework\sdkInstallRoot')
+    except SCons.Util.RegError:
+        pass
+
+    # MS Platform SDK dir
+    try:
+        (rv['PLATFORMSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
+            r'Software\Microsoft\MicrosoftSDK\Directories\Install Dir')
+    except SCons.Util.RegError:
+        pass
+
+    if rv.has_key('PLATFORMSDKDIR'):
+        # if we have a platform SDK, try and get some info on it.
+        vers = {}
+        try:
+            loc = r'Software\Microsoft\MicrosoftSDK\InstalledSDKs'
+            k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,loc)
+            i = 0
+            while 1:
+                try:
+                    key = SCons.Util.RegEnumKey(k,i)
+                    sdk = SCons.Util.RegOpenKeyEx(k,key)
+                    j = 0
+                    name = ''
+                    date = ''
+                    version = ''
+                    while 1:
+                        try:
+                            (vk,vv,t) = SCons.Util.RegEnumValue(sdk,j)
+                            if vk.lower() == 'keyword':
+                                name = vv
+                            if vk.lower() == 'propagation_date':
+                                date = vv
+                            if vk.lower() == 'version':
+                                version = vv
+                            j = j + 1
+                        except SCons.Util.RegError:
+                            break
+                    if name:
+                        vers[name] = (date, version)
+                    i = i + 1
+                except SCons.Util.RegError:
+                    break
+            rv['PLATFORMSDK_MODULES'] = vers
+        except SCons.Util.RegError:
+            pass
+
+    return rv;
+
+def GenerateProject(target, source, env):
+    # generate the dsp file, according to the version of MSVS.
+    builddspfile = target[0]
+    builddswfile = target[1]
+    dswfile = builddswfile.srcnode()
+    dspfile = builddspfile.srcnode()
+
+#     print "SConscript    :",str(source[0])
+#     print "DSW file      :",dswfile
+#     print "DSP file      :",dspfile
+#     print "Build DSW file:",builddswfile
+#     print "Build DSP file:",builddspfile
+
+    # this detects whether or not we're using a BuildDir
+    if os.path.abspath(os.path.normcase(str(dspfile))) != \
+           os.path.abspath(os.path.normcase(str(builddspfile))):
+        try:
+            bdsp = file(str(builddspfile), "w+")
+        except IOError, detail:
+            print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
+            raise
+
+        bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
+
+        try:
+            bdsw = file(str(builddswfile), "w+")
+        except IOError, detail:
+            print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
+            raise
+
+        bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
+
+    GenerateDSP(dspfile, source, env)
+    GenerateDSW(dswfile, dspfile, source, env) 
+
+def projectEmitter(target, source, env):
+    """Sets up the DSP and DSW dependencies for an SConscript file."""
+
+    if source[0] == target[0]:
+        source = []
+
+    # make sure the suffix is correct for the version of MSVS we're running.
+    (base, suff) = os.path.splitext(str(target[0]))
+    suff = env['MSVSPROJECTSUFFIX']
+    target[0] = base + suff
+
+    dspfile = SCons.Node.FS.default_fs.File(target[0]).srcnode()
+    dswfile = SCons.Node.FS.default_fs.File(os.path.splitext(str(dspfile))[0] + env['MSVSSOLUTIONSUFFIX'])
+
+    if not source:
+        source = [SCons.Script.SConscript.stack[-1].sconscript.srcnode()]
+
+    source[0].attributes.sconstruct = SCons.Script.SConscript.stack[0].sconscript
+
+    bdswpath = os.path.splitext(str(target[0]))[0] + env['MSVSSOLUTIONSUFFIX']
+    bdswfile = SCons.Node.FS.default_fs.File(bdswpath)
+
+    # only make these side effects if they're
+    # not the same file.
+    if os.path.abspath(os.path.normcase(str(dspfile))) != \
+           os.path.abspath(os.path.normcase(str(target[0]))):
+        env.SideEffect(dspfile, target[0])
+        env.Precious(dspfile)
+        # dswfile isn't precious -- it can be blown away and rewritten each time.
+        env.SideEffect(dswfile, target[0])
+    
+    return ([target[0],bdswfile], source)
+
+projectGeneratorAction = SCons.Action.Action(GenerateProject, None)
+
+projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
+                                       suffix = '$MSVSPROJECTSUFFIX',
+                                       emitter = projectEmitter)
+
+def generate(env):
+    """Add Builders and construction variables for Microsoft Visual
+    Studio project files to an Environment."""
+    try:
+        bld = env['BUILDERS']['MSVSProject']
+    except KeyError:
+        env['BUILDERS']['MSVSProject'] = projectBuilder
+
+    env['MSVSPROJECTCOM']     = projectGeneratorAction
+
+    version = get_default_visualstudio_version(env)
+
+    # keep a record of some of the MSVS info so the user can use it.
+    try:
+        dirs = get_msvs_install_dirs(version)
+        env['MSVS'].update(dirs)
+    except (SCons.Util.RegError, SCons.Errors.InternalError):
+        # we don't care if we can't do this -- if we can't, it's
+        # because we don't have access to the registry, or because the
+        # tools aren't installed.  In either case, the user will have to
+        # find them on their own.
+        pass
+
+    if (float(env['MSVS_VERSION']) < 7.0):
+        env['MSVSPROJECTSUFFIX']  = '.dsp'
+        env['MSVSSOLUTIONSUFFIX'] = '.dsw'
+    else:
+        env['MSVSPROJECTSUFFIX']  = '.vcproj'
+        env['MSVSSOLUTIONSUFFIX'] = '.sln'
+
+def exists(env):
+    if not SCons.Util.can_read_reg or not get_visualstudio_versions():
+        if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0:
+            return env.Detect('devenv')
+        else:
+            return env.Detect('msdev')
+    else:
+        # there's at least one version of MSVS installed.
+        return True
+
diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py
new file mode 100644 (file)
index 0000000..731cc2d
--- /dev/null
@@ -0,0 +1,476 @@
+#
+# __COPYRIGHT__
+#
+# Permission is hereby granted, free of charge, to any person obtaining
+# a copy of this software and associated documentation files (the
+# "Software"), to deal in the Software without restriction, including
+# without limitation the rights to use, copy, modify, merge, publish,
+# distribute, sublicense, and/or sell copies of the Software, and to
+# permit persons to whom the Software is furnished to do so, subject to
+# the following conditions:
+#
+# The above copyright notice and this permission notice shall be included
+# in all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
+# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
+# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+#
+
+__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
+
+import os
+import string
+import sys
+import TestCmd
+import unittest
+
+from SCons.Tool.msvs import *
+import SCons.Util
+import SCons.Warnings
+
+regdata_6a = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks]
+"sp3"=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup]
+"VsCommonDir"="C:\Program Files\Microsoft Visual Studio\Common"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Developer Network Library - Visual Studio 6.0a]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio\MSDN98\98VSa\1033"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio\VC98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion]
+"ProgramFilesDir"="C:\Program Files"
+"CommonFilesDir"="C:\Program Files\Common Files"
+"MediaPath"="C:\WINDOWS\Media"
+''','\n')
+
+regdata_6b = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0]
+"InstallDir"="C:\VS6\Common\IDE\IDE98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks]
+"sp5"=""
+"latest"=dword:00000005
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup]
+"VsCommonDir"="C:\VS6\Common"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic]
+"ProductDir"="C:\VS6\VB98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++]
+"ProductDir"="C:\VS6\VC98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio]
+"ProductDir"="C:\VS6"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft VSEE Client]
+"ProductDir"="C:\VS6\Common\Tools"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Visual Studio 98]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion]
+"ProgramFilesDir"="C:\Program Files"
+"CommonFilesDir"="C:\Program Files\Common Files"
+"MediaPath"="C:\WINDOWS\Media"
+''','\n')
+
+regdata_7 = string.split(r'''
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0]
+"InstallDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"Source Directories"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\crt\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\atl\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\CrystalReports]
+@="#15007"
+"Package"="{F05E92C6-8346-11D3-B4AD-00A0C9B04E7B}"
+"ProductDetails"="#15009"
+"LogoID"="0"
+"PID"="#15008"
+"UseInterface"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual Basic.NET]
+@=""
+"DefaultProductAttribute"="VB"
+"Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}"
+"UseInterface"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual C#]
+@=""
+"Package"="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}"
+"UseInterface"=dword:00000001
+"DefaultProductAttribute"="C#"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\VisualC++]
+"UseInterface"=dword:00000001
+"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}"
+"DefaultProductAttribute"="VC"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup]
+"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"dw_dir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\MSDN]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Msdn\1033\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Servicing\SKU]
+"Visual Studio .NET Professional - English"="{D0610409-7D65-11D5-A54F-0090278A1BB8}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VB]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vb7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC#]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\VC#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Visual Studio .NET Professional - English]
+"InstallSuccess"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS]
+"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe"
+"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe"
+"MSMDir"="C:\Program Files\Common Files\Merge Modules\"
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\"
+"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\"
+"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\"
+"VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET\Setup\VSUpdate\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\BuildNumber]
+"1033"="7.0.9466"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\Pro]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32]
+@="{A54AAE91-30C2-11D3-87BF-A04A4CC10000}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]
+"Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\perl\bin;C:\cygwin\bin;c:\cygwin\usr\bin;C:\bin;C:\program files\perforce;C:\cygwin\usr\local\bin\i686-pc-cygwin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem"
+"Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib"
+"Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include"
+"Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src"
+"Reference Dirs"=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion]
+"ProgramFilesDir"="C:\Program Files"
+"CommonFilesDir"="C:\Program Files\Common Files"
+"MediaPath"="C:\WINDOWS\Media"
+''','\n')
+
+regdata_67 = string.split(r'''
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0]
+"InstallDir"="C:\VS6\Common\IDE\IDE98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks]
+"sp5"=""
+"latest"=dword:00000005
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup]
+"VsCommonDir"="C:\VS6\Common"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic]
+"ProductDir"="C:\VS6\VB98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++]
+"ProductDir"="C:\VS6\VC98"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio]
+"ProductDir"="C:\VS6"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft VSEE Client]
+"ProductDir"="C:\VS6\Common\Tools"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Visual Studio 98]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0]
+"InstallDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"Source Directories"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\crt\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\atl\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\CrystalReports]
+@="#15007"
+"Package"="{F05E92C6-8346-11D3-B4AD-00A0C9B04E7B}"
+"ProductDetails"="#15009"
+"LogoID"="0"
+"PID"="#15008"
+"UseInterface"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual Basic.NET]
+@=""
+"DefaultProductAttribute"="VB"
+"Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}"
+"UseInterface"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual C#]
+@=""
+"Package"="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}"
+"UseInterface"=dword:00000001
+"DefaultProductAttribute"="C#"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\VisualC++]
+"UseInterface"=dword:00000001
+"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}"
+"DefaultProductAttribute"="VC"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup]
+"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"dw_dir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\MSDN]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Msdn\1033\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Servicing\SKU]
+"Visual Studio .NET Professional - English"="{D0610409-7D65-11D5-A54F-0090278A1BB8}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VB]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vb7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC#]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\VC#\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Visual Studio .NET Professional - English]
+"InstallSuccess"=dword:00000001
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS]
+"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\"
+"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe"
+"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe"
+"MSMDir"="C:\Program Files\Common Files\Merge Modules\"
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\"
+"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\"
+"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\"
+"VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET\Setup\VSUpdate\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\BuildNumber]
+"1033"="7.0.9466"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\Pro]
+"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO]
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32]
+@="{A54AAE91-30C2-11D3-87BF-A04A4CC10000}"
+[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]
+"Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\perl\bin;C:\cygwin\bin;c:\cygwin\usr\bin;C:\bin;C:\program files\perforce;C:\cygwin\usr\local\bin\i686-pc-cygwin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem"
+"Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib"
+"Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include"
+"Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src"
+"Reference Dirs"=""
+[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion]
+"ProgramFilesDir"="C:\Program Files"
+"CommonFilesDir"="C:\Program Files\Common Files"
+"MediaPath"="C:\WINDOWS\Media"
+''','\n')
+
+
+class DummyEnv:
+    def __init__(self, dict=None):
+        if dict:
+            self.dict = dict
+        else:
+            self.dict = {}
+
+    def Dictionary(self, key = None):
+        if not key:
+            return self.dict
+        return self.dict[key]
+
+    def __setitem__(self,key,value):
+        self.dict[key] = value
+
+    def __getitem__(self,key):
+        return self.dict[key]
+
+    def has_key(self,name):
+        return self.dict.has_key(name)
+
+class RegKey:
+    """key class for storing an 'open' registry key"""
+    def __init__(self,key):
+        self.key = key
+
+class RegNode:
+    """node in the dummy registry"""
+    def __init__(self,name):
+        self.valdict = {}
+        self.keydict = {}
+        self.keyarray = []
+        self.valarray = []
+        self.name = name
+
+    def value(self,val):
+        if self.valdict.has_key(val):
+            return (self.valdict[val],1)
+        else:
+            raise SCons.Util.RegError
+    
+    def addValue(self,name,val):
+        self.valdict[name] = val
+        self.valarray.append(name)
+
+    def valindex(self,index):
+        rv = None
+        try:
+            rv = (self.valarray[index],self.valdict[self.valarray[index]],1)
+        except KeyError:
+            raise SCons.Util.RegError
+        return rv
+
+    def key(self,key,sep = '\\'):
+        if key.find(sep) != -1:
+            keyname, subkeys = key.split(sep,1)
+        else:
+            keyname = key
+            subkeys = ""
+        try:
+            # recurse, and return the lowest level key node
+            if subkeys:
+                return self.keydict[keyname].key(subkeys)
+            else:
+                return self.keydict[keyname]
+        except KeyError:
+            raise SCons.Util.RegError
+
+    def addKey(self,name,sep = '\\'):
+        if name.find(sep) != -1:
+            keyname, subkeys = name.split(sep,1)
+        else:
+            keyname = name
+            subkeys = ""
+
+        if not self.keydict.has_key(keyname):       
+            self.keydict[keyname] = RegNode(keyname)
+            self.keyarray.append(keyname)
+
+        # recurse, and return the lowest level key node
+        if subkeys:
+            return self.keydict[keyname].addKey(subkeys)
+        else:
+            return self.keydict[keyname]
+
+    def keyindex(self,index):
+        return self.keydict[self.keyarray[index]]
+
+    def __str__(self):
+        return self._doStr()
+
+    def _doStr(self, indent = ''):
+        rv = ""
+        for value in self.valarray:
+            rv = rv + '%s"%s" = "%s"\n' % (indent, value, self.valdict[value])
+        for key in self.keyarray:
+            rv = rv + "%s%s: {\n"%(indent, key)
+            rv = rv + self.keydict[key]._doStr(indent + '  ')
+            rv = rv + indent + '}\n'
+        return rv
+
+class DummyRegistry:
+    """registry class for storing fake registry attributes"""
+    def __init__(self,data):
+        """parse input data into the fake registry"""
+        self.root = RegNode('REGISTRY')
+        self.root.addKey('HKEY_LOCAL_MACHINE')
+        self.root.addKey('HKEY_CURRENT_USER')
+        self.root.addKey('HKEY_USERS')
+        self.root.addKey('HKEY_CLASSES_ROOT')
+        
+        self.parse(data)
+        
+    def parse(self, data):
+        parent = self.root
+        keymatch = re.compile('^\[(.*)\]$')
+        valmatch = re.compile('^(?:"(.*)"|[@])="(.*)"$')
+        for line in data:
+            m1 = keymatch.match(line)
+            if m1:
+                # add a key, set it to current parent
+                parent = self.root.addKey(m1.group(1))
+            else:
+                m2 = valmatch.match(line)
+                if m2:
+                    parent.addValue(m2.group(1),m2.group(2))
+
+    def OpenKeyEx(self,root,key):
+        if root == SCons.Util.HKEY_CLASSES_ROOT:
+            mykey = 'HKEY_CLASSES_ROOT\\' + key
+        if root == SCons.Util.HKEY_USERS:
+            mykey = 'HKEY_USERS\\' + key
+        if root == SCons.Util.HKEY_CURRENT_USER:
+            mykey = 'HKEY_CURRENT_USER\\' + key
+        if root == SCons.Util.HKEY_LOCAL_MACHINE:
+            mykey = 'HKEY_LOCAL_MACHINE\\' + key
+        #print "Open Key",mykey
+        return self.root.key(mykey)
+        
+def DummyOpenKeyEx(root, key):
+    return registry.OpenKeyEx(root,key)
+
+def DummyEnumKey(key, index):
+    rv = None
+    try:
+        rv = key.keyarray[index]
+    except IndexError:
+        raise SCons.Util.RegError
+#    print "Enum Key",key.name,"[",index,"] =>",rv
+    return rv
+
+def DummyEnumValue(key, index):
+    rv = key.valindex(index)
+#    print "Enum Value",key.name,"[",index,"] =>",rv
+    return rv
+
+def DummyQueryValue(key, value):
+    rv = key.value(value)
+#    print "Query Value",key.name+"\\"+value,"=>",rv
+    return rv
+
+def DummyExists(path):
+    return True
+
+class msvsTestCase(unittest.TestCase):
+    def test_get_default_visual_studio_version(self):
+        """Test retrieval of the default visual studio version"""
+        env = DummyEnv()
+        v1 = get_default_visualstudio_version(env)
+        assert env['MSVS_VERSION'] == default_version
+        assert env['MSVS']['VERSION'] == default_version
+        assert v1 == default_version
+
+        env = DummyEnv({'MSVS_VERSION':'7.0'})
+        v2 = get_default_visualstudio_version(env)
+        assert env['MSVS_VERSION'] == '7.0'
+        assert env['MSVS']['VERSION'] == '7.0'
+        assert v2 == '7.0'
+
+    def test_get_visual_studio_versions(self):
+        """Test retrieval of the list of visual studio versions"""
+        v1 = get_visualstudio_versions()
+        assert v1[0] == highest_version
+        assert len(v1) == number_of_versions
+
+    def test_get_msvs_install_dirs(self):
+        """Test retrieval of the list of visual studio installed locations"""
+        v1 = get_msvs_install_dirs()
+        v2 = get_msvs_install_dirs('7.0')
+        assert v1 == install_location1
+        assert v2 == install_location2
+        
+if __name__ == "__main__":
+
+    # only makes sense to test this on win32
+    if sys.platform != 'win32':
+        sys.exit(0)
+    
+    SCons.Util.RegOpenKeyEx = DummyOpenKeyEx
+    SCons.Util.RegEnumKey = DummyEnumKey
+    SCons.Util.RegEnumValue = DummyEnumValue
+    SCons.Util.RegQueryValueEx = DummyQueryValue
+    os.path.exists = DummyExists # make sure all files exist :-)
+
+    # try it for each possible setup.
+    suite = unittest.makeSuite(msvsTestCase, 'test_')
+    registry = DummyRegistry(regdata_6a)
+    default_version = '6.0'
+    highest_version = '6.0'
+    number_of_versions = 1
+    install_location1 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio\\VC98'}
+    install_location2 = {}
+    # print str(registry.root)
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+       sys.exit(1)
+
+    registry = DummyRegistry(regdata_6b)
+    default_version = '6.0'
+    highest_version = '6.0'
+    number_of_versions = 1
+    install_location1 = {'VSINSTALLDIR': 'C:\\VS6', 'VCINSTALLDIR': 'C:\\VS6\\VC98'}
+    install_location2 = {}        
+    # print str(registry.root)
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+       sys.exit(1)
+
+    registry = DummyRegistry(regdata_67)
+    default_version = '7.0'
+    highest_version = '7.0'
+    number_of_versions = 2
+    install_location1 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'}
+    install_location2 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'}
+    # print str(registry.root)
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+       sys.exit(1)
+
+    registry = DummyRegistry(regdata_7)
+    default_version = '7.0'
+    highest_version = '7.0'
+    number_of_versions = 1
+    install_location1 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'}
+    install_location2 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'}
+    # print str(registry.root)
+    if not unittest.TextTestRunner().run(suite).wasSuccessful():
+       sys.exit(1)
index 2ebe0d98652a39f8f68db347073f83de794d37ae..06a3dacf28ca270a5afad113bb5ee89e74a56003 100644 (file)
@@ -737,6 +737,16 @@ if can_read_reg:
     HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER
     HKEY_USERS = hkey_mod.HKEY_USERS
 
+    def RegGetValue(root, key):
+        """Returns a value in the registry without
+        having to open the key first."""
+        # I would use os.path.split here, but it's not a filesystem
+        # path...
+        p = key.rfind('\\') + 1
+        keyp = key[:p]
+        val = key[p:]
+        k = SCons.Util.RegOpenKeyEx(root, keyp)
+        return SCons.Util.RegQueryValueEx(k,val)
 
 if sys.platform == 'win32':
 
@@ -803,6 +813,84 @@ else:
                     return os.path.normpath(f)
         return None
 
+def PrependPath(oldpath, newpath, sep = os.pathsep):
+    """Prepend newpath elements to the given oldpath.  Will only add
+    any particular path once (leaving the first one it encounters and
+    ignoring the rest, to preserve path order), and will normpath and
+    normcase all paths to help assure this.  This can also handle the
+    case where the given oldpath variable is a list instead of a
+    string, in which case a list will be returned instead of a string.
+    """
+
+    orig = oldpath
+    is_list = 1
+    paths = orig
+    if not SCons.Util.is_List(orig):
+        paths = string.split(paths, sep)
+        is_list = 0
+
+    if SCons.Util.is_List(newpath):
+        newpaths = newpath
+    else:
+        newpaths = string.split(newpath, sep)
+
+    newpaths = newpaths + paths # prepend new paths
+
+    normpaths = []
+    paths = []
+    # now we add them only of they are unique
+    for path in newpaths:
+        normpath = os.path.normpath(os.path.normcase(path))
+        if path and not normpath in normpaths:
+            paths.append(path)
+            normpaths.append(normpath)
+
+    if is_list:
+        return paths
+    else:
+        return string.join(paths, sep)
+
+def AppendPath(oldpath, newpath, sep = os.pathsep):
+    """Append newpath elements to the given oldpath.  Will only add
+    any particular path once (leaving the first one it encounters and
+    ignoring the rest, to preserve path order), and will normpath and
+    normcase all paths to help assure this.  This can also handle the
+    case where the given oldpath variable is a list instead of a
+    string, in which case a list will be returned instead of a string.
+    """
+
+    orig = oldpath
+    is_list = 1
+    paths = orig
+    if not SCons.Util.is_List(orig):
+        paths = string.split(paths, sep)
+        is_list = 0
+
+    if SCons.Util.is_List(newpath):
+        newpaths = newpath
+    else:
+        newpaths = string.split(newpath, sep)
+
+    newpaths = paths + newpaths # append new paths
+    newpaths.reverse()
+    
+    normpaths = []
+    paths = []
+    # now we add them only of they are unique
+    for path in newpaths:
+        normpath = os.path.normpath(os.path.normcase(path))
+        if path and not normpath in normpaths:
+            paths.append(path)
+            normpaths.append(normpath)
+
+    paths.reverse()
+
+    if is_list:
+        return paths
+    else:
+        return string.join(paths, sep)
+
+
 def ParseConfig(env, command, function=None):
     """Use the specified function to parse the output of the command in order
     to modify the specified environment. The 'command' can be a string or a
index 7f5f16613f3dc6c1f00bd729f0e41da020fdba60..b7b41be06bff93850c56c0550602cada5f204da0 100644 (file)
@@ -815,6 +815,30 @@ class UtilTestCase(unittest.TestCase):
         SOURCES.sort()
         assert SOURCES == ['rstr-s4', 's3'], d['SOURCES']
 
+    def test_PrependPath(self):
+        """Test prepending to a path"""
+        p1 = r'C:\dir\num\one;C:\dir\num\two'
+        p2 = r'C:\mydir\num\one;C:\mydir\num\two'
+        # have to include the pathsep here so that the test will work on UNIX too.
+        p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';') 
+        p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';')
+        p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';')
+        p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';')
+        assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one')
+        assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two')
+
+    def test_AppendPath(self):
+        """Test appending to a path."""
+        p1 = r'C:\dir\num\one;C:\dir\num\two'
+        p2 = r'C:\mydir\num\one;C:\mydir\num\two'
+        # have to include the pathsep here so that the test will work on UNIX too.
+        p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';') 
+        p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';')
+        p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';')
+        p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';')
+        assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three')
+        assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one')
+
     def test_NodeList(self):
         """Test NodeList class"""
         class TestClass:
index d66658bd9951717ed9ba4ec36bc2ad42d35b00a3..8dc3682a38628cd52088c71610cff388456799d2 100644 (file)
@@ -45,38 +45,61 @@ x = SCons.Platform.%s.generate
 
 tools = [
     # Can't import '386asm' directly due to initial '3' syntax error...
+    'aixcc',
+    'aixf77',
+    'aixlink',
     'ar',
     'as',
+    'BitKeeper',
     'cc',
+    'CVS',
     'default',
     'dvipdf',
     'dvips',
+    'f77',
+    # Can't import 'g++' directly due to '+' syntax error...
     'g77',
     'gas',
     'gcc',
     'gnulink',
-    # Can't import 'g++' directly due to '+' syntax error...
+    'gs',
+    'hpcc',
+    'hplink',
     'icc',
     'ifl',
     'ilink',
+    'jar',
+    'javac',
+    'javah',
     'latex',
     'lex',
     'link',
     # Can't import 'linkloc' everywhere due to Windows registry dependency...
     'masm',
+    'midl',
     'mingw',
     'mslib',
     'mslink',
     'msvc',
+    'msvs',
     'nasm',
     'pdflatex',
     'pdftex',
+    'Perforce',
+    'RCS',
+    'rmic',
+    'SCCS',
     'sgiar',
     'sgicc',
     'sgilink',
+    'sunar',
+    'suncc',
+    'sunlink',
+    'Subversion',
     'tar',
     'tex',
     'yacc',
+    'zip',
 ]
 
 for tool in tools:
index 59fba57c9b577fc3cce17793df05585c6e85cd7d..2e48db2f6bf0c45ca843ebe7a57cd7ed83987e77 100644 (file)
@@ -63,7 +63,7 @@ local = env.Copy(WIN32_INSERT_DEF = 1)
 barsrc = [
     'BarObject.cpp',
     'bar.cpp',
-    local.RES('bar.rc', RCFLAGS= '/I\"${SOURCE.srcdir}\"'),
+    local.RES('bar.rc', RCFLAGS= '/I${SOURCE.srcdir}'),
     ]
 
 local.TypeLibrary('bar.idl')
diff --git a/test/msvs.py b/test/msvs.py
new file mode 100644 (file)
index 0000000..03f08cb
--- /dev/null
@@ -0,0 +1,415 @@
+#!/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__"
+
+import TestSCons
+import sys
+import re
+import os.path
+import os
+import TestCmd
+import time
+
+expected_dspfile = '''\
+# Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) External Target" 0x0106
+
+CFG=Test - Win32 Release
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Test.mak".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Release"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Test - Win32 Release" (based on "Win32 (x86) External Target")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+
+!IF  "$(CFG)" == "Test - Win32 Release"
+
+# PROP BASE Use_MFC 0
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "<WORKPATH>"
+# PROP BASE Intermediate_Dir "<WORKPATH>"
+# PROP BASE Cmd_Line "<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe"
+# PROP BASE Rebuild_Opt "-c && <PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe"
+# PROP BASE Target_File "<WORKPATH>\Test.exe"
+# PROP BASE Bsc_Name ""
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 0
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "<WORKPATH>"
+# PROP Intermediate_Dir "<WORKPATH>"
+# PROP Cmd_Line "<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe"
+# PROP Rebuild_Opt "-c && <PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe"
+# PROP Target_File "<WORKPATH>\Test.exe"
+# PROP Bsc_Name ""
+# PROP Target_Dir ""
+
+!ENDIF
+
+# Begin Target
+
+# Name "Test - Win32 Release"
+
+!IF  "$(CFG)" == "Test - Win32 Release"
+
+!ENDIF 
+
+# Begin Group " Source Files"
+
+# PROP Default_Filter "cpp;c;cxx;l;y;def;odl;idl;hpj;bat"
+# Begin Source File
+
+SOURCE="test.cpp"
+# End Source File
+# End Group
+# Begin Group "Header Files"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="sdk.h"
+# End Source File
+# End Group
+# Begin Group "Local Headers"
+
+# PROP Default_Filter "h;hpp;hxx;hm;inl"
+# Begin Source File
+
+SOURCE="test.h"
+# End Source File
+# End Group
+# Begin Group "Other Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE="readme.txt"
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter "r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"
+# Begin Source File
+
+SOURCE="test.rc"
+# End Source File
+# End Group
+# Begin Source File
+
+SOURCE="<WORKPATH>\SConstruct"
+# End Source File
+# End Target
+# End Project
+'''
+
+expected_dswfile = '''\
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Test"="<WORKPATH>\Test.dsp" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+'''
+
+expected_slnfile = '''\
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "{SLNGUID}"
+EndProject
+Global
+       GlobalSection(SolutionConfiguration) = preSolution
+               ConfigName.0 = Release
+       EndGlobalSection
+       GlobalSection(ProjectDependencies) = postSolution
+       EndGlobalSection
+       GlobalSection(ProjectConfiguration) = postSolution
+               {SLNGUID}.Release.ActiveCfg = Release|Win32
+               {SLNGUID}.Release.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(ExtensibilityGlobals) = postSolution
+       EndGlobalSection
+       GlobalSection(ExtensibilityAddIns) = postSolution
+       EndGlobalSection
+EndGlobal
+'''
+
+expected_vcprojfile = '''\
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+       ProjectType="Visual C++"
+       Version="7.00"
+       Name="Test"
+       SccProjectName=""
+       SccLocalPath=""
+       Keyword="MakeFileProj">
+       <Platforms>
+               <Platform
+                       Name="Win32"/>
+       </Platforms>
+       <Configurations>
+               <Configuration
+                       Name="Release|Win32"
+                       OutputDirectory="<WORKPATH>"
+                       IntermediateDirectory="<WORKPATH>"
+                       ConfigurationType="0"
+                       UseOfMFC="0"
+                       ATLMinimizesCRunTimeLibraryUsage="FALSE">
+                       <Tool
+                               Name="VCNMakeTool"
+                               BuildCommandLine="<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe
+"
+                               CleanCommandLine="<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct -c <WORKPATH>\Test.exe"
+                               RebuildCommandLine="<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe
+"
+                               Output="<WORKPATH>\Test.exe"/>
+               </Configuration>
+       </Configurations>
+       <Files>
+               <Filter
+                       Name=" Source Files"
+                       Filter="cpp;c;cxx;l;y;def;odl;idl;hpj;bat">
+                       <File
+                               RelativePath="test.cpp">
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Header Files"
+                       Filter="h;hpp;hxx;hm;inl">
+                       <File
+                               RelativePath="sdk.h">
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Local Headers"
+                       Filter="h;hpp;hxx;hm;inl">
+                       <File
+                               RelativePath="test.h">
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Other Files"
+                       Filter="">
+                       <File
+                               RelativePath="readme.txt">
+                       </File>
+               </Filter>
+               <Filter
+                       Name="Resource Files"
+                       Filter="r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe">
+                       <File
+                               RelativePath="test.rc">
+                       </File>
+               </Filter>
+               <File
+                       RelativePath="<WORKPATH>\SConstruct">
+               </File>
+       </Files>
+       <Globals>
+       </Globals>
+</VisualStudioProject>
+'''
+
+test = TestSCons.TestSCons(match = TestCmd.match_re)
+
+if sys.platform != 'win32':
+    test.pass_test()
+
+####
+# Determine which environments are installed on the test machine.
+test.write('SConstruct','''
+env = Environment()
+
+f = open('versions','w')
+f.write('versions = ' + str(env['MSVS']['VERSIONS']))
+f.close()
+''')
+
+test.run()
+versions = []
+execfile(test.workpath('versions'))
+
+#####
+# Test v6.0 output
+
+if '6.0' in versions:
+    test.write('SConstruct','''
+env=Environment(MSVS_VERSION = '6.0')
+
+testsrc = ['test.cpp']
+testincs = ['sdk.h']
+testlocalincs = ['test.h']
+testresources = ['test.rc']
+testmisc = ['readme.txt']
+
+env.MSVSProject(target = 'Test.dsp',
+                srcs = testsrc,
+                incs = testincs,
+                localincs = testlocalincs,
+                resources = testresources,
+                misc = testmisc,
+                buildtarget = 'Test.exe',
+                variant = 'Release')
+    ''')
+
+    test.run(arguments="Test.dsp")
+
+    test.fail_test(not os.path.exists(test.workpath('Test.dsp')))
+    test.fail_test(not os.path.exists(test.workpath('Test.dsw')))
+
+    # check to see that we got what we expected:
+    expected_dspfile = expected_dspfile.replace(r'<WORKPATH>',test.workpath())
+    expected_dspfile = expected_dspfile.replace(r'<PYTHON>',sys.executable)
+    expected_dspfile = expected_dspfile.replace(r'<SCONS>',os.path.join(os.environ['SCONS_SCRIPT_DIR'],'scons.py'))
+    expected_dswfile = expected_dswfile.replace(r'<WORKPATH>',test.workpath())
+
+    f = open(test.workpath('Test.dsp'))
+    dsp = f.read()
+    f.close()
+
+    # don't compare the pickled data
+    assert dsp[:len(expected_dspfile)] == expected_dspfile
+
+    f = open(test.workpath('Test.dsw'))
+    dsw = f.read()
+    f.close()
+    assert dsw == expected_dswfile
+
+    test.run(arguments='-c .')
+
+    test.fail_test(os.path.exists(test.workpath('Test.dsp')))
+    test.fail_test(os.path.exists(test.workpath('Test.dsw')))
+
+    test.run(arguments='Test.dsp')
+
+    test.fail_test(not os.path.exists(test.workpath('Test.dsp')))
+    test.fail_test(not os.path.exists(test.workpath('Test.dsw')))
+
+    test.run(arguments='-c Test.dsw')
+
+    test.fail_test(os.path.exists(test.workpath('Test.dsp')))
+    test.fail_test(os.path.exists(test.workpath('Test.dsw')))
+
+#####
+# Test .NET output
+
+if '7.0' in versions:
+    test.write('SConstruct','''
+env=Environment(MSVS_VERSION = '7.0')
+
+testsrc = ['test.cpp']
+testincs = ['sdk.h']
+testlocalincs = ['test.h']
+testresources = ['test.rc']
+testmisc = ['readme.txt']
+
+env.MSVSProject(target = 'Test.vcproj',
+                slnguid = '{SLNGUID}',
+                srcs = testsrc,
+                incs = testincs,
+                localincs = testlocalincs,
+                resources = testresources,
+                misc = testmisc,
+                buildtarget = 'Test.exe',
+                variant = 'Release')
+    ''')
+
+    test.run(arguments="Test.vcproj")
+
+    test.fail_test(not os.path.exists(test.workpath('Test.vcproj')))
+    test.fail_test(not os.path.exists(test.workpath('Test.sln')))
+
+    f = open(test.workpath('Test.vcproj'))
+    vcproj = f.read()
+    f.close()
+    expected_vcprojfile = expected_vcprojfile.replace(r'<WORKPATH>',test.workpath())
+    expected_vcprojfile = expected_vcprojfile.replace(r'<PYTHON>',sys.executable)
+    expected_vcprojfile = expected_vcprojfile.replace(r'<SCONS>',os.path.join(os.environ['SCONS_SCRIPT_DIR'],'scons.py'))
+
+    # don't compare the pickled data
+    assert vcproj[:len(expected_vcprojfile)] == expected_vcprojfile
+
+    f = open(test.workpath('Test.sln'))
+    sln = f.read()
+    f.close()
+
+    assert sln[:len(expected_slnfile)] == expected_slnfile
+
+    test.run(arguments='-c .')
+
+    test.fail_test(os.path.exists(test.workpath('Test.vcproj')))
+    test.fail_test(os.path.exists(test.workpath('Test.sln')))
+
+    test.run(arguments='Test.vcproj')
+
+    test.fail_test(not os.path.exists(test.workpath('Test.vcproj')))
+    test.fail_test(not os.path.exists(test.workpath('Test.sln')))
+
+    test.run(arguments='-c Test.sln')
+
+    test.fail_test(os.path.exists(test.workpath('Test.vcproj')))
+    test.fail_test(os.path.exists(test.workpath('Test.sln')))
+
+test.pass_test()
+
+
+
+
+