summaryrefslogtreecommitdiffstats
path: root/src/engine/SCons/Tool/msvs.py
diff options
context:
space:
mode:
authorSteven Knight <knight@baldmt.com>2005-10-08 15:31:13 (GMT)
committerSteven Knight <knight@baldmt.com>2005-10-08 15:31:13 (GMT)
commite664e763f95c2e24f1f08f11e61828c68baf9854 (patch)
treebfe0b21a2202526bf29df983e32dde3f8b16173a /src/engine/SCons/Tool/msvs.py
parentee11284e78bb1405e112a594ba36ef4a2c14c481 (diff)
downloadSCons-e664e763f95c2e24f1f08f11e61828c68baf9854.zip
SCons-e664e763f95c2e24f1f08f11e61828c68baf9854.tar.gz
SCons-e664e763f95c2e24f1f08f11e61828c68baf9854.tar.bz2
MSVC.py improvements: new MSVSSolution() Builder, new variables to contro generation of project and solution files. (Stanislav Baranov)
Diffstat (limited to 'src/engine/SCons/Tool/msvs.py')
-rw-r--r--src/engine/SCons/Tool/msvs.py666
1 files changed, 493 insertions, 173 deletions
diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py
index fb4f1b3..280c456 100644
--- a/src/engine/SCons/Tool/msvs.py
+++ b/src/engine/SCons/Tool/msvs.py
@@ -40,12 +40,11 @@ import pickle
import re
import string
import sys
-import types
import SCons.Builder
import SCons.Node.FS
import SCons.Platform.win32
-import SCons.Script
+import SCons.Script.SConscript
import SCons.Util
import SCons.Warnings
@@ -86,7 +85,6 @@ def _generateGUID(slnfile, name):
# 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, "'", "&apos;")
# 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,52 +95,109 @@ 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 not dict.has_key(part):
+ 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 """
+
+ srcargs = [
+ 'srcs',
+ 'incs',
+ 'localincs',
+ 'resources',
+ 'misc']
+
def __init__(self, dspfile, source, env):
- if type(dspfile) == types.StringType:
+ if SCons.Util.is_String(dspfile):
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:
+ if not env.has_key('variant'):
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()
+ elif SCons.Util.is_String(env['variant']):
+ variants = [env['variant']]
+ elif SCons.Util.is_List(env['variant']):
+ variants = env['variant']
+
+ if not env.has_key('buildtarget') 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:
- 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])
+ buildtarget = [env['buildtarget'].get_abspath()]
+ if len(buildtarget) == 1:
+ bt = buildtarget[0]
+ buildtarget = []
+ for v in variants:
+ buildtarget.append(bt)
+
+ if not env.has_key('outdir') 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:
- self.source = source[0].get_abspath()
+ outdir = [env['outdir'].get_abspath()]
+ if len(outdir) == 1:
+ s = outdir[0]
+ outdir = []
+ for v in variants:
+ outdir.append(s)
+
+ self.sconscript = env['MSVSSCONSCRIPT']
self.env = env
@@ -151,34 +206,29 @@ class _DSPGenerator:
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) + "'"
-
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 env.has_key('nokeep') and env['variant'] != 0:
+ self.nokeep = 1
+
+ if self.nokeep == 0 and os.path.exists(self.dspfile):
self.Parse()
- for t in zip(sourcenames,srcargs):
+ for t in zip(sourcenames,self.srcargs):
if self.env.has_key(t[1]):
- if type(self.env[t[1]]) == types.ListType:
+ 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,9 +237,32 @@ 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(variant, buildtarget, outdir):
+ config = Config()
+ config.buildtarget = buildtarget
+ config.outdir = outdir
- self.configs[self.config.variant] = self.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(dspfile) + "'"
+
+ for i in range(len(variants)):
+ AddConfig(variants[i], buildtarget[i], outdir[i])
+
+ 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
@@ -253,6 +326,10 @@ class _GenerateV6DSP(_DSPGenerator):
else:
self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
+ env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
+ 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'
@@ -263,16 +340,17 @@ class _GenerateV6DSP(_DSPGenerator):
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 = 'echo Starting SCons && "%s" -c "%s" -C %s -f %s %s'
- cmd = cmd % (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:
@@ -290,23 +368,24 @@ class _GenerateV6DSP(_DSPGenerator):
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
@@ -322,9 +401,9 @@ 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):
@@ -386,7 +465,7 @@ class _GenerateV6DSP(_DSPGenerator):
self.file.close()
V7DSPHeader = """\
-<?xml version="1.0" encoding = "Windows-1252"?>
+<?xml version="1.0" encoding = "%(encoding)s"?>
<VisualStudioProject
\tProjectType="Visual C++"
\tVersion="%(versionstr)s"
@@ -394,15 +473,11 @@ V7DSPHeader = """\
\tSccProjectName=""
\tSccLocalPath=""
\tKeyword="MakeFileProj">
-\t<Platforms>
-\t\t<Platform
-\t\t\tName="Win32"/>
-\t</Platforms>
"""
V7DSPConfiguration = """\
\t\t<Configuration
-\t\t\tName="%(capitalized_kind)s|Win32"
+\t\t\tName="%(variant)s|Win32"
\t\t\tOutputDirectory="%(outdir)s"
\t\t\tIntermediateDirectory="%(outdir)s"
\t\t\tConfigurationType="0"
@@ -410,9 +485,9 @@ V7DSPConfiguration = """\
\t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
\t\t\t<Tool
\t\t\t\tName="VCNMakeTool"
-\t\t\t\tBuildCommandLine="%(cmd)s"
+\t\t\t\tBuildCommandLine="%(buildcmd)s"
\t\t\t\tCleanCommandLine="%(cleancmd)s"
-\t\t\t\tRebuildCommandLine="%(cmd)s"
+\t\t\t\tRebuildCommandLine="%(rebuildcmd)s"
\t\t\t\tOutput="%(buildtarget)s"/>
\t\t</Configuration>
"""
@@ -428,7 +503,18 @@ class _GenerateV7DSP(_DSPGenerator):
self.versionstr = '7.10'
def PrintHeader(self):
- self.file.write(V7DSPHeader % self.__dict__)
+ versionstr = self.versionstr
+ name = self.name
+ encoding = self.env.subst('$MSVSENCODING')
+
+ self.file.write(V7DSPHeader % locals())
+
+ 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')
def PrintProject(self):
self.file.write('\t<Configurations>\n')
@@ -436,39 +522,52 @@ class _GenerateV7DSP(_DSPGenerator):
confkeys = self.configs.keys()
confkeys.sort()
for kind in confkeys:
- capitalized_kind = kind.capitalize()
+ variant = self.configs[kind].variant
+ platform = self.configs[kind].platform
outdir = self.configs[kind].outdir
buildtarget = self.configs[kind].buildtarget
- (d,c) = os.path.split(str(self.conspath))
- fmt = 'echo Starting SCons &amp;&amp; &quot;%s&quot; -c &quot;%s&quot; -C %s -f %s%s %s'
- cmd = fmt % (python_executable, exec_script_main_xml,
- d, c, '', buildtarget)
- cleancmd = fmt % (python_executable, exec_script_main_xml,
- d, c, ' -c', buildtarget)
+ def xmlify(cmd):
+ cmd = string.replace(cmd, "&", "&amp;") # do this first
+ cmd = string.replace(cmd, "'", "&apos;")
+ cmd = string.replace(cmd, '"', "&quot;")
+ return cmd
+
+ env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
+ if not env_has_buildtarget:
+ self.env['MSVSBUILDTARGET'] = buildtarget
+
+ starting = 'echo Starting SCons && '
+ buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1))
+ rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1))
+ cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1))
+
+ if not env_has_buildtarget:
+ del self.env['MSVSBUILDTARGET']
self.file.write(V7DSPConfiguration % locals())
self.file.write('\t</Configurations>\n')
if self.version >= 7.1:
- self.file.write('\t<References>\n'
+ 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 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',
@@ -477,30 +576,61 @@ class _GenerateV7DSP(_DSPGenerator):
self.file.write('\t<Files>\n')
cats = categories.keys()
- cats.sort()
+ cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
+ cats = filter(lambda k, s=self: s.sources[k], cats)
for kind in cats:
- if not self.sources[kind]:
- continue # skip empty groups
-
- self.file.write('\t\t<Filter\n'
- '\t\t\tName="%s"\n'
- '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
-
- for file in self.sources[kind]:
- 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)
-
- self.file.write('\t\t</Filter>\n')
-
- # add the Conscript file outside of the groups
+ if len(cats) > 1:
+ self.file.write('\t\t<Filter\n'
+ '\t\t\tName="%s"\n'
+ '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
+
+
+ def printSources(hierarchy):
+ 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))
+ printSources(value)
+ 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))
+
+ sources = self.sources[kind]
+
+ # First remove any common prefix
+ commonprefix = None
+ if len(sources) > 1:
+ commonprefix = os.path.commonprefix(sources)
+ prefixlen = len(commonprefix)
+ if prefixlen:
+ sources = map(lambda s, p=prefixlen: s[p:], sources)
+
+ hierarchy = makeHierarchy(sources)
+ printSources(hierarchy)
+
+ 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'
- '\t</Files>\n' % str(self.source))
+ '\t\t</File>\n' % str(self.sconscript))
- self.file.write('\t<Globals>\n'
+ self.file.write('\t</Files>\n'
+ '\t<Globals>\n'
'\t</Globals>\n')
def Parse(self):
@@ -562,11 +692,26 @@ 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 not env.has_key('projects'):
+ 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."
+ if len(projects) > 1:
+ raise SCons.Errors.UserError, \
+ "Currently you can specify at most one project to create an MSVSSolution."
+ self.dspfile = str(projects[0])
+
if self.env.has_key('name'):
self.name = self.env['name']
else:
@@ -577,8 +722,8 @@ class _DSWGenerator:
class _GenerateV7DSW(_DSWGenerator):
"""Generates a Solution file for MSVS .NET"""
- def __init__(self, dswfile, dspfile, source, env):
- _DSWGenerator.__init__(self, dswfile, dspfile, source, env)
+ def __init__(self, dswfile, source, env):
+ _DSWGenerator.__init__(self, dswfile, source, env)
self.version = float(self.env['MSVS_VERSION'])
self.versionstr = '7.00'
@@ -590,20 +735,44 @@ class _GenerateV7DSW(_DSWGenerator):
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 env.has_key('nokeep') 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(variant):
+ 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 not env.has_key('variant'):
+ 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(env['variant'])
+ elif SCons.Util.is_List(env['variant']):
+ for variant in env['variant']:
+ AddConfig(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:
@@ -650,7 +819,8 @@ class _GenerateV7DSW(_DSWGenerator):
confkeys.sort()
cnt = 0
for name in confkeys:
- self.file.write('\t\tConfigName.%d = %s\n' % (cnt, name.capitalize()))
+ variant = self.configs[name].variant
+ self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant))
cnt = cnt + 1
self.file.write('\tEndGlobalSection\n')
if self.version < 7.1:
@@ -658,18 +828,21 @@ class _GenerateV7DSW(_DSWGenerator):
'\tEndGlobalSection\n')
self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
for name in confkeys:
- name = name.capitalize()
- self.file.write('\t\t%s.%s.ActiveCfg = %s|Win32\n'
- '\t\t%s.%s.Build.0 = %s|Win32\n' %(self.slnguid,name,name,self.slnguid,name,name))
+ name = name
+ variant = self.configs[name].variant
+ platform = self.configs[name].platform
+ self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
+ '\t\t%s.%s.Build.0 = %s|%s\n' %(self.slnguid,variant,variant,platform,self.slnguid,variant,variant,platform))
self.file.write('\tEndGlobalSection\n'
'\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
'\tEndGlobalSection\n'
'\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
'\tEndGlobalSection\n'
'EndGlobal\n')
- pdata = pickle.dumps(self.configs,1)
- pdata = base64.encodestring(pdata)
- self.file.write(pdata + '\n')
+ if self.nokeep == 0:
+ pdata = pickle.dumps(self.configs,1)
+ pdata = base64.encodestring(pdata)
+ self.file.write(pdata + '\n')
def Build(self):
try:
@@ -716,7 +889,9 @@ class _GenerateV6DSW(_DSWGenerator):
def PrintWorkspace(self):
""" writes a DSW file """
- self.file.write(V6DSWHeader % self.__dict__)
+ name = self.name
+ dspfile = self.dspfile
+ self.file.write(V6DSWHeader % locals())
def Build(self):
try:
@@ -738,14 +913,14 @@ def GenerateDSP(dspfile, source, env):
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)
+ g = _GenerateV7DSW(dswfile, source, env)
g.Build()
else:
- g = _GenerateV6DSW(dswfile, dspfile, source, env)
+ g = _GenerateV6DSW(dswfile, source, env)
g.Build()
@@ -761,7 +936,7 @@ def get_default_visualstudio_version(env):
version = '6.0'
versions = [version]
- if not env.has_key('MSVS') or type(env['MSVS']) != types.DictType:
+ if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']):
env['MSVS'] = {}
if env.has_key('MSVS_VERSION'):
@@ -1022,19 +1197,10 @@ def GetMSVSSolutionSuffix(target, source, env, for_signature):
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))):
+ if not dspfile is builddspfile:
try:
bdsp = open(str(builddspfile), "w+")
except IOError, detail:
@@ -1043,20 +1209,33 @@ def GenerateProject(target, source, env):
bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
- try:
- bdsw = open(str(builddswfile), "w+")
- except IOError, detail:
- print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
- raise
+ GenerateDSP(dspfile, source, env)
- 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):
+ builddswfile = target[1]
+ dswfile = builddswfile.srcnode()
- GenerateDSP(dspfile, source, env)
- GenerateDSW(dswfile, dspfile, source, env)
+ if not dswfile is builddswfile:
+
+ try:
+ bdsw = open(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())
+
+ GenerateDSW(dswfile, source, env)
+
+def GenerateSolution(target, source, env):
+ GenerateDSW(target[0], source, env)
def projectEmitter(target, source, env):
- """Sets up the DSP and DSW dependencies for an SConscript file."""
+ """Sets up the DSP 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['MSVSPROJECTSUFFIX'] to target.
if source[0] == target[0]:
source = []
@@ -1065,36 +1244,157 @@ def projectEmitter(target, source, env):
suff = env.subst('$MSVSPROJECTSUFFIX')
target[0] = base + suff
- dspfile = env.File(target[0]).srcnode()
- dswfile = env.File(SCons.Util.splitext(str(dspfile))[0] + env.subst('$MSVSSOLUTIONSUFFIX'))
+ if not source:
+ source = 'prj_inputs:'
+ source = source + env.subst('$MSVSSCONSCOM', 1)
+ source = source + env.subst('$MSVSENCODING', 1)
+
+ if env.has_key('buildtarget') 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 env.has_key('outdir') 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"
+
+ if env.has_key('name'):
+ if SCons.Util.is_String(env['name']):
+ source = source + ' "%s"' % env['name']
+ else:
+ raise SCons.Errors.InternalError, "name must be a string"
+
+ if env.has_key('variant'):
+ 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 env.has_key(s):
+ 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"
+
+ source = source + ' "%s"' % str(target[0])
+ source = [SCons.Node.Python.Value(source)]
+
+ targetlist = [target[0]]
+ sourcelist = source
+
+ if env.get('auto_build_solution', 1):
+ env['projects'] = targetlist
+ t, s = solutionEmitter(target, target, env)
+ targetlist = targetlist + t
+
+ return (targetlist, sourcelist)
+
+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.subst('$MSVSSOLUTIONSUFFIX')
+ target[0] = base + suff
- # XXX Need to find a way to abstract this; the build engine
- # shouldn't depend on anything in SCons.Script.
- stack = SCons.Script.call_stack
if not source:
- source = [stack[-1].sconscript.srcnode()]
- source[0].attributes.sconstruct = stack[0].sconscript
+ source = 'sln_inputs:'
- bdswpath = SCons.Util.splitext(str(target[0]))[0] + env.subst('$MSVSSOLUTIONSUFFIX')
- bdswfile = env.File(bdswpath)
+ if env.has_key('name'):
+ if SCons.Util.is_String(env['name']):
+ source = source + ' "%s"' % env['name']
+ else:
+ raise SCons.Errors.InternalError, "name must be a string"
- # 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])
+ if env.has_key('variant'):
+ 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"
- return ([target[0],bdswfile], source)
+ if env.has_key('slnguid'):
+ if SCons.Util.is_String(env['slnguid']):
+ source = source + ' "%s"' % env['slnguid']
+ else:
+ raise SCons.Errors.InternalError, "slnguid must be a string"
+
+ if env.has_key('projects'):
+ 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
+
+ source = source + ' "%s"' % str(target[0])
+ source = [SCons.Node.Python.Value(source)]
+
+ return ([target[0]], source)
-projectGeneratorAction = SCons.Action.Action(GenerateProject, None)
+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)
+
def generate(env):
"""Add Builders and construction variables for Microsoft Visual
Studio project files to an Environment."""
@@ -1103,7 +1403,28 @@ def generate(env):
except KeyError:
env['BUILDERS']['MSVSProject'] = projectBuilder
- env['MSVSPROJECTCOM'] = projectGeneratorAction
+ try:
+ env['BUILDERS']['MSVSSolution']
+ except KeyError:
+ env['BUILDERS']['MSVSSolution'] = solutionBuilder
+
+ 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:
+ env['MSVSSCONSCRIPT'] = env.File('SConstruct')
+
+ env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, exec_script_main)
+ 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'
try:
version = get_default_visualstudio_version(env)
@@ -1143,4 +1464,3 @@ def exists(env):
else:
# there's at least one version of MSVS installed.
return 1
-