X-Git-Url: http://git.tremily.us/?a=blobdiff_plain;f=src%2Fengine%2FSCons%2FTool%2Fmsvc.py;h=41e793aade6a7c5c807140c39bb95cedfa692f9e;hb=6a218d30e5fa1a14835a31129881b4288db7dc1d;hp=a1067c08d27e88d6ec96f33a2683e6b7f73c472a;hpb=59e64828f020d3f654718821030a23200947bacc;p=scons.git diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index a1067c08..41e793aa 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -35,7 +35,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path import re -import string +import sys import SCons.Action import SCons.Builder @@ -45,570 +45,20 @@ import SCons.Tool import SCons.Tool.msvs import SCons.Util import SCons.Warnings +import SCons.Scanner.RC + +from MSCommon import msvc_exists, msvc_setup_env_once CSuffixes = ['.c', '.C'] CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] -def _parse_msvc7_overrides(version,platform): - """ Parse any overridden defaults for MSVS directory locations - in MSVS .NET. """ - - # First, we get the shell folder for this user: - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - - comps = "" - try: - (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion' +\ - r'\Explorer\Shell Folders\Local AppData') - except SCons.Util.RegError: - raise SCons.Errors.InternalError, \ - "The Local AppData directory was not found in the registry." - - comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VCComponents.dat' - dirs = {} - - if os.path.exists(comps): - # now we parse the directories from this file, if it exists. - # We only look for entries after: - # [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories], - # since this file could contain a number of things... - lines = None - try: - import codecs - except ImportError: - pass - else: - try: - f = codecs.open(comps, 'r', 'utf16') - encoder = codecs.getencoder('ascii') - lines = map(lambda l, e=encoder: e(l)[0], f.readlines()) - except (LookupError, UnicodeError): - lines = codecs.open(comps, 'r', 'utf8').readlines() - if lines is None: - lines = open(comps, 'r').readlines() - if 'x86' == platform: platform = 'Win32' - - found = 0 - for line in lines: - line.strip() - if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories]'%platform) >= 0: - found = 1 - elif line == '' or line[:1] == '[': - found = 0 - elif found == 1: - kv = line.split('=', 1) - if len(kv) == 2: - (key, val) = kv - key = key.replace(' Dirs','') - dirs[key.upper()] = val - f.close() - else: - # since the file didn't exist, we have only the defaults in - # the registry to work with. - - if 'x86' == platform: platform = 'Win32' - - try: - K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version - K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\%s\Directories'%platform - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K) - i = 0 - while 1: - try: - (key,val,t) = SCons.Util.RegEnumValue(k,i) - key = key.replace(' Dirs','') - dirs[key.upper()] = val - i = i + 1 - except SCons.Util.RegError: - break - except SCons.Util.RegError: - # if we got here, then we didn't find the registry entries: - raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry." - return dirs - -def _parse_msvc8_overrides(version,platform,suite): - """ Parse any overridden defaults for MSVC directory locations - in MSVC 2005. """ - - # In VS8 the user can change the location of the settings file that - # contains the include, lib and binary paths. Try to get the location - # from registry - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - - # XXX This code assumes anything that isn't EXPRESS uses the default - # registry key string. Is this really true for all VS suites? - if suite == 'EXPRESS': - s = '\\VCExpress\\' - else: - s = '\\VisualStudio\\' - - settings_path = "" - try: - (settings_path, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, - r'Software\Microsoft' + s + version +\ - r'\Profile\AutoSaveFile') - settings_path = settings_path.upper() - except SCons.Util.RegError: - raise SCons.Errors.InternalError, \ - "The VS8 settings file location was not found in the registry." - - # Look for potential environment variables in the settings path - if settings_path.find('%VSSPV_VISUALSTUDIO_DIR%') >= 0: - # First replace a special variable named %vsspv_visualstudio_dir% - # that is not found in the OSs environment variables... - try: - (value, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, - r'Software\Microsoft' + s + version +\ - r'\VisualStudioLocation') - settings_path = settings_path.replace('%VSSPV_VISUALSTUDIO_DIR%', value) - except SCons.Util.RegError: - raise SCons.Errors.InternalError, "The VS8 settings file location was not found in the registry." - - if settings_path.find('%') >= 0: - # Collect global environment variables - env_vars = {} - - # Read all the global environment variables of the current user - k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_CURRENT_USER, r'Environment') - i = 0 - while 1: - try: - (key,val,t) = SCons.Util.RegEnumValue(k,i) - env_vars[key.upper()] = val.upper() - i = i + 1 - except SCons.Util.RegError: - break - - # And some more variables that are not found in the registry - env_vars['USERPROFILE'] = os.getenv('USERPROFILE') - env_vars['SystemDrive'] = os.getenv('SystemDrive') - - found_var = 1 - while found_var: - found_var = 0 - for env_var in env_vars: - if settings_path.find(r'%' + env_var + r'%') >= 0: - settings_path = settings_path.replace(r'%' + env_var + r'%', env_vars[env_var]) - found_var = 1 - - dirs = {} - - if os.path.exists(settings_path): - # now we parse the directories from this file, if it exists. - import xml.dom.minidom - doc = xml.dom.minidom.parse(settings_path) - user_settings = doc.getElementsByTagName('UserSettings')[0] - tool_options = user_settings.getElementsByTagName('ToolsOptions')[0] - tool_options_categories = tool_options.getElementsByTagName('ToolsOptionsCategory') - for category in tool_options_categories: - category_name = category.attributes.get('name') - if category_name is not None and category_name.value == 'Projects': - subcategories = category.getElementsByTagName('ToolsOptionsSubCategory') - for subcategory in subcategories: - subcategory_name = subcategory.attributes.get('name') - if subcategory_name is not None and subcategory_name.value == 'VCDirectories': - properties = subcategory.getElementsByTagName('PropertyValue') - for property in properties: - property_name = property.attributes.get('name') - if property_name is None: - continue - elif property_name.value == 'IncludeDirectories': - include_dirs = property.childNodes[0].data - # ToDo: Support for other destinations than Win32 - include_dirs = include_dirs.replace('Win32|', '') - dirs['INCLUDE'] = include_dirs - elif property_name.value == 'LibraryDirectories': - lib_dirs = property.childNodes[0].data.replace('Win32|', '') - # ToDo: Support for other destinations than Win32 - lib_dirs = lib_dirs.replace('Win32|', '') - dirs['LIBRARY'] = lib_dirs - elif property_name.value == 'ExecutableDirectories': - path_dirs = property.childNodes[0].data.replace('Win32|', '') - # ToDo: Support for other destinations than Win32 - path_dirs = path_dirs.replace('Win32|', '') - dirs['PATH'] = path_dirs - else: - # There are no default directories in the registry for VS8 Express :( - raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry." - return dirs - -def _get_msvc7_path(path, version, platform): - """ - Get Visual Studio directories from version 7 (MSVS .NET) - (it has a different registry structure than versions before it) - """ - # first, look for a customization of the default values in the - # registry: These are sometimes stored in the Local Settings area - # for Visual Studio, in a file, so we have to parse it. - dirs = _parse_msvc7_overrides(version,platform) - - if dirs.has_key(path): - p = dirs[path] - else: - raise SCons.Errors.InternalError, \ - "Unable to retrieve the %s path from MS VC++."%path - - # collect some useful information for later expansions... - paths = SCons.Tool.msvs.get_msvs_install_dirs(version) - - # expand the directory path variables that we support. If there - # is a variable we don't support, then replace that entry with - # "---Unknown Location VSInstallDir---" or something similar, to clue - # people in that we didn't find something, and so env expansion doesn't - # do weird things with the $(xxx)'s - s = re.compile('\$\(([a-zA-Z0-9_]+?)\)') - - def repl(match, paths=paths): - key = string.upper(match.group(1)) - if paths.has_key(key): - return paths[key] - else: - # Now look in the global environment variables - envresult = os.getenv(key) - if not envresult is None: - return envresult + '\\' - else: - return '---Unknown Location %s---' % match.group() - - rv = [] - for entry in p.split(os.pathsep): - entry = s.sub(repl,entry).rstrip('\n\r') - rv.append(entry) - - return string.join(rv,os.pathsep) - -def _get_msvc8_path(path, version, platform, suite): - """ - Get Visual Studio directories from version 8 (MSVS 2005) - (it has a different registry structure than versions before it) - """ - # first, look for a customization of the default values in the - # registry: These are sometimes stored in the Local Settings area - # for Visual Studio, in a file, so we have to parse it. - dirs = _parse_msvc8_overrides(version, platform, suite) - - if dirs.has_key(path): - p = dirs[path] - else: - raise SCons.Errors.InternalError, \ - "Unable to retrieve the %s path from MS VC++."%path - - # collect some useful information for later expansions... - paths = SCons.Tool.msvs.get_msvs_install_dirs(version, suite) - - # expand the directory path variables that we support. If there - # is a variable we don't support, then replace that entry with - # "---Unknown Location VSInstallDir---" or something similar, to clue - # people in that we didn't find something, and so env expansion doesn't - # do weird things with the $(xxx)'s - s = re.compile('\$\(([a-zA-Z0-9_]+?)\)') - - def repl(match, paths=paths): - key = string.upper(match.group(1)) - if paths.has_key(key): - return paths[key] - else: - return '---Unknown Location %s---' % match.group() - - rv = [] - for entry in p.split(os.pathsep): - entry = s.sub(repl,entry).rstrip('\n\r') - rv.append(entry) - - return string.join(rv,os.pathsep) - -def get_msvc_path(env, path, version): - """ - Get a list of visualstudio directories (include, lib or path). - Return a string delimited by the os.pathsep separator (';'). An - exception will be raised if unable to access the registry or - appropriate registry keys not found. - """ - - if not SCons.Util.can_read_reg: - raise SCons.Errors.InternalError, "No Windows registry module was found" - - # normalize the case for comparisons (since the registry is case - # insensitive) - path = string.upper(path) - - if path=='LIB': - path= 'LIBRARY' - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num >= 8.0: - platform = env.get('MSVS8_PLATFORM', 'x86') - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - else: - platform = 'x86' - - if version_num >= 8.0: - return _get_msvc8_path(path, str(version_num), platform, suite) - elif version_num >= 7.0: - return _get_msvc7_path(path, str(version_num), platform) - - path = string.upper(path + ' Dirs') - K = ('Software\\Microsoft\\Devstudio\\%s\\' + - 'Build System\\Components\\Platforms\\Win32 (x86)\\Directories') % \ - (version) - for base in (SCons.Util.HKEY_CURRENT_USER, - SCons.Util.HKEY_LOCAL_MACHINE): - try: - k = SCons.Util.RegOpenKeyEx(base,K) - i = 0 - while 1: - try: - (p,v,t) = SCons.Util.RegEnumValue(k,i) - if string.upper(p) == path: - return v - i = i + 1 - except SCons.Util.RegError: - break - except SCons.Util.RegError: - pass - - # if we got here, then we didn't find the registry entries: - raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path - -def _get_msvc6_default_paths(version, use_mfc_dirs): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC 6.0 tools properly, if the information wasn't available - from the registry.""" - MVSdir = None - paths = {} - exe_path = '' - lib_path = '' - include_path = '' - try: - paths = SCons.Tool.msvs.get_msvs_install_dirs(version) - MVSdir = paths['VSINSTALLDIR'] - except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError): - if os.environ.has_key('MSDEVDIR'): - MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..')) - else: - MVSdir = r'C:\Program Files\Microsoft Visual Studio' - if MVSdir: - if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): - MVSVCdir = paths['VCINSTALLDIR'] - else: - MVSVCdir = os.path.join(MVSdir,'VC98') - - MVSCommondir = r'%s\Common' % MVSdir - if use_mfc_dirs: - mfc_include_ = r'%s\ATL\include;%s\MFC\include;' % (MVSVCdir, MVSVCdir) - mfc_lib_ = r'%s\MFC\lib;' % MVSVCdir - else: - mfc_include_ = '' - mfc_lib_ = '' - include_path = r'%s%s\include' % (mfc_include_, MVSVCdir) - lib_path = r'%s%s\lib' % (mfc_lib_, MVSVCdir) - - if os.environ.has_key('OS') and os.environ['OS'] == "Windows_NT": - osdir = 'WINNT' - else: - osdir = 'WIN95' - - exe_path = r'%s\tools\%s;%s\MSDev98\bin;%s\tools;%s\bin' % (MVSCommondir, osdir, MVSCommondir, MVSCommondir, MVSVCdir) - return (include_path, lib_path, exe_path) - -def _get_msvc7_default_paths(env, version, use_mfc_dirs): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC .NET tools properly, if the information wasn't available - from the registry.""" - - MVSdir = None - paths = {} - exe_path = '' - lib_path = '' - include_path = '' - try: - paths = SCons.Tool.msvs.get_msvs_install_dirs(version) - MVSdir = paths['VSINSTALLDIR'] - except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError): - if os.environ.has_key('VSCOMNTOOLS'): - MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..')) - else: - # last resort -- default install location - MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET' - - if MVSdir: - if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): - MVSVCdir = paths['VCINSTALLDIR'] - else: - MVSVCdir = os.path.join(MVSdir,'Vc7') - - MVSCommondir = r'%s\Common7' % MVSdir - if use_mfc_dirs: - mfc_include_ = r'%s\atlmfc\include;' % MVSVCdir - mfc_lib_ = r'%s\atlmfc\lib;' % MVSVCdir - else: - mfc_include_ = '' - mfc_lib_ = '' - include_path = r'%s%s\include;%s\PlatformSDK\include' % (mfc_include_, MVSVCdir, MVSVCdir) - lib_path = r'%s%s\lib;%s\PlatformSDK\lib' % (mfc_lib_, MVSVCdir, MVSVCdir) - exe_path = r'%s\IDE;%s\bin;%s\Tools;%s\Tools\bin' % (MVSCommondir,MVSVCdir, MVSCommondir, MVSCommondir ) - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'): - include_path = include_path + r';%s\include'%paths['FRAMEWORKSDKDIR'] - lib_path = lib_path + r';%s\lib'%paths['FRAMEWORKSDKDIR'] - exe_path = exe_path + r';%s\bin'%paths['FRAMEWORKSDKDIR'] - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'): - exe_path = exe_path + r';%s\%s'%(paths['FRAMEWORKDIR'],paths['FRAMEWORKVERSION']) - - return (include_path, lib_path, exe_path) - -def _get_msvc8_default_paths(env, version, suite, use_mfc_dirs): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC 8 tools properly, if the information wasn't available - from the registry.""" - - MVSdir = None - paths = {} - exe_paths = [] - lib_paths = [] - include_paths = [] - try: - paths = SCons.Tool.msvs.get_msvs_install_dirs(version, suite) - MVSdir = paths['VSINSTALLDIR'] - except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError): - if os.environ.has_key('VSCOMNTOOLS'): - MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..')) - else: - # last resort -- default install location - MVSdir = os.getenv('ProgramFiles') + r'\Microsoft Visual Studio 8' - - if MVSdir: - if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): - MVSVCdir = paths['VCINSTALLDIR'] - else: - MVSVCdir = os.path.join(MVSdir,'VC') - - MVSCommondir = os.path.join(MVSdir, 'Common7') - include_paths.append( os.path.join(MVSVCdir, 'include') ) - lib_paths.append( os.path.join(MVSVCdir, 'lib') ) - for base, subdir in [(MVSCommondir,'IDE'), (MVSVCdir,'bin'), - (MVSCommondir,'Tools'), (MVSCommondir,r'Tools\bin')]: - exe_paths.append( os.path.join( base, subdir) ) - - if paths.has_key('PLATFORMSDKDIR'): - PlatformSdkDir = paths['PLATFORMSDKDIR'] - else: - PlatformSdkDir = os.path.join(MVSVCdir,'PlatformSDK') - platform_include_path = os.path.join( PlatformSdkDir, 'Include' ) - include_paths.append( platform_include_path ) - lib_paths.append( os.path.join( PlatformSdkDir, 'Lib' ) ) - if use_mfc_dirs: - if paths.has_key('PLATFORMSDKDIR'): - include_paths.append( os.path.join( platform_include_path, 'mfc' ) ) - include_paths.append( os.path.join( platform_include_path, 'atl' ) ) - else: - atlmfc_path = os.path.join( MVSVCdir, 'atlmfc' ) - include_paths.append( os.path.join( atlmfc_path, 'include' ) ) - lib_paths.append( os.path.join( atlmfc_path, 'lib' ) ) - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKSDKDIR'): - fwdir = paths['FRAMEWORKSDKDIR'] - include_paths.append( os.path.join( fwdir, 'include' ) ) - lib_paths.append( os.path.join( fwdir, 'lib' ) ) - exe_paths.append( os.path.join( fwdir, 'bin' ) ) - - if SCons.Util.can_read_reg and paths.has_key('FRAMEWORKDIR') and paths.has_key('FRAMEWORKVERSION'): - exe_paths.append( os.path.join( paths['FRAMEWORKDIR'], paths['FRAMEWORKVERSION'] ) ) - - include_path = string.join( include_paths, os.pathsep ) - lib_path = string.join(lib_paths, os.pathsep ) - exe_path = string.join(exe_paths, os.pathsep ) - return (include_path, lib_path, exe_path) - -def get_msvc_paths(env, version=None, use_mfc_dirs=0): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values - of those three environment variables that should be set - in order to execute the MSVC tools properly.""" - exe_path = '' - lib_path = '' - include_path = '' - - if not version: - versions = SCons.Tool.msvs.get_visualstudio_versions() - if versions: - version = versions[0] #use highest version by default - else: - version = '6.0' - - # Some of the configured directories only - # appear if the user changes them from the default. - # Therefore, we'll see if we can get the path to the MSDev - # base installation from the registry and deduce the default - # directories. - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num >= 8.0: - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - defpaths = _get_msvc8_default_paths(env, version, suite, use_mfc_dirs) - elif version_num >= 7.0: - defpaths = _get_msvc7_default_paths(env, version, use_mfc_dirs) - else: - defpaths = _get_msvc6_default_paths(version, use_mfc_dirs) - - try: - include_path = get_msvc_path(env, "include", version) - except (SCons.Util.RegError, SCons.Errors.InternalError): - include_path = defpaths[0] - - try: - lib_path = get_msvc_path(env, "lib", version) - except (SCons.Util.RegError, SCons.Errors.InternalError): - lib_path = defpaths[1] - - try: - exe_path = get_msvc_path(env, "path", version) - except (SCons.Util.RegError, SCons.Errors.InternalError): - exe_path = defpaths[2] - - return (include_path, lib_path, exe_path) - -def get_msvc_default_paths(env, version=None, use_mfc_dirs=0): - """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those - three environment variables that should be set in order to execute - the MSVC tools properly. This will only return the default - locations for the tools, not the values used by MSVS in their - directory setup area. This can help avoid problems with different - developers having different settings, and should allow the tools - to run in most cases.""" - - if not version and not SCons.Util.can_read_reg: - version = '6.0' - - try: - if not version: - version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version - except KeyboardInterrupt: - raise - except: - pass - - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num >= 8.0: - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - return _get_msvc8_default_paths(env, version, suite, use_mfc_dirs) - elif version_num >= 7.0: - return _get_msvc7_default_paths(env, version, use_mfc_dirs) - else: - return _get_msvc6_default_paths(version, use_mfc_dirs) - def validate_vars(env): """Validate the PCH and PCHSTOP construction variables.""" - if env.has_key('PCH') and env['PCH']: - if not env.has_key('PCHSTOP'): - raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined." + if 'PCH' in env and env['PCH']: + if 'PCHSTOP' not in env: + raise SCons.Errors.UserError("The PCHSTOP construction must be defined if PCH is defined.") if not SCons.Util.is_String(env['PCHSTOP']): - raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP'] + raise SCons.Errors.UserError("The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']) def pch_emitter(target, source, env): """Adds the object file target.""" @@ -638,8 +88,20 @@ def object_emitter(target, source, env, parent_emitter): parent_emitter(target, source, env) - if env.has_key('PCH') and env['PCH']: - env.Depends(target, env['PCH']) + # Add a dependency, but only if the target (e.g. 'Source1.obj') + # doesn't correspond to the pre-compiled header ('Source1.pch'). + # If the basenames match, then this was most likely caused by + # someone adding the source file to both the env.PCH() and the + # env.Program() calls, and adding the explicit dependency would + # cause a cycle on the .pch file itself. + # + # See issue #2505 for a discussion of what to do if it turns + # out this assumption causes trouble in the wild: + # http://scons.tigris.org/issues/show_bug.cgi?id=2505 + if 'PCH' in env: + pch = env['PCH'] + if str(target[0]) != SCons.Util.splitext(str(pch))[0] + '.obj': + env.Depends(target, pch) return (target, source) @@ -655,47 +117,114 @@ pch_action = SCons.Action.Action('$PCHCOM', '$PCHCOMSTR') pch_builder = SCons.Builder.Builder(action=pch_action, suffix='.pch', emitter=pch_emitter, source_scanner=SCons.Tool.SourceFileScanner) -res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') + + +# Logic to build .rc files into .res files (resource files) +res_scanner = SCons.Scanner.RC.RCScan() +res_action = SCons.Action.Action('$RCCOM', '$RCCOMSTR') res_builder = SCons.Builder.Builder(action=res_action, src_suffix='.rc', suffix='.res', src_builder=[], - source_scanner=SCons.Tool.SourceFileScanner) -SCons.Tool.SourceFileScanner.add_scanner('.rc', SCons.Defaults.CScan) + source_scanner=res_scanner) + +def msvc_batch_key(action, env, target, source): + """ + Returns a key to identify unique batches of sources for compilation. + + If batching is enabled (via the $MSVC_BATCH setting), then all + target+source pairs that use the same action, defined by the same + environment, and have the same target and source directories, will + be batched. + + Returning None specifies that the specified target+source should not + be batched with other compilations. + """ + b = env.subst('$MSVC_BATCH') + if b in (None, '', '0'): + # We're not using batching; return no key. + return None + t = target[0] + s = source[0] + if os.path.splitext(t.name)[0] != os.path.splitext(s.name)[0]: + # The base names are different, so this *must* be compiled + # separately; return no key. + return None + return (id(action), id(env), t.dir, s.dir) + +def msvc_output_flag(target, source, env, for_signature): + """ + Returns the correct /Fo flag for batching. + + If batching is disabled or there's only one source file, then we + return an /Fo string that specifies the target explicitly. Otherwise, + we return an /Fo string that just specifies the first target's + directory (where the Visual C/C++ compiler will put the .obj files). + """ + b = env.subst('$MSVC_BATCH') + if b in (None, '', '0') or len(source) == 1: + return '/Fo$TARGET' + else: + # The Visual C/C++ compiler requires a \ at the end of the /Fo + # option to indicate an output directory. We use os.sep here so + # that the test(s) for this can be run on non-Windows systems + # without having a hard-coded backslash mess up command-line + # argument parsing. + return '/Fo${TARGET.dir}' + os.sep + +CAction = SCons.Action.Action("$CCCOM", "$CCCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +ShCAction = SCons.Action.Action("$SHCCCOM", "$SHCCCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +CXXAction = SCons.Action.Action("$CXXCOM", "$CXXCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') +ShCXXAction = SCons.Action.Action("$SHCXXCOM", "$SHCXXCOMSTR", + batch_key=msvc_batch_key, + targets='$CHANGED_TARGETS') def generate(env): """Add Builders and construction variables for MSVC++ to an Environment.""" static_obj, shared_obj = SCons.Tool.createObjBuilders(env) + # TODO(batch): shouldn't reach in to cmdgen this way; necessary + # for now to bypass the checks in Builder.DictCmdGenerator.__call__() + # and allow .cc and .cpp to be compiled in the same command line. + static_obj.cmdgen.source_ext_match = False + shared_obj.cmdgen.source_ext_match = False + for suffix in CSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCAction) + static_obj.add_action(suffix, CAction) + shared_obj.add_action(suffix, ShCAction) static_obj.add_emitter(suffix, static_object_emitter) shared_obj.add_emitter(suffix, shared_object_emitter) for suffix in CXXSuffixes: - static_obj.add_action(suffix, SCons.Defaults.CXXAction) - shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction) + static_obj.add_action(suffix, CXXAction) + shared_obj.add_action(suffix, ShCXXAction) static_obj.add_emitter(suffix, static_object_emitter) shared_obj.add_emitter(suffix, shared_object_emitter) env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Z7") or ""}']) env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}']) - env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS' + env['_MSVC_OUTPUT_FLAG'] = msvc_output_flag + env['_CCCOMCOM'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS $CCPCHFLAGS $CCPDBFLAGS' env['CC'] = 'cl' env['CCFLAGS'] = SCons.Util.CLVar('/nologo') env['CFLAGS'] = SCons.Util.CLVar('') - env['CCCOM'] = '$CC $CFLAGS $CCFLAGS $CCCOMFLAGS' + env['CCCOM'] = '$CC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CFLAGS $CCFLAGS $_CCCOMCOM' env['SHCC'] = '$CC' env['SHCCFLAGS'] = SCons.Util.CLVar('$CCFLAGS') env['SHCFLAGS'] = SCons.Util.CLVar('$CFLAGS') - env['SHCCCOM'] = '$SHCC $SHCFLAGS $SHCCFLAGS $CCCOMFLAGS' + env['SHCCCOM'] = '$SHCC $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCFLAGS $SHCCFLAGS $_CCCOMCOM' env['CXX'] = '$CC' - env['CXXFLAGS'] = SCons.Util.CLVar('$CCFLAGS $( /TP $)') - env['CXXCOM'] = '$CXX $CXXFLAGS $CCCOMFLAGS' + env['CXXFLAGS'] = SCons.Util.CLVar('$( /TP $)') + env['CXXCOM'] = '$CXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $CXXFLAGS $CCFLAGS $_CCCOMCOM' env['SHCXX'] = '$CXX' env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS') - env['SHCXXCOM'] = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS' + env['SHCXXCOM'] = '$SHCXX $_MSVC_OUTPUT_FLAG /c $CHANGED_SOURCES $SHCXXFLAGS $SHCCFLAGS $_CCCOMCOM' env['CPPDEFPREFIX'] = '/D' env['CPPDEFSUFFIX'] = '' env['INCPREFIX'] = '/I' @@ -706,6 +235,7 @@ def generate(env): env['RC'] = 'rc' env['RCFLAGS'] = SCons.Util.CLVar('') + env['RCSUFFIXES']=['.rc','.rc2'] env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES' env['BUILDERS']['RES'] = res_builder env['OBJPREFIX'] = '' @@ -713,43 +243,26 @@ def generate(env): env['SHOBJPREFIX'] = '$OBJPREFIX' env['SHOBJSUFFIX'] = '$OBJSUFFIX' - try: - version = SCons.Tool.msvs.get_default_visualstudio_version(env) - version_num, suite = SCons.Tool.msvs.msvs_parse_version(version) - if version_num == 8.0: - suite = SCons.Tool.msvs.get_default_visualstudio8_suite(env) - - use_mfc_dirs = env.get('MSVS_USE_MFC_DIRS', 0) - if env.get('MSVS_IGNORE_IDE_PATHS', 0): - _get_paths = get_msvc_default_paths - else: - _get_paths = get_msvc_paths - include_path, lib_path, exe_path = _get_paths(env, version, use_mfc_dirs) - - # since other tools can set these, we just make sure that the - # relevant stuff from MSVS is in there somewhere. - env.PrependENVPath('INCLUDE', include_path) - env.PrependENVPath('LIB', lib_path) - env.PrependENVPath('PATH', exe_path) - except (SCons.Util.RegError, SCons.Errors.InternalError): - pass + # Set-up ms tools paths + msvc_setup_env_once(env) env['CFILESUFFIX'] = '.c' env['CXXFILESUFFIX'] = '.cc' env['PCHPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Yd") or ""}']) - env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' + env['PCHCOM'] = '$CXX /Fo${TARGETS[1]} $CXXFLAGS $CCFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS $PCHPDBFLAGS' env['BUILDERS']['PCH'] = pch_builder - if not env.has_key('ENV'): + if 'ENV' not in env: env['ENV'] = {} - if not env['ENV'].has_key('SystemRoot'): # required for dlls in the winsxs folders + if 'SystemRoot' not in env['ENV']: # required for dlls in the winsxs folders env['ENV']['SystemRoot'] = SCons.Platform.win32.get_system_root() def exists(env): - if SCons.Tool.msvs.is_msvs_installed(): - # there's at least one version of MSVS installed. - return 1 - else: - return env.Detect('cl') + return msvc_exists() +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: