mirror of https://github.com/pybind/pybind11
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
423 lines
16 KiB
423 lines
16 KiB
# CMakeLists.txt -- Build system for the pybind11 modules |
|
# |
|
# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch> |
|
# |
|
# All rights reserved. Use of this source code is governed by a |
|
# BSD-style license that can be found in the LICENSE file. |
|
|
|
# Propagate this policy (FindPythonInterp removal) so it can be detected later |
|
if(NOT CMAKE_VERSION VERSION_LESS "3.27") |
|
cmake_policy(GET CMP0148 _pybind11_cmp0148) |
|
endif() |
|
|
|
cmake_minimum_required(VERSION 3.15...4.0) |
|
|
|
if(_pybind11_cmp0148) |
|
cmake_policy(SET CMP0148 ${_pybind11_cmp0148}) |
|
unset(_pybind11_cmp0148) |
|
endif() |
|
|
|
# Avoid infinite recursion if tests include this as a subdirectory |
|
include_guard(GLOBAL) |
|
|
|
# Extract project version from source |
|
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/include/pybind11/detail/common.h" |
|
pybind11_version_defines REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") |
|
|
|
foreach(ver ${pybind11_version_defines}) |
|
if(ver MATCHES [[#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$]]) |
|
set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}") |
|
endif() |
|
endforeach() |
|
|
|
if(PYBIND11_VERSION_PATCH MATCHES [[\.([a-zA-Z0-9]+)$]]) |
|
set(pybind11_VERSION_TYPE "${CMAKE_MATCH_1}") |
|
endif() |
|
string(REGEX MATCH "^[0-9]+" PYBIND11_VERSION_PATCH "${PYBIND11_VERSION_PATCH}") |
|
|
|
project( |
|
pybind11 |
|
LANGUAGES CXX |
|
VERSION "${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}") |
|
|
|
# Standard includes |
|
include(GNUInstallDirs) |
|
include(CMakePackageConfigHelpers) |
|
include(CMakeDependentOption) |
|
|
|
if(NOT pybind11_FIND_QUIETLY) |
|
message(STATUS "pybind11 v${pybind11_VERSION} ${pybind11_VERSION_TYPE}") |
|
endif() |
|
|
|
# Check if pybind11 is being used directly or via add_subdirectory |
|
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) |
|
### Warn if not an out-of-source builds |
|
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) |
|
set(lines |
|
"You are building in-place. If that is not what you intended to " |
|
"do, you can clean the source directory with:\n" |
|
"rm -r CMakeCache.txt CMakeFiles/ cmake_uninstall.cmake pybind11Config.cmake " |
|
"pybind11ConfigVersion.cmake tests/CMakeFiles/\n") |
|
message(AUTHOR_WARNING ${lines}) |
|
endif() |
|
|
|
set(PYBIND11_MASTER_PROJECT ON) |
|
|
|
message(STATUS "CMake ${CMAKE_VERSION}") |
|
|
|
if(DEFINED SKBUILD AND DEFINED ENV{PYBIND11_GLOBAL_SDIST}) |
|
message( |
|
FATAL_ERROR |
|
"PYBIND11_GLOBAL_SDIST is not supported, use nox -s build_global or a pybind11-global SDist instead." |
|
) |
|
endif() |
|
|
|
if(CMAKE_CXX_STANDARD) |
|
set(CMAKE_CXX_EXTENSIONS OFF) |
|
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
|
endif() |
|
|
|
set(pybind11_system "") |
|
|
|
set_property(GLOBAL PROPERTY USE_FOLDERS ON) |
|
if(CMAKE_VERSION VERSION_LESS "3.18") |
|
set(_pybind11_findpython_default OFF) |
|
else() |
|
set(_pybind11_findpython_default ON) |
|
endif() |
|
else() |
|
set(PYBIND11_MASTER_PROJECT OFF) |
|
set(pybind11_system SYSTEM) |
|
set(_pybind11_findpython_default COMPAT) |
|
endif() |
|
|
|
# Options |
|
option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJECT}) |
|
option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) |
|
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) |
|
option(PYBIND11_SIMPLE_GIL_MANAGEMENT |
|
"Use simpler GIL management logic that does not support disassociation" OFF) |
|
set(PYBIND11_INTERNALS_VERSION |
|
"" |
|
CACHE STRING "Override the ABI version, may be used to enable the unstable ABI.") |
|
option(PYBIND11_USE_CROSSCOMPILING "Respect CMAKE_CROSSCOMPILING" OFF) |
|
|
|
if(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION) |
|
add_compile_definitions(PYBIND11_DISABLE_HANDLE_TYPE_NAME_DEFAULT_IMPLEMENTATION) |
|
endif() |
|
if(PYBIND11_SIMPLE_GIL_MANAGEMENT) |
|
add_compile_definitions(PYBIND11_SIMPLE_GIL_MANAGEMENT) |
|
endif() |
|
|
|
cmake_dependent_option( |
|
USE_PYTHON_INCLUDE_DIR |
|
"Install pybind11 headers in Python include directory instead of default installation prefix" |
|
OFF "PYBIND11_INSTALL" OFF) |
|
|
|
set(PYBIND11_FINDPYTHON |
|
${_pybind11_findpython_default} |
|
CACHE STRING "Force new FindPython - NEW, OLD, COMPAT") |
|
|
|
if(PYBIND11_MASTER_PROJECT) |
|
|
|
# Allow PYTHON_EXECUTABLE if in FINDPYTHON mode and building pybind11's tests |
|
# (makes transition easier while we support both modes). |
|
if(PYBIND11_FINDPYTHON |
|
AND DEFINED PYTHON_EXECUTABLE |
|
AND NOT DEFINED Python_EXECUTABLE) |
|
set(Python_EXECUTABLE "${PYTHON_EXECUTABLE}") |
|
endif() |
|
|
|
# This is a shortcut that is primarily for the venv cmake preset, |
|
# but can be used to quickly setup tests manually, too |
|
set(PYBIND11_CREATE_WITH_UV |
|
"" |
|
CACHE STRING "Create a virtualenv if it doesn't exist") |
|
|
|
if(NOT PYBIND11_CREATE_WITH_UV STREQUAL "") |
|
set(Python_ROOT_DIR "${CMAKE_CURRENT_BINARY_DIR}/.venv") |
|
if(EXISTS "${Python_ROOT_DIR}") |
|
if(EXISTS "${CMAKE_BINARY_DIR}/CMakeCache.txt") |
|
message(STATUS "Using existing venv at ${Python_ROOT_DIR}, remove or --fresh to recreate") |
|
else() |
|
# --fresh used to remove the cache |
|
file(REMOVE_RECURSE "${CMAKE_CURRENT_BINARY_DIR}/.venv") |
|
endif() |
|
endif() |
|
if(NOT EXISTS "${Python_ROOT_DIR}") |
|
find_program(UV uv REQUIRED) |
|
# CMake 3.19+ would be able to use COMMAND_ERROR_IS_FATAL |
|
message( |
|
STATUS "Creating venv with ${UV} venv -p ${PYBIND11_CREATE_WITH_UV} '${Python_ROOT_DIR}'") |
|
execute_process(COMMAND ${UV} venv -p ${PYBIND11_CREATE_WITH_UV} "${Python_ROOT_DIR}" |
|
RESULT_VARIABLE _venv_result) |
|
if(_venv_result AND NOT _venv_result EQUAL 0) |
|
message(FATAL_ERROR "uv venv failed with '${_venv_result}'") |
|
endif() |
|
message( |
|
STATUS |
|
"Installing deps with ${UV} pip install -p '${Python_ROOT_DIR}' -r tests/requirements.txt" |
|
) |
|
execute_process( |
|
COMMAND ${UV} pip install -p "${Python_ROOT_DIR}" -r |
|
"${CMAKE_CURRENT_SOURCE_DIR}/tests/requirements.txt" RESULT_VARIABLE _pip_result) |
|
if(_pip_result AND NOT _pip_result EQUAL 0) |
|
message(FATAL_ERROR "uv pip install failed with '${_pip_result}'") |
|
endif() |
|
endif() |
|
else() |
|
if(NOT DEFINED Python3_EXECUTABLE |
|
AND NOT DEFINED Python_EXECUTABLE |
|
AND NOT DEFINED Python_ROOT_DIR |
|
AND NOT DEFINED ENV{VIRTUALENV} |
|
AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.venv") |
|
message(STATUS "Autodetecting Python in virtual environment") |
|
set(Python_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.venv") |
|
endif() |
|
endif() |
|
endif() |
|
|
|
set(PYBIND11_HEADERS |
|
include/pybind11/detail/class.h |
|
include/pybind11/detail/common.h |
|
include/pybind11/detail/cpp_conduit.h |
|
include/pybind11/detail/descr.h |
|
include/pybind11/detail/dynamic_raw_ptr_cast_if_possible.h |
|
include/pybind11/detail/exception_translation.h |
|
include/pybind11/detail/function_record_pyobject.h |
|
include/pybind11/detail/init.h |
|
include/pybind11/detail/internals.h |
|
include/pybind11/detail/native_enum_data.h |
|
include/pybind11/detail/pybind11_namespace_macros.h |
|
include/pybind11/detail/struct_smart_holder.h |
|
include/pybind11/detail/type_caster_base.h |
|
include/pybind11/detail/typeid.h |
|
include/pybind11/detail/using_smart_holder.h |
|
include/pybind11/detail/value_and_holder.h |
|
include/pybind11/attr.h |
|
include/pybind11/buffer_info.h |
|
include/pybind11/cast.h |
|
include/pybind11/chrono.h |
|
include/pybind11/common.h |
|
include/pybind11/complex.h |
|
include/pybind11/conduit/pybind11_conduit_v1.h |
|
include/pybind11/conduit/pybind11_platform_abi_id.h |
|
include/pybind11/conduit/wrap_include_python_h.h |
|
include/pybind11/critical_section.h |
|
include/pybind11/options.h |
|
include/pybind11/eigen.h |
|
include/pybind11/eigen/common.h |
|
include/pybind11/eigen/matrix.h |
|
include/pybind11/eigen/tensor.h |
|
include/pybind11/embed.h |
|
include/pybind11/eval.h |
|
include/pybind11/gil.h |
|
include/pybind11/gil_safe_call_once.h |
|
include/pybind11/gil_simple.h |
|
include/pybind11/iostream.h |
|
include/pybind11/functional.h |
|
include/pybind11/native_enum.h |
|
include/pybind11/numpy.h |
|
include/pybind11/operators.h |
|
include/pybind11/pybind11.h |
|
include/pybind11/pytypes.h |
|
include/pybind11/subinterpreter.h |
|
include/pybind11/stl.h |
|
include/pybind11/stl_bind.h |
|
include/pybind11/stl/filesystem.h |
|
include/pybind11/trampoline_self_life_support.h |
|
include/pybind11/type_caster_pyobject_ptr.h |
|
include/pybind11/typing.h |
|
include/pybind11/warnings.h) |
|
|
|
# Compare with grep and warn if mismatched |
|
if(PYBIND11_MASTER_PROJECT) |
|
file( |
|
GLOB_RECURSE _pybind11_header_check |
|
LIST_DIRECTORIES false |
|
RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" |
|
CONFIGURE_DEPENDS "include/pybind11/*.h") |
|
set(_pybind11_here_only ${PYBIND11_HEADERS}) |
|
set(_pybind11_disk_only ${_pybind11_header_check}) |
|
list(REMOVE_ITEM _pybind11_here_only ${_pybind11_header_check}) |
|
list(REMOVE_ITEM _pybind11_disk_only ${PYBIND11_HEADERS}) |
|
if(_pybind11_here_only) |
|
message(AUTHOR_WARNING "PYBIND11_HEADERS has extra files:" ${_pybind11_here_only}) |
|
endif() |
|
if(_pybind11_disk_only) |
|
message(AUTHOR_WARNING "PYBIND11_HEADERS is missing files:" ${_pybind11_disk_only}) |
|
endif() |
|
endif() |
|
|
|
list(TRANSFORM PYBIND11_HEADERS PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/") |
|
|
|
# Cache variable so this can be used in parent projects |
|
set(pybind11_INCLUDE_DIR |
|
"${CMAKE_CURRENT_LIST_DIR}/include" |
|
CACHE INTERNAL "Directory where pybind11 headers are located") |
|
|
|
# Backward compatible variable for add_subdirectory mode |
|
if(NOT PYBIND11_MASTER_PROJECT) |
|
set(PYBIND11_INCLUDE_DIR |
|
"${pybind11_INCLUDE_DIR}" |
|
CACHE INTERNAL "") |
|
endif() |
|
|
|
# Note: when creating targets, you cannot use if statements at configure time - |
|
# you need generator expressions, because those will be placed in the target file. |
|
# You can also place ifs *in* the Config.in, but not here. |
|
|
|
# This section builds targets, but does *not* touch Python |
|
# Non-IMPORT targets cannot be defined twice |
|
if(NOT TARGET pybind11_headers) |
|
# Build the headers-only target (no Python included): |
|
# (long name used here to keep this from clashing in subdirectory mode) |
|
add_library(pybind11_headers INTERFACE) |
|
add_library(pybind11::pybind11_headers ALIAS pybind11_headers) # to match exported target |
|
add_library(pybind11::headers ALIAS pybind11_headers) # easier to use/remember |
|
|
|
target_include_directories( |
|
pybind11_headers ${pybind11_system} INTERFACE $<BUILD_INTERFACE:${pybind11_INCLUDE_DIR}> |
|
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>) |
|
|
|
target_compile_features(pybind11_headers INTERFACE cxx_inheriting_constructors cxx_user_literals |
|
cxx_right_angle_brackets) |
|
if(NOT "${PYBIND11_INTERNALS_VERSION}" STREQUAL "") |
|
target_compile_definitions( |
|
pybind11_headers INTERFACE "PYBIND11_INTERNALS_VERSION=${PYBIND11_INTERNALS_VERSION}") |
|
endif() |
|
else() |
|
# It is invalid to install a target twice, too. |
|
set(PYBIND11_INSTALL OFF) |
|
endif() |
|
|
|
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11Common.cmake") |
|
# https://github.com/jtojnar/cmake-snips/#concatenating-paths-when-building-pkg-config-files |
|
# TODO: cmake 3.20 adds the cmake_path() function, which obsoletes this snippet |
|
include("${CMAKE_CURRENT_SOURCE_DIR}/tools/JoinPaths.cmake") |
|
|
|
# Relative directory setting |
|
if(USE_PYTHON_INCLUDE_DIR AND DEFINED Python_INCLUDE_DIRS) |
|
file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${Python_INCLUDE_DIRS}) |
|
elseif(USE_PYTHON_INCLUDE_DIR AND DEFINED PYTHON_INCLUDE_DIR) |
|
file(RELATIVE_PATH CMAKE_INSTALL_INCLUDEDIR ${CMAKE_INSTALL_PREFIX} ${PYTHON_INCLUDE_DIRS}) |
|
endif() |
|
|
|
if(PYBIND11_INSTALL) |
|
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11_global") |
|
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION "${SKBUILD_HEADERS_DIR}") |
|
endif() |
|
install(DIRECTORY ${pybind11_INCLUDE_DIR}/pybind11 DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) |
|
set(PYBIND11_CMAKECONFIG_INSTALL_DIR |
|
"${CMAKE_INSTALL_DATAROOTDIR}/cmake/${PROJECT_NAME}" |
|
CACHE STRING "install path for pybind11Config.cmake") |
|
|
|
if(IS_ABSOLUTE "${CMAKE_INSTALL_INCLUDEDIR}") |
|
set(pybind11_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}") |
|
else() |
|
set(pybind11_INCLUDEDIR "\$\{PACKAGE_PREFIX_DIR\}/${CMAKE_INSTALL_INCLUDEDIR}") |
|
endif() |
|
|
|
configure_package_config_file( |
|
tools/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" |
|
INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) |
|
|
|
# CMake natively supports header-only libraries |
|
write_basic_package_version_file( |
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake |
|
VERSION ${PROJECT_VERSION} |
|
COMPATIBILITY AnyNewerVersion ARCH_INDEPENDENT) |
|
|
|
install( |
|
FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake |
|
${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake |
|
tools/FindPythonLibsNew.cmake |
|
tools/pybind11Common.cmake |
|
tools/pybind11Tools.cmake |
|
tools/pybind11NewTools.cmake |
|
tools/pybind11GuessPythonExtSuffix.cmake |
|
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) |
|
|
|
if(NOT PYBIND11_EXPORT_NAME) |
|
set(PYBIND11_EXPORT_NAME "${PROJECT_NAME}Targets") |
|
endif() |
|
|
|
install(TARGETS pybind11_headers EXPORT "${PYBIND11_EXPORT_NAME}") |
|
|
|
install( |
|
EXPORT "${PYBIND11_EXPORT_NAME}" |
|
NAMESPACE "pybind11::" |
|
DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) |
|
|
|
# pkg-config support |
|
if(NOT prefix_for_pc_file) |
|
if(IS_ABSOLUTE "${CMAKE_INSTALL_DATAROOTDIR}") |
|
set(prefix_for_pc_file "${CMAKE_INSTALL_PREFIX}") |
|
else() |
|
set(pc_datarootdir "${CMAKE_INSTALL_DATAROOTDIR}") |
|
if(CMAKE_VERSION VERSION_LESS 3.20) |
|
set(prefix_for_pc_file "\${pcfiledir}/..") |
|
while(pc_datarootdir) |
|
get_filename_component(pc_datarootdir "${pc_datarootdir}" DIRECTORY) |
|
string(APPEND prefix_for_pc_file "/..") |
|
endwhile() |
|
else() |
|
cmake_path(RELATIVE_PATH CMAKE_INSTALL_PREFIX BASE_DIRECTORY CMAKE_INSTALL_DATAROOTDIR |
|
OUTPUT_VARIABLE prefix_for_pc_file) |
|
endif() |
|
endif() |
|
endif() |
|
join_paths(includedir_for_pc_file "\${prefix}" "${CMAKE_INSTALL_INCLUDEDIR}") |
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/pybind11.pc.in" |
|
"${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" @ONLY) |
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/pybind11.pc" |
|
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/") |
|
|
|
# When building a wheel, include __init__.py's for modules |
|
# (see https://github.com/pybind/pybind11/pull/5552) |
|
if(DEFINED SKBUILD_PROJECT_NAME AND SKBUILD_PROJECT_NAME STREQUAL "pybind11") |
|
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/empty") |
|
file(TOUCH "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py") |
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py" |
|
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/") |
|
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/empty/__init__.py" |
|
DESTINATION "${CMAKE_INSTALL_DATAROOTDIR}/pkgconfig/") |
|
endif() |
|
|
|
# Uninstall target |
|
if(PYBIND11_MASTER_PROJECT) |
|
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/tools/cmake_uninstall.cmake.in" |
|
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" IMMEDIATE @ONLY) |
|
|
|
add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P |
|
${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) |
|
endif() |
|
endif() |
|
|
|
# BUILD_TESTING takes priority, but only if this is the master project |
|
if(PYBIND11_MASTER_PROJECT AND DEFINED BUILD_TESTING) |
|
if(BUILD_TESTING) |
|
if(_pybind11_nopython) |
|
message(FATAL_ERROR "Cannot activate tests in NOPYTHON mode") |
|
else() |
|
add_subdirectory(tests) |
|
endif() |
|
endif() |
|
else() |
|
if(PYBIND11_TEST) |
|
if(_pybind11_nopython) |
|
message(FATAL_ERROR "Cannot activate tests in NOPYTHON mode") |
|
else() |
|
add_subdirectory(tests) |
|
endif() |
|
endif() |
|
endif() |
|
|
|
# Better symmetry with find_package(pybind11 CONFIG) mode. |
|
if(NOT PYBIND11_MASTER_PROJECT) |
|
set(pybind11_FOUND |
|
TRUE |
|
CACHE INTERNAL "True if pybind11 and all required components found on the system") |
|
endif()
|
|
|