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__"
47 import SCons.Platform.win32
52 ##############################################################################
53 # Below here are the classes and functions for generation of
54 # DSP/DSW/SLN/VCPROJ files.
55 ##############################################################################
58 """Return a string as a string of hex characters.
60 # NOTE: This routine is a method in the Python 2.0 interface
61 # of the native md5 module, but we want SCons to operate all
62 # the way back to at least Python 1.5.2, which doesn't have it.
67 r = r + h[(i >> 4) & 0xF] + h[i & 0xF]
70 def _generateGUID(slnfile, name):
71 """This generates a dummy GUID for the sln file to use. It is
72 based on the MD5 signatures of the sln filename plus the name of
73 the project. It basically just needs to be unique, and not
74 change with each invocation."""
75 solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper()
76 # convert most of the signature to GUID form (discard the rest)
77 solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}"
80 # This is how we re-invoke SCons from inside MSVS Project files.
81 # The problem is that we might have been invoked as either scons.bat
82 # or scons.py. If we were invoked directly as scons.py, then we could
83 # use sys.argv[0] to find the SCons "executable," but that doesn't work
84 # if we were invoked as scons.bat, which uses "python -c" to execute
85 # things and ends up with "-c" as sys.argv[0]. Consequently, we have
86 # the MSVS Project file invoke SCons the same way that scons.bat does,
87 # which works regardless of how we were invoked.
88 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()"
89 exec_script_main_xml = string.replace(exec_script_main, "'", "'")
91 # The string for the Python executable we tell the Project file to use
92 # is either sys.executable or, if an external PYTHON_ROOT environment
93 # variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to
94 # pluck the actual executable name from sys.executable).
96 python_root = os.environ['PYTHON_ROOT']
98 python_executable = sys.executable
100 python_executable = os.path.join('$(PYTHON_ROOT)',
101 os.path.split(sys.executable)[1])
107 """ Base class for DSP generators """
108 def __init__(self, dspfile, source, env):
109 if type(dspfile) == types.StringType:
110 self.dspfile = os.path.abspath(dspfile)
112 self.dspfile = dspfile.get_abspath()
115 self.conspath = source[0].attributes.sconstruct.get_abspath()
117 raise SCons.Errors.InternalError, \
118 "Unable to determine where the SConstruct is"
120 self.config = Config()
121 if env.has_key('variant'):
122 self.config.variant = env['variant'].capitalize()
124 raise SCons.Errors.InternalError, \
125 "You must specify a 'variant' argument (i.e. 'Debug' or " +\
126 "'Release') to create an MSVSProject."
128 if env.has_key('buildtarget'):
129 if type(env['buildtarget']) == types.StringType:
130 self.config.buildtarget = os.path.abspath(env['buildtarget'])
131 elif type(env['buildtarget']) == types.ListType:
132 self.config.buildtarget = env['buildtarget'][0].get_abspath()
134 self.config.buildtarget = env['buildtarget'].get_abspath()
136 raise SCons.Errors.InternalError, \
137 "You must specify a target 'buildtarget' file argument (such as the target" +\
138 " executable) to create an MSVSProject."
140 self.config.outdir = os.path.dirname(self.config.buildtarget)
142 if type(source[0]) == types.StringType:
143 self.source = os.path.abspath(source[0])
145 self.source = source[0].get_abspath()
149 if self.env.has_key('name'):
150 self.name = self.env['name']
152 self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
154 print "Adding '" + self.name + ' - ' + self.config.variant + "' to Visual Studio Project '" + str(dspfile) + "'"
171 for n in sourcenames:
176 if os.path.exists(self.dspfile):
179 for t in zip(sourcenames,srcargs):
180 if self.env.has_key(t[1]):
181 if type(self.env[t[1]]) == types.ListType:
182 for i in self.env[t[1]]:
183 if not i in self.sources[t[0]]:
184 self.sources[t[0]].append(i)
186 if not self.env[t[1]] in self.sources[t[0]]:
187 self.sources[t[0]].append(self.env[t[1]])
189 for n in sourcenames:
190 self.sources[n].sort()
192 self.configs[self.config.variant] = self.config
198 # Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4>
199 # Microsoft Developer Studio Generated Build File, Format Version 6.00
202 # TARGTYPE "Win32 (x86) External Target" 0x0106
204 CFG=%(name)s - Win32 %(confkey)s
205 !MESSAGE This is not a valid makefile. To build this project using NMAKE,
206 !MESSAGE use the Export Makefile command and run
208 !MESSAGE NMAKE /f "%(name)s.mak".
210 !MESSAGE You can specify a configuration when running NMAKE
211 !MESSAGE by defining the macro CFG on the command line. For example:
213 !MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s"
215 !MESSAGE Possible choices for configuration are:
219 class _GenerateV6DSP(_DSPGenerator):
220 """Generates a Project file for MSVS 6.0"""
222 def PrintHeader(self):
223 # pick a default config
224 confkeys = self.configs.keys()
228 confkey = confkeys[0]
230 self.file.write(V6DSPHeader % locals())
232 for kind in confkeys:
233 self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind))
235 self.file.write('!MESSAGE \n\n')
237 def PrintProject(self):
239 self.file.write('# Begin Project\n'
240 '# PROP AllowPerConfigDependencies 0\n'
241 '# PROP Scc_ProjName ""\n'
242 '# PROP Scc_LocalPath ""\n\n')
245 confkeys = self.configs.keys()
247 for kind in confkeys:
248 outdir = self.configs[kind].outdir
249 buildtarget = self.configs[kind].buildtarget
251 self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
254 self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
256 # have to write this twice, once with the BASE settings, and once without
257 for base in ("BASE ",""):
258 self.file.write('# PROP %sUse_MFC 0\n'
259 '# PROP %sUse_Debug_Libraries ' % (base, base))
260 if kind.lower().find('debug') < 0:
261 self.file.write('0\n')
263 self.file.write('1\n')
264 self.file.write('# PROP %sOutput_Dir "%s"\n'
265 '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir))
266 (d,c) = os.path.split(str(self.conspath))
267 cmd = 'echo Starting SCons && "%s" -c "%s" -C %s -f %s %s'
268 cmd = cmd % (python_executable, exec_script_main, d, c, buildtarget)
269 self.file.write('# PROP %sCmd_Line "%s"\n'
270 '# PROP %sRebuild_Opt "-c && %s"\n'
271 '# PROP %sTarget_File "%s"\n'
272 '# PROP %sBsc_Name ""\n'
273 '# PROP %sTarget_Dir ""\n'\
274 %(base,cmd,base,cmd,base,buildtarget,base,base))
276 self.file.write('\n!ENDIF\n\n'
277 '# Begin Target\n\n')
278 for kind in confkeys:
279 self.file.write('# Name "%s - Win32 %s"\n' % (name,kind))
280 self.file.write('\n')
282 for kind in confkeys:
284 self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
287 self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
288 self.file.write('!ENDIF \n\n')
289 self.PrintSourceFiles()
290 self.file.write('# End Target\n'
293 # now we pickle some data and add it to the file -- MSDEV will ignore it.
294 pdata = pickle.dumps(self.configs,1)
295 pdata = base64.encodestring(pdata)
296 self.file.write(pdata + '\n')
297 pdata = pickle.dumps(self.sources,1)
298 pdata = base64.encodestring(pdata)
299 self.file.write(pdata + '\n')
301 def PrintSourceFiles(self):
302 categories = {' Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat',
303 'Header Files': 'h|hpp|hxx|hm|inl',
304 'Local Headers': 'h|hpp|hxx|hm|inl',
305 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe',
308 cats = categories.keys()
311 if not self.sources[kind]:
312 continue # skip empty groups
314 self.file.write('# Begin Group "' + kind + '"\n\n')
315 typelist = categories[kind].replace('|',';')
316 self.file.write('# PROP Default_Filter "' + typelist + '"\n')
318 for file in self.sources[kind]:
319 file = os.path.normpath(file)
320 self.file.write('# Begin Source File\n\n'
321 'SOURCE="' + file + '"\n'
322 '# End Source File\n')
323 self.file.write('# End Group\n')
325 # add the Conscript file outside of the groups
326 self.file.write('# Begin Source File\n\n'
327 'SOURCE="' + str(self.source) + '"\n'
328 '# End Source File\n')
332 dspfile = open(self.dspfile,'r')
334 return # doesn't exist yet, so can't add anything to configs.
336 line = dspfile.readline()
338 if line.find("# End Project") > -1:
340 line = dspfile.readline()
342 line = dspfile.readline()
344 while line and line != '\n':
345 line = dspfile.readline()
348 # OK, we've found our little pickled cache of data.
350 datas = base64.decodestring(datas)
351 data = pickle.loads(datas)
352 except KeyboardInterrupt:
355 return # unable to unpickle any data for some reason
357 self.configs.update(data)
360 line = dspfile.readline()
362 while line and line != '\n':
363 line = dspfile.readline()
366 # OK, we've found our little pickled cache of data.
367 # it has a "# " in front of it, so we strip that.
369 datas = base64.decodestring(datas)
370 data = pickle.loads(datas)
371 except KeyboardInterrupt:
374 return # unable to unpickle any data for some reason
376 self.sources.update(data)
380 self.file = open(self.dspfile,'w')
381 except IOError, detail:
382 raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail)
389 <?xml version="1.0" encoding = "Windows-1252"?>
391 \tProjectType="Visual C++"
392 \tVersion="%(versionstr)s"
396 \tKeyword="MakeFileProj">
403 V7DSPConfiguration = """\
405 \t\t\tName="%(capitalized_kind)s|Win32"
406 \t\t\tOutputDirectory="%(outdir)s"
407 \t\t\tIntermediateDirectory="%(outdir)s"
408 \t\t\tConfigurationType="0"
410 \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
412 \t\t\t\tName="VCNMakeTool"
413 \t\t\t\tBuildCommandLine="%(cmd)s"
414 \t\t\t\tCleanCommandLine="%(cleancmd)s"
415 \t\t\t\tRebuildCommandLine="%(cmd)s"
416 \t\t\t\tOutput="%(buildtarget)s"/>
420 class _GenerateV7DSP(_DSPGenerator):
421 """Generates a Project file for MSVS .NET"""
423 def __init__(self, dspfile, source, env):
424 _DSPGenerator.__init__(self, dspfile, source, env)
425 self.version = float(env['MSVS_VERSION'])
426 self.versionstr = '7.00'
427 if self.version >= 7.1:
428 self.versionstr = '7.10'
430 def PrintHeader(self):
431 self.file.write(V7DSPHeader % self.__dict__)
433 def PrintProject(self):
434 self.file.write('\t<Configurations>\n')
436 confkeys = self.configs.keys()
438 for kind in confkeys:
439 capitalized_kind = kind.capitalize()
440 outdir = self.configs[kind].outdir
441 buildtarget = self.configs[kind].buildtarget
443 (d,c) = os.path.split(str(self.conspath))
444 fmt = 'echo Starting SCons && "%s" -c "%s" -C %s -f %s%s %s'
445 cmd = fmt % (python_executable, exec_script_main_xml,
446 d, c, '', buildtarget)
447 cleancmd = fmt % (python_executable, exec_script_main_xml,
448 d, c, ' -c', buildtarget)
450 self.file.write(V7DSPConfiguration % locals())
452 self.file.write('\t</Configurations>\n')
454 if self.version >= 7.1:
455 self.file.write('\t<References>\n'
458 self.PrintSourceFiles()
460 self.file.write('</VisualStudioProject>\n')
462 # now we pickle some data and add it to the file -- MSDEV will ignore it.
463 pdata = pickle.dumps(self.configs,1)
464 pdata = base64.encodestring(pdata)
465 self.file.write('<!-- SCons Data:\n' + pdata + '\n')
466 pdata = pickle.dumps(self.sources,1)
467 pdata = base64.encodestring(pdata)
468 self.file.write(pdata + '-->\n')
470 def PrintSourceFiles(self):
471 categories = {' Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
472 'Header Files': 'h;hpp;hxx;hm;inl',
473 'Local Headers': 'h;hpp;hxx;hm;inl',
474 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe',
477 self.file.write('\t<Files>\n')
479 cats = categories.keys()
482 if not self.sources[kind]:
483 continue # skip empty groups
485 self.file.write('\t\t<Filter\n'
487 '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
489 for file in self.sources[kind]:
490 file = os.path.normpath(file)
491 self.file.write('\t\t\t<File\n'
492 '\t\t\t\tRelativePath="%s">\n'
493 '\t\t\t</File>\n' % file)
495 self.file.write('\t\t</Filter>\n')
497 # add the Conscript file outside of the groups
498 self.file.write('\t\t<File\n'
499 '\t\t\tRelativePath="%s">\n'
501 '\t</Files>\n' % str(self.source))
503 self.file.write('\t<Globals>\n'
508 dspfile = open(self.dspfile,'r')
510 return # doesn't exist yet, so can't add anything to configs.
512 line = dspfile.readline()
514 if line.find('<!-- SCons Data:') > -1:
516 line = dspfile.readline()
518 line = dspfile.readline()
520 while line and line != '\n':
521 line = dspfile.readline()
524 # OK, we've found our little pickled cache of data.
526 datas = base64.decodestring(datas)
527 data = pickle.loads(datas)
528 except KeyboardInterrupt:
531 return # unable to unpickle any data for some reason
533 self.configs.update(data)
536 line = dspfile.readline()
538 while line and line != '\n':
539 line = dspfile.readline()
542 # OK, we've found our little pickled cache of data.
544 datas = base64.decodestring(datas)
545 data = pickle.loads(datas)
546 except KeyboardInterrupt:
549 return # unable to unpickle any data for some reason
551 self.sources.update(data)
555 self.file = open(self.dspfile,'w')
556 except IOError, detail:
557 raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail)
564 """ Base class for DSW generators """
565 def __init__(self, dswfile, dspfile, source, env):
566 self.dswfile = os.path.normpath(str(dswfile))
567 self.dspfile = os.path.abspath(str(dspfile))
570 if self.env.has_key('name'):
571 self.name = self.env['name']
573 self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
578 class _GenerateV7DSW(_DSWGenerator):
579 """Generates a Solution file for MSVS .NET"""
580 def __init__(self, dswfile, dspfile, source, env):
581 _DSWGenerator.__init__(self, dswfile, dspfile, source, env)
583 self.version = float(self.env['MSVS_VERSION'])
584 self.versionstr = '7.00'
585 if self.version >= 7.1:
586 self.versionstr = '8.00'
588 if env.has_key('slnguid') and env['slnguid']:
589 self.slnguid = env['slnguid']
591 self.slnguid = _generateGUID(dswfile, self.name)
593 self.config = Config()
594 if env.has_key('variant'):
595 self.config.variant = env['variant'].capitalize()
597 raise SCons.Errors.InternalError, \
598 "You must specify a 'variant' argument (i.e. 'Debug' or " +\
599 "'Release') to create an MSVS Solution File."
603 if os.path.exists(self.dswfile):
606 self.configs[self.config.variant] = self.config
610 dswfile = open(self.dswfile,'r')
612 return # doesn't exist yet, so can't add anything to configs.
614 line = dswfile.readline()
616 if line[:9] == "EndGlobal":
618 line = dswfile.readline()
620 line = dswfile.readline()
623 line = dswfile.readline()
626 # OK, we've found our little pickled cache of data.
628 datas = base64.decodestring(datas)
629 data = pickle.loads(datas)
630 except KeyboardInterrupt:
633 return # unable to unpickle any data for some reason
635 self.configs.update(data)
637 def PrintSolution(self):
638 """Writes a solution file"""
639 self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n'
640 # the next line has the GUID for an external makefile project.
641 'Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%s", "%s", "%s"\n'
642 % (self.versionstr, self.name, os.path.basename(self.dspfile), self.slnguid))
643 if self.version >= 7.1:
644 self.file.write('\tProjectSection(ProjectDependencies) = postProject\n'
645 '\tEndProjectSection\n')
646 self.file.write('EndProject\n'
648 '\tGlobalSection(SolutionConfiguration) = preSolution\n')
649 confkeys = self.configs.keys()
652 for name in confkeys:
653 self.file.write('\t\tConfigName.%d = %s\n' % (cnt, name.capitalize()))
655 self.file.write('\tEndGlobalSection\n')
656 if self.version < 7.1:
657 self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n'
658 '\tEndGlobalSection\n')
659 self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
660 for name in confkeys:
661 name = name.capitalize()
662 self.file.write('\t\t%s.%s.ActiveCfg = %s|Win32\n'
663 '\t\t%s.%s.Build.0 = %s|Win32\n' %(self.slnguid,name,name,self.slnguid,name,name))
664 self.file.write('\tEndGlobalSection\n'
665 '\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
666 '\tEndGlobalSection\n'
667 '\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
668 '\tEndGlobalSection\n'
670 pdata = pickle.dumps(self.configs,1)
671 pdata = base64.encodestring(pdata)
672 self.file.write(pdata + '\n')
676 self.file = open(self.dswfile,'w')
677 except IOError, detail:
678 raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
684 Microsoft Developer Studio Workspace File, Format Version 6.00
685 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
687 ###############################################################################
689 Project: "%(name)s"="%(dspfile)s" - Package Owner=<4>
699 ###############################################################################
711 ###############################################################################
714 class _GenerateV6DSW(_DSWGenerator):
715 """Generates a Workspace file for MSVS 6.0"""
717 def PrintWorkspace(self):
718 """ writes a DSW file """
719 self.file.write(V6DSWHeader % self.__dict__)
723 self.file = open(self.dswfile,'w')
724 except IOError, detail:
725 raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
727 self.PrintWorkspace()
731 def GenerateDSP(dspfile, source, env):
732 """Generates a Project file based on the version of MSVS that is being used"""
734 if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0:
735 g = _GenerateV7DSP(dspfile, source, env)
738 g = _GenerateV6DSP(dspfile, source, env)
741 def GenerateDSW(dswfile, dspfile, source, env):
742 """Generates a Solution/Workspace file based on the version of MSVS that is being used"""
744 if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0:
745 g = _GenerateV7DSW(dswfile, dspfile, source, env)
748 g = _GenerateV6DSW(dswfile, dspfile, source, env)
752 ##############################################################################
753 # Above here are the classes and functions for generation of
754 # DSP/DSW/SLN/VCPROJ files.
755 ##############################################################################
757 def get_default_visualstudio_version(env):
758 """Returns the version set in the env, or the latest version
759 installed, if it can find it, or '6.0' if all else fails. Also
760 updated the environment with what it found."""
764 if not env.has_key('MSVS') or type(env['MSVS']) != types.DictType:
767 if env.has_key('MSVS_VERSION'):
768 version = env['MSVS_VERSION']
771 if SCons.Util.can_read_reg:
772 versions = get_visualstudio_versions()
774 version = versions[0] #use highest version by default
776 env['MSVS_VERSION'] = version
777 env['MSVS']['VERSIONS'] = versions
778 env['MSVS']['VERSION'] = version
782 def get_visualstudio_versions():
784 Get list of visualstudio versions from the Windows registry.
785 Returns a list of strings containing version numbers. An empty list
786 is returned if we were unable to accees the register (for example,
787 we couldn't import the registry-access module) or the appropriate
788 registry keys weren't found.
791 if not SCons.Util.can_read_reg:
794 HLM = SCons.Util.HKEY_LOCAL_MACHINE
795 K = r'Software\Microsoft\VisualStudio'
798 k = SCons.Util.RegOpenKeyEx(HLM, K)
802 p = SCons.Util.RegEnumKey(k,i)
803 except SCons.Util.RegError:
806 if not p[0] in '123456789' or p in L:
808 # Only add this version number if there is a valid
809 # registry structure (includes the "Setup" key),
810 # and at least some of the correct directories
811 # exist. Sometimes VS uninstall leaves around
812 # some registry/filesystem turds that we don't
813 # want to trip over. Also, some valid registry
814 # entries are MSDN entries, not MSVS ('7.1',
815 # notably), and we want to skip those too.
817 SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p + '\\Setup')
818 except SCons.Util.RegError:
822 idk = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p)
823 # This is not always here -- it only exists if the
824 # user installed into a non-standard location (at
825 # least in VS6 it works that way -- VS7 seems to
828 id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
829 except SCons.Util.RegError:
832 # If the InstallDir key doesn't exist,
833 # then we check the default locations.
834 if not id or not id[0]:
835 files_dir = SCons.Platform.win32.get_program_files_dir()
837 vs = r'Microsoft Visual Studio\Common\MSDev98'
839 vs = r'Microsoft Visual Studio .NET\Common7\IDE'
840 id = [ os.path.join(files_dir, vs) ]
841 if os.path.exists(id[0]):
843 except SCons.Util.RegError:
849 # This is a hack to get around the fact that certain Visual Studio
850 # patches place a "6.1" version in the registry, which does not have
851 # any of the keys we need to find include paths, install directories,
852 # etc. Therefore we ignore it if it is there, since it throws all
864 def is_msvs_installed():
866 Check the registry for an installed visual studio.
869 v = SCons.Tool.msvs.get_visualstudio_versions()
871 except (SCons.Util.RegError, SCons.Errors.InternalError):
874 def get_msvs_install_dirs(version = None):
876 Get installed locations for various msvc-related products, like the .NET SDK
877 and the Platform SDK.
880 if not SCons.Util.can_read_reg:
884 versions = get_visualstudio_versions()
886 version = versions[0] #use highest version by default
890 K = 'Software\\Microsoft\\VisualStudio\\' + version
895 if (float(version) < 7.0):
896 (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
897 K + r'\Setup\Microsoft Visual C++\ProductDir')
899 (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
900 K + r'\Setup\VC\ProductDir')
901 except SCons.Util.RegError:
904 # visual studio install dir
905 if (float(version) < 7.0):
907 (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
908 K + r'\Setup\Microsoft Visual Studio\ProductDir')
909 except SCons.Util.RegError:
912 if not rv.has_key('VSINSTALLDIR') or not rv['VSINSTALLDIR']:
913 if rv.has_key('VCINSTALLDIR') and rv['VCINSTALLDIR']:
914 rv['VSINSTALLDIR'] = os.path.dirname(rv['VCINSTALLDIR'])
916 rv['VSINSTALLDIR'] = os.path.join(SCons.Platform.win32.get_program_files_dir(),'Microsoft Visual Studio')
919 (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
920 K + r'\Setup\VS\ProductDir')
921 except SCons.Util.RegError:
924 # .NET framework install dir
926 (rv['FRAMEWORKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
927 r'Software\Microsoft\.NETFramework\InstallRoot')
928 except SCons.Util.RegError:
931 if rv.has_key('FRAMEWORKDIR'):
932 # try and enumerate the installed versions of the .NET framework.
933 contents = os.listdir(rv['FRAMEWORKDIR'])
934 l = re.compile('v[0-9]+.*')
936 for entry in contents:
938 versions.append(entry)
941 # since version numbers aren't really floats...
946 c = int(bbl[0]) - int(aal[0])
948 c = int(bbl[1]) - int(aal[1])
950 c = int(bbl[2]) - int(aal[2])
953 versions.sort(versrt)
955 rv['FRAMEWORKVERSIONS'] = versions
956 # assume that the highest version is the latest version installed
957 rv['FRAMEWORKVERSION'] = versions[0]
959 # .NET framework SDK install dir
961 if rv.has_key('FRAMEWORKVERSION') and rv['FRAMEWORKVERSION'][:4] == 'v1.1':
962 key = r'Software\Microsoft\.NETFramework\sdkInstallRootv1.1'
964 key = r'Software\Microsoft\.NETFramework\sdkInstallRoot'
966 (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,key)
968 except SCons.Util.RegError:
971 # MS Platform SDK dir
973 (rv['PLATFORMSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
974 r'Software\Microsoft\MicrosoftSDK\Directories\Install Dir')
975 except SCons.Util.RegError:
978 if rv.has_key('PLATFORMSDKDIR'):
979 # if we have a platform SDK, try and get some info on it.
982 loc = r'Software\Microsoft\MicrosoftSDK\InstalledSDKs'
983 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,loc)
987 key = SCons.Util.RegEnumKey(k,i)
988 sdk = SCons.Util.RegOpenKeyEx(k,key)
995 (vk,vv,t) = SCons.Util.RegEnumValue(sdk,j)
996 if vk.lower() == 'keyword':
998 if vk.lower() == 'propagation_date':
1000 if vk.lower() == 'version':
1003 except SCons.Util.RegError:
1006 vers[name] = (date, version)
1008 except SCons.Util.RegError:
1010 rv['PLATFORMSDK_MODULES'] = vers
1011 except SCons.Util.RegError:
1016 def GetMSVSProjectSuffix(target, source, env, for_signature):
1017 return env['MSVS']['PROJECTSUFFIX'];
1019 def GetMSVSSolutionSuffix(target, source, env, for_signature):
1020 return env['MSVS']['SOLUTIONSUFFIX'];
1022 def GenerateProject(target, source, env):
1023 # generate the dsp file, according to the version of MSVS.
1024 builddspfile = target[0]
1025 builddswfile = target[1]
1026 dswfile = builddswfile.srcnode()
1027 dspfile = builddspfile.srcnode()
1029 # print "SConscript :",str(source[0])
1030 # print "DSW file :",dswfile
1031 # print "DSP file :",dspfile
1032 # print "Build DSW file:",builddswfile
1033 # print "Build DSP file:",builddspfile
1035 # this detects whether or not we're using a BuildDir
1036 if os.path.abspath(os.path.normcase(str(dspfile))) != \
1037 os.path.abspath(os.path.normcase(str(builddspfile))):
1039 bdsp = open(str(builddspfile), "w+")
1040 except IOError, detail:
1041 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1044 bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
1047 bdsw = open(str(builddswfile), "w+")
1048 except IOError, detail:
1049 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1052 bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
1054 GenerateDSP(dspfile, source, env)
1055 GenerateDSW(dswfile, dspfile, source, env)
1057 def projectEmitter(target, source, env):
1058 """Sets up the DSP and DSW dependencies for an SConscript file."""
1060 if source[0] == target[0]:
1063 # make sure the suffix is correct for the version of MSVS we're running.
1064 (base, suff) = SCons.Util.splitext(str(target[0]))
1065 suff = env.subst('$MSVSPROJECTSUFFIX')
1066 target[0] = base + suff
1068 dspfile = env.File(target[0]).srcnode()
1069 dswfile = env.File(SCons.Util.splitext(str(dspfile))[0] + env.subst('$MSVSSOLUTIONSUFFIX'))
1071 # XXX Need to find a way to abstract this; the build engine
1072 # shouldn't depend on anything in SCons.Script.
1073 stack = SCons.Script.call_stack
1075 source = [stack[-1].sconscript.srcnode()]
1076 source[0].attributes.sconstruct = stack[0].sconscript
1078 bdswpath = SCons.Util.splitext(str(target[0]))[0] + env.subst('$MSVSSOLUTIONSUFFIX')
1079 bdswfile = env.File(bdswpath)
1081 # only make these side effects if they're
1082 # not the same file.
1083 if os.path.abspath(os.path.normcase(str(dspfile))) != \
1084 os.path.abspath(os.path.normcase(str(target[0]))):
1085 env.SideEffect(dspfile, target[0])
1086 env.Precious(dspfile)
1087 # dswfile isn't precious -- it can be blown away and rewritten each time.
1088 env.SideEffect(dswfile, target[0])
1090 return ([target[0],bdswfile], source)
1092 projectGeneratorAction = SCons.Action.Action(GenerateProject, None)
1094 projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
1095 suffix = '$MSVSPROJECTSUFFIX',
1096 emitter = projectEmitter)
1099 """Add Builders and construction variables for Microsoft Visual
1100 Studio project files to an Environment."""
1102 env['BUILDERS']['MSVSProject']
1104 env['BUILDERS']['MSVSProject'] = projectBuilder
1106 env['MSVSPROJECTCOM'] = projectGeneratorAction
1109 version = get_default_visualstudio_version(env)
1110 # keep a record of some of the MSVS info so the user can use it.
1111 dirs = get_msvs_install_dirs(version)
1112 env['MSVS'].update(dirs)
1113 except (SCons.Util.RegError, SCons.Errors.InternalError):
1114 # we don't care if we can't do this -- if we can't, it's
1115 # because we don't have access to the registry, or because the
1116 # tools aren't installed. In either case, the user will have to
1117 # find them on their own.
1120 if (float(env['MSVS_VERSION']) < 7.0):
1121 env['MSVS']['PROJECTSUFFIX'] = '.dsp'
1122 env['MSVS']['SOLUTIONSUFFIX'] = '.dsw'
1124 env['MSVS']['PROJECTSUFFIX'] = '.vcproj'
1125 env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
1127 env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix
1128 env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix
1129 env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}'
1130 env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}'
1134 v = SCons.Tool.msvs.get_visualstudio_versions()
1135 except (SCons.Util.RegError, SCons.Errors.InternalError):
1139 if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0:
1140 return env.Detect('devenv')
1142 return env.Detect('msdev')
1144 # there's at least one version of MSVS installed.