summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Tool/msvs.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/engine/SCons/Tool/msvs.py')
-rw-r--r--src/engine/SCons/Tool/msvs.py1030
1 files changed, 1030 insertions, 0 deletions
diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py
new file mode 100644
index 0000000..9b8eb41
--- /dev/null
+++ b/src/engine/SCons/Tool/msvs.py
@@ -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
+