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