Relocate links using prefix to prefix map (#33636)
Previously symlinks were not relocated when they pointed across packages
This commit is contained in:
		| @@ -1646,8 +1646,8 @@ def is_backup_file(file): | |||||||
|             ) |             ) | ||||||
| 
 | 
 | ||||||
|         # Relocate links to the new install prefix |         # Relocate links to the new install prefix | ||||||
|         links = [link for link in buildinfo.get("relocate_links", [])] |         links = [os.path.join(workdir, f) for f in buildinfo.get("relocate_links", [])] | ||||||
|         relocate.relocate_links(links, old_layout_root, old_prefix, new_prefix) |         relocate.relocate_links(links, prefix_to_prefix_bin) | ||||||
| 
 | 
 | ||||||
|         # For all buildcaches |         # For all buildcaches | ||||||
|         # relocate the install prefixes in text files including dependencies |         # relocate the install prefixes in text files including dependencies | ||||||
|   | |||||||
| @@ -702,41 +702,27 @@ def raise_if_not_relocatable(binaries, allow_root): | |||||||
|             raise InstallRootStringError(binary, spack.store.layout.root) |             raise InstallRootStringError(binary, spack.store.layout.root) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def relocate_links(links, orig_layout_root, orig_install_prefix, new_install_prefix): | def warn_if_link_cant_be_relocated(link, target): | ||||||
|     """Relocate links to a new install prefix. |     if not os.path.isabs(target): | ||||||
|  |         return | ||||||
|  |     tty.warn('Symbolic link at "{}" to "{}" cannot be relocated'.format(link, target)) | ||||||
| 
 | 
 | ||||||
|     The symbolic links are relative to the original installation prefix. |  | ||||||
|     The old link target is read and the placeholder is replaced by the old |  | ||||||
|     layout root. If the old link target is in the old install prefix, the new |  | ||||||
|     link target is create by replacing the old install prefix with the new |  | ||||||
|     install prefix. |  | ||||||
| 
 | 
 | ||||||
|     Args: | def relocate_links(links, prefix_to_prefix): | ||||||
|         links (list): list of links to be relocated |     """Relocate links to a new install prefix.""" | ||||||
|         orig_layout_root (str): original layout root |     regex = re.compile("|".join(re.escape(p) for p in prefix_to_prefix.keys())) | ||||||
|         orig_install_prefix (str): install prefix of the original installation |     for link in links: | ||||||
|         new_install_prefix (str): install prefix where we want to relocate |         old_target = os.readlink(link) | ||||||
|     """ |         match = regex.match(old_target) | ||||||
|     placeholder = _placeholder(orig_layout_root) |  | ||||||
|     abs_links = [os.path.join(new_install_prefix, link) for link in links] |  | ||||||
|     for abs_link in abs_links: |  | ||||||
|         link_target = os.readlink(abs_link) |  | ||||||
|         link_target = re.sub(placeholder, orig_layout_root, link_target) |  | ||||||
|         # If the link points to a file in the original install prefix, |  | ||||||
|         # compute the corresponding target in the new prefix and relink |  | ||||||
|         if link_target.startswith(orig_install_prefix): |  | ||||||
|             link_target = re.sub(orig_install_prefix, new_install_prefix, link_target) |  | ||||||
|             os.unlink(abs_link) |  | ||||||
|             symlink(link_target, abs_link) |  | ||||||
| 
 | 
 | ||||||
|         # If the link is absolute and has not been relocated then |         # No match. | ||||||
|         # warn the user about that |         if match is None: | ||||||
|         if os.path.isabs(link_target) and not link_target.startswith(new_install_prefix): |             warn_if_link_cant_be_relocated(link, old_target) | ||||||
|             msg = ( |             continue | ||||||
|                 'Link target "{0}" for symbolic link "{1}" is outside' | 
 | ||||||
|                 " of the new install prefix {2}" |         new_target = prefix_to_prefix[match.group()] + old_target[match.end() :] | ||||||
|             ) |         os.unlink(link) | ||||||
|             tty.warn(msg.format(link_target, abs_link, new_install_prefix)) |         symlink(new_target, link) | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def utf8_path_to_binary_regex(prefix): | def utf8_path_to_binary_regex(prefix): | ||||||
|   | |||||||
| @@ -9,10 +9,10 @@ | |||||||
| import argparse | import argparse | ||||||
| import os | import os | ||||||
| import platform | import platform | ||||||
| import re |  | ||||||
| import shutil | import shutil | ||||||
| import stat | import stat | ||||||
| import sys | import sys | ||||||
|  | from collections import OrderedDict | ||||||
| 
 | 
 | ||||||
| import pytest | import pytest | ||||||
| 
 | 
 | ||||||
| @@ -28,7 +28,6 @@ | |||||||
| from spack.fetch_strategy import FetchStrategyComposite, URLFetchStrategy | from spack.fetch_strategy import FetchStrategyComposite, URLFetchStrategy | ||||||
| from spack.paths import mock_gpg_keys_path | from spack.paths import mock_gpg_keys_path | ||||||
| from spack.relocate import ( | from spack.relocate import ( | ||||||
|     _placeholder, |  | ||||||
|     file_is_relocatable, |     file_is_relocatable, | ||||||
|     macho_find_paths, |     macho_find_paths, | ||||||
|     macho_make_paths_normal, |     macho_make_paths_normal, | ||||||
| @@ -213,27 +212,42 @@ def test_unsafe_relocate_text(tmpdir): | |||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_relocate_links(tmpdir): | def test_relocate_links(tmpdir): | ||||||
|     with tmpdir.as_cwd(): |     tmpdir.ensure("new_prefix_a", dir=True) | ||||||
|         old_layout_root = os.path.join("%s" % tmpdir, "home", "spack", "opt", "spack") | 
 | ||||||
|         old_install_prefix = os.path.join("%s" % old_layout_root, "debian6", "test") |     own_prefix_path = str(tmpdir.join("prefix_a", "file")) | ||||||
|         old_binname = os.path.join(old_install_prefix, "binfile") |     dep_prefix_path = str(tmpdir.join("prefix_b", "file")) | ||||||
|         placeholder = _placeholder(old_layout_root) |     system_path = os.path.join(os.path.sep, "system", "path") | ||||||
|         re.sub(old_layout_root, placeholder, old_binname) | 
 | ||||||
|         filenames = ["link.ln", "outsideprefix.ln"] |     # Old prefixes to new prefixes | ||||||
|         new_layout_root = os.path.join("%s" % tmpdir, "opt", "rh", "devtoolset") |     prefix_to_prefix = OrderedDict( | ||||||
|         new_install_prefix = os.path.join("%s" % new_layout_root, "test", "debian6") |         [ | ||||||
|         new_linkname = os.path.join(new_install_prefix, "link.ln") |             # map <tmpdir>/prefix_a -> <tmpdir>/new_prefix_a | ||||||
|         new_linkname2 = os.path.join(new_install_prefix, "outsideprefix.ln") |             (str(tmpdir.join("prefix_a")), str(tmpdir.join("new_prefix_a"))), | ||||||
|         new_binname = os.path.join(new_install_prefix, "binfile") |             # map <tmpdir>/prefix_b -> <tmpdir>/new_prefix_b | ||||||
|         mkdirp(new_install_prefix) |             (str(tmpdir.join("prefix_b")), str(tmpdir.join("new_prefix_b"))), | ||||||
|         with open(new_binname, "w") as f: |             # map <tmpdir> -> /fallback/path -- this is just to see we respect order. | ||||||
|             f.write("\n") |             (str(tmpdir), os.path.join(os.path.sep, "fallback", "path")), | ||||||
|         os.utime(new_binname, None) |         ] | ||||||
|         symlink(old_binname, new_linkname) |     ) | ||||||
|         symlink("/usr/lib/libc.so", new_linkname2) | 
 | ||||||
|         relocate_links(filenames, old_layout_root, old_install_prefix, new_install_prefix) |     with tmpdir.join("new_prefix_a").as_cwd(): | ||||||
|         assert os.readlink(new_linkname) == new_binname |         # To be relocated | ||||||
|         assert os.readlink(new_linkname2) == "/usr/lib/libc.so" |         os.symlink(own_prefix_path, "to_self") | ||||||
|  |         os.symlink(dep_prefix_path, "to_dependency") | ||||||
|  | 
 | ||||||
|  |         # To be ignored | ||||||
|  |         os.symlink(system_path, "to_system") | ||||||
|  |         os.symlink("relative", "to_self_but_relative") | ||||||
|  | 
 | ||||||
|  |         relocate_links(["to_self", "to_dependency", "to_system"], prefix_to_prefix) | ||||||
|  | 
 | ||||||
|  |         # These two are relocated | ||||||
|  |         assert os.readlink("to_self") == str(tmpdir.join("new_prefix_a", "file")) | ||||||
|  |         assert os.readlink("to_dependency") == str(tmpdir.join("new_prefix_b", "file")) | ||||||
|  | 
 | ||||||
|  |         # These two are not. | ||||||
|  |         assert os.readlink("to_system") == system_path | ||||||
|  |         assert os.readlink("to_self_but_relative") == "relative" | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| def test_needs_relocation(): | def test_needs_relocation(): | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Harmen Stoppels
					Harmen Stoppels