Skip to content

Commit bb01e9a

Browse files
Fix hardcoded paths to libpython on macOS builds
Uses `install_name_tool` to adjust the search path of `libpython` to be relative to the executable. Adds a test to validate that the path is relative on macOS. Co-authored-by: Dean Moldovan <[email protected]>
1 parent 00493e8 commit bb01e9a

File tree

3 files changed

+39
-0
lines changed

3 files changed

+39
-0
lines changed

changelog.md

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## v1.5.2 | 2023-04-18
44

55
- Added a list of all installed packages to `licenses/packages.txt`.
6+
- Fixed executable's hardcoded search path for `libpython` on macOS.
67

78
## v1.5.1 | 2023-03-13
89

conanfile.py

+19
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import pathlib
33
import re
4+
from io import StringIO
45
from conan import ConanFile
56
from conan.tools.files import get, replace_in_file, download, rmdir, copy
67
from conan.tools.scm import Version
@@ -337,6 +338,7 @@ def install(self):
337338

338339
ver = ".".join(self.conanfile.pyversion.split(".")[:2])
339340
exe = str(self.prefix / f"bin/python{ver}")
341+
self._patch_libpython_path(exe)
340342

341343
specs = [
342344
f"pip=={self.conanfile.options.pip_version}",
@@ -362,3 +364,20 @@ def enable_site_packages(self):
362364
def build_bootstrap(self):
363365
"""For now, as a shortcut, we'll let the Unix-like builds bootstrap themselves"""
364366
return self.prefix / "bin/python3"
367+
368+
def _patch_libpython_path(self, exe):
369+
"""Patch libpython search path"""
370+
if self.conanfile.settings.os != "Macos":
371+
return
372+
373+
buffer = StringIO()
374+
self.conanfile.run(f"otool -L {exe}", output=buffer)
375+
lines = buffer.getvalue().strip().split('\n')[1:]
376+
libraries = [line.split()[0] for line in lines]
377+
378+
prefix = str(self.prefix)
379+
hardcoded_libraries = [lib for lib in libraries if lib.startswith(prefix)]
380+
for lib in hardcoded_libraries:
381+
relocatable_library = lib.replace(prefix, "@executable_path/..")
382+
self.conanfile.output.info(f"Patching {exe}, replace {lib} with {relocatable_library}")
383+
self.conanfile.run(f"install_name_tool -change {lib} {relocatable_library} {exe}")

test_package/conanfile.py

+19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pathlib
22
import sys
3+
from io import StringIO
34
from conan import ConanFile
45
from conan.tools.cmake import CMake, CMakeToolchain
56

@@ -53,6 +54,23 @@ def _test_env(self):
5354
name = str(self.options.env) if self.options.env else "baseline"
5455
self.run(f"{python_exe} {project_root / name / 'test.py'}", run_environment=True)
5556

57+
58+
def _test_libpython_path(self):
59+
if self.settings.os != "Macos":
60+
return
61+
62+
python_exe = str(pathlib.Path("./bin/python/bin/python3").resolve())
63+
buffer = StringIO()
64+
self.run(f'otool -L {python_exe}', run_environment=True, output=buffer)
65+
lines = buffer.getvalue().strip().split('\n')[1:]
66+
libraries = [line.split()[0] for line in lines]
67+
candidates = [lib for lib in libraries if "libpython" in lib]
68+
assert candidates, f"libpython dependency not found in 'otool' output: {libraries}"
69+
70+
for lib in candidates:
71+
assert lib.startswith("@executable_path"), f"libpython has an unexpected prefix: {lib}"
72+
73+
5674
def _test_embed(self):
5775
"""Ensure that everything is available to compile and link to the embedded Python"""
5876
self.run(pathlib.Path("bin", "test_package"), run_environment=True)
@@ -70,5 +88,6 @@ def _test_licenses(self):
7088

7189
def test(self):
7290
self._test_env()
91+
self._test_libpython_path()
7392
self._test_embed()
7493
self._test_licenses()

0 commit comments

Comments
 (0)