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)
diff --git a/CMakeLists.txt b/CMakeLists.txt
index ee2d0fd..bd483d3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -84,18 +84,25 @@
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 $<BUILD_INTERFACE:${PYBIND11_INCLUDE_DIR}>
+ $<BUILD_INTERFACE:${PYTHON_INCLUDE_DIRS}>
+ $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+ target_compile_options(pybind11 INTERFACE $<BUILD_INTERFACE:${PYBIND11_CPP_STANDARD}>)
+
add_library(module INTERFACE)
- target_include_directories(module INTERFACE $<BUILD_INTERFACE:${PYBIND11_INCLUDE_DIR}>
- $<BUILD_INTERFACE:${PYTHON_INCLUDE_DIRS}>
- $<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>)
+ add_library(pybind11::module ALIAS module)
+ target_link_libraries(module INTERFACE pybind11::pybind11)
if(WIN32 OR CYGWIN)
target_link_libraries(module INTERFACE $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
elseif(APPLE)
target_link_libraries(module INTERFACE "-undefined dynamic_lookup")
endif()
- target_compile_options(module INTERFACE $<BUILD_INTERFACE:${PYBIND11_CPP_STANDARD}>)
- 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 $<BUILD_INTERFACE:${PYTHON_LIBRARIES}>)
endif()
if (PYBIND11_INSTALL)
@@ -122,7 +129,7 @@
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 2c2437f..580b791 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -233,6 +233,9 @@
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 @@
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 0000000..44900c8
--- /dev/null
+++ b/tests/test_cmake_build/embed.cpp
@@ -0,0 +1,30 @@
+#include <pybind11/pybind11.h>
+#include <pybind11/eval.h>
+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<int>() != 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 0000000..f7fc09c
--- /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 $<TARGET_FILE:test_cmake_build> ${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 0000000..7b3babb
--- /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 $<TARGET_FILE:test_cmake_build> ${PROJECT_SOURCE_DIR}/../test.py)
diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in
index ae6d2da..6f0a406 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 @@
# 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()