From a0abb3557ff59094e0f604638cac124798865b60 Mon Sep 17 00:00:00 2001 From: Joink <755969798@qq.com> Date: Fri, 4 Sep 2026 04:35:03 +0800 Subject: [PATCH 1/2] fix(cmake): allow opting out of system pybind11 headers --- CMakeLists.txt | 4 ++ docs/compiling.rst | 6 +++ tests/test_cmake_build/CMakeLists.txt | 1 + .../fake_system/pybind11/pybind11.h | 1 + .../CMakeLists.txt | 37 +++++++++++++++++++ tools/pybind11NewTools.cmake | 7 ++++ tools/pybind11Tools.cmake | 7 ++++ 7 files changed, 63 insertions(+) create mode 100644 tests/test_cmake_build/fake_system/pybind11/pybind11.h create mode 100644 tests/test_cmake_build/subdirectory_system_headers/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 65f794a49d..f9020230e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -94,6 +94,8 @@ endif() # Options option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) +option(PYBIND11_USE_SYSTEM_HEADERS + "Mark pybind11 headers as system headers in add_subdirectory mode" ON) option(PYBIND11_NOPYTHON "Disable search for Python" OFF) option(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION "To enforce that a handle_type_name<> specialization exists" OFF) @@ -293,6 +295,8 @@ if(NOT TARGET pybind11_headers) target_include_directories( pybind11_headers ${pybind11_system} INTERFACE $ $) + set_property( + TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS "${PYBIND11_USE_SYSTEM_HEADERS}") target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals cxx_right_angle_brackets) diff --git a/docs/compiling.rst b/docs/compiling.rst index a6bee86ffe..f852589a3c 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -506,6 +506,12 @@ You can also use the targets (as listed below) with FindPython. If you define (mostly useful when building pybind11's own tests, or as a way to change search algorithms from the CMake invocation, with ``-DPYBIND11_FINDPYTHON=ON``. +When adding pybind11 as a subdirectory, its headers are marked as system headers +by default. If a parent project globally adds a prefix containing another +pybind11 installation, add ``set(PYBIND11_USE_SYSTEM_HEADERS OFF)`` before +``add_subdirectory(pybind11)``. ``pybind11_add_module()`` then prioritizes the +subdirectory's headers over the parent include path. + .. warning:: If you use FindPython to multi-target Python versions, use the individual diff --git a/tests/test_cmake_build/CMakeLists.txt b/tests/test_cmake_build/CMakeLists.txt index a4d25448e5..bed0cfc3e4 100644 --- a/tests/test_cmake_build/CMakeLists.txt +++ b/tests/test_cmake_build/CMakeLists.txt @@ -68,6 +68,7 @@ endif() possibly_uninitialized(PYTHON_MODULE_EXTENSION Python_INTERPRETER_ID) pybind11_add_build_test(subdirectory_function) +pybind11_add_build_test(subdirectory_system_headers) pybind11_add_build_test(subdirectory_target) if("${PYTHON_MODULE_EXTENSION}" MATCHES "pypy" OR "${Python_INTERPRETER_ID}" STREQUAL "PyPy" diff --git a/tests/test_cmake_build/fake_system/pybind11/pybind11.h b/tests/test_cmake_build/fake_system/pybind11/pybind11.h new file mode 100644 index 0000000000..b6d599f334 --- /dev/null +++ b/tests/test_cmake_build/fake_system/pybind11/pybind11.h @@ -0,0 +1 @@ +#error "The globally installed pybind11 headers were selected before the add_subdirectory source tree." diff --git a/tests/test_cmake_build/subdirectory_system_headers/CMakeLists.txt b/tests/test_cmake_build/subdirectory_system_headers/CMakeLists.txt new file mode 100644 index 0000000000..38a1b85265 --- /dev/null +++ b/tests/test_cmake_build/subdirectory_system_headers/CMakeLists.txt @@ -0,0 +1,37 @@ +cmake_minimum_required(VERSION 3.15...4.2) + +project(test_subdirectory_system_headers CXX) + +# Model an older pybind11 copy installed in a global include prefix by a parent project. +include_directories("${CMAKE_CURRENT_LIST_DIR}/../fake_system") + +set(PYBIND11_USE_SYSTEM_HEADERS OFF) + +# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests +# (makes transition easier while we support both modes). +if(DEFINED PYTHON_EXECUTABLE AND NOT DEFINED Python_EXECUTABLE) + set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") +endif() + +add_subdirectory("${pybind11_SOURCE_DIR}" pybind11) +pybind11_add_module(test_subdirectory_system_headers NO_EXTRAS ../main.cpp) +set_target_properties(test_subdirectory_system_headers PROPERTIES OUTPUT_NAME test_cmake_build) + +if(DEFINED Python_EXECUTABLE) + set(_Python_EXECUTABLE "${Python_EXECUTABLE}") +elseif(DEFINED PYTHON_EXECUTABLE) + set(_Python_EXECUTABLE "${PYTHON_EXECUTABLE}") +else() + message(FATAL_ERROR "No Python executable defined (should not be possible at this stage)") +endif() + +add_custom_target( + check_subdirectory_system_headers + ${CMAKE_COMMAND} + -E + env + PYTHONPATH=$ + ${_Python_EXECUTABLE} + ${PROJECT_SOURCE_DIR}/../test.py + ${PROJECT_NAME} + DEPENDS test_subdirectory_system_headers) diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index b0fe20768d..2bb0dde9ff 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -276,6 +276,13 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::headers) + get_property( + _pybind11_use_system_headers TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS) + if(NOT _pybind11_use_system_headers) + set_property(TARGET pybind11_headers PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "") + target_include_directories(${target_name} BEFORE PRIVATE "${pybind11_INCLUDE_DIR}") + endif() + if(lib_type STREQUAL "MODULE") target_link_libraries(${target_name} PRIVATE pybind11::module) else() diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 81faee7d8b..f32c7d5331 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -160,6 +160,13 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::module) + get_property( + _pybind11_use_system_headers TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS) + if(NOT _pybind11_use_system_headers) + set_property(TARGET pybind11_headers PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "") + target_include_directories(${target_name} BEFORE PRIVATE "${pybind11_INCLUDE_DIR}") + endif() + if(ARG_SYSTEM) message( STATUS From b8f6e6241bb3993b390f13e6fc62dcc7a28a427a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 3 Sep 2026 21:07:17 +0000 Subject: [PATCH 2/2] style: pre-commit fixes --- CMakeLists.txt | 4 ++-- tests/test_cmake_build/fake_system/pybind11/pybind11.h | 3 ++- tools/pybind11NewTools.cmake | 4 +++- tools/pybind11Tools.cmake | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f9020230e0..18e230d5db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -295,8 +295,8 @@ if(NOT TARGET pybind11_headers) target_include_directories( pybind11_headers ${pybind11_system} INTERFACE $ $) - set_property( - TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS "${PYBIND11_USE_SYSTEM_HEADERS}") + set_property(TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS + "${PYBIND11_USE_SYSTEM_HEADERS}") target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals cxx_right_angle_brackets) diff --git a/tests/test_cmake_build/fake_system/pybind11/pybind11.h b/tests/test_cmake_build/fake_system/pybind11/pybind11.h index b6d599f334..350866310f 100644 --- a/tests/test_cmake_build/fake_system/pybind11/pybind11.h +++ b/tests/test_cmake_build/fake_system/pybind11/pybind11.h @@ -1 +1,2 @@ -#error "The globally installed pybind11 headers were selected before the add_subdirectory source tree." +#error \ + "The globally installed pybind11 headers were selected before the add_subdirectory source tree." diff --git a/tools/pybind11NewTools.cmake b/tools/pybind11NewTools.cmake index 2bb0dde9ff..71b0135b5f 100644 --- a/tools/pybind11NewTools.cmake +++ b/tools/pybind11NewTools.cmake @@ -277,7 +277,9 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::headers) get_property( - _pybind11_use_system_headers TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS) + _pybind11_use_system_headers + TARGET pybind11_headers + PROPERTY PYBIND11_USE_SYSTEM_HEADERS) if(NOT _pybind11_use_system_headers) set_property(TARGET pybind11_headers PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "") target_include_directories(${target_name} BEFORE PRIVATE "${pybind11_INCLUDE_DIR}") diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index f32c7d5331..a566bbf977 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -161,7 +161,9 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE pybind11::module) get_property( - _pybind11_use_system_headers TARGET pybind11_headers PROPERTY PYBIND11_USE_SYSTEM_HEADERS) + _pybind11_use_system_headers + TARGET pybind11_headers + PROPERTY PYBIND11_USE_SYSTEM_HEADERS) if(NOT _pybind11_use_system_headers) set_property(TARGET pybind11_headers PROPERTY INTERFACE_SYSTEM_INCLUDE_DIRECTORIES "") target_include_directories(${target_name} BEFORE PRIVATE "${pybind11_INCLUDE_DIR}")