Add support for Visual Studio 2005 Professional. Windows portability fixes for vario...
[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
233         dirs['VCINSTALLDIR'] = os.getenv('VCInstallDir')
234         dirs['VSINSTALLDIR'] = os.getenv('VSInstallDir')
235     else:
236         # There are no default directories in the registry for VS8 Express :(
237         raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
238     return dirs
239
240 def _get_msvc7_path(path, version, platform):
241     """
242     Get Visual Studio directories from version 7 (MSVS .NET)
243     (it has a different registry structure than versions before it)
244     """
245     # first, look for a customization of the default values in the
246     # registry: These are sometimes stored in the Local Settings area
247     # for Visual Studio, in a file, so we have to parse it.
248     dirs = _parse_msvc7_overrides(version,platform)
249
250     if dirs.has_key(path):
251         p = dirs[path]
252     else:
253         raise SCons.Errors.InternalError, \
254               "Unable to retrieve the %s path from MS VC++."%path
255
256     # collect some useful information for later expansions...
257     paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
258
259     # expand the directory path variables that we support.  If there
260     # is a variable we don't support, then replace that entry with
261     # "---Unknown Location VSInstallDir---" or something similar, to clue
262     # people in that we didn't find something, and so env expansion doesn't
263     # do weird things with the $(xxx)'s
264     s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
265
266     def repl(match, paths=paths):
267         key = string.upper(match.group(1))
268         if paths.has_key(key):
269             return paths[key]
270         else:
271             return '---Unknown Location %s---' % match.group()
272
273     rv = []
274     for entry in p.split(os.pathsep):
275         entry = s.sub(repl,entry).rstrip('\n\r')
276         rv.append(entry)
277
278     return string.join(rv,os.pathsep)
279
280 def _get_msvc8_path(path, version, platform, suite):
281     """
282     Get Visual Studio directories from version 8 (MSVS 2005)
283     (it has a different registry structure than versions before it)
284     """
285     # first, look for a customization of the default values in the
286     # registry: These are sometimes stored in the Local Settings area
287     # for Visual Studio, in a file, so we have to parse it.
288     dirs = _parse_msvc8_overrides(version, platform, suite)
289
290     if dirs.has_key(path):
291         p = dirs[path]
292     else:
293         raise SCons.Errors.InternalError, \
294               "Unable to retrieve the %s path from MS VC++."%path
295
296     # collect some useful information for later expansions...
297     paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
298
299     # expand the directory path variables that we support.  If there
300     # is a variable we don't support, then replace that entry with
301     # "---Unknown Location VSInstallDir---" or something similar, to clue
302     # people in that we didn't find something, and so env expansion doesn't
303     # do weird things with the $(xxx)'s
304     s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
305
306     def repl(match, paths=paths):
307         key = string.upper(match.group(1))
308         if paths.has_key(key):
309             return paths[key]
310         else:
311             return '---Unknown Location %s---' % match.group()
312
313     rv = []
314     for entry in p.split(os.pathsep):
315         entry = s.sub(repl,entry).rstrip('\n\r')
316         rv.append(entry)
317
318     return string.join(rv,os.pathsep)
319
320 def get_msvc_path(env, path, version):
321     """
322     Get a list of visualstudio directories (include, lib or path).
323     Return a string delimited by the os.pathsep separator (';'). An
324     exception will be raised if unable to access the registry or
325     appropriate registry keys not found.
326     """
327
328     if not SCons.Util.can_read_reg:
329         raise SCons.Errors.InternalError, "No Windows registry module was found"
330
331     # normalize the case for comparisons (since the registry is case
332     # insensitive)
333     path = string.upper(path)
334
335     if path=='LIB':
336         path= 'LIBRARY'
337
338     version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
339     if version_num >= 8.0:
340         platform = env.get('MSVS8_PLATFORM', 'x86')
341         suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
342     else:
343         platform = 'x86'
344
345     if version_num >= 8.0:
346         return _get_msvc8_path(path, version, platform, suite)
347     elif version_num >= 7.0:
348         return _get_msvc7_path(path, version, platform)
349
350     path = string.upper(path + ' Dirs')
351     K = ('Software\\Microsoft\\Devstudio\\%s\\' +
352          'Build System\\Components\\Platforms\\Win32 (x86)\\Directories') % \
353         (version)
354     for base in (SCons.Util.HKEY_CURRENT_USER,
355                  SCons.Util.HKEY_LOCAL_MACHINE):
356         try:
357             k = SCons.Util.RegOpenKeyEx(base,K)
358             i = 0
359             while 1:
360                 try:
361                     (p,v,t) = SCons.Util.RegEnumValue(k,i)
362                     if string.upper(p) == path:
363                         return v
364                     i = i + 1
365                 except SCons.Util.RegError:
366                     break
367         except SCons.Util.RegError:
368             pass
369
370     # if we got here, then we didn't find the registry entries:
371     raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
372
373 def _get_msvc6_default_paths(version, use_mfc_dirs):
374     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
375     three environment variables that should be set in order to execute
376     the MSVC 6.0 tools properly, if the information wasn't available
377     from the registry."""
378     MVSdir = None
379     paths = {}
380     exe_path = ''
381     lib_path = ''
382     include_path = ''
383     try:
384         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
385         MVSdir = paths['VSINSTALLDIR']
386     except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
387         if os.environ.has_key('MSDEVDIR'):
388             MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
389         else:
390             MVSdir = r'C:\Program Files\Microsoft Visual Studio'
391     if MVSdir:
392         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
393             MVSVCdir = paths['VCINSTALLDIR']
394         else:
395             MVSVCdir = os.path.join(MVSdir,'VC98')
396
397         MVSCommondir = r'%s\Common' % MVSdir
398         if use_mfc_dirs:
399             mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir)
400             mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir
401         else:
402             mfc_include_ = ''
403             mfc_lib_ = ''
404         include_path = r'%s%s\include' % (mfc_include_, MVSVCdir)
405         lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir)
406
407         if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT":
408             osdir = 'WINNT'
409         else:
410             osdir = 'WIN95'
411
412         exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir,  MVSCommondir, MVSVCdir)
413     return (include_path, lib_path, exe_path)
414
415 def _get_msvc7_default_paths(env, version, use_mfc_dirs):
416     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
417     three environment variables that should be set in order to execute
418     the MSVC .NET tools properly, if the information wasn't available
419     from the registry."""
420
421     MVSdir = None
422     paths = {}
423     exe_path = ''
424     lib_path = ''
425     include_path = ''
426     try:
427         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
428         MVSdir = paths['VSINSTALLDIR']
429     except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
430         if os.environ.has_key('VSCOMNTOOLS'):
431             MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
432         else:
433             # last resort -- default install location
434             MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
435
436     if MVSdir:
437         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
438             MVSVCdir = paths['VCINSTALLDIR']
439         else:
440             MVSVCdir = os.path.join(MVSdir,'Vc7')
441
442         MVSCommondir = r'%s\Common7' % MVSdir
443         if use_mfc_dirs:
444             mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir
445             mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir
446         else:
447             mfc_include_ = ''
448             mfc_lib_ = ''
449         include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir)
450         lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir)
451         exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir )
452
453         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
454             include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR']
455             lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR']
456             exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR']
457
458         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
459             exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION'])
460
461     return (include_path, lib_path, exe_path)
462
463 def _get_msvc8_default_paths(env, version, suite, use_mfc_dirs):
464     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
465     three environment variables that should be set in order to execute
466     the MSVC 8 tools properly, if the information wasn't available
467     from the registry."""
468
469     MVSdir = None
470     paths = {}
471     exe_paths = []
472     lib_paths = []
473     include_paths = []
474     try:
475         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
476         MVSdir = paths['VSINSTALLDIR']
477     except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
478         if os.environ.has_key('VSCOMNTOOLS'):
479             MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
480         else:
481             # last resort -- default install location
482             MVSdir = os.getenv('ProgramFiles') + r'\Microsoft Visual Studio 8'
483
484     if MVSdir:
485         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
486             MVSVCdir = paths['VCINSTALLDIR']
487         else:
488             MVSVCdir = os.path.join(MVSdir,'VC')
489
490         MVSCommondir = os.path.join(MVSdir, 'Common7')
491         include_paths.append( os.path.join(MVSVCdir, 'include') )
492         lib_paths.append( os.path.join(MVSVCdir, 'lib') )
493         for base, subdir in [(MVSCommondir,'IDE'), (MVSVCdir,'bin'),
494                              (MVSCommondir,'Tools'), (MVSCommondir,r'Tools\bin')]:
495             exe_paths.append( os.path.join( base, subdir) )
496
497         if paths.has_key('PLATFORMSDKDIR'):
498             PlatformSdkDir = paths['PLATFORMSDKDIR']
499         else:
500             PlatformSdkDir = os.path.join(MVSVCdir,'PlatformSDK')
501         platform_include_path = os.path.join( PlatformSdkDir, 'Include' )
502         include_paths.append( platform_include_path )
503         lib_paths.append( os.path.join( PlatformSdkDir, 'Lib' ) )
504         if use_mfc_dirs:
505             if paths.has_key('PLATFORMSDKDIR'):
506                 include_paths.append( os.path.join( platform_include_path, 'mfc' ) )
507                 include_paths.append( os.path.join( platform_include_path, 'atl' ) )
508             else:
509                 atlmfc_path = os.path.join( MVSVCdir, 'atlmfc' )
510                 include_paths.append( os.path.join( atlmfc_path, 'include' ) )
511                 lib_paths.append( os.path.join( atlmfc_path, 'lib' ) )
512
513         env_include_path = SCons.Util.get_environment_var('INCLUDE')
514         if env_include_path:
515             include_paths.append( env_include_path )
516
517         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
518             include_paths.append( os.path.join( paths['FRAMEWORKSDKDIR'], 'include' ) )
519             lib_paths.append( os.path.join( paths['FRAMEWORKSDKDIR'], 'lib' ) )
520             exe_paths.append( paths['FRAMEWORKSDKDIR'], 'bin' )
521
522         if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
523             exe_paths.append( os.path.join( paths['FRAMEWORKDIR'], paths['FRAMEWORKVERSION'] ) )
524
525     include_path = string.join( include_paths, os.pathsep )
526     lib_path = string.join(lib_paths, os.pathsep )
527     exe_path = string.join(exe_paths, os.pathsep )
528     return (include_path, lib_path, exe_path)
529
530 def get_msvc_paths(env, version=None, use_mfc_dirs=0):
531     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
532     of those three environment variables that should be set
533     in order to execute the MSVC tools properly."""
534     exe_path = ''
535     lib_path = ''
536     include_path = ''
537
538     if not version:
539         versions = SCons.Tool.msvs.get_visualstudio_versions()
540         if versions:
541             version = versions[0] #use highest version by default
542         else:
543             version = '6.0'
544
545     # Some of the configured directories only
546     # appear if the user changes them from the default.
547     # Therefore, we'll see if we can get the path to the MSDev
548     # base installation from the registry and deduce the default
549     # directories.
550     version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
551     if version_num >= 8.0:
552         suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
553         defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs)
554     elif version_num >= 7.0:
555         defpaths = _get_msvc7_default_paths(env, version, use_mfc_dirs)
556     else:
557         defpaths = _get_msvc6_default_paths(version, use_mfc_dirs)
558
559     try:
560         include_path = get_msvc_path(env, "include", version)
561     except (SCons.Util.RegError, SCons.Errors.InternalError):
562         include_path = defpaths[0]
563
564     try:
565         lib_path = get_msvc_path(env, "lib", version)
566     except (SCons.Util.RegError, SCons.Errors.InternalError):
567         lib_path = defpaths[1]
568
569     try:
570         exe_path = get_msvc_path(env, "path", version)
571     except (SCons.Util.RegError, SCons.Errors.InternalError):
572         exe_path = defpaths[2]
573
574     return (include_path, lib_path, exe_path)
575
576 def get_msvc_default_paths(env, version=None, use_mfc_dirs=0):
577     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
578     three environment variables that should be set in order to execute
579     the MSVC tools properly.  This will only return the default
580     locations for the tools, not the values used by MSVS in their
581     directory setup area.  This can help avoid problems with different
582     developers having different settings, and should allow the tools
583     to run in most cases."""
584
585     if not version and not SCons.Util.can_read_reg:
586         version = '6.0'
587
588     try:
589         if not version:
590             version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
591     except KeyboardInterrupt:
592         raise
593     except:
594         pass
595
596     version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
597     if version_num >= 8.0:
598         suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
599         defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs)
600     elif version_num >= 7.0:
601         return _get_msvc7_default_paths(env, version, use_mfc_dirs)
602     else:
603         return _get_msvc6_default_paths(version, use_mfc_dirs)
604
605 def validate_vars(env):
606     """Validate the PCH and PCHSTOP construction variables."""
607     if env.has_key('PCH') and env['PCH']:
608         if not env.has_key('PCHSTOP'):
609             raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
610         if not SCons.Util.is_String(env['PCHSTOP']):
611             raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
612
613 def pch_emitter(target, source, env):
614     """Adds the object file target."""
615
616     validate_vars(env)
617
618     pch = None
619     obj = None
620
621     for t in target:
622         if SCons.Util.splitext(str(t))[1] == '.pch':
623             pch = t
624         if SCons.Util.splitext(str(t))[1] == '.obj':
625             obj = t
626
627     if not obj:
628         obj = SCons.Util.splitext(str(pch))[0]+'.obj'
629
630     target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
631
632     return (target, source)
633
634 def object_emitter(target, source, env, parent_emitter):
635     """Sets up the PCH dependencies for an object file."""
636
637     validate_vars(env)
638
639     parent_emitter(target, source, env)
640
641     if env.has_key('PCH') and env['PCH']:
642         env.Depends(target, env['PCH'])
643
644     return (target, source)
645
646 def static_object_emitter(target, source, env):
647     return object_emitter(target, source, env,
648                           SCons.Defaults.StaticObjectEmitter)
649
650 def shared_object_emitter(target, source, env):
651     return object_emitter(target, source, env,
652                           SCons.Defaults.SharedObjectEmitter)
653
654 pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
655 pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
656                                     emitter=pch_emitter,
657                                     source_scanner=SCons.Tool.SourceFileScanner)
658 res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
659 res_builder = SCons.Builder.Builder(action=res_action, suffix='.res',
660                                     source_scanner=SCons.Tool.SourceFileScanner)
661 SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan)
662
663 def generate(env):
664     """Add Builders and construction variables for MSVC++ to an Environment."""
665     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
666
667     for suffix in CSuffixes:
668         static_obj.add_action(suffix, SCons.Defaults.CAction)
669         shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
670         static_obj.add_emitter(suffix, static_object_emitter)
671         shared_obj.add_emitter(suffix, shared_object_emitter)
672
673     for suffix in CXXSuffixes:
674         static_obj.add_action(suffix, SCons.Defaults.CXXAction)
675         shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
676         static_obj.add_emitter(suffix, static_object_emitter)
677         shared_obj.add_emitter(suffix, shared_object_emitter)
678
679     env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
680     env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
681     env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
682     env['CC']         = 'cl'
683     env['CCFLAGS']    = SCons.Util.CLVar('/nologo')
684     env['CCCOM']      = '$CC $CCFLAGS $CCCOMFLAGS'
685     env['SHCC']       = '$CC'
686     env['SHCCFLAGS']  = SCons.Util.CLVar('$CCFLAGS')
687     env['SHCCCOM']    = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
688     env['CXX']        = '$CC'
689     env['CXXFLAGS']   = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
690     env['CXXCOM']     = '$CXX $CXXFLAGS $CCCOMFLAGS'
691     env['SHCXX']      = '$CXX'
692     env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
693     env['SHCXXCOM']   = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
694     env['CPPDEFPREFIX']  = '/D'
695     env['CPPDEFSUFFIX']  = ''
696     env['INCPREFIX']  = '/I'
697     env['INCSUFFIX']  = ''
698 #    env.Append(OBJEMITTER = [static_object_emitter])
699 #    env.Append(SHOBJEMITTER = [shared_object_emitter])
700     env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
701
702     env['RC'] = 'rc'
703     env['RCFLAGS'] = SCons.Util.CLVar('')
704     env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
705     env['BUILDERS']['RES'] = res_builder
706     env['OBJPREFIX']      = ''
707     env['OBJSUFFIX']      = '.obj'
708     env['SHOBJPREFIX']    = '$OBJPREFIX'
709     env['SHOBJSUFFIX']    = '$OBJSUFFIX'
710
711     try:
712         version = SCons.Tool.msvs.get_default_visualstudio_version(env)
713         version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
714         if version_num == 8.0:
715             suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
716
717         use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 0)
718         if env.get('MSVS_IGNORE_IDE_PATHS', 0):
719             _get_paths = get_msvc_default_paths
720         else:
721             _get_paths = get_msvc_paths
722         include_path, lib_path, exe_path = _get_paths(env, version, use_mfc_dirs)
723
724         # since other tools can set these, we just make sure that the
725         # relevant stuff from MSVS is in there somewhere.
726         env.PrependENVPath('INCLUDE', include_path)
727         env.PrependENVPath('LIB', lib_path)
728         env.PrependENVPath('PATH', exe_path)
729     except (SCons.Util.RegError, SCons.Errors.InternalError):
730         pass
731
732     env['CFILESUFFIX'] = '.c'
733     env['CXXFILESUFFIX'] = '.cc'
734
735     env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
736     env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
737     env['BUILDERS']['PCH'] = pch_builder
738
739     if not env.has_key('ENV'):
740         env['ENV'] = {}
741     if not env['ENV'].has_key('SystemRoot'):    # required for dlls in the winsxs folders
742         env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()
743
744 def exists(env):
745     if SCons.Tool.msvs.is_msvs_installed():
746         # there's at least one version of MSVS installed.
747         return 1
748     else:
749         return env.Detect('cl')
750