detection: prefer dir instead of symlink in case of duplicate search paths (#46957)
This commit is contained in:
parent
66a3c7bc42
commit
21c2eedb80
@ -66,6 +66,21 @@ def file_identifier(path):
|
|||||||
return s.st_dev, s.st_ino
|
return s.st_dev, s.st_ino
|
||||||
|
|
||||||
|
|
||||||
|
def dedupe_paths(paths: List[str]) -> List[str]:
|
||||||
|
"""Deduplicate paths based on inode and device number. In case the list contains first a
|
||||||
|
symlink and then the directory it points to, the symlink is replaced with the directory path.
|
||||||
|
This ensures that we pick for example ``/usr/bin`` over ``/bin`` if the latter is a symlink to
|
||||||
|
the former`."""
|
||||||
|
seen: Dict[Tuple[int, int], str] = {}
|
||||||
|
for path in paths:
|
||||||
|
identifier = file_identifier(path)
|
||||||
|
if identifier not in seen:
|
||||||
|
seen[identifier] = path
|
||||||
|
elif not os.path.islink(path):
|
||||||
|
seen[identifier] = path
|
||||||
|
return list(seen.values())
|
||||||
|
|
||||||
|
|
||||||
def executables_in_path(path_hints: List[str]) -> Dict[str, str]:
|
def executables_in_path(path_hints: List[str]) -> Dict[str, str]:
|
||||||
"""Get the paths of all executables available from the current PATH.
|
"""Get the paths of all executables available from the current PATH.
|
||||||
|
|
||||||
@ -82,8 +97,7 @@ def executables_in_path(path_hints: List[str]) -> Dict[str, str]:
|
|||||||
"""
|
"""
|
||||||
search_paths = llnl.util.filesystem.search_paths_for_executables(*path_hints)
|
search_paths = llnl.util.filesystem.search_paths_for_executables(*path_hints)
|
||||||
# Make use we don't doubly list /usr/lib and /lib etc
|
# Make use we don't doubly list /usr/lib and /lib etc
|
||||||
search_paths = list(llnl.util.lang.dedupe(search_paths, key=file_identifier))
|
return path_to_dict(dedupe_paths(search_paths))
|
||||||
return path_to_dict(search_paths)
|
|
||||||
|
|
||||||
|
|
||||||
def accept_elf(path, host_compat):
|
def accept_elf(path, host_compat):
|
||||||
@ -144,7 +158,7 @@ def libraries_in_ld_and_system_library_path(
|
|||||||
search_paths = list(filter(os.path.isdir, search_paths))
|
search_paths = list(filter(os.path.isdir, search_paths))
|
||||||
|
|
||||||
# Make use we don't doubly list /usr/lib and /lib etc
|
# Make use we don't doubly list /usr/lib and /lib etc
|
||||||
search_paths = list(llnl.util.lang.dedupe(search_paths, key=file_identifier))
|
search_paths = dedupe_paths(search_paths)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
host_compat = elf_utils.get_elf_compat(sys.executable)
|
host_compat = elf_utils.get_elf_compat(sys.executable)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
import spack.config
|
import spack.config
|
||||||
import spack.detection
|
import spack.detection
|
||||||
import spack.detection.common
|
import spack.detection.common
|
||||||
|
import spack.detection.path
|
||||||
import spack.spec
|
import spack.spec
|
||||||
|
|
||||||
|
|
||||||
@ -26,3 +27,28 @@ def test_detection_update_config(mutable_config):
|
|||||||
external_gcc = externals[0]
|
external_gcc = externals[0]
|
||||||
assert external_gcc["spec"] == "cmake@3.27.5"
|
assert external_gcc["spec"] == "cmake@3.27.5"
|
||||||
assert external_gcc["prefix"] == "/usr/bin"
|
assert external_gcc["prefix"] == "/usr/bin"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dedupe_paths(tmp_path):
|
||||||
|
"""Test that ``dedupe_paths`` deals with symlinked directories, retaining the target"""
|
||||||
|
x = tmp_path / "x"
|
||||||
|
y = tmp_path / "y"
|
||||||
|
z = tmp_path / "z"
|
||||||
|
|
||||||
|
x.mkdir()
|
||||||
|
y.mkdir()
|
||||||
|
z.symlink_to("x", target_is_directory=True)
|
||||||
|
|
||||||
|
# dedupe repeated dirs, should preserve order
|
||||||
|
assert spack.detection.path.dedupe_paths([str(x), str(y), str(x)]) == [str(x), str(y)]
|
||||||
|
assert spack.detection.path.dedupe_paths([str(y), str(x), str(y)]) == [str(y), str(x)]
|
||||||
|
|
||||||
|
# dedupe repeated symlinks
|
||||||
|
assert spack.detection.path.dedupe_paths([str(z), str(y), str(z)]) == [str(z), str(y)]
|
||||||
|
assert spack.detection.path.dedupe_paths([str(y), str(z), str(y)]) == [str(y), str(z)]
|
||||||
|
|
||||||
|
# when both symlink and target are present, only target is retained, and it comes at the
|
||||||
|
# priority of the first occurrence.
|
||||||
|
assert spack.detection.path.dedupe_paths([str(x), str(y), str(z)]) == [str(x), str(y)]
|
||||||
|
assert spack.detection.path.dedupe_paths([str(z), str(y), str(x)]) == [str(x), str(y)]
|
||||||
|
assert spack.detection.path.dedupe_paths([str(y), str(z), str(x)]) == [str(y), str(x)]
|
||||||
|
Loading…
Reference in New Issue
Block a user