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__"
43 import SCons.Platform.win32
45 import SCons.Tool.msvs
49 CSuffixes = ['.c', '.C']
50 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
52 def _parse_msvc7_overrides(version):
53 """ Parse any overridden defaults for MSVS directory locations in MSVS .NET. """
55 # First, we get the shell folder for this user:
56 if not SCons.Util.can_read_reg:
57 raise SCons.Errors.InternalError, "No Windows registry module was found"
61 (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
62 r'Software\Microsoft\Windows\CurrentVersion' +\
63 r'\Explorer\Shell Folders\Local AppData')
64 except SCons.Util.RegError:
65 raise SCons.Errors.InternalError, "The Local AppData directory was not found in the registry."
67 comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VCComponents.dat'
70 if os.path.exists(comps):
71 # now we parse the directories from this file, if it exists.
72 # We only look for entries after: [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories],
73 # since this file could contain a number of things...
79 if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0:
81 elif line == '' or line[:1] == '[':
84 kv = line.split('=', 1)
87 key = key.replace(' Dirs','')
88 dirs[key.upper()] = val
92 # since the file didn't exist, we have only the defaults in
93 # the registry to work with.
95 K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
96 K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories'
97 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
101 (key,val,t) = SCons.Util.RegEnumValue(k,i)
102 key = key.replace(' Dirs','')
103 dirs[key.upper()] = val
105 except SCons.Util.RegError:
107 except SCons.Util.RegError:
108 # if we got here, then we didn't find the registry entries:
109 raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
112 def _get_msvc7_path(path, version, platform):
114 Get Visual Studio directories from version 7 (MSVS .NET)
115 (it has a different registry structure than versions before it)
117 # first, look for a customization of the default values in the
118 # registry: These are sometimes stored in the Local Settings area
119 # for Visual Studio, in a file, so we have to parse it.
120 dirs = _parse_msvc7_overrides(version)
122 if dirs.has_key(path):
125 raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path
127 # collect some useful information for later expansions...
128 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
130 # expand the directory path variables that we support. If there
131 # is a variable we don't support, then replace that entry with
132 # "---Unknown Location VSInstallDir---" or something similar, to clue
133 # people in that we didn't find something, and so env expansion doesn't
134 # do weird things with the $(xxx)'s
135 s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
137 def repl(match, paths=paths):
138 key = string.upper(match.group(1))
139 if paths.has_key(key):
142 return '---Unknown Location %s---' % match.group()
145 for entry in p.split(os.pathsep):
146 entry = s.sub(repl,entry)
149 return string.join(rv,os.pathsep)
151 def get_msvc_path (path, version, platform='x86'):
153 Get a list of visualstudio directories (include, lib or path). Return
154 a string delimited by ';'. An exception will be raised if unable to
155 access the registry or appropriate registry keys not found.
158 if not SCons.Util.can_read_reg:
159 raise SCons.Errors.InternalError, "No Windows registry module was found"
161 # normalize the case for comparisons (since the registry is case
163 path = string.upper(path)
168 if float(version) >= 7.0:
169 return _get_msvc7_path(path, version, platform)
171 path = string.upper(path + ' Dirs')
172 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
173 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
175 for base in (SCons.Util.HKEY_CURRENT_USER,
176 SCons.Util.HKEY_LOCAL_MACHINE):
178 k = SCons.Util.RegOpenKeyEx(base,K)
182 (p,v,t) = SCons.Util.RegEnumValue(k,i)
183 if string.upper(p) == path:
186 except SCons.Util.RegError:
188 except SCons.Util.RegError:
191 # if we got here, then we didn't find the registry entries:
192 raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
194 def _get_msvc6_default_paths(version, use_mfc_dirs):
195 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
196 three environment variables that should be set in order to execute
197 the MSVC 6.0 tools properly, if the information wasn't available
198 from the registry."""
205 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
206 MVSdir = paths['VSINSTALLDIR']
207 except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
208 if os.environ.has_key('MSDEVDIR'):
209 MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
211 MVSdir = r'C:\Program Files\Microsoft Visual Studio'
213 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
214 MVSVCdir = paths['VCINSTALLDIR']
216 MVSVCdir = os.path.join(MVSdir,'VC98')
218 MVSCommondir = r'%s\Common' % MVSdir
220 mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir)
221 mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir
225 include_path = r'%s%s\include' % (mfc_include_, MVSVCdir)
226 lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir)
228 if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT":
233 exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir, MVSCommondir, MVSVCdir)
234 return (include_path, lib_path, exe_path)
236 def _get_msvc7_default_paths(version, use_mfc_dirs):
237 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
238 three environment variables that should be set in order to execute
239 the MSVC .NET tools properly, if the information wasn't available
240 from the registry."""
248 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
249 MVSdir = paths['VSINSTALLDIR']
250 except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
251 if os.environ.has_key('VSCOMNTOOLS'):
252 MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
254 # last resort -- default install location
255 MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
258 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
259 MVSVCdir = paths['VCINSTALLDIR']
261 MVSVCdir = os.path.join(MVSdir,'Vc7')
263 MVSCommondir = r'%s\Common7' % MVSdir
265 mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir
266 mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir
270 include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir)
271 lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir)
272 exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir )
274 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
275 include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR']
276 lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR']
277 exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR']
279 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
280 exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION'])
282 return (include_path, lib_path, exe_path)
284 def get_msvc_paths(version=None, use_mfc_dirs=0):
285 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
286 of those three environment variables that should be set
287 in order to execute the MSVC tools properly."""
293 versions = SCons.Tool.msvs.get_visualstudio_versions()
295 version = versions[0] #use highest version by default
299 # Some of the configured directories only
300 # appear if the user changes them from the default.
301 # Therefore, we'll see if we can get the path to the MSDev
302 # base installation from the registry and deduce the default
304 if float(version) >= 7.0:
305 defpaths = _get_msvc7_default_paths(version, use_mfc_dirs)
307 defpaths = _get_msvc6_default_paths(version, use_mfc_dirs)
310 include_path = get_msvc_path("include", version)
311 except (SCons.Util.RegError, SCons.Errors.InternalError):
312 include_path = defpaths[0]
315 lib_path = get_msvc_path("lib", version)
316 except (SCons.Util.RegError, SCons.Errors.InternalError):
317 lib_path = defpaths[1]
320 exe_path = get_msvc_path("path", version)
321 except (SCons.Util.RegError, SCons.Errors.InternalError):
322 exe_path = defpaths[2]
324 return (include_path, lib_path, exe_path)
326 def get_msvc_default_paths(version=None, use_mfc_dirs=0):
327 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
328 three environment variables that should be set in order to execute
329 the MSVC tools properly. This will only return the default
330 locations for the tools, not the values used by MSVS in their
331 directory setup area. This can help avoid problems with different
332 developers having different settings, and should allow the tools
333 to run in most cases."""
335 if not version and not SCons.Util.can_read_reg:
340 version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
341 except KeyboardInterrupt:
346 if float(version) >= 7.0:
347 return _get_msvc7_default_paths(version, use_mfc_dirs)
349 return _get_msvc6_default_paths(version, use_mfc_dirs)
351 def validate_vars(env):
352 """Validate the PCH and PCHSTOP construction variables."""
353 if env.has_key('PCH') and env['PCH']:
354 if not env.has_key('PCHSTOP'):
355 raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
356 if not SCons.Util.is_String(env['PCHSTOP']):
357 raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
359 def pch_emitter(target, source, env):
360 """Adds the object file target."""
368 if SCons.Util.splitext(str(t))[1] == '.pch':
370 if SCons.Util.splitext(str(t))[1] == '.obj':
374 obj = SCons.Util.splitext(str(pch))[0]+'.obj'
376 target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
378 return (target, source)
380 def object_emitter(target, source, env, parent_emitter):
381 """Sets up the PCH dependencies for an object file."""
385 parent_emitter(target, source, env)
387 if env.has_key('PCH') and env['PCH']:
388 env.Depends(target, env['PCH'])
390 return (target, source)
392 def static_object_emitter(target, source, env):
393 return object_emitter(target, source, env,
394 SCons.Defaults.StaticObjectEmitter)
396 def shared_object_emitter(target, source, env):
397 return object_emitter(target, source, env,
398 SCons.Defaults.SharedObjectEmitter)
400 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter,
401 source_scanner=SCons.Defaults.ObjSourceScan)
402 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res',
403 source_scanner=SCons.Defaults.ObjSourceScan)
404 SCons.Defaults.ObjSourceScan.add_scanner('.rc', SCons.Defaults.CScan)
407 """Add Builders and construction variables for MSVC++ to an Environment."""
408 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
410 for suffix in CSuffixes:
411 static_obj.add_action(suffix, SCons.Defaults.CAction)
412 shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
413 static_obj.add_emitter(suffix, static_object_emitter)
414 shared_obj.add_emitter(suffix, shared_object_emitter)
416 for suffix in CXXSuffixes:
417 static_obj.add_action(suffix, SCons.Defaults.CXXAction)
418 shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
419 static_obj.add_emitter(suffix, static_object_emitter)
420 shared_obj.add_emitter(suffix, shared_object_emitter)
422 env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
423 env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
424 env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
426 env['CCFLAGS'] = SCons.Util.CLVar('/nologo')
427 env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
429 env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
430 env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
432 env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
433 env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
434 env['SHCXX'] = '$CXX'
435 env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
436 env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
437 env['CPPDEFPREFIX'] = '/D'
438 env['CPPDEFSUFFIX'] = ''
439 env['INCPREFIX'] = '/I'
440 env['INCSUFFIX'] = ''
441 # env.Append(OBJEMITTER = [static_object_emitter])
442 # env.Append(SHOBJEMITTER = [shared_object_emitter])
443 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
446 env['RCFLAGS'] = SCons.Util.CLVar('')
447 env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
448 env['BUILDERS']['RES'] = res_builder
449 env['OBJPREFIX'] = ''
450 env['OBJSUFFIX'] = '.obj'
451 env['SHOBJPREFIX'] = '$OBJPREFIX'
452 env['SHOBJSUFFIX'] = '$OBJSUFFIX'
455 version = SCons.Tool.msvs.get_default_visualstudio_version(env)
457 # By default, add the MFC directories, because this is what
458 # we've been doing for a long time. We may change this.
459 use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 1)
460 if env.get('MSVS_IGNORE_IDE_PATHS', 0):
461 _get_paths = get_msvc_default_paths
463 _get_paths = get_msvc_paths
464 include_path, lib_path, exe_path = _get_paths(version, use_mfc_dirs)
466 # since other tools can set these, we just make sure that the
467 # relevant stuff from MSVS is in there somewhere.
468 env.PrependENVPath('INCLUDE', include_path)
469 env.PrependENVPath('LIB', lib_path)
470 env.PrependENVPath('PATH', exe_path)
471 except (SCons.Util.RegError, SCons.Errors.InternalError):
474 env['CFILESUFFIX'] = '.c'
475 env['CXXFILESUFFIX'] = '.cc'
477 env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
478 env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
479 env['BUILDERS']['PCH'] = pch_builder
482 if SCons.Tool.msvs.is_msvs_installed():
483 # there's at least one version of MSVS installed.
486 return env.Detect('cl')