1111
1212
1313if typing .TYPE_CHECKING :
14- from typing import List
14+ from typing import List , Optional , TypeVar
1515
1616 from mesonpy ._compat import Iterable , Path
1717
18+ T = TypeVar ('T' )
1819
19- if sys .platform == 'win32' or sys .platform == 'cygwin' :
2020
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
2327
24- elif sys .platform == 'darwin' :
2528
26- def _get_rpath (filepath : Path ) -> List [str ]:
29+ class RPATH :
30+ origin = '$ORIGIN'
31+
32+ @staticmethod
33+ def get_rpath (filepath : Path ) -> List [str ]:
34+ raise NotImplementedError
35+
36+ @staticmethod
37+ def set_rpath (filepath : Path , old : List [str ], rpath : List [str ]) -> None :
38+ raise NotImplementedError
39+
40+ @classmethod
41+ def fix_rpath (self , filepath : Path , install_rpath : Optional [str ], libs_rpath : Optional [str ]) -> None :
42+ old_rpath = self .get_rpath (filepath )
43+ new_rpath = []
44+ if libs_rpath is not None :
45+ if libs_rpath == '.' :
46+ libs_rpath = ''
47+ for path in old_rpath :
48+ if path .split ('/' , 1 )[0 ] == self .origin :
49+ # Any RPATH entry relative to ``$ORIGIN`` is interpreted as
50+ # pointing to a location in the build directory added by
51+ # Meson. These need to be removed. Their presence indicates
52+ # that the executable, shared library, or Python module
53+ # depends on libraries build as part of the package. These
54+ # entries are thus replaced with entries pointing to the
55+ # ``.<package-name>.mesonpy.libs`` folder where meson-python
56+ # relocates shared libraries distributed with the package.
57+ # The package may however explicitly install these in a
58+ # different location, thus this is not a perfect heuristic
59+ # and may add not required RPATH entries. These are however
60+ # harmless.
61+ path = f'{ self .origin } /{ libs_rpath } '
62+ # Any other RPATH entry is preserved.
63+ new_rpath .append (path )
64+ if install_rpath :
65+ # Add the RPATH entry spcified with the ``install_rpath`` argument.
66+ new_rpath .append (install_rpath )
67+ # Make the RPATH entries unique.
68+ new_rpath = unique (new_rpath )
69+ if new_rpath != old_rpath :
70+ self .set_rpath (filepath , old_rpath , new_rpath )
71+
72+
73+ class _MacOS (RPATH ):
74+ origin = '@loader_path'
75+
76+ @staticmethod
77+ def get_rpath (filepath : Path ) -> List [str ]:
2778 rpath = []
2879 r = subprocess .run (['otool' , '-l' , os .fspath (filepath )], capture_output = True , text = True )
2980 rpath_tag = False
@@ -35,17 +86,31 @@ def _get_rpath(filepath: Path) -> List[str]:
3586 rpath_tag = False
3687 return rpath
3788
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 ]:
89+ @staticmethod
90+ def set_rpath (filepath : Path , old : List [str ], rpath : List [str ]) -> None :
91+ args : List [str ] = []
92+ for path in rpath :
93+ if path not in old :
94+ args += ['-add_rpath' , path ]
95+ for path in old :
96+ if path not in rpath :
97+ args += ['-delete_rpath' , path ]
98+ subprocess .run (['install_name_tool' , * args , os .fspath (filepath )], check = True )
99+
100+ @classmethod
101+ def fix_rpath (self , filepath : Path , install_rpath : Optional [str ], libs_rpath : Optional [str ]) -> None :
102+ if install_rpath is not None :
103+ root , sep , stem = install_rpath .partition ('/' )
104+ if root == '$ORIGIN' :
105+ install_rpath = f'{ self .origin } { sep } { stem } '
106+ # warnings.warn('...')
107+ super ().fix_rpath (filepath , install_rpath , libs_rpath )
108+
109+
110+ class _SunOS (RPATH ):
111+
112+ @staticmethod
113+ def get_rpath (filepath : Path ) -> List [str ]:
49114 rpath = []
50115 r = subprocess .run (['/usr/bin/elfedit' , '-r' , '-e' , 'dyn:rpath' , os .fspath (filepath )],
51116 capture_output = True , check = True , text = True )
@@ -56,35 +121,39 @@ def _get_rpath(filepath: Path) -> List[str]:
56121 rpath .append (path )
57122 return rpath
58123
59- def _set_rpath (filepath : Path , rpath : Iterable [str ]) -> None :
124+ @staticmethod
125+ def set_rpath (filepath : Path , old : Iterable [str ], rpath : Iterable [str ]) -> None :
60126 subprocess .run (['/usr/bin/elfedit' , '-e' , 'dyn:rpath ' + ':' .join (rpath ), os .fspath (filepath )], check = True )
61127
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 )
71128
72- else :
73- # Assume that any other platform uses ELF binaries.
129+ class _ELF (RPATH ):
74130
75- def _get_rpath (filepath : Path ) -> List [str ]:
131+ @staticmethod
132+ def get_rpath (filepath : Path ) -> List [str ]:
76133 r = subprocess .run (['patchelf' , '--print-rpath' , os .fspath (filepath )], capture_output = True , text = True )
77134 return r .stdout .strip ().split (':' )
78135
79- def _set_rpath (filepath : Path , rpath : Iterable [str ]) -> None :
136+ @staticmethod
137+ def set_rpath (filepath : Path , old : Iterable [str ], rpath : Iterable [str ]) -> None :
80138 subprocess .run (['patchelf' ,'--set-rpath' , ':' .join (rpath ), os .fspath (filepath )], check = True )
81139
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 )
140+
141+ if sys .platform == 'win32' or sys .platform == 'cygwin' :
142+
143+ def _get_rpath (filepath : Path ) -> List [str ]:
144+ return []
145+
146+ def fix_rpath (filepath : Path , install_rpath : Optional [str ], libs_rpath : Optional [str ]) -> None :
147+ pass
148+
149+ elif sys .platform == 'darwin' :
150+ _get_rpath = _MacOS .get_rpath
151+ fix_rpath = _MacOS .fix_rpath
152+
153+ elif sys .platform == 'sunos5' :
154+ _get_rpath = _SunOS .get_rpath
155+ fix_rpath = _SunOS .fix_rpath
156+
157+ else :
158+ _get_rpath = _ELF .get_rpath
159+ fix_rpath = _ELF .fix_rpath
0 commit comments