diff --git a/CHANGES.txt b/CHANGES.txt index e592ff2859..ecd4c604b6 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -109,6 +109,15 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER processors for testing threads. - Fixed crash in C scanner's dictify_CPPDEFINES() function which happens if AppendUnique is called on CPPPATH. (Issue #4108). + - The MSVC script_env_cache now contains a sanity check: if the retrieved + tools path does not exist, the entry is invalidated so it will + be recomputed, in an attempt to avoid scons failing when certain + compiler version bumps have taken place. The dictionary key (uses + the name of a batch file and any arguments which may have been + passes), is now computed a bit differently: the dashes are left + off if there are no arguments. The default cachefile is changed + to have a .json suffix, for better recognition on Windows since + the contents are json. - As "code modernization" all of SCons now uses the current super() zero-argument syntax instead of direct calls to a parent class method or the super() two-argument syntax. diff --git a/RELEASE.txt b/RELEASE.txt index a6ffbeec27..15b78dcb8d 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -30,9 +30,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY --------------------------------------- - On Windows, %AllUsersProfile%\scons\site_scons is now the default "system" - location for a site_scons. %AllUsersProfile%\Application Data\scons\site_scons - will continue to work. There does not seem to be any convention to use - an "Application Data" subdirectory here. + location for a site_scons directory. + %AllUsersProfile%\Application Data\scons\site_scons will continue to work. + There does not seem to be any existing convention to use an + "Application Data" subdirectory here. - Action._subproc() can now be used as a python context manager to ensure that the POpen object is properly closed. - SCons help (-H) no longer prints the "ignored for compatibility" options, @@ -45,6 +46,12 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY - The change to "content" and "content-timestamp" Decider names is reflected in the User Guide as well, since the hash function may be other than md5 (tidying up from earlier change) +- If SCONS_CACHE_MSVC_CONFIG is used, it will now attempt a sanity check for + the cached compiler information, and regenerate the information + if needed, rather than just failing after certain compiler version + changes have happened. The cache file can still be manually removed + if there are issues to force a regen. The default cache filename now + has a .json suffix - the contents have always been json. - Update ninja file generation to only create response files for build commands which exceed MAXLINELENGTH - Update the debug output written to stdout for MSVC initialization which is enabled diff --git a/SCons/Tool/MSCommon/common.py b/SCons/Tool/MSCommon/common.py index 83bd2fd8b6..35a55b2683 100644 --- a/SCons/Tool/MSCommon/common.py +++ b/SCons/Tool/MSCommon/common.py @@ -31,6 +31,8 @@ import re import subprocess import sys +from contextlib import suppress +from pathlib import Path import SCons.Util @@ -94,7 +96,7 @@ def debug(x, *args): # SCONS_CACHE_MSVC_CONFIG is public, and is documented. CONFIG_CACHE = os.environ.get('SCONS_CACHE_MSVC_CONFIG') if CONFIG_CACHE in ('1', 'true', 'True'): - CONFIG_CACHE = os.path.join(os.path.expanduser('~'), '.scons_msvc_cache') + CONFIG_CACHE = os.path.join(os.path.expanduser('~'), 'scons_msvc_cache.json') def read_script_env_cache(): @@ -102,8 +104,13 @@ def read_script_env_cache(): envcache = {} if CONFIG_CACHE: try: - with open(CONFIG_CACHE, 'r') as f: - envcache = json.load(f) + p = Path(CONFIG_CACHE) + with p.open('r') as f: + # Convert the list of cache entry dictionaries read from + # json to the cache dictionary. Reconstruct the cache key + # tuple from the key list written to json. + envcache_list = json.load(f) + envcache = {tuple(d['key']): d['data'] for d in envcache_list} except FileNotFoundError: # don't fail if no cache file, just proceed without it pass @@ -114,11 +121,17 @@ def write_script_env_cache(cache): """ write out cache of msvc env vars if requested """ if CONFIG_CACHE: try: - with open(CONFIG_CACHE, 'w') as f: - json.dump(cache, f, indent=2) + p = Path(CONFIG_CACHE) + with p.open('w') as f: + # Convert the cache dictionary to a list of cache entry + # dictionaries. The cache key is converted from a tuple to + # a list for compatibility with json. + envcache_list = [{'key': list(key), 'data': data} for key, data in cache.items()] + json.dump(envcache_list, f, indent=2) except TypeError: # data can't serialize to json, don't leave partial file - os.remove(CONFIG_CACHE) + with suppress(FileNotFoundError): + p.unlink() except IOError: # can't write the file, just skip pass diff --git a/SCons/Tool/MSCommon/vc.py b/SCons/Tool/MSCommon/vc.py index 7789bca23e..7f758f57c1 100644 --- a/SCons/Tool/MSCommon/vc.py +++ b/SCons/Tool/MSCommon/vc.py @@ -43,6 +43,7 @@ import subprocess import os import platform +from pathlib import Path from string import digits as string_digits from subprocess import PIPE import re @@ -985,8 +986,25 @@ def script_env(script, args=None): if script_env_cache is None: script_env_cache = common.read_script_env_cache() - cache_key = "{}--{}".format(script, args) + cache_key = (script, args if args else None) cache_data = script_env_cache.get(cache_key, None) + + # Brief sanity check: if we got a value for the key, + # see if it has a VCToolsInstallDir entry that is not empty. + # If so, and that path does not exist, invalidate the entry. + # If empty, this is an old compiler, just leave it alone. + if cache_data is not None: + try: + toolsdir = cache_data["VCToolsInstallDir"] + except KeyError: + # we write this value, so should not happen + pass + else: + if toolsdir: + toolpath = Path(toolsdir[0]) + if not toolpath.exists(): + cache_data = None + if cache_data is None: stdout = common.get_output(script, args) diff --git a/doc/man/scons.xml b/doc/man/scons.xml index c7ec409375..a5eff28584 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -8155,7 +8155,7 @@ in tightly controlled Continuous Integration setups. If set to a True-like value ("1", "true" or "True") will cache to a file named -.scons_msvc_cache in the user's home directory. +.scons_msvc_cache.json in the user's home directory. If set to a pathname, will use that pathname for the cache. Note: use this cache with caution as it