diff --git a/.appveyor.yml b/.appveyor.yml index c657858272..740bd5c62f 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -30,3 +30,4 @@ 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% diff --git a/.gitignore b/.gitignore index 4df6229ad9..c444c17ed1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,3 +33,5 @@ MANIFEST /cmake/ .cache/ sosize-*.txt +pybind11Config*.cmake +pybind11Targets.cmake diff --git a/.travis.yml b/.travis.yml index 39b436248a..28d3e840bb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -110,5 +110,6 @@ script: -DPYBIND11_CPP_STANDARD=$CPP -DPYBIND11_WERROR=ON - $SCRIPT_RUN_PREFIX make pytest -j 2 +- $SCRIPT_RUN_PREFIX make test_install after_script: - if [ -n "$DOCKER" ]; then docker stop "$containerid"; docker rm "$containerid"; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index 6aa2beaeb9..28012b8dab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,30 +19,9 @@ option(PYBIND11_INSTALL "Install pybind11 header files?" ${PYBIND11_MASTER_PROJE option(PYBIND11_TEST "Build pybind11 test suite?" ${PYBIND11_MASTER_PROJECT}) option(PYBIND11_WERROR "Report all warnings as errors" OFF) -# Add a CMake parameter for choosing a desired Python version -set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") - list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/tools") -set(Python_ADDITIONAL_VERSIONS 3.4 3.5 3.6 3.7) -find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) - -include(CheckCXXCompilerFlag) - -if(NOT MSVC AND NOT PYBIND11_CPP_STANDARD) - check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) - check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) - - if (HAS_CPP14_FLAG) - set(PYBIND11_CPP_STANDARD -std=c++14) - elseif (HAS_CPP11_FLAG) - set(PYBIND11_CPP_STANDARD -std=c++11) - else() - message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") - endif() - set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING - "C++ standard flag, e.g. -std=c++11 or -std=c++14. Defaults to latest available." FORCE) -endif() +include(pybind11Tools) # Cache variables so pybind11_add_module can be used in parent projects set(PYBIND11_INCLUDE_DIR "${CMAKE_CURRENT_LIST_DIR}/include" CACHE INTERNAL "") @@ -51,91 +30,6 @@ set(PYTHON_LIBRARIES ${PYTHON_LIBRARIES} CACHE INTERNAL "") set(PYTHON_MODULE_PREFIX ${PYTHON_MODULE_PREFIX} CACHE INTERNAL "") set(PYTHON_MODULE_EXTENSION ${PYTHON_MODULE_EXTENSION} CACHE INTERNAL "") -# Build a Python extension module: -# pybind11_add_module( source1 [source2 ...]) -# -function(pybind11_add_module target_name) - add_library(${target_name} MODULE ${ARGN}) - target_include_directories(${target_name} - PRIVATE ${PYBIND11_INCLUDE_DIR} - PRIVATE ${PYTHON_INCLUDE_DIRS}) - - # The prefix and extension are provided by FindPythonLibsNew.cmake - set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") - set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") - - if(WIN32 OR CYGWIN) - # Link against the Python shared library on Windows - target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) - elseif(APPLE) - # It's quite common to have multiple copies of the same Python version - # installed on one's system. E.g.: one copy from the OS and another copy - # that's statically linked into an application like Blender or Maya. - # If we link our plugin library against the OS Python here and import it - # into Blender or Maya later on, this will cause segfaults when multiple - # conflicting Python instances are active at the same time (even when they - # are of the same version). - - # Windows is not affected by this issue since it handles DLL imports - # differently. The solution for Linux and Mac OS is simple: we just don't - # link against the Python library. The resulting shared library will have - # missing symbols, but that's perfectly fine -- they will be resolved at - # import time. - - target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") - endif() - - if(NOT MSVC) - # Make sure C++11/14 are enabled - target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) - - # 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() - endif() - - # Default symbol visibility - target_compile_options(${target_name} PRIVATE "-fvisibility=hidden") - - # Strip unnecessary sections of the binary on Linux/Mac OS - if(CMAKE_STRIP) - if(APPLE) - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_STRIP} -u -r $) - else() - add_custom_command(TARGET ${target_name} POST_BUILD - COMMAND ${CMAKE_STRIP} $) - endif() - endif() - endif() - elseif(MSVC) - # /MP enables multithreaded builds (relevant when there are many files), /bigobj is - # needed for bigger binding projects due to the limit to 64k addressable sections - target_compile_options(${target_name} PRIVATE /MP /bigobj) - - # Enforce link time code generation on MSVC, except in debug mode - target_compile_options(${target_name} PRIVATE $<$>:/GL>) - - # Fancy generator expressions don't work with linker flags, for reasons unknown - set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE /LTCG) - set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL /LTCG) - set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO /LTCG) - endif() -endfunction() - # Compile with compiler warnings turned on function(pybind11_enable_warnings target_name) if(MSVC) @@ -179,6 +73,53 @@ if (PYBIND11_TEST) add_subdirectory(tests) endif() +include(GNUInstallDirs) +include(CMakePackageConfigHelpers) + +# extract project version from source +file(STRINGS "${PYBIND11_INCLUDE_DIR}/pybind11/common.h" pybind11_version_defines + REGEX "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) ") +foreach(ver ${pybind11_version_defines}) + if (ver MATCHES "#define PYBIND11_VERSION_(MAJOR|MINOR|PATCH) +([^ ]+)$") + set(PYBIND11_VERSION_${CMAKE_MATCH_1} "${CMAKE_MATCH_2}" CACHE INTERNAL "") + endif() +endforeach() +set(${PROJECT_NAME}_VERSION ${PYBIND11_VERSION_MAJOR}.${PYBIND11_VERSION_MINOR}.${PYBIND11_VERSION_PATCH}) + +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") + endif() +endif() + if (PYBIND11_INSTALL) - install(FILES ${PYBIND11_HEADERS} DESTINATION include/pybind11) + install(FILES ${PYBIND11_HEADERS} + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/pybind11) + # GNUInstallDirs "DATADIR" wrong here; CMake search path wants "share". + set(PYBIND11_CMAKECONFIG_INSTALL_DIR "share/cmake/${PROJECT_NAME}" CACHE STRING "install path for pybind11Config.cmake") + + configure_package_config_file(tools/${PROJECT_NAME}Config.cmake.in + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + INSTALL_DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + write_basic_package_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + VERSION ${${PROJECT_NAME}_VERSION} + COMPATIBILITY AnyNewerVersion) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake + ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake + tools/FindPythonLibsNew.cmake + tools/pybind11Tools.cmake + DESTINATION ${PYBIND11_CMAKECONFIG_INSTALL_DIR}) + + if(NOT (CMAKE_VERSION VERSION_LESS 3.0)) + install(TARGETS pybind11 + EXPORT "${PROJECT_NAME}Targets") + 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/docs/compiling.rst b/docs/compiling.rst index 30cd83b791..35768fe455 100644 --- a/docs/compiling.rst +++ b/docs/compiling.rst @@ -51,3 +51,66 @@ A working sample project, including a way to invoke CMake from :file:`setup.py` 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. + +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:: cmake + + cmake_minimum_required(VERSION 2.8.12) + project(example) + + 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 + +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]_ . + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.0) + project(example) + + add_library(example MODULE main.cpp) + + find_package(pybind11 REQUIRED) + target_link_libraries(example PRIVATE pybind11::pybind11) + set_target_properties(example PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}" + SUFFIX "${PYTHON_MODULE_EXTENSION}") + +.. warning:: + + Since pybind11 is a metatemplate library, it is crucial that certain + compiler flags are provided to ensure high quality code generation. In + contrast to the ``pybind11_add_module()`` command, the CMake interface + library only provides the *minimal* set of parameters to ensure that the + code using pybind11 compiles, but it does **not** pass these extra compiler + flags (i.e. this is up to you). + + These include Link Time Optimization (``-flto`` on GCC/Clang/ICPC, ``/GL`` + and ``/LTCG`` on Visual Studio). Default-hidden symbols on GCC/Clang/ICPC + (``-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/docs/faq.rst b/docs/faq.rst index 7f9bd35b49..34002b42dc 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -162,6 +162,8 @@ culprit is generally the generation of function signatures at compile time using C++14 template metaprogramming. +.. _`faq:symhidden`: + How can I create smaller binaries? ================================== diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cb87fd8120..27cb652911 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -105,6 +105,54 @@ 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 + # 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}" + ) + 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}" + ) + else() + add_custom_target(test_installed_target) + add_custom_target(test_installed_module) + 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 diff --git a/tests/test_installed_module/CMakeLists.txt b/tests/test_installed_module/CMakeLists.txt new file mode 100644 index 0000000000..77fd49dc29 --- /dev/null +++ b/tests/test_installed_module/CMakeLists.txt @@ -0,0 +1,14 @@ +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 new file mode 100644 index 0000000000..a0bda45426 --- /dev/null +++ b/tests/test_installed_module/main.cpp @@ -0,0 +1,10 @@ +#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 new file mode 100644 index 0000000000..2f06320495 --- /dev/null +++ b/tests/test_installed_module/test.py @@ -0,0 +1,3 @@ +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 new file mode 100644 index 0000000000..4333dc1076 --- /dev/null +++ b/tests/test_installed_target/CMakeLists.txt @@ -0,0 +1,20 @@ +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/main.cpp b/tests/test_installed_target/main.cpp new file mode 100644 index 0000000000..2a84c11ce3 --- /dev/null +++ b/tests/test_installed_target/main.cpp @@ -0,0 +1,10 @@ +#include +namespace py = pybind11; + +PYBIND11_PLUGIN(test_installed_target) { + py::module m("test_installed_target"); + + m.def("add", [](int i, int j) { return i + j; }); + + return m.ptr(); +} diff --git a/tests/test_installed_target/test.py b/tests/test_installed_target/test.py new file mode 100644 index 0000000000..b2888a72b4 --- /dev/null +++ b/tests/test_installed_target/test.py @@ -0,0 +1,3 @@ +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/pybind11Config.cmake.in b/tools/pybind11Config.cmake.in new file mode 100644 index 0000000000..23588c300b --- /dev/null +++ b/tools/pybind11Config.cmake.in @@ -0,0 +1,92 @@ +# pybind11Config.cmake +# -------------------- +# +# PYBIND11 cmake module. +# This module sets the following variables in your project:: +# +# pybind11_FOUND - true if pybind11 and all required components found on the system +# pybind11_VERSION - pybind11 version in format Major.Minor.Release +# pybind11_INCLUDE_DIRS - Directories where pybind11 and python headers are located. +# pybind11_INCLUDE_DIR - Directory where pybind11 headers are located. +# pybind11_DEFINITIONS - Definitions necessary to use pybind11, namely USING_pybind11. +# pybind11_LIBRARIES - compile flags and python libraries (as needed) to link against. +# pybind11_LIBRARY - empty. +# CMAKE_MODULE_PATH - appends location of accompanying FindPythonLibsNew.cmake and +# pybind11Tools.cmake modules. +# +# +# Available components: None +# +# +# 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 +# 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::pybind11 - the main pybind11 interface library (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) +# +# Suggested usage:: +# +# find_package with version info is not recommended except for release versions. :: +# +# find_package(pybind11 CONFIG) +# find_package(pybind11 2.0 EXACT CONFIG REQUIRED) +# +# +# The following variables can be set to guide the search for this package:: +# +# pybind11_DIR - CMake variable, set to directory containing this Config file +# CMAKE_PREFIX_PATH - CMake variable, set to root directory of this package +# PATH - environment variable, set to bin directory of this package +# CMAKE_DISABLE_FIND_PACKAGE_pybind11 - CMake variable, disables +# find_package(pybind11) when not REQUIRED, perhaps to force internal build + +@PACKAGE_INIT@ + +set(PN pybind11) + +# location of pybind11/pybind11.h +set(${PN}_INCLUDE_DIR "${PACKAGE_PREFIX_DIR}/@CMAKE_INSTALL_INCLUDEDIR@") + +set(${PN}_LIBRARY "") +set(${PN}_DEFINITIONS USING_${PN}) + +check_required_components(${PN}) + +# make detectable the FindPythonLibsNew.cmake module +list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) + +include(pybind11Tools) + +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}::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}) + if(WIN32 OR CYGWIN) + set_property(TARGET ${PN}::pybind11 APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${PYTHON_LIBRARIES}) + endif() + + select_cxx_standard() + set_property(TARGET ${PN}::pybind11 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) + set(${PN}_INCLUDE_DIRS ${_iid}) + set(${PN}_LIBRARIES ${_ico} ${_ill}) +endif() +endif() diff --git a/tools/pybind11Tools.cmake b/tools/pybind11Tools.cmake new file mode 100644 index 0000000000..4922982fad --- /dev/null +++ b/tools/pybind11Tools.cmake @@ -0,0 +1,140 @@ +# tools/pybind11Tools.cmake -- Build system for the pybind11 modules +# +# Copyright (c) 2015 Wenzel Jakob +# +# All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +cmake_minimum_required(VERSION 2.8.12) + +# Add a CMake parameter for choosing a desired Python version +set(PYBIND11_PYTHON_VERSION "" CACHE STRING "Python version to use for compiling modules") + +set(Python_ADDITIONAL_VERSIONS 3.7 3.6 3.5 3.4) +find_package(PythonLibsNew ${PYBIND11_PYTHON_VERSION} REQUIRED) + +include(CheckCXXCompilerFlag) + +function(select_cxx_standard) + if(NOT MSVC AND NOT PYBIND11_CPP_STANDARD) + check_cxx_compiler_flag("-std=c++14" HAS_CPP14_FLAG) + check_cxx_compiler_flag("-std=c++11" HAS_CPP11_FLAG) + + if (HAS_CPP14_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++14) + elseif (HAS_CPP11_FLAG) + set(PYBIND11_CPP_STANDARD -std=c++11) + else() + message(FATAL_ERROR "Unsupported compiler -- pybind11 requires C++11 support!") + endif() + + set(PYBIND11_CPP_STANDARD ${PYBIND11_CPP_STANDARD} CACHE STRING + "C++ standard flag, e.g. -std=c++11 or -std=c++14. Defaults to latest available." FORCE) + endif() +endfunction() + +# Build a Python extension module: +# pybind11_add_module( [MODULE | SHARED] [EXCLUDE_FROM_ALL] 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() + + add_library(${target_name} ${lib_type} ${exclude_from_all} ${sources}) + + target_include_directories(${target_name} + PRIVATE ${PYBIND11_INCLUDE_DIR} # from project CMakeLists.txt + PRIVATE ${pybind11_INCLUDE_DIR} # from pybind11Config + PRIVATE ${PYTHON_INCLUDE_DIRS}) + + # The prefix and extension are provided by FindPythonLibsNew.cmake + set_target_properties(${target_name} PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}") + set_target_properties(${target_name} PROPERTIES SUFFIX "${PYTHON_MODULE_EXTENSION}") + + if(WIN32 OR CYGWIN) + # Link against the Python shared library on Windows + target_link_libraries(${target_name} PRIVATE ${PYTHON_LIBRARIES}) + elseif(APPLE) + # It's quite common to have multiple copies of the same Python version + # installed on one's system. E.g.: one copy from the OS and another copy + # that's statically linked into an application like Blender or Maya. + # If we link our plugin library against the OS Python here and import it + # into Blender or Maya later on, this will cause segfaults when multiple + # conflicting Python instances are active at the same time (even when they + # are of the same version). + + # Windows is not affected by this issue since it handles DLL imports + # differently. The solution for Linux and Mac OS is simple: we just don't + # link against the Python library. The resulting shared library will have + # missing symbols, but that's perfectly fine -- they will be resolved at + # import time. + + target_link_libraries(${target_name} PRIVATE "-undefined dynamic_lookup") + endif() + + select_cxx_standard() + if(NOT MSVC) + # Make sure C++11/14 are enabled + target_compile_options(${target_name} PUBLIC ${PYBIND11_CPP_STANDARD}) + + # 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() + endif() + + # Default symbol visibility + target_compile_options(${target_name} PRIVATE "-fvisibility=hidden") + + # Strip unnecessary sections of the binary on Linux/Mac OS + if(CMAKE_STRIP) + if(APPLE) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} -u -r $) + else() + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND ${CMAKE_STRIP} $) + endif() + endif() + endif() + elseif(MSVC) + # /MP enables multithreaded builds (relevant when there are many files), /bigobj is + # needed for bigger binding projects due to the limit to 64k addressable sections + target_compile_options(${target_name} PRIVATE /MP /bigobj) + + # Enforce link time code generation on MSVC, except in debug mode + target_compile_options(${target_name} PRIVATE $<$>:/GL>) + + # Fancy generator expressions don't work with linker flags, for reasons unknown + set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_RELEASE /LTCG) + set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_MINSIZEREL /LTCG) + set_property(TARGET ${target_name} APPEND_STRING PROPERTY LINK_FLAGS_RELWITHDEBINFO /LTCG) + endif() +endfunction()