From 7f5c85c861e56c0bb9563eaaba2615c70773b9a5 Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Thu, 23 Mar 2017 13:32:54 +0100 Subject: [PATCH] Add CMake target for embedding the Python interpreter All targets provided by pybind11: * pybind11::module - the existing target for creating extension modules * pybind11::embed - new target for embedding the interpreter * pybind11::pybind11 - common "base" target (headers only) --- CMakeLists.txt | 19 ++++++++---- tests/CMakeLists.txt | 6 ++++ tests/test_cmake_build/embed.cpp | 30 +++++++++++++++++++ .../installed_embed/CMakeLists.txt | 15 ++++++++++ .../subdirectory_embed/CMakeLists.txt | 9 ++++++ tools/pybind11Config.cmake.in | 27 +++++++++++------ 6 files changed, 91 insertions(+), 15 deletions(-) create mode 100644 tests/test_cmake_build/embed.cpp create mode 100644 tests/test_cmake_build/installed_embed/CMakeLists.txt create mode 100644 tests/test_cmake_build/subdirectory_embed/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index ee2d0fdf2..bd483d3e6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,18 +84,25 @@ endif() if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0 # Build an interface library target: + add_library(pybind11 INTERFACE) + add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target + target_include_directories(pybind11 INTERFACE $ + $ + $) + target_compile_options(pybind11 INTERFACE $) + add_library(module INTERFACE) - target_include_directories(module INTERFACE $ - $ - $) + add_library(pybind11::module ALIAS module) + target_link_libraries(module INTERFACE pybind11::pybind11) if(WIN32 OR CYGWIN) target_link_libraries(module INTERFACE $) elseif(APPLE) target_link_libraries(module INTERFACE "-undefined dynamic_lookup") endif() - target_compile_options(module INTERFACE $) - add_library(pybind11::module ALIAS module) # to match exported target + add_library(embed INTERFACE) + add_library(pybind11::embed ALIAS embed) + target_link_libraries(embed INTERFACE pybind11::pybind11 $) endif() if (PYBIND11_INSTALL) @@ -122,7 +129,7 @@ if (PYBIND11_INSTALL) DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) - install(TARGETS module + install(TARGETS pybind11 module embed EXPORT "${PROJECT_NAME}Targets") install(EXPORT "${PROJECT_NAME}Targets" NAMESPACE "${PROJECT_NAME}::" diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2c2437f23..580b79153 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -233,6 +233,9 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.1) pybind11_add_build_test(subdirectory_function) pybind11_add_build_test(subdirectory_target) + if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") + pybind11_add_build_test(subdirectory_embed) + endif() if(PYBIND11_INSTALL) add_custom_target(mock_install ${CMAKE_COMMAND} @@ -242,6 +245,9 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.1) pybind11_add_build_test(installed_function INSTALL) pybind11_add_build_test(installed_target INSTALL) + if(NOT ${PYTHON_MODULE_EXTENSION} MATCHES "pypy") + pybind11_add_build_test(installed_embed INSTALL) + endif() endif() endif() diff --git a/tests/test_cmake_build/embed.cpp b/tests/test_cmake_build/embed.cpp new file mode 100644 index 000000000..44900c8b5 --- /dev/null +++ b/tests/test_cmake_build/embed.cpp @@ -0,0 +1,30 @@ +#include +#include +namespace py = pybind11; + +PyObject *make_module() { + py::module m("test_cmake_build"); + + m.def("add", [](int i, int j) { return i + j; }); + + return m.ptr(); +} + +int main(int argc, char *argv[]) { + if (argc != 2) + throw std::runtime_error("Expected test.py file as the first argument"); + auto test_py_file = argv[1]; + + PyImport_AppendInittab("test_cmake_build", &make_module); + Py_Initialize(); + { + auto m = py::module::import("test_cmake_build"); + if (m.attr("add")(1, 2).cast() != 3) + throw std::runtime_error("embed.cpp failed"); + + auto globals = py::module::import("__main__").attr("__dict__"); + py::module::import("sys").attr("argv") = py::make_tuple("test.py", "embed.cpp"); + py::eval_file(test_py_file, globals); + } + Py_Finalize(); +} diff --git a/tests/test_cmake_build/installed_embed/CMakeLists.txt b/tests/test_cmake_build/installed_embed/CMakeLists.txt new file mode 100644 index 000000000..f7fc09c21 --- /dev/null +++ b/tests/test_cmake_build/installed_embed/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.0) +project(test_installed_embed CXX) + +set(CMAKE_MODULE_PATH "") +find_package(pybind11 CONFIG REQUIRED) +message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + +add_executable(test_cmake_build ../embed.cpp) +target_link_libraries(test_cmake_build PRIVATE pybind11::embed) + +# Do not treat includes from IMPORTED target as SYSTEM (Python headers in pybind11::embed). +# This may be needed to resolve header conflicts, e.g. between Python release and debug headers. +set_target_properties(test_cmake_build PROPERTIES NO_SYSTEM_FROM_IMPORTED ON) + +add_custom_target(check $ ${PROJECT_SOURCE_DIR}/../test.py) diff --git a/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt b/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt new file mode 100644 index 000000000..7b3babbcb --- /dev/null +++ b/tests/test_cmake_build/subdirectory_embed/CMakeLists.txt @@ -0,0 +1,9 @@ +cmake_minimum_required(VERSION 3.0) +project(test_subdirectory_embed CXX) + +add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) + +add_executable(test_cmake_build ../embed.cpp) +target_link_libraries(test_cmake_build PRIVATE pybind11::embed) + +add_custom_target(check $ ${PROJECT_SOURCE_DIR}/../test.py) diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index ae6d2daee..6f0a406fb 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -21,18 +21,27 @@ # Exported targets:: # # If pybind11 is found, this module defines the following :prop_tgt:`IMPORTED` -# target. Python headers, libraries (as needed by platform), and C++ standard +# interface library targets:: +# +# pybind11::module - for extension modules +# pybind11::embed - for embedding the Python interpreter +# +# Python headers, libraries (as needed by platform), and the C++ standard # are attached to the target. Set PythonLibsNew variables to influence # python detection and PYBIND11_CPP_STANDARD (-std=c++11 or -std=c++14) to # influence standard setting. :: # -# pybind11::module - the main pybind11 interface library for extension modules (i.e., headers) -# # find_package(pybind11 CONFIG REQUIRED) -# message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIR} (found version ${pybind11_VERSION} & Py${PYTHON_VERSION_STRING})") +# message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") +# +# # Create an extension module # add_library(mylib MODULE main.cpp) # target_link_libraries(mylib pybind11::module) # +# # Or embed the Python interpreter into an executable +# add_executable(myexe main.cpp) +# target_link_libraries(myexe pybind11::embed) +# # Suggested usage:: # # find_package with version info is not recommended except for release versions. :: @@ -71,21 +80,21 @@ if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # Don't include targets if this file is being picked up by another # project which has already built this as a subproject #----------------------------------------------------------------------------- -if(NOT TARGET ${PN}::module) +if(NOT TARGET ${PN}::pybind11) include("${CMAKE_CURRENT_LIST_DIR}/${PN}Targets.cmake") find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} MODULE REQUIRED) - set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS}) + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS}) + set_property(TARGET ${PN}::embed APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) if(WIN32 OR CYGWIN) set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) endif() select_cxx_standard() - set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") - get_property(_iid TARGET ${PN}::module PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES) get_property(_ill TARGET ${PN}::module PROPERTY INTERFACE_LINK_LIBRARIES) - get_property(_ico TARGET ${PN}::module PROPERTY INTERFACE_COMPILE_OPTIONS) set(${PN}_INCLUDE_DIRS ${_iid}) set(${PN}_LIBRARIES ${_ico} ${_ill}) endif()