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