44
55from __future__ import annotations
66
7+ import os .path
8+ from pathlib import Path
79from typing import TYPE_CHECKING
810
9- import auditwheel # noqa: F401
11+ from auditwheel .elfutils import elf_read_rpaths
12+ from auditwheel .patcher import Patchelf
1013
14+ from .._logging import logger
1115from . import WheelRepairer
1216
1317if TYPE_CHECKING :
@@ -23,6 +27,64 @@ class LinuxWheelRepairer(WheelRepairer):
2327
2428 _platform = "Linux"
2529
30+ def patch_linux_library_rpath (self , lib : Path , rpaths : list [str ]) -> None :
31+ """Patch the rpaths of a specific library."""
32+ # Flatten the current rpaths
33+ curr_rpaths = {
34+ path for dt_rpaths in elf_read_rpaths (lib ).values () for path in dt_rpaths
35+ }
36+ final_rpaths = set ()
37+ # Patch pre-existing rpaths
38+ for rpath_str in curr_rpaths :
39+ # If the rpath is already relative keep it
40+ if rpath_str .startswith ("$ORIGIN" ):
41+ final_rpaths .add (rpath_str )
42+ continue
43+ # Otherwise check if we need to patch it
44+ rpath_path = Path (rpath_str )
45+ try :
46+ rpath_path .relative_to (self .install_dir )
47+ except ValueError :
48+ # If it does not point to wheel install path, just keep it
49+ final_rpaths .add (rpath_str )
50+ continue
51+ # Otherwise change the RPATH to point use $ORIGIN
52+ new_rpath = os .path .relpath (rpath_str , lib .parent )
53+ new_rpath = f"$ORIGIN/{ new_rpath } "
54+ final_rpaths .add (new_rpath )
55+ # Merge with all the rpaths we were given
56+ final_rpaths = final_rpaths .union (rpaths )
57+ patcher = Patchelf ()
58+ patcher .set_rpath (lib , ":" .join (final_rpaths ))
59+
2660 def patch_target (self , target : Target ) -> None :
27- # TODO: Implement patching
28- pass
61+ # Get the target install paths where the $ORIGIN is calculated from
62+ target_install_paths = self .get_wheel_install_paths (target )
63+ if not target_install_paths :
64+ logger .debug (
65+ "Skip patching {target} because all install paths are outside the wheel." ,
66+ target = target .name ,
67+ )
68+ return
69+ if len (set (target .artifacts )) != 1 :
70+ logger .warning (
71+ "Unexpected multiple artifacts for target {target}: {artifacts}" ,
72+ target = target .name ,
73+ artifacts = [item .path for item in target .artifacts ],
74+ )
75+ return
76+ artifact = target .artifacts [0 ]
77+ for install_path in target_install_paths :
78+ target_path = self .install_dir / install_path
79+ dependency_rpaths = []
80+ for dep_target in self .get_library_dependencies (target ):
81+ dep_install_paths = self .get_wheel_install_paths (dep_target )
82+ assert len (dep_install_paths ) == 1
83+ dep_path = self .install_dir / next (iter (dep_install_paths ))
84+ rpath = os .path .relpath (dep_path , target_path )
85+ rpath = f"$ORIGIN/{ rpath } "
86+ dependency_rpaths .append (rpath )
87+ self .patch_linux_library_rpath (
88+ lib = target_path / artifact .path ,
89+ rpaths = dependency_rpaths ,
90+ )
0 commit comments