diff options
Diffstat (limited to 'src/engine/SCons/Tool/msvs.py')
-rw-r--r-- | src/engine/SCons/Tool/msvs.py | 1030 |
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 + |