Skip to content

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

Merged
henryiii merged 3 commits into
feat/precompile-macro-infrafrom
feat/precompile-pytypes
Oct 3, 2026
Merged

henryiii merged 3 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/


#if defined(PYBIND11_PRECOMPILED)
// PYBIND11_PRECOMPILED_CONFIG_CHECK names a do-nothing function defined in the precompiled
// pybind11 library. The identifier encodes every configuration macro that must match between

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we can remove the ones that are not user-settable (PYBIND11_HAS_SUBINTERPRETER_SUPPORT, Py_GIL_DISABLED, PYBIND11_BACKWARD_COMPATIBILITY_TP_DICTOFFSET)

@henryiii
henryiii force-pushed the feat/precompile-pytypes branch from 47e0301 to e98311a Compare August 15, 2026 03:45
@henryiii
henryiii force-pushed the feat/precompile-pytypes branch from e98311a to 86a984f Compare August 17, 2026 04:50
…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
@henryiii
henryiii force-pushed the feat/precompile-pytypes branch from 86a984f to 413c923 Compare September 27, 2026 02:19
@henryiii
henryiii merged commit b143f6c into master Oct 3, 2026
96 of 165 checks passed
@henryiii
henryiii deleted the feat/precompile-pytypes branch October 3, 2026 17:56
@github-actions github-actions Bot added the needs changelog Possibly needs a changelog entry label Oct 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs changelog Possibly needs a changelog entry

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant