summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMichael Kuhn <michael.kuhn@informatik.uni-hamburg.de>2019-10-17 00:15:24 +0200
committerTodd Gamblin <tgamblin@llnl.gov>2019-10-16 15:15:24 -0700
commitffe87ed49f928035bc0bafa5f2b834a38e694b51 (patch)
tree1a01993ce08937857b3114fa6456d3127bf08527 /lib
parent1ef71376f2e2b41229471501e91448858f2df61d (diff)
downloadspack-ffe87ed49f928035bc0bafa5f2b834a38e694b51.tar.gz
spack-ffe87ed49f928035bc0bafa5f2b834a38e694b51.tar.bz2
spack-ffe87ed49f928035bc0bafa5f2b834a38e694b51.tar.xz
spack-ffe87ed49f928035bc0bafa5f2b834a38e694b51.zip
filter_file: fix multiple invocations on the same file (#13234)
Since the backup file is only created on the first invocation, it will contain the original file without any modifications. Further invocations will then read the backup file, effectively reverting prior invocations. This can be reproduced easily by trying to install likwid, which will try to install into /usr/local. Work around this by creating a temporary file to read from.
Diffstat (limited to 'lib')
-rw-r--r--lib/spack/llnl/util/filesystem.py11
-rw-r--r--lib/spack/spack/test/llnl/util/filesystem.py24
2 files changed, 33 insertions, 2 deletions
diff --git a/lib/spack/llnl/util/filesystem.py b/lib/spack/llnl/util/filesystem.py
index 0af9c915b6..0095e3fd21 100644
--- a/lib/spack/llnl/util/filesystem.py
+++ b/lib/spack/llnl/util/filesystem.py
@@ -153,6 +153,7 @@ def filter_file(regex, repl, *filenames, **kwargs):
tty.debug(msg.format(filename, regex))
backup_filename = filename + "~"
+ tmp_filename = filename + ".spack~"
if ignore_absent and not os.path.exists(filename):
msg = 'FILTER FILE: file "{0}" not found. Skipping to next file.'
@@ -164,6 +165,10 @@ def filter_file(regex, repl, *filenames, **kwargs):
if not os.path.exists(backup_filename):
shutil.copy(filename, backup_filename)
+ # Create a temporary file to read from. We cannot use backup_filename
+ # in case filter_file is invoked multiple times on the same file.
+ shutil.copy(filename, tmp_filename)
+
try:
extra_kwargs = {}
if sys.version_info > (3, 0):
@@ -171,7 +176,7 @@ def filter_file(regex, repl, *filenames, **kwargs):
# Open as a text file and filter until the end of the file is
# reached or we found a marker in the line if it was specified
- with open(backup_filename, mode='r', **extra_kwargs) as input_file:
+ with open(tmp_filename, mode='r', **extra_kwargs) as input_file:
with open(filename, mode='w', **extra_kwargs) as output_file:
# Using iter and readline is a workaround needed not to
# disable input_file.tell(), which will happen if we call
@@ -190,17 +195,19 @@ def filter_file(regex, repl, *filenames, **kwargs):
# If we stopped filtering at some point, reopen the file in
# binary mode and copy verbatim the remaining part
if current_position and stop_at:
- with open(backup_filename, mode='rb') as input_file:
+ with open(tmp_filename, mode='rb') as input_file:
input_file.seek(current_position)
with open(filename, mode='ab') as output_file:
output_file.writelines(input_file.readlines())
except BaseException:
+ os.remove(tmp_filename)
# clean up the original file on failure.
shutil.move(backup_filename, filename)
raise
finally:
+ os.remove(tmp_filename)
if not backup and os.path.exists(backup_filename):
os.remove(backup_filename)
diff --git a/lib/spack/spack/test/llnl/util/filesystem.py b/lib/spack/spack/test/llnl/util/filesystem.py
index f6d8845800..6c26c5fb43 100644
--- a/lib/spack/spack/test/llnl/util/filesystem.py
+++ b/lib/spack/spack/test/llnl/util/filesystem.py
@@ -362,3 +362,27 @@ def test_filter_files_with_different_encodings(
with open(target_file, mode='r', **extra_kwargs) as f:
assert replacement in f.read()
+
+
+def test_filter_files_multiple(tmpdir):
+ # All files given as input to this test must satisfy the pre-requisite
+ # that the 'replacement' string is not present in the file initially and
+ # that there's at least one match for the regex
+ original_file = os.path.join(
+ spack.paths.test_path, 'data', 'filter_file', 'x86_cpuid_info.c'
+ )
+ target_file = os.path.join(str(tmpdir), 'x86_cpuid_info.c')
+ shutil.copy(original_file, target_file)
+ # This should not raise exceptions
+ fs.filter_file(r'\<malloc.h\>', '<unistd.h>', target_file)
+ fs.filter_file(r'\<string.h\>', '<unistd.h>', target_file)
+ fs.filter_file(r'\<stdio.h\>', '<unistd.h>', target_file)
+ # Check the strings have been replaced
+ extra_kwargs = {}
+ if sys.version_info > (3, 0):
+ extra_kwargs = {'errors': 'surrogateescape'}
+
+ with open(target_file, mode='r', **extra_kwargs) as f:
+ assert '<malloc.h>' not in f.read()
+ assert '<string.h>' not in f.read()
+ assert '<stdio.h>' not in f.read()