3 Tool-specific initialization for Microsoft Visual Studio project files.
5 There normally shouldn't be any need to import this module directly.
6 It will usually be imported through the generic SCons.Tool.Tool()
14 # Permission is hereby granted, free of charge, to any person obtaining
15 # a copy of this software and associated documentation files (the
16 # "Software"), to deal in the Software without restriction, including
17 # without limitation the rights to use, copy, modify, merge, publish,
18 # distribute, sublicense, and/or sell copies of the Software, and to
19 # permit persons to whom the Software is furnished to do so, subject to
20 # the following conditions:
22 # The above copyright notice and this permission notice shall be included
23 # in all copies or substantial portions of the Software.
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
26 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
27 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
46 import SCons.Platform.win32
47 import SCons.Script.SConscript
51 ##############################################################################
52 # Below here are the classes and functions for generation of
53 # DSP/DSW/SLN/VCPROJ files.
54 ##############################################################################
57 """Return a string as a string of hex characters.
59 # NOTE: This routine is a method in the Python 2.0 interface
60 # of the native md5 module, but we want SCons to operate all
61 # the way back to at least Python 1.5.2, which doesn't have it.
66 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
70 s = string.replace(s, "&", "&") # do this first
71 s = string.replace(s, "'", "'")
72 s = string.replace(s, '"', """)
75 external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}'
77 def _generateGUID(slnfile, name):
78 """This generates a dummy GUID for the sln file to use. It is
79 based on the MD5 signatures of the sln filename plus the name of
80 the project. It basically just needs to be unique, and not
81 change with each invocation."""
82 solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper()
83 # convert most of the signature to GUID form (discard the rest)
84 solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}"
87 version_re = re.compile(r'(\d+\.\d+)(.*)')
89 def msvs_parse_version(s):
91 Split a Visual Studio version, which may in fact be something like
92 '7.0Exp', into is version number (returned as a float) and trailing
95 num, suite = version_re.match(s).groups()
96 return float(num), suite
98 # This is how we re-invoke SCons from inside MSVS Project files.
99 # The problem is that we might have been invoked as either scons.bat
100 # or scons.py. If we were invoked directly as scons.py, then we could
101 # use sys.argv[0] to find the SCons "executable," but that doesn't work
102 # if we were invoked as scons.bat, which uses "python -c" to execute
103 # things and ends up with "-c" as sys.argv[0]. Consequently, we have
104 # the MSVS Project file invoke SCons the same way that scons.bat does,
105 # which works regardless of how we were invoked.
106 def getExecScriptMain(env, xml=None):
107 scons_home = env.get('SCONS_HOME')
108 if not scons_home and os.environ.has_key('SCONS_LIB_DIR'):
109 scons_home = os.environ['SCONS_LIB_DIR']
111 exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home
113 version = SCons.__version__
114 exec_script_main = "from os.path import join; import sys; sys.path = [ join(sys.prefix, 'Lib', 'site-packages', 'scons-%(version)s'), join(sys.prefix, 'scons-%(version)s'), join(sys.prefix, 'Lib', 'site-packages', 'scons'), join(sys.prefix, 'scons') ] + sys.path; import SCons.Script; SCons.Script.main()" % locals()
116 exec_script_main = xmlify(exec_script_main)
117 return exec_script_main
119 # The string for the Python executable we tell the Project file to use
120 # is either sys.executable or, if an external PYTHON_ROOT environment
121 # variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to
122 # pluck the actual executable name from sys.executable).
124 python_root = os.environ['PYTHON_ROOT']
126 python_executable = sys.executable
128 python_executable = os.path.join('$$(PYTHON_ROOT)',
129 os.path.split(sys.executable)[1])
134 def splitFully(path):
135 dir, base = os.path.split(path)
136 if dir and dir != '' and dir != path:
137 return splitFully(dir)+[base]
142 def makeHierarchy(sources):
143 '''Break a list of files into a hierarchy; for each value, if it is a string,
144 then it is a file. If it is a dictionary, it is a folder. The string is
145 the original path of the file.'''
149 path = splitFully(file)
152 for part in path[:-1]:
153 if not dict.has_key(part):
156 dict[path[-1]] = file
158 # print 'Warning: failed to decompose path for '+str(file)
162 """ Base class for DSP generators """
171 def __init__(self, dspfile, source, env):
172 self.dspfile = str(dspfile)
174 get_abspath = dspfile.get_abspath
175 except AttributeError:
176 self.dspabs = os.path.abspath(dspfile)
178 self.dspabs = get_abspath()
180 if not env.has_key('variant'):
181 raise SCons.Errors.InternalError, \
182 "You must specify a 'variant' argument (i.e. 'Debug' or " +\
183 "'Release') to create an MSVSProject."
184 elif SCons.Util.is_String(env['variant']):
185 variants = [env['variant']]
186 elif SCons.Util.is_List(env['variant']):
187 variants = env['variant']
189 if not env.has_key('buildtarget') or env['buildtarget'] == None:
191 elif SCons.Util.is_String(env['buildtarget']):
192 buildtarget = [env['buildtarget']]
193 elif SCons.Util.is_List(env['buildtarget']):
194 if len(env['buildtarget']) != len(variants):
195 raise SCons.Errors.InternalError, \
196 "Sizes of 'buildtarget' and 'variant' lists must be the same."
198 for bt in env['buildtarget']:
199 if SCons.Util.is_String(bt):
200 buildtarget.append(bt)
202 buildtarget.append(bt.get_abspath())
204 buildtarget = [env['buildtarget'].get_abspath()]
205 if len(buildtarget) == 1:
209 buildtarget.append(bt)
211 if not env.has_key('outdir') or env['outdir'] == None:
213 elif SCons.Util.is_String(env['outdir']):
214 outdir = [env['outdir']]
215 elif SCons.Util.is_List(env['outdir']):
216 if len(env['outdir']) != len(variants):
217 raise SCons.Errors.InternalError, \
218 "Sizes of 'outdir' and 'variant' lists must be the same."
220 for s in env['outdir']:
221 if SCons.Util.is_String(s):
224 outdir.append(s.get_abspath())
226 outdir = [env['outdir'].get_abspath()]
233 if not env.has_key('runfile') or env['runfile'] == None:
234 runfile = buildtarget[-1:]
235 elif SCons.Util.is_String(env['runfile']):
236 runfile = [env['runfile']]
237 elif SCons.Util.is_List(env['runfile']):
238 if len(env['runfile']) != len(variants):
239 raise SCons.Errors.InternalError, \
240 "Sizes of 'runfile' and 'variant' lists must be the same."
242 for s in env['runfile']:
243 if SCons.Util.is_String(s):
246 runfile.append(s.get_abspath())
248 runfile = [env['runfile'].get_abspath()]
249 if len(runfile) == 1:
255 self.sconscript = env['MSVSSCONSCRIPT']
257 cmdargs = env.get('cmdargs', '')
261 if self.env.has_key('name'):
262 self.name = self.env['name']
264 self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
265 self.name = self.env.subst(self.name)
275 for n in sourcenames:
281 if env.has_key('nokeep') and env['variant'] != 0:
284 if self.nokeep == 0 and os.path.exists(self.dspabs):
287 for t in zip(sourcenames,self.srcargs):
288 if self.env.has_key(t[1]):
289 if SCons.Util.is_List(self.env[t[1]]):
290 for i in self.env[t[1]]:
291 if not i in self.sources[t[0]]:
292 self.sources[t[0]].append(i)
294 if not self.env[t[1]] in self.sources[t[0]]:
295 self.sources[t[0]].append(self.env[t[1]])
297 for n in sourcenames:
298 self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower()))
300 def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile):
302 config.buildtarget = buildtarget
303 config.outdir = outdir
304 config.cmdargs = cmdargs
305 config.runfile = runfile
307 match = re.match('(.*)\|(.*)', variant)
309 config.variant = match.group(1)
310 config.platform = match.group(2)
312 config.variant = variant
313 config.platform = 'Win32'
315 self.configs[variant] = config
316 print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'"
318 for i in range(len(variants)):
319 AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs)
322 for key in self.configs.keys():
323 platform = self.configs[key].platform
324 if not platform in self.platforms:
325 self.platforms.append(platform)
331 # Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4>
332 # Microsoft Developer Studio Generated Build File, Format Version 6.00
335 # TARGTYPE "Win32 (x86) External Target" 0x0106
337 CFG=%(name)s - Win32 %(confkey)s
338 !MESSAGE This is not a valid makefile. To build this project using NMAKE,
339 !MESSAGE use the Export Makefile command and run
341 !MESSAGE NMAKE /f "%(name)s.mak".
343 !MESSAGE You can specify a configuration when running NMAKE
344 !MESSAGE by defining the macro CFG on the command line. For example:
346 !MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s"
348 !MESSAGE Possible choices for configuration are:
352 class _GenerateV6DSP(_DSPGenerator):
353 """Generates a Project file for MSVS 6.0"""
355 def PrintHeader(self):
356 # pick a default config
357 confkeys = self.configs.keys()
361 confkey = confkeys[0]
363 self.file.write(V6DSPHeader % locals())
365 for kind in confkeys:
366 self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind))
368 self.file.write('!MESSAGE \n\n')
370 def PrintProject(self):
372 self.file.write('# Begin Project\n'
373 '# PROP AllowPerConfigDependencies 0\n'
374 '# PROP Scc_ProjName ""\n'
375 '# PROP Scc_LocalPath ""\n\n')
378 confkeys = self.configs.keys()
380 for kind in confkeys:
381 outdir = self.configs[kind].outdir
382 buildtarget = self.configs[kind].buildtarget
384 self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
387 self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
389 env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
390 if not env_has_buildtarget:
391 self.env['MSVSBUILDTARGET'] = buildtarget
393 # have to write this twice, once with the BASE settings, and once without
394 for base in ("BASE ",""):
395 self.file.write('# PROP %sUse_MFC 0\n'
396 '# PROP %sUse_Debug_Libraries ' % (base, base))
397 if kind.lower().find('debug') < 0:
398 self.file.write('0\n')
400 self.file.write('1\n')
401 self.file.write('# PROP %sOutput_Dir "%s"\n'
402 '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir))
403 cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1)
404 self.file.write('# PROP %sCmd_Line "%s"\n'
405 '# PROP %sRebuild_Opt "-c && %s"\n'
406 '# PROP %sTarget_File "%s"\n'
407 '# PROP %sBsc_Name ""\n'
408 '# PROP %sTarget_Dir ""\n'\
409 %(base,cmd,base,cmd,base,buildtarget,base,base))
411 if not env_has_buildtarget:
412 del self.env['MSVSBUILDTARGET']
414 self.file.write('\n!ENDIF\n\n'
415 '# Begin Target\n\n')
416 for kind in confkeys:
417 self.file.write('# Name "%s - Win32 %s"\n' % (name,kind))
418 self.file.write('\n')
420 for kind in confkeys:
422 self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
425 self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
426 self.file.write('!ENDIF \n\n')
427 self.PrintSourceFiles()
428 self.file.write('# End Target\n'
432 # now we pickle some data and add it to the file -- MSDEV will ignore it.
433 pdata = pickle.dumps(self.configs,1)
434 pdata = base64.encodestring(pdata)
435 self.file.write(pdata + '\n')
436 pdata = pickle.dumps(self.sources,1)
437 pdata = base64.encodestring(pdata)
438 self.file.write(pdata + '\n')
440 def PrintSourceFiles(self):
441 categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat',
442 'Header Files': 'h|hpp|hxx|hm|inl',
443 'Local Headers': 'h|hpp|hxx|hm|inl',
444 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe',
447 cats = categories.keys()
448 cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
450 if not self.sources[kind]:
451 continue # skip empty groups
453 self.file.write('# Begin Group "' + kind + '"\n\n')
454 typelist = categories[kind].replace('|',';')
455 self.file.write('# PROP Default_Filter "' + typelist + '"\n')
457 for file in self.sources[kind]:
458 file = os.path.normpath(file)
459 self.file.write('# Begin Source File\n\n'
460 'SOURCE="' + file + '"\n'
461 '# End Source File\n')
462 self.file.write('# End Group\n')
464 # add the SConscript file outside of the groups
465 self.file.write('# Begin Source File\n\n'
466 'SOURCE="' + str(self.sconscript) + '"\n'
467 '# End Source File\n')
471 dspfile = open(self.dspabs,'r')
473 return # doesn't exist yet, so can't add anything to configs.
475 line = dspfile.readline()
477 if line.find("# End Project") > -1:
479 line = dspfile.readline()
481 line = dspfile.readline()
483 while line and line != '\n':
484 line = dspfile.readline()
487 # OK, we've found our little pickled cache of data.
489 datas = base64.decodestring(datas)
490 data = pickle.loads(datas)
491 except KeyboardInterrupt:
494 return # unable to unpickle any data for some reason
496 self.configs.update(data)
499 line = dspfile.readline()
501 while line and line != '\n':
502 line = dspfile.readline()
505 # OK, we've found our little pickled cache of data.
506 # it has a "# " in front of it, so we strip that.
508 datas = base64.decodestring(datas)
509 data = pickle.loads(datas)
510 except KeyboardInterrupt:
513 return # unable to unpickle any data for some reason
515 self.sources.update(data)
519 self.file = open(self.dspabs,'w')
520 except IOError, detail:
521 raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail)
528 <?xml version="1.0" encoding = "%(encoding)s"?>
530 \tProjectType="Visual C++"
531 \tVersion="%(versionstr)s"
534 \tKeyword="MakeFileProj">
537 V7DSPConfiguration = """\
539 \t\t\tName="%(variant)s|%(platform)s"
540 \t\t\tOutputDirectory="%(outdir)s"
541 \t\t\tIntermediateDirectory="%(outdir)s"
542 \t\t\tConfigurationType="0"
544 \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
546 \t\t\t\tName="VCNMakeTool"
547 \t\t\t\tBuildCommandLine="%(buildcmd)s"
548 \t\t\t\tCleanCommandLine="%(cleancmd)s"
549 \t\t\t\tRebuildCommandLine="%(rebuildcmd)s"
550 \t\t\t\tOutput="%(runfile)s"/>
555 <?xml version="1.0" encoding="%(encoding)s"?>
557 \tProjectType="Visual C++"
558 \tVersion="%(versionstr)s"
561 \tRootNamespace="%(name)s"
562 \tKeyword="MakeFileProj">
565 V8DSPConfiguration = """\
567 \t\t\tName="%(variant)s|Win32"
568 \t\t\tConfigurationType="0"
570 \t\t\tATLMinimizesCRunTimeLibraryUsage="false"
573 \t\t\t\tName="VCNMakeTool"
574 \t\t\t\tBuildCommandLine="%(buildcmd)s"
575 \t\t\t\tReBuildCommandLine="%(rebuildcmd)s"
576 \t\t\t\tCleanCommandLine="%(cleancmd)s"
577 \t\t\t\tOutput="%(runfile)s"
578 \t\t\t\tPreprocessorDefinitions=""
579 \t\t\t\tIncludeSearchPath=""
580 \t\t\t\tForcedIncludes=""
581 \t\t\t\tAssemblySearchPath=""
582 \t\t\t\tForcedUsingAssemblies=""
583 \t\t\t\tCompileAsManaged=""
587 class _GenerateV7DSP(_DSPGenerator):
588 """Generates a Project file for MSVS .NET"""
590 def __init__(self, dspfile, source, env):
591 _DSPGenerator.__init__(self, dspfile, source, env)
592 self.version = env['MSVS_VERSION']
593 self.version_num, self.suite = msvs_parse_version(self.version)
594 if self.version_num >= 8.0:
595 self.versionstr = '8.00'
596 self.dspheader = V8DSPHeader
597 self.dspconfiguration = V8DSPConfiguration
599 if self.version_num >= 7.1:
600 self.versionstr = '7.10'
602 self.versionstr = '7.00'
603 self.dspheader = V7DSPHeader
604 self.dspconfiguration = V7DSPConfiguration
607 def PrintHeader(self):
609 versionstr = self.versionstr
611 encoding = self.env.subst('$MSVSENCODING')
612 scc_provider = env.get('MSVS_SCC_PROVIDER', '')
613 scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
614 scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
615 scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
616 project_guid = env.get('MSVS_PROJECT_GUID', '')
617 if self.version_num >= 8.0 and not project_guid:
618 project_guid = _generateGUID(self.dspfile, '')
619 if scc_provider != '':
620 scc_attrs = ('\tProjectGUID="%s"\n'
621 '\tSccProjectName="%s"\n'
622 '\tSccAuxPath="%s"\n'
623 '\tSccLocalPath="%s"\n'
624 '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider))
626 scc_attrs = ('\tProjectGUID="%s"\n'
627 '\tSccProjectName="%s"\n'
628 '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path))
630 self.file.write(self.dspheader % locals())
632 self.file.write('\t<Platforms>\n')
633 for platform in self.platforms:
636 '\t\t\tName="%s"/>\n' % platform)
637 self.file.write('\t</Platforms>\n')
639 if self.version_num >= 8.0:
640 self.file.write('\t<ToolFiles>\n'
643 def PrintProject(self):
644 self.file.write('\t<Configurations>\n')
646 confkeys = self.configs.keys()
648 for kind in confkeys:
649 variant = self.configs[kind].variant
650 platform = self.configs[kind].platform
651 outdir = self.configs[kind].outdir
652 buildtarget = self.configs[kind].buildtarget
653 runfile = self.configs[kind].runfile
654 cmdargs = self.configs[kind].cmdargs
656 env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
657 if not env_has_buildtarget:
658 self.env['MSVSBUILDTARGET'] = buildtarget
660 starting = 'echo Starting SCons && '
662 cmdargs = ' ' + cmdargs
665 buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs)
666 rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs)
667 cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs)
669 if not env_has_buildtarget:
670 del self.env['MSVSBUILDTARGET']
672 self.file.write(self.dspconfiguration % locals())
674 self.file.write('\t</Configurations>\n')
676 if self.version_num >= 7.1:
677 self.file.write('\t<References>\n'
680 self.PrintSourceFiles()
682 self.file.write('</VisualStudioProject>\n')
685 # now we pickle some data and add it to the file -- MSDEV will ignore it.
686 pdata = pickle.dumps(self.configs,1)
687 pdata = base64.encodestring(pdata)
688 self.file.write('<!-- SCons Data:\n' + pdata + '\n')
689 pdata = pickle.dumps(self.sources,1)
690 pdata = base64.encodestring(pdata)
691 self.file.write(pdata + '-->\n')
693 def printSources(self, hierarchy, commonprefix):
694 sorteditems = hierarchy.items()
695 sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower()))
697 # First folders, then files
698 for key, value in sorteditems:
699 if SCons.Util.is_Dict(value):
700 self.file.write('\t\t\t<Filter\n'
701 '\t\t\t\tName="%s"\n'
702 '\t\t\t\tFilter="">\n' % (key))
703 self.printSources(value, commonprefix)
704 self.file.write('\t\t\t</Filter>\n')
706 for key, value in sorteditems:
707 if SCons.Util.is_String(value):
710 file = os.path.join(commonprefix, value)
711 file = os.path.normpath(file)
712 self.file.write('\t\t\t<File\n'
713 '\t\t\t\tRelativePath="%s">\n'
714 '\t\t\t</File>\n' % (file))
716 def PrintSourceFiles(self):
717 categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
718 'Header Files': 'h;hpp;hxx;hm;inl',
719 'Local Headers': 'h;hpp;hxx;hm;inl',
720 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe',
723 self.file.write('\t<Files>\n')
725 cats = categories.keys()
726 cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
727 cats = filter(lambda k, s=self: s.sources[k], cats)
730 self.file.write('\t\t<Filter\n'
732 '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
734 sources = self.sources[kind]
736 # First remove any common prefix
739 s = map(os.path.normpath, sources)
740 # take the dirname because the prefix may include parts
741 # of the filenames (e.g. if you have 'dir\abcd' and
742 # 'dir\acde' then the cp will be 'dir\a' )
743 cp = os.path.dirname( os.path.commonprefix(s) )
744 if cp and s[0][len(cp)] == os.sep:
745 # +1 because the filename starts after the separator
746 sources = map(lambda s, l=len(cp)+1: s[l:], sources)
748 elif len(sources) == 1:
749 commonprefix = os.path.dirname( sources[0] )
750 sources[0] = os.path.basename( sources[0] )
752 hierarchy = makeHierarchy(sources)
753 self.printSources(hierarchy, commonprefix=commonprefix)
756 self.file.write('\t\t</Filter>\n')
758 # add the SConscript file outside of the groups
759 self.file.write('\t\t<File\n'
760 '\t\t\tRelativePath="%s">\n'
761 '\t\t</File>\n' % str(self.sconscript))
763 self.file.write('\t</Files>\n'
769 dspfile = open(self.dspabs,'r')
771 return # doesn't exist yet, so can't add anything to configs.
773 line = dspfile.readline()
775 if line.find('<!-- SCons Data:') > -1:
777 line = dspfile.readline()
779 line = dspfile.readline()
781 while line and line != '\n':
782 line = dspfile.readline()
785 # OK, we've found our little pickled cache of data.
787 datas = base64.decodestring(datas)
788 data = pickle.loads(datas)
789 except KeyboardInterrupt:
792 return # unable to unpickle any data for some reason
794 self.configs.update(data)
797 line = dspfile.readline()
799 while line and line != '\n':
800 line = dspfile.readline()
803 # OK, we've found our little pickled cache of data.
805 datas = base64.decodestring(datas)
806 data = pickle.loads(datas)
807 except KeyboardInterrupt:
810 return # unable to unpickle any data for some reason
812 self.sources.update(data)
816 self.file = open(self.dspabs,'w')
817 except IOError, detail:
818 raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail)
825 """ Base class for DSW generators """
826 def __init__(self, dswfile, source, env):
827 self.dswfile = os.path.normpath(str(dswfile))
830 if not env.has_key('projects'):
831 raise SCons.Errors.UserError, \
832 "You must specify a 'projects' argument to create an MSVSSolution."
833 projects = env['projects']
834 if not SCons.Util.is_List(projects):
835 raise SCons.Errors.InternalError, \
836 "The 'projects' argument must be a list of nodes."
837 projects = SCons.Util.flatten(projects)
838 if len(projects) < 1:
839 raise SCons.Errors.UserError, \
840 "You must specify at least one project to create an MSVSSolution."
841 self.dspfiles = map(str, projects)
843 if self.env.has_key('name'):
844 self.name = self.env['name']
846 self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0])
847 self.name = self.env.subst(self.name)
852 class _GenerateV7DSW(_DSWGenerator):
853 """Generates a Solution file for MSVS .NET"""
854 def __init__(self, dswfile, source, env):
855 _DSWGenerator.__init__(self, dswfile, source, env)
858 self.version = self.env['MSVS_VERSION']
859 self.version_num, self.suite = msvs_parse_version(self.version)
860 self.versionstr = '7.00'
861 if self.version_num >= 8.0:
862 self.versionstr = '9.00'
863 elif self.version_num >= 7.1:
864 self.versionstr = '8.00'
865 if self.version_num >= 8.0:
866 self.versionstr = '9.00'
868 if env.has_key('slnguid') and env['slnguid']:
869 self.slnguid = env['slnguid']
871 self.slnguid = _generateGUID(dswfile, self.name)
876 if env.has_key('nokeep') and env['variant'] != 0:
879 if self.nokeep == 0 and os.path.exists(self.dswfile):
882 def AddConfig(self, variant, dswfile=dswfile):
885 match = re.match('(.*)\|(.*)', variant)
887 config.variant = match.group(1)
888 config.platform = match.group(2)
890 config.variant = variant
891 config.platform = 'Win32'
893 self.configs[variant] = config
894 print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'"
896 if not env.has_key('variant'):
897 raise SCons.Errors.InternalError, \
898 "You must specify a 'variant' argument (i.e. 'Debug' or " +\
899 "'Release') to create an MSVS Solution File."
900 elif SCons.Util.is_String(env['variant']):
901 AddConfig(self, env['variant'])
902 elif SCons.Util.is_List(env['variant']):
903 for variant in env['variant']:
904 AddConfig(self, variant)
907 for key in self.configs.keys():
908 platform = self.configs[key].platform
909 if not platform in self.platforms:
910 self.platforms.append(platform)
914 dswfile = open(self.dswfile,'r')
916 return # doesn't exist yet, so can't add anything to configs.
918 line = dswfile.readline()
920 if line[:9] == "EndGlobal":
922 line = dswfile.readline()
924 line = dswfile.readline()
927 line = dswfile.readline()
930 # OK, we've found our little pickled cache of data.
932 datas = base64.decodestring(datas)
933 data = pickle.loads(datas)
934 except KeyboardInterrupt:
937 return # unable to unpickle any data for some reason
939 self.configs.update(data)
941 def PrintSolution(self):
942 """Writes a solution file"""
943 self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr )
944 if self.version_num >= 8.0:
945 self.file.write('# Visual Studio 2005\n')
946 for p in self.dspfiles:
947 name = os.path.basename(p)
948 base, suffix = SCons.Util.splitext(name)
949 if suffix == '.vcproj':
951 guid = _generateGUID(p, '')
952 self.file.write('Project("%s") = "%s", "%s", "%s"\n'
953 % ( external_makefile_guid, name, p, guid ) )
954 if self.version_num >= 7.1 and self.version_num < 8.0:
955 self.file.write('\tProjectSection(ProjectDependencies) = postProject\n'
956 '\tEndProjectSection\n')
957 self.file.write('EndProject\n')
959 self.file.write('Global\n')
962 if env.has_key('MSVS_SCC_PROVIDER'):
963 dspfile_base = os.path.basename(self.dspfile)
964 slnguid = self.slnguid
965 scc_provider = env.get('MSVS_SCC_PROVIDER', '')
966 scc_provider = string.replace(scc_provider, ' ', r'\u0020')
967 scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
968 # scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
969 scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
970 scc_project_base_path = env.get('MSVS_SCC_PROJECT_BASE_PATH', '')
971 # project_guid = env.get('MSVS_PROJECT_GUID', '')
973 self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n'
974 '\t\tSccNumberOfProjects = 2\n'
975 '\t\tSccProjectUniqueName0 = %(dspfile_base)s\n'
976 '\t\tSccLocalPath0 = %(scc_local_path)s\n'
977 '\t\tCanCheckoutShared = true\n'
978 '\t\tSccProjectFilePathRelativizedFromConnection0 = %(scc_project_base_path)s\n'
979 '\t\tSccProjectName1 = %(scc_project_name)s\n'
980 '\t\tSccLocalPath1 = %(scc_local_path)s\n'
981 '\t\tSccProvider1 = %(scc_provider)s\n'
982 '\t\tCanCheckoutShared = true\n'
983 '\t\tSccProjectFilePathRelativizedFromConnection1 = %(scc_project_base_path)s\n'
984 '\t\tSolutionUniqueID = %(slnguid)s\n'
985 '\tEndGlobalSection\n' % locals())
987 if self.version_num >= 8.0:
988 self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
990 self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n')
992 confkeys = self.configs.keys()
995 for name in confkeys:
996 variant = self.configs[name].variant
997 platform = self.configs[name].platform
998 if self.version_num >= 8.0:
999 self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform))
1001 self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant))
1003 self.file.write('\tEndGlobalSection\n')
1004 if self.version_num < 7.1:
1005 self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n'
1006 '\tEndGlobalSection\n')
1007 if self.version_num >= 8.0:
1008 self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
1010 self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
1012 for name in confkeys:
1013 variant = self.configs[name].variant
1014 platform = self.configs[name].platform
1015 if self.version_num >= 8.0:
1016 for p in self.dspfiles:
1017 guid = _generateGUID(p, '')
1018 self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n'
1019 '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform))
1021 self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
1022 '\t\t%s.%s.Build.0 = %s|%s\n' %(self.slnguid,variant,variant,platform,self.slnguid,variant,variant,platform))
1024 self.file.write('\tEndGlobalSection\n')
1026 if self.version_num >= 8.0:
1027 self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n'
1028 '\t\tHideSolutionNode = FALSE\n'
1029 '\tEndGlobalSection\n')
1031 self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
1032 '\tEndGlobalSection\n'
1033 '\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
1034 '\tEndGlobalSection\n')
1035 self.file.write('EndGlobal\n')
1036 if self.nokeep == 0:
1037 pdata = pickle.dumps(self.configs,1)
1038 pdata = base64.encodestring(pdata)
1039 self.file.write(pdata + '\n')
1043 self.file = open(self.dswfile,'w')
1044 except IOError, detail:
1045 raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
1047 self.PrintSolution()
1051 Microsoft Developer Studio Workspace File, Format Version 6.00
1052 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
1054 ###############################################################################
1056 Project: "%(name)s"="%(dspfile)s" - Package Owner=<4>
1066 ###############################################################################
1078 ###############################################################################
1081 class _GenerateV6DSW(_DSWGenerator):
1082 """Generates a Workspace file for MSVS 6.0"""
1084 def PrintWorkspace(self):
1085 """ writes a DSW file """
1087 dspfile = self.dspfiles[0]
1088 self.file.write(V6DSWHeader % locals())
1092 self.file = open(self.dswfile,'w')
1093 except IOError, detail:
1094 raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
1096 self.PrintWorkspace()
1100 def GenerateDSP(dspfile, source, env):
1101 """Generates a Project file based on the version of MSVS that is being used"""
1104 if env.has_key('MSVS_VERSION'):
1105 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1106 if version_num >= 7.0:
1107 g = _GenerateV7DSP(dspfile, source, env)
1110 g = _GenerateV6DSP(dspfile, source, env)
1113 def GenerateDSW(dswfile, source, env):
1114 """Generates a Solution/Workspace file based on the version of MSVS that is being used"""
1117 if env.has_key('MSVS_VERSION'):
1118 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1119 if version_num >= 7.0:
1120 g = _GenerateV7DSW(dswfile, source, env)
1123 g = _GenerateV6DSW(dswfile, source, env)
1127 ##############################################################################
1128 # Above here are the classes and functions for generation of
1129 # DSP/DSW/SLN/VCPROJ files.
1130 ##############################################################################
1132 def get_default_visualstudio_version(env):
1133 """Returns the version set in the env, or the latest version
1134 installed, if it can find it, or '6.0' if all else fails. Also
1135 updates the environment with what it found."""
1139 if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']):
1140 v = get_visualstudio_versions()
1143 env['MSVS'] = {'VERSIONS' : versions}
1145 versions = env['MSVS'].get('VERSIONS', versions)
1147 if not env.has_key('MSVS_VERSION'):
1148 env['MSVS_VERSION'] = versions[0] #use highest version by default
1150 env['MSVS']['VERSION'] = env['MSVS_VERSION']
1152 return env['MSVS_VERSION']
1154 def get_visualstudio_versions():
1156 Get list of visualstudio versions from the Windows registry.
1157 Returns a list of strings containing version numbers. An empty list
1158 is returned if we were unable to accees the register (for example,
1159 we couldn't import the registry-access module) or the appropriate
1160 registry keys weren't found.
1163 if not SCons.Util.can_read_reg:
1166 HLM = SCons.Util.HKEY_LOCAL_MACHINE
1168 r'Software\Microsoft\VisualStudio' : '',
1169 r'Software\Microsoft\VCExpress' : 'Exp',
1172 for K, suite_suffix in KEYS.items():
1174 k = SCons.Util.RegOpenKeyEx(HLM, K)
1178 p = SCons.Util.RegEnumKey(k,i)
1179 except SCons.Util.RegError:
1182 if not p[0] in '123456789' or p in L:
1184 # Only add this version number if there is a valid
1185 # registry structure (includes the "Setup" key),
1186 # and at least some of the correct directories
1187 # exist. Sometimes VS uninstall leaves around
1188 # some registry/filesystem turds that we don't
1189 # want to trip over. Also, some valid registry
1190 # entries are MSDN entries, not MSVS ('7.1',
1191 # notably), and we want to skip those too.
1193 SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p + '\\Setup')
1194 except SCons.Util.RegError:
1198 idk = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p)
1199 # This is not always here -- it only exists if the
1200 # user installed into a non-standard location (at
1201 # least in VS6 it works that way -- VS7 seems to
1204 id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
1205 except SCons.Util.RegError:
1208 # If the InstallDir key doesn't exist,
1209 # then we check the default locations.
1210 # Note: The IDE's executable is not devenv.exe for VS8 Express.
1211 if not id or not id[0]:
1212 files_dir = SCons.Platform.win32.get_program_files_dir()
1213 version_num, suite = msvs_parse_version(p)
1214 if version_num < 7.0:
1215 vs = r'Microsoft Visual Studio\Common\MSDev98'
1216 elif version_num < 8.0:
1217 vs = r'Microsoft Visual Studio .NET\Common7\IDE'
1219 vs = r'Microsoft Visual Studio 8\Common7\IDE'
1220 id = [ os.path.join(files_dir, vs) ]
1221 if os.path.exists(id[0]):
1222 L.append(p + suite_suffix)
1223 except SCons.Util.RegError:
1229 # This is a hack to get around the fact that certain Visual Studio
1230 # patches place a "6.1" version in the registry, which does not have
1231 # any of the keys we need to find include paths, install directories,
1232 # etc. Therefore we ignore it if it is there, since it throws all
1244 def get_default_visualstudio8_suite(env):
1246 Returns the Visual Studio 2005 suite identifier set in the env, or the
1247 highest suite installed.
1249 if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']):
1252 if env.has_key('MSVS_SUITE'):
1253 suite = env['MSVS_SUITE'].upper()
1258 if SCons.Util.can_read_reg:
1259 suites = get_visualstudio8_suites()
1261 suite = suites[0] #use best suite by default
1263 env['MSVS_SUITE'] = suite
1264 env['MSVS']['SUITES'] = suites
1265 env['MSVS']['SUITE'] = suite
1269 def get_visualstudio8_suites():
1271 Returns a sorted list of all installed Visual Studio 2005 suites found
1272 in the registry. The highest version should be the first entry in the list.
1277 # Detect Standard, Professional and Team edition
1279 idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1280 r'Software\Microsoft\VisualStudio\8.0')
1281 SCons.Util.RegQueryValueEx(idk, 'InstallDir')
1282 editions = { 'PRO': r'Setup\VS\Pro' } # ToDo: add standard and team editions
1283 edition_name = 'STD'
1284 for name, key_suffix in editions.items():
1286 idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1287 r'Software\Microsoft\VisualStudio\8.0' + '\\' + key_suffix )
1289 except SCons.Util.RegError:
1291 suites.append(edition_name)
1292 except SCons.Util.RegError:
1295 # Detect Express edition
1297 idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1298 r'Software\Microsoft\VCExpress\8.0')
1299 SCons.Util.RegQueryValueEx(idk, 'InstallDir')
1300 suites.append('EXPRESS')
1301 except SCons.Util.RegError:
1306 def is_msvs_installed():
1308 Check the registry for an installed visual studio.
1311 v = SCons.Tool.msvs.get_visualstudio_versions()
1313 except (SCons.Util.RegError, SCons.Errors.InternalError):
1316 def get_msvs_install_dirs(version = None, vs8suite = None):
1318 Get installed locations for various msvc-related products, like the .NET SDK
1319 and the Platform SDK.
1322 if not SCons.Util.can_read_reg:
1326 versions = get_visualstudio_versions()
1328 version = versions[0] #use highest version by default
1332 version_num, suite = msvs_parse_version(version)
1334 K = 'Software\\Microsoft\\VisualStudio\\' + str(version_num)
1335 if (version_num >= 8.0):
1336 if vs8suite == None:
1337 # We've been given no guidance about which Visual Studio 8
1338 # suite to use, so attempt to autodetect.
1339 suites = get_visualstudio8_suites()
1341 vs8suite = suites[0]
1343 if vs8suite == 'EXPRESS':
1344 K = 'Software\\Microsoft\\VCExpress\\' + str(version_num)
1348 if (version_num < 7.0):
1349 key = K + r'\Setup\Microsoft Visual C++\ProductDir'
1351 key = K + r'\Setup\VC\ProductDir'
1353 (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, key)
1354 except SCons.Util.RegError:
1357 # visual studio install dir
1358 if (version_num < 7.0):
1360 (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1361 K + r'\Setup\Microsoft Visual Studio\ProductDir')
1362 except SCons.Util.RegError:
1365 if not rv.has_key('VSINSTALLDIR') or not rv['VSINSTALLDIR']:
1366 if rv.has_key('VCINSTALLDIR') and rv['VCINSTALLDIR']:
1367 rv['VSINSTALLDIR'] = os.path.dirname(rv['VCINSTALLDIR'])
1369 rv['VSINSTALLDIR'] = os.path.join(SCons.Platform.win32.get_program_files_dir(),'Microsoft Visual Studio')
1372 (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1373 K + r'\Setup\VS\ProductDir')
1374 except SCons.Util.RegError:
1377 # .NET framework install dir
1379 (rv['FRAMEWORKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1380 r'Software\Microsoft\.NETFramework\InstallRoot')
1381 except SCons.Util.RegError:
1384 if rv.has_key('FRAMEWORKDIR'):
1385 # try and enumerate the installed versions of the .NET framework.
1386 contents = os.listdir(rv['FRAMEWORKDIR'])
1387 l = re.compile('v[0-9]+.*')
1388 installed_framework_versions = filter(lambda e, l=l: l.match(e), contents)
1391 # since version numbers aren't really floats...
1396 c = int(bbl[0]) - int(aal[0])
1398 c = int(bbl[1]) - int(aal[1])
1400 c = int(bbl[2]) - int(aal[2])
1403 installed_framework_versions.sort(versrt)
1405 rv['FRAMEWORKVERSIONS'] = installed_framework_versions
1407 # TODO: allow a specific framework version to be set
1409 # Choose a default framework version based on the Visual
1411 DefaultFrameworkVersionMap = {
1415 # TODO: Does .NET 3.0 need to be worked into here somewhere?
1418 default_framework_version = DefaultFrameworkVersionMap[version[:3]]
1419 except (KeyError, TypeError):
1422 # Look for the first installed directory in FRAMEWORKDIR that
1423 # begins with the framework version string that's appropriate
1424 # for the Visual Studio version we're using.
1425 for v in installed_framework_versions:
1426 if v[:4] == default_framework_version:
1427 rv['FRAMEWORKVERSION'] = v
1430 # If the framework version couldn't be worked out by the previous
1431 # code then fall back to using the latest version of the .NET
1433 if not rv.has_key('FRAMEWORKVERSION'):
1434 rv['FRAMEWORKVERSION'] = installed_framework_versions[0]
1436 # .NET framework SDK install dir
1437 if rv.has_key('FRAMEWORKVERSION'):
1438 # The .NET SDK version used must match the .NET version used,
1439 # so we deliberately don't fall back to other .NET framework SDK
1440 # versions that might be present.
1441 ver = rv['FRAMEWORKVERSION'][:4]
1442 key = r'Software\Microsoft\.NETFramework\sdkInstallRoot' + ver
1444 (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1446 except SCons.Util.RegError:
1449 # MS Platform SDK dir
1451 (rv['PLATFORMSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1452 r'Software\Microsoft\MicrosoftSDK\Directories\Install Dir')
1453 except SCons.Util.RegError:
1456 if rv.has_key('PLATFORMSDKDIR'):
1457 # if we have a platform SDK, try and get some info on it.
1460 loc = r'Software\Microsoft\MicrosoftSDK\InstalledSDKs'
1461 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,loc)
1465 key = SCons.Util.RegEnumKey(k,i)
1466 sdk = SCons.Util.RegOpenKeyEx(k,key)
1473 (vk,vv,t) = SCons.Util.RegEnumValue(sdk,j)
1474 if vk.lower() == 'keyword':
1476 if vk.lower() == 'propagation_date':
1478 if vk.lower() == 'version':
1481 except SCons.Util.RegError:
1484 vers[name] = (date, version)
1486 except SCons.Util.RegError:
1488 rv['PLATFORMSDK_MODULES'] = vers
1489 except SCons.Util.RegError:
1494 def GetMSVSProjectSuffix(target, source, env, for_signature):
1495 return env['MSVS']['PROJECTSUFFIX']
1497 def GetMSVSSolutionSuffix(target, source, env, for_signature):
1498 return env['MSVS']['SOLUTIONSUFFIX']
1500 def GenerateProject(target, source, env):
1501 # generate the dsp file, according to the version of MSVS.
1502 builddspfile = target[0]
1503 dspfile = builddspfile.srcnode()
1505 # this detects whether or not we're using a BuildDir
1506 if not dspfile is builddspfile:
1508 bdsp = open(str(builddspfile), "w+")
1509 except IOError, detail:
1510 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1513 bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
1515 GenerateDSP(dspfile, source, env)
1517 if env.get('auto_build_solution', 1):
1518 builddswfile = target[1]
1519 dswfile = builddswfile.srcnode()
1521 if not dswfile is builddswfile:
1524 bdsw = open(str(builddswfile), "w+")
1525 except IOError, detail:
1526 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1529 bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
1531 GenerateDSW(dswfile, source, env)
1533 def GenerateSolution(target, source, env):
1534 GenerateDSW(target[0], source, env)
1536 def projectEmitter(target, source, env):
1537 """Sets up the DSP dependencies."""
1539 # todo: Not sure what sets source to what user has passed as target,
1540 # but this is what happens. When that is fixed, we also won't have
1541 # to make the user always append env['MSVSPROJECTSUFFIX'] to target.
1542 if source[0] == target[0]:
1545 # make sure the suffix is correct for the version of MSVS we're running.
1546 (base, suff) = SCons.Util.splitext(str(target[0]))
1547 suff = env.subst('$MSVSPROJECTSUFFIX')
1548 target[0] = base + suff
1551 source = 'prj_inputs:'
1552 source = source + env.subst('$MSVSSCONSCOM', 1)
1553 source = source + env.subst('$MSVSENCODING', 1)
1555 if env.has_key('buildtarget') and env['buildtarget'] != None:
1556 if SCons.Util.is_String(env['buildtarget']):
1557 source = source + ' "%s"' % env['buildtarget']
1558 elif SCons.Util.is_List(env['buildtarget']):
1559 for bt in env['buildtarget']:
1560 if SCons.Util.is_String(bt):
1561 source = source + ' "%s"' % bt
1563 try: source = source + ' "%s"' % bt.get_abspath()
1564 except AttributeError: raise SCons.Errors.InternalError, \
1565 "buildtarget can be a string, a node, a list of strings or nodes, or None"
1567 try: source = source + ' "%s"' % env['buildtarget'].get_abspath()
1568 except AttributeError: raise SCons.Errors.InternalError, \
1569 "buildtarget can be a string, a node, a list of strings or nodes, or None"
1571 if env.has_key('outdir') and env['outdir'] != None:
1572 if SCons.Util.is_String(env['outdir']):
1573 source = source + ' "%s"' % env['outdir']
1574 elif SCons.Util.is_List(env['outdir']):
1575 for s in env['outdir']:
1576 if SCons.Util.is_String(s):
1577 source = source + ' "%s"' % s
1579 try: source = source + ' "%s"' % s.get_abspath()
1580 except AttributeError: raise SCons.Errors.InternalError, \
1581 "outdir can be a string, a node, a list of strings or nodes, or None"
1583 try: source = source + ' "%s"' % env['outdir'].get_abspath()
1584 except AttributeError: raise SCons.Errors.InternalError, \
1585 "outdir can be a string, a node, a list of strings or nodes, or None"
1587 if env.has_key('name'):
1588 if SCons.Util.is_String(env['name']):
1589 source = source + ' "%s"' % env['name']
1591 raise SCons.Errors.InternalError, "name must be a string"
1593 if env.has_key('variant'):
1594 if SCons.Util.is_String(env['variant']):
1595 source = source + ' "%s"' % env['variant']
1596 elif SCons.Util.is_List(env['variant']):
1597 for variant in env['variant']:
1598 if SCons.Util.is_String(variant):
1599 source = source + ' "%s"' % variant
1601 raise SCons.Errors.InternalError, "name must be a string or a list of strings"
1603 raise SCons.Errors.InternalError, "variant must be a string or a list of strings"
1605 raise SCons.Errors.InternalError, "variant must be specified"
1607 for s in _DSPGenerator.srcargs:
1609 if SCons.Util.is_String(env[s]):
1610 source = source + ' "%s' % env[s]
1611 elif SCons.Util.is_List(env[s]):
1613 if SCons.Util.is_String(t):
1614 source = source + ' "%s"' % t
1616 raise SCons.Errors.InternalError, s + " must be a string or a list of strings"
1618 raise SCons.Errors.InternalError, s + " must be a string or a list of strings"
1620 source = source + ' "%s"' % str(target[0])
1621 source = [SCons.Node.Python.Value(source)]
1623 targetlist = [target[0]]
1626 if env.get('auto_build_solution', 1):
1627 env['projects'] = targetlist
1628 t, s = solutionEmitter(target, target, env)
1629 targetlist = targetlist + t
1631 return (targetlist, sourcelist)
1633 def solutionEmitter(target, source, env):
1634 """Sets up the DSW dependencies."""
1636 # todo: Not sure what sets source to what user has passed as target,
1637 # but this is what happens. When that is fixed, we also won't have
1638 # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target.
1639 if source[0] == target[0]:
1642 # make sure the suffix is correct for the version of MSVS we're running.
1643 (base, suff) = SCons.Util.splitext(str(target[0]))
1644 suff = env.subst('$MSVSSOLUTIONSUFFIX')
1645 target[0] = base + suff
1648 source = 'sln_inputs:'
1650 if env.has_key('name'):
1651 if SCons.Util.is_String(env['name']):
1652 source = source + ' "%s"' % env['name']
1654 raise SCons.Errors.InternalError, "name must be a string"
1656 if env.has_key('variant'):
1657 if SCons.Util.is_String(env['variant']):
1658 source = source + ' "%s"' % env['variant']
1659 elif SCons.Util.is_List(env['variant']):
1660 for variant in env['variant']:
1661 if SCons.Util.is_String(variant):
1662 source = source + ' "%s"' % variant
1664 raise SCons.Errors.InternalError, "name must be a string or a list of strings"
1666 raise SCons.Errors.InternalError, "variant must be a string or a list of strings"
1668 raise SCons.Errors.InternalError, "variant must be specified"
1670 if env.has_key('slnguid'):
1671 if SCons.Util.is_String(env['slnguid']):
1672 source = source + ' "%s"' % env['slnguid']
1674 raise SCons.Errors.InternalError, "slnguid must be a string"
1676 if env.has_key('projects'):
1677 if SCons.Util.is_String(env['projects']):
1678 source = source + ' "%s"' % env['projects']
1679 elif SCons.Util.is_List(env['projects']):
1680 for t in env['projects']:
1681 if SCons.Util.is_String(t):
1682 source = source + ' "%s"' % t
1684 source = source + ' "%s"' % str(target[0])
1685 source = [SCons.Node.Python.Value(source)]
1687 return ([target[0]], source)
1689 projectAction = SCons.Action.Action(GenerateProject, None)
1691 solutionAction = SCons.Action.Action(GenerateSolution, None)
1693 projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
1694 suffix = '$MSVSPROJECTSUFFIX',
1695 emitter = projectEmitter)
1697 solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM',
1698 suffix = '$MSVSSOLUTIONSUFFIX',
1699 emitter = solutionEmitter)
1701 default_MSVS_SConscript = None
1704 """Add Builders and construction variables for Microsoft Visual
1705 Studio project files to an Environment."""
1707 env['BUILDERS']['MSVSProject']
1709 env['BUILDERS']['MSVSProject'] = projectBuilder
1712 env['BUILDERS']['MSVSSolution']
1714 env['BUILDERS']['MSVSSolution'] = solutionBuilder
1716 env['MSVSPROJECTCOM'] = projectAction
1717 env['MSVSSOLUTIONCOM'] = solutionAction
1719 if SCons.Script.call_stack:
1720 # XXX Need to find a way to abstract this; the build engine
1721 # shouldn't depend on anything in SCons.Script.
1722 env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript
1724 global default_MSVS_SConscript
1725 if default_MSVS_SConscript is None:
1726 default_MSVS_SConscript = env.File('SConstruct')
1727 env['MSVSSCONSCRIPT'] = default_MSVS_SConscript
1729 env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env))
1730 env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}'
1731 env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS'
1732 env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
1733 env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
1734 env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"'
1735 env['MSVSENCODING'] = 'Windows-1252'
1738 version = get_default_visualstudio_version(env)
1739 # keep a record of some of the MSVS info so the user can use it.
1740 dirs = get_msvs_install_dirs(version)
1741 env['MSVS'].update(dirs)
1742 except (SCons.Util.RegError, SCons.Errors.InternalError):
1743 # we don't care if we can't do this -- if we can't, it's
1744 # because we don't have access to the registry, or because the
1745 # tools aren't installed. In either case, the user will have to
1746 # find them on their own.
1749 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1750 if (version_num < 7.0):
1751 env['MSVS']['PROJECTSUFFIX'] = '.dsp'
1752 env['MSVS']['SOLUTIONSUFFIX'] = '.dsw'
1754 env['MSVS']['PROJECTSUFFIX'] = '.vcproj'
1755 env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
1757 env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix
1758 env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix
1759 env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}'
1760 env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}'
1761 env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
1765 v = SCons.Tool.msvs.get_visualstudio_versions()
1766 except (SCons.Util.RegError, SCons.Errors.InternalError):
1771 if env.has_key('MSVS_VERSION'):
1772 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1773 if version_num >= 7.0:
1774 # The executable is 'devenv' in Visual Studio Pro,
1775 # Team System and others. Express Editions have different
1776 # executable names. Right now we're only going to worry
1777 # about Visual C++ 2005 Express Edition.
1778 return env.Detect('devenv') or env.Detect('vcexpress')
1780 return env.Detect('msdev')
1782 # there's at least one version of MSVS installed.