diff options
author | William Deegan <bill@baddogconsulting.com> | 2019-04-28 20:51:46 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-04-28 20:51:46 (GMT) |
commit | c3b0bb3b4d15a33c93413cb3cfaea3469f56c31b (patch) | |
tree | ed8ee5a8637731e38010ff2517e93849a276165d /src/engine | |
parent | 35e6bbe16a859b42efca4592b435695a530f0717 (diff) | |
parent | d745eb81c18d448837740d9543570ddd110243d6 (diff) | |
download | SCons-c3b0bb3b4d15a33c93413cb3cfaea3469f56c31b.zip SCons-c3b0bb3b4d15a33c93413cb3cfaea3469f56c31b.tar.gz SCons-c3b0bb3b4d15a33c93413cb3cfaea3469f56c31b.tar.bz2 |
Merge pull request #3353 from mwichmann/cacheDir
[WIP] Avoid cachedir races
Diffstat (limited to 'src/engine')
-rw-r--r-- | src/engine/SCons/CacheDir.py | 88 |
1 files changed, 75 insertions, 13 deletions
diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py index dabda8e..0a378c2 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -35,6 +35,7 @@ import sys import SCons.Action import SCons.Warnings +from SCons.Util import PY3 cache_enabled = True cache_debug = False @@ -139,34 +140,95 @@ warned = dict() class CacheDir(object): def __init__(self, path): + """ + Initialize a CacheDir object. + + The cache configuration is stored in the object. It + is read from the config file in the supplied path if + one exists, if not the config file is created and + the default config is written, as well as saved in the object. + """ self.path = path self.current_cache_debug = None self.debugFP = None self.config = dict() if path is None: return - # See if there's a config file in the cache directory. If there is, - # use it. If there isn't, and the directory exists and isn't empty, - # produce a warning. If the directory doesn't exist or is empty, - # write a config file. + + if PY3: + self._readconfig3(path) + else: + self._readconfig2(path) + + + def _readconfig3(self, path): + """ + Python3 version of reading the cache config. + + If directory or config file do not exist, create. Take advantage + of Py3 capability in os.makedirs() and in file open(): just try + the operation and handle failure appropriately. + + Omit the check for old cache format, assume that's old enough + there will be none of those left to worry about. + + :param path: path to the cache directory + """ + config_file = os.path.join(path, 'config') + try: + os.makedirs(path, exist_ok=True) + except FileExistsError: + pass + except OSError: + msg = "Failed to create cache directory " + path + raise SCons.Errors.EnvironmentError(msg) + + try: + with open(config_file, 'x') as config: + self.config['prefix_len'] = 2 + try: + json.dump(self.config, config) + except Exception: + msg = "Failed to write cache configuration for " + path + raise SCons.Errors.EnvironmentError(msg) + except FileExistsError: + try: + with open(config_file) as config: + self.config = json.load(config) + except ValueError: + msg = "Failed to read cache configuration for " + path + raise SCons.Errors.EnvironmentError(msg) + + + def _readconfig2(self, path): + """ + Python2 version of reading cache config. + + See if there is a config file in the cache directory. If there is, + use it. If there isn't, and the directory exists and isn't empty, + produce a warning. If the directory does not exist or is empty, + write a config file. + + :param path: path to the cache directory + """ config_file = os.path.join(path, 'config') if not os.path.exists(config_file): - # A note: There is a race hazard here, if two processes start and + # A note: There is a race hazard here if two processes start and # attempt to create the cache directory at the same time. However, - # python doesn't really give you the option to do exclusive file - # creation (it doesn't even give you the option to error on opening - # an existing file for writing...). The ordering of events here - # as an attempt to alleviate this, on the basis that it's a pretty - # unlikely occurence (it'd require two builds with a brand new cache + # Python 2.x does not give you the option to do exclusive file + # creation (not even the option to error on opening an existing + # file for writing...). The ordering of events here is an attempt + # to alleviate this, on the basis that it's a pretty unlikely + # occurrence (would require two builds with a brand new cache # directory) - if os.path.isdir(path) and len(os.listdir(path)) != 0: + if os.path.isdir(path) and any(f != "config" for f in os.listdir(path)): self.config['prefix_len'] = 1 # When building the project I was testing this on, the warning # was output over 20 times. That seems excessive global warned if self.path not in warned: msg = "Please upgrade your cache by running " +\ - " scons-configure-cache.py " + self.path + "scons-configure-cache.py " + self.path SCons.Warnings.warn(SCons.Warnings.CacheVersionWarning, msg) warned[self.path] = True else: @@ -184,7 +246,7 @@ class CacheDir(object): try: with open(config_file, 'w') as config: json.dump(self.config, config) - except: + except Exception: msg = "Failed to write cache configuration for " + path raise SCons.Errors.SConsEnvironmentError(msg) else: |