Tweak svn/git ignores
[zxing.git] / cpp / scons / scons-local-2.0.0.final.0 / SCons / Tool / msvs.py
1 """SCons.Tool.msvs
2
3 Tool-specific initialization for Microsoft Visual Studio project files.
4
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()
7 selection method.
8
9 """
10
11 #
12 # Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 The SCons Foundation
13 #
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:
21 #
22 # The above copyright notice and this permission notice shall be included
23 # in all copies or substantial portions of the Software.
24 #
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.
32
33 __revision__ = "src/engine/SCons/Tool/msvs.py 5023 2010/06/14 22:05:46 scons"
34
35 import SCons.compat
36
37 import base64
38 import hashlib
39 import ntpath
40 import os
41 # compat layer imports "cPickle" for us if it's available.
42 import pickle
43 import re
44 import sys
45
46 import SCons.Builder
47 import SCons.Node.FS
48 import SCons.Platform.win32
49 import SCons.Script.SConscript
50 import SCons.Util
51 import SCons.Warnings
52
53 from MSCommon import msvc_exists, msvc_setup_env_once
54 from SCons.Defaults import processDefines
55
56 ##############################################################################
57 # Below here are the classes and functions for generation of
58 # DSP/DSW/SLN/VCPROJ files.
59 ##############################################################################
60
61 def xmlify(s):
62     s = s.replace("&", "&") # do this first
63     s = s.replace("'", "'")
64     s = s.replace('"', """)
65     return s
66
67 external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}'
68
69 def _generateGUID(slnfile, name):
70     """This generates a dummy GUID for the sln file to use.  It is
71     based on the MD5 signatures of the sln filename plus the name of
72     the project.  It basically just needs to be unique, and not
73     change with each invocation."""
74     m = hashlib.md5()
75     # Normalize the slnfile path to a Windows path (\ separators) so
76     # the generated file has a consistent GUID even if we generate
77     # it on a non-Windows platform.
78     m.update(ntpath.normpath(str(slnfile)) + str(name))
79     solution = m.hexdigest().upper()
80     # convert most of the signature to GUID form (discard the rest)
81     solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:20] + "-" + solution[20:32] + "}"
82     return solution
83
84 version_re = re.compile(r'(\d+\.\d+)(.*)')
85
86 def msvs_parse_version(s):
87     """
88     Split a Visual Studio version, which may in fact be something like
89     '7.0Exp', into is version number (returned as a float) and trailing
90     "suite" portion.
91     """
92     num, suite = version_re.match(s).groups()
93     return float(num), suite
94
95 # This is how we re-invoke SCons from inside MSVS Project files.
96 # The problem is that we might have been invoked as either scons.bat
97 # or scons.py.  If we were invoked directly as scons.py, then we could
98 # use sys.argv[0] to find the SCons "executable," but that doesn't work
99 # if we were invoked as scons.bat, which uses "python -c" to execute
100 # things and ends up with "-c" as sys.argv[0].  Consequently, we have
101 # the MSVS Project file invoke SCons the same way that scons.bat does,
102 # which works regardless of how we were invoked.
103 def getExecScriptMain(env, xml=None):
104     scons_home = env.get('SCONS_HOME')
105     if not scons_home and 'SCONS_LIB_DIR' in os.environ:
106         scons_home = os.environ['SCONS_LIB_DIR']
107     if scons_home:
108         exec_script_main = "from os.path import join; import sys; sys.path = [ r'%s' ] + sys.path; import SCons.Script; SCons.Script.main()" % scons_home
109     else:
110         version = SCons.__version__
111         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()
112     if xml:
113         exec_script_main = xmlify(exec_script_main)
114     return exec_script_main
115
116 # The string for the Python executable we tell the Project file to use
117 # is either sys.executable or, if an external PYTHON_ROOT environment
118 # variable exists, $(PYTHON)ROOT\\python.exe (generalized a little to
119 # pluck the actual executable name from sys.executable).
120 try:
121     python_root = os.environ['PYTHON_ROOT']
122 except KeyError:
123     python_executable = sys.executable
124 else:
125     python_executable = os.path.join('$$(PYTHON_ROOT)',
126                                      os.path.split(sys.executable)[1])
127
128 class Config(object):
129     pass
130
131 def splitFully(path):
132     dir, base = os.path.split(path)
133     if dir and dir != '' and dir != path:
134         return splitFully(dir)+[base]
135     if base == '':
136         return []
137     return [base]
138
139 def makeHierarchy(sources):
140     '''Break a list of files into a hierarchy; for each value, if it is a string,
141        then it is a file.  If it is a dictionary, it is a folder.  The string is
142        the original path of the file.'''
143
144     hierarchy = {}
145     for file in sources:
146         path = splitFully(file)
147         if len(path):
148             dict = hierarchy
149             for part in path[:-1]:
150                 if part not in dict:
151                     dict[part] = {}
152                 dict = dict[part]
153             dict[path[-1]] = file
154         #else:
155         #    print 'Warning: failed to decompose path for '+str(file)
156     return hierarchy
157
158 class _DSPGenerator(object):
159     """ Base class for DSP generators """
160
161     srcargs = [
162         'srcs',
163         'incs',
164         'localincs',
165         'resources',
166         'misc']
167
168     def __init__(self, dspfile, source, env):
169         self.dspfile = str(dspfile)
170         try:
171             get_abspath = dspfile.get_abspath
172         except AttributeError:
173             self.dspabs = os.path.abspath(dspfile)
174         else:
175             self.dspabs = get_abspath()
176
177         if 'variant' not in env:
178             raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\
179                   "'Release') to create an MSVSProject.")
180         elif SCons.Util.is_String(env['variant']):
181             variants = [env['variant']]
182         elif SCons.Util.is_List(env['variant']):
183             variants = env['variant']
184
185         if 'buildtarget' not in env or env['buildtarget'] == None:
186             buildtarget = ['']
187         elif SCons.Util.is_String(env['buildtarget']):
188             buildtarget = [env['buildtarget']]
189         elif SCons.Util.is_List(env['buildtarget']):
190             if len(env['buildtarget']) != len(variants):
191                 raise SCons.Errors.InternalError("Sizes of 'buildtarget' and 'variant' lists must be the same.")
192             buildtarget = []
193             for bt in env['buildtarget']:
194                 if SCons.Util.is_String(bt):
195                     buildtarget.append(bt)
196                 else:
197                     buildtarget.append(bt.get_abspath())
198         else:
199             buildtarget = [env['buildtarget'].get_abspath()]
200         if len(buildtarget) == 1:
201             bt = buildtarget[0]
202             buildtarget = []
203             for _ in variants:
204                 buildtarget.append(bt)
205
206         if 'outdir' not in env or env['outdir'] == None:
207             outdir = ['']
208         elif SCons.Util.is_String(env['outdir']):
209             outdir = [env['outdir']]
210         elif SCons.Util.is_List(env['outdir']):
211             if len(env['outdir']) != len(variants):
212                 raise SCons.Errors.InternalError("Sizes of 'outdir' and 'variant' lists must be the same.")
213             outdir = []
214             for s in env['outdir']:
215                 if SCons.Util.is_String(s):
216                     outdir.append(s)
217                 else:
218                     outdir.append(s.get_abspath())
219         else:
220             outdir = [env['outdir'].get_abspath()]
221         if len(outdir) == 1:
222             s = outdir[0]
223             outdir = []
224             for v in variants:
225                 outdir.append(s)
226
227         if 'runfile' not in env or env['runfile'] == None:
228             runfile = buildtarget[-1:]
229         elif SCons.Util.is_String(env['runfile']):
230             runfile = [env['runfile']]
231         elif SCons.Util.is_List(env['runfile']):
232             if len(env['runfile']) != len(variants):
233                 raise SCons.Errors.InternalError("Sizes of 'runfile' and 'variant' lists must be the same.")
234             runfile = []
235             for s in env['runfile']:
236                 if SCons.Util.is_String(s):
237                     runfile.append(s)
238                 else:
239                     runfile.append(s.get_abspath())
240         else:
241             runfile = [env['runfile'].get_abspath()]
242         if len(runfile) == 1:
243             s = runfile[0]
244             runfile = []
245             for v in variants:
246                 runfile.append(s)
247
248         self.sconscript = env['MSVSSCONSCRIPT']
249
250         cmdargs = env.get('cmdargs', '')
251
252         self.env = env
253
254         if 'name' in self.env:
255             self.name = self.env['name']
256         else:
257             self.name = os.path.basename(SCons.Util.splitext(self.dspfile)[0])
258         self.name = self.env.subst(self.name)
259
260         sourcenames = [
261             'Source Files',
262             'Header Files',
263             'Local Headers',
264             'Resource Files',
265             'Other Files']
266
267         self.sources = {}
268         for n in sourcenames:
269             self.sources[n] = []
270
271         self.configs = {}
272
273         self.nokeep = 0
274         if 'nokeep' in env and env['variant'] != 0:
275             self.nokeep = 1
276
277         if self.nokeep == 0 and os.path.exists(self.dspabs):
278             self.Parse()
279
280         for t in zip(sourcenames,self.srcargs):
281             if t[1] in self.env:
282                 if SCons.Util.is_List(self.env[t[1]]):
283                     for i in self.env[t[1]]:
284                         if not i in self.sources[t[0]]:
285                             self.sources[t[0]].append(i)
286                 else:
287                     if not self.env[t[1]] in self.sources[t[0]]:
288                         self.sources[t[0]].append(self.env[t[1]])
289
290         for n in sourcenames:
291             #TODO 2.4: compat layer supports sorted(key=) but not sort(key=)
292             #TODO 2.4: self.sources[n].sort(key=lambda a: a.lower())
293             self.sources[n] = sorted(self.sources[n], key=lambda a: a.lower())
294
295         def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, dspfile=dspfile):
296             config = Config()
297             config.buildtarget = buildtarget
298             config.outdir = outdir
299             config.cmdargs = cmdargs
300             config.runfile = runfile
301
302             match = re.match('(.*)\|(.*)', variant)
303             if match:
304                 config.variant = match.group(1)
305                 config.platform = match.group(2)
306             else:
307                 config.variant = variant
308                 config.platform = 'Win32'
309
310             self.configs[variant] = config
311             print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'"
312
313         for i in range(len(variants)):
314             AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs)
315
316         self.platforms = []
317         for key in self.configs.keys():
318             platform = self.configs[key].platform
319             if not platform in self.platforms:
320                 self.platforms.append(platform)
321
322     def Build(self):
323         pass
324
325 V6DSPHeader = """\
326 # Microsoft Developer Studio Project File - Name="%(name)s" - Package Owner=<4>
327 # Microsoft Developer Studio Generated Build File, Format Version 6.00
328 # ** DO NOT EDIT **
329
330 # TARGTYPE "Win32 (x86) External Target" 0x0106
331
332 CFG=%(name)s - Win32 %(confkey)s
333 !MESSAGE This is not a valid makefile. To build this project using NMAKE,
334 !MESSAGE use the Export Makefile command and run
335 !MESSAGE 
336 !MESSAGE NMAKE /f "%(name)s.mak".
337 !MESSAGE 
338 !MESSAGE You can specify a configuration when running NMAKE
339 !MESSAGE by defining the macro CFG on the command line. For example:
340 !MESSAGE 
341 !MESSAGE NMAKE /f "%(name)s.mak" CFG="%(name)s - Win32 %(confkey)s"
342 !MESSAGE 
343 !MESSAGE Possible choices for configuration are:
344 !MESSAGE 
345 """
346
347 class _GenerateV6DSP(_DSPGenerator):
348     """Generates a Project file for MSVS 6.0"""
349
350     def PrintHeader(self):
351         # pick a default config
352         confkeys = sorted(self.configs.keys())
353
354         name = self.name
355         confkey = confkeys[0]
356
357         self.file.write(V6DSPHeader % locals())
358
359         for kind in confkeys:
360             self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind))
361
362         self.file.write('!MESSAGE \n\n')
363
364     def PrintProject(self):
365         name = self.name
366         self.file.write('# Begin Project\n'
367                         '# PROP AllowPerConfigDependencies 0\n'
368                         '# PROP Scc_ProjName ""\n'
369                         '# PROP Scc_LocalPath ""\n\n')
370
371         first = 1
372         confkeys = sorted(self.configs.keys())
373         for kind in confkeys:
374             outdir = self.configs[kind].outdir
375             buildtarget = self.configs[kind].buildtarget
376             if first == 1:
377                 self.file.write('!IF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
378                 first = 0
379             else:
380                 self.file.write('\n!ELSEIF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind))
381
382             env_has_buildtarget = 'MSVSBUILDTARGET' in self.env
383             if not env_has_buildtarget:
384                 self.env['MSVSBUILDTARGET'] = buildtarget
385
386             # have to write this twice, once with the BASE settings, and once without
387             for base in ("BASE ",""):
388                 self.file.write('# PROP %sUse_MFC 0\n'
389                                 '# PROP %sUse_Debug_Libraries ' % (base, base))
390                 if kind.lower().find('debug') < 0:
391                     self.file.write('0\n')
392                 else:
393                     self.file.write('1\n')
394                 self.file.write('# PROP %sOutput_Dir "%s"\n'
395                                 '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir))
396                 cmd = 'echo Starting SCons && ' + self.env.subst('$MSVSBUILDCOM', 1)
397                 self.file.write('# PROP %sCmd_Line "%s"\n'
398                                 '# PROP %sRebuild_Opt "-c && %s"\n'
399                                 '# PROP %sTarget_File "%s"\n'
400                                 '# PROP %sBsc_Name ""\n'
401                                 '# PROP %sTarget_Dir ""\n'\
402                                 %(base,cmd,base,cmd,base,buildtarget,base,base))
403
404             if not env_has_buildtarget:
405                 del self.env['MSVSBUILDTARGET']
406
407         self.file.write('\n!ENDIF\n\n'
408                         '# Begin Target\n\n')
409         for kind in confkeys:
410             self.file.write('# Name "%s - Win32 %s"\n' % (name,kind))
411         self.file.write('\n')
412         first = 0
413         for kind in confkeys:
414             if first == 0:
415                 self.file.write('!IF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
416                 first = 1
417             else:
418                 self.file.write('!ELSEIF  "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind))
419         self.file.write('!ENDIF \n\n')
420         self.PrintSourceFiles()
421         self.file.write('# End Target\n'
422                         '# End Project\n')
423
424         if self.nokeep == 0:
425             # now we pickle some data and add it to the file -- MSDEV will ignore it.
426             pdata = pickle.dumps(self.configs,1)
427             pdata = base64.encodestring(pdata)
428             self.file.write(pdata + '\n')
429             pdata = pickle.dumps(self.sources,1)
430             pdata = base64.encodestring(pdata)
431             self.file.write(pdata + '\n')
432
433     def PrintSourceFiles(self):
434         categories = {'Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat',
435                       'Header Files': 'h|hpp|hxx|hm|inl',
436                       'Local Headers': 'h|hpp|hxx|hm|inl',
437                       'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe',
438                       'Other Files': ''}
439
440         for kind in sorted(categories.keys(), key=lambda a: a.lower()):
441             if not self.sources[kind]:
442                 continue # skip empty groups
443
444             self.file.write('# Begin Group "' + kind + '"\n\n')
445             typelist = categories[kind].replace('|', ';')
446             self.file.write('# PROP Default_Filter "' + typelist + '"\n')
447
448             for file in self.sources[kind]:
449                 file = os.path.normpath(file)
450                 self.file.write('# Begin Source File\n\n'
451                                 'SOURCE="' + file + '"\n'
452                                 '# End Source File\n')
453             self.file.write('# End Group\n')
454
455         # add the SConscript file outside of the groups
456         self.file.write('# Begin Source File\n\n'
457                         'SOURCE="' + str(self.sconscript) + '"\n'
458                         '# End Source File\n')
459
460     def Parse(self):
461         try:
462             dspfile = open(self.dspabs,'r')
463         except IOError:
464             return # doesn't exist yet, so can't add anything to configs.
465
466         line = dspfile.readline()
467         while line:
468             if line.find("# End Project") > -1:
469                 break
470             line = dspfile.readline()
471
472         line = dspfile.readline()
473         datas = line
474         while line and line != '\n':
475             line = dspfile.readline()
476             datas = datas + line
477
478         # OK, we've found our little pickled cache of data.
479         try:
480             datas = base64.decodestring(datas)
481             data = pickle.loads(datas)
482         except KeyboardInterrupt:
483             raise
484         except:
485             return # unable to unpickle any data for some reason
486
487         self.configs.update(data)
488
489         data = None
490         line = dspfile.readline()
491         datas = line
492         while line and line != '\n':
493             line = dspfile.readline()
494             datas = datas + line
495
496         # OK, we've found our little pickled cache of data.
497         # it has a "# " in front of it, so we strip that.
498         try:
499             datas = base64.decodestring(datas)
500             data = pickle.loads(datas)
501         except KeyboardInterrupt:
502             raise
503         except:
504             return # unable to unpickle any data for some reason
505
506         self.sources.update(data)
507
508     def Build(self):
509         try:
510             self.file = open(self.dspabs,'w')
511         except IOError, detail:
512             raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail))
513         else:
514             self.PrintHeader()
515             self.PrintProject()
516             self.file.close()
517
518 V7DSPHeader = """\
519 <?xml version="1.0" encoding = "%(encoding)s"?>
520 <VisualStudioProject
521 \tProjectType="Visual C++"
522 \tVersion="%(versionstr)s"
523 \tName="%(name)s"
524 %(scc_attrs)s
525 \tKeyword="MakeFileProj">
526 """
527
528 V7DSPConfiguration = """\
529 \t\t<Configuration
530 \t\t\tName="%(variant)s|%(platform)s"
531 \t\t\tOutputDirectory="%(outdir)s"
532 \t\t\tIntermediateDirectory="%(outdir)s"
533 \t\t\tConfigurationType="0"
534 \t\t\tUseOfMFC="0"
535 \t\t\tATLMinimizesCRunTimeLibraryUsage="FALSE">
536 \t\t\t<Tool
537 \t\t\t\tName="VCNMakeTool"
538 \t\t\t\tBuildCommandLine="%(buildcmd)s"
539 \t\t\t\tCleanCommandLine="%(cleancmd)s"
540 \t\t\t\tRebuildCommandLine="%(rebuildcmd)s"
541 \t\t\t\tOutput="%(runfile)s"/>
542 \t\t</Configuration>
543 """
544
545 V8DSPHeader = """\
546 <?xml version="1.0" encoding="%(encoding)s"?>
547 <VisualStudioProject
548 \tProjectType="Visual C++"
549 \tVersion="%(versionstr)s"
550 \tName="%(name)s"
551 %(scc_attrs)s
552 \tRootNamespace="%(name)s"
553 \tKeyword="MakeFileProj">
554 """
555
556 V8DSPConfiguration = """\
557 \t\t<Configuration
558 \t\t\tName="%(variant)s|%(platform)s"
559 \t\t\tConfigurationType="0"
560 \t\t\tUseOfMFC="0"
561 \t\t\tATLMinimizesCRunTimeLibraryUsage="false"
562 \t\t\t>
563 \t\t\t<Tool
564 \t\t\t\tName="VCNMakeTool"
565 \t\t\t\tBuildCommandLine="%(buildcmd)s"
566 \t\t\t\tReBuildCommandLine="%(rebuildcmd)s"
567 \t\t\t\tCleanCommandLine="%(cleancmd)s"
568 \t\t\t\tOutput="%(runfile)s"
569 \t\t\t\tPreprocessorDefinitions="%(preprocdefs)s"
570 \t\t\t\tIncludeSearchPath="%(includepath)s"
571 \t\t\t\tForcedIncludes=""
572 \t\t\t\tAssemblySearchPath=""
573 \t\t\t\tForcedUsingAssemblies=""
574 \t\t\t\tCompileAsManaged=""
575 \t\t\t/>
576 \t\t</Configuration>
577 """
578 class _GenerateV7DSP(_DSPGenerator):
579     """Generates a Project file for MSVS .NET"""
580
581     def __init__(self, dspfile, source, env):
582         _DSPGenerator.__init__(self, dspfile, source, env)
583         self.version = env['MSVS_VERSION']
584         self.version_num, self.suite = msvs_parse_version(self.version)
585         if self.version_num >= 8.0:
586             self.versionstr = '8.00'
587             self.dspheader = V8DSPHeader
588             self.dspconfiguration = V8DSPConfiguration
589         else:
590             if self.version_num >= 7.1:
591                 self.versionstr = '7.10'
592             else:
593                 self.versionstr = '7.00'
594             self.dspheader = V7DSPHeader
595             self.dspconfiguration = V7DSPConfiguration
596         self.file = None
597
598     def PrintHeader(self):
599         env = self.env
600         versionstr = self.versionstr
601         name = self.name
602         encoding = self.env.subst('$MSVSENCODING')
603         scc_provider = env.get('MSVS_SCC_PROVIDER', '')
604         scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
605         scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
606         scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
607         project_guid = env.get('MSVS_PROJECT_GUID', '')
608         if self.version_num >= 8.0 and not project_guid:
609             project_guid = _generateGUID(self.dspfile, '')
610         if scc_provider != '':
611             scc_attrs = ('\tProjectGUID="%s"\n'
612                          '\tSccProjectName="%s"\n'
613                          '\tSccAuxPath="%s"\n'
614                          '\tSccLocalPath="%s"\n'
615                          '\tSccProvider="%s"' % (project_guid, scc_project_name, scc_aux_path, scc_local_path, scc_provider))
616         else:
617             scc_attrs = ('\tProjectGUID="%s"\n'
618                          '\tSccProjectName="%s"\n'
619                          '\tSccLocalPath="%s"' % (project_guid, scc_project_name, scc_local_path))
620
621         self.file.write(self.dspheader % locals())
622
623         self.file.write('\t<Platforms>\n')
624         for platform in self.platforms:
625             self.file.write(
626                         '\t\t<Platform\n'
627                         '\t\t\tName="%s"/>\n' % platform)
628         self.file.write('\t</Platforms>\n')
629
630         if self.version_num >= 8.0:
631             self.file.write('\t<ToolFiles>\n'
632                             '\t</ToolFiles>\n')
633
634     def PrintProject(self):
635         self.file.write('\t<Configurations>\n')
636
637         confkeys = sorted(self.configs.keys())
638         for kind in confkeys:
639             variant = self.configs[kind].variant
640             platform = self.configs[kind].platform
641             outdir = self.configs[kind].outdir
642             buildtarget = self.configs[kind].buildtarget
643             runfile     = self.configs[kind].runfile
644             cmdargs = self.configs[kind].cmdargs
645
646             env_has_buildtarget = 'MSVSBUILDTARGET' in self.env
647             if not env_has_buildtarget:
648                 self.env['MSVSBUILDTARGET'] = buildtarget
649
650             starting = 'echo Starting SCons && '
651             if cmdargs:
652                 cmdargs = ' ' + cmdargs
653             else:
654                 cmdargs = ''
655             buildcmd    = xmlify(starting + self.env.subst('$MSVSBUILDCOM', 1) + cmdargs)
656             rebuildcmd  = xmlify(starting + self.env.subst('$MSVSREBUILDCOM', 1) + cmdargs)
657             cleancmd    = xmlify(starting + self.env.subst('$MSVSCLEANCOM', 1) + cmdargs)
658
659             preprocdefs = xmlify(';'.join(processDefines(self.env.get('CPPDEFINES', []))))
660             includepath = xmlify(';'.join(self.env.get('CPPPATH', [])))
661
662             if not env_has_buildtarget:
663                 del self.env['MSVSBUILDTARGET']
664
665             self.file.write(self.dspconfiguration % locals())
666
667         self.file.write('\t</Configurations>\n')
668
669         if self.version_num >= 7.1:
670             self.file.write('\t<References>\n'
671                             '\t</References>\n')
672
673         self.PrintSourceFiles()
674
675         self.file.write('</VisualStudioProject>\n')
676
677         if self.nokeep == 0:
678             # now we pickle some data and add it to the file -- MSDEV will ignore it.
679             pdata = pickle.dumps(self.configs,1)
680             pdata = base64.encodestring(pdata)
681             self.file.write('<!-- SCons Data:\n' + pdata + '\n')
682             pdata = pickle.dumps(self.sources,1)
683             pdata = base64.encodestring(pdata)
684             self.file.write(pdata + '-->\n')
685
686     def printSources(self, hierarchy, commonprefix):
687         sorteditems = sorted(hierarchy.items(), key=lambda a: a[0].lower())
688
689         # First folders, then files
690         for key, value in sorteditems:
691             if SCons.Util.is_Dict(value):
692                 self.file.write('\t\t\t<Filter\n'
693                                 '\t\t\t\tName="%s"\n'
694                                 '\t\t\t\tFilter="">\n' % (key))
695                 self.printSources(value, commonprefix)
696                 self.file.write('\t\t\t</Filter>\n')
697
698         for key, value in sorteditems:
699             if SCons.Util.is_String(value):
700                 file = value
701                 if commonprefix:
702                     file = os.path.join(commonprefix, value)
703                 file = os.path.normpath(file)
704                 self.file.write('\t\t\t<File\n'
705                                 '\t\t\t\tRelativePath="%s">\n'
706                                 '\t\t\t</File>\n' % (file))
707
708     def PrintSourceFiles(self):
709         categories = {'Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat',
710                       'Header Files': 'h;hpp;hxx;hm;inl',
711                       'Local Headers': 'h;hpp;hxx;hm;inl',
712                       'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe',
713                       'Other Files': ''}
714
715         self.file.write('\t<Files>\n')
716
717         cats = sorted([k for k in categories.keys() if self.sources[k]],
718                       key=lambda a: a.lower())
719         for kind in cats:
720             if len(cats) > 1:
721                 self.file.write('\t\t<Filter\n'
722                                 '\t\t\tName="%s"\n'
723                                 '\t\t\tFilter="%s">\n' % (kind, categories[kind]))
724
725             sources = self.sources[kind]
726
727             # First remove any common prefix
728             commonprefix = None
729             if len(sources) > 1:
730                 s = list(map(os.path.normpath, sources))
731                 # take the dirname because the prefix may include parts
732                 # of the filenames (e.g. if you have 'dir\abcd' and
733                 # 'dir\acde' then the cp will be 'dir\a' )
734                 cp = os.path.dirname( os.path.commonprefix(s) )
735                 if cp and s[0][len(cp)] == os.sep:
736                     # +1 because the filename starts after the separator
737                     sources = [s[len(cp)+1:] for s in sources]
738                     commonprefix = cp
739             elif len(sources) == 1:
740                 commonprefix = os.path.dirname( sources[0] )
741                 sources[0] = os.path.basename( sources[0] )
742
743             hierarchy = makeHierarchy(sources)
744             self.printSources(hierarchy, commonprefix=commonprefix)
745
746             if len(cats)>1:
747                 self.file.write('\t\t</Filter>\n')
748
749         # add the SConscript file outside of the groups
750         self.file.write('\t\t<File\n'
751                         '\t\t\tRelativePath="%s">\n'
752                         '\t\t</File>\n' % str(self.sconscript))
753
754         self.file.write('\t</Files>\n'
755                         '\t<Globals>\n'
756                         '\t</Globals>\n')
757
758     def Parse(self):
759         try:
760             dspfile = open(self.dspabs,'r')
761         except IOError:
762             return # doesn't exist yet, so can't add anything to configs.
763
764         line = dspfile.readline()
765         while line:
766             if line.find('<!-- SCons Data:') > -1:
767                 break
768             line = dspfile.readline()
769
770         line = dspfile.readline()
771         datas = line
772         while line and line != '\n':
773             line = dspfile.readline()
774             datas = datas + line
775
776         # OK, we've found our little pickled cache of data.
777         try:
778             datas = base64.decodestring(datas)
779             data = pickle.loads(datas)
780         except KeyboardInterrupt:
781             raise
782         except:
783             return # unable to unpickle any data for some reason
784
785         self.configs.update(data)
786
787         data = None
788         line = dspfile.readline()
789         datas = line
790         while line and line != '\n':
791             line = dspfile.readline()
792             datas = datas + line
793
794         # OK, we've found our little pickled cache of data.
795         try:
796             datas = base64.decodestring(datas)
797             data = pickle.loads(datas)
798         except KeyboardInterrupt:
799             raise
800         except:
801             return # unable to unpickle any data for some reason
802
803         self.sources.update(data)
804
805     def Build(self):
806         try:
807             self.file = open(self.dspabs,'w')
808         except IOError, detail:
809             raise SCons.Errors.InternalError('Unable to open "' + self.dspabs + '" for writing:' + str(detail))
810         else:
811             self.PrintHeader()
812             self.PrintProject()
813             self.file.close()
814
815 class _DSWGenerator(object):
816     """ Base class for DSW generators """
817     def __init__(self, dswfile, source, env):
818         self.dswfile = os.path.normpath(str(dswfile))
819         self.env = env
820
821         if 'projects' not in env:
822             raise SCons.Errors.UserError("You must specify a 'projects' argument to create an MSVSSolution.")
823         projects = env['projects']
824         if not SCons.Util.is_List(projects):
825             raise SCons.Errors.InternalError("The 'projects' argument must be a list of nodes.")
826         projects = SCons.Util.flatten(projects)
827         if len(projects) < 1:
828             raise SCons.Errors.UserError("You must specify at least one project to create an MSVSSolution.")
829         self.dspfiles = list(map(str, projects))
830
831         if 'name' in self.env:
832             self.name = self.env['name']
833         else:
834             self.name = os.path.basename(SCons.Util.splitext(self.dswfile)[0])
835         self.name = self.env.subst(self.name)
836
837     def Build(self):
838         pass
839
840 class _GenerateV7DSW(_DSWGenerator):
841     """Generates a Solution file for MSVS .NET"""
842     def __init__(self, dswfile, source, env):
843         _DSWGenerator.__init__(self, dswfile, source, env)
844
845         self.file = None
846         self.version = self.env['MSVS_VERSION']
847         self.version_num, self.suite = msvs_parse_version(self.version)
848         self.versionstr = '7.00'
849         if self.version_num >= 8.0:
850             self.versionstr = '9.00'
851         elif self.version_num >= 7.1:
852             self.versionstr = '8.00'
853         if self.version_num >= 8.0:
854             self.versionstr = '9.00'
855
856         if 'slnguid' in env and env['slnguid']:
857             self.slnguid = env['slnguid']
858         else:
859             self.slnguid = _generateGUID(dswfile, self.name)
860
861         self.configs = {}
862
863         self.nokeep = 0
864         if 'nokeep' in env and env['variant'] != 0:
865             self.nokeep = 1
866
867         if self.nokeep == 0 and os.path.exists(self.dswfile):
868             self.Parse()
869
870         def AddConfig(self, variant, dswfile=dswfile):
871             config = Config()
872
873             match = re.match('(.*)\|(.*)', variant)
874             if match:
875                 config.variant = match.group(1)
876                 config.platform = match.group(2)
877             else:
878                 config.variant = variant
879                 config.platform = 'Win32'
880
881             self.configs[variant] = config
882             print "Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dswfile) + "'"
883
884         if 'variant' not in env:
885             raise SCons.Errors.InternalError("You must specify a 'variant' argument (i.e. 'Debug' or " +\
886                   "'Release') to create an MSVS Solution File.")
887         elif SCons.Util.is_String(env['variant']):
888             AddConfig(self, env['variant'])
889         elif SCons.Util.is_List(env['variant']):
890             for variant in env['variant']:
891                 AddConfig(self, variant)
892
893         self.platforms = []
894         for key in self.configs.keys():
895             platform = self.configs[key].platform
896             if not platform in self.platforms:
897                 self.platforms.append(platform)
898
899     def Parse(self):
900         try:
901             dswfile = open(self.dswfile,'r')
902         except IOError:
903             return # doesn't exist yet, so can't add anything to configs.
904
905         line = dswfile.readline()
906         while line:
907             if line[:9] == "EndGlobal":
908                 break
909             line = dswfile.readline()
910
911         line = dswfile.readline()
912         datas = line
913         while line:
914             line = dswfile.readline()
915             datas = datas + line
916
917         # OK, we've found our little pickled cache of data.
918         try:
919             datas = base64.decodestring(datas)
920             data = pickle.loads(datas)
921         except KeyboardInterrupt:
922             raise
923         except:
924             return # unable to unpickle any data for some reason
925
926         self.configs.update(data)
927
928     def PrintSolution(self):
929         """Writes a solution file"""
930         self.file.write('Microsoft Visual Studio Solution File, Format Version %s\n' % self.versionstr )
931         if self.version_num >= 8.0:
932             self.file.write('# Visual Studio 2005\n')
933         for p in self.dspfiles:
934             name = os.path.basename(p)
935             base, suffix = SCons.Util.splitext(name)
936             if suffix == '.vcproj':
937                 name = base
938             guid = _generateGUID(p, '')
939             self.file.write('Project("%s") = "%s", "%s", "%s"\n'
940                             % ( external_makefile_guid, name, p, guid ) )
941             if self.version_num >= 7.1 and self.version_num < 8.0:
942                 self.file.write('\tProjectSection(ProjectDependencies) = postProject\n'
943                                 '\tEndProjectSection\n')
944             self.file.write('EndProject\n')
945
946         self.file.write('Global\n')
947
948         env = self.env
949         if 'MSVS_SCC_PROVIDER' in env:
950             dspfile_base = os.path.basename(self.dspfile)
951             slnguid = self.slnguid
952             scc_provider = env.get('MSVS_SCC_PROVIDER', '')
953             scc_provider = scc_provider.replace(' ', r'\u0020')
954             scc_project_name = env.get('MSVS_SCC_PROJECT_NAME', '')
955             # scc_aux_path = env.get('MSVS_SCC_AUX_PATH', '')
956             scc_local_path = env.get('MSVS_SCC_LOCAL_PATH', '')
957             scc_project_base_path = env.get('MSVS_SCC_PROJECT_BASE_PATH', '')
958             # project_guid = env.get('MSVS_PROJECT_GUID', '')
959
960             self.file.write('\tGlobalSection(SourceCodeControl) = preSolution\n'
961                             '\t\tSccNumberOfProjects = 2\n'
962                             '\t\tSccProjectUniqueName0 = %(dspfile_base)s\n'
963                             '\t\tSccLocalPath0 = %(scc_local_path)s\n'
964                             '\t\tCanCheckoutShared = true\n'
965                             '\t\tSccProjectFilePathRelativizedFromConnection0 = %(scc_project_base_path)s\n'
966                             '\t\tSccProjectName1 = %(scc_project_name)s\n'
967                             '\t\tSccLocalPath1 = %(scc_local_path)s\n'
968                             '\t\tSccProvider1 = %(scc_provider)s\n'
969                             '\t\tCanCheckoutShared = true\n'
970                             '\t\tSccProjectFilePathRelativizedFromConnection1 = %(scc_project_base_path)s\n'
971                             '\t\tSolutionUniqueID = %(slnguid)s\n'
972                             '\tEndGlobalSection\n' % locals())
973
974         if self.version_num >= 8.0:
975             self.file.write('\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n')
976         else:
977             self.file.write('\tGlobalSection(SolutionConfiguration) = preSolution\n')
978
979         confkeys = sorted(self.configs.keys())
980         cnt = 0
981         for name in confkeys:
982             variant = self.configs[name].variant
983             platform = self.configs[name].platform
984             if self.version_num >= 8.0:
985                 self.file.write('\t\t%s|%s = %s|%s\n' % (variant, platform, variant, platform))
986             else:
987                 self.file.write('\t\tConfigName.%d = %s\n' % (cnt, variant))
988             cnt = cnt + 1
989         self.file.write('\tEndGlobalSection\n')
990         if self.version_num < 7.1:
991             self.file.write('\tGlobalSection(ProjectDependencies) = postSolution\n'
992                             '\tEndGlobalSection\n')
993         if self.version_num >= 8.0:
994             self.file.write('\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n')
995         else:
996             self.file.write('\tGlobalSection(ProjectConfiguration) = postSolution\n')
997
998         for name in confkeys:
999             variant = self.configs[name].variant
1000             platform = self.configs[name].platform
1001             if self.version_num >= 8.0:
1002                 for p in self.dspfiles:
1003                     guid = _generateGUID(p, '')
1004                     self.file.write('\t\t%s.%s|%s.ActiveCfg = %s|%s\n'
1005                                     '\t\t%s.%s|%s.Build.0 = %s|%s\n'  % (guid,variant,platform,variant,platform,guid,variant,platform,variant,platform))
1006             else:
1007                 for p in self.dspfiles:
1008                     guid = _generateGUID(p, '')
1009                     self.file.write('\t\t%s.%s.ActiveCfg = %s|%s\n'
1010                                     '\t\t%s.%s.Build.0 = %s|%s\n'  %(guid,variant,variant,platform,guid,variant,variant,platform))
1011
1012         self.file.write('\tEndGlobalSection\n')
1013
1014         if self.version_num >= 8.0:
1015             self.file.write('\tGlobalSection(SolutionProperties) = preSolution\n'
1016                             '\t\tHideSolutionNode = FALSE\n'
1017                             '\tEndGlobalSection\n')
1018         else:
1019             self.file.write('\tGlobalSection(ExtensibilityGlobals) = postSolution\n'
1020                             '\tEndGlobalSection\n'
1021                             '\tGlobalSection(ExtensibilityAddIns) = postSolution\n'
1022                             '\tEndGlobalSection\n')
1023         self.file.write('EndGlobal\n')
1024         if self.nokeep == 0:
1025             pdata = pickle.dumps(self.configs,1)
1026             pdata = base64.encodestring(pdata)
1027             self.file.write(pdata + '\n')
1028
1029     def Build(self):
1030         try:
1031             self.file = open(self.dswfile,'w')
1032         except IOError, detail:
1033             raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail))
1034         else:
1035             self.PrintSolution()
1036             self.file.close()
1037
1038 V6DSWHeader = """\
1039 Microsoft Developer Studio Workspace File, Format Version 6.00
1040 # WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
1041
1042 ###############################################################################
1043
1044 Project: "%(name)s"="%(dspfile)s" - Package Owner=<4>
1045
1046 Package=<5>
1047 {{{
1048 }}}
1049
1050 Package=<4>
1051 {{{
1052 }}}
1053
1054 ###############################################################################
1055
1056 Global:
1057
1058 Package=<5>
1059 {{{
1060 }}}
1061
1062 Package=<3>
1063 {{{
1064 }}}
1065
1066 ###############################################################################
1067 """
1068
1069 class _GenerateV6DSW(_DSWGenerator):
1070     """Generates a Workspace file for MSVS 6.0"""
1071
1072     def PrintWorkspace(self):
1073         """ writes a DSW file """
1074         name = self.name
1075         dspfile = self.dspfiles[0]
1076         self.file.write(V6DSWHeader % locals())
1077
1078     def Build(self):
1079         try:
1080             self.file = open(self.dswfile,'w')
1081         except IOError, detail:
1082             raise SCons.Errors.InternalError('Unable to open "' + self.dswfile + '" for writing:' + str(detail))
1083         else:
1084             self.PrintWorkspace()
1085             self.file.close()
1086
1087
1088 def GenerateDSP(dspfile, source, env):
1089     """Generates a Project file based on the version of MSVS that is being used"""
1090
1091     version_num = 6.0
1092     if 'MSVS_VERSION' in env:
1093         version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1094     if version_num >= 7.0:
1095         g = _GenerateV7DSP(dspfile, source, env)
1096         g.Build()
1097     else:
1098         g = _GenerateV6DSP(dspfile, source, env)
1099         g.Build()
1100
1101 def GenerateDSW(dswfile, source, env):
1102     """Generates a Solution/Workspace file based on the version of MSVS that is being used"""
1103
1104     version_num = 6.0
1105     if 'MSVS_VERSION' in env:
1106         version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1107     if version_num >= 7.0:
1108         g = _GenerateV7DSW(dswfile, source, env)
1109         g.Build()
1110     else:
1111         g = _GenerateV6DSW(dswfile, source, env)
1112         g.Build()
1113
1114
1115 ##############################################################################
1116 # Above here are the classes and functions for generation of
1117 # DSP/DSW/SLN/VCPROJ files.
1118 ##############################################################################
1119
1120 def GetMSVSProjectSuffix(target, source, env, for_signature):
1121      return env['MSVS']['PROJECTSUFFIX']
1122
1123 def GetMSVSSolutionSuffix(target, source, env, for_signature):
1124      return env['MSVS']['SOLUTIONSUFFIX']
1125
1126 def GenerateProject(target, source, env):
1127     # generate the dsp file, according to the version of MSVS.
1128     builddspfile = target[0]
1129     dspfile = builddspfile.srcnode()
1130
1131     # this detects whether or not we're using a VariantDir
1132     if not dspfile is builddspfile:
1133         try:
1134             bdsp = open(str(builddspfile), "w+")
1135         except IOError, detail:
1136             print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1137             raise
1138
1139         bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath())
1140
1141     GenerateDSP(dspfile, source, env)
1142
1143     if env.get('auto_build_solution', 1):
1144         builddswfile = target[1]
1145         dswfile = builddswfile.srcnode()
1146
1147         if not dswfile is builddswfile:
1148
1149             try:
1150                 bdsw = open(str(builddswfile), "w+")
1151             except IOError, detail:
1152                 print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n'
1153                 raise
1154
1155             bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath())
1156
1157         GenerateDSW(dswfile, source, env)
1158
1159 def GenerateSolution(target, source, env):
1160     GenerateDSW(target[0], source, env)
1161
1162 def projectEmitter(target, source, env):
1163     """Sets up the DSP dependencies."""
1164
1165     # todo: Not sure what sets source to what user has passed as target,
1166     # but this is what happens. When that is fixed, we also won't have
1167     # to make the user always append env['MSVSPROJECTSUFFIX'] to target.
1168     if source[0] == target[0]:
1169         source = []
1170
1171     # make sure the suffix is correct for the version of MSVS we're running.
1172     (base, suff) = SCons.Util.splitext(str(target[0]))
1173     suff = env.subst('$MSVSPROJECTSUFFIX')
1174     target[0] = base + suff
1175
1176     if not source:
1177         source = 'prj_inputs:'
1178         source = source + env.subst('$MSVSSCONSCOM', 1)
1179         source = source + env.subst('$MSVSENCODING', 1)
1180
1181         if 'buildtarget' in env and env['buildtarget'] != None:
1182             if SCons.Util.is_String(env['buildtarget']):
1183                 source = source + ' "%s"' % env['buildtarget']
1184             elif SCons.Util.is_List(env['buildtarget']):
1185                 for bt in env['buildtarget']:
1186                     if SCons.Util.is_String(bt):
1187                         source = source + ' "%s"' % bt
1188                     else:
1189                         try: source = source + ' "%s"' % bt.get_abspath()
1190                         except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None")
1191             else:
1192                 try: source = source + ' "%s"' % env['buildtarget'].get_abspath()
1193                 except AttributeError: raise SCons.Errors.InternalError("buildtarget can be a string, a node, a list of strings or nodes, or None")
1194
1195         if 'outdir' in env and env['outdir'] != None:
1196             if SCons.Util.is_String(env['outdir']):
1197                 source = source + ' "%s"' % env['outdir']
1198             elif SCons.Util.is_List(env['outdir']):
1199                 for s in env['outdir']:
1200                     if SCons.Util.is_String(s):
1201                         source = source + ' "%s"' % s
1202                     else:
1203                         try: source = source + ' "%s"' % s.get_abspath()
1204                         except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None")
1205             else:
1206                 try: source = source + ' "%s"' % env['outdir'].get_abspath()
1207                 except AttributeError: raise SCons.Errors.InternalError("outdir can be a string, a node, a list of strings or nodes, or None")
1208
1209         if 'name' in env:
1210             if SCons.Util.is_String(env['name']):
1211                 source = source + ' "%s"' % env['name']
1212             else:
1213                 raise SCons.Errors.InternalError("name must be a string")
1214
1215         if 'variant' in env:
1216             if SCons.Util.is_String(env['variant']):
1217                 source = source + ' "%s"' % env['variant']
1218             elif SCons.Util.is_List(env['variant']):
1219                 for variant in env['variant']:
1220                     if SCons.Util.is_String(variant):
1221                         source = source + ' "%s"' % variant
1222                     else:
1223                         raise SCons.Errors.InternalError("name must be a string or a list of strings")
1224             else:
1225                 raise SCons.Errors.InternalError("variant must be a string or a list of strings")
1226         else:
1227             raise SCons.Errors.InternalError("variant must be specified")
1228
1229         for s in _DSPGenerator.srcargs:
1230             if s in env:
1231                 if SCons.Util.is_String(env[s]):
1232                     source = source + ' "%s' % env[s]
1233                 elif SCons.Util.is_List(env[s]):
1234                     for t in env[s]:
1235                         if SCons.Util.is_String(t):
1236                             source = source + ' "%s"' % t
1237                         else:
1238                             raise SCons.Errors.InternalError(s + " must be a string or a list of strings")
1239                 else:
1240                     raise SCons.Errors.InternalError(s + " must be a string or a list of strings")
1241
1242         source = source + ' "%s"' % str(target[0])
1243         source = [SCons.Node.Python.Value(source)]
1244
1245     targetlist = [target[0]]
1246     sourcelist = source
1247
1248     if env.get('auto_build_solution', 1):
1249         env['projects'] = targetlist
1250         t, s = solutionEmitter(target, target, env)
1251         targetlist = targetlist + t
1252
1253     return (targetlist, sourcelist)
1254
1255 def solutionEmitter(target, source, env):
1256     """Sets up the DSW dependencies."""
1257
1258     # todo: Not sure what sets source to what user has passed as target,
1259     # but this is what happens. When that is fixed, we also won't have
1260     # to make the user always append env['MSVSSOLUTIONSUFFIX'] to target.
1261     if source[0] == target[0]:
1262         source = []
1263
1264     # make sure the suffix is correct for the version of MSVS we're running.
1265     (base, suff) = SCons.Util.splitext(str(target[0]))
1266     suff = env.subst('$MSVSSOLUTIONSUFFIX')
1267     target[0] = base + suff
1268
1269     if not source:
1270         source = 'sln_inputs:'
1271
1272         if 'name' in env:
1273             if SCons.Util.is_String(env['name']):
1274                 source = source + ' "%s"' % env['name']
1275             else:
1276                 raise SCons.Errors.InternalError("name must be a string")
1277
1278         if 'variant' in env:
1279             if SCons.Util.is_String(env['variant']):
1280                 source = source + ' "%s"' % env['variant']
1281             elif SCons.Util.is_List(env['variant']):
1282                 for variant in env['variant']:
1283                     if SCons.Util.is_String(variant):
1284                         source = source + ' "%s"' % variant
1285                     else:
1286                         raise SCons.Errors.InternalError("name must be a string or a list of strings")
1287             else:
1288                 raise SCons.Errors.InternalError("variant must be a string or a list of strings")
1289         else:
1290             raise SCons.Errors.InternalError("variant must be specified")
1291
1292         if 'slnguid' in env:
1293             if SCons.Util.is_String(env['slnguid']):
1294                 source = source + ' "%s"' % env['slnguid']
1295             else:
1296                 raise SCons.Errors.InternalError("slnguid must be a string")
1297
1298         if 'projects' in env:
1299             if SCons.Util.is_String(env['projects']):
1300                 source = source + ' "%s"' % env['projects']
1301             elif SCons.Util.is_List(env['projects']):
1302                 for t in env['projects']:
1303                     if SCons.Util.is_String(t):
1304                         source = source + ' "%s"' % t
1305
1306         source = source + ' "%s"' % str(target[0])
1307         source = [SCons.Node.Python.Value(source)]
1308
1309     return ([target[0]], source)
1310
1311 projectAction = SCons.Action.Action(GenerateProject, None)
1312
1313 solutionAction = SCons.Action.Action(GenerateSolution, None)
1314
1315 projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM',
1316                                        suffix = '$MSVSPROJECTSUFFIX',
1317                                        emitter = projectEmitter)
1318
1319 solutionBuilder = SCons.Builder.Builder(action = '$MSVSSOLUTIONCOM',
1320                                         suffix = '$MSVSSOLUTIONSUFFIX',
1321                                         emitter = solutionEmitter)
1322
1323 default_MSVS_SConscript = None
1324
1325 def generate(env):
1326     """Add Builders and construction variables for Microsoft Visual
1327     Studio project files to an Environment."""
1328     try:
1329         env['BUILDERS']['MSVSProject']
1330     except KeyError:
1331         env['BUILDERS']['MSVSProject'] = projectBuilder
1332
1333     try:
1334         env['BUILDERS']['MSVSSolution']
1335     except KeyError:
1336         env['BUILDERS']['MSVSSolution'] = solutionBuilder
1337
1338     env['MSVSPROJECTCOM'] = projectAction
1339     env['MSVSSOLUTIONCOM'] = solutionAction
1340
1341     if SCons.Script.call_stack:
1342         # XXX Need to find a way to abstract this; the build engine
1343         # shouldn't depend on anything in SCons.Script.
1344         env['MSVSSCONSCRIPT'] = SCons.Script.call_stack[0].sconscript
1345     else:
1346         global default_MSVS_SConscript
1347         if default_MSVS_SConscript is None:
1348             default_MSVS_SConscript = env.File('SConstruct')
1349         env['MSVSSCONSCRIPT'] = default_MSVS_SConscript
1350
1351     env['MSVSSCONS'] = '"%s" -c "%s"' % (python_executable, getExecScriptMain(env))
1352     env['MSVSSCONSFLAGS'] = '-C "${MSVSSCONSCRIPT.dir.abspath}" -f ${MSVSSCONSCRIPT.name}'
1353     env['MSVSSCONSCOM'] = '$MSVSSCONS $MSVSSCONSFLAGS'
1354     env['MSVSBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
1355     env['MSVSREBUILDCOM'] = '$MSVSSCONSCOM "$MSVSBUILDTARGET"'
1356     env['MSVSCLEANCOM'] = '$MSVSSCONSCOM -c "$MSVSBUILDTARGET"'
1357     env['MSVSENCODING'] = 'Windows-1252'
1358
1359     # Set-up ms tools paths for default version
1360     msvc_setup_env_once(env)
1361
1362     if 'MSVS_VERSION' in env:
1363         version_num, suite = msvs_parse_version(env['MSVS_VERSION'])
1364     else:
1365         (version_num, suite) = (7.0, None) # guess at a default
1366     if 'MSVS' not in env:
1367         env['MSVS'] = {}
1368     if (version_num < 7.0):
1369         env['MSVS']['PROJECTSUFFIX']  = '.dsp'
1370         env['MSVS']['SOLUTIONSUFFIX'] = '.dsw'
1371     else:
1372         env['MSVS']['PROJECTSUFFIX']  = '.vcproj'
1373         env['MSVS']['SOLUTIONSUFFIX'] = '.sln'
1374
1375     env['GET_MSVSPROJECTSUFFIX']  = GetMSVSProjectSuffix
1376     env['GET_MSVSSOLUTIONSUFFIX']  = GetMSVSSolutionSuffix
1377     env['MSVSPROJECTSUFFIX']  = '${GET_MSVSPROJECTSUFFIX}'
1378     env['MSVSSOLUTIONSUFFIX']  = '${GET_MSVSSOLUTIONSUFFIX}'
1379     env['SCONS_HOME'] = os.environ.get('SCONS_HOME')
1380
1381 def exists(env):
1382     return msvc_exists()
1383
1384 # Local Variables:
1385 # tab-width:4
1386 # indent-tabs-mode:nil
1387 # End:
1388 # vim: set expandtab tabstop=4 shiftwidth=4: