Fix VS8 integration for use with Express. (Christian Maaser) Portability fix in...
[scons.git] / src / engine / SCons / Tool / msvc.py
1 """engine.SCons.Tool.msvc
2
3 Tool-specific initialization for Microsoft Visual C/C++.
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__
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
34 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35
36 import os.path
37 import re
38 import string
39
40 import SCons.Action
41 import SCons.Builder
42 import SCons.Errors
43 import SCons.Platform.win32
44 import SCons.Tool
45 import SCons.Tool.msvs
46 import SCons.Util
47 import SCons.Warnings
48
49 CSuffixes = ['.c', '.C']
50 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
51
52 def _parse_msvc7_overrides(version,platform):
53     """ Parse any overridden defaults for MSVS directory locations
54     in MSVS .NET. """
55
56     # First, we get the shell folder for this user:
57     if not SCons.Util.can_read_reg:
58         raise SCons.Errors.InternalError, "No Windows registry module was found"
59
60     comps = ""
61     try:
62         (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
63                                             r'Software\Microsoft\Windows\CurrentVersion' +\
64                                             r'\Explorer\Shell Folders\Local AppData')
65     except SCons.Util.RegError:
66         raise SCons.Errors.InternalError, \
67               "The Local AppData directory was not found in the registry."
68
69     comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VCComponents.dat'
70     dirs = {}
71
72     if os.path.exists(comps):
73         # now we parse the directories from this file, if it exists.
74         # We only look for entries after:
75         # [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories],
76         # since this file could contain a number of things...
77         lines = None
78         try:
79             import codecs
80         except ImportError:
81             pass
82         else:
83             try:
84                 f = codecs.open(comps, 'r', 'utf16')
85                 encoder = codecs.getencoder('ascii')
86                 lines = map(lambda l, e=encoder: e(l)[0], f.readlines())
87             except (LookupError, UnicodeError):
88                 lines = codecs.open(comps, 'r', 'utf8').readlines()
89         if lines is None:
90             lines = open(comps, 'r').readlines()
91         if 'x86' == platform: platform = 'Win32'
92
93         found = 0
94         for line in lines:
95             line.strip()
96             if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories]'%platform) >= 0:
97                 found = 1
98             elif line == '' or line[:1] == '[':
99                 found = 0
100             elif found == 1:
101                 kv = line.split('=', 1)
102                 if len(kv) == 2:
103                     (key, val) = kv
104                 key = key.replace(' Dirs','')
105                 dirs[key.upper()] = val
106         f.close()
107     else:
108         # since the file didn't exist, we have only the defaults in
109         # the registry to work with.
110
111         if 'x86' == platform: platform = 'Win32'
112
113         try:
114             K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
115             K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories'%platform
116             k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
117             i = 0
118             while 1:
119                 try:
120                     (key,val,t) = SCons.Util.RegEnumValue(k,i)
121                     key = key.replace(' Dirs','')
122                     dirs[key.upper()] = val
123                     i = i + 1
124                 except SCons.Util.RegError:
125                     break
126         except SCons.Util.RegError:
127             # if we got here, then we didn't find the registry entries:
128             raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
129     return dirs
130
131 def _parse_msvc8_overrides(version,platform,suite):
132     """ Parse any overridden defaults for MSVC directory locations
133     in MSVC 2005. """
134
135     # In VS8 the user can change the location of the settings file that
136     # contains the include, lib and binary paths. Try to get the location
137     # from registry
138     if not SCons.Util.can_read_reg:
139         raise SCons.Errors.InternalError, "No Windows registry module was found"
140
141     s = ''
142     if suite == 'EXPRESS':
143         s = '\\VCExpress\\'
144
145     # ToDo: add registry key strings for the other versions of visual
146     # studio 2005.
147     settings_path = ""
148     try:
149         (settings_path, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
150                                                     r'Software\Microsoft' + s + version +\
151                                                     r'\Profile\AutoSaveFile')
152         settings_path = settings_path.upper()
153     except SCons.Util.RegError:
154         raise SCons.Errors.InternalError, \
155               "The VS8 settings file location was not found in the registry."
156
157     # Look for potential environment variables in the settings path
158     if settings_path.find('%VSSPV_VISUALSTUDIO_DIR%') >= 0:
159         # First replace a special variable named %vsspv_visualstudio_dir%
160         # that is not found in the OSs environment variables...
161         try:
162             (value, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
163                                                 r'Software\Microsoft' + s + version +\
164                                                 r'\VisualStudioLocation')
165             settings_path = settings_path.replace('%VSSPV_VISUALSTUDIO_DIR%', value)
166         except SCons.Util.RegError:
167             raise SCons.Errors.InternalError, "The VS8 settings file location was not found in the registry."
168
169     if settings_path.find('%') >= 0:
170         # Collect global environment variables
171         env_vars = {}
172
173         # Read all the global environment variables of the current user
174         k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_CURRENT_USER, r'Environment')
175         i = 0
176         while 1:
177             try:
178                 (key,val,t) = SCons.Util.RegEnumValue(k,i)
179                 env_vars[key.upper()] = val.upper()
180                 i = i + 1
181             except SCons.Util.RegError:
182                 break
183
184         # And some more variables that are not found in the registry
185         env_vars['USERPROFILE'] = os.getenv('USERPROFILE')
186         env_vars['SystemDrive'] = os.getenv('SystemDrive')
187
188         found_var = 1
189         while found_var:
190             found_var = 0
191             for env_var in env_vars:
192                 if settings_path.find(r'%' + env_var + r'%') >= 0:
193                     settings_path = settings_path.replace(r'%' + env_var + r'%', env_vars[env_var])
194                     found_var = 1
195
196     dirs = {}
197
198     if os.path.exists(settings_path):
199         # now we parse the directories from this file, if it exists.
200         import xml.dom.minidom
201         doc = xml.dom.minidom.parse(settings_path)
202         user_settings = doc.getElementsByTagName('UserSettings')[0]
203         tool_options = user_settings.getElementsByTagName('ToolsOptions')[0]
204         tool_options_categories = tool_options.getElementsByTagName('ToolsOptionsCategory')
205         for category in tool_options_categories:
206             category_name = category.attributes.get('name')
207             if category_name is not None and category_name.value == 'Projects':
208                 subcategories = category.getElementsByTagName('ToolsOptionsSubCategory')
209                 for subcategory in subcategories:
210                     subcategory_name = subcategory.attributes.get('name')
211                     if subcategory_name is not None and subcategory_name.value == 'VCDirectories':
212                         properties = subcategory.getElementsByTagName('PropertyValue')
213                         for property in properties:
214                             property_name = property.attributes.get('name')
215                             if property_name is None:
216                                 continue
217                             elif property_name.value == 'IncludeDirectories':
218                                 include_dirs = property.childNodes[0].data
219                                 # ToDo: Support for other destinations than Win32
220                                 include_dirs = include_dirs.replace('Win32|', '')
221                                 dirs['INCLUDE'] = include_dirs
222                             elif property_name.value == 'LibraryDirectories':
223                                 lib_dirs = property.childNodes[0].data.replace('Win32|', '')
224                                 # ToDo: Support for other destinations than Win32
225                                 lib_dirs = lib_dirs.replace('Win32|', '')
226                                 dirs['LIBRARY'] = lib_dirs
227                             elif property_name.value == 'ExecutableDirectories':
228                                 path_dirs = property.childNodes[0].data.replace('Win32|', '')
229                                 # ToDo: Support for other destinations than Win32
230                                 path_dirs = path_dirs.replace('Win32|', '')
231                                 dirs['PATH'] = path_dirs
232     else:
233         # There are no default directories in the registry for VS8 Express :(
234         raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
235     return dirs
236
237 def _get_msvc7_path(path, version, platform):
238     """
239     Get Visual Studio directories from version 7 (MSVS .NET)
240     (it has a different registry structure than versions before it)
241     """
242     # first, look for a customization of the default values in the
243     # registry: These are sometimes stored in the Local Settings area
244     # for Visual Studio, in a file, so we have to parse it.
245     dirs = _parse_msvc7_overrides(version,platform)
246
247     if dirs.has_key(path):
248         p = dirs[path]
249     else:
250         raise SCons.Errors.InternalError, \
251               "Unable to retrieve the %s path from MS VC++."%path
252
253     # collect some useful information for later expansions...
254     paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
255
256     # expand the directory path variables that we support.  If there
257     # is a variable we don't support, then replace that entry with
258     # "---Unknown Location VSInstallDir---" or something similar, to clue
259     # people in that we didn't find something, and so env expansion doesn't
260     # do weird things with the $(xxx)'s
261     s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
262
263     def repl(match, paths=paths):
264         key = string.upper(match.group(1))
265         if paths.has_key(key):
266             return paths[key]
267         else:
268             # Now look in the global environment variables
269             envresult = os.getenv(key)
270             if not envresult is None:
271                 return envresult + '\\'
272             else:
273                 return '---Unknown Location %s---' % match.group()
274
275     rv = []
276     for entry in p.split(os.pathsep):
277         entry = s.sub(repl,entry).rstrip('\n\r')
278         rv.append(entry)
279
280     return string.join(rv,os.pathsep)
281
282 def _get_msvc8_path(path, version, platform, suite):
283     """
284     Get Visual Studio directories from version 8 (MSVS 2005)
285     (it has a different registry structure than versions before it)
286     """
287     # first, look for a customization of the default values in the
288     # registry: These are sometimes stored in the Local Settings area
289     # for Visual Studio, in a file, so we have to parse it.
290     dirs = _parse_msvc8_overrides(version, platform, suite)
291
292     if dirs.has_key(path):
293         p = dirs[path]
294     else:
295         raise SCons.Errors.InternalError, \
296               "Unable to retrieve the %s path from MS VC++."%path
297
298     # collect some useful information for later expansions...
299     paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
300
301     # expand the directory path variables that we support.  If there
302     # is a variable we don't support, then replace that entry with
303     # "---Unknown Location VSInstallDir---" or something similar, to clue
304     # people in that we didn't find something, and so env expansion doesn't
305     # do weird things with the $(xxx)'s
306     s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
307
308     def repl(match, paths=paths):
309         key = string.upper(match.group(1))
310         if paths.has_key(key):
311             return paths[key]
312         else:
313             return '---Unknown Location %s---' % match.group()
314
315     rv = []
316     for entry in p.split(os.pathsep):
317         entry = s.sub(repl,entry).rstrip('\n\r')
318         rv.append(entry)
319
320     return string.join(rv,os.pathsep)
321
322 def get_msvc_path(env, path, version):
323     """
324     Get a list of visualstudio directories (include, lib or path).
325     Return a string delimited by the os.pathsep separator (';'). An
326     exception will be raised if unable to access the registry or
327     appropriate registry keys not found.
328     """
329
330     if not SCons.Util.can_read_reg:
331         raise SCons.Errors.InternalError, "No Windows registry module was found"
332
333     # normalize the case for comparisons (since the registry is case
334     # insensitive)
335     path = string.upper(path)
336
337     if path=='LIB':
338         path= 'LIBRARY'
339
340     version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
341     if version_num >= 8.0:
342         platform = env.get('MSVS8_PLATFORM', 'x86')
343         suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
344     else:
345         platform = 'x86'
346
347     if version_num >= 8.0:
348         return _get_msvc8_path(path, str(version_num), platform, suite)
349     elif version_num >= 7.0:
350         return _get_msvc7_path(path, str(version_num), platform)
351
352     path = string.upper(path + ' Dirs')
353     K = ('Software\\Microsoft\\Devstudio\\%s\\' +
354          'Build System\\Components\\Platforms\\Win32 (x86)\\Directories') % \
355         (version)
356     for base in (SCons.Util.HKEY_CURRENT_USER,
357                  SCons.Util.HKEY_LOCAL_MACHINE):
358         try:
359             k = SCons.Util.RegOpenKeyEx(base,K)
360             i = 0
361             while 1:
362                 try:
363                     (p,v,t) = SCons.Util.RegEnumValue(k,i)
364                     if string.upper(p) == path:
365                         return v
366                     i = i + 1
367                 except SCons.Util.RegError:
368                     break
369         except SCons.Util.RegError:
370             pass
371
372     # if we got here, then we didn't find the registry entries:
373     raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
374
375 def _get_msvc6_default_paths(version, use_mfc_dirs):
376     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
377     three environment variables that should be set in order to execute
378     the MSVC 6.0 tools properly, if the information wasn't available
379     from the registry."""
380     MVSdir = None
381     paths = {}
382     exe_path = ''
383     lib_path = ''
384     include_path = ''
385     try:
386         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
387         MVSdir = paths['VSINSTALLDIR']
388     except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
389         if os.environ.has_key('MSDEVDIR'):
390             MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
391         else:
392             MVSdir = r'C:\Program Files\Microsoft Visual Studio'
393     if MVSdir:
394         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
395             MVSVCdir = paths['VCINSTALLDIR']
396         else:
397             MVSVCdir = os.path.join(MVSdir,'VC98')
398
399         MVSCommondir = r'%s\Common' % MVSdir
400         if use_mfc_dirs:
401             mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir)
402             mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir
403         else:
404             mfc_include_ = ''
405             mfc_lib_ = ''
406         include_path = r'%s%s\include' % (mfc_include_, MVSVCdir)
407         lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir)
408
409         if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT":
410             osdir = 'WINNT'
411         else:
412             osdir = 'WIN95'
413
414         exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir,  MVSCommondir, MVSVCdir)
415     return (include_path, lib_path, exe_path)
416
417 def _get_msvc7_default_paths(env, version, use_mfc_dirs):
418     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
419     three environment variables that should be set in order to execute
420     the MSVC .NET tools properly, if the information wasn't available
421     from the registry."""
422
423     MVSdir = None
424     paths = {}
425     exe_path = ''
426     lib_path = ''
427     include_path = ''
428     try:
429         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
430         MVSdir = paths['VSINSTALLDIR']
431     except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
432         if os.environ.has_key('VSCOMNTOOLS'):
433             MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
434         else:
435             # last resort -- default install location
436             MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
437
438     if MVSdir:
439         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
440             MVSVCdir = paths['VCINSTALLDIR']
441         else:
442             MVSVCdir = os.path.join(MVSdir,'Vc7')
443
444         MVSCommondir = r'%s\Common7' % MVSdir
445         if use_mfc_dirs:
446             mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir
447             mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir
448         else:
449             mfc_include_ = ''
450             mfc_lib_ = ''
451         include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir)
452         lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir)
453         exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir )
454
455         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
456             include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR']
457             lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR']
458             exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR']
459
460         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
461             exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION'])
462
463     return (include_path, lib_path, exe_path)
464
465 def _get_msvc8_default_paths(env, version, suite, use_mfc_dirs):
466     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
467     three environment variables that should be set in order to execute
468     the MSVC 8 tools properly, if the information wasn't available
469     from the registry."""
470
471     MVSdir = None
472     paths = {}
473     exe_paths = []
474     lib_paths = []
475     include_paths = []
476     try:
477         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
478         MVSdir = paths['VSINSTALLDIR']
479     except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
480         if os.environ.has_key('VSCOMNTOOLS'):
481             MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
482         else:
483             # last resort -- default install location
484             MVSdir = os.getenv('ProgramFiles') + r'\Microsoft Visual Studio 8'
485
486     if MVSdir:
487         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
488             MVSVCdir = paths['VCINSTALLDIR']
489         else:
490             MVSVCdir = os.path.join(MVSdir,'VC')
491
492         MVSCommondir = os.path.join(MVSdir, 'Common7')
493         include_paths.append( os.path.join(MVSVCdir, 'include') )
494         lib_paths.append( os.path.join(MVSVCdir, 'lib') )
495         for base, subdir in [(MVSCommondir,'IDE'), (MVSVCdir,'bin'),
496                              (MVSCommondir,'Tools'), (MVSCommondir,r'Tools\bin')]:
497             exe_paths.append( os.path.join( base, subdir) )
498
499         if paths.has_key('PLATFORMSDKDIR'):
500             PlatformSdkDir = paths['PLATFORMSDKDIR']
501         else:
502             PlatformSdkDir = os.path.join(MVSVCdir,'PlatformSDK')
503         platform_include_path = os.path.join( PlatformSdkDir, 'Include' )
504         include_paths.append( platform_include_path )
505         lib_paths.append( os.path.join( PlatformSdkDir, 'Lib' ) )
506         if use_mfc_dirs:
507             if paths.has_key('PLATFORMSDKDIR'):
508                 include_paths.append( os.path.join( platform_include_path, 'mfc' ) )
509                 include_paths.append( os.path.join( platform_include_path, 'atl' ) )
510             else:
511                 atlmfc_path = os.path.join( MVSVCdir, 'atlmfc' )
512                 include_paths.append( os.path.join( atlmfc_path, 'include' ) )
513                 lib_paths.append( os.path.join( atlmfc_path, 'lib' ) )
514
515         env_include_path = SCons.Util.get_environment_var('INCLUDE')
516         if env_include_path:
517             include_paths.append( env_include_path )
518
519         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
520             include_paths.append( os.path.join( paths['FRAMEWORKSDKDIR'], 'include' ) )
521             lib_paths.append( os.path.join( paths['FRAMEWORKSDKDIR'], 'lib' ) )
522             exe_paths.append( paths['FRAMEWORKSDKDIR'], 'bin' )
523
524         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
525             exe_paths.append( os.path.join( paths['FRAMEWORKDIR'], paths['FRAMEWORKVERSION'] ) )
526
527     include_path = string.join( include_paths, os.pathsep )
528     lib_path = string.join(lib_paths, os.pathsep )
529     exe_path = string.join(exe_paths, os.pathsep )
530     return (include_path, lib_path, exe_path)
531
532 def get_msvc_paths(env, version=None, use_mfc_dirs=0):
533     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
534     of those three environment variables that should be set
535     in order to execute the MSVC tools properly."""
536     exe_path = ''
537     lib_path = ''
538     include_path = ''
539
540     if not version:
541         versions = SCons.Tool.msvs.get_visualstudio_versions()
542         if versions:
543             version = versions[0] #use highest version by default
544         else:
545             version = '6.0'
546
547     # Some of the configured directories only
548     # appear if the user changes them from the default.
549     # Therefore, we'll see if we can get the path to the MSDev
550     # base installation from the registry and deduce the default
551     # directories.
552     version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
553     if version_num >= 8.0:
554         suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
555         defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs)
556     elif version_num >= 7.0:
557         defpaths = _get_msvc7_default_paths(env, version, use_mfc_dirs)
558     else:
559         defpaths = _get_msvc6_default_paths(version, use_mfc_dirs)
560
561     try:
562         include_path = get_msvc_path(env, "include", version)
563     except (SCons.Util.RegError, SCons.Errors.InternalError):
564         include_path = defpaths[0]
565
566     try:
567         lib_path = get_msvc_path(env, "lib", version)
568     except (SCons.Util.RegError, SCons.Errors.InternalError):
569         lib_path = defpaths[1]
570
571     try:
572         exe_path = get_msvc_path(env, "path", version)
573     except (SCons.Util.RegError, SCons.Errors.InternalError):
574         exe_path = defpaths[2]
575
576     return (include_path, lib_path, exe_path)
577
578 def get_msvc_default_paths(env, version=None, use_mfc_dirs=0):
579     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
580     three environment variables that should be set in order to execute
581     the MSVC tools properly.  This will only return the default
582     locations for the tools, not the values used by MSVS in their
583     directory setup area.  This can help avoid problems with different
584     developers having different settings, and should allow the tools
585     to run in most cases."""
586
587     if not version and not SCons.Util.can_read_reg:
588         version = '6.0'
589
590     try:
591         if not version:
592             version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
593     except KeyboardInterrupt:
594         raise
595     except:
596         pass
597
598     version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
599     if version_num >= 8.0:
600         suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
601         defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs)
602     elif version_num >= 7.0:
603         return _get_msvc7_default_paths(env, version, use_mfc_dirs)
604     else:
605         return _get_msvc6_default_paths(version, use_mfc_dirs)
606
607 def validate_vars(env):
608     """Validate the PCH and PCHSTOP construction variables."""
609     if env.has_key('PCH') and env['PCH']:
610         if not env.has_key('PCHSTOP'):
611             raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
612         if not SCons.Util.is_String(env['PCHSTOP']):
613             raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
614
615 def pch_emitter(target, source, env):
616     """Adds the object file target."""
617
618     validate_vars(env)
619
620     pch = None
621     obj = None
622
623     for t in target:
624         if SCons.Util.splitext(str(t))[1] == '.pch':
625             pch = t
626         if SCons.Util.splitext(str(t))[1] == '.obj':
627             obj = t
628
629     if not obj:
630         obj = SCons.Util.splitext(str(pch))[0]+'.obj'
631
632     target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
633
634     return (target, source)
635
636 def object_emitter(target, source, env, parent_emitter):
637     """Sets up the PCH dependencies for an object file."""
638
639     validate_vars(env)
640
641     parent_emitter(target, source, env)
642
643     if env.has_key('PCH') and env['PCH']:
644         env.Depends(target, env['PCH'])
645
646     return (target, source)
647
648 def static_object_emitter(target, source, env):
649     return object_emitter(target, source, env,
650                           SCons.Defaults.StaticObjectEmitter)
651
652 def shared_object_emitter(target, source, env):
653     return object_emitter(target, source, env,
654                           SCons.Defaults.SharedObjectEmitter)
655
656 pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
657 pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
658                                     emitter=pch_emitter,
659                                     source_scanner=SCons.Tool.SourceFileScanner)
660 res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
661 res_builder = SCons.Builder.Builder(action=res_action, suffix='.res',
662                                     source_scanner=SCons.Tool.SourceFileScanner)
663 SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan)
664
665 def generate(env):
666     """Add Builders and construction variables for MSVC++ to an Environment."""
667     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
668
669     for suffix in CSuffixes:
670         static_obj.add_action(suffix, SCons.Defaults.CAction)
671         shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
672         static_obj.add_emitter(suffix, static_object_emitter)
673         shared_obj.add_emitter(suffix, shared_object_emitter)
674
675     for suffix in CXXSuffixes:
676         static_obj.add_action(suffix, SCons.Defaults.CXXAction)
677         shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
678         static_obj.add_emitter(suffix, static_object_emitter)
679         shared_obj.add_emitter(suffix, shared_object_emitter)
680
681     env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
682     env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
683     env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
684     env['CC']         = 'cl'
685     env['CCFLAGS']    = SCons.Util.CLVar('/nologo')
686     env['CCCOM']      = '$CC $CCFLAGS $CCCOMFLAGS'
687     env['SHCC']       = '$CC'
688     env['SHCCFLAGS']  = SCons.Util.CLVar('$CCFLAGS')
689     env['SHCCCOM']    = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
690     env['CXX']        = '$CC'
691     env['CXXFLAGS']   = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
692     env['CXXCOM']     = '$CXX $CXXFLAGS $CCCOMFLAGS'
693     env['SHCXX']      = '$CXX'
694     env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
695     env['SHCXXCOM']   = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
696     env['CPPDEFPREFIX']  = '/D'
697     env['CPPDEFSUFFIX']  = ''
698     env['INCPREFIX']  = '/I'
699     env['INCSUFFIX']  = ''
700 #    env.Append(OBJEMITTER = [static_object_emitter])
701 #    env.Append(SHOBJEMITTER = [shared_object_emitter])
702     env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
703
704     env['RC'] = 'rc'
705     env['RCFLAGS'] = SCons.Util.CLVar('')
706     env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
707     env['BUILDERS']['RES'] = res_builder
708     env['OBJPREFIX']      = ''
709     env['OBJSUFFIX']      = '.obj'
710     env['SHOBJPREFIX']    = '$OBJPREFIX'
711     env['SHOBJSUFFIX']    = '$OBJSUFFIX'
712
713     try:
714         version = SCons.Tool.msvs.get_default_visualstudio_version(env)
715         version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
716         if version_num == 8.0:
717             suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
718
719         use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 0)
720         if env.get('MSVS_IGNORE_IDE_PATHS', 0):
721             _get_paths = get_msvc_default_paths
722         else:
723             _get_paths = get_msvc_paths
724         include_path, lib_path, exe_path = _get_paths(env, version, use_mfc_dirs)
725
726         # since other tools can set these, we just make sure that the
727         # relevant stuff from MSVS is in there somewhere.
728         env.PrependENVPath('INCLUDE', include_path)
729         env.PrependENVPath('LIB', lib_path)
730         env.PrependENVPath('PATH', exe_path)
731     except (SCons.Util.RegError, SCons.Errors.InternalError):
732         pass
733
734     env['CFILESUFFIX'] = '.c'
735     env['CXXFILESUFFIX'] = '.cc'
736
737     env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
738     env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
739     env['BUILDERS']['PCH'] = pch_builder
740
741     if not env.has_key('ENV'):
742         env['ENV'] = {}
743     if not env['ENV'].has_key('SystemRoot'):    # required for dlls in the winsxs folders
744         env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()
745
746 def exists(env):
747     if SCons.Tool.msvs.is_msvs_installed():
748         # there's at least one version of MSVS installed.
749         return 1
750     else:
751         return env.Detect('cl')
752