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
48 import SCons.Scanner.RC
50 CSuffixes = ['.c', '.C']
51 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
53 def _parse_msvc7_overrides(version,platform):
54 """ Parse any overridden defaults for MSVS directory locations
57 # First, we get the shell folder for this user:
58 if not SCons.Util.can_read_reg:
59 raise SCons.Errors.InternalError, "No Windows registry module was found"
63 (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
64 r'Software\Microsoft\Windows\CurrentVersion' +\
65 r'\Explorer\Shell Folders\Local AppData')
66 except SCons.Util.RegError:
67 raise SCons.Errors.InternalError, \
68 "The Local AppData directory was not found in the registry."
70 comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VCComponents.dat'
73 if os.path.exists(comps):
74 # now we parse the directories from this file, if it exists.
75 # We only look for entries after:
76 # [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories],
77 # since this file could contain a number of things...
85 f = codecs.open(comps, 'r', 'utf16')
86 encoder = codecs.getencoder('ascii')
87 lines = map(lambda l, e=encoder: e(l)[0], f.readlines())
88 except (LookupError, UnicodeError):
89 lines = codecs.open(comps, 'r', 'utf8').readlines()
91 lines = open(comps, 'r').readlines()
92 if 'x86' == platform: platform = 'Win32'
97 if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories]'%platform) >= 0:
99 elif line == '' or line[:1] == '[':
102 kv = line.split('=', 1)
105 key = key.replace(' Dirs','')
106 dirs[key.upper()] = val
109 # since the file didn't exist, we have only the defaults in
110 # the registry to work with.
112 if 'x86' == platform: platform = 'Win32'
115 K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
116 K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories'%platform
117 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
121 (key,val,t) = SCons.Util.RegEnumValue(k,i)
122 key = key.replace(' Dirs','')
123 dirs[key.upper()] = val
125 except SCons.Util.RegError:
127 except SCons.Util.RegError:
128 # if we got here, then we didn't find the registry entries:
129 raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
132 def _parse_msvc8_overrides(version,platform,suite):
133 """ Parse any overridden defaults for MSVC directory locations
136 # In VS8 the user can change the location of the settings file that
137 # contains the include, lib and binary paths. Try to get the location
139 if not SCons.Util.can_read_reg:
140 raise SCons.Errors.InternalError, "No Windows registry module was found"
142 # XXX This code assumes anything that isn't EXPRESS uses the default
143 # registry key string. Is this really true for all VS suites?
144 if suite == 'EXPRESS':
147 s = '\\VisualStudio\\'
151 (settings_path, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
152 r'Software\Microsoft' + s + version +\
153 r'\Profile\AutoSaveFile')
154 settings_path = settings_path.upper()
155 except SCons.Util.RegError:
156 raise SCons.Errors.InternalError, \
157 "The VS8 settings file location was not found in the registry."
159 # Look for potential environment variables in the settings path
160 if settings_path.find('%VSSPV_VISUALSTUDIO_DIR%') >= 0:
161 # First replace a special variable named %vsspv_visualstudio_dir%
162 # that is not found in the OSs environment variables...
164 (value, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
165 r'Software\Microsoft' + s + version +\
166 r'\VisualStudioLocation')
167 settings_path = settings_path.replace('%VSSPV_VISUALSTUDIO_DIR%', value)
168 except SCons.Util.RegError:
169 raise SCons.Errors.InternalError, "The VS8 settings file location was not found in the registry."
171 if settings_path.find('%') >= 0:
172 # Collect global environment variables
175 # Read all the global environment variables of the current user
176 k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_CURRENT_USER, r'Environment')
180 (key,val,t) = SCons.Util.RegEnumValue(k,i)
181 env_vars[key.upper()] = val.upper()
183 except SCons.Util.RegError:
186 # And some more variables that are not found in the registry
187 env_vars['USERPROFILE'] = os.getenv('USERPROFILE')
188 env_vars['SystemDrive'] = os.getenv('SystemDrive')
193 for env_var in env_vars:
194 if settings_path.find(r'%' + env_var + r'%') >= 0:
195 settings_path = settings_path.replace(r'%' + env_var + r'%', env_vars[env_var])
200 if os.path.exists(settings_path):
201 # now we parse the directories from this file, if it exists.
202 import xml.dom.minidom
203 doc = xml.dom.minidom.parse(settings_path)
204 user_settings = doc.getElementsByTagName('UserSettings')[0]
205 tool_options = user_settings.getElementsByTagName('ToolsOptions')[0]
206 tool_options_categories = tool_options.getElementsByTagName('ToolsOptionsCategory')
207 environment_var_map = {
208 'IncludeDirectories' : 'INCLUDE',
209 'LibraryDirectories' : 'LIBRARY',
210 'ExecutableDirectories' : 'PATH',
212 for category in tool_options_categories:
213 category_name = category.attributes.get('name')
214 if category_name is not None and category_name.value == 'Projects':
215 subcategories = category.getElementsByTagName('ToolsOptionsSubCategory')
216 for subcategory in subcategories:
217 subcategory_name = subcategory.attributes.get('name')
218 if subcategory_name is not None and subcategory_name.value == 'VCDirectories':
219 properties = subcategory.getElementsByTagName('PropertyValue')
220 for property in properties:
221 property_name = property.attributes.get('name')
222 if property_name is None:
224 var_name = environment_var_map.get(property_name)
226 data = property.childNodes[0].data
227 value_list = string.split(data, '|')
228 if len(value_list) == 1:
229 dirs[var_name] = value_list[0]
232 dest, value = value_list[:2]
234 # ToDo: Support for destinations
237 dirs[var_name] = value
240 # There are no default directories in the registry for VS8 Express :(
241 raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
244 def _get_msvc7_path(path, version, platform):
246 Get Visual Studio directories from version 7 (MSVS .NET)
247 (it has a different registry structure than versions before it)
249 # first, look for a customization of the default values in the
250 # registry: These are sometimes stored in the Local Settings area
251 # for Visual Studio, in a file, so we have to parse it.
252 dirs = _parse_msvc7_overrides(version,platform)
254 if dirs.has_key(path):
257 raise SCons.Errors.InternalError, \
258 "Unable to retrieve the %s path from MS VC++."%path
260 # collect some useful information for later expansions...
261 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
263 # expand the directory path variables that we support. If there
264 # is a variable we don't support, then replace that entry with
265 # "---Unknown Location VSInstallDir---" or something similar, to clue
266 # people in that we didn't find something, and so env expansion doesn't
267 # do weird things with the $(xxx)'s
268 s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
270 def repl(match, paths=paths):
271 key = string.upper(match.group(1))
272 if paths.has_key(key):
275 # Now look in the global environment variables
276 envresult = os.getenv(key)
277 if not envresult is None:
278 return envresult + '\\'
280 return '---Unknown Location %s---' % match.group()
283 for entry in p.split(os.pathsep):
284 entry = s.sub(repl,entry).rstrip('\n\r')
287 return string.join(rv,os.pathsep)
289 def _get_msvc8_path(path, version, platform, suite):
291 Get Visual Studio directories from version 8 (MSVS 2005)
292 (it has a different registry structure than versions before it)
294 # first, look for a customization of the default values in the
295 # registry: These are sometimes stored in the Local Settings area
296 # for Visual Studio, in a file, so we have to parse it.
297 dirs = _parse_msvc8_overrides(version, platform, suite)
299 if dirs.has_key(path):
302 raise SCons.Errors.InternalError, \
303 "Unable to retrieve the %s path from MS VC++."%path
305 # collect some useful information for later expansions...
306 paths = SCons.Tool.msvs.get_msvs_install_dirs(version, suite)
308 # expand the directory path variables that we support. If there
309 # is a variable we don't support, then replace that entry with
310 # "---Unknown Location VSInstallDir---" or something similar, to clue
311 # people in that we didn't find something, and so env expansion doesn't
312 # do weird things with the $(xxx)'s
313 s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
315 def repl(match, paths=paths):
316 key = string.upper(match.group(1))
317 if paths.has_key(key):
320 return '---Unknown Location %s---' % match.group()
323 for entry in p.split(os.pathsep):
324 entry = s.sub(repl,entry).rstrip('\n\r')
327 return string.join(rv,os.pathsep)
329 def get_msvc_path(env, path, version):
331 Get a list of visualstudio directories (include, lib or path).
332 Return a string delimited by the os.pathsep separator (';'). An
333 exception will be raised if unable to access the registry or
334 appropriate registry keys not found.
337 if not SCons.Util.can_read_reg:
338 raise SCons.Errors.InternalError, "No Windows registry module was found"
340 # normalize the case for comparisons (since the registry is case
342 path = string.upper(path)
347 version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
348 if version_num >= 8.0:
349 platform = env.get('MSVS8_PLATFORM', 'x86')
350 suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
354 if version_num >= 8.0:
355 return _get_msvc8_path(path, str(version_num), platform, suite)
356 elif version_num >= 7.0:
357 return _get_msvc7_path(path, str(version_num), platform)
359 path = string.upper(path + ' Dirs')
360 K = ('Software\\Microsoft\\Devstudio\\%s\\' +
361 'Build System\\Components\\Platforms\\Win32 (x86)\\Directories') % \
363 for base in (SCons.Util.HKEY_CURRENT_USER,
364 SCons.Util.HKEY_LOCAL_MACHINE):
366 k = SCons.Util.RegOpenKeyEx(base,K)
370 (p,v,t) = SCons.Util.RegEnumValue(k,i)
371 if string.upper(p) == path:
374 except SCons.Util.RegError:
376 except SCons.Util.RegError:
379 # if we got here, then we didn't find the registry entries:
380 raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
382 def _get_msvc6_default_paths(version, use_mfc_dirs):
383 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
384 three environment variables that should be set in order to execute
385 the MSVC 6.0 tools properly, if the information wasn't available
386 from the registry."""
393 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
394 MVSdir = paths['VSINSTALLDIR']
395 except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
396 if os.environ.has_key('MSDEVDIR'):
397 MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
399 MVSdir = r'C:\Program Files\Microsoft Visual Studio'
401 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
402 MVSVCdir = paths['VCINSTALLDIR']
404 MVSVCdir = os.path.join(MVSdir,'VC98')
406 MVSCommondir = r'%s\Common' % MVSdir
408 mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir)
409 mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir
413 include_path = r'%s%s\include' % (mfc_include_, MVSVCdir)
414 lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir)
416 if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT":
421 exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir, MVSCommondir, MVSVCdir)
422 return (include_path, lib_path, exe_path)
424 def _get_msvc7_default_paths(env, version, use_mfc_dirs):
425 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
426 three environment variables that should be set in order to execute
427 the MSVC .NET tools properly, if the information wasn't available
428 from the registry."""
436 paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
437 MVSdir = paths['VSINSTALLDIR']
438 except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
439 if os.environ.has_key('VSCOMNTOOLS'):
440 MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
442 # last resort -- default install location
443 MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
446 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
447 MVSVCdir = paths['VCINSTALLDIR']
449 MVSVCdir = os.path.join(MVSdir,'Vc7')
451 MVSCommondir = r'%s\Common7' % MVSdir
453 mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir
454 mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir
458 include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir)
459 lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir)
460 exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir )
462 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
463 include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR']
464 lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR']
465 exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR']
467 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
468 exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION'])
470 return (include_path, lib_path, exe_path)
472 def _get_msvc8_default_paths(env, version, suite, use_mfc_dirs):
473 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
474 three environment variables that should be set in order to execute
475 the MSVC 8 tools properly, if the information wasn't available
476 from the registry."""
484 paths = SCons.Tool.msvs.get_msvs_install_dirs(version, suite)
485 MVSdir = paths['VSINSTALLDIR']
486 except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
487 if os.environ.has_key('VSCOMNTOOLS'):
488 MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
490 # last resort -- default install location
491 MVSdir = os.getenv('ProgramFiles') + r'\Microsoft Visual Studio 8'
494 if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
495 MVSVCdir = paths['VCINSTALLDIR']
497 MVSVCdir = os.path.join(MVSdir,'VC')
499 MVSCommondir = os.path.join(MVSdir, 'Common7')
500 include_paths.append( os.path.join(MVSVCdir, 'include') )
501 lib_paths.append( os.path.join(MVSVCdir, 'lib') )
502 for base, subdir in [(MVSCommondir,'IDE'), (MVSVCdir,'bin'),
503 (MVSCommondir,'Tools'), (MVSCommondir,r'Tools\bin')]:
504 exe_paths.append( os.path.join( base, subdir) )
506 if paths.has_key('PLATFORMSDKDIR'):
507 PlatformSdkDir = paths['PLATFORMSDKDIR']
509 PlatformSdkDir = os.path.join(MVSVCdir,'PlatformSDK')
510 platform_include_path = os.path.join( PlatformSdkDir, 'Include' )
511 include_paths.append( platform_include_path )
512 lib_paths.append( os.path.join( PlatformSdkDir, 'Lib' ) )
514 if paths.has_key('PLATFORMSDKDIR'):
515 include_paths.append( os.path.join( platform_include_path, 'mfc' ) )
516 include_paths.append( os.path.join( platform_include_path, 'atl' ) )
518 atlmfc_path = os.path.join( MVSVCdir, 'atlmfc' )
519 include_paths.append( os.path.join( atlmfc_path, 'include' ) )
520 lib_paths.append( os.path.join( atlmfc_path, 'lib' ) )
522 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'):
523 fwdir = paths['FRAMEWORKSDKDIR']
524 include_paths.append( os.path.join( fwdir, 'include' ) )
525 lib_paths.append( os.path.join( fwdir, 'lib' ) )
526 exe_paths.append( os.path.join( fwdir, 'bin' ) )
528 if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'):
529 exe_paths.append( os.path.join( paths['FRAMEWORKDIR'], paths['FRAMEWORKVERSION'] ) )
531 include_path = string.join( include_paths, os.pathsep )
532 lib_path = string.join(lib_paths, os.pathsep )
533 exe_path = string.join(exe_paths, os.pathsep )
534 return (include_path, lib_path, exe_path)
536 def get_msvc_paths(env, version=None, use_mfc_dirs=0):
537 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
538 of those three environment variables that should be set
539 in order to execute the MSVC tools properly."""
545 versions = SCons.Tool.msvs.get_visualstudio_versions()
547 version = versions[0] #use highest version by default
551 # Some of the configured directories only
552 # appear if the user changes them from the default.
553 # Therefore, we'll see if we can get the path to the MSDev
554 # base installation from the registry and deduce the default
556 version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
557 if version_num >= 8.0:
558 suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
559 defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs)
560 elif version_num >= 7.0:
561 defpaths = _get_msvc7_default_paths(env, version, use_mfc_dirs)
563 defpaths = _get_msvc6_default_paths(version, use_mfc_dirs)
566 include_path = get_msvc_path(env, "include", version)
567 except (SCons.Util.RegError, SCons.Errors.InternalError):
568 include_path = defpaths[0]
571 lib_path = get_msvc_path(env, "lib", version)
572 except (SCons.Util.RegError, SCons.Errors.InternalError):
573 lib_path = defpaths[1]
576 exe_path = get_msvc_path(env, "path", version)
577 except (SCons.Util.RegError, SCons.Errors.InternalError):
578 exe_path = defpaths[2]
580 return (include_path, lib_path, exe_path)
582 def get_msvc_default_paths(env, version=None, use_mfc_dirs=0):
583 """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
584 three environment variables that should be set in order to execute
585 the MSVC tools properly. This will only return the default
586 locations for the tools, not the values used by MSVS in their
587 directory setup area. This can help avoid problems with different
588 developers having different settings, and should allow the tools
589 to run in most cases."""
591 if not version and not SCons.Util.can_read_reg:
596 version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
597 except KeyboardInterrupt:
602 version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
603 if version_num >= 8.0:
604 suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
605 return _get_msvc8_default_paths(env, version, suite, use_mfc_dirs)
606 elif version_num >= 7.0:
607 return _get_msvc7_default_paths(env, version, use_mfc_dirs)
609 return _get_msvc6_default_paths(version, use_mfc_dirs)
611 def validate_vars(env):
612 """Validate the PCH and PCHSTOP construction variables."""
613 if env.has_key('PCH') and env['PCH']:
614 if not env.has_key('PCHSTOP'):
615 raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
616 if not SCons.Util.is_String(env['PCHSTOP']):
617 raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
619 def pch_emitter(target, source, env):
620 """Adds the object file target."""
628 if SCons.Util.splitext(str(t))[1] == '.pch':
630 if SCons.Util.splitext(str(t))[1] == '.obj':
634 obj = SCons.Util.splitext(str(pch))[0]+'.obj'
636 target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
638 return (target, source)
640 def object_emitter(target, source, env, parent_emitter):
641 """Sets up the PCH dependencies for an object file."""
645 parent_emitter(target, source, env)
647 if env.has_key('PCH') and env['PCH']:
648 env.Depends(target, env['PCH'])
650 return (target, source)
652 def static_object_emitter(target, source, env):
653 return object_emitter(target, source, env,
654 SCons.Defaults.StaticObjectEmitter)
656 def shared_object_emitter(target, source, env):
657 return object_emitter(target, source, env,
658 SCons.Defaults.SharedObjectEmitter)
660 pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR')
661 pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch',
663 source_scanner=SCons.Tool.SourceFileScanner)
666 # Logic to build .rc files into .res files (resource files)
667 res_scanner = SCons.Scanner.RC.RCScan()
668 res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR')
669 res_builder = SCons.Builder.Builder(action=res_action,
673 source_scanner=res_scanner)
675 def msvc_batch_key(action, env, target, source):
677 Returns a key to identify unique batches of sources for compilation.
679 If batching is enabled (via the $MSVC_BATCH setting), then all
680 target+source pairs that use the same action, defined by the same
681 environment, and have the same target and source directories, will
684 Returning None specifies that the specified target+source should not
685 be batched with other compilations.
687 b = env.subst('$MSVC_BATCH')
688 if b in (None, '', '0'):
689 # We're not using batching; return no key.
693 if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]:
694 # The base names are different, so this *must* be compiled
695 # separately; return no key.
697 return (id(action), id(env), t.dir, s.dir)
699 def msvc_output_flag(target, source, env, for_signature):
701 Returns the correct /Fo flag for batching.
703 If batching is disabled or there's only one source file, then we
704 return an /Fo string that specifies the target explicitly. Otherwise,
705 we return an /Fo string that just specifies the first target's
706 directory (where the Visual C/C++ compiler will put the .obj files).
708 b = env.subst('$MSVC_BATCH')
709 if b in (None, '', '0') or len(source) == 1:
712 # The Visual C/C++ compiler requires a \ at the end of the /Fo
713 # option to indicate an output directory. We use os.sep here so
714 # that the test(s) for this can be run on non-Windows systems
715 # without having a hard-coded backslash mess up command-line
717 return '/Fo${TARGET.dir}' + os.sep
719 CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR",
720 batch_key=msvc_batch_key,
721 targets='$CHANGED_TARGETS')
722 ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR",
723 batch_key=msvc_batch_key,
724 targets='$CHANGED_TARGETS')
725 CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR",
726 batch_key=msvc_batch_key,
727 targets='$CHANGED_TARGETS')
728 ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR",
729 batch_key=msvc_batch_key,
730 targets='$CHANGED_TARGETS')
733 """Add Builders and construction variables for MSVC++ to an Environment."""
734 static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
736 # TODO(batch): shouldn't reach in to cmdgen this way; necessary
737 # for now to bypass the checks in Builder.DictCmdGenerator.__call__()
738 # and allow .cc and .cpp to be compiled in the same command line.
739 static_obj.cmdgen.source_ext_match = False
740 shared_obj.cmdgen.source_ext_match = False
742 for suffix in CSuffixes:
743 static_obj.add_action(suffix, CAction)
744 shared_obj.add_action(suffix, ShCAction)
745 static_obj.add_emitter(suffix, static_object_emitter)
746 shared_obj.add_emitter(suffix, shared_object_emitter)
748 for suffix in CXXSuffixes:
749 static_obj.add_action(suffix, CXXAction)
750 shared_obj.add_action(suffix, ShCXXAction)
751 static_obj.add_emitter(suffix, static_object_emitter)
752 shared_obj.add_emitter(suffix, shared_object_emitter)
754 env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}'])
755 env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
756 env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag
757 env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS'
759 env['CCFLAGS'] = SCons.Util.CLVar('/nologo')
760 env['CFLAGS'] = SCons.Util.CLVar('')
761 env['CCCOM'] = '$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM'
763 env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS')
764 env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS')
765 env['SHCCCOM'] = '$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM'
767 env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
768 env['CXXCOM'] = '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM'
769 env['SHCXX'] = '$CXX'
770 env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
771 env['SHCXXCOM'] = '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM'
772 env['CPPDEFPREFIX'] = '/D'
773 env['CPPDEFSUFFIX'] = ''
774 env['INCPREFIX'] = '/I'
775 env['INCSUFFIX'] = ''
776 # env.Append(OBJEMITTER = [static_object_emitter])
777 # env.Append(SHOBJEMITTER = [shared_object_emitter])
778 env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
781 env['RCFLAGS'] = SCons.Util.CLVar('')
782 env['RCSUFFIXES']=['.rc','.rc2']
783 env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
784 env['BUILDERS']['RES'] = res_builder
785 env['OBJPREFIX'] = ''
786 env['OBJSUFFIX'] = '.obj'
787 env['SHOBJPREFIX'] = '$OBJPREFIX'
788 env['SHOBJSUFFIX'] = '$OBJSUFFIX'
791 version = SCons.Tool.msvs.get_default_visualstudio_version(env)
792 version_num, suite = SCons.Tool.msvs.msvs_parse_version(version)
793 if version_num == 8.0:
794 suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env)
796 use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 0)
797 if env.get('MSVS_IGNORE_IDE_PATHS', 0):
798 _get_paths = get_msvc_default_paths
800 _get_paths = get_msvc_paths
801 include_path, lib_path, exe_path = _get_paths(env, version, use_mfc_dirs)
803 # since other tools can set these, we just make sure that the
804 # relevant stuff from MSVS is in there somewhere.
805 env.PrependENVPath('INCLUDE', include_path)
806 env.PrependENVPath('LIB', lib_path)
807 env.PrependENVPath('PATH', exe_path)
808 except (SCons.Util.RegError, SCons.Errors.InternalError):
811 env['CFILESUFFIX'] = '.c'
812 env['CXXFILESUFFIX'] = '.cc'
814 env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}'])
815 env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS'
816 env['BUILDERS']['PCH'] = pch_builder
818 if not env.has_key('ENV'):
820 if not env['ENV'].has_key('SystemRoot'): # required for dlls in the winsxs folders
821 env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root()
824 if SCons.Tool.msvs.is_msvs_installed():
825 # there's at least one version of MSVS installed.
828 return env.Detect('cl')