From af215baac1dea09c2384e8025ab7571ead22df29 Mon Sep 17 00:00:00 2001 From: Mergen Nachin Date: Sat, 9 Aug 2025 09:34:47 +0100 Subject: [PATCH] RPATH Fix for portable_lib Python Extension Problem: The _portable_lib.so Python extension built on CI couldn't find PyTorch libraries when installed locally because it had hardcoded absolute paths from the CI build environment. Error: ImportError: dlopen(.../_portable_lib.cpython-311-darwin.so, 0x0002): Library not loaded: @rpath/libtorch_python.dylib Referenced from: .../executorch/extension/pybindings/_portable_lib.cpython-311-darwin.so Reason: tried: '/Users/runner/work/_temp/.../torch/lib/libtorch_python.dylib' (no such file) Root Cause: The CMake build was linking to PyTorch libraries using absolute paths from the build environment, without setting proper relative RPATHs for runtime library resolution. Solution: Added platform-specific relative RPATH settings to the portable_lib target in /Users/mnachin/executorch/CMakeLists.txt (lines 657-669): - macOS: Uses @loader_path/../../../torch/lib to find PyTorch libraries relative to the .so file location - Linux: Uses $ORIGIN/../../../torch/lib for the same purpose - Sets both BUILD_RPATH and INSTALL_RPATH to ensure consistency Impact: This allows the wheel-packaged _portable_lib.so to find PyTorch libraries regardless of the installation location, fixing the runtime linking issue when using ExecutorTorch wheels built on CI. Note: The same fix may be needed for _training_lib if it experiences similar issues. --- .ci/scripts/wheel/test_base.py | 12 ++++++++++++ CMakeLists.txt | 14 ++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/.ci/scripts/wheel/test_base.py b/.ci/scripts/wheel/test_base.py index f8a7309a6c2..278e46fe75a 100644 --- a/.ci/scripts/wheel/test_base.py +++ b/.ci/scripts/wheel/test_base.py @@ -41,6 +41,18 @@ class ModelTest: def run_tests(model_tests: List[ModelTest]) -> None: + # Test that we can import the portable_lib module - verifies RPATH is correct + print("Testing portable_lib import...") + try: + from executorch.extension.pybindings._portable_lib import ( # noqa: F401 + _load_for_executorch, + ) + + print("✓ Successfully imported _load_for_executorch from portable_lib") + except ImportError as e: + print(f"✗ Failed to import portable_lib: {e}") + raise + # Why are we doing this envvar shenanigans? Since we build the testers, which # uses buck, we cannot run as root. This is a sneaky of getting around that # test. diff --git a/CMakeLists.txt b/CMakeLists.txt index 1bbd700df55..57afd265446 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -654,6 +654,20 @@ if(EXECUTORCH_BUILD_PYBIND) target_compile_options(portable_lib PUBLIC ${_pybind_compile_options}) target_link_libraries(portable_lib PRIVATE ${_dep_libs}) + # Set RPATH to find PyTorch libraries relative to the installation location + # This goes from executorch/extension/pybindings up to site-packages, then to torch/lib + if(APPLE) + set_target_properties(portable_lib PROPERTIES + BUILD_RPATH "@loader_path/../../../torch/lib" + INSTALL_RPATH "@loader_path/../../../torch/lib" + ) + else() + set_target_properties(portable_lib PROPERTIES + BUILD_RPATH "$ORIGIN/../../../torch/lib" + INSTALL_RPATH "$ORIGIN/../../../torch/lib" + ) + endif() + install(TARGETS portable_lib LIBRARY DESTINATION executorch/extension/pybindings )