@@ -157,6 +157,13 @@ if(NOT PYTHON_EXECUTABLE)
157157endif ()
158158announce_configured_options (PYTHON_EXECUTABLE )
159159
160+ # pybind11 >=3.0 uses find_package(Python) which looks for Python_EXECUTABLE,
161+ # not the legacy PYTHON_EXECUTABLE variable. Bridge the two so pybind11 picks up
162+ # the interpreter that setup.py (or the user) specified.
163+ if (PYTHON_EXECUTABLE AND NOT Python_EXECUTABLE)
164+ set (Python_EXECUTABLE "${PYTHON_EXECUTABLE} " )
165+ endif ()
166+
160167announce_configured_options (CMAKE_CXX_COMPILER_ID )
161168announce_configured_options (CMAKE_TOOLCHAIN_FILE )
162169announce_configured_options (BUILD_TESTING )
@@ -260,6 +267,27 @@ endif()
260267
261268add_subdirectory (third-party )
262269
270+ # On Apple, pybind11 SHARED extension modules must not link libpython directly.
271+ # When python_add_library creates a SHARED library, it links Python::Python (via
272+ # pybind11::embed) which pulls in libpython. This conflicts with pybind11
273+ # >=3.0's multi-phase module init — the duplicate Python runtime causes a GIL
274+ # state crash at import time. This function replaces pybind11::embed with
275+ # pybind11::module (which links Python::Module instead of Python::Python —
276+ # headers and ABI only, no libpython) and adds -undefined dynamic_lookup for
277+ # symbol resolution. No-op on non-Apple platforms.
278+ function (strip_python_lib target )
279+ if (NOT APPLE )
280+ return ()
281+ endif ()
282+ get_target_property (_libs ${target} LINK_LIBRARIES )
283+ if (_libs)
284+ list (REMOVE_ITEM _libs Python::Python pybind11::embed)
285+ list (APPEND _libs pybind11::module)
286+ set_target_properties (${target} PROPERTIES LINK_LIBRARIES "${_libs} " )
287+ endif ()
288+ target_link_options (${target} PRIVATE "LINKER:-undefined,dynamic_lookup" )
289+ endfunction ()
290+
263291# Size-optimized builds disable exceptions, RTTI, and unwind tables.
264292# ExecuTorch's runtime uses Result<T>/Error instead of exceptions, so
265293# -fno-exceptions is safe. Suppressing .eh_frame via -fno-unwind-tables is safe
@@ -1114,6 +1142,7 @@ if(EXECUTORCH_BUILD_PYBIND)
11141142
11151143 # pybind portable_lib
11161144 pybind11_add_module (portable_lib SHARED extension/pybindings/pybindings.cpp )
1145+ strip_python_lib (portable_lib )
11171146 # The actual output file needs a leading underscore so it can coexist with
11181147 # portable_lib.py in the same python package. PyTorch requires C++20, so
11191148 # pybindings must be compiled with C++20.
@@ -1155,6 +1184,7 @@ if(EXECUTORCH_BUILD_PYBIND)
11551184 pybind11_add_module (
11561185 data_loader SHARED extension/pybindings/pybindings_data_loader.cpp
11571186 )
1187+ strip_python_lib (data_loader )
11581188 target_include_directories (data_loader PRIVATE ${_common_include_directories} )
11591189 target_compile_options (data_loader PUBLIC ${_pybind_compile_options} )
11601190 target_link_libraries (data_loader PRIVATE executorch )
0 commit comments