1 """engine.SCons.Tool.msvc
3 Tool-specific initialization for Microsoft Visual C/C++.
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()
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:
22 # The above copyright notice and this permission notice shall be included
23 # in all copies or substantial portions of the Software.
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.
34 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
47 import SCons.Platform.win32
48 import SCons.Tool.msvs
50 CSuffixes = ['.c', '.C']
51 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
53 def _parse_msvc7_overrides(version):
54 """ Parse any overridden defaults for MSVS directory locations in MSVS .NET. """
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"
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, "The Local AppData directory was not found in the registry."
68 comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VSComponents.dat'
71 if os.path.exists(comps):
72 # now we parse the directories from this file, if it exists.
73 # We only look for entries after: [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories],
74 # since this file could contain a number of things...
81 (key, val) = line.split('=',1)
82 key = key.replace(' Dirs','')
83 dirs[key.upper()] = val
84 if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0:
91 # since the file didn't exist, we have only the defaults in
92 # the registry to work with.
94 K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
95 K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories'
96 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
100 (key,val,t) = SCons.Util.RegEnumValue(k,i)
101 key = key.replace(' Dirs','')
102 dirs[key.upper()] = val
104 except SCons.Util.RegError:
106 except SCons.Util.RegError:
107 # if we got here, then we didn't find the registry entries:
108 raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
111 def _get_msvc7_path(path, version, platform):
113 Get Visual Studio directories from version 7 (MSVS .NET)
114 (it has a different registry structure than versions before it)
116 # first, look for a customization of the default values in the
117 # registry: These are sometimes stored in the Local Settings area
118 # for Visual Studio, in a file, so we have to parse it.
119 dirs = _parse_msvc7_overrides(version)
121 if dirs.has_key(path):
124 raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path
126 # collect some useful information for later expansions...
127 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
129 # expand the directory path variables that we support. If there
130 # is a variable we don't support, then replace that entry with
131 # "---Unknown Location VSInstallDir---" or something similar, to clue
132 # people in that we didn't find something, and so env expansion doesn't
133 # do weird things with the $(xxx)'s
134 s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
137 key = string.upper(match.group(1))
138 if paths.has_key(key):
141 return '---Unknown Location %s---' % match.group()
144 for entry in p.split(os.pathsep):
145 entry = s.sub(repl,entry)
148 return string.join(rv,os.pathsep)
150 def get_msvc_path (path, version, platform='x86'):
152 Get a list of visualstudio directories (include, lib or path). Return
153 a string delimited by ';'. An exception will be raised if unable to
154 access the registry or appropriate registry keys not found.
157 if not SCons.Util.can_read_reg:
158 raise SCons.Errors.InternalError, "No Windows registry module was found"
160 # normalize the case for comparisons (since the registry is case
162 path = string.upper(path)
167 if float(version) >= 7.0:
168 return _get_msvc7_path(path, version, platform)
170 path = string.upper(path + ' Dirs')
171 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
172 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
174 for base in (SCons.Util.HKEY_CURRENT_USER,
175 SCons.Util.HKEY_LOCAL_MACHINE):
177 k = SCons.Util.RegOpenKeyEx(base,K)
181 (p,v,t) = SCons.Util.RegEnumValue(k,i)
182 if string.upper(p) == path:
185 except SCons.Util.RegError:
187 except SCons.Util.RegError:
190 # if we got here, then we didn't find the registry entries:
191 raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
193 def _get_msvc6_default_paths(version):
194 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
195 three environment variables that should be set in order to execute
196 the MSVC 6.0 tools properly, if the information wasn't available
197 from the registry."""
204 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
205 MVSdir = paths['VSINSTALLDIR']
206 except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
207 if os.environ.has_key('MSDEVDIR'):
208 MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
210 MVSdir = r'C:\Program Files\Microsoft Visual Studio'
212 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
213 MVSVCdir = paths['VCINSTALLDIR']
215 MVSVCdir = os.path.join(MVSdir,'VC98')
217 MVSCommondir = r'%s\Common' % MVSdir
218 include_path = r'%s\ATL\include;%s\MFC\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
219 lib_path = r'%s\MFC\lib;%s\lib' % (MVSVCdir, MVSVCdir)
220 exe_path = r'%s\MSDev98\bin;%s\bin' % (MVSCommondir, MVSVCdir)
221 return (include_path, lib_path, exe_path)
223 def _get_msvc7_default_paths(version):
224 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
225 three environment variables that should be set in order to execute
226 the MSVC .NET tools properly, if the information wasn't available
227 from the registry."""
235 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
236 MVSdir = paths['VSINSTALLDIR']
237 except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
238 if os.environ.has_key('VSCOMNTOOLS'):
239 MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
241 # last resort -- default install location
242 MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
245 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
246 MVSVCdir = paths['VCINSTALLDIR']
248 MVSVCdir = os.path.join(MVSdir,'Vc7')
250 MVSCommondir = r'%s\Common7' % MVSdir
251 include_path = r'%s\atlmfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
252 lib_path = r'%s\atlmfc\lib;%s\lib' % (MVSVCdir, MVSVCdir)
253 exe_path = r'%s\Tools\bin;%s\Tools;%s\bin' % (MVSCommondir, MVSCommondir, MVSVCdir)
254 return (include_path, lib_path, exe_path)
256 def get_msvc_paths(version=None):
257 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
258 of those three environment variables that should be set
259 in order to execute the MSVC tools properly."""
264 if not version and not SCons.Util.can_read_reg:
269 version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
271 include_path = get_msvc_path("include", version)
272 lib_path = get_msvc_path("lib", version)
273 exe_path = get_msvc_path("path", version)
275 except (SCons.Util.RegError, SCons.Errors.InternalError):
276 # Could not get all the configured directories from the
277 # registry. However, some of the configured directories only
278 # appear if the user changes them from the default.
279 # Therefore, we'll see if we can get the path to the MSDev
280 # base installation from the registry and deduce the default
282 if float(version) >= 7.0:
283 return _get_msvc7_default_paths(version)
285 return _get_msvc6_default_paths(version)
287 return (include_path, lib_path, exe_path)
289 def get_msvc_default_paths(version = None):
290 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
291 three environment variables that should be set in order to execute
292 the MSVC tools properly. This will only return the default
293 locations for the tools, not the values used by MSVS in their
294 directory setup area. This can help avoid problems with different
295 developers having different settings, and should allow the tools
296 to run in most cases."""
298 if not version and not SCons.Util.can_read_reg:
303 version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
307 if float(version) >= 7.0:
308 return _get_msvc7_default_paths(version)
310 return _get_msvc6_default_paths(version)
312 def validate_vars(env):
313 """Validate the PDB, PCH, and PCHSTOP construction variables."""
314 if env.has_key('PCH') and env['PCH']:
315 if not env.has_key('PCHSTOP'):
316 raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
317 if not SCons.Util.is_String(env['PCHSTOP']):
318 raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
320 def pch_emitter(target, source, env):
321 """Sets up the PDB dependencies for a pch file, and adds the object
330 if SCons.Util.splitext(str(t))[1] == '.pch':
332 if SCons.Util.splitext(str(t))[1] == '.obj':
336 obj = SCons.Util.splitext(str(pch))[0]+'.obj'
338 target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
340 if env.has_key('PDB') and env['PDB']:
341 env.SideEffect(env['PDB'], target)
342 env.Precious(env['PDB'])
344 return (target, source)
346 def object_emitter(target, source, env):
347 """Sets up the PDB and PCH dependencies for an object file."""
351 if env.has_key('PDB') and env['PDB']:
352 env.SideEffect(env['PDB'], target)
353 env.Precious(env['PDB'])
355 if env.has_key('PCH') and env['PCH']:
356 env.Depends(target, env['PCH'])
358 return (target, source)
360 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
361 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res')
364 """Add Builders and construction variables for MSVC++ to an Environment."""
365 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
367 for suffix in CSuffixes:
368 static_obj.add_action(suffix, SCons.Defaults.CAction)
369 shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
371 for suffix in CXXSuffixes:
372 static_obj.add_action(suffix, SCons.Defaults.CXXAction)
373 shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
375 env['CCPDBFLAGS'] = '${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}'
376 env['CCPCHFLAGS'] = '${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'
377 env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
379 env['CCFLAGS'] = '/nologo'
380 env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
382 env['SHCCFLAGS'] = '$CCFLAGS'
383 env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
385 env['CXXFLAGS'] = '$CCFLAGS $( /TP $)'
386 env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
387 env['SHCXX'] = '$CXX'
388 env['SHCXXFLAGS'] = '$CXXFLAGS'
389 env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
390 env['CPPDEFPREFIX'] = '/D'
391 env['CPPDEFSUFFIX'] = ''
392 env['INCPREFIX'] = '/I'
393 env['INCSUFFIX'] = ''
394 env['OBJEMITTER'] = object_emitter
395 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
399 env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
400 CScan = env.get_scanner('.c')
402 CScan.add_skey('.rc')
403 env['BUILDERS']['RES'] = res_builder
406 version = SCons.Tool.msvs.get_default_visualstudio_version(env)
408 if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
409 include_path, lib_path, exe_path = get_msvc_default_paths(version)
411 include_path, lib_path, exe_path = get_msvc_paths(version)
413 # since other tools can set these, we just make sure that the
414 # relevant stuff from MSVS is in there somewhere.
415 env.PrependENVPath('INCLUDE', include_path)
416 env.PrependENVPath('LIB', lib_path)
417 env.PrependENVPath('PATH', exe_path)
418 except (SCons.Util.RegError, SCons.Errors.InternalError):
421 env['CFILESUFFIX'] = '.c'
422 env['CXXFILESUFFIX'] = '.cc'
424 env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
425 env['BUILDERS']['PCH'] = pch_builder
429 v = SCons.Tool.msvs.get_visualstudio_versions()
430 except (SCons.Util.RegError, SCons.Errors.InternalError):
434 return env.Detect('cl')
436 # there's at least one version of MSVS installed.