diff --git a/src/auditwheel/patcher.py b/src/auditwheel/patcher.py index 55398665..bc797ec6 100644 --- a/src/auditwheel/patcher.py +++ b/src/auditwheel/patcher.py @@ -1,10 +1,12 @@ import re from distutils.spawn import find_executable +from itertools import chain from subprocess import CalledProcessError, check_call, check_output +from typing import Tuple class ElfPatcher: - def replace_needed(self, file_name: str, so_name: str, new_so_name: str) -> None: + def replace_needed(self, file_name: str, *old_new_pairs: Tuple[str, str]) -> None: raise NotImplementedError def set_soname(self, file_name: str, new_so_name: str) -> None: @@ -41,8 +43,16 @@ class Patchelf(ElfPatcher): def __init__(self) -> None: _verify_patchelf() - def replace_needed(self, file_name: str, so_name: str, new_so_name: str) -> None: - check_call(["patchelf", "--replace-needed", so_name, new_so_name, file_name]) + def replace_needed(self, file_name: str, *old_new_pairs: Tuple[str, str]) -> None: + check_call( + [ + "patchelf", + *chain.from_iterable( + ("--replace-needed", *pair) for pair in old_new_pairs + ), + file_name, + ] + ) def set_soname(self, file_name: str, new_so_name: str) -> None: check_call(["patchelf", "--set-soname", new_so_name, file_name]) diff --git a/src/auditwheel/repair.py b/src/auditwheel/repair.py index c3386aec..4edb6250 100644 --- a/src/auditwheel/repair.py +++ b/src/auditwheel/repair.py @@ -67,8 +67,8 @@ def repair_wheel( # here, fn is a path to a python extension library in # the wheel, and v['libs'] contains its required libs for fn, v in external_refs_by_fn.items(): - ext_libs = v[abis[0]]["libs"] # type: Dict[str, str] + replacements = [] # type: List[Tuple[str, str]] for soname, src_path in ext_libs.items(): if src_path is None: raise ValueError( @@ -81,7 +81,9 @@ def repair_wheel( new_soname, new_path = copylib(src_path, dest_dir, patcher) soname_map[soname] = (new_soname, new_path) - patcher.replace_needed(fn, soname, new_soname) + replacements.append((soname, new_soname)) + if replacements: + patcher.replace_needed(fn, *replacements) if len(ext_libs) > 0: new_rpath = os.path.relpath(dest_dir, os.path.dirname(fn)) @@ -94,9 +96,12 @@ def repair_wheel( # name of the other. for old_soname, (new_soname, path) in soname_map.items(): needed = elf_read_dt_needed(path) + replacements = [] for n in needed: if n in soname_map: - patcher.replace_needed(path, n, soname_map[n][0]) + replacements.append((n, soname_map[n][0])) + if replacements: + patcher.replace_needed(path, *replacements) if update_tags: ctx.out_wheel = add_platforms(ctx, abis, get_replace_platforms(abis[0])) diff --git a/tests/unit/test_elfpatcher.py b/tests/unit/test_elfpatcher.py index ccca54aa..62f10d8b 100644 --- a/tests/unit/test_elfpatcher.py +++ b/tests/unit/test_elfpatcher.py @@ -41,16 +41,35 @@ def test_patchelf_version_check_fail(check_output, version): class TestPatchElf: """ "Validate that patchelf is invoked with the correct arguments.""" - def test_replace_needed(self, check_call, _0, _1): + def test_replace_needed_one(self, check_call, _0, _1): patcher = Patchelf() filename = "test.so" soname_old = "TEST_OLD" soname_new = "TEST_NEW" - patcher.replace_needed(filename, soname_old, soname_new) + patcher.replace_needed(filename, (soname_old, soname_new)) check_call.assert_called_once_with( ["patchelf", "--replace-needed", soname_old, soname_new, filename] ) + def test_replace_needed_multple(self, check_call, _0, _1): + patcher = Patchelf() + filename = "test.so" + replacements = [ + ("TEST_OLD1", "TEST_NEW1"), + ("TEST_OLD2", "TEST_NEW2"), + ] + patcher.replace_needed(filename, *replacements) + check_call.assert_called_once_with( + [ + "patchelf", + "--replace-needed", + *replacements[0], + "--replace-needed", + *replacements[1], + filename, + ] + ) + def test_set_soname(self, check_call, _0, _1): patcher = Patchelf() filename = "test.so"