diff options
author | Mats Wichmann <mats@linux.com> | 2019-04-16 19:55:04 (GMT) |
---|---|---|
committer | Mats Wichmann <mats@linux.com> | 2019-04-28 13:57:37 (GMT) |
commit | ce51b20a0b22a3eecd8602bdca7a6ff3906113af (patch) | |
tree | b5a9daad4baa849d50707a6eec71d52f13e30229 /src/engine | |
parent | 44c7b81e1a47ff5d4439740b1e929ea723ee1f18 (diff) | |
download | SCons-ce51b20a0b22a3eecd8602bdca7a6ff3906113af.zip SCons-ce51b20a0b22a3eecd8602bdca7a6ff3906113af.tar.gz SCons-ce51b20a0b22a3eecd8602bdca7a6ff3906113af.tar.bz2 |
Avoid cachedir races
1. Add a Py3-only version of the cachedir config read,
using exclusive open to avoid races.
2. In the Py2-only version, add the hack from issue #3351
to dodge one source of races.
Signed-off-by: Mats Wichmann <mats@linux.com>
Diffstat (limited to 'src/engine')
-rw-r--r-- | src/engine/SCons/CacheDir.py | 75 |
1 files changed, 69 insertions, 6 deletions
diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py index dabda8e..f2d3810 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -139,16 +139,78 @@ 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. + + from SCons.Util import PY3 + 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: + 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'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. + + :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 @@ -159,14 +221,15 @@ class CacheDir(object): # 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 # directory) - if os.path.isdir(path) and len(os.listdir(path)) != 0: + #if os.path.isdir(path) and len(os.listdir(path)) != 0: + if os.path.isdir(path) and len([f for f in os.listdir(path) if os.path.basename(f) != "config"]) != 0: 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: |