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...
81 f = codecs.open(comps, 'r', 'utf16')
82 encoder = codecs.getencoder('ascii')
84 lines = codecs.open(comps, 'r', 'utf8').readlines()
86 lines = map(lambda l, e=encoder: e(l)[0], f.readlines())
88 lines = open(comps, 'r').readlines()
93 if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0:
95 elif line == '' or line[:1] == '[':
98 kv = line.split('=', 1)
101 key = key.replace(' Dirs','')
102 dirs[key.upper()] = val
105 # since the file didn't exist, we have only the defaults in
106 # the registry to work with.
108 K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
109 K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories'
110 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
114 (key,val,t) = SCons.Util.RegEnumValue(k,i)
115 key = key.replace(' Dirs','')
116 dirs[key.upper()] = val
118 except SCons.Util.RegError:
120 except SCons.Util.RegError:
121 # if we got here, then we didn't find the registry entries:
122 raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
125 def _get_msvc7_path(path, version, platform):
127 Get Visual Studio directories from version 7 (MSVS .NET)
128 (it has a different registry structure than versions before it)
130 # first, look for a customization of the default values in the
131 # registry: These are sometimes stored in the Local Settings area
132 # for Visual Studio, in a file, so we have to parse it.
133 dirs = _parse_msvc7_overrides(version)
135 if dirs.has_key(path):
138 raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path
140 # collect some useful information for later expansions...
141 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
143 # expand the directory path variables that we support. If there
144 # is a variable we don't support, then replace that entry with
145 # "---Unknown Location VSInstallDir---" or something similar, to clue
146 # people in that we didn't find something, and so env expansion doesn't
147 # do weird things with the $(xxx)'s
148 s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
150 def repl(match, paths=paths):
151 key = string.upper(match.group(1))
152 if paths.has_key(key):
155 return '---Unknown Location %s---' % match.group()
158 for entry in p.split(os.pathsep):
159 entry = s.sub(repl,entry).rstrip('\n\r')
162 return string.join(rv,os.pathsep)
164 def get_msvc_path (path, version, platform='x86'):
166 Get a list of visualstudio directories (include, lib or path). Return
167 a string delimited by ';'. An exception will be raised if unable to
168 access the registry or appropriate registry keys not found.
171 if not SCons.Util.can_read_reg:
172 raise SCons.Errors.InternalError, "No Windows registry module was found"
174 # normalize the case for comparisons (since the registry is case
176 path = string.upper(path)
181 if float(version) >= 7.0:
182 return _get_msvc7_path(path, version, platform)
184 path = string.upper(path + ' Dirs')
185 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
186 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
188 for base in (SCons.Util.HKEY_CURRENT_USER,
189 SCons.Util.HKEY_LOCAL_MACHINE):
191 k = SCons.Util.RegOpenKeyEx(base,K)
195 (p,v,t) = SCons.Util.RegEnumValue(k,i)
196 if string.upper(p) == path:
199 except SCons.Util.RegError:
201 except SCons.Util.RegError:
204 # if we got here, then we didn't find the registry entries:
205 raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
207 def _get_msvc6_default_paths(version, use_mfc_dirs):
208 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
209 three environment variables that should be set in order to execute
210 the MSVC 6.0 tools properly, if the information wasn't available
211 from the registry."""
218 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
219 MVSdir = paths['VSINSTALLDIR']
220 except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
221 if os.environ.has_key('MSDEVDIR'):
222 MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
224 MVSdir = r'C:\Program Files\Microsoft Visual Studio'
226 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
227 MVSVCdir = paths['VCINSTALLDIR']
229 MVSVCdir = os.path.join(MVSdir,'VC98')
231 MVSCommondir = r'%s\Common' % MVSdir
233 mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir)
234 mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir
238 include_path = r'%s%s\include' % (mfc_include_, MVSVCdir)
239 lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir)
241 if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT":
246 exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir, MVSCommondir, MVSVCdir)
247 return (include_path, lib_path, exe_path)
249 def _get_msvc7_default_paths(version, use_mfc_dirs):
250 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
251 three environment variables that should be set in order to execute
252 the MSVC .NET tools properly, if the information wasn't available
253 from the registry."""
261 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
262 MVSdir = paths['VSINSTALLDIR']
263 except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
264 if os.environ.has_key('VSCOMNTOOLS'):
265 MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
267 # last resort -- default install location
268 MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
271 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
272 MVSVCdir = paths['VCINSTALLDIR']
274 MVSVCdir = os.path.join(MVSdir,'Vc7')
276 MVSCommondir = r'%s\Common7' % MVSdir
278 mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir
279 mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir
283 include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir)
284 lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir)
285 exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir )
287 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
288 include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR']
289 lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR']
290 exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR']
292 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
293 exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION'])
295 return (include_path, lib_path, exe_path)
297 def get_msvc_paths(version=None, use_mfc_dirs=0):
298 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
299 of those three environment variables that should be set
300 in order to execute the MSVC tools properly."""
306 versions = SCons.Tool.msvs.get_visualstudio_versions()
308 version = versions[0] #use highest version by default
312 # Some of the configured directories only
313 # appear if the user changes them from the default.
314 # Therefore, we'll see if we can get the path to the MSDev
315 # base installation from the registry and deduce the default
317 if float(version) >= 7.0:
318 defpaths = _get_msvc7_default_paths(version, use_mfc_dirs)
320 defpaths = _get_msvc6_default_paths(version, use_mfc_dirs)
323 include_path = get_msvc_path("include", version)
324 except (SCons.Util.RegError, SCons.Errors.InternalError):
325 include_path = defpaths[0]
328 lib_path = get_msvc_path("lib", version)
329 except (SCons.Util.RegError, SCons.Errors.InternalError):
330 lib_path = defpaths[1]
333 exe_path = get_msvc_path("path", version)
334 except (SCons.Util.RegError, SCons.Errors.InternalError):
335 exe_path = defpaths[2]
337 return (include_path, lib_path, exe_path)
339 def get_msvc_default_paths(version=None, use_mfc_dirs=0):
340 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
341 three environment variables that should be set in order to execute
342 the MSVC tools properly. This will only return the default
343 locations for the tools, not the values used by MSVS in their
344 directory setup area. This can help avoid problems with different
345 developers having different settings, and should allow the tools
346 to run in most cases."""
348 if not version and not SCons.Util.can_read_reg:
353 version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
354 except KeyboardInterrupt:
359 if float(version) >= 7.0:
360 return _get_msvc7_default_paths(version, use_mfc_dirs)
362 return _get_msvc6_default_paths(version, use_mfc_dirs)
364 def validate_vars(env):
365 """Validate the PCH and PCHSTOP construction variables."""
366 if env.has_key('PCH') and env['PCH']:
367 if not env.has_key('PCHSTOP'):
368 raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
369 if not SCons.Util.is_String(env['PCHSTOP']):
370 raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
372 def pch_emitter(target, source, env):
373 """Adds the object file target."""
381 if SCons.Util.splitext(str(t))[1] == '.pch':
383 if SCons.Util.splitext(str(t))[1] == '.obj':
387 obj = SCons.Util.splitext(str(pch))[0]+'.obj'
389 target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
391 return (target, source)
393 def object_emitter(target, source, env, parent_emitter):
394 """Sets up the PCH dependencies for an object file."""
398 parent_emitter(target, source, env)
400 if env.has_key('PCH') and env['PCH']:
401 env.Depends(target, env['PCH'])
403 return (target, source)
405 def static_object_emitter(target, source, env):
406 return object_emitter(target, source, env,
407 SCons.Defaults.StaticObjectEmitter)
409 def shared_object_emitter(target, source, env):
410 return object_emitter(target, source, env,
411 SCons.Defaults.SharedObjectEmitter)
413 pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
414 pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
416 source_scanner=SCons.Tool.SourceFileScanner)
417 res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
418 res_builder = SCons.Builder.Builder(action=res_action, suffix='.res',
419 source_scanner=SCons.Tool.SourceFileScanner)
420 SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan)
423 """Add Builders and construction variables for MSVC++ to an Environment."""
424 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
426 for suffix in CSuffixes:
427 static_obj.add_action(suffix, SCons.Defaults.CAction)
428 shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
429 static_obj.add_emitter(suffix, static_object_emitter)
430 shared_obj.add_emitter(suffix, shared_object_emitter)
432 for suffix in CXXSuffixes:
433 static_obj.add_action(suffix, SCons.Defaults.CXXAction)
434 shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
435 static_obj.add_emitter(suffix, static_object_emitter)
436 shared_obj.add_emitter(suffix, shared_object_emitter)
438 env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
439 env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
440 env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
442 env['CCFLAGS'] = SCons.Util.CLVar('/nologo')
443 env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
445 env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
446 env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
448 env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
449 env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
450 env['SHCXX'] = '$CXX'
451 env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
452 env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
453 env['CPPDEFPREFIX'] = '/D'
454 env['CPPDEFSUFFIX'] = ''
455 env['INCPREFIX'] = '/I'
456 env['INCSUFFIX'] = ''
457 # env.Append(OBJEMITTER = [static_object_emitter])
458 # env.Append(SHOBJEMITTER = [shared_object_emitter])
459 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
462 env['RCFLAGS'] = SCons.Util.CLVar('')
463 env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
464 env['BUILDERS']['RES'] = res_builder
465 env['OBJPREFIX'] = ''
466 env['OBJSUFFIX'] = '.obj'
467 env['SHOBJPREFIX'] = '$OBJPREFIX'
468 env['SHOBJSUFFIX'] = '$OBJSUFFIX'
471 version = SCons.Tool.msvs.get_default_visualstudio_version(env)
473 use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 0)
474 if env.get('MSVS_IGNORE_IDE_PATHS', 0):
475 _get_paths = get_msvc_default_paths
477 _get_paths = get_msvc_paths
478 include_path, lib_path, exe_path = _get_paths(version, use_mfc_dirs)
480 # since other tools can set these, we just make sure that the
481 # relevant stuff from MSVS is in there somewhere.
482 env.PrependENVPath('INCLUDE', include_path)
483 env.PrependENVPath('LIB', lib_path)
484 env.PrependENVPath('PATH', exe_path)
485 except (SCons.Util.RegError, SCons.Errors.InternalError):
488 env['CFILESUFFIX'] = '.c'
489 env['CXXFILESUFFIX'] = '.cc'
491 env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
492 env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
493 env['BUILDERS']['PCH'] = pch_builder
496 if SCons.Tool.msvs.is_msvs_installed():
497 # there's at least one version of MSVS installed.
500 return env.Detect('cl')