Skip to content

fix(cmake): don't link SHARED modules against libpython in FindPython mode - #6140

Open
henryiii wants to merge 1 commit into
masterfrom
fix-shared-libpython-link
Open

fix(cmake): don't link SHARED modules against libpython in FindPython mode#6140
henryiii wants to merge 1 commit into
masterfrom
fix-shared-libpython-link

Conversation

@henryiii

@henryiii henryiii commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

Our own test suite fails if using python-build-standalone (or any other statically linked Python). This is one option for a fix.

🤖 AI text below 🤖

Description

In FindPython mode, pybind11_add_module(... SHARED ...) routes through CMake's Python_add_library, which hard-links every non-MODULE library against Python::Python. An extension module must not link libpython on macOS: with a statically linked interpreter (uv / python-build-standalone), the linked libpython3.x.dylib loads as a second, uninitialized copy of the Python runtime, two-level namespace binds the module's C-API calls to it, and the import aborts with:

Fatal Python error: PyInterpreterState_Get: the function must be called with the GIL
held, ... but the GIL is released (the current Python thread state is NULL)

This is exactly what the installed_function case of test_cmake_build (the only case that uses SHARED) hits when the repo's documented cmake --workflow venv flow (uv-created venv) runs on macOS. Linux hides the same duplicate-runtime setup through ELF symbol interposition, and macOS CI uses dynamically linked python.org builds, so CI never sees it.

Fix, in pybind11NewTools.cmake:

  • SHARED now calls plain add_library instead of Python_add_library, and
  • links pybind11::module instead of pybind11::embed (only STATIC keeps pybind11::embed).

This matches what classic mode (pybind11Tools.cmake) has always done for SHARED, so it aligns the two code paths rather than inventing new semantics. Verified locally on macOS arm64 with a uv-managed CPython 3.13: the built module no longer links libpython, the previously-failing installed_function case passes, and all other test_cmake_build cases (including both embed cases) still pass.

Behavior notes:

  • SHARED no longer requires the Development.Embed component and no longer records a libpython dependency. An application that links a SHARED pybind11 library into an embedding executable must link libpython itself — already the case in classic mode.
  • SHARED + WITH_SOABI previously produced an author warning from FindPython ("MODULE only") and was a no-op; it now fails at configure because WITH_SOABI is no longer parsed out. Left unhandled to keep the change minimal.

Suggested changelog entry:

  • pybind11_add_module in FindPython mode no longer links SHARED extension modules against libpython, matching classic mode; this fixes import crashes with statically linked interpreters (e.g. uv / python-build-standalone) on macOS.

… mode

Python_add_library() hard-links non-MODULE libraries against
Python::Python. On macOS with a statically linked interpreter (e.g.
uv / python-build-standalone), importing such a module loads a second,
uninitialized copy of the Python runtime and aborts with a fatal
PyInterpreterState_Get error. Bypass python_add_library for SHARED and
link pybind11::module instead of pybind11::embed, matching the classic
pybind11Tools.cmake behavior.

Assisted-by: ClaudeCode:claude-fable-5
@henryiii
henryiii marked this pull request as ready for review August 7, 2026 12:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant