1# CMakeLists.txt -- Build system for the pybind11 test suite 2# 3# Copyright (c) 2015 Wenzel Jakob <wenzel@inf.ethz.ch> 4# 5# All rights reserved. Use of this source code is governed by a 6# BSD-style license that can be found in the LICENSE file. 7 8cmake_minimum_required(VERSION 3.4) 9 10# The `cmake_minimum_required(VERSION 3.4...3.18)` syntax does not work with 11# some versions of VS that have a patched CMake 3.11. This forces us to emulate 12# the behavior using the following workaround: 13if(${CMAKE_VERSION} VERSION_LESS 3.18) 14 cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) 15else() 16 cmake_policy(VERSION 3.18) 17endif() 18 19# Only needed for CMake < 3.5 support 20include(CMakeParseArguments) 21 22# Filter out items; print an optional message if any items filtered 23# 24# Usage: 25# pybind11_filter_tests(LISTNAME file1.cpp file2.cpp ... MESSAGE "") 26# 27macro(pybind11_filter_tests LISTNAME) 28 cmake_parse_arguments(ARG "" "MESSAGE" "" ${ARGN}) 29 set(PYBIND11_FILTER_TESTS_FOUND OFF) 30 foreach(filename IN LISTS ARG_UNPARSED_ARGUMENTS) 31 list(FIND ${LISTNAME} ${filename} _FILE_FOUND) 32 if(_FILE_FOUND GREATER -1) 33 list(REMOVE_AT ${LISTNAME} ${_FILE_FOUND}) 34 set(PYBIND11_FILTER_TESTS_FOUND ON) 35 endif() 36 endforeach() 37 if(PYBIND11_FILTER_TESTS_FOUND AND ARG_MESSAGE) 38 message(STATUS "${ARG_MESSAGE}") 39 endif() 40endmacro() 41 42macro(possibly_uninitialized) 43 foreach(VARNAME ${ARGN}) 44 if(NOT DEFINED "${VARNAME}") 45 set("${VARNAME}" "") 46 endif() 47 endforeach() 48endmacro() 49 50# New Python support 51if(DEFINED Python_EXECUTABLE) 52 set(PYTHON_EXECUTABLE "${Python_EXECUTABLE}") 53 set(PYTHON_VERSION "${Python_VERSION}") 54endif() 55 56# There's no harm in including a project in a project 57project(pybind11_tests CXX) 58 59# Access FindCatch and more 60list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/../tools") 61 62option(PYBIND11_WERROR "Report all warnings as errors" OFF) 63option(DOWNLOAD_EIGEN "Download EIGEN (requires CMake 3.11+)" OFF) 64option(PYBIND11_CUDA_TESTS "Enable building CUDA tests (requires CMake 3.12+)" OFF) 65set(PYBIND11_TEST_OVERRIDE 66 "" 67 CACHE STRING "Tests from ;-separated list of *.cpp files will be built instead of all tests") 68set(PYBIND11_TEST_FILTER 69 "" 70 CACHE STRING "Tests from ;-separated list of *.cpp files will be removed from all tests") 71 72if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 73 # We're being loaded directly, i.e. not via add_subdirectory, so make this 74 # work as its own project and load the pybind11Config to get the tools we need 75 find_package(pybind11 REQUIRED CONFIG) 76endif() 77 78if(NOT CMAKE_BUILD_TYPE AND NOT DEFINED CMAKE_CONFIGURATION_TYPES) 79 message(STATUS "Setting tests build type to MinSizeRel as none was specified") 80 set(CMAKE_BUILD_TYPE 81 MinSizeRel 82 CACHE STRING "Choose the type of build." FORCE) 83 set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" 84 "RelWithDebInfo") 85endif() 86 87if(PYBIND11_CUDA_TESTS) 88 enable_language(CUDA) 89 if(DEFINED CMAKE_CXX_STANDARD) 90 set(CMAKE_CUDA_STANDARD ${CMAKE_CXX_STANDARD}) 91 endif() 92 set(CMAKE_CUDA_STANDARD_REQUIRED ON) 93endif() 94 95# Full set of test files (you can override these; see below) 96set(PYBIND11_TEST_FILES 97 test_async.cpp 98 test_buffers.cpp 99 test_builtin_casters.cpp 100 test_call_policies.cpp 101 test_callbacks.cpp 102 test_chrono.cpp 103 test_class.cpp 104 test_constants_and_functions.cpp 105 test_copy_move.cpp 106 test_custom_type_casters.cpp 107 test_docstring_options.cpp 108 test_eigen.cpp 109 test_enum.cpp 110 test_eval.cpp 111 test_exceptions.cpp 112 test_factory_constructors.cpp 113 test_gil_scoped.cpp 114 test_iostream.cpp 115 test_kwargs_and_defaults.cpp 116 test_local_bindings.cpp 117 test_methods_and_attributes.cpp 118 test_modules.cpp 119 test_multiple_inheritance.cpp 120 test_numpy_array.cpp 121 test_numpy_dtypes.cpp 122 test_numpy_vectorize.cpp 123 test_opaque_types.cpp 124 test_operator_overloading.cpp 125 test_pickling.cpp 126 test_pytypes.cpp 127 test_sequences_and_iterators.cpp 128 test_smart_ptr.cpp 129 test_stl.cpp 130 test_stl_binders.cpp 131 test_tagbased_polymorphic.cpp 132 test_union.cpp 133 test_virtual_functions.cpp) 134 135# Invoking cmake with something like: 136# cmake -DPYBIND11_TEST_OVERRIDE="test_callbacks.cpp;test_pickling.cpp" .. 137# lets you override the tests that get compiled and run. You can restore to all tests with: 138# cmake -DPYBIND11_TEST_OVERRIDE= .. 139if(PYBIND11_TEST_OVERRIDE) 140 set(PYBIND11_TEST_FILES ${PYBIND11_TEST_OVERRIDE}) 141endif() 142 143# You can also filter tests: 144if(PYBIND11_TEST_FILTER) 145 pybind11_filter_tests(PYBIND11_TEST_FILES ${PYBIND11_TEST_FILTER}) 146endif() 147 148if(PYTHON_VERSION VERSION_LESS 3.5) 149 pybind11_filter_tests(PYBIND11_TEST_FILES test_async.cpp MESSAGE 150 "Skipping test_async on Python 2") 151endif() 152 153# Skip tests for CUDA check: 154# /pybind11/tests/test_constants_and_functions.cpp(125): 155# error: incompatible exception specifications 156if(PYBIND11_CUDA_TESTS) 157 pybind11_filter_tests( 158 PYBIND11_TEST_FILES test_constants_and_functions.cpp MESSAGE 159 "Skipping test_constants_and_functions due to incompatible exception specifications") 160endif() 161 162string(REPLACE ".cpp" ".py" PYBIND11_PYTEST_FILES "${PYBIND11_TEST_FILES}") 163 164# Contains the set of test files that require pybind11_cross_module_tests to be 165# built; if none of these are built (i.e. because TEST_OVERRIDE is used and 166# doesn't include them) the second module doesn't get built. 167set(PYBIND11_CROSS_MODULE_TESTS test_exceptions.py test_local_bindings.py test_stl.py 168 test_stl_binders.py) 169 170set(PYBIND11_CROSS_MODULE_GIL_TESTS test_gil_scoped.py) 171 172# Check if Eigen is available; if not, remove from PYBIND11_TEST_FILES (but 173# keep it in PYBIND11_PYTEST_FILES, so that we get the "eigen is not installed" 174# skip message). 175list(FIND PYBIND11_TEST_FILES test_eigen.cpp PYBIND11_TEST_FILES_EIGEN_I) 176if(PYBIND11_TEST_FILES_EIGEN_I GREATER -1) 177 # Try loading via newer Eigen's Eigen3Config first (bypassing tools/FindEigen3.cmake). 178 # Eigen 3.3.1+ exports a cmake 3.0+ target for handling dependency requirements, but also 179 # produces a fatal error if loaded from a pre-3.0 cmake. 180 if(DOWNLOAD_EIGEN) 181 if(CMAKE_VERSION VERSION_LESS 3.11) 182 message(FATAL_ERROR "CMake 3.11+ required when using DOWNLOAD_EIGEN") 183 endif() 184 185 set(EIGEN3_VERSION_STRING "3.3.8") 186 187 include(FetchContent) 188 FetchContent_Declare( 189 eigen 190 GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git 191 GIT_TAG ${EIGEN3_VERSION_STRING}) 192 193 FetchContent_GetProperties(eigen) 194 if(NOT eigen_POPULATED) 195 message(STATUS "Downloading Eigen") 196 FetchContent_Populate(eigen) 197 endif() 198 199 set(EIGEN3_INCLUDE_DIR ${eigen_SOURCE_DIR}) 200 set(EIGEN3_FOUND TRUE) 201 202 else() 203 find_package(Eigen3 3.2.7 QUIET CONFIG) 204 205 if(NOT EIGEN3_FOUND) 206 # Couldn't load via target, so fall back to allowing module mode finding, which will pick up 207 # tools/FindEigen3.cmake 208 find_package(Eigen3 3.2.7 QUIET) 209 endif() 210 endif() 211 212 if(EIGEN3_FOUND) 213 if(NOT TARGET Eigen3::Eigen) 214 add_library(Eigen3::Eigen IMPORTED INTERFACE) 215 set_property(TARGET Eigen3::Eigen PROPERTY INTERFACE_INCLUDE_DIRECTORIES 216 "${EIGEN3_INCLUDE_DIR}") 217 endif() 218 219 # Eigen 3.3.1+ cmake sets EIGEN3_VERSION_STRING (and hard codes the version when installed 220 # rather than looking it up in the cmake script); older versions, and the 221 # tools/FindEigen3.cmake, set EIGEN3_VERSION instead. 222 if(NOT EIGEN3_VERSION AND EIGEN3_VERSION_STRING) 223 set(EIGEN3_VERSION ${EIGEN3_VERSION_STRING}) 224 endif() 225 message(STATUS "Building tests with Eigen v${EIGEN3_VERSION}") 226 else() 227 list(REMOVE_AT PYBIND11_TEST_FILES ${PYBIND11_TEST_FILES_EIGEN_I}) 228 message(STATUS "Building tests WITHOUT Eigen, use -DDOWNLOAD_EIGEN on CMake 3.11+ to download") 229 endif() 230endif() 231 232# Optional dependency for some tests (boost::variant is only supported with version >= 1.56) 233find_package(Boost 1.56) 234 235if(Boost_FOUND) 236 if(NOT TARGET Boost::headers) 237 add_library(Boost::headers IMPORTED INTERFACE) 238 if(TARGET Boost::boost) 239 # Classic FindBoost 240 set_property(TARGET Boost::boost PROPERTY INTERFACE_LINK_LIBRARIES Boost::boost) 241 else() 242 # Very old FindBoost, or newer Boost than CMake in older CMakes 243 set_property(TARGET Boost::headers PROPERTY INTERFACE_INCLUDE_DIRECTORIES 244 ${Boost_INCLUDE_DIRS}) 245 endif() 246 endif() 247endif() 248 249# Compile with compiler warnings turned on 250function(pybind11_enable_warnings target_name) 251 if(MSVC) 252 target_compile_options(${target_name} PRIVATE /W4) 253 elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)" AND NOT PYBIND11_CUDA_TESTS) 254 target_compile_options( 255 ${target_name} 256 PRIVATE -Wall 257 -Wextra 258 -Wconversion 259 -Wcast-qual 260 -Wdeprecated 261 -Wundef 262 -Wnon-virtual-dtor) 263 endif() 264 265 if(PYBIND11_WERROR) 266 if(MSVC) 267 target_compile_options(${target_name} PRIVATE /WX) 268 elseif(PYBIND11_CUDA_TESTS) 269 target_compile_options(${target_name} PRIVATE "SHELL:-Werror all-warnings") 270 elseif(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Intel|Clang)") 271 target_compile_options(${target_name} PRIVATE -Werror) 272 endif() 273 endif() 274 275 # Needs to be readded since the ordering requires these to be after the ones above 276 if(CMAKE_CXX_STANDARD 277 AND CMAKE_CXX_COMPILER_ID MATCHES "Clang" 278 AND PYTHON_VERSION VERSION_LESS 3.0) 279 if(CMAKE_CXX_STANDARD LESS 17) 280 target_compile_options(${target_name} PUBLIC -Wno-deprecated-register) 281 else() 282 target_compile_options(${target_name} PUBLIC -Wno-register) 283 endif() 284 endif() 285endfunction() 286 287set(test_targets pybind11_tests) 288 289# Build pybind11_cross_module_tests if any test_whatever.py are being built that require it 290foreach(t ${PYBIND11_CROSS_MODULE_TESTS}) 291 list(FIND PYBIND11_PYTEST_FILES ${t} i) 292 if(i GREATER -1) 293 list(APPEND test_targets pybind11_cross_module_tests) 294 break() 295 endif() 296endforeach() 297 298foreach(t ${PYBIND11_CROSS_MODULE_GIL_TESTS}) 299 list(FIND PYBIND11_PYTEST_FILES ${t} i) 300 if(i GREATER -1) 301 list(APPEND test_targets cross_module_gil_utils) 302 break() 303 endif() 304endforeach() 305 306# Support CUDA testing by forcing the target file to compile with NVCC 307if(PYBIND11_CUDA_TESTS) 308 set_property(SOURCE ${PYBIND11_TEST_FILES} PROPERTY LANGUAGE CUDA) 309endif() 310 311foreach(target ${test_targets}) 312 set(test_files ${PYBIND11_TEST_FILES}) 313 if(NOT "${target}" STREQUAL "pybind11_tests") 314 set(test_files "") 315 endif() 316 317 # Support CUDA testing by forcing the target file to compile with NVCC 318 if(PYBIND11_CUDA_TESTS) 319 set_property(SOURCE ${target}.cpp PROPERTY LANGUAGE CUDA) 320 endif() 321 322 # Create the binding library 323 pybind11_add_module(${target} THIN_LTO ${target}.cpp ${test_files} ${PYBIND11_HEADERS}) 324 pybind11_enable_warnings(${target}) 325 326 if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) 327 get_property( 328 suffix 329 TARGET ${target} 330 PROPERTY SUFFIX) 331 set(source_output "${CMAKE_CURRENT_SOURCE_DIR}/${target}${suffix}") 332 if(suffix AND EXISTS "${source_output}") 333 message(WARNING "Output file also in source directory; " 334 "please remove to avoid confusion: ${source_output}") 335 endif() 336 endif() 337 338 if(MSVC) 339 target_compile_options(${target} PRIVATE /utf-8) 340 endif() 341 342 if(EIGEN3_FOUND) 343 target_link_libraries(${target} PRIVATE Eigen3::Eigen) 344 target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_EIGEN) 345 endif() 346 347 if(Boost_FOUND) 348 target_link_libraries(${target} PRIVATE Boost::headers) 349 target_compile_definitions(${target} PRIVATE -DPYBIND11_TEST_BOOST) 350 endif() 351 352 # Always write the output file directly into the 'tests' directory (even on MSVC) 353 if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY) 354 set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY 355 "${CMAKE_CURRENT_BINARY_DIR}") 356 357 if(DEFINED CMAKE_CONFIGURATION_TYPES) 358 foreach(config ${CMAKE_CONFIGURATION_TYPES}) 359 string(TOUPPER ${config} config) 360 set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY_${config} 361 "${CMAKE_CURRENT_BINARY_DIR}") 362 endforeach() 363 endif() 364 endif() 365endforeach() 366 367# Make sure pytest is found or produce a warning 368pybind11_find_import(pytest VERSION 3.1) 369 370if(NOT CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_CURRENT_BINARY_DIR) 371 # This is not used later in the build, so it's okay to regenerate each time. 372 configure_file("${CMAKE_CURRENT_SOURCE_DIR}/pytest.ini" "${CMAKE_CURRENT_BINARY_DIR}/pytest.ini" 373 COPYONLY) 374 file(APPEND "${CMAKE_CURRENT_BINARY_DIR}/pytest.ini" 375 "\ntestpaths = \"${CMAKE_CURRENT_SOURCE_DIR}\"") 376 377endif() 378 379# cmake 3.12 added list(transform <list> prepend 380# but we can't use it yet 381string(REPLACE "test_" "${CMAKE_CURRENT_SOURCE_DIR}/test_" PYBIND11_ABS_PYTEST_FILES 382 "${PYBIND11_PYTEST_FILES}") 383 384set(PYBIND11_TEST_PREFIX_COMMAND 385 "" 386 CACHE STRING "Put this before pytest, use for checkers and such") 387 388# A single command to compile and run the tests 389add_custom_target( 390 pytest 391 COMMAND ${PYBIND11_TEST_PREFIX_COMMAND} ${PYTHON_EXECUTABLE} -m pytest 392 ${PYBIND11_ABS_PYTEST_FILES} 393 DEPENDS ${test_targets} 394 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 395 USES_TERMINAL) 396 397if(PYBIND11_TEST_OVERRIDE) 398 add_custom_command( 399 TARGET pytest 400 POST_BUILD 401 COMMAND ${CMAKE_COMMAND} -E echo 402 "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") 403endif() 404 405# cmake-format: off 406add_custom_target( 407 memcheck 408 COMMAND 409 PYTHONMALLOC=malloc 410 valgrind 411 --leak-check=full 412 --show-leak-kinds=definite,indirect 413 --errors-for-leak-kinds=definite,indirect 414 --error-exitcode=1 415 --read-var-info=yes 416 --track-origins=yes 417 --suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-python.supp" 418 --suppressions="${CMAKE_CURRENT_SOURCE_DIR}/valgrind-numpy-scipy.supp" 419 --gen-suppressions=all 420 ${PYTHON_EXECUTABLE} -m pytest ${PYBIND11_ABS_PYTEST_FILES} 421 DEPENDS ${test_targets} 422 WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" 423 USES_TERMINAL) 424# cmake-format: on 425 426# Add a check target to run all the tests, starting with pytest (we add dependencies to this below) 427add_custom_target(check DEPENDS pytest) 428 429# The remaining tests only apply when being built as part of the pybind11 project, but not if the 430# tests are being built independently. 431if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR) 432 return() 433endif() 434 435# Add a post-build comment to show the primary test suite .so size and, if a previous size, compare it: 436add_custom_command( 437 TARGET pybind11_tests 438 POST_BUILD 439 COMMAND 440 ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/../tools/libsize.py 441 $<TARGET_FILE:pybind11_tests> 442 ${CMAKE_CURRENT_BINARY_DIR}/sosize-$<TARGET_FILE_NAME:pybind11_tests>.txt) 443 444if(NOT PYBIND11_CUDA_TESTS) 445 # Test embedding the interpreter. Provides the `cpptest` target. 446 add_subdirectory(test_embed) 447 448 # Test CMake build using functions and targets from subdirectory or installed location 449 add_subdirectory(test_cmake_build) 450endif() 451