Skip to content

[feat/precompile-macro-infra] feat(cmake): opt-in precompiled static library, starting with pytypes.h - #6132

Open
henryiii wants to merge 4 commits into
feat/precompile-macro-infrafrom
feat/precompile-pytypes
Open

[feat/precompile-macro-infra] feat(cmake): opt-in precompiled static library, starting with pytypes.h#6132
henryiii wants to merge 4 commits into
feat/precompile-macro-infrafrom
feat/precompile-pytypes

Conversation

@henryiii

@henryiii henryiii commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

🤖 AI text below 🤖

Description

Stacked on #6131 (which is stacked on #6130). Implements the opt-in pre-compilation mode discussed in #2445 / #4001, using the nanobind/CLI11 model: a static library built lazily inside the consumer's own project, never shipped as a binary, so all per-DSO behavior (local internals key, module-local self-checks, libc++ exception classes) is preserved. Header-only stays the default and is byte-for-byte unchanged (verified: test binary size identical).

What this first slice contains:

  • pytypes.h split: out-of-line definitions (error_fetch_and_normalize, error_string, raise_from, memoryview::from_buffer) move to pytypes-inl.h, tagged PYBIND11_INLINE, included at the bottom of the header unless PYBIND11_PRECOMPILED is defined. Later PRs move detail/class.h, the non-template parts of detail/type_caster_base.h, detail/internals.h, and the pybind11.h dispatcher machinery through the same mechanism.
  • CMake: pybind11_precompile() creates a lazy pybind11::precompiled STATIC EXCLUDE_FROM_ALL target from src/*.cpp; pybind11_add_module(... PRECOMPILE), a global PYBIND11_PRECOMPILE switch, and a NO_PRECOMPILE opt-out (needed for modules that redefine PYBIND11_INTERNALS_VERSION per TU, like exo_planet_pybind11). Works in both subdirectory and installed/config modes.
  • Link-time config guard: PYBIND11_MODULE calls a do-nothing library function whose name encodes PYBIND11_INTERNALS_VERSION, Py_GIL_DISABLED, PYBIND11_SIMPLE_GIL_MANAGEMENT, and PYBIND11_DETAILED_ERROR_MESSAGES, so a config mismatch (or forgetting to link) is one readable undefined symbol. It caught the exo_planet_pybind11 internals-version mismatch on the first test run.
  • Packaging: src/ installs to share/pybind11/src (wheel remains py3-none-any; it ships sources, not binaries); src/pybind11_combined.cpp is a single-TU amalgam for non-CMake builds; pybind11_SRC_DIR exported from the CMake config.
  • Testing/CI: -DPYBIND11_TEST_PRECOMPILE=ON builds the entire test suite against the library (any definition missed by the split becomes a link error across ~50 modules; full pytest passes in both modes locally), two new test_cmake_build cases (subdirectory_precompile, installed_precompile), packaging file-set updates, clang-tidy preset coverage, and a 3-platform CI job.

setup_helpers / CLI / pkg-config / Meson support and docs+benchmarks follow in later PRs.

Suggested changelog entry:

  • Added an opt-in precompiled mode: pybind11_add_module(... PRECOMPILE) (or the global PYBIND11_PRECOMPILE CMake option) compiles part of pybind11 once into a per-project static library instead of into every translation unit, reducing build times. Header-only remains the default.

📚 Documentation preview 📚: https://pybind11--6132.org.readthedocs.build/

henryiii and others added 2 commits August 6, 2026 21:54
…h pytypes.h

Split the out-of-line pytypes.h definitions into pytypes-inl.h (fmtlib/
CLI11 style): inline by default, compiled once into a per-project static
library when PYBIND11_PRECOMPILED is defined. Infrastructure:

- pybind11_precompile() creates the lazy pybind11::precompiled STATIC
  library from the installed or in-tree src/ sources; PRECOMPILE /
  NO_PRECOMPILE keywords on pybind11_add_module and a global
  PYBIND11_PRECOMPILE switch select it per target.
- A link-time guard symbol encodes PYBIND11_INTERNALS_VERSION,
  Py_GIL_DISABLED, PYBIND11_SIMPLE_GIL_MANAGEMENT, and
  PYBIND11_DETAILED_ERROR_MESSAGES, so a configuration mismatch is one
  readable undefined symbol.
- src/ is installed to share/pybind11/src (wheel stays pure);
  src/pybind11_combined.cpp is a single-TU build for non-CMake use.
- Tests: PYBIND11_TEST_PRECOMPILE builds the whole suite against the
  library, two new test_cmake_build cases, packaging file lists, tidy
  preset, and a 3-platform CI job.

Assisted-by: ClaudeCode:claude-fable-5
In precompiled mode the -inl.h definitions are intentionally
non-inline, so misc-definitions-in-headers fires on every one. The
header-only tidy build already analyzes all -inl.h bodies via the
bottom-of-header includes.

Assisted-by: ClaudeCode:claude-fable-5
…macros

pybind11::pybind11 only carries headers; Py_GIL_DISABLED lives on
Python::Module via pybind11::module. Without it the library is
ABI-mismatched on free-threaded builds, and on Windows the pyconfig.h
autolink pragma in its objects requests pythonXY.lib instead of
pythonXYt.lib.

Assisted-by: ClaudeCode:claude-fable-5
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