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