From d9f32e1a5c4d75719956f13bb01950948a2d1e9b Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Wed, 14 Dec 2016 01:43:39 +0100 Subject: [PATCH 1/4] Make sure add_subdirectory and find_package behave identically Add a BUILD_INTERFACE and a pybind11::pybind11 alias for the interface library to match the installed target. Add new cmake tests for add_subdirectory and consolidates the .cpp and .py files needed for the cmake build tests: Before: tests |-- test_installed_module | |-- CMakeLists.txt | |-- main.cpp | \-- test.py \-- test_installed_target |-- CMakeLists.txt |-- main.cpp \-- test.py After: tests \-- test_cmake_build |-- installed_module/CMakeLists.txt |-- installed_target/CMakeLists.txt |-- subdirectory_module/CMakeLists.txt |-- subdirectory_target/CMakeLists.txt |-- main.cpp \-- test.py --- .appveyor.yml | 3 +- .travis.yml | 3 +- CMakeLists.txt | 12 ++- tests/CMakeLists.txt | 96 ++++++++++--------- .../installed_function/CMakeLists.txt | 12 +++ .../installed_target/CMakeLists.txt | 18 ++++ .../main.cpp | 4 +- .../subdirectory_function/CMakeLists.txt | 8 ++ .../subdirectory_target/CMakeLists.txt | 15 +++ tests/test_cmake_build/test.py | 5 + tests/test_installed_module/CMakeLists.txt | 14 --- tests/test_installed_module/main.cpp | 10 -- tests/test_installed_module/test.py | 3 - tests/test_installed_target/CMakeLists.txt | 20 ---- tests/test_installed_target/test.py | 3 - tools/pybind11Tools.cmake | 5 + 16 files changed, 127 insertions(+), 104 deletions(-) create mode 100644 tests/test_cmake_build/installed_function/CMakeLists.txt create mode 100644 tests/test_cmake_build/installed_target/CMakeLists.txt rename tests/{test_installed_target => test_cmake_build}/main.cpp (61%) create mode 100644 tests/test_cmake_build/subdirectory_function/CMakeLists.txt create mode 100644 tests/test_cmake_build/subdirectory_target/CMakeLists.txt create mode 100644 tests/test_cmake_build/test.py delete mode 100644 tests/test_installed_module/CMakeLists.txt delete mode 100644 tests/test_installed_module/main.cpp delete mode 100644 tests/test_installed_module/test.py delete mode 100644 tests/test_installed_target/CMakeLists.txt delete mode 100644 tests/test_installed_target/test.py diff --git a/.appveyor.yml b/.appveyor.yml index 740bd5c62f..15a7933d80 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -30,4 +30,5 @@ build_script: - cmake -A "%CMAKE_ARCH%" -DPYBIND11_WERROR=ON - set MSBuildLogger="C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" - cmake --build . --config Release --target pytest -- /v:m /logger:%MSBuildLogger% -- cmake --build . --config Release --target test_install -- /v:m /logger:%MSBuildLogger% +- cmake --build . --config Release --target test_cmake_build -- /v:m /logger:%MSBuildLogger% +on_failure: type tests\test_cmake_build\*.log diff --git a/.travis.yml b/.travis.yml index d4bdafd7e0..ccaa6b429c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -135,6 +135,7 @@ script: -DPYBIND11_CPP_STANDARD=$CPP -DPYBIND11_WERROR=ON - $SCRIPT_RUN_PREFIX make pytest -j 2 -- $SCRIPT_RUN_PREFIX make test_install +- $SCRIPT_RUN_PREFIX make test_cmake_build +after_failure: cat tests/test_cmake_build/*.log after_script: - if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 28012b8dab..4fd68edf02 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,15 +85,22 @@ foreach(ver ${pybind11_version_defines}) endif() endforeach() set(${PROJECT_NAME}_VERSION ${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}) +message(STATUS "pybind11 v${${PROJECT_NAME}_VERSION}") if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0 # Build an interface library target: add_library(pybind11 INTERFACE) target_include_directories(pybind11 INTERFACE $ + $ $) - if(APPLE) - target_link_libraries(pybind11 INTERFACE "-undefined dynamic_lookup") + if(WIN32 OR CYGWIN) + target_link_libraries(pybind11 INTERFACE $) + elseif(APPLE) + target_link_libraries(pybind11 INTERFACE "-undefined dynamic_lookup") endif() + target_compile_options(pybind11 INTERFACE $) + + add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target endif() if (PYBIND11_INSTALL) @@ -120,6 +127,5 @@ if (PYBIND11_INSTALL) install(EXPORT "${PROJECT_NAME}Targets" NAMESPACE "${PROJECT_NAME}::" DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) - message(STATUS "Exporting ${PROJECT_NAME}::pybind11 interface library target version ${${PROJECT_NAME}_VERSION}") endif() endif() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 27cb652911..1229acc93a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -105,55 +105,57 @@ if(PYBIND11_TEST_OVERRIDE) COMMAND ${CMAKE_COMMAND} -E echo "Note: not all tests run: -DPYBIND11_TEST_OVERRIDE is in effect") endif() -# test use of installation -if(PYBIND11_INSTALL) - # 2.8.12 needed for test_installed_module - # 3.0 needed for interface library for test_installed_target +# And another to show the .so size and, if a previous size, compare it: +add_custom_command(TARGET pybind11_tests POST_BUILD + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/libsize.py + $ ${CMAKE_CURRENT_BINARY_DIR}/sosize-$.txt) + +# Test CMake build using functions and targets from subdirectory or installed location +add_custom_target(test_cmake_build) +if(NOT CMAKE_VERSION VERSION_LESS 3.1) + # 3.0 needed for interface library for subdirectory_target/installed_target # 3.1 needed for cmake -E env for testing - if(NOT CMAKE_VERSION VERSION_LESS 3.1) - add_custom_target(test_installed_target - COMMAND ${CMAKE_COMMAND} - "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/test_install" - -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" - COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/test_installed_target" - "${CMAKE_CURRENT_BINARY_DIR}/test_installed_target" - --build-noclean - --build-generator ${CMAKE_GENERATOR} - $<$:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-target check - --build-options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/test_install" - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}" - "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}" + + include(CMakeParseArguments) + function(pybind11_add_build_test name) + cmake_parse_arguments(ARG "INSTALL" "" "" ${ARGN}) + + set(build_options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/mock_install" + "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}" + "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}") + if(NOT ARG_INSTALL) + list(APPEND build_options "-DPYBIND11_PROJECT_DIR=${PROJECT_SOURCE_DIR}") + endif() + + add_custom_target(test_${name} ${CMAKE_CTEST_COMMAND} + --quiet --output-log test_cmake_build/${name}.log + --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/test_cmake_build/${name}" + "${CMAKE_CURRENT_BINARY_DIR}/test_cmake_build/${name}" + --build-config Release + --build-noclean + --build-generator ${CMAKE_GENERATOR} + $<$:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM} + --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-target check + --build-options ${build_options} ) - add_custom_target(test_installed_module - COMMAND ${CMAKE_COMMAND} - "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/test_install" - -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" - COMMAND ${CMAKE_CTEST_COMMAND} - --build-and-test "${CMAKE_CURRENT_SOURCE_DIR}/test_installed_module" - "${CMAKE_CURRENT_BINARY_DIR}/test_installed_module" - --build-noclean - --build-generator ${CMAKE_GENERATOR} - $<$:--build-generator-platform> ${CMAKE_GENERATOR_PLATFORM} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-target check - --build-options "-DCMAKE_PREFIX_PATH=${PROJECT_BINARY_DIR}/test_install" - "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" - "-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}" - "-DPYBIND11_CPP_STANDARD=${PYBIND11_CPP_STANDARD}" + if(ARG_INSTALL) + add_dependencies(test_${name} mock_install) + endif() + add_dependencies(test_cmake_build test_${name}) + endfunction() + + pybind11_add_build_test(subdirectory_function) + pybind11_add_build_test(subdirectory_target) + + if(PYBIND11_INSTALL) + add_custom_target(mock_install ${CMAKE_COMMAND} + "-DCMAKE_INSTALL_PREFIX=${PROJECT_BINARY_DIR}/mock_install" + -P "${PROJECT_BINARY_DIR}/cmake_install.cmake" ) - else() - add_custom_target(test_installed_target) - add_custom_target(test_installed_module) + + pybind11_add_build_test(installed_function INSTALL) + pybind11_add_build_test(installed_target INSTALL) endif() - add_custom_target(test_install) - add_dependencies(test_install test_installed_target test_installed_module) endif() - -# And another to show the .so size and, if a previous size, compare it: -add_custom_command(TARGET pybind11_tests POST_BUILD - COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/libsize.py - $ ${CMAKE_CURRENT_BINARY_DIR}/sosize-$.txt) diff --git a/tests/test_cmake_build/installed_function/CMakeLists.txt b/tests/test_cmake_build/installed_function/CMakeLists.txt new file mode 100644 index 0000000000..ff0b32e9c5 --- /dev/null +++ b/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8.12) +project(test_installed_module CXX) + +set(CMAKE_MODULE_PATH "") + +find_package(pybind11 CONFIG REQUIRED) +message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + +pybind11_add_module(test_cmake_build SHARED ../main.cpp) + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/installed_target/CMakeLists.txt b/tests/test_cmake_build/installed_target/CMakeLists.txt new file mode 100644 index 0000000000..e09a2fa832 --- /dev/null +++ b/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -0,0 +1,18 @@ +cmake_minimum_required(VERSION 3.0) +project(test_installed_target CXX) + +set(CMAKE_MODULE_PATH "") + +find_package(pybind11 CONFIG REQUIRED) +message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") + +add_library(test_cmake_build MODULE ../main.cpp) + +target_link_libraries(test_cmake_build PRIVATE pybind11::pybind11) + +# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib +set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/tests/test_installed_target/main.cpp b/tests/test_cmake_build/main.cpp similarity index 61% rename from tests/test_installed_target/main.cpp rename to tests/test_cmake_build/main.cpp index 2a84c11ce3..e0f5b69c9f 100644 --- a/tests/test_installed_target/main.cpp +++ b/tests/test_cmake_build/main.cpp @@ -1,8 +1,8 @@ #include namespace py = pybind11; -PYBIND11_PLUGIN(test_installed_target) { - py::module m("test_installed_target"); +PYBIND11_PLUGIN(test_cmake_build) { + py::module m("test_cmake_build"); m.def("add", [](int i, int j) { return i + j; }); diff --git a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt new file mode 100644 index 0000000000..71b77359f4 --- /dev/null +++ b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 2.8.12) +project(test_subdirectory_module CXX) + +add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) +pybind11_add_module(test_cmake_build ../main.cpp) + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt new file mode 100644 index 0000000000..ee3f48944f --- /dev/null +++ b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 3.0) +project(test_subdirectory_target CXX) + +add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) + +add_library(test_cmake_build MODULE ../main.cpp) + +target_link_libraries(test_cmake_build PRIVATE pybind11::pybind11) + +# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib +set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") + +add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ + ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/test.py b/tests/test_cmake_build/test.py new file mode 100644 index 0000000000..1467a61dc0 --- /dev/null +++ b/tests/test_cmake_build/test.py @@ -0,0 +1,5 @@ +import sys +import test_cmake_build + +assert test_cmake_build.add(1, 2) == 3 +print("{} imports, runs, and adds: 1 + 2 = 3".format(sys.argv[1])) diff --git a/tests/test_installed_module/CMakeLists.txt b/tests/test_installed_module/CMakeLists.txt deleted file mode 100644 index 77fd49dc29..0000000000 --- a/tests/test_installed_module/CMakeLists.txt +++ /dev/null @@ -1,14 +0,0 @@ -cmake_minimum_required(VERSION 2.8.12) -project(test_installed_module CXX) - -set(CMAKE_MODULE_PATH "") - -find_package(pybind11 CONFIG REQUIRED) - -message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIRS} (found version ${pybind11_VERSION})") -message(STATUS "Found Python: ${PYTHON_INCLUDE_DIRS} (found version ${PYTHON_VERSION_STRING})") - -pybind11_add_module(test_installed_module SHARED main.cpp) - -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test.py) diff --git a/tests/test_installed_module/main.cpp b/tests/test_installed_module/main.cpp deleted file mode 100644 index a0bda45426..0000000000 --- a/tests/test_installed_module/main.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include -namespace py = pybind11; - -PYBIND11_PLUGIN(test_installed_module) { - py::module m("test_installed_module"); - - m.def("add", [](int i, int j) { return i + j; }); - - return m.ptr(); -} diff --git a/tests/test_installed_module/test.py b/tests/test_installed_module/test.py deleted file mode 100644 index 2f06320495..0000000000 --- a/tests/test_installed_module/test.py +++ /dev/null @@ -1,3 +0,0 @@ -import test_installed_module -assert test_installed_module.add(11, 22) == 33 -print('test_installed_module imports, runs, and adds: 11 + 22 = 33') diff --git a/tests/test_installed_target/CMakeLists.txt b/tests/test_installed_target/CMakeLists.txt deleted file mode 100644 index 4333dc1076..0000000000 --- a/tests/test_installed_target/CMakeLists.txt +++ /dev/null @@ -1,20 +0,0 @@ -cmake_minimum_required(VERSION 3.0) -project(test_installed_target CXX) - -set(CMAKE_MODULE_PATH "") - -find_package(pybind11 CONFIG REQUIRED) - -message(STATUS "Found pybind11: ${pybind11_INCLUDE_DIRS} (found version ${pybind11_VERSION})") -message(STATUS "Found Python: ${PYTHON_INCLUDE_DIRS} (found version ${PYTHON_VERSION_STRING})") - -add_library(test_installed_target MODULE main.cpp) - -target_link_libraries(test_installed_target PRIVATE pybind11::pybind11) - -# make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib -set_target_properties(test_installed_target PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" - SUFFIX "${PYTHON_MODULE_EXTENSION}") - -add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ - ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/test.py) diff --git a/tests/test_installed_target/test.py b/tests/test_installed_target/test.py deleted file mode 100644 index b2888a72b4..0000000000 --- a/tests/test_installed_target/test.py +++ /dev/null @@ -1,3 +0,0 @@ -import test_installed_target -assert test_installed_target.add(1, 2) == 3 -print('test_installed_target imports, runs, and adds: 1 + 2 = 3') diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index 4922982fad..d629a6fbaa 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -85,6 +85,11 @@ function(pybind11_add_module target_name) # import time. target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") + + if(${lib_type} STREQUAL "SHARED") + # Suppress CMake >= 3.0 warning for shared libraries + set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) + endif() endif() select_cxx_standard() From 48aa499a212cbed65fc81b1dd168677207d401cf Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Fri, 16 Dec 2016 22:58:37 +0100 Subject: [PATCH 2/4] Add new options and docs for pybind11_add_module See the documentation for a description of the options. --- docs/compiling.rst | 126 +++++++++++++----- .../installed_function/CMakeLists.txt | 2 +- .../subdirectory_function/CMakeLists.txt | 2 +- tools/pybind11Tools.cmake | 82 +++++++----- 4 files changed, 145 insertions(+), 67 deletions(-) diff --git a/docs/compiling.rst b/docs/compiling.rst index 35768fe455..ccb198b304 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -39,30 +39,88 @@ extension module can be created with just a few lines of code: This assumes that the pybind11 repository is located in a subdirectory named :file:`pybind11` and that the code is located in a file named :file:`example.cpp`. -The CMake command ``add_subdirectory`` will import a function with the signature -``pybind11_add_module( source1 [source2 ...])``. It will take care of all -the details needed to build a Python extension module on any platform. - -The target Python version can be selected by setting the ``PYBIND11_PYTHON_VERSION`` -variable before adding the pybind11 subdirectory. Alternatively, an exact Python -installation can be specified by setting ``PYTHON_EXECUTABLE``. +The CMake command ``add_subdirectory`` will import the pybind11 project which +provides the ``pybind11_add_module`` function. It will take care of all the +details needed to build a Python extension module on any platform. A working sample project, including a way to invoke CMake from :file:`setup.py` for PyPI integration, can be found in the [cmake_example]_ repository. .. [cmake_example] https://github.com/pybind/cmake_example -For CMake-based projects that don't include the pybind11 -repository internally, an external installation can be detected -through `find_package(pybind11 ... CONFIG ...)`. See the `Config file -`_ -docstring for details of relevant CMake variables. +pybind11_add_module +------------------- + +To ease the creation of Python extension modules, pybind11 provides a CMake +function with the following signature: + +.. code-block:: cmake + + pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] + [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) + +This function behaves very much like CMake's builtin ``add_library`` (in fact, +it's a wrapper function around that command). It will add a library target +called ```` to be built from the listed source files. In addition, it +will take care of all the Python-specific compiler and linker flags as well +as the OS- and Python-version-specific file extension. The produced target +```` can be further manipulated with regular CMake commands. + +``MODULE`` or ``SHARED`` may be given to specify the type of library. If no +type is given, ``MODULE`` is used by default which ensures the creation of a +Python-exclusive module. Specifying ``SHARED`` will create a more traditional +dynamic library which can also be linked from elsewhere. ``EXCLUDE_FROM_ALL`` +removes this target from the default build (see CMake docs for details). + +Since pybind11 is a template library, ``pybind11_add_module`` adds compiler +flags to ensure high quality code generation without bloat arising from long +symbol names and duplication of code in different translation units. The +additional flags enable LTO (Link Time Optimization), set default visibility +to *hidden* and strip unneeded symbols. See the :ref:`FAQ entry ` +for a more detailed explanation. These optimizations are never applied in +``Debug`` mode. If ``NO_EXTRAS`` is given, they will always be disabled, even +in ``Release`` mode. However, this will result in code bloat and is generally +not recommended. + +As stated above, LTO is enabled by default. Some newer compilers also support +different flavors of LTO such as `ThinLTO`_. Setting ``THIN_LTO`` will cause +the function to prefer this flavor if available. The function falls back to +regular LTO if ``-flto=thin`` is not available. + +.. _ThinLTO: http://clang.llvm.org/docs/ThinLTO.html + +Configuration variables +----------------------- + +By default, pybind11 will compile modules with the latest C++ standard +available on the target compiler. To override this, the standard flag can +be given explicitly in ``PYBIND11_CPP_STANDARD``: + +.. code-block:: cmake + + set(PYBIND11_CPP_STANDARD -std=c++11) + add_subdirectory(pybind11) # or find_package(pybind11) + +Note that this and all other configuration variables must be set **before** the +call to ``add_subdiretory`` or ``find_package``. The variables can also be set +when calling CMake from the command line using the ``-D=`` flag. + +The target Python version can be selected by setting ``PYBIND11_PYTHON_VERSION`` +or an exact Python installation can be specified with ``PYTHON_EXECUTABLE``. +For example: -Once detected, and after setting any variables to guide Python and -C++ standard detection, the aforementioned ``pybind11_add_module`` -wrapper to ``add_library`` can be employed as described above (after -``include(pybind11Tools)``). This procedure is available when using CMake ->= 2.8.12. A working example can be found at [test_installed_module]_ . +.. code-block:: bash + + cmake -DPYBIND11_PYTHON_VERSION=3.6 .. + # or + cmake -DPYTHON_EXECUTABLE=path/to/python .. + +find_package vs. add_subdirectory +--------------------------------- + +For CMake-based projects that don't include the pybind11 repository internally, +an external installation can be detected through ``find_package(pybind11)``. +See the `Config file`_ docstring for details of relevant CMake variables. .. code-block:: cmake @@ -72,27 +130,32 @@ wrapper to ``add_library`` can be employed as described above (after find_package(pybind11 REQUIRED) pybind11_add_module(example example.cpp) -.. [test_installed_module] https://github.com/pybind/pybind11/blob/master/tests/test_installed_module/CMakeLists.txt +Once detected, the aforementioned ``pybind11_add_module`` can be employed as +before. The function usage and configuration variables are identical no matter +if pybind11 is added as a subdirectory or found as an installed package. You +can refer to the same [cmake_example]_ repository for a full sample project +-- just swap out ``add_subdirectory`` for ``find_package``. + +.. _Config file: https://github.com/pybind/pybind11/blob/master/tools/pybind11Config.cmake.in + +Advanced: interface library target +---------------------------------- -When using a version of CMake greater than 3.0, pybind11 can -additionally be used as a special *interface library* following the -call to ``find_package``. CMake variables to guide Python and C++ -standard detection should be set *before* ``find_package``. When -``find_package`` returns, the target ``pybind11::pybind11`` is -available with pybind11 headers, Python headers and libraries as -needed, and C++ compile definitions attached. This target is suitable -for linking to an independently constructed (through ``add_library``, -not ``pybind11_add_module``) target in the consuming project. A working -example can be found at [test_installed_target]_ . +When using a version of CMake greater than 3.0, pybind11 can additionally +be used as a special *interface library* . The target ``pybind11::pybind11`` +is available with pybind11 headers, Python headers and libraries as needed, +and C++ compile definitions attached. This target is suitable for linking +to an independently constructed (through ``add_library``, not +``pybind11_add_module``) target in the consuming project. .. code-block:: cmake cmake_minimum_required(VERSION 3.0) project(example) - add_library(example MODULE main.cpp) + find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) - find_package(pybind11 REQUIRED) + add_library(example MODULE main.cpp) target_link_libraries(example PRIVATE pybind11::pybind11) set_target_properties(example PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}") @@ -111,6 +174,3 @@ example can be found at [test_installed_target]_ . (``-fvisibility=hidden``) and .OBJ files with many sections on Visual Studio (``/bigobj``). The :ref:`FAQ ` contains an explanation on why these are needed. - -.. [test_installed_target] https://github.com/pybind/pybind11/blob/master/tests/test_installed_target/CMakeLists.txt - diff --git a/tests/test_cmake_build/installed_function/CMakeLists.txt b/tests/test_cmake_build/installed_function/CMakeLists.txt index ff0b32e9c5..e0c20a8a36 100644 --- a/tests/test_cmake_build/installed_function/CMakeLists.txt +++ b/tests/test_cmake_build/installed_function/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_MODULE_PATH "") find_package(pybind11 CONFIG REQUIRED) message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") -pybind11_add_module(test_cmake_build SHARED ../main.cpp) +pybind11_add_module(test_cmake_build SHARED NO_EXTRAS ../main.cpp) add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt index 71b77359f4..278007aebd 100644 --- a/tests/test_cmake_build/subdirectory_function/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_function/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 2.8.12) project(test_subdirectory_module CXX) add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) -pybind11_add_module(test_cmake_build ../main.cpp) +pybind11_add_module(test_cmake_build THIN_LTO ../main.cpp) add_custom_target(check ${CMAKE_COMMAND} -E env PYTHONPATH=$ ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/../test.py ${PROJECT_NAME}) diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake index d629a6fbaa..fba76adee9 100644 --- a/tools/pybind11Tools.cmake +++ b/tools/pybind11Tools.cmake @@ -14,6 +14,7 @@ set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4) find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) include(CheckCXXCompilerFlag) +include(CMakeParseArguments) function(select_cxx_standard) if(NOT MSVC AND NOT PYBIND11_CPP_STANDARD) @@ -33,29 +34,48 @@ function(select_cxx_standard) endif() endfunction() +# Internal: find the appropriate LTO flag for this compiler +macro(_pybind11_find_lto_flag output_var prefer_thin_lto) + if(${prefer_thin_lto}) + # Check for ThinLTO support (Clang) + check_cxx_compiler_flag("-flto=thin" HAS_THIN_LTO_FLAG) + set(${output_var} $<${HAS_THIN_LTO_FLAG}:-flto=thin>) + endif() + + if(NOT ${prefer_thin_lto} OR NOT HAS_THIN_LTO_FLAG) + if(NOT CMAKE_CXX_COMPILER_ID MATCHES "Intel") + # Check for Link Time Optimization support (GCC/Clang) + check_cxx_compiler_flag("-flto" HAS_LTO_FLAG) + set(${output_var} $<${HAS_LTO_FLAG}:-flto>) + else() + # Intel equivalent to LTO is called IPO + check_cxx_compiler_flag("-ipo" HAS_IPO_FLAG) + set(${output_var} $<${HAS_IPO_FLAG}:-ipo>) + endif() + endif() +endmacro() + # Build a Python extension module: -# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] source1 [source2 ...]) +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] +# [NO_EXTRAS] [THIN_LTO] source1 [source2 ...]) # function(pybind11_add_module target_name) - set(lib_type "MODULE") - set(do_lto True) - set(exclude_from_all "") - set(sources "") - - set(_args_to_try "${ARGN}") - foreach(_ex_arg IN LISTS _args_to_try) - if(${_ex_arg} STREQUAL "MODULE") - set(lib_type "MODULE") - elseif(${_ex_arg} STREQUAL "SHARED") - set(lib_type "SHARED") - elseif(${_ex_arg} STREQUAL "EXCLUDE_FROM_ALL") - set(exclude_from_all "EXCLUDE_FROM_ALL") - else() - list(APPEND sources "${_ex_arg}") - endif() - endforeach() + set(options MODULE SHARED EXCLUDE_FROM_ALL NO_EXTRAS THIN_LTO) + cmake_parse_arguments(ARG "${options}" "" "" ${ARGN}) + + if(ARG_MODULE AND ARG_SHARED) + message(FATAL_ERROR "Can't be both MODULE and SHARED") + elseif(ARG_SHARED) + set(lib_type SHARED) + else() + set(lib_type MODULE) + endif() - add_library(${target_name} ${lib_type} ${exclude_from_all} ${sources}) + if(ARG_EXCLUDE_FROM_ALL) + set(exclude_from_all EXCLUDE_FROM_ALL) + endif() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${ARG_UNPARSED_ARGUMENTS}) target_include_directories(${target_name} PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt @@ -86,7 +106,7 @@ function(pybind11_add_module target_name) target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") - if(${lib_type} STREQUAL "SHARED") + if(ARG_SHARED) # Suppress CMake >= 3.0 warning for shared libraries set_target_properties(${target_name} PROPERTIES MACOSX_RPATH ON) endif() @@ -96,23 +116,21 @@ function(pybind11_add_module target_name) if(NOT MSVC) # Make sure C++11/14 are enabled target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + endif() + if(ARG_NO_EXTRAS) + return() + endif() + + if(NOT MSVC) # Enable link time optimization and set the default symbol # visibility to hidden (very important to obtain small binaries) string(TOUPPER "${CMAKE_BUILD_TYPE}" U_CMAKE_BUILD_TYPE) if (NOT ${U_CMAKE_BUILD_TYPE} MATCHES DEBUG) - # Check for Link Time Optimization support (GCC/Clang) - check_cxx_compiler_flag("-flto" HAS_LTO_FLAG) - if(HAS_LTO_FLAG AND NOT CYGWIN) - target_compile_options(${target_name} PRIVATE -flto) - endif() - - # Intel equivalent to LTO is called IPO - if(CMAKE_CXX_COMPILER_ID MATCHES "Intel") - check_cxx_compiler_flag("-ipo" HAS_IPO_FLAG) - if(HAS_IPO_FLAG) - target_compile_options(${target_name} PRIVATE -ipo) - endif() + # Link Time Optimization + if(NOT CYGWIN) + _pybind11_find_lto_flag(lto_flag ARG_THIN_LTO) + target_compile_options(${target_name} PRIVATE ${lto_flag}) endif() # Default symbol visibility From dff8d9146df780699a8762046943c425006aad2a Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Sat, 17 Dec 2016 21:38:57 +0100 Subject: [PATCH 3/4] Rename target from pybind11::pybind11 to pybind11::module Makes room for an eventual pybind11::embedded target. --- CMakeLists.txt | 18 +++++++++--------- docs/compiling.rst | 4 ++-- .../installed_target/CMakeLists.txt | 2 +- .../subdirectory_target/CMakeLists.txt | 2 +- tools/pybind11Config.cmake.in | 16 ++++++++-------- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fd68edf02..a2198baed4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -89,18 +89,18 @@ message(STATUS "pybind11 v${${PROJECT_NAME}_VERSION}") if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) # CMake >= 3.0 # Build an interface library target: - add_library(pybind11 INTERFACE) - target_include_directories(pybind11 INTERFACE $ - $ - $) + add_library(module INTERFACE) + target_include_directories(module INTERFACE $ + $ + $) if(WIN32 OR CYGWIN) - target_link_libraries(pybind11 INTERFACE $) + target_link_libraries(module INTERFACE $) elseif(APPLE) - target_link_libraries(pybind11 INTERFACE "-undefined dynamic_lookup") + target_link_libraries(module INTERFACE "-undefined dynamic_lookup") endif() - target_compile_options(pybind11 INTERFACE $) + target_compile_options(module INTERFACE $) - add_library(pybind11::pybind11 ALIAS pybind11) # to match exported target + add_library(pybind11::module ALIAS module) # to match exported target endif() if (PYBIND11_INSTALL) @@ -122,7 +122,7 @@ if (PYBIND11_INSTALL) DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) - install(TARGETS pybind11 + install(TARGETS module EXPORT "${PROJECT_NAME}Targets") install(EXPORT "${PROJECT_NAME}Targets" NAMESPACE "${PROJECT_NAME}::" diff --git a/docs/compiling.rst b/docs/compiling.rst index ccb198b304..143d4a3480 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -142,7 +142,7 @@ Advanced: interface library target ---------------------------------- When using a version of CMake greater than 3.0, pybind11 can additionally -be used as a special *interface library* . The target ``pybind11::pybind11`` +be used as a special *interface library* . The target ``pybind11::module`` is available with pybind11 headers, Python headers and libraries as needed, and C++ compile definitions attached. This target is suitable for linking to an independently constructed (through ``add_library``, not @@ -156,7 +156,7 @@ to an independently constructed (through ``add_library``, not find_package(pybind11 REQUIRED) # or add_subdirectory(pybind11) add_library(example MODULE main.cpp) - target_link_libraries(example PRIVATE pybind11::pybind11) + target_link_libraries(example PRIVATE pybind11::module) set_target_properties(example PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" SUFFIX "${PYTHON_MODULE_EXTENSION}") diff --git a/tests/test_cmake_build/installed_target/CMakeLists.txt b/tests/test_cmake_build/installed_target/CMakeLists.txt index e09a2fa832..dd206592ff 100644 --- a/tests/test_cmake_build/installed_target/CMakeLists.txt +++ b/tests/test_cmake_build/installed_target/CMakeLists.txt @@ -8,7 +8,7 @@ message(STATUS "Found pybind11 v${pybind11_VERSION}: ${pybind11_INCLUDE_DIRS}") add_library(test_cmake_build MODULE ../main.cpp) -target_link_libraries(test_cmake_build PRIVATE pybind11::pybind11) +target_link_libraries(test_cmake_build PRIVATE pybind11::module) # make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" diff --git a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt index ee3f48944f..6b142d62a9 100644 --- a/tests/test_cmake_build/subdirectory_target/CMakeLists.txt +++ b/tests/test_cmake_build/subdirectory_target/CMakeLists.txt @@ -5,7 +5,7 @@ add_subdirectory(${PYBIND11_PROJECT_DIR} pybind11) add_library(test_cmake_build MODULE ../main.cpp) -target_link_libraries(test_cmake_build PRIVATE pybind11::pybind11) +target_link_libraries(test_cmake_build PRIVATE pybind11::module) # make sure result is, for example, test_installed_target.so, not libtest_installed_target.dylib set_target_properties(test_cmake_build PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" diff --git a/tools/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in index 23588c300b..a4206c1663 100644 --- a/tools/pybind11Config.cmake.in +++ b/tools/pybind11Config.cmake.in @@ -26,12 +26,12 @@ # python detection and PYBIND11_CPP_STANDARD (-std=c++11 or -std=c++14) to # influence standard setting. :: # -# pybind11::pybind11 - the main pybind11 interface library (i.e., headers) +# 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})") # add_library(mylib MODULE main.cpp) -# target_link_libraries(mylib pybind11::pybind11) +# target_link_libraries(mylib pybind11::module) # # Suggested usage:: # @@ -75,17 +75,17 @@ 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}::pybind11 APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS}) + set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${PYTHON_INCLUDE_DIRS}) if(WIN32 OR CYGWIN) - set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) + set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) endif() select_cxx_standard() - set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") + set_property(TARGET ${PN}::module APPEND PROPERTY INTERFACE_COMPILE_OPTIONS "${PYBIND11_CPP_STANDARD}") - get_property(_iid TARGET ${PN}::pybind11 PROPERTY INTERFACE_INCLUDE_DIRECTORIES) - get_property(_ill TARGET ${PN}::pybind11 PROPERTY INTERFACE_LINK_LIBRARIES) - get_property(_ico TARGET ${PN}::pybind11 PROPERTY INTERFACE_COMPILE_OPTIONS) + get_property(_iid TARGET ${PN}::module 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() From 117006f027d55304302cb108d8b72dac55acdf0d Mon Sep 17 00:00:00 2001 From: Dean Moldovan Date: Sat, 17 Dec 2016 21:39:17 +0100 Subject: [PATCH 4/4] Add 'check' target to run all available tests --- tests/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 1229acc93a..584c617b42 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -159,3 +159,6 @@ if(NOT CMAKE_VERSION VERSION_LESS 3.1) pybind11_add_build_test(installed_target INSTALL) endif() endif() + +# Run all the tests +add_custom_target(check DEPENDS pytest test_cmake_build)