4
4
5
5
from __future__ import annotations
6
6
7
+ import os .path
8
+ from pathlib import Path
7
9
from typing import TYPE_CHECKING
8
10
9
- import auditwheel # noqa: F401
11
+ from auditwheel .elfutils import elf_read_rpaths
12
+ from auditwheel .patcher import Patchelf
10
13
14
+ from .._logging import logger
11
15
from . import WheelRepairer
12
16
13
17
if TYPE_CHECKING :
@@ -23,6 +27,64 @@ class LinuxWheelRepairer(WheelRepairer):
23
27
24
28
_platform = "Linux"
25
29
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
+
26
60
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