#[======================================================[.rst Adds the following targets:: pybind11::pybind11 - link to headers and pybind11 pybind11::module - Adds module links pybind11::embed - Adds embed links pybind11::lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set) pybind11::thin_lto - Link time optimizations (only if CMAKE_INTERPROCEDURAL_OPTIMIZATION is not set) pybind11::python_link_helper - Adds link to Python libraries pybind11::windows_extras - MSVC bigobj and mp for building multithreaded pybind11::opt_size - avoid optimizations that increase code size Adds the following functions:: pybind11_strip(target) - strip target after building on linux/macOS pybind11_find_import(module) - See if a module is installed. #]======================================================] # CMake 3.10 has an include_guard command, but we can't use that yet # include_guard(global) (pre-CMake 3.10) if(TARGET pybind11::pybind11) return() endif() # If we are in subdirectory mode, all IMPORTED targets must be GLOBAL. If we # are in CONFIG mode, they should be "normal" targets instead. # In CMake 3.11+ you can promote a target to global after you create it, # which might be simpler than this check. get_property( is_config TARGET pybind11::headers PROPERTY IMPORTED) if(NOT is_config) set(optional_global GLOBAL) endif() # If not run in Python mode, we still would like this to at least # include pybind11's include directory: set(pybind11_INCLUDE_DIRS "${pybind11_INCLUDE_DIR}" CACHE INTERNAL "Include directory for pybind11 (Python not requested)") if(CMAKE_CROSSCOMPILING AND PYBIND11_USE_CROSSCOMPILING) set(_PYBIND11_CROSSCOMPILING ON CACHE INTERNAL "") else() set(_PYBIND11_CROSSCOMPILING OFF CACHE INTERNAL "") endif() # --------------------- Shared targets ---------------------------- # Build an interface library target: add_library(pybind11::pybind11 IMPORTED INTERFACE ${optional_global}) set_property( TARGET pybind11::pybind11 APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::headers) # Build a module target: add_library(pybind11::module IMPORTED INTERFACE ${optional_global}) set_property( TARGET pybind11::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) # Build an embed library target: add_library(pybind11::embed IMPORTED INTERFACE ${optional_global}) set_property( TARGET pybind11::embed APPEND PROPERTY INTERFACE_LINK_LIBRARIES pybind11::pybind11) # --------------------------- link helper --------------------------- add_library(pybind11::python_link_helper IMPORTED INTERFACE ${optional_global}) if(CMAKE_VERSION VERSION_LESS 3.13) # In CMake 3.11+, you can set INTERFACE properties via the normal methods, and # this would be simpler. set_property( TARGET pybind11::python_link_helper APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$<$:-undefined dynamic_lookup>") else() # link_options was added in 3.13+ # This is safer, because you are ensured the deduplication pass in CMake will not consider # these separate and remove one but not the other. set_property( TARGET pybind11::python_link_helper APPEND PROPERTY INTERFACE_LINK_OPTIONS "$<$:LINKER:-undefined,dynamic_lookup>") endif() # ------------------------ Windows extras ------------------------- add_library(pybind11::windows_extras IMPORTED INTERFACE ${optional_global}) if(MSVC) # That's also clang-cl # /bigobj is needed for bigger binding projects due to the limit to 64k # addressable sections set_property( TARGET pybind11::windows_extras APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$:/bigobj>) # /MP enables multithreaded builds (relevant when there are many files) for MSVC if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # no Clang no Intel if(CMAKE_VERSION VERSION_LESS 3.11) set_property( TARGET pybind11::windows_extras APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$>:/MP>) else() # Only set these options for C++ files. This is important so that, for # instance, projects that include other types of source files like CUDA # .cu files don't get these options propagated to nvcc since that would # cause the build to fail. set_property( TARGET pybind11::windows_extras APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$>:$<$:/MP>>) endif() endif() endif() # ----------------------- Optimize binary size -------------------------- add_library(pybind11::opt_size IMPORTED INTERFACE ${optional_global}) if(MSVC) set(PYBIND11_OPT_SIZE /Os) else() set(PYBIND11_OPT_SIZE -Os) endif() set_property( TARGET pybind11::opt_size APPEND PROPERTY INTERFACE_COMPILE_OPTIONS $<$:${PYBIND11_OPT_SIZE}> $<$:${PYBIND11_OPT_SIZE}> $<$:${PYBIND11_OPT_SIZE}>) # ----------------------- Legacy option -------------------------- # Warn or error if old variable name used if(PYBIND11_CPP_STANDARD) string(REGEX MATCH [[..$]] VAL "${PYBIND11_CPP_STANDARD}") if(CMAKE_CXX_STANDARD) if(NOT CMAKE_CXX_STANDARD STREQUAL VAL) message(WARNING "CMAKE_CXX_STANDARD=${CMAKE_CXX_STANDARD} does not match " "PYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}, " "please remove PYBIND11_CPP_STANDARD from your cache") endif() else() set(supported_standards 11 14 17 20) if("${VAL}" IN_LIST supported_standards) message(WARNING "USE -DCMAKE_CXX_STANDARD=${VAL} instead of PYBIND11_CPP_STANDARD") set(CMAKE_CXX_STANDARD ${VAL} CACHE STRING "From PYBIND11_CPP_STANDARD") else() message(FATAL_ERROR "PYBIND11_CPP_STANDARD should be replaced with CMAKE_CXX_STANDARD " "(last two chars: ${VAL} not understood as a valid CXX std)") endif() endif() endif() # --------------------- Python specifics ------------------------- # CMake 3.27 removes the classic FindPythonInterp if CMP0148 is NEW if(CMAKE_VERSION VERSION_LESS "3.27") set(_pybind11_missing_old_python "OLD") else() cmake_policy(GET CMP0148 _pybind11_missing_old_python) endif() # Check to see which Python mode we are in, new, old, or no python if(PYBIND11_NOPYTHON) set(_pybind11_nopython ON) # We won't use new FindPython if PYBIND11_FINDPYTHON is defined and falselike # Otherwise, we use if FindPythonLibs is missing or if FindPython was already used elseif( (NOT DEFINED PYBIND11_FINDPYTHON OR PYBIND11_FINDPYTHON) AND (_pybind11_missing_old_python STREQUAL "NEW" OR PYBIND11_FINDPYTHON OR Python_FOUND OR Python3_FOUND )) # New mode include("${CMAKE_CURRENT_LIST_DIR}/pybind11NewTools.cmake") else() # Classic mode include("${CMAKE_CURRENT_LIST_DIR}/pybind11Tools.cmake") endif() # --------------------- pybind11_find_import ------------------------------- if(NOT _pybind11_nopython AND NOT _PYBIND11_CROSSCOMPILING) # Check to see if modules are importable. Use REQUIRED to force an error if # one of the modules is not found. _FOUND will be set if the # package was found (underscores replace dashes if present). QUIET will hide # the found message, and VERSION will require a minimum version. A successful # find will cache the result. function(pybind11_find_import PYPI_NAME) # CMake variables need underscores (PyPI doesn't care) string(REPLACE "-" "_" NORM_PYPI_NAME "${PYPI_NAME}") # Return if found previously if(${NORM_PYPI_NAME}_FOUND) return() endif() set(options "REQUIRED;QUIET") set(oneValueArgs "VERSION") cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "" ${ARGN}) if(ARG_REQUIRED) set(status_level FATAL_ERROR) else() set(status_level WARNING) endif() execute_process( COMMAND ${${_Python}_EXECUTABLE} -c " try: from importlib.metadata import version except ImportError: from pkg_resources import get_distribution def version(s): return get_distribution(s).version print(version('${PYPI_NAME}')) " RESULT_VARIABLE RESULT_PRESENT OUTPUT_VARIABLE PKG_VERSION ERROR_QUIET) string(STRIP "${PKG_VERSION}" PKG_VERSION) # If a result is present, this failed if(RESULT_PRESENT) set(${NORM_PYPI_NAME}_FOUND ${NORM_PYPI_NAME}-NOTFOUND CACHE INTERNAL "") # Always warn or error message( ${status_level} "Missing: ${PYPI_NAME} ${ARG_VERSION}\nTry: ${${_Python}_EXECUTABLE} -m pip install ${PYPI_NAME}" ) else() if(ARG_VERSION AND PKG_VERSION VERSION_LESS ARG_VERSION) message( ${status_level} "Version incorrect: ${PYPI_NAME} ${PKG_VERSION} found, ${ARG_VERSION} required - try upgrading" ) else() set(${NORM_PYPI_NAME}_FOUND YES CACHE INTERNAL "") set(${NORM_PYPI_NAME}_VERSION ${PKG_VERSION} CACHE INTERNAL "") endif() if(NOT ARG_QUIET) message(STATUS "Found ${PYPI_NAME} ${PKG_VERSION}") endif() endif() if(NOT ARG_VERSION OR (NOT PKG_VERSION VERSION_LESS ARG_VERSION)) # We have successfully found a good version, cache to avoid calling again. endif() endfunction() endif() # --------------------- LTO ------------------------------- include(CheckCXXCompilerFlag) # Checks whether the given CXX/linker flags can compile and link a cxx file. # cxxflags and linkerflags are lists of flags to use. The result variable is a # unique variable name for each set of flags: the compilation result will be # cached base on the result variable. If the flags work, sets them in # cxxflags_out/linkerflags_out internal cache variables (in addition to # ${result}). function(_pybind11_return_if_cxx_and_linker_flags_work result cxxflags linkerflags cxxflags_out linkerflags_out) set(CMAKE_REQUIRED_LIBRARIES ${linkerflags}) check_cxx_compiler_flag("${cxxflags}" ${result}) if(${result}) set(${cxxflags_out} "${cxxflags}" PARENT_SCOPE) set(${linkerflags_out} "${linkerflags}" PARENT_SCOPE) endif() endfunction() function(_pybind11_generate_lto target prefer_thin_lto) if(MINGW) message(STATUS "${target} disabled (problems with undefined symbols for MinGW for now)") return() endif() if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") set(cxx_append "") set(linker_append "") if(CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND NOT APPLE) # Clang Gold plugin does not support -Os; append -O3 to MinSizeRel builds to override it set(linker_append ";$<$:-O3>") elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU" AND NOT MINGW) set(cxx_append ";-fno-fat-lto-objects") endif() if(prefer_thin_lto) set(thin "=thin") else() set(thin "") endif() if(CMAKE_SYSTEM_PROCESSOR MATCHES "ppc64le" OR CMAKE_SYSTEM_PROCESSOR MATCHES "mips64") # Do nothing elseif(CMAKE_SYSTEM_PROCESSOR MATCHES emscripten) # This compile is very costly when cross-compiling, so set this without checking set(PYBIND11_LTO_CXX_FLAGS "-flto${thin}${cxx_append}") set(PYBIND11_LTO_LINKER_FLAGS "-flto${thin}${linker_append}") elseif(CMAKE_CXX_COMPILER_ID MATCHES "Clang") _pybind11_return_if_cxx_and_linker_flags_work( HAS_FLTO_THIN "-flto${thin}${cxx_append}" "-flto${thin}${linker_append}" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) endif() if(NOT HAS_FLTO_THIN) _pybind11_return_if_cxx_and_linker_flags_work( HAS_FLTO "-flto${cxx_append}" "-flto${linker_append}" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "IntelLLVM") # IntelLLVM equivalent to LTO is called IPO; also IntelLLVM is WIN32/UNIX # WARNING/HELP WANTED: This block of code is currently not covered by pybind11 GitHub Actions! if(WIN32) _pybind11_return_if_cxx_and_linker_flags_work( HAS_INTEL_IPO "-Qipo" "-Qipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) else() _pybind11_return_if_cxx_and_linker_flags_work( HAS_INTEL_IPO "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) endif() elseif(CMAKE_CXX_COMPILER_ID MATCHES "Intel") # Intel equivalent to LTO is called IPO _pybind11_return_if_cxx_and_linker_flags_work(HAS_INTEL_IPO "-ipo" "-ipo" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) elseif(MSVC) # cmake only interprets libraries as linker flags when they start with a - (otherwise it # converts /LTCG to \LTCG as if it was a Windows path). Luckily MSVC supports passing flags # with - instead of /, even if it is a bit non-standard: _pybind11_return_if_cxx_and_linker_flags_work(HAS_MSVC_GL_LTCG "/GL" "-LTCG" PYBIND11_LTO_CXX_FLAGS PYBIND11_LTO_LINKER_FLAGS) endif() # Enable LTO flags if found, except for Debug builds if(PYBIND11_LTO_CXX_FLAGS) # CONFIG takes multiple values in CMake 3.19+, until then we have to use OR set(is_debug "$,$>") set(not_debug "$") set(cxx_lang "$") if(MSVC AND CMAKE_VERSION VERSION_LESS 3.11) set(genex "${not_debug}") else() set(genex "$") endif() set_property( TARGET ${target} APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "$<${genex}:${PYBIND11_LTO_CXX_FLAGS}>") if(CMAKE_PROJECT_NAME STREQUAL "pybind11") message(STATUS "${target} enabled") endif() else() if(CMAKE_PROJECT_NAME STREQUAL "pybind11") message(STATUS "${target} disabled (not supported by the compiler and/or linker)") endif() endif() if(PYBIND11_LTO_LINKER_FLAGS) if(CMAKE_VERSION VERSION_LESS 3.11) set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") else() set_property( TARGET ${target} APPEND PROPERTY INTERFACE_LINK_OPTIONS "$<${not_debug}:${PYBIND11_LTO_LINKER_FLAGS}>") endif() endif() endfunction() if(NOT DEFINED CMAKE_INTERPROCEDURAL_OPTIMIZATION) add_library(pybind11::lto IMPORTED INTERFACE ${optional_global}) _pybind11_generate_lto(pybind11::lto FALSE) add_library(pybind11::thin_lto IMPORTED INTERFACE ${optional_global}) _pybind11_generate_lto(pybind11::thin_lto TRUE) endif() # ---------------------- pybind11_strip ----------------------------- function(pybind11_strip target_name) # Strip unnecessary sections of the binary on Linux/macOS if(CMAKE_STRIP) if(APPLE) set(x_opt -x) endif() add_custom_command( TARGET ${target_name} POST_BUILD COMMAND ${CMAKE_STRIP} ${x_opt} $) endif() endfunction()