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_]+?)\)')
136 def repl(match, paths=paths):
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."""
265 versions = SCons.Tool.msvs.get_visualstudio_versions()
267 version = versions[0] #use highest version by default
271 # Some of the configured directories only
272 # appear if the user changes them from the default.
273 # Therefore, we'll see if we can get the path to the MSDev
274 # base installation from the registry and deduce the default
276 if float(version) >= 7.0:
277 defpaths = _get_msvc7_default_paths(version)
279 defpaths = _get_msvc6_default_paths(version)
282 include_path = get_msvc_path("include", version)
283 except (SCons.Util.RegError, SCons.Errors.InternalError):
284 include_path = defpaths[0]
287 lib_path = get_msvc_path("lib", version)
288 except (SCons.Util.RegError, SCons.Errors.InternalError):
289 lib_path = defpaths[1]
292 exe_path = get_msvc_path("path", version)
293 except (SCons.Util.RegError, SCons.Errors.InternalError):
294 exe_path = defpaths[2]
296 return (include_path, lib_path, exe_path)
298 def get_msvc_default_paths(version = None):
299 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
300 three environment variables that should be set in order to execute
301 the MSVC tools properly. This will only return the default
302 locations for the tools, not the values used by MSVS in their
303 directory setup area. This can help avoid problems with different
304 developers having different settings, and should allow the tools
305 to run in most cases."""
307 if not version and not SCons.Util.can_read_reg:
312 version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
313 except KeyboardInterrupt:
318 if float(version) >= 7.0:
319 return _get_msvc7_default_paths(version)
321 return _get_msvc6_default_paths(version)
323 def validate_vars(env):
324 """Validate the PDB, PCH, and PCHSTOP construction variables."""
325 if env.has_key('PCH') and env['PCH']:
326 if not env.has_key('PCHSTOP'):
327 raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
328 if not SCons.Util.is_String(env['PCHSTOP']):
329 raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
331 def pch_emitter(target, source, env):
332 """Sets up the PDB dependencies for a pch file, and adds the object
341 if SCons.Util.splitext(str(t))[1] == '.pch':
343 if SCons.Util.splitext(str(t))[1] == '.obj':
347 obj = SCons.Util.splitext(str(pch))[0]+'.obj'
349 target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
351 if env.has_key('PDB') and env['PDB']:
352 env.SideEffect(env['PDB'], target)
353 env.Precious(env['PDB'])
355 return (target, source)
357 def object_emitter(target, source, env, parent_emitter):
358 """Sets up the PDB and PCH dependencies for an object file."""
362 parent_emitter(target, source, env)
364 if env.has_key('PDB') and env['PDB']:
365 env.SideEffect(env['PDB'], target)
366 env.Precious(env['PDB'])
368 if env.has_key('PCH') and env['PCH']:
369 env.Depends(target, env['PCH'])
371 return (target, source)
373 def static_object_emitter(target, source, env):
374 return object_emitter(target, source, env,
375 SCons.Defaults.StaticObjectEmitter)
377 def shared_object_emitter(target, source, env):
378 return object_emitter(target, source, env,
379 SCons.Defaults.SharedObjectEmitter)
381 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
382 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res')
385 """Add Builders and construction variables for MSVC++ to an Environment."""
386 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
388 for suffix in CSuffixes:
389 static_obj.add_action(suffix, SCons.Defaults.CAction)
390 shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
392 for suffix in CXXSuffixes:
393 static_obj.add_action(suffix, SCons.Defaults.CXXAction)
394 shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
396 env['CCPDBFLAGS'] = '${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}'
397 env['CCPCHFLAGS'] = '${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'
398 env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
400 env['CCFLAGS'] = '/nologo'
401 env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
403 env['SHCCFLAGS'] = '$CCFLAGS'
404 env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
406 env['CXXFLAGS'] = ['$CCFLAGS', '$(', '/TP', '$)']
407 env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
408 env['SHCXX'] = '$CXX'
409 env['SHCXXFLAGS'] = '$CXXFLAGS'
410 env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
411 env['CPPDEFPREFIX'] = '/D'
412 env['CPPDEFSUFFIX'] = ''
413 env['INCPREFIX'] = '/I'
414 env['INCSUFFIX'] = ''
415 env['OBJEMITTER'] = static_object_emitter
416 env['SHOBJEMITTER'] = shared_object_emitter
417 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
421 env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
422 CScan = env.get_scanner('.c')
424 CScan.add_skey('.rc')
425 env['BUILDERS']['RES'] = res_builder
428 version = SCons.Tool.msvs.get_default_visualstudio_version(env)
430 if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
431 include_path, lib_path, exe_path = get_msvc_default_paths(version)
433 include_path, lib_path, exe_path = get_msvc_paths(version)
435 # since other tools can set these, we just make sure that the
436 # relevant stuff from MSVS is in there somewhere.
437 env.PrependENVPath('INCLUDE', include_path)
438 env.PrependENVPath('LIB', lib_path)
439 env.PrependENVPath('PATH', exe_path)
440 except (SCons.Util.RegError, SCons.Errors.InternalError):
443 env['CFILESUFFIX'] = '.c'
444 env['CXXFILESUFFIX'] = '.cc'
446 env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
447 env['BUILDERS']['PCH'] = pch_builder
451 v = SCons.Tool.msvs.get_visualstudio_versions()
452 except (SCons.Util.RegError, SCons.Errors.InternalError):
456 return env.Detect('cl')
458 # there's at least one version of MSVS installed.