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__"
45 CSuffixes = ['.c', '.C']
46 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
48 def get_devstudio_versions():
50 Get list of devstudio versions from the Windows registry. Return a
51 list of strings containing version numbers; an exception will be raised
52 if we were unable to access the registry (eg. couldn't import
53 a registry-access module) or the appropriate registry keys weren't
57 if not SCons.Util.can_read_reg:
58 raise SCons.Errors.InternalError, "No Windows registry module was found"
60 K = 'Software\\Microsoft\\Devstudio'
62 for base in (SCons.Util.HKEY_CLASSES_ROOT,
63 SCons.Util.HKEY_LOCAL_MACHINE,
64 SCons.Util.HKEY_CURRENT_USER,
65 SCons.Util.HKEY_USERS):
67 k = SCons.Util.RegOpenKeyEx(base,K)
71 p = SCons.Util.RegEnumKey(k,i)
72 if p[0] in '123456789' and p not in L:
74 except SCons.Util.RegError:
77 except SCons.Util.RegError:
81 raise SCons.Errors.InternalError, "DevStudio was not found."
87 def get_msvc_path (path, version, platform='x86'):
89 Get a list of devstudio directories (include, lib or path). Return
90 a string delimited by ';'. An exception will be raised if unable to
91 access the registry or appropriate registry keys not found.
94 if not SCons.Util.can_read_reg:
95 raise SCons.Errors.InternalError, "No Windows registry module was found"
99 path = string.upper(path + ' Dirs')
100 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
101 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
103 for base in (SCons.Util.HKEY_CLASSES_ROOT,
104 SCons.Util.HKEY_LOCAL_MACHINE,
105 SCons.Util.HKEY_CURRENT_USER,
106 SCons.Util.HKEY_USERS):
108 k = SCons.Util.RegOpenKeyEx(base,K)
112 (p,v,t) = SCons.Util.RegEnumValue(k,i)
113 if string.upper(p) == path:
116 except SCons.Util.RegError:
118 except SCons.Util.RegError:
121 # if we got here, then we didn't find the registry entries:
122 raise SCons.Errors.InternalError, "%s was not found in the registry."%path
124 def get_msdev_dir(version):
125 """Returns the root directory of the MSDev installation from the
126 registry if it can be found, otherwise we guess."""
127 if SCons.Util.can_read_reg:
128 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
129 'Products\\Microsoft Visual C++') % \
131 for base in (SCons.Util.HKEY_LOCAL_MACHINE,
132 SCons.Util.HKEY_CURRENT_USER):
134 k = SCons.Util.RegOpenKeyEx(base,K)
135 val, tok = SCons.Util.RegQueryValueEx(k, 'ProductDir')
136 return os.path.split(val)[0]
137 except SCons.Util.RegError:
140 def get_msdev_paths(version=None):
141 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
142 of those three environment variables that should be set
143 in order to execute the MSVC tools properly."""
149 version = get_devstudio_versions()[0] #use highest version
150 include_path = get_msvc_path("include", version)
151 lib_path = get_msvc_path("lib", version)
152 exe_path = get_msvc_path("path", version) + ";" + os.environ['PATH']
153 except (SCons.Util.RegError, SCons.Errors.InternalError):
154 # Could not get the configured directories from the registry.
155 # However, the configured directories only appear if the user
156 # changes them from the default. Therefore, we'll see if
157 # we can get the path to the MSDev base installation from
158 # the registry and deduce the default directories.
161 MVSdir = get_msdev_dir(version)
163 MVSVCdir = r'%s\VC98' % MVSdir
164 MVSCommondir = r'%s\Common' % MVSdir
165 include_path = r'%s\atl\include;%s\mfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
166 lib_path = r'%s\mfc\lib;%s\lib' % (MVSVCdir, MVSVCdir)
168 extra_path = os.pathsep + os.environ['PATH']
171 exe_path = (r'%s\MSDev98\Bin;%s\Bin' % (MVSCommondir, MVSVCdir)) + extra_path
173 # The DevStudio environment variables don't exist,
174 # so just use the variables from the source environment.
175 MVSdir = r'C:\Program Files\Microsoft Visual Studio'
176 MVSVCdir = r'%s\VC98' % MVSdir
177 MVSCommondir = r'%s\Common' % MVSdir
179 include_path = os.environ['INCLUDE']
183 lib_path = os.environ['LIB']
187 exe_path = os.environ['PATH']
190 return (include_path, lib_path, exe_path)
192 def validate_vars(env):
193 """Validate the PDB, PCH, and PCHSTOP construction variables."""
194 if env.has_key('PCH') and env['PCH']:
195 if not env.has_key('PCHSTOP'):
196 raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
197 if not SCons.Util.is_String(env['PCHSTOP']):
198 raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
200 def pch_emitter(target, source, env):
201 """Sets up the PDB dependencies for a pch file, and adds the object
210 if os.path.splitext(str(t))[1] == '.pch':
212 if os.path.splitext(str(t))[1] == '.obj':
216 obj = os.path.splitext(str(pch))[0]+'.obj'
218 target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
220 if env.has_key('PDB') and env['PDB']:
221 env.SideEffect(env['PDB'], target)
222 env.Precious(env['PDB'])
224 return (target, source)
226 def object_emitter(target, source, env):
227 """Sets up the PDB and PCH dependencies for an object file."""
231 if env.has_key('PDB') and env['PDB']:
232 env.SideEffect(env['PDB'], target)
233 env.Precious(env['PDB'])
235 if env.has_key('PCH') and env['PCH']:
236 env.Depends(target, env['PCH'])
238 return (target, source)
240 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
241 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res')
243 def generate(env, platform):
244 """Add Builders and construction variables for MSVC++ to an Environment."""
245 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
247 for suffix in CSuffixes:
248 static_obj.add_action(suffix, SCons.Defaults.CAction)
249 shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
251 for suffix in CXXSuffixes:
252 static_obj.add_action(suffix, SCons.Defaults.CXXAction)
253 shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
255 env['CCPDBFLAGS'] = '${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}'
256 env['CCPCHFLAGS'] = '${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'
257 env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
259 env['CCFLAGS'] = '/nologo'
260 env['CCCOM'] = '$CC $CCFLAGS $CCCOMFLAGS'
262 env['SHCCFLAGS'] = '$CCFLAGS'
263 env['SHCCCOM'] = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
265 env['CXXFLAGS'] = '$CCFLAGS'
266 env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS'
267 env['SHCXX'] = '$CXX'
268 env['SHCXXFLAGS'] = '$CXXFLAGS'
269 env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
270 env['INCPREFIX'] = '/I'
271 env['INCSUFFIX'] = ''
272 env['OBJEMITTER'] = object_emitter
273 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
277 env['RCCOM'] = '$RC $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
278 env.CScan.add_skey('.rc')
279 env['BUILDERS']['RES'] = res_builder
281 include_path, lib_path, exe_path = get_msdev_paths()
282 env['ENV']['INCLUDE'] = include_path
283 env['ENV']['PATH'] = exe_path
285 env['CFILESUFFIX'] = '.c'
286 env['CXXFILESUFFIX'] = '.cc'
288 env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
289 env['BUILDERS']['PCH'] = pch_builder
292 return env.Detect('cl')