@@ -177,6 +177,8 @@ def test_cpp_consumer(work_dir: Path) -> None:
177177 subprocess .run ([str (consumer )], check = True , env = environment )
178178 print ("✓ C++ consumer builds and runs against the installed wheel" )
179179
180+ _assert_runs_relocated (consumer , package_dir , work_dir , environment )
181+
180182 assert shutil .which ("readelf" ) is not None , "readelf is required to check the ELF"
181183
182184 dynamic = subprocess .run (
@@ -193,6 +195,49 @@ def test_cpp_consumer(work_dir: Path) -> None:
193195 print ("✓ consumer depends on the shipped runtime with a relocatable RUNPATH" )
194196
195197
198+ def _assert_runs_relocated (consumer , package_dir , work_dir , environment ) -> None :
199+ """The app still runs after being moved away from the wheel.
200+
201+ Building in place leaves an absolute path to the wheel's lib directory in the
202+ binary's RUNPATH, which resolves the runtime no matter what `$ORIGIN` says.
203+ Copying the app next to a copy of the runtime, with that absolute entry
204+ removed, is what actually proves the package is relocatable.
205+
206+ The layout mirrors what the package config supports: the app in `bin/` with
207+ the libraries in a sibling `lib/`, which is what `$ORIGIN/../lib` resolves.
208+ """
209+ if shutil .which ("patchelf" ) is None :
210+ print ("- patchelf not available, skipping the relocated run" )
211+ return
212+
213+ deploy = work_dir / "deployed"
214+ (deploy / "bin" ).mkdir (parents = True , exist_ok = True )
215+ (deploy / "lib" ).mkdir (parents = True , exist_ok = True )
216+ moved = deploy / "bin" / consumer .name
217+ shutil .copy2 (consumer , moved )
218+ for library in (package_dir / "lib" ).glob ("*.so*" ):
219+ shutil .copy2 (library , deploy / "lib" / library .name )
220+
221+ # Keep only the $ORIGIN-relative entries, so nothing absolute can help.
222+ current = subprocess .run (
223+ ["patchelf" , "--print-rpath" , str (moved )],
224+ capture_output = True ,
225+ text = True ,
226+ check = True ,
227+ ).stdout .strip ()
228+ relative = [entry for entry in current .split (":" ) if entry .startswith ("$ORIGIN" )]
229+ assert relative , (
230+ "the consumer has no $ORIGIN-relative RUNPATH entry, so it cannot be "
231+ f"relocated; RUNPATH was: { current } "
232+ )
233+ subprocess .run (
234+ ["patchelf" , "--set-rpath" , ":" .join (relative ), str (moved )], check = True
235+ )
236+
237+ subprocess .run ([str (moved )], check = True , env = environment , cwd = str (deploy ))
238+ print ("✓ consumer still runs when deployed beside a copy of the runtime" )
239+
240+
196241def run_tests (work_dir : Path ) -> None :
197242 test_single_backend_registry ()
198243 test_single_threadpool ()
0 commit comments