http://scons.tigris.org/issues/show_bug.cgi?id=2345
[scons.git] / src / engine / SCons / Tool / msvs.py
index e4272c3cc073733c2371576da5cc54108a581166..304c35ea863bc78f9dd3eb40c106076837156ec1 100644 (file)
@@ -30,17 +30,20 @@ selection method.
 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
+from __future__ import generators  ### KEEP FOR COMPATIBILITY FIXERS
 
 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
 
+import SCons.compat
+
 import base64
-import md5
-import os.path
+import hashlib
+import ntpath
+import os
+# compat layer imports "cPickle" for us if it's available.
 import pickle
 import re
-import string
 import sys
-import types
 
 import SCons.Builder
 import SCons.Node.FS
@@ -49,34 +52,48 @@ import SCons.Script.SConscript
 import SCons.Util
 import SCons.Warnings
 
+from MSCommon import msvc_exists, msvc_setup_env_once
+from SCons.Defaults import processDefines
+
 ##############################################################################
 # 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 xmlify(s):
+    s = s.replace("&", "&") # do this first
+    s = s.replace("'", "'")
+    s = s.replace('"', """)
+    return s
+
+external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}'
 
 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()
+    m = hashlib.md5()
+    # Normalize the slnfile path to a Windows path (\ separators) so
+    # the generated file has a consistent GUID even if we generate
+    # it on a non-Windows platform.
+    m.update(ntpath.normpath(str(slnfile)) + str(name))
+    solution = m.hexdigest().upper()
     # convert most of the signature to GUID form (discard the rest)
-    solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:28] + "}"
+    solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}"
     return solution
 
+version_re = re.compile(r'(\d+\.\d+)(.*)')
+
+def msvs_parse_version(s):
+    """
+    Split a Visual Studio version, which may in fact be something like
+    '7.0Exp', into is version number (returned as a float) and trailing
+    "suite" portion.
+    """
+    num, suite = version_re.match(s).groups()
+    return float(num), suite
+
 # This is how we re-invoke SCons from inside MSVS Project files.
 # The problem is that we might have been invoked as either scons.bat
 # or scons.py.  If we were invoked directly as scons.py, then we could
@@ -85,8 +102,18 @@ def _generateGUID(slnfile, name):
 # things and ends up with "-c" as sys.argv[0].  Consequently, we have
 # the MSVS Project file invoke SCons the same way that scons.bat does,
 # which works regardless of how we were invoked.
-exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-__VERSION__'), join(sys.prefix, 'scons-__VERSION__'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()"
-exec_script_main_xml = string.replace(exec_script_main, "'", "'")
+def getExecScriptMain(env, xml=None):
+    scons_home = env.get('SCONS_HOME')
+    if not scons_home and 'SCONS_LIB_DIR' in os.environ:
+        scons_home = os.environ['SCONS_LIB_DIR']
+    if scons_home:
+        exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home
+    else:
+        version = SCons.__version__
+        exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals()
+    if xml:
+        exec_script_main = xmlify(exec_script_main)
+    return exec_script_main
 
 # The string for the Python executable we tell the Project file to use
 # is either sys.executable or, if an external PYTHON_ROOT environment
@@ -97,88 +124,164 @@ try:
 except KeyError:
     python_executable = sys.executable
 else:
-    python_executable = os.path.join('$(PYTHON_ROOT)',
+    python_executable = os.path.join('$$(PYTHON_ROOT)',
                                      os.path.split(sys.executable)[1])
 
 class Config:
     pass
 
+def splitFully(path):
+    dir, base = os.path.split(path)
+    if dir and dir != '' and dir != path:
+        return splitFully(dir)+[base]
+    if base == '':
+        return []
+    return [base]
+
+def makeHierarchy(sources):
+    '''Break a list of files into a hierarchy; for each value, if it is a string,
+       then it is a file.  If it is a dictionary, it is a folder.  The string is
+       the original path of the file.'''
+
+    hierarchy = {}
+    for file in sources:
+        path = splitFully(file)
+        if len(path):
+            dict = hierarchy
+            for part in path[:-1]:
+                if part not in dict:
+                    dict[part] = {}
+                dict = dict[part]
+            dict[path[-1]] = file
+        #else:
+        #    print 'Warning: failed to decompose path for '+str(file)
+    return hierarchy
+
 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()
 
+    srcargs = [
+        'srcs',
+        'incs',
+        'localincs',
+        'resources',
+        'misc']
+
+    def __init__(self, dspfile, source, env):
+        self.dspfile = str(dspfile)
         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()
+            get_abspath = dspfile.get_abspath
+        except AttributeError:
+            self.dspabs = os.path.abspath(dspfile)
         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()
+            self.dspabs = get_abspath()
+
+        if 'variant' not in env:
+            raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\
+                  "'Release') to create an MSVSProject.")
+        elif SCons.Util.is_String(env['variant']):
+            variants = [env['variant']]
+        elif SCons.Util.is_List(env['variant']):
+            variants = env['variant']
+
+        if 'buildtarget' not in env or env['buildtarget'] == None:
+            buildtarget = ['']
+        elif SCons.Util.is_String(env['buildtarget']):
+            buildtarget = [env['buildtarget']]
+        elif SCons.Util.is_List(env['buildtarget']):
+            if len(env['buildtarget']) != len(variants):
+                raise SCons.Errors.InternalError("Sizes of 'buildtarget' and 'variant' lists must be the same.")
+            buildtarget = []
+            for bt in env['buildtarget']:
+                if SCons.Util.is_String(bt):
+                    buildtarget.append(bt)
+                else:
+                    buildtarget.append(bt.get_abspath())
+        else:
+            buildtarget = [env['buildtarget'].get_abspath()]
+        if len(buildtarget) == 1:
+            bt = buildtarget[0]
+            buildtarget = []
+            for _ in variants:
+                buildtarget.append(bt)
+
+        if 'outdir' not in env or env['outdir'] == None:
+            outdir = ['']
+        elif SCons.Util.is_String(env['outdir']):
+            outdir = [env['outdir']]
+        elif SCons.Util.is_List(env['outdir']):
+            if len(env['outdir']) != len(variants):
+                raise SCons.Errors.InternalError("Sizes of 'outdir' and 'variant' lists must be the same.")
+            outdir = []
+            for s in env['outdir']:
+                if SCons.Util.is_String(s):
+                    outdir.append(s)
+                else:
+                    outdir.append(s.get_abspath())
+        else:
+            outdir = [env['outdir'].get_abspath()]
+        if len(outdir) == 1:
+            s = outdir[0]
+            outdir = []
+            for v in variants:
+                outdir.append(s)
+
+        if 'runfile' not in env or env['runfile'] == None:
+            runfile = buildtarget[-1:]
+        elif SCons.Util.is_String(env['runfile']):
+            runfile = [env['runfile']]
+        elif SCons.Util.is_List(env['runfile']):
+            if len(env['runfile']) != len(variants):
+                raise SCons.Errors.InternalError("Sizes of 'runfile' and 'variant' lists must be the same.")
+            runfile = []
+            for s in env['runfile']:
+                if SCons.Util.is_String(s):
+                    runfile.append(s)
+                else:
+                    runfile.append(s.get_abspath())
         else:
-            raise SCons.Errors.InternalError, \
-                  "You must specify a target 'buildtarget' file argument (such as the target" +\
-                  " executable) to create an MSVSProject."
+            runfile = [env['runfile'].get_abspath()]
+        if len(runfile) == 1:
+            s = runfile[0]
+            runfile = []
+            for v in variants:
+                runfile.append(s)
 
-        self.config.outdir = os.path.dirname(self.config.buildtarget)
+        self.sconscript = env['MSVSSCONSCRIPT']
+
+        cmdargs = env.get('cmdargs', '')
 
-        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'):
+        if 'name' in self.env:
             self.name = self.env['name']
         else:
             self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
-
-        print "Adding '" + self.name + ' - ' + self.config.variant + "' to Visual Studio Project '" + str(dspfile) + "'"
+        self.name = self.env.subst(self.name)
 
         sourcenames = [
-            ' Source Files',
+            '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.nokeep = 0
+        if 'nokeep' in env and env['variant'] != 0:
+            self.nokeep = 1
+
+        if self.nokeep == 0 and os.path.exists(self.dspabs):
             self.Parse()
 
-        for t in zip(sourcenames,srcargs):
-            if self.env.has_key(t[1]):
-                if type(self.env[t[1]]) == types.ListType:
+        for t in zip(sourcenames,self.srcargs):
+            if t[1] in self.env:
+                if SCons.Util.is_List(self.env[t[1]]):
                     for i in self.env[t[1]]:
                         if not i in self.sources[t[0]]:
                             self.sources[t[0]].append(i)
@@ -187,43 +290,75 @@ class _DSPGenerator:
                         self.sources[t[0]].append(self.env[t[1]])
 
         for n in sourcenames:
-            self.sources[n].sort()
+            self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower()))
+
+        def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile):
+            config = Config()
+            config.buildtarget = buildtarget
+            config.outdir = outdir
+            config.cmdargs = cmdargs
+            config.runfile = runfile
+
+            match = re.match('(.*)\|(.*)', variant)
+            if match:
+                config.variant = match.group(1)
+                config.platform = match.group(2)
+            else:
+                config.variant = variant
+                config.platform = 'Win32'
 
-        self.configs[self.config.variant] = self.config
+            self.configs[variant] = config
+            print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'"
+
+        for i in range(len(variants)):
+            AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs)
+
+        self.platforms = []
+        for key in self.configs.keys():
+            platform = self.configs[key].platform
+            if not platform in self.platforms:
+                self.platforms.append(platform)
 
     def Build(self):
         pass
-        
+
+V6DSPHeader = """\
+# Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4>
+# Microsoft Developer Studio Generated Build File, Format Version 6.00
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (x86) External Target" 0x0106
+
+CFG=%(name)s - Win32 %(confkey)s
+!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 "%(name)s.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 "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+"""
+
 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]))
+        confkeys = sorted(self.configs.keys())
+
+        name = self.name
+        confkey = confkeys[0]
+
+        self.file.write(V6DSPHeader % locals())
 
         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):
@@ -234,8 +369,7 @@ class _GenerateV6DSP(_DSPGenerator):
                         '# PROP Scc_LocalPath ""\n\n')
 
         first = 1
-        confkeys = self.configs.keys()
-        confkeys.sort()
+        confkeys = sorted(self.configs.keys())
         for kind in confkeys:
             outdir = self.configs[kind].outdir
             buildtarget = self.configs[kind].buildtarget
@@ -245,27 +379,33 @@ class _GenerateV6DSP(_DSPGenerator):
             else:
                 self.file.write('\n!ELSEIF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
 
+            env_has_buildtarget = 'MSVSBUILDTARGET' in self.env
+            if not env_has_buildtarget:
+                self.env['MSVSBUILDTARGET'] = buildtarget
+
             # 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))
+                # TODO(1.5):
+                #if kind.lower().find('debug') < 0:
                 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 -c "%s" -C %s -f %s %s' % (python_executable,
-                                                     exec_script_main,
-                                                     d, c, buildtarget)
-                self.file.write('# PROP %sCmd_Line "%s"\n' 
+                cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1)
+                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))
-            
+
+            if not env_has_buildtarget:
+                del self.env['MSVSBUILDTARGET']
+
         self.file.write('\n!ENDIF\n\n'
                         '# Begin Target\n\n')
         for kind in confkeys:
@@ -282,32 +422,35 @@ class _GenerateV6DSP(_DSPGenerator):
         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,1)
-        pdata = base64.encodestring(pdata)
-        self.file.write(pdata + '\n')
-        pdata = pickle.dumps(self.sources,1)
-        pdata = base64.encodestring(pdata)
-        self.file.write(pdata + '\n')
-  
+
+        if self.nokeep == 0:
+            # now we pickle some data and add it to the file -- MSDEV will ignore it.
+            pdata = pickle.dumps(self.configs,1)
+            pdata = base64.encodestring(pdata)
+            self.file.write(pdata + '\n')
+            pdata = pickle.dumps(self.sources,1)
+            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',
+        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()
+        cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
         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('|',';')
+            # TODO(1.5)
+            #typelist = categories[kind].replace('|', ';')
+            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'
@@ -315,19 +458,21 @@ class _GenerateV6DSP(_DSPGenerator):
                                 '# End Source File\n')
             self.file.write('# End Group\n')
 
-        # add the Conscript file outside of the groups
+        # add the SConscript file outside of the groups
         self.file.write('# Begin Source File\n\n'
-                        'SOURCE="' + str(self.source) + '"\n'
+                        'SOURCE="' + str(self.sconscript) + '"\n'
                         '# End Source File\n')
 
     def Parse(self):
         try:
-            dspfile = open(self.dspfile,'r')
+            dspfile = open(self.dspabs,'r')
         except IOError:
             return # doesn't exist yet, so can't add anything to configs.
 
         line = dspfile.readline()
         while line:
+            # TODO(1.5):
+            #if line.find("# End Project") > -1:
             if line.find("# End Project") > -1:
                 break
             line = dspfile.readline()
@@ -367,133 +512,272 @@ class _GenerateV6DSP(_DSPGenerator):
             return # unable to unpickle any data for some reason
 
         self.sources.update(data)
-    
+
     def Build(self):
         try:
-            self.file = open(self.dspfile,'w')
+            self.file = open(self.dspabs,'w')
         except IOError, detail:
-            raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail)
+            raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail))
         else:
             self.PrintHeader()
             self.PrintProject()
             self.file.close()
 
+V7DSPHeader = """\
+<?xml version="1.0" encoding = "%(encoding)s"?>
+<VisualStudioProject
+\tProjectType="Visual C++"
+\tVersion="%(versionstr)s"
+\tName="%(name)s"
+%(scc_attrs)s
+\tKeyword="MakeFileProj">
+"""
+
+V7DSPConfiguration = """\
+\t\t<Configuration
+\t\t\tName="%(variant)s|%(platform)s"
+\t\t\tOutputDirectory="%(outdir)s"
+\t\t\tIntermediateDirectory="%(outdir)s"
+\t\t\tConfigurationType="0"
+\t\t\tUseOfMFC="0"
+\t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
+\t\t\t<Tool
+\t\t\t\tName="VCNMakeTool"
+\t\t\t\tBuildCommandLine="%(buildcmd)s"
+\t\t\t\tCleanCommandLine="%(cleancmd)s"
+\t\t\t\tRebuildCommandLine="%(rebuildcmd)s"
+\t\t\t\tOutput="%(runfile)s"/>
+\t\t</Configuration>
+"""
+
+V8DSPHeader = """\
+<?xml version="1.0" encoding="%(encoding)s"?>
+<VisualStudioProject
+\tProjectType="Visual C++"
+\tVersion="%(versionstr)s"
+\tName="%(name)s"
+%(scc_attrs)s
+\tRootNamespace="%(name)s"
+\tKeyword="MakeFileProj">
+"""
+
+V8DSPConfiguration = """\
+\t\t<Configuration
+\t\t\tName="%(variant)s|%(platform)s"
+\t\t\tConfigurationType="0"
+\t\t\tUseOfMFC="0"
+\t\t\tATLMinimizesCRunTimeLibraryUsage="false"
+\t\t\t>
+\t\t\t<Tool
+\t\t\t\tName="VCNMakeTool"
+\t\t\t\tBuildCommandLine="%(buildcmd)s"
+\t\t\t\tReBuildCommandLine="%(rebuildcmd)s"
+\t\t\t\tCleanCommandLine="%(cleancmd)s"
+\t\t\t\tOutput="%(runfile)s"
+\t\t\t\tPreprocessorDefinitions="%(preprocdefs)s"
+\t\t\t\tIncludeSearchPath="%(includepath)s"
+\t\t\t\tForcedIncludes=""
+\t\t\t\tAssemblySearchPath=""
+\t\t\t\tForcedUsingAssemblies=""
+\t\t\t\tCompileAsManaged=""
+\t\t\t/>
+\t\t</Configuration>
+"""
 class _GenerateV7DSP(_DSPGenerator):
     """Generates a Project file for MSVS .NET"""
 
-    def __init__(self, dspfile, source, env, version):
+    def __init__(self, dspfile, source, env):
         _DSPGenerator.__init__(self, dspfile, source, env)
-        if version==7.0: self.version="7.00"
-        else: self.version="7.10"
+        self.version = env['MSVS_VERSION']
+        self.version_num, self.suite = msvs_parse_version(self.version)
+        if self.version_num >= 8.0:
+            self.versionstr = '8.00'
+            self.dspheader = V8DSPHeader
+            self.dspconfiguration = V8DSPConfiguration
+        else:
+            if self.version_num >= 7.1:
+                self.versionstr = '7.10'
+            else:
+                self.versionstr = '7.00'
+            self.dspheader = V7DSPHeader
+            self.dspconfiguration = V7DSPConfiguration
+        self.file = None
 
     def PrintHeader(self):
-        self.file.write('<?xml version="1.0" encoding = "Windows-1252"?>\n'
-                        '<VisualStudioProject\n'
-                        '      ProjectType="Visual C++"\n'
-                        '      Version="%s"\n'
-                        '      Name="%s"\n'
-                        '      SccProjectName=""\n'
-                        '      SccLocalPath=""\n'
-                        '      Keyword="MakeFileProj">\n'
-                        '      <Platforms>\n'
-                        '              <Platform\n'
-                        '                      Name="Win32"/>\n'
-                        '      </Platforms>\n' % (self.version, self.name))
+        env = self.env
+        versionstr = self.versionstr
+        name = self.name
+        encoding = self.env.subst('$MSVSENCODING')
+        scc_provider = env.get('MSVS_SCC_PROVIDER', '')
+        scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
+        scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
+        scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
+        project_guid = env.get('MSVS_PROJECT_GUID', '')
+        if self.version_num >= 8.0 and not project_guid:
+            project_guid = _generateGUID(self.dspfile, '')
+        if scc_provider != '':
+            scc_attrs = ('\tProjectGUID="%s"\n'
+                         '\tSccProjectName="%s"\n'
+                         '\tSccAuxPath="%s"\n'
+                         '\tSccLocalPath="%s"\n'
+                         '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider))
+        else:
+            scc_attrs = ('\tProjectGUID="%s"\n'
+                         '\tSccProjectName="%s"\n'
+                         '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path))
 
-    def PrintProject(self):
-        
+        self.file.write(self.dspheader % locals())
 
-        self.file.write('      <Configurations>\n')
+        self.file.write('\t<Platforms>\n')
+        for platform in self.platforms:
+            self.file.write(
+                        '\t\t<Platform\n'
+                        '\t\t\tName="%s"/>\n' % platform)
+        self.file.write('\t</Platforms>\n')
 
-        first = 1
-        confkeys = self.configs.keys()
-        confkeys.sort()
+        if self.version_num >= 8.0:
+            self.file.write('\t<ToolFiles>\n'
+                            '\t</ToolFiles>\n')
+
+    def PrintProject(self):
+        self.file.write('\t<Configurations>\n')
+
+        confkeys = sorted(self.configs.keys())
         for kind in confkeys:
+            variant = self.configs[kind].variant
+            platform = self.configs[kind].platform
             outdir = self.configs[kind].outdir
             buildtarget = self.configs[kind].buildtarget
+            runfile     = self.configs[kind].runfile
+            cmdargs = self.configs[kind].cmdargs
+
+            env_has_buildtarget = 'MSVSBUILDTARGET' in self.env
+            if not env_has_buildtarget:
+                self.env['MSVSBUILDTARGET'] = buildtarget
+
+            starting = 'echo Starting SCons && '
+            if cmdargs:
+                cmdargs = ' ' + cmdargs
+            else:
+                cmdargs = ''
+            buildcmd    = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs)
+            rebuildcmd  = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs)
+            cleancmd    = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs)
+
+            # TODO(1.5)
+            #preprocdefs = xmlify(';'.join(self.env.get('CPPDEFINES', [])))
+            #includepath = xmlify(';'.join(self.env.get('CPPPATH', [])))
+            preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', []))))
+            includepath = xmlify(';'.join(self.env.get('CPPPATH', [])))
 
-            (d,c) = os.path.split(str(self.conspath))
-            cmd = '%s -c &quot;%s&quot; -C %s -f %s %s' % (python_executable,
-                                                   exec_script_main_xml,
-                                                   d, c, buildtarget)
-
-            cleancmd = '%s -c &quot;%s&quot; -C %s -f %s -c %s' % (python_executable,
-                                                         exec_script_main_xml,
-                                                         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')
+            if not env_has_buildtarget:
+                del self.env['MSVSBUILDTARGET']
+
+            self.file.write(self.dspconfiguration % locals())
+
+        self.file.write('\t</Configurations>\n')
+
+        if self.version_num >= 7.1:
+            self.file.write('\t<References>\n'
+                            '\t</References>\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,1)
-        pdata = base64.encodestring(pdata)
-        self.file.write('<!-- SCons Data:\n' + pdata + '\n')
-        pdata = pickle.dumps(self.sources,1)
-        pdata = base64.encodestring(pdata)
-        self.file.write(pdata + '-->\n')
-  
+        if self.nokeep == 0:
+            # now we pickle some data and add it to the file -- MSDEV will ignore it.
+            pdata = pickle.dumps(self.configs,1)
+            pdata = base64.encodestring(pdata)
+            self.file.write('<!-- SCons Data:\n' + pdata + '\n')
+            pdata = pickle.dumps(self.sources,1)
+            pdata = base64.encodestring(pdata)
+            self.file.write(pdata + '-->\n')
+
+    def printSources(self, hierarchy, commonprefix):
+        sorteditems = hierarchy.items()
+        sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower()))
+
+        # First folders, then files
+        for key, value in sorteditems:
+            if SCons.Util.is_Dict(value):
+                self.file.write('\t\t\t<Filter\n'
+                                '\t\t\t\tName="%s"\n'
+                                '\t\t\t\tFilter="">\n' % (key))
+                self.printSources(value, commonprefix)
+                self.file.write('\t\t\t</Filter>\n')
+
+        for key, value in sorteditems:
+            if SCons.Util.is_String(value):
+                file = value
+                if commonprefix:
+                    file = os.path.join(commonprefix, value)
+                file = os.path.normpath(file)
+                self.file.write('\t\t\t<File\n'
+                                '\t\t\t\tRelativePath="%s">\n'
+                                '\t\t\t</File>\n' % (file))
+
     def PrintSourceFiles(self):
-        categories = {' Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
+        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')
-        
+        self.file.write('\t<Files>\n')
+
         cats = categories.keys()
-        cats.sort()
+        cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
+        cats = [k for k in cats if self.sources[k]]
         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))
+            if len(cats) > 1:
+                self.file.write('\t\t<Filter\n'
+                                '\t\t\tName="%s"\n'
+                                '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
+
+            sources = self.sources[kind]
+
+            # First remove any common prefix
+            commonprefix = None
+            if len(sources) > 1:
+                s = list(map(os.path.normpath, sources))
+                # take the dirname because the prefix may include parts
+                # of the filenames (e.g. if you have 'dir\abcd' and
+                # 'dir\acde' then the cp will be 'dir\a' )
+                cp = os.path.dirname( os.path.commonprefix(s) )
+                if cp and s[0][len(cp)] == os.sep:
+                    # +1 because the filename starts after the separator
+                    sources = [s[len(cp)+1:] for s in sources]
+                    commonprefix = cp
+            elif len(sources) == 1:
+                commonprefix = os.path.dirname( sources[0] )
+                sources[0] = os.path.basename( sources[0] )
+
+            hierarchy = makeHierarchy(sources)
+            self.printSources(hierarchy, commonprefix=commonprefix)
+
+            if len(cats)>1:
+                self.file.write('\t\t</Filter>\n')
+
+        # add the SConscript file outside of the groups
+        self.file.write('\t\t<File\n'
+                        '\t\t\tRelativePath="%s">\n'
+                        '\t\t</File>\n' % str(self.sconscript))
+
+        self.file.write('\t</Files>\n'
+                        '\t<Globals>\n'
+                        '\t</Globals>\n')
 
     def Parse(self):
         try:
-            dspfile = open(self.dspfile,'r')
+            dspfile = open(self.dspabs,'r')
         except IOError:
             return # doesn't exist yet, so can't add anything to configs.
 
         line = dspfile.readline()
         while line:
+            # TODO(1.5)
+            #if line.find('<!-- SCons Data:') > -1:
             if line.find('<!-- SCons Data:') > -1:
                 break
             line = dspfile.readline()
@@ -532,12 +816,12 @@ class _GenerateV7DSP(_DSPGenerator):
             return # unable to unpickle any data for some reason
 
         self.sources.update(data)
-    
+
     def Build(self):
         try:
-            self.file = open(self.dspfile,'w')
+            self.file = open(self.dspabs,'w')
         except IOError, detail:
-            raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail)
+            raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail))
         else:
             self.PrintHeader()
             self.PrintProject()
@@ -545,46 +829,87 @@ class _GenerateV7DSP(_DSPGenerator):
 
 class _DSWGenerator:
     """ Base class for DSW generators """
-    def __init__(self, dswfile, dspfile, source, env):
+    def __init__(self, dswfile, 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'):
+        if 'projects' not in env:
+            raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.")
+        projects = env['projects']
+        if not SCons.Util.is_List(projects):
+            raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.")
+        projects = SCons.Util.flatten(projects)
+        if len(projects) < 1:
+            raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.")
+        self.dspfiles = list(map(str, projects))
+
+        if 'name' in self.env:
             self.name = self.env['name']
         else:
-            self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
+            self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0])
+        self.name = self.env.subst(self.name)
 
     def Build(self):
         pass
 
 class _GenerateV7DSW(_DSWGenerator):
     """Generates a Solution file for MSVS .NET"""
-    def __init__(self, dswfile, dspfile, source, env, version):
-        _DSWGenerator.__init__(self, dswfile, dspfile, source, env)
-
-        if version==7.0: self.version="7.00"
-        else: self.version="8.00"
-
-        if env.has_key('slnguid') and env['slnguid']:
+    def __init__(self, dswfile, source, env):
+        _DSWGenerator.__init__(self, dswfile, source, env)
+
+        self.file = None
+        self.version = self.env['MSVS_VERSION']
+        self.version_num, self.suite = msvs_parse_version(self.version)
+        self.versionstr = '7.00'
+        if self.version_num >= 8.0:
+            self.versionstr = '9.00'
+        elif self.version_num >= 7.1:
+            self.versionstr = '8.00'
+        if self.version_num >= 8.0:
+            self.versionstr = '9.00'
+
+        if 'slnguid' in env 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.nokeep = 0
+        if 'nokeep' in env and env['variant'] != 0:
+            self.nokeep = 1
+
+        if self.nokeep == 0 and os.path.exists(self.dswfile):
             self.Parse()
 
-        self.configs[self.config.variant] = self.config
+        def AddConfig(self, variant, dswfile=dswfile):
+            config = Config()
+
+            match = re.match('(.*)\|(.*)', variant)
+            if match:
+                config.variant = match.group(1)
+                config.platform = match.group(2)
+            else:
+                config.variant = variant
+                config.platform = 'Win32'
+
+            self.configs[variant] = config
+            print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'"
+
+        if 'variant' not in env:
+            raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\
+                  "'Release') to create an MSVS Solution File.")
+        elif SCons.Util.is_String(env['variant']):
+            AddConfig(self, env['variant'])
+        elif SCons.Util.is_List(env['variant']):
+            for variant in env['variant']:
+                AddConfig(self, variant)
+
+        self.platforms = []
+        for key in self.configs.keys():
+            platform = self.configs[key].platform
+            if not platform in self.platforms:
+                self.platforms.append(platform)
 
     def Parse(self):
         try:
@@ -617,86 +942,159 @@ class _GenerateV7DSW(_DSWGenerator):
 
     def PrintSolution(self):
         """Writes a solution file"""
-        self.file.write('Microsoft Visual Studio Solution File, Format Version %s\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.version, self.name, os.path.basename(self.dspfile), self.slnguid))
-        confkeys = self.configs.keys()
-        confkeys.sort()
+        self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr )
+        if self.version_num >= 8.0:
+            self.file.write('# Visual Studio 2005\n')
+        for p in self.dspfiles:
+            name = os.path.basename(p)
+            base, suffix = SCons.Util.splitext(name)
+            if suffix == '.vcproj':
+                name = base
+            guid = _generateGUID(p, '')
+            self.file.write('Project("%s") = "%s", "%s", "%s"\n'
+                            % ( external_makefile_guid, name, p, guid ) )
+            if self.version_num >= 7.1 and self.version_num < 8.0:
+                self.file.write('\tProjectSection(ProjectDependencies) = postProject\n'
+                                '\tEndProjectSection\n')
+            self.file.write('EndProject\n')
+
+        self.file.write('Global\n')
+
+        env = self.env
+        if 'MSVS_SCC_PROVIDER' in env:
+            dspfile_base = os.path.basename(self.dspfile)
+            slnguid = self.slnguid
+            scc_provider = env.get('MSVS_SCC_PROVIDER', '')
+            scc_provider = scc_provider.replace(' ', r'\u0020')
+            scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
+            # scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
+            scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
+            scc_project_base_path = env.get('MSVS_SCC_PROJECT_BASE_PATH', '')
+            # project_guid = env.get('MSVS_PROJECT_GUID', '')
+
+            self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n'
+                            '\t\tSccNumberOfProjects = 2\n'
+                            '\t\tSccProjectUniqueName0 = %(dspfile_base)s\n'
+                            '\t\tSccLocalPath0 = %(scc_local_path)s\n'
+                            '\t\tCanCheckoutShared = true\n'
+                            '\t\tSccProjectFilePathRelativizedFromConnection0 = %(scc_project_base_path)s\n'
+                            '\t\tSccProjectName1 = %(scc_project_name)s\n'
+                            '\t\tSccLocalPath1 = %(scc_local_path)s\n'
+                            '\t\tSccProvider1 = %(scc_provider)s\n'
+                            '\t\tCanCheckoutShared = true\n'
+                            '\t\tSccProjectFilePathRelativizedFromConnection1 = %(scc_project_base_path)s\n'
+                            '\t\tSolutionUniqueID = %(slnguid)s\n'
+                            '\tEndGlobalSection\n' % locals())
+
+        if self.version_num >= 8.0:
+            self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
+        else:
+            self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n')
+
+        confkeys = sorted(self.configs.keys())
         cnt = 0
         for name in confkeys:
-            self.file.write('          ConfigName.%d = %s\n' % (cnt, name.capitalize()))
+            variant = self.configs[name].variant
+            platform = self.configs[name].platform
+            if self.version_num >= 8.0:
+                self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform))
+            else:
+                self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant))
             cnt = cnt + 1
-        self.file.write('      EndGlobalSection\n'
-                        '      GlobalSection(ProjectDependencies) = postSolution\n'
-                        '      EndGlobalSection\n'
-                        '      GlobalSection(ProjectConfiguration) = postSolution\n')
+        self.file.write('\tEndGlobalSection\n')
+        if self.version_num < 7.1:
+            self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n'
+                            '\tEndGlobalSection\n')
+        if self.version_num >= 8.0:
+            self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
+        else:
+            self.file.write('\tGlobalSection(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,1)
-        pdata = base64.encodestring(pdata)
-        self.file.write(pdata + '\n')
+            variant = self.configs[name].variant
+            platform = self.configs[name].platform
+            if self.version_num >= 8.0:
+                for p in self.dspfiles:
+                    guid = _generateGUID(p, '')
+                    self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n'
+                                    '\t\t%s.%s|%s.Build.0 = %s|%s\n'  % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform))
+            else:
+                for p in self.dspfiles:
+                    guid = _generateGUID(p, '')
+                    self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
+                                    '\t\t%s.%s.Build.0 = %s|%s\n'  %(guid,variant,variant,platform,guid,variant,variant,platform))
+
+        self.file.write('\tEndGlobalSection\n')
+
+        if self.version_num >= 8.0:
+            self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n'
+                            '\t\tHideSolutionNode = FALSE\n'
+                            '\tEndGlobalSection\n')
+        else:
+            self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
+                            '\tEndGlobalSection\n'
+                            '\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
+                            '\tEndGlobalSection\n')
+        self.file.write('EndGlobal\n')
+        if self.nokeep == 0:
+            pdata = pickle.dumps(self.configs,1)
+            pdata = base64.encodestring(pdata)
+            self.file.write(pdata + '\n')
 
     def Build(self):
         try:
             self.file = open(self.dswfile,'w')
         except IOError, detail:
-            raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
+            raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail))
         else:
             self.PrintSolution()
             self.file.close()
 
+V6DSWHeader = """\
+Microsoft Developer Studio Workspace File, Format Version 6.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "%(name)s"="%(dspfile)s" - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+"""
+
 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))
+        name = self.name
+        dspfile = self.dspfiles[0]
+        self.file.write(V6DSWHeader % locals())
 
     def Build(self):
         try:
             self.file = open(self.dswfile,'w')
         except IOError, detail:
-            raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
+            raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail))
         else:
             self.PrintWorkspace()
             self.file.close()
@@ -705,21 +1103,27 @@ class _GenerateV6DSW(_DSWGenerator):
 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, float(env['MSVS_VERSION']))
+    version_num = 6.0
+    if 'MSVS_VERSION' in env:
+        version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
+    if version_num >= 7.0:
+        g = _GenerateV7DSP(dspfile, source, env)
         g.Build()
     else:
         g = _GenerateV6DSP(dspfile, source, env)
         g.Build()
 
-def GenerateDSW(dswfile, dspfile, source, env):
+def GenerateDSW(dswfile, 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, float(env['MSVS_VERSION']))
+
+    version_num = 6.0
+    if 'MSVS_VERSION' in env:
+        version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
+    if version_num >= 7.0:
+        g = _GenerateV7DSW(dswfile, source, env)
         g.Build()
     else:
-        g = _GenerateV6DSW(dswfile, dspfile, source, env)
+        g = _GenerateV6DSW(dswfile, source, env)
         g.Build()
 
 
@@ -728,365 +1132,272 @@ def GenerateDSW(dswfile, dspfile, source, env):
 # 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()
-            if 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:
-        return []
-
-    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
+def GetMSVSProjectSuffix(target, source, env, for_signature):
+     return env['MSVS']['PROJECTSUFFIX']
 
-    if not L:
-        return []
+def GetMSVSSolutionSuffix(target, source, env, for_signature):
+     return env['MSVS']['SOLUTIONSUFFIX']
 
-    # This is a hack to get around the fact that certain Visual Studio
-    # patches place a "6.1" version in the registry, which does not have
-    # any of the keys we need to find include paths, install directories,
-    # etc.  Therefore we ignore it if it is there, since it throws all
-    # other logic off.
-    try:
-        L.remove("6.1")
-    except ValueError:
-        pass
-    
-    L.sort()
-    L.reverse()
+def GenerateProject(target, source, env):
+    # generate the dsp file, according to the version of MSVS.
+    builddspfile = target[0]
+    dspfile = builddspfile.srcnode()
 
-    return L
+    # this detects whether or not we're using a VariantDir
+    if not dspfile is builddspfile:
+        try:
+            bdsp = open(str(builddspfile), "w+")
+        except IOError, detail:
+            print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
+            raise
 
-def get_msvs_install_dirs(version = None):
-    """
-    Get installed locations for various msvc-related products, like the .NET SDK
-    and the Platform SDK.
-    """
+        bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
 
-    if not SCons.Util.can_read_reg:
-        return {}
+    GenerateDSP(dspfile, source, env)
 
-    if not version:
-        versions = get_visualstudio_versions()
-        if versions:
-            version = versions[0] #use highest version by default
-        else:
-            return {}
+    if env.get('auto_build_solution', 1):
+        builddswfile = target[1]
+        dswfile = builddswfile.srcnode()
 
-    K = 'Software\\Microsoft\\VisualStudio\\' + version
+        if not dswfile is builddswfile:
 
-    # 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
+            try:
+                bdsw = open(str(builddswfile), "w+")
+            except IOError, detail:
+                print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
+                raise
 
-    # 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
+            bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
 
-    # .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
+        GenerateDSW(dswfile, source, env)
 
-    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
+def GenerateSolution(target, source, env):
+    GenerateDSW(target[0], source, env)
 
-    # 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
+def projectEmitter(target, source, env):
+    """Sets up the DSP dependencies."""
 
-    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;
+    # todo: Not sure what sets source to what user has passed as target,
+    # but this is what happens. When that is fixed, we also won't have
+    # to make the user always append env['MSVSPROJECTSUFFIX'] to target.
+    if source[0] == target[0]:
+        source = []
 
-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()
+    # make sure the suffix is correct for the version of MSVS we're running.
+    (base, suff) = SCons.Util.splitext(str(target[0]))
+    suff = env.subst('$MSVSPROJECTSUFFIX')
+    target[0] = base + suff
 
-#     print "SConscript    :",str(source[0])
-#     print "DSW file      :",dswfile
-#     print "DSP file      :",dspfile
-#     print "Build DSW file:",builddswfile
-#     print "Build DSP file:",builddspfile
+    if not source:
+        source = 'prj_inputs:'
+        source = source + env.subst('$MSVSSCONSCOM', 1)
+        source = source + env.subst('$MSVSENCODING', 1)
+
+        if 'buildtarget' in env and env['buildtarget'] != None:
+            if SCons.Util.is_String(env['buildtarget']):
+                source = source + ' "%s"' % env['buildtarget']
+            elif SCons.Util.is_List(env['buildtarget']):
+                for bt in env['buildtarget']:
+                    if SCons.Util.is_String(bt):
+                        source = source + ' "%s"' % bt
+                    else:
+                        try: source = source + ' "%s"' % bt.get_abspath()
+                        except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None")
+            else:
+                try: source = source + ' "%s"' % env['buildtarget'].get_abspath()
+                except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None")
+
+        if 'outdir' in env and env['outdir'] != None:
+            if SCons.Util.is_String(env['outdir']):
+                source = source + ' "%s"' % env['outdir']
+            elif SCons.Util.is_List(env['outdir']):
+                for s in env['outdir']:
+                    if SCons.Util.is_String(s):
+                        source = source + ' "%s"' % s
+                    else:
+                        try: source = source + ' "%s"' % s.get_abspath()
+                        except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None")
+            else:
+                try: source = source + ' "%s"' % env['outdir'].get_abspath()
+                except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None")
 
-    # 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 = open(str(builddspfile), "w+")
-        except IOError, detail:
-            print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
-            raise
+        if 'name' in env:
+            if SCons.Util.is_String(env['name']):
+                source = source + ' "%s"' % env['name']
+            else:
+                raise SCons.Errors.InternalError("name must be a string")
+
+        if 'variant' in env:
+            if SCons.Util.is_String(env['variant']):
+                source = source + ' "%s"' % env['variant']
+            elif SCons.Util.is_List(env['variant']):
+                for variant in env['variant']:
+                    if SCons.Util.is_String(variant):
+                        source = source + ' "%s"' % variant
+                    else:
+                        raise SCons.Errors.InternalError("name must be a string or a list of strings")
+            else:
+                raise SCons.Errors.InternalError("variant must be a string or a list of strings")
+        else:
+            raise SCons.Errors.InternalError("variant must be specified")
+
+        for s in _DSPGenerator.srcargs:
+            if s in env:
+                if SCons.Util.is_String(env[s]):
+                    source = source + ' "%s' % env[s]
+                elif SCons.Util.is_List(env[s]):
+                    for t in env[s]:
+                        if SCons.Util.is_String(t):
+                            source = source + ' "%s"' % t
+                        else:
+                            raise SCons.Errors.InternalError(s + " must be a string or a list of strings")
+                else:
+                    raise SCons.Errors.InternalError(s + " must be a string or a list of strings")
 
-        bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
+        source = source + ' "%s"' % str(target[0])
+        source = [SCons.Node.Python.Value(source)]
 
-        try:
-            bdsw = open(str(builddswfile), "w+")
-        except IOError, detail:
-            print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
-            raise
+    targetlist = [target[0]]
+    sourcelist = source
 
-        bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
+    if env.get('auto_build_solution', 1):
+        env['projects'] = targetlist
+        t, s = solutionEmitter(target, target, env)
+        targetlist = targetlist + t
 
-    GenerateDSP(dspfile, source, env)
-    GenerateDSW(dswfile, dspfile, source, env) 
+    return (targetlist, sourcelist)
 
-def projectEmitter(target, source, env):
-    """Sets up the DSP and DSW dependencies for an SConscript file."""
+def solutionEmitter(target, source, env):
+    """Sets up the DSW dependencies."""
 
+    # todo: Not sure what sets source to what user has passed as target,
+    # but this is what happens. When that is fixed, we also won't have
+    # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target.
     if source[0] == target[0]:
         source = []
 
     # make sure the suffix is correct for the version of MSVS we're running.
     (base, suff) = SCons.Util.splitext(str(target[0]))
-    suff = env['MSVSPROJECTSUFFIX']
+    suff = env.subst('$MSVSSOLUTIONSUFFIX')
     target[0] = base + suff
 
-    dspfile = SCons.Node.FS.default_fs.File(target[0]).srcnode()
-    dswfile = SCons.Node.FS.default_fs.File(SCons.Util.splitext(str(dspfile))[0] + env['MSVSSOLUTIONSUFFIX'])
-
     if not source:
-        source = [SCons.Script.SConscript.stack[-1].sconscript.srcnode()]
+        source = 'sln_inputs:'
 
-    source[0].attributes.sconstruct = SCons.Script.SConscript.stack[0].sconscript
+        if 'name' in env:
+            if SCons.Util.is_String(env['name']):
+                source = source + ' "%s"' % env['name']
+            else:
+                raise SCons.Errors.InternalError("name must be a string")
+
+        if 'variant' in env:
+            if SCons.Util.is_String(env['variant']):
+                source = source + ' "%s"' % env['variant']
+            elif SCons.Util.is_List(env['variant']):
+                for variant in env['variant']:
+                    if SCons.Util.is_String(variant):
+                        source = source + ' "%s"' % variant
+                    else:
+                        raise SCons.Errors.InternalError("name must be a string or a list of strings")
+            else:
+                raise SCons.Errors.InternalError("variant must be a string or a list of strings")
+        else:
+            raise SCons.Errors.InternalError("variant must be specified")
+
+        if 'slnguid' in env:
+            if SCons.Util.is_String(env['slnguid']):
+                source = source + ' "%s"' % env['slnguid']
+            else:
+                raise SCons.Errors.InternalError("slnguid must be a string")
 
-    bdswpath = SCons.Util.splitext(str(target[0]))[0] + env['MSVSSOLUTIONSUFFIX']
-    bdswfile = SCons.Node.FS.default_fs.File(bdswpath)
+        if 'projects' in env:
+            if SCons.Util.is_String(env['projects']):
+                source = source + ' "%s"' % env['projects']
+            elif SCons.Util.is_List(env['projects']):
+                for t in env['projects']:
+                    if SCons.Util.is_String(t):
+                        source = source + ' "%s"' % t
 
-    # 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)
+        source = source + ' "%s"' % str(target[0])
+        source = [SCons.Node.Python.Value(source)]
 
-projectGeneratorAction = SCons.Action.Action(GenerateProject, None)
+    return ([target[0]], source)
+
+projectAction = SCons.Action.Action(GenerateProject, None)
+
+solutionAction = SCons.Action.Action(GenerateSolution, None)
 
 projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
                                        suffix = '$MSVSPROJECTSUFFIX',
                                        emitter = projectEmitter)
 
+solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM',
+                                        suffix = '$MSVSSOLUTIONSUFFIX',
+                                        emitter = solutionEmitter)
+
+default_MSVS_SConscript = None
+
 def generate(env):
     """Add Builders and construction variables for Microsoft Visual
     Studio project files to an Environment."""
     try:
-        bld = env['BUILDERS']['MSVSProject']
+        env['BUILDERS']['MSVSProject']
     except KeyError:
         env['BUILDERS']['MSVSProject'] = projectBuilder
 
-    env['MSVSPROJECTCOM'] = projectGeneratorAction
-
     try:
-        version = get_default_visualstudio_version(env)
-        # keep a record of some of the MSVS info so the user can use it.
-        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
+        env['BUILDERS']['MSVSSolution']
+    except KeyError:
+        env['BUILDERS']['MSVSSolution'] = solutionBuilder
 
-    if (float(env['MSVS_VERSION']) < 7.0):
-        env['MSVSPROJECTSUFFIX']  = '.dsp'
-        env['MSVSSOLUTIONSUFFIX'] = '.dsw'
+    env['MSVSPROJECTCOM'] = projectAction
+    env['MSVSSOLUTIONCOM'] = solutionAction
+
+    if SCons.Script.call_stack:
+        # XXX Need to find a way to abstract this; the build engine
+        # shouldn't depend on anything in SCons.Script.
+        env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript
+    else:
+        global default_MSVS_SConscript
+        if default_MSVS_SConscript is None:
+            default_MSVS_SConscript = env.File('SConstruct')
+        env['MSVSSCONSCRIPT'] = default_MSVS_SConscript
+
+    env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env))
+    env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}'
+    env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS'
+    env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
+    env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
+    env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"'
+    env['MSVSENCODING'] = 'Windows-1252'
+
+    # Set-up ms tools paths for default version
+    msvc_setup_env_once(env)
+
+    if 'MSVS_VERSION' in env:
+        version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
     else:
-        env['MSVSPROJECTSUFFIX']  = '.vcproj'
-        env['MSVSSOLUTIONSUFFIX'] = '.sln'
+        (version_num, suite) = (7.0, None) # guess at a default
+    if 'MSVS' not in env:
+        env['MSVS'] = {}
+    if (version_num < 7.0):
+        env['MSVS']['PROJECTSUFFIX']  = '.dsp'
+        env['MSVS']['SOLUTIONSUFFIX'] = '.dsw'
+    else:
+        env['MSVS']['PROJECTSUFFIX']  = '.vcproj'
+        env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
+
+    env['GET_MSVSPROJECTSUFFIX']  = GetMSVSProjectSuffix
+    env['GET_MSVSSOLUTIONSUFFIX']  = GetMSVSSolutionSuffix
+    env['MSVSPROJECTSUFFIX']  = '${GET_MSVSPROJECTSUFFIX}'
+    env['MSVSSOLUTIONSUFFIX']  = '${GET_MSVSSOLUTIONSUFFIX}'
+    env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
 
 def exists(env):
-    try:
-        v = SCons.Tool.msvs.get_visualstudio_versions()
-    except (SCons.Util.RegError, SCons.Errors.InternalError):
-        pass
-    
-    if not v:
-        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 1
+    return msvc_exists()
 
+# Local Variables:
+# tab-width:4
+# indent-tabs-mode:nil
+# End:
+# vim: set expandtab tabstop=4 shiftwidth=4: