summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMassimiliano Culpo <massimiliano.culpo@gmail.com>2024-10-08 13:32:28 +0200
committerGitHub <noreply@github.com>2024-10-08 13:32:28 +0200
commitd70e9e131d12d9c1ee9f155ecb44684999d1f593 (patch)
treec3a13465675c6423093938bdd963e3c9135a1257
parentd7643d4f88e8e378bba900d84e7e7c6fe090463f (diff)
downloadspack-d70e9e131d12d9c1ee9f155ecb44684999d1f593.tar.gz
spack-d70e9e131d12d9c1ee9f155ecb44684999d1f593.tar.bz2
spack-d70e9e131d12d9c1ee9f155ecb44684999d1f593.tar.xz
spack-d70e9e131d12d9c1ee9f155ecb44684999d1f593.zip
Fix relocating MachO binary, when store projection changes (#46840)
* Remove "modify_object_macholib" According to documentation, this function is used when installing Mach-O binaries on linux. The implementation seems questionable at least, and the code seems to be never hit (Spack currently doesn't support installing Mach-O binaries on linux). * Fix relocation on macOS, when store projection changes --------- Signed-off-by: Massimiliano Culpo <massimiliano.culpo@gmail.com>
-rw-r--r--lib/spack/spack/relocate.py66
-rw-r--r--lib/spack/spack/test/packaging.py32
2 files changed, 49 insertions, 49 deletions
diff --git a/lib/spack/spack/relocate.py b/lib/spack/spack/relocate.py
index 764c51a819..67e980625e 100644
--- a/lib/spack/spack/relocate.py
+++ b/lib/spack/spack/relocate.py
@@ -205,23 +205,33 @@ def macho_find_paths(orig_rpaths, deps, idpath, old_layout_root, prefix_to_prefi
paths_to_paths dictionary which maps all of the old paths to new paths
"""
paths_to_paths = dict()
+ # Sort from longest path to shortest, to ensure we try /foo/bar/baz before /foo/bar
+ prefix_iteration_order = sorted(prefix_to_prefix, key=len, reverse=True)
for orig_rpath in orig_rpaths:
if orig_rpath.startswith(old_layout_root):
- for old_prefix, new_prefix in prefix_to_prefix.items():
+ for old_prefix in prefix_iteration_order:
+ new_prefix = prefix_to_prefix[old_prefix]
if orig_rpath.startswith(old_prefix):
new_rpath = re.sub(re.escape(old_prefix), new_prefix, orig_rpath)
paths_to_paths[orig_rpath] = new_rpath
+ break
else:
paths_to_paths[orig_rpath] = orig_rpath
if idpath:
- for old_prefix, new_prefix in prefix_to_prefix.items():
+ for old_prefix in prefix_iteration_order:
+ new_prefix = prefix_to_prefix[old_prefix]
if idpath.startswith(old_prefix):
paths_to_paths[idpath] = re.sub(re.escape(old_prefix), new_prefix, idpath)
+ break
+
for dep in deps:
- for old_prefix, new_prefix in prefix_to_prefix.items():
+ for old_prefix in prefix_iteration_order:
+ new_prefix = prefix_to_prefix[old_prefix]
if dep.startswith(old_prefix):
paths_to_paths[dep] = re.sub(re.escape(old_prefix), new_prefix, dep)
+ break
+
if dep.startswith("@"):
paths_to_paths[dep] = dep
@@ -270,36 +280,6 @@ def modify_macho_object(cur_path, rpaths, deps, idpath, paths_to_paths):
install_name_tool = executable.Executable("install_name_tool")
install_name_tool(*args)
- return
-
-
-def modify_object_macholib(cur_path, paths_to_paths):
- """
- This function is used when install machO buildcaches on linux by
- rewriting mach-o loader commands for dependency library paths of
- mach-o binaries and the id path for mach-o libraries.
- Rewritting of rpaths is handled by replace_prefix_bin.
- Inputs
- mach-o binary to be modified
- dictionary mapping paths in old install layout to new install layout
- """
-
- dll = macholib.MachO.MachO(cur_path)
- dll.rewriteLoadCommands(paths_to_paths.get)
-
- try:
- f = open(dll.filename, "rb+")
- for header in dll.headers:
- f.seek(0)
- dll.write(f)
- f.seek(0, 2)
- f.flush()
- f.close()
- except Exception:
- pass
-
- return
-
def macholib_get_paths(cur_path):
"""Get rpaths, dependent libraries, and library id of mach-o objects."""
@@ -415,10 +395,7 @@ def relocate_macho_binaries(
# normalized paths
rel_to_orig = macho_make_paths_normal(orig_path_name, rpaths, deps, idpath)
# replace the relativized paths with normalized paths
- if sys.platform == "darwin":
- modify_macho_object(path_name, rpaths, deps, idpath, rel_to_orig)
- else:
- modify_object_macholib(path_name, rel_to_orig)
+ modify_macho_object(path_name, rpaths, deps, idpath, rel_to_orig)
# get the normalized paths in the mach-o binary
rpaths, deps, idpath = macholib_get_paths(path_name)
# get the mapping of paths in old prefix to path in new prefix
@@ -426,10 +403,7 @@ def relocate_macho_binaries(
rpaths, deps, idpath, old_layout_root, prefix_to_prefix
)
# replace the old paths with new paths
- if sys.platform == "darwin":
- modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
- else:
- modify_object_macholib(path_name, paths_to_paths)
+ modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
# get the new normalized path in the mach-o binary
rpaths, deps, idpath = macholib_get_paths(path_name)
# get the mapping of paths to relative paths in the new prefix
@@ -437,10 +411,7 @@ def relocate_macho_binaries(
path_name, new_layout_root, rpaths, deps, idpath
)
# replace the new paths with relativized paths in the new prefix
- if sys.platform == "darwin":
- modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
- else:
- modify_object_macholib(path_name, paths_to_paths)
+ modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
else:
# get the paths in the old prefix
rpaths, deps, idpath = macholib_get_paths(path_name)
@@ -449,10 +420,7 @@ def relocate_macho_binaries(
rpaths, deps, idpath, old_layout_root, prefix_to_prefix
)
# replace the old paths with new paths
- if sys.platform == "darwin":
- modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
- else:
- modify_object_macholib(path_name, paths_to_paths)
+ modify_macho_object(path_name, rpaths, deps, idpath, paths_to_paths)
def _transform_rpaths(orig_rpaths, orig_root, new_prefixes):
diff --git a/lib/spack/spack/test/packaging.py b/lib/spack/spack/test/packaging.py
index d6ac1b190d..0f2d89dcce 100644
--- a/lib/spack/spack/test/packaging.py
+++ b/lib/spack/spack/test/packaging.py
@@ -549,3 +549,35 @@ def test_fetch_external_package_is_noop(default_mock_concretization, fetching_no
spec.external_path = "/some/where"
assert spec.external
spec.package.do_fetch()
+
+
+@pytest.mark.parametrize(
+ "relocation_dict",
+ [
+ {"/foo/bar/baz": "/a/b/c", "/foo/bar": "/a/b"},
+ # Ensure correctness does not depend on the ordering of the dict
+ {"/foo/bar": "/a/b", "/foo/bar/baz": "/a/b/c"},
+ ],
+)
+def test_macho_relocation_with_changing_projection(relocation_dict):
+ """Tests that prefix relocation is computed correctly when the prefixes to be relocated
+ contain a directory and its subdirectories.
+
+ This happens when relocating to a new place AND changing the store projection. In that case we
+ might have a relocation dict like:
+
+ /foo/bar/baz/ -> /a/b/c
+ /foo/bar -> /a/b
+
+ What we need to check is that we don't end up in situations where we relocate to a mixture of
+ the two schemes, like /a/b/baz.
+ """
+ original_rpath = "/foo/bar/baz/abcdef"
+ result = macho_find_paths(
+ [original_rpath],
+ deps=[],
+ idpath=None,
+ old_layout_root="/foo",
+ prefix_to_prefix=relocation_dict,
+ )
+ assert result[original_rpath] == "/a/b/c/abcdef"