11
11
12
12
13
13
if typing .TYPE_CHECKING :
14
- from typing import List
14
+ from typing import List , Optional , TypeVar
15
15
16
16
from mesonpy ._compat import Iterable , Path
17
17
18
+ T = TypeVar ('T' )
18
19
19
- if sys .platform == 'win32' or sys .platform == 'cygwin' :
20
20
21
- def fix_rpath (filepath : Path , libs_relative_path : str ) -> None :
22
- pass
21
+ def unique (values : List [T ]) -> List [T ]:
22
+ r = []
23
+ for value in values :
24
+ if value not in r :
25
+ r .append (value )
26
+ return r
23
27
24
- elif sys .platform == 'darwin' :
25
28
29
+ class _Windows :
30
+
31
+ @staticmethod
26
32
def _get_rpath (filepath : Path ) -> List [str ]:
33
+ return []
34
+
35
+ @classmethod
36
+ def fix_rpath (cls , filepath : Path , install_rpath : Optional [str ], libs_rpath : Optional [str ]) -> None :
37
+ pass
38
+
39
+
40
+ class RPATH :
41
+ origin = '$ORIGIN'
42
+
43
+ @staticmethod
44
+ def get_rpath (filepath : Path ) -> List [str ]:
45
+ raise NotImplementedError
46
+
47
+ @staticmethod
48
+ def set_rpath (filepath : Path , old : List [str ], rpath : List [str ]) -> None :
49
+ raise NotImplementedError
50
+
51
+ @classmethod
52
+ def fix_rpath (cls , filepath : Path , install_rpath : Optional [str ], libs_rpath : Optional [str ]) -> None :
53
+ old_rpath = cls .get_rpath (filepath )
54
+ new_rpath = []
55
+ if libs_rpath is not None :
56
+ if libs_rpath == '.' :
57
+ libs_rpath = ''
58
+ for path in old_rpath :
59
+ if path .split ('/' , 1 )[0 ] == cls .origin :
60
+ # Any RPATH entry relative to ``$ORIGIN`` is interpreted as
61
+ # pointing to a location in the build directory added by
62
+ # Meson. These need to be removed. Their presence indicates
63
+ # that the executable, shared library, or Python module
64
+ # depends on libraries build as part of the package. These
65
+ # entries are thus replaced with entries pointing to the
66
+ # ``.<package-name>.mesonpy.libs`` folder where meson-python
67
+ # relocates shared libraries distributed with the package.
68
+ # The package may however explicitly install these in a
69
+ # different location, thus this is not a perfect heuristic
70
+ # and may add not required RPATH entries. These are however
71
+ # harmless.
72
+ path = f'{ cls .origin } /{ libs_rpath } '
73
+ # Any other RPATH entry is preserved.
74
+ new_rpath .append (path )
75
+ if install_rpath :
76
+ # Add the RPATH entry spcified with the ``install_rpath`` argument.
77
+ new_rpath .append (install_rpath )
78
+ # Make the RPATH entries unique.
79
+ new_rpath = unique (new_rpath )
80
+ if new_rpath != old_rpath :
81
+ cls .set_rpath (filepath , old_rpath , new_rpath )
82
+
83
+
84
+ class _MacOS (RPATH ):
85
+ origin = '@loader_path'
86
+
87
+ @staticmethod
88
+ def get_rpath (filepath : Path ) -> List [str ]:
27
89
rpath = []
28
90
r = subprocess .run (['otool' , '-l' , os .fspath (filepath )], capture_output = True , text = True )
29
91
rpath_tag = False
@@ -35,17 +97,31 @@ def _get_rpath(filepath: Path) -> List[str]:
35
97
rpath_tag = False
36
98
return rpath
37
99
38
- def _replace_rpath (filepath : Path , old : str , new : str ) -> None :
39
- subprocess .run (['install_name_tool' , '-rpath' , old , new , os .fspath (filepath )], check = True )
40
-
41
- def fix_rpath (filepath : Path , libs_relative_path : str ) -> None :
42
- for path in _get_rpath (filepath ):
43
- if path .startswith ('@loader_path/' ):
44
- _replace_rpath (filepath , path , '@loader_path/' + libs_relative_path )
45
-
46
- elif sys .platform == 'sunos5' :
47
-
48
- def _get_rpath (filepath : Path ) -> List [str ]:
100
+ @staticmethod
101
+ def set_rpath (filepath : Path , old : List [str ], rpath : List [str ]) -> None :
102
+ args : List [str ] = []
103
+ for path in rpath :
104
+ if path not in old :
105
+ args += ['-add_rpath' , path ]
106
+ for path in old :
107
+ if path not in rpath :
108
+ args += ['-delete_rpath' , path ]
109
+ subprocess .run (['install_name_tool' , * args , os .fspath (filepath )], check = True )
110
+
111
+ @classmethod
112
+ def fix_rpath (cls , filepath : Path , install_rpath : Optional [str ], libs_rpath : Optional [str ]) -> None :
113
+ if install_rpath is not None :
114
+ root , sep , stem = install_rpath .partition ('/' )
115
+ if root == '$ORIGIN' :
116
+ install_rpath = f'{ cls .origin } { sep } { stem } '
117
+ # warnings.warn('...')
118
+ super ().fix_rpath (filepath , install_rpath , libs_rpath )
119
+
120
+
121
+ class _SunOS (RPATH ):
122
+
123
+ @staticmethod
124
+ def get_rpath (filepath : Path ) -> List [str ]:
49
125
rpath = []
50
126
r = subprocess .run (['/usr/bin/elfedit' , '-r' , '-e' , 'dyn:rpath' , os .fspath (filepath )],
51
127
capture_output = True , check = True , text = True )
@@ -56,35 +132,31 @@ def _get_rpath(filepath: Path) -> List[str]:
56
132
rpath .append (path )
57
133
return rpath
58
134
59
- def _set_rpath (filepath : Path , rpath : Iterable [str ]) -> None :
135
+ @staticmethod
136
+ def set_rpath (filepath : Path , old : Iterable [str ], rpath : Iterable [str ]) -> None :
60
137
subprocess .run (['/usr/bin/elfedit' , '-e' , 'dyn:rpath ' + ':' .join (rpath ), os .fspath (filepath )], check = True )
61
138
62
- def fix_rpath (filepath : Path , libs_relative_path : str ) -> None :
63
- old_rpath = _get_rpath (filepath )
64
- new_rpath = []
65
- for path in old_rpath :
66
- if path .startswith ('$ORIGIN/' ):
67
- path = '$ORIGIN/' + libs_relative_path
68
- new_rpath .append (path )
69
- if new_rpath != old_rpath :
70
- _set_rpath (filepath , new_rpath )
71
139
72
- else :
73
- # Assume that any other platform uses ELF binaries.
140
+ class _ELF (RPATH ):
74
141
75
- def _get_rpath (filepath : Path ) -> List [str ]:
142
+ @staticmethod
143
+ def get_rpath (filepath : Path ) -> List [str ]:
76
144
r = subprocess .run (['patchelf' , '--print-rpath' , os .fspath (filepath )], capture_output = True , text = True )
77
145
return r .stdout .strip ().split (':' )
78
146
79
- def _set_rpath (filepath : Path , rpath : Iterable [str ]) -> None :
147
+ @staticmethod
148
+ def set_rpath (filepath : Path , old : Iterable [str ], rpath : Iterable [str ]) -> None :
80
149
subprocess .run (['patchelf' ,'--set-rpath' , ':' .join (rpath ), os .fspath (filepath )], check = True )
81
150
82
- def fix_rpath (filepath : Path , libs_relative_path : str ) -> None :
83
- old_rpath = _get_rpath (filepath )
84
- new_rpath = []
85
- for path in old_rpath :
86
- if path .startswith ('$ORIGIN/' ):
87
- path = '$ORIGIN/' + libs_relative_path
88
- new_rpath .append (path )
89
- if new_rpath != old_rpath :
90
- _set_rpath (filepath , new_rpath )
151
+
152
+ if sys .platform == 'win32' or sys .platform == 'cygwin' :
153
+ _cls = _Windows
154
+ elif sys .platform == 'darwin' :
155
+ _cls = _MacOS
156
+ elif sys .platform == 'sunos5' :
157
+ _cls = _SunOS
158
+ else :
159
+ _cls = _ELF
160
+
161
+ _get_rpath = _cls .get_rpath
162
+ fix_rpath = _cls .fix_rpath
0 commit comments