summaryrefslogtreecommitdiffstats
path: root/src/engine
diff options
context:
space:
mode:
authorMats Wichmann <mats@linux.com>2019-04-16 19:55:04 (GMT)
committerMats Wichmann <mats@linux.com>2019-04-28 13:57:37 (GMT)
commitce51b20a0b22a3eecd8602bdca7a6ff3906113af (patch)
treeb5a9daad4baa849d50707a6eec71d52f13e30229 /src/engine
parent44c7b81e1a47ff5d4439740b1e929ea723ee1f18 (diff)
downloadSCons-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.py75
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: