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."""
83 #solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper()
84 solution = string.upper(_hexdigest(md5.new(str(slnfile)+str(name)).digest()))
85 # convert most of the signature to GUID form (discard the rest)
86 solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}"
89 version_re = re.compile(r'(\d+\.\d+)(.*)')
91 def msvs_parse_version(s):
93 Split a Visual Studio version, which may in fact be something like
94 '7.0Exp', into is version number (returned as a float) and trailing
97 num, suite = version_re.match(s).groups()
98 return float(num), suite
100 # This is how we re-invoke SCons from inside MSVS Project files.
101 # The problem is that we might have been invoked as either scons.bat
102 # or scons.py. If we were invoked directly as scons.py, then we could
103 # use sys.argv[0] to find the SCons "executable," but that doesn't work
104 # if we were invoked as scons.bat, which uses "python -c" to execute
105 # things and ends up with "-c" as sys.argv[0]. Consequently, we have
106 # the MSVS Project file invoke SCons the same way that scons.bat does,
107 # which works regardless of how we were invoked.
108 def getExecScriptMain(env, xml=None):
109 scons_home = env.get('SCONS_HOME')
110 if not scons_home and os.environ.has_key('SCONS_LIB_DIR'):
111 scons_home = os.environ['SCONS_LIB_DIR']
113 exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home
115 version = SCons.__version__
116 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()
118 exec_script_main = xmlify(exec_script_main)
119 return exec_script_main
121 # The string for the Python executable we tell the Project file to use
122 # is either sys.executable or, if an external PYTHON_ROOT environment
123 # variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to
124 # pluck the actual executable name from sys.executable).
126 python_root = os.environ['PYTHON_ROOT']
128 python_executable = sys.executable
130 python_executable = os.path.join('$$(PYTHON_ROOT)',
131 os.path.split(sys.executable)[1])
136 def splitFully(path):
137 dir, base = os.path.split(path)
138 if dir and dir != '' and dir != path:
139 return splitFully(dir)+[base]
144 def makeHierarchy(sources):
145 '''Break a list of files into a hierarchy; for each value, if it is a string,
146 then it is a file. If it is a dictionary, it is a folder. The string is
147 the original path of the file.'''
151 path = splitFully(file)
154 for part in path[:-1]:
155 if not dict.has_key(part):
158 dict[path[-1]] = file
160 # print 'Warning: failed to decompose path for '+str(file)
164 """ Base class for DSP generators """
173 def __init__(self, dspfile, source, env):
174 self.dspfile = str(dspfile)
176 get_abspath = dspfile.get_abspath
177 except AttributeError:
178 self.dspabs = os.path.abspath(dspfile)
180 self.dspabs = get_abspath()
182 if not env.has_key('variant'):
183 raise SCons.Errors.InternalError, \
184 "You must specify a 'variant' argument (i.e. 'Debug' or " +\
185 "'Release') to create an MSVSProject."
186 elif SCons.Util.is_String(env['variant']):
187 variants = [env['variant']]
188 elif SCons.Util.is_List(env['variant']):
189 variants = env['variant']
191 if not env.has_key('buildtarget') or env['buildtarget'] == None:
193 elif SCons.Util.is_String(env['buildtarget']):
194 buildtarget = [env['buildtarget']]
195 elif SCons.Util.is_List(env['buildtarget']):
196 if len(env['buildtarget']) != len(variants):
197 raise SCons.Errors.InternalError, \
198 "Sizes of 'buildtarget' and 'variant' lists must be the same."
200 for bt in env['buildtarget']:
201 if SCons.Util.is_String(bt):
202 buildtarget.append(bt)
204 buildtarget.append(bt.get_abspath())
206 buildtarget = [env['buildtarget'].get_abspath()]
207 if len(buildtarget) == 1:
211 buildtarget.append(bt)
213 if not env.has_key('outdir') or env['outdir'] == None:
215 elif SCons.Util.is_String(env['outdir']):
216 outdir = [env['outdir']]
217 elif SCons.Util.is_List(env['outdir']):
218 if len(env['outdir']) != len(variants):
219 raise SCons.Errors.InternalError, \
220 "Sizes of 'outdir' and 'variant' lists must be the same."
222 for s in env['outdir']:
223 if SCons.Util.is_String(s):
226 outdir.append(s.get_abspath())
228 outdir = [env['outdir'].get_abspath()]
235 if not env.has_key('runfile') or env['runfile'] == None:
236 runfile = buildtarget[-1:]
237 elif SCons.Util.is_String(env['runfile']):
238 runfile = [env['runfile']]
239 elif SCons.Util.is_List(env['runfile']):
240 if len(env['runfile']) != len(variants):
241 raise SCons.Errors.InternalError, \
242 "Sizes of 'runfile' and 'variant' lists must be the same."
244 for s in env['runfile']:
245 if SCons.Util.is_String(s):
248 runfile.append(s.get_abspath())
250 runfile = [env['runfile'].get_abspath()]
251 if len(runfile) == 1:
257 self.sconscript = env['MSVSSCONSCRIPT']
259 cmdargs = env.get('cmdargs', '')
263 if self.env.has_key('name'):
264 self.name = self.env['name']
266 self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
267 self.name = self.env.subst(self.name)
277 for n in sourcenames:
283 if env.has_key('nokeep') and env['variant'] != 0:
286 if self.nokeep == 0 and os.path.exists(self.dspabs):
289 for t in zip(sourcenames,self.srcargs):
290 if self.env.has_key(t[1]):
291 if SCons.Util.is_List(self.env[t[1]]):
292 for i in self.env[t[1]]:
293 if not i in self.sources[t[0]]:
294 self.sources[t[0]].append(i)
296 if not self.env[t[1]] in self.sources[t[0]]:
297 self.sources[t[0]].append(self.env[t[1]])
299 for n in sourcenames:
301 #self.sources[n].sort(lambda a, b: cmp(a.lower(), b.lower()))
302 self.sources[n].sort(lambda a, b: cmp(string.lower(a), string.lower(b)))
304 def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile):
306 config.buildtarget = buildtarget
307 config.outdir = outdir
308 config.cmdargs = cmdargs
309 config.runfile = runfile
311 match = re.match('(.*)\|(.*)', variant)
313 config.variant = match.group(1)
314 config.platform = match.group(2)
316 config.variant = variant
317 config.platform = 'Win32'
319 self.configs[variant] = config
320 print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'"
322 for i in range(len(variants)):
323 AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs)
326 for key in self.configs.keys():
327 platform = self.configs[key].platform
328 if not platform in self.platforms:
329 self.platforms.append(platform)
335 # Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4>
336 # Microsoft Developer Studio Generated Build File, Format Version 6.00
339 # TARGTYPE "Win32 (x86) External Target" 0x0106
341 CFG=%(name)s - Win32 %(confkey)s
342 !MESSAGE This is not a valid makefile. To build this project using NMAKE,
343 !MESSAGE use the Export Makefile command and run
345 !MESSAGE NMAKE /f "%(name)s.mak".
347 !MESSAGE You can specify a configuration when running NMAKE
348 !MESSAGE by defining the macro CFG on the command line. For example:
350 !MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s"
352 !MESSAGE Possible choices for configuration are:
356 class _GenerateV6DSP(_DSPGenerator):
357 """Generates a Project file for MSVS 6.0"""
359 def PrintHeader(self):
360 # pick a default config
361 confkeys = self.configs.keys()
365 confkey = confkeys[0]
367 self.file.write(V6DSPHeader % locals())
369 for kind in confkeys:
370 self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind))
372 self.file.write('!MESSAGE \n\n')
374 def PrintProject(self):
376 self.file.write('# Begin Project\n'
377 '# PROP AllowPerConfigDependencies 0\n'
378 '# PROP Scc_ProjName ""\n'
379 '# PROP Scc_LocalPath ""\n\n')
382 confkeys = self.configs.keys()
384 for kind in confkeys:
385 outdir = self.configs[kind].outdir
386 buildtarget = self.configs[kind].buildtarget
388 self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
391 self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
393 env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
394 if not env_has_buildtarget:
395 self.env['MSVSBUILDTARGET'] = buildtarget
397 # have to write this twice, once with the BASE settings, and once without
398 for base in ("BASE ",""):
399 self.file.write('# PROP %sUse_MFC 0\n'
400 '# PROP %sUse_Debug_Libraries ' % (base, base))
402 #if kind.lower().find('debug') < 0:
403 if string.find(string.lower(kind), 'debug') < 0:
404 self.file.write('0\n')
406 self.file.write('1\n')
407 self.file.write('# PROP %sOutput_Dir "%s"\n'
408 '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir))
409 cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1)
410 self.file.write('# PROP %sCmd_Line "%s"\n'
411 '# PROP %sRebuild_Opt "-c && %s"\n'
412 '# PROP %sTarget_File "%s"\n'
413 '# PROP %sBsc_Name ""\n'
414 '# PROP %sTarget_Dir ""\n'\
415 %(base,cmd,base,cmd,base,buildtarget,base,base))
417 if not env_has_buildtarget:
418 del self.env['MSVSBUILDTARGET']
420 self.file.write('\n!ENDIF\n\n'
421 '# Begin Target\n\n')
422 for kind in confkeys:
423 self.file.write('# Name "%s - Win32 %s"\n' % (name,kind))
424 self.file.write('\n')
426 for kind in confkeys:
428 self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
431 self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
432 self.file.write('!ENDIF \n\n')
433 self.PrintSourceFiles()
434 self.file.write('# End Target\n'
438 # now we pickle some data and add it to the file -- MSDEV will ignore it.
439 pdata = pickle.dumps(self.configs,1)
440 pdata = base64.encodestring(pdata)
441 self.file.write(pdata + '\n')
442 pdata = pickle.dumps(self.sources,1)
443 pdata = base64.encodestring(pdata)
444 self.file.write(pdata + '\n')
446 def PrintSourceFiles(self):
447 categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat',
448 'Header Files': 'h|hpp|hxx|hm|inl',
449 'Local Headers': 'h|hpp|hxx|hm|inl',
450 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe',
453 cats = categories.keys()
455 #cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
456 cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b)))
458 if not self.sources[kind]:
459 continue # skip empty groups
461 self.file.write('# Begin Group "' + kind + '"\n\n')
463 #typelist = categories[kind].replace('|', ';')
464 typelist = string.replace(categories[kind], '|', ';')
465 self.file.write('# PROP Default_Filter "' + typelist + '"\n')
467 for file in self.sources[kind]:
468 file = os.path.normpath(file)
469 self.file.write('# Begin Source File\n\n'
470 'SOURCE="' + file + '"\n'
471 '# End Source File\n')
472 self.file.write('# End Group\n')
474 # add the SConscript file outside of the groups
475 self.file.write('# Begin Source File\n\n'
476 'SOURCE="' + str(self.sconscript) + '"\n'
477 '# End Source File\n')
481 dspfile = open(self.dspabs,'r')
483 return # doesn't exist yet, so can't add anything to configs.
485 line = dspfile.readline()
488 #if line.find("# End Project") > -1:
489 if string.find(line, "# End Project") > -1:
491 line = dspfile.readline()
493 line = dspfile.readline()
495 while line and line != '\n':
496 line = dspfile.readline()
499 # OK, we've found our little pickled cache of data.
501 datas = base64.decodestring(datas)
502 data = pickle.loads(datas)
503 except KeyboardInterrupt:
506 return # unable to unpickle any data for some reason
508 self.configs.update(data)
511 line = dspfile.readline()
513 while line and line != '\n':
514 line = dspfile.readline()
517 # OK, we've found our little pickled cache of data.
518 # it has a "# " in front of it, so we strip that.
520 datas = base64.decodestring(datas)
521 data = pickle.loads(datas)
522 except KeyboardInterrupt:
525 return # unable to unpickle any data for some reason
527 self.sources.update(data)
531 self.file = open(self.dspabs,'w')
532 except IOError, detail:
533 raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail)
540 <?xml version="1.0" encoding = "%(encoding)s"?>
542 \tProjectType="Visual C++"
543 \tVersion="%(versionstr)s"
546 \tKeyword="MakeFileProj">
549 V7DSPConfiguration = """\
551 \t\t\tName="%(variant)s|%(platform)s"
552 \t\t\tOutputDirectory="%(outdir)s"
553 \t\t\tIntermediateDirectory="%(outdir)s"
554 \t\t\tConfigurationType="0"
556 \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
558 \t\t\t\tName="VCNMakeTool"
559 \t\t\t\tBuildCommandLine="%(buildcmd)s"
560 \t\t\t\tCleanCommandLine="%(cleancmd)s"
561 \t\t\t\tRebuildCommandLine="%(rebuildcmd)s"
562 \t\t\t\tOutput="%(runfile)s"/>
567 <?xml version="1.0" encoding="%(encoding)s"?>
569 \tProjectType="Visual C++"
570 \tVersion="%(versionstr)s"
573 \tRootNamespace="%(name)s"
574 \tKeyword="MakeFileProj">
577 V8DSPConfiguration = """\
579 \t\t\tName="%(variant)s|Win32"
580 \t\t\tConfigurationType="0"
582 \t\t\tATLMinimizesCRunTimeLibraryUsage="false"
585 \t\t\t\tName="VCNMakeTool"
586 \t\t\t\tBuildCommandLine="%(buildcmd)s"
587 \t\t\t\tReBuildCommandLine="%(rebuildcmd)s"
588 \t\t\t\tCleanCommandLine="%(cleancmd)s"
589 \t\t\t\tOutput="%(runfile)s"
590 \t\t\t\tPreprocessorDefinitions=""
591 \t\t\t\tIncludeSearchPath=""
592 \t\t\t\tForcedIncludes=""
593 \t\t\t\tAssemblySearchPath=""
594 \t\t\t\tForcedUsingAssemblies=""
595 \t\t\t\tCompileAsManaged=""
599 class _GenerateV7DSP(_DSPGenerator):
600 """Generates a Project file for MSVS .NET"""
602 def __init__(self, dspfile, source, env):
603 _DSPGenerator.__init__(self, dspfile, source, env)
604 self.version = env['MSVS_VERSION']
605 self.version_num, self.suite = msvs_parse_version(self.version)
606 if self.version_num >= 8.0:
607 self.versionstr = '8.00'
608 self.dspheader = V8DSPHeader
609 self.dspconfiguration = V8DSPConfiguration
611 if self.version_num >= 7.1:
612 self.versionstr = '7.10'
614 self.versionstr = '7.00'
615 self.dspheader = V7DSPHeader
616 self.dspconfiguration = V7DSPConfiguration
619 def PrintHeader(self):
621 versionstr = self.versionstr
623 encoding = self.env.subst('$MSVSENCODING')
624 scc_provider = env.get('MSVS_SCC_PROVIDER', '')
625 scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
626 scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
627 scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
628 project_guid = env.get('MSVS_PROJECT_GUID', '')
629 if self.version_num >= 8.0 and not project_guid:
630 project_guid = _generateGUID(self.dspfile, '')
631 if scc_provider != '':
632 scc_attrs = ('\tProjectGUID="%s"\n'
633 '\tSccProjectName="%s"\n'
634 '\tSccAuxPath="%s"\n'
635 '\tSccLocalPath="%s"\n'
636 '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider))
638 scc_attrs = ('\tProjectGUID="%s"\n'
639 '\tSccProjectName="%s"\n'
640 '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path))
642 self.file.write(self.dspheader % locals())
644 self.file.write('\t<Platforms>\n')
645 for platform in self.platforms:
648 '\t\t\tName="%s"/>\n' % platform)
649 self.file.write('\t</Platforms>\n')
651 if self.version_num >= 8.0:
652 self.file.write('\t<ToolFiles>\n'
655 def PrintProject(self):
656 self.file.write('\t<Configurations>\n')
658 confkeys = self.configs.keys()
660 for kind in confkeys:
661 variant = self.configs[kind].variant
662 platform = self.configs[kind].platform
663 outdir = self.configs[kind].outdir
664 buildtarget = self.configs[kind].buildtarget
665 runfile = self.configs[kind].runfile
666 cmdargs = self.configs[kind].cmdargs
668 env_has_buildtarget = self.env.has_key('MSVSBUILDTARGET')
669 if not env_has_buildtarget:
670 self.env['MSVSBUILDTARGET'] = buildtarget
672 starting = 'echo Starting SCons && '
674 cmdargs = ' ' + cmdargs
677 buildcmd = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs)
678 rebuildcmd = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs)
679 cleancmd = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs)
681 if not env_has_buildtarget:
682 del self.env['MSVSBUILDTARGET']
684 self.file.write(self.dspconfiguration % locals())
686 self.file.write('\t</Configurations>\n')
688 if self.version_num >= 7.1:
689 self.file.write('\t<References>\n'
692 self.PrintSourceFiles()
694 self.file.write('</VisualStudioProject>\n')
697 # now we pickle some data and add it to the file -- MSDEV will ignore it.
698 pdata = pickle.dumps(self.configs,1)
699 pdata = base64.encodestring(pdata)
700 self.file.write('<!-- SCons Data:\n' + pdata + '\n')
701 pdata = pickle.dumps(self.sources,1)
702 pdata = base64.encodestring(pdata)
703 self.file.write(pdata + '-->\n')
705 def printSources(self, hierarchy, commonprefix):
706 sorteditems = hierarchy.items()
708 #sorteditems.sort(lambda a, b: cmp(a[0].lower(), b[0].lower()))
709 sorteditems.sort(lambda a, b: cmp(string.lower(a[0]), string.lower(b[0])))
711 # First folders, then files
712 for key, value in sorteditems:
713 if SCons.Util.is_Dict(value):
714 self.file.write('\t\t\t<Filter\n'
715 '\t\t\t\tName="%s"\n'
716 '\t\t\t\tFilter="">\n' % (key))
717 self.printSources(value, commonprefix)
718 self.file.write('\t\t\t</Filter>\n')
720 for key, value in sorteditems:
721 if SCons.Util.is_String(value):
724 file = os.path.join(commonprefix, value)
725 file = os.path.normpath(file)
726 self.file.write('\t\t\t<File\n'
727 '\t\t\t\tRelativePath="%s">\n'
728 '\t\t\t</File>\n' % (file))
730 def PrintSourceFiles(self):
731 categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
732 'Header Files': 'h;hpp;hxx;hm;inl',
733 'Local Headers': 'h;hpp;hxx;hm;inl',
734 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe',
737 self.file.write('\t<Files>\n')
739 cats = categories.keys()
741 #cats.sort(lambda a, b: cmp(a.lower(), b.lower()))
742 cats.sort(lambda a, b: cmp(string.lower(a), string.lower(b)))
743 cats = filter(lambda k, s=self: s.sources[k], cats)
746 self.file.write('\t\t<Filter\n'
748 '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
750 sources = self.sources[kind]
752 # First remove any common prefix
755 s = map(os.path.normpath, sources)
756 # take the dirname because the prefix may include parts
757 # of the filenames (e.g. if you have 'dir\abcd' and
758 # 'dir\acde' then the cp will be 'dir\a' )
759 cp = os.path.dirname( os.path.commonprefix(s) )
760 if cp and s[0][len(cp)] == os.sep:
761 # +1 because the filename starts after the separator
762 sources = map(lambda s, l=len(cp)+1: s[l:], sources)
764 elif len(sources) == 1:
765 commonprefix = os.path.dirname( sources[0] )
766 sources[0] = os.path.basename( sources[0] )
768 hierarchy = makeHierarchy(sources)
769 self.printSources(hierarchy, commonprefix=commonprefix)
772 self.file.write('\t\t</Filter>\n')
774 # add the SConscript file outside of the groups
775 self.file.write('\t\t<File\n'
776 '\t\t\tRelativePath="%s">\n'
777 '\t\t</File>\n' % str(self.sconscript))
779 self.file.write('\t</Files>\n'
785 dspfile = open(self.dspabs,'r')
787 return # doesn't exist yet, so can't add anything to configs.
789 line = dspfile.readline()
792 #if line.find('<!-- SCons Data:') > -1:
793 if string.find(line, '<!-- SCons Data:') > -1:
795 line = dspfile.readline()
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.configs.update(data)
815 line = dspfile.readline()
817 while line and line != '\n':
818 line = dspfile.readline()
821 # OK, we've found our little pickled cache of data.
823 datas = base64.decodestring(datas)
824 data = pickle.loads(datas)
825 except KeyboardInterrupt:
828 return # unable to unpickle any data for some reason
830 self.sources.update(data)
834 self.file = open(self.dspabs,'w')
835 except IOError, detail:
836 raise SCons.Errors.InternalError, 'Unable to open "' + self.dspabs + '" for writing:' + str(detail)
843 """ Base class for DSW generators """
844 def __init__(self, dswfile, source, env):
845 self.dswfile = os.path.normpath(str(dswfile))
848 if not env.has_key('projects'):
849 raise SCons.Errors.UserError, \
850 "You must specify a 'projects' argument to create an MSVSSolution."
851 projects = env['projects']
852 if not SCons.Util.is_List(projects):
853 raise SCons.Errors.InternalError, \
854 "The 'projects' argument must be a list of nodes."
855 projects = SCons.Util.flatten(projects)
856 if len(projects) < 1:
857 raise SCons.Errors.UserError, \
858 "You must specify at least one project to create an MSVSSolution."
859 self.dspfiles = map(str, projects)
861 if self.env.has_key('name'):
862 self.name = self.env['name']
864 self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0])
865 self.name = self.env.subst(self.name)
870 class _GenerateV7DSW(_DSWGenerator):
871 """Generates a Solution file for MSVS .NET"""
872 def __init__(self, dswfile, source, env):
873 _DSWGenerator.__init__(self, dswfile, source, env)
876 self.version = self.env['MSVS_VERSION']
877 self.version_num, self.suite = msvs_parse_version(self.version)
878 self.versionstr = '7.00'
879 if self.version_num >= 8.0:
880 self.versionstr = '9.00'
881 elif self.version_num >= 7.1:
882 self.versionstr = '8.00'
883 if self.version_num >= 8.0:
884 self.versionstr = '9.00'
886 if env.has_key('slnguid') and env['slnguid']:
887 self.slnguid = env['slnguid']
889 self.slnguid = _generateGUID(dswfile, self.name)
894 if env.has_key('nokeep') and env['variant'] != 0:
897 if self.nokeep == 0 and os.path.exists(self.dswfile):
900 def AddConfig(self, variant, dswfile=dswfile):
903 match = re.match('(.*)\|(.*)', variant)
905 config.variant = match.group(1)
906 config.platform = match.group(2)
908 config.variant = variant
909 config.platform = 'Win32'
911 self.configs[variant] = config
912 print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'"
914 if not env.has_key('variant'):
915 raise SCons.Errors.InternalError, \
916 "You must specify a 'variant' argument (i.e. 'Debug' or " +\
917 "'Release') to create an MSVS Solution File."
918 elif SCons.Util.is_String(env['variant']):
919 AddConfig(self, env['variant'])
920 elif SCons.Util.is_List(env['variant']):
921 for variant in env['variant']:
922 AddConfig(self, variant)
925 for key in self.configs.keys():
926 platform = self.configs[key].platform
927 if not platform in self.platforms:
928 self.platforms.append(platform)
932 dswfile = open(self.dswfile,'r')
934 return # doesn't exist yet, so can't add anything to configs.
936 line = dswfile.readline()
938 if line[:9] == "EndGlobal":
940 line = dswfile.readline()
942 line = dswfile.readline()
945 line = dswfile.readline()
948 # OK, we've found our little pickled cache of data.
950 datas = base64.decodestring(datas)
951 data = pickle.loads(datas)
952 except KeyboardInterrupt:
955 return # unable to unpickle any data for some reason
957 self.configs.update(data)
959 def PrintSolution(self):
960 """Writes a solution file"""
961 self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr )
962 if self.version_num >= 8.0:
963 self.file.write('# Visual Studio 2005\n')
964 for p in self.dspfiles:
965 name = os.path.basename(p)
966 base, suffix = SCons.Util.splitext(name)
967 if suffix == '.vcproj':
969 guid = _generateGUID(p, '')
970 self.file.write('Project("%s") = "%s", "%s", "%s"\n'
971 % ( external_makefile_guid, name, p, guid ) )
972 if self.version_num >= 7.1 and self.version_num < 8.0:
973 self.file.write('\tProjectSection(ProjectDependencies) = postProject\n'
974 '\tEndProjectSection\n')
975 self.file.write('EndProject\n')
977 self.file.write('Global\n')
980 if env.has_key('MSVS_SCC_PROVIDER'):
981 dspfile_base = os.path.basename(self.dspfile)
982 slnguid = self.slnguid
983 scc_provider = env.get('MSVS_SCC_PROVIDER', '')
984 scc_provider = string.replace(scc_provider, ' ', r'\u0020')
985 scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
986 # scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
987 scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
988 scc_project_base_path = env.get('MSVS_SCC_PROJECT_BASE_PATH', '')
989 # project_guid = env.get('MSVS_PROJECT_GUID', '')
991 self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n'
992 '\t\tSccNumberOfProjects = 2\n'
993 '\t\tSccProjectUniqueName0 = %(dspfile_base)s\n'
994 '\t\tSccLocalPath0 = %(scc_local_path)s\n'
995 '\t\tCanCheckoutShared = true\n'
996 '\t\tSccProjectFilePathRelativizedFromConnection0 = %(scc_project_base_path)s\n'
997 '\t\tSccProjectName1 = %(scc_project_name)s\n'
998 '\t\tSccLocalPath1 = %(scc_local_path)s\n'
999 '\t\tSccProvider1 = %(scc_provider)s\n'
1000 '\t\tCanCheckoutShared = true\n'
1001 '\t\tSccProjectFilePathRelativizedFromConnection1 = %(scc_project_base_path)s\n'
1002 '\t\tSolutionUniqueID = %(slnguid)s\n'
1003 '\tEndGlobalSection\n' % locals())
1005 if self.version_num >= 8.0:
1006 self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
1008 self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n')
1010 confkeys = self.configs.keys()
1013 for name in confkeys:
1014 variant = self.configs[name].variant
1015 platform = self.configs[name].platform
1016 if self.version_num >= 8.0:
1017 self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform))
1019 self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant))
1021 self.file.write('\tEndGlobalSection\n')
1022 if self.version_num < 7.1:
1023 self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n'
1024 '\tEndGlobalSection\n')
1025 if self.version_num >= 8.0:
1026 self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
1028 self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
1030 for name in confkeys:
1031 variant = self.configs[name].variant
1032 platform = self.configs[name].platform
1033 if self.version_num >= 8.0:
1034 for p in self.dspfiles:
1035 guid = _generateGUID(p, '')
1036 self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n'
1037 '\t\t%s.%s|%s.Build.0 = %s|%s\n' % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform))
1039 for p in self.dspfiles:
1040 guid = _generateGUID(p, '')
1041 self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
1042 '\t\t%s.%s.Build.0 = %s|%s\n' %(guid,variant,variant,platform,guid,variant,variant,platform))
1044 self.file.write('\tEndGlobalSection\n')
1046 if self.version_num >= 8.0:
1047 self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n'
1048 '\t\tHideSolutionNode = FALSE\n'
1049 '\tEndGlobalSection\n')
1051 self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
1052 '\tEndGlobalSection\n'
1053 '\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
1054 '\tEndGlobalSection\n')
1055 self.file.write('EndGlobal\n')
1056 if self.nokeep == 0:
1057 pdata = pickle.dumps(self.configs,1)
1058 pdata = base64.encodestring(pdata)
1059 self.file.write(pdata + '\n')
1063 self.file = open(self.dswfile,'w')
1064 except IOError, detail:
1065 raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
1067 self.PrintSolution()
1071 Microsoft Developer Studio Workspace File, Format Version 6.00
1072 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
1074 ###############################################################################
1076 Project: "%(name)s"="%(dspfile)s" - Package Owner=<4>
1086 ###############################################################################
1098 ###############################################################################
1101 class _GenerateV6DSW(_DSWGenerator):
1102 """Generates a Workspace file for MSVS 6.0"""
1104 def PrintWorkspace(self):
1105 """ writes a DSW file """
1107 dspfile = self.dspfiles[0]
1108 self.file.write(V6DSWHeader % locals())
1112 self.file = open(self.dswfile,'w')
1113 except IOError, detail:
1114 raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail)
1116 self.PrintWorkspace()
1120 def GenerateDSP(dspfile, source, env):
1121 """Generates a Project file based on the version of MSVS that is being used"""
1124 if env.has_key('MSVS_VERSION'):
1125 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1126 if version_num >= 7.0:
1127 g = _GenerateV7DSP(dspfile, source, env)
1130 g = _GenerateV6DSP(dspfile, source, env)
1133 def GenerateDSW(dswfile, source, env):
1134 """Generates a Solution/Workspace file based on the version of MSVS that is being used"""
1137 if env.has_key('MSVS_VERSION'):
1138 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1139 if version_num >= 7.0:
1140 g = _GenerateV7DSW(dswfile, source, env)
1143 g = _GenerateV6DSW(dswfile, source, env)
1147 ##############################################################################
1148 # Above here are the classes and functions for generation of
1149 # DSP/DSW/SLN/VCPROJ files.
1150 ##############################################################################
1152 def get_default_visualstudio_version(env):
1153 """Returns the version set in the env, or the latest version
1154 installed, if it can find it, or '6.0' if all else fails. Also
1155 updates the environment with what it found."""
1159 if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']):
1160 v = get_visualstudio_versions()
1163 env['MSVS'] = {'VERSIONS' : versions}
1165 versions = env['MSVS'].get('VERSIONS', versions)
1167 if not env.has_key('MSVS_VERSION'):
1168 env['MSVS_VERSION'] = versions[0] #use highest version by default
1170 env['MSVS']['VERSION'] = env['MSVS_VERSION']
1172 return env['MSVS_VERSION']
1174 def get_visualstudio_versions():
1176 Get list of visualstudio versions from the Windows registry.
1177 Returns a list of strings containing version numbers. An empty list
1178 is returned if we were unable to accees the register (for example,
1179 we couldn't import the registry-access module) or the appropriate
1180 registry keys weren't found.
1183 if not SCons.Util.can_read_reg:
1186 HLM = SCons.Util.HKEY_LOCAL_MACHINE
1188 r'Software\Microsoft\VisualStudio' : '',
1189 r'Software\Microsoft\VCExpress' : 'Exp',
1192 for K, suite_suffix in KEYS.items():
1194 k = SCons.Util.RegOpenKeyEx(HLM, K)
1198 p = SCons.Util.RegEnumKey(k,i)
1199 except SCons.Util.RegError:
1202 if not p[0] in '123456789' or p in L:
1204 # Only add this version number if there is a valid
1205 # registry structure (includes the "Setup" key),
1206 # and at least some of the correct directories
1207 # exist. Sometimes VS uninstall leaves around
1208 # some registry/filesystem turds that we don't
1209 # want to trip over. Also, some valid registry
1210 # entries are MSDN entries, not MSVS ('7.1',
1211 # notably), and we want to skip those too.
1213 SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p + '\\Setup')
1214 except SCons.Util.RegError:
1218 idk = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p)
1219 # This is not always here -- it only exists if the
1220 # user installed into a non-standard location (at
1221 # least in VS6 it works that way -- VS7 seems to
1224 id = SCons.Util.RegQueryValueEx(idk, 'InstallDir')
1225 except SCons.Util.RegError:
1228 # If the InstallDir key doesn't exist,
1229 # then we check the default locations.
1230 # Note: The IDE's executable is not devenv.exe for VS8 Express.
1231 if not id or not id[0]:
1232 files_dir = SCons.Platform.win32.get_program_files_dir()
1233 version_num, suite = msvs_parse_version(p)
1234 if version_num < 7.0:
1235 vs = r'Microsoft Visual Studio\Common\MSDev98'
1236 elif version_num < 8.0:
1237 vs = r'Microsoft Visual Studio .NET\Common7\IDE'
1239 vs = r'Microsoft Visual Studio 8\Common7\IDE'
1240 id = [ os.path.join(files_dir, vs) ]
1241 if os.path.exists(id[0]):
1242 L.append(p + suite_suffix)
1243 except SCons.Util.RegError:
1249 # This is a hack to get around the fact that certain Visual Studio
1250 # patches place a "6.1" version in the registry, which does not have
1251 # any of the keys we need to find include paths, install directories,
1252 # etc. Therefore we ignore it if it is there, since it throws all
1264 def get_default_visualstudio8_suite(env):
1266 Returns the Visual Studio 2005 suite identifier set in the env, or the
1267 highest suite installed.
1269 if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']):
1272 if env.has_key('MSVS_SUITE'):
1274 #suite = env['MSVS_SUITE'].upper()
1275 suite = string.upper(env['MSVS_SUITE'])
1280 if SCons.Util.can_read_reg:
1281 suites = get_visualstudio8_suites()
1283 suite = suites[0] #use best suite by default
1285 env['MSVS_SUITE'] = suite
1286 env['MSVS']['SUITES'] = suites
1287 env['MSVS']['SUITE'] = suite
1291 def get_visualstudio8_suites():
1293 Returns a sorted list of all installed Visual Studio 2005 suites found
1294 in the registry. The highest version should be the first entry in the list.
1299 # Detect Standard, Professional and Team edition
1301 idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1302 r'Software\Microsoft\VisualStudio\8.0')
1303 SCons.Util.RegQueryValueEx(idk, 'InstallDir')
1304 editions = { 'PRO': r'Setup\VS\Pro' } # ToDo: add standard and team editions
1305 edition_name = 'STD'
1306 for name, key_suffix in editions.items():
1308 idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1309 r'Software\Microsoft\VisualStudio\8.0' + '\\' + key_suffix )
1311 except SCons.Util.RegError:
1313 suites.append(edition_name)
1314 except SCons.Util.RegError:
1317 # Detect Express edition
1319 idk = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,
1320 r'Software\Microsoft\VCExpress\8.0')
1321 SCons.Util.RegQueryValueEx(idk, 'InstallDir')
1322 suites.append('EXPRESS')
1323 except SCons.Util.RegError:
1328 def is_msvs_installed():
1330 Check the registry for an installed visual studio.
1333 v = SCons.Tool.msvs.get_visualstudio_versions()
1335 except (SCons.Util.RegError, SCons.Errors.InternalError):
1338 def get_msvs_install_dirs(version = None, vs8suite = None):
1340 Get installed locations for various msvc-related products, like the .NET SDK
1341 and the Platform SDK.
1344 if not SCons.Util.can_read_reg:
1348 versions = get_visualstudio_versions()
1350 version = versions[0] #use highest version by default
1354 version_num, suite = msvs_parse_version(version)
1356 K = 'Software\\Microsoft\\VisualStudio\\' + str(version_num)
1357 if (version_num >= 8.0):
1358 if vs8suite == None:
1359 # We've been given no guidance about which Visual Studio 8
1360 # suite to use, so attempt to autodetect.
1361 suites = get_visualstudio8_suites()
1363 vs8suite = suites[0]
1365 if vs8suite == 'EXPRESS':
1366 K = 'Software\\Microsoft\\VCExpress\\' + str(version_num)
1370 if (version_num < 7.0):
1371 key = K + r'\Setup\Microsoft Visual C++\ProductDir'
1373 key = K + r'\Setup\VC\ProductDir'
1375 (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, key)
1376 except SCons.Util.RegError:
1379 # visual studio install dir
1380 if (version_num < 7.0):
1382 (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1383 K + r'\Setup\Microsoft Visual Studio\ProductDir')
1384 except SCons.Util.RegError:
1387 if not rv.has_key('VSINSTALLDIR') or not rv['VSINSTALLDIR']:
1388 if rv.has_key('VCINSTALLDIR') and rv['VCINSTALLDIR']:
1389 rv['VSINSTALLDIR'] = os.path.dirname(rv['VCINSTALLDIR'])
1391 rv['VSINSTALLDIR'] = os.path.join(SCons.Platform.win32.get_program_files_dir(),'Microsoft Visual Studio')
1394 (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1395 K + r'\Setup\VS\ProductDir')
1396 except SCons.Util.RegError:
1399 # .NET framework install dir
1401 (rv['FRAMEWORKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1402 r'Software\Microsoft\.NETFramework\InstallRoot')
1403 except SCons.Util.RegError:
1406 if rv.has_key('FRAMEWORKDIR'):
1407 # try and enumerate the installed versions of the .NET framework.
1408 contents = os.listdir(rv['FRAMEWORKDIR'])
1409 l = re.compile('v[0-9]+.*')
1410 installed_framework_versions = filter(lambda e, l=l: l.match(e), contents)
1413 # since version numbers aren't really floats...
1416 aal = string.split(aa, '.')
1417 bbl = string.split(bb, '.')
1418 # sequence comparison in python is lexicographical
1419 # which is exactly what we want.
1420 # Note we sort backwards so the highest version is first.
1423 installed_framework_versions.sort(versrt)
1425 rv['FRAMEWORKVERSIONS'] = installed_framework_versions
1427 # TODO: allow a specific framework version to be set
1429 # Choose a default framework version based on the Visual
1431 DefaultFrameworkVersionMap = {
1435 # TODO: Does .NET 3.0 need to be worked into here somewhere?
1438 default_framework_version = DefaultFrameworkVersionMap[version[:3]]
1439 except (KeyError, TypeError):
1442 # Look for the first installed directory in FRAMEWORKDIR that
1443 # begins with the framework version string that's appropriate
1444 # for the Visual Studio version we're using.
1445 for v in installed_framework_versions:
1446 if v[:4] == default_framework_version:
1447 rv['FRAMEWORKVERSION'] = v
1450 # If the framework version couldn't be worked out by the previous
1451 # code then fall back to using the latest version of the .NET
1453 if not rv.has_key('FRAMEWORKVERSION'):
1454 rv['FRAMEWORKVERSION'] = installed_framework_versions[0]
1456 # .NET framework SDK install dir
1457 if rv.has_key('FRAMEWORKVERSION'):
1458 # The .NET SDK version used must match the .NET version used,
1459 # so we deliberately don't fall back to other .NET framework SDK
1460 # versions that might be present.
1461 ver = rv['FRAMEWORKVERSION'][:4]
1462 key = r'Software\Microsoft\.NETFramework\sdkInstallRoot' + ver
1464 (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1466 except SCons.Util.RegError:
1469 # MS Platform SDK dir
1471 (rv['PLATFORMSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE,
1472 r'Software\Microsoft\MicrosoftSDK\Directories\Install Dir')
1473 except SCons.Util.RegError:
1476 if rv.has_key('PLATFORMSDKDIR'):
1477 # if we have a platform SDK, try and get some info on it.
1480 loc = r'Software\Microsoft\MicrosoftSDK\InstalledSDKs'
1481 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,loc)
1485 key = SCons.Util.RegEnumKey(k,i)
1486 sdk = SCons.Util.RegOpenKeyEx(k,key)
1493 (vk,vv,t) = SCons.Util.RegEnumValue(sdk,j)
1495 #if vk.lower() == 'keyword':
1497 #if vk.lower() == 'propagation_date':
1499 #if vk.lower() == 'version':
1501 if string.lower(vk) == 'keyword':
1503 if string.lower(vk) == 'propagation_date':
1505 if string.lower(vk) == 'version':
1508 except SCons.Util.RegError:
1511 vers[name] = (date, version)
1513 except SCons.Util.RegError:
1515 rv['PLATFORMSDK_MODULES'] = vers
1516 except SCons.Util.RegError:
1521 def GetMSVSProjectSuffix(target, source, env, for_signature):
1522 return env['MSVS']['PROJECTSUFFIX']
1524 def GetMSVSSolutionSuffix(target, source, env, for_signature):
1525 return env['MSVS']['SOLUTIONSUFFIX']
1527 def GenerateProject(target, source, env):
1528 # generate the dsp file, according to the version of MSVS.
1529 builddspfile = target[0]
1530 dspfile = builddspfile.srcnode()
1532 # this detects whether or not we're using a VariantDir
1533 if not dspfile is builddspfile:
1535 bdsp = open(str(builddspfile), "w+")
1536 except IOError, detail:
1537 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1540 bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
1542 GenerateDSP(dspfile, source, env)
1544 if env.get('auto_build_solution', 1):
1545 builddswfile = target[1]
1546 dswfile = builddswfile.srcnode()
1548 if not dswfile is builddswfile:
1551 bdsw = open(str(builddswfile), "w+")
1552 except IOError, detail:
1553 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1556 bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
1558 GenerateDSW(dswfile, source, env)
1560 def GenerateSolution(target, source, env):
1561 GenerateDSW(target[0], source, env)
1563 def projectEmitter(target, source, env):
1564 """Sets up the DSP dependencies."""
1566 # todo: Not sure what sets source to what user has passed as target,
1567 # but this is what happens. When that is fixed, we also won't have
1568 # to make the user always append env['MSVSPROJECTSUFFIX'] to target.
1569 if source[0] == target[0]:
1572 # make sure the suffix is correct for the version of MSVS we're running.
1573 (base, suff) = SCons.Util.splitext(str(target[0]))
1574 suff = env.subst('$MSVSPROJECTSUFFIX')
1575 target[0] = base + suff
1578 source = 'prj_inputs:'
1579 source = source + env.subst('$MSVSSCONSCOM', 1)
1580 source = source + env.subst('$MSVSENCODING', 1)
1582 if env.has_key('buildtarget') and env['buildtarget'] != None:
1583 if SCons.Util.is_String(env['buildtarget']):
1584 source = source + ' "%s"' % env['buildtarget']
1585 elif SCons.Util.is_List(env['buildtarget']):
1586 for bt in env['buildtarget']:
1587 if SCons.Util.is_String(bt):
1588 source = source + ' "%s"' % bt
1590 try: source = source + ' "%s"' % bt.get_abspath()
1591 except AttributeError: raise SCons.Errors.InternalError, \
1592 "buildtarget can be a string, a node, a list of strings or nodes, or None"
1594 try: source = source + ' "%s"' % env['buildtarget'].get_abspath()
1595 except AttributeError: raise SCons.Errors.InternalError, \
1596 "buildtarget can be a string, a node, a list of strings or nodes, or None"
1598 if env.has_key('outdir') and env['outdir'] != None:
1599 if SCons.Util.is_String(env['outdir']):
1600 source = source + ' "%s"' % env['outdir']
1601 elif SCons.Util.is_List(env['outdir']):
1602 for s in env['outdir']:
1603 if SCons.Util.is_String(s):
1604 source = source + ' "%s"' % s
1606 try: source = source + ' "%s"' % s.get_abspath()
1607 except AttributeError: raise SCons.Errors.InternalError, \
1608 "outdir can be a string, a node, a list of strings or nodes, or None"
1610 try: source = source + ' "%s"' % env['outdir'].get_abspath()
1611 except AttributeError: raise SCons.Errors.InternalError, \
1612 "outdir can be a string, a node, a list of strings or nodes, or None"
1614 if env.has_key('name'):
1615 if SCons.Util.is_String(env['name']):
1616 source = source + ' "%s"' % env['name']
1618 raise SCons.Errors.InternalError, "name must be a string"
1620 if env.has_key('variant'):
1621 if SCons.Util.is_String(env['variant']):
1622 source = source + ' "%s"' % env['variant']
1623 elif SCons.Util.is_List(env['variant']):
1624 for variant in env['variant']:
1625 if SCons.Util.is_String(variant):
1626 source = source + ' "%s"' % variant
1628 raise SCons.Errors.InternalError, "name must be a string or a list of strings"
1630 raise SCons.Errors.InternalError, "variant must be a string or a list of strings"
1632 raise SCons.Errors.InternalError, "variant must be specified"
1634 for s in _DSPGenerator.srcargs:
1636 if SCons.Util.is_String(env[s]):
1637 source = source + ' "%s' % env[s]
1638 elif SCons.Util.is_List(env[s]):
1640 if SCons.Util.is_String(t):
1641 source = source + ' "%s"' % t
1643 raise SCons.Errors.InternalError, s + " must be a string or a list of strings"
1645 raise SCons.Errors.InternalError, s + " must be a string or a list of strings"
1647 source = source + ' "%s"' % str(target[0])
1648 source = [SCons.Node.Python.Value(source)]
1650 targetlist = [target[0]]
1653 if env.get('auto_build_solution', 1):
1654 env['projects'] = targetlist
1655 t, s = solutionEmitter(target, target, env)
1656 targetlist = targetlist + t
1658 return (targetlist, sourcelist)
1660 def solutionEmitter(target, source, env):
1661 """Sets up the DSW dependencies."""
1663 # todo: Not sure what sets source to what user has passed as target,
1664 # but this is what happens. When that is fixed, we also won't have
1665 # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target.
1666 if source[0] == target[0]:
1669 # make sure the suffix is correct for the version of MSVS we're running.
1670 (base, suff) = SCons.Util.splitext(str(target[0]))
1671 suff = env.subst('$MSVSSOLUTIONSUFFIX')
1672 target[0] = base + suff
1675 source = 'sln_inputs:'
1677 if env.has_key('name'):
1678 if SCons.Util.is_String(env['name']):
1679 source = source + ' "%s"' % env['name']
1681 raise SCons.Errors.InternalError, "name must be a string"
1683 if env.has_key('variant'):
1684 if SCons.Util.is_String(env['variant']):
1685 source = source + ' "%s"' % env['variant']
1686 elif SCons.Util.is_List(env['variant']):
1687 for variant in env['variant']:
1688 if SCons.Util.is_String(variant):
1689 source = source + ' "%s"' % variant
1691 raise SCons.Errors.InternalError, "name must be a string or a list of strings"
1693 raise SCons.Errors.InternalError, "variant must be a string or a list of strings"
1695 raise SCons.Errors.InternalError, "variant must be specified"
1697 if env.has_key('slnguid'):
1698 if SCons.Util.is_String(env['slnguid']):
1699 source = source + ' "%s"' % env['slnguid']
1701 raise SCons.Errors.InternalError, "slnguid must be a string"
1703 if env.has_key('projects'):
1704 if SCons.Util.is_String(env['projects']):
1705 source = source + ' "%s"' % env['projects']
1706 elif SCons.Util.is_List(env['projects']):
1707 for t in env['projects']:
1708 if SCons.Util.is_String(t):
1709 source = source + ' "%s"' % t
1711 source = source + ' "%s"' % str(target[0])
1712 source = [SCons.Node.Python.Value(source)]
1714 return ([target[0]], source)
1716 projectAction = SCons.Action.Action(GenerateProject, None)
1718 solutionAction = SCons.Action.Action(GenerateSolution, None)
1720 projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
1721 suffix = '$MSVSPROJECTSUFFIX',
1722 emitter = projectEmitter)
1724 solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM',
1725 suffix = '$MSVSSOLUTIONSUFFIX',
1726 emitter = solutionEmitter)
1728 default_MSVS_SConscript = None
1731 """Add Builders and construction variables for Microsoft Visual
1732 Studio project files to an Environment."""
1734 env['BUILDERS']['MSVSProject']
1736 env['BUILDERS']['MSVSProject'] = projectBuilder
1739 env['BUILDERS']['MSVSSolution']
1741 env['BUILDERS']['MSVSSolution'] = solutionBuilder
1743 env['MSVSPROJECTCOM'] = projectAction
1744 env['MSVSSOLUTIONCOM'] = solutionAction
1746 if SCons.Script.call_stack:
1747 # XXX Need to find a way to abstract this; the build engine
1748 # shouldn't depend on anything in SCons.Script.
1749 env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript
1751 global default_MSVS_SConscript
1752 if default_MSVS_SConscript is None:
1753 default_MSVS_SConscript = env.File('SConstruct')
1754 env['MSVSSCONSCRIPT'] = default_MSVS_SConscript
1756 env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env))
1757 env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}'
1758 env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS'
1759 env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
1760 env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
1761 env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"'
1762 env['MSVSENCODING'] = 'Windows-1252'
1765 version = get_default_visualstudio_version(env)
1766 # keep a record of some of the MSVS info so the user can use it.
1767 dirs = get_msvs_install_dirs(version)
1768 env['MSVS'].update(dirs)
1769 except (SCons.Util.RegError, SCons.Errors.InternalError):
1770 # we don't care if we can't do this -- if we can't, it's
1771 # because we don't have access to the registry, or because the
1772 # tools aren't installed. In either case, the user will have to
1773 # find them on their own.
1776 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1777 if (version_num < 7.0):
1778 env['MSVS']['PROJECTSUFFIX'] = '.dsp'
1779 env['MSVS']['SOLUTIONSUFFIX'] = '.dsw'
1781 env['MSVS']['PROJECTSUFFIX'] = '.vcproj'
1782 env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
1784 env['GET_MSVSPROJECTSUFFIX'] = GetMSVSProjectSuffix
1785 env['GET_MSVSSOLUTIONSUFFIX'] = GetMSVSSolutionSuffix
1786 env['MSVSPROJECTSUFFIX'] = '${GET_MSVSPROJECTSUFFIX}'
1787 env['MSVSSOLUTIONSUFFIX'] = '${GET_MSVSSOLUTIONSUFFIX}'
1788 env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
1791 if not env['PLATFORM'] in ('win32', 'cygwin'):
1795 v = SCons.Tool.msvs.get_visualstudio_versions()
1796 except (SCons.Util.RegError, SCons.Errors.InternalError):
1801 if env.has_key('MSVS_VERSION'):
1802 version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1803 if version_num >= 7.0:
1804 # The executable is 'devenv' in Visual Studio Pro,
1805 # Team System and others. Express Editions have different
1806 # executable names. Right now we're only going to worry
1807 # about Visual C++ 2005 Express Edition.
1808 return env.Detect('devenv') or env.Detect('vcexpress')
1810 return env.Detect('msdev')
1812 # there's at least one version of MSVS installed.