mindspore2022/cmake/modules/Findpybind11.cmake

289 lines
11 KiB
CMake

# ==============================================================================
#
# Copyright 2022 <Huawei Technologies Co., Ltd>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# ==============================================================================
#
# Looking for pybind11 can be somewhat complicated now that there are two
# possible packages: - pybind11 - pybind11-global
#
# Here we try our best to look for pybind11 in all possible locations and in a
# meaningful way. In practice, this means that we look for a valid package in
# the following order: 1. First we look in a virtualenv (if one is active) for
# a) pybind11-global b) pybind11 >= 2.6.0 c) pybind11 < 2.6.0 && pybind11-cmake
# 2. pybind11-gobal in user site (if not in a virtualenv) 3. pybind11-gobal in
# global site package (also done if within a virtualenv) 4. pybind11 >= 2.6.0 in
# user and global sites NB: if the version of pybind11 is more recent than
# pybind11-global, we choose pybind11 5. pybind11 < 2.6.0 && pybind11-cmake in
# user and global sites
#
# ==============================================================================
# ~~~
# Make sure that pybind11 finds the correct python version
# - pybind11 < 2.6.0 uses find_package(PythonInterp ...)
# - pybind11 >= 2.6.0 looks for the `python` command or PYTHON_EXECUTABLE
# => specify PYTHON_EXECUTABLE manually to guarantee we find the same
# interpreter and libraries as for other python packages
# ~~~
set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
set(PYBIND11_PYTHON_VERSION ${Python_VERSION}) # maybe not strictly required
macro(_pybind11_message)
if(NOT pybind11_FIND_QUIETLY)
message(${ARGN})
endif()
endmacro()
_pybind11_message(CHECK_START "Looking for pybind11")
list(APPEND CMAKE_MESSAGE_INDENT " ")
# ==============================================================================
# First detect whether we are in a virtualenv or not
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import sys; print(int(sys.prefix != sys.base_prefix))"
OUTPUT_VARIABLE _is_virtualenv
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
if(_is_virtualenv)
# ~~~
# Try to find in order within the virtualenv:
# - pybind11-global
# - pybind11 >= 2.6.0
# - pybind11 < 2.6.0 with pybind11-cmake
# - if all of that fails, revert to user and global sites (as far as is possible)
# ~~~
_pybind11_message(CHECK_START "Looking for pybind11-global in virtualenv")
# Look for pybind11-global
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import sys, os; print(os.path.join(sys.prefix, 'share', 'cmake', 'pybind11'))"
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
if(NOT EXISTS pybind11_DIR)
_pybind11_message(CHECK_FAIL "Not-found")
# Could not find pybind11-global, try pybind11 >= 2.6.0
_pybind11_message(CHECK_START "Looking for pybind11 >= 2.6.0 in virtualenv")
__find_python_module(pybind11 VERSION 2.6.0)
if(PYMOD_PYBIND11_FOUND)
_pybind11_message(CHECK_PASS "Found")
execute_process(
COMMAND ${Python_EXECUTABLE} -m pybind11 --cmakedir
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
else()
_pybind11_message(CHECK_FAIL "Not-found")
# Now try pybind11 < 2.6.0 && pybind11-cmake
__find_python_module(pybind11-cmake)
if(PYMOD_PYBIND11_CMAKE_FOUND)
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import pybind11_cmake; print(pybind11_cmake.__path__[0])"
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
endif()
else()
_pybind11_message(CHECK_PASS "Found")
endif()
# Try to find some valid pybind11 config within the virtualenv
find_package(pybind11 2.6.0 CONFIG QUIET NO_DEFAULT_PATH)
endif()
# ------------------------------------------------------------------------------
if(NOT pybind11_FOUND)
_pybind11_message(CHECK_START "Looking for pybind11-global in global and user sites")
if(NOT _is_virtualenv)
# Try pybind11-global in user site
execute_process(
COMMAND ${Python_EXECUTABLE} -m site --user-base
RESULT_VARIABLE _status
OUTPUT_VARIABLE _user_base
OUTPUT_STRIP_TRAILING_WHITESPACE)
# NB: _status == 0 means user site enabled. Anything else and we really should not consider it
if(_status EQUAL 0 AND EXISTS "${_user_base}/share/cmake/pybind11/")
# cmake-lint: disable=C0103
set(pybind11_DIR "${_user_base}/share/cmake/pybind11/")
endif()
endif()
# Try to find pybind11-global either in user-site or global-site (even in the case of a virtualenv since we did not
# find anything useful in it previously)
find_package(pybind11 2.6.0 CONFIG QUIET)
if(pybind11_FOUND)
_pybind11_message(CHECK_PASS "Found")
else()
_pybind11_message(CHECK_PASS "Not-found")
endif()
if(NOT _is_virtualenv)
_pybind11_message(CHECK_START "Looking for pybind11 Python module")
# Try to find pybind11 either in user-site or global-site
if(pybind11_FIND_QUIETLY)
set(_args QUIET)
endif()
__find_python_module(pybind11 VERSION 2.6.0 ${_args})
unset(_args)
if(PYMOD_PYBIND11_FOUND)
_pybind11_message(CHECK_PASS "Found")
else()
_pybind11_message(CHECK_PASS "Not-found")
endif()
if(PYMOD_PYBIND11_FOUND AND PYMOD_PYBIND11_VERSION VERSION_GREATER pybind11_VERSION)
# We prefer pybind11 over pybind11 global if its version is more recent. This could typically be the case if a
# user installs a more recent version of pybind11 in its user site.
execute_process(
COMMAND ${Python_EXECUTABLE} -m pybind11 --cmakedir
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
endif()
_pybind11_message(CHECK_START "Looking for pybind11 (final)")
# Look for pybind11 again in case we have pybind11 more recent than pybind11-global
find_package(pybind11 2.6.0 CONFIG QUIET NO_DEFAULT_PATH)
# If we are not in a virtualenv, we should still try to look for pybind11 < 2.6.0 && pybind11-cmake
if(NOT pybind11_FOUND)
_pybind11_message(CHECK_PASS "Not-found")
_pybind11_message(CHECK_START "Looking for pybind11-cmake")
# If everything else fails, rely on the pybind11_cmake package
__find_python_module(pybind11-cmake)
if(PYMOD_PYBIND11_CMAKE_FOUND)
_pybind11_message(CHECK_PASS "Done")
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import pybind11_cmake; print(pybind11_cmake.__path__[0])"
OUTPUT_VARIABLE pybind11_DIR
OUTPUT_STRIP_TRAILING_WHITESPACE)
else()
_pybind11_message(CHECK_FAIL "Failed")
endif()
else()
_pybind11_message(CHECK_PASS "Found")
endif()
endif()
endif()
# ------------------------------------------------------------------------------
# Now look for pybind11 using the CONFIG method one last time. This would typically only be useful in the case of
# pybind11 < 2.6.0 located in either user or global sites.
list(POP_BACK CMAKE_MESSAGE_INDENT)
if(pybind11_DIR)
_pybind11_message(CHECK_PASS "Done")
else()
_pybind11_message(CHECK_FAIL "Failed")
endif()
find_package(pybind11 2.6.0 CONFIG NO_DEFAULT_PATH)
# ==============================================================================
# With pybind11-cmake 1.0.0 we might need to fix the include path
if(PYMOD_PYBIND11_CMAKE_FOUND)
include(CheckCXXSourceCompiles)
check_cxx_source_compiles("#include <pybind11/pybind11.h>
int main() {return 0;}" pybind11_compiles)
if(NOT pybind11_compiles)
# This could happen with pybind11_cmake == 1.0.0 because there is a typo in the pybind11Config.cmake
execute_process(
COMMAND ${Python_EXECUTABLE} -c "import pybind11
print(pybind11.get_include(False) + ';' + pybind11.get_include(True))"
OUTPUT_VARIABLE _pybind11_inc_dir
OUTPUT_STRIP_TRAILING_WHITESPACE)
get_directory_property(_inc_dirs INCLUDE_DIRECTORIES)
list(FILTER _inc_dirs EXCLUDE REGEX .*pybind11_INCLUDE_DIR$)
list(APPEND _inc_dirs ${_pybind11_inc_dir})
set_directory_properties(PROPERTIES INCLUDE_DIRECTORIES "${_inc_dirs}")
endif()
endif()
# ==============================================================================
if(pybind11_FOUND)
if(NOT TARGET pybind11::headers)
_pybind11_message(SEND_ERROR
"Target pybind11::headers was not defined! Perhaps try updating pybind11 on your system?")
endif()
if(NOT TARGET pybind11::pybind11)
_pybind11_message(SEND_ERROR
"Target pybind11::pybind11 was not defined! Perhaps try updating pybind11 on your system?")
endif()
if(NOT TARGET pybind11::module)
_pybind11_message(SEND_ERROR
"Target pybind11::module was not defined! Perhaps try updating pybind11 on your system?")
endif()
# NB: workardound for NVHPC compiler that requires -isystem (e.g. for /usr/include)
get_target_property(_include_dir pybind11::pybind11_headers INTERFACE_INCLUDE_DIRECTORIES)
if(_include_dir)
target_include_directories(pybind11::pybind11_headers SYSTEM INTERFACE ${_include_dir})
endif()
else()
_pybind11_message(STATUS "pybind11 was not found on your system or it is an incompatible version")
_pybind11_message(STATUS " -> will be fetching pybind11 from an external Git repository")
set(PKG_NAME pybind11)
set(PKG_ROOT ${DEP_DIR}/${PKG_NAME}-src)
set(URL "https://gitee.com/mirrors/pybind11/repository/archive/v2.9.0.tar.gz")
FetchContent_Declare(${PKG_NAME} URL ${URL})
FetchContent_GetProperties(${PKG_NAME})
if(NOT ${PKG_NAME}_POPULATED)
FetchContent_Populate(${PKG_NAME})
endif()
include_directories(${PKG_ROOT}/include)
add_subdirectory(${PKG_ROOT} pybind11-build)
endif()
# ------------------------------------------------------------------------------
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(
pybind11
REQUIRED_VARS pybind11_INCLUDE_DIR
VERSION_VAR pybind11_VERSION)
# ------------------------------------------------------------------------------
# For debugging
if(pybind11_FOUND)
_pybind11_message(STATUS "Found pybind11 using the CONFIG method in ${pybind11_DIR}")
_pybind11_message(STATUS "Found pybind11 and defined the pybind11::pybind11 imported target:")
_pybind11_message(STATUS " - include: ${pybind11_INCLUDE_DIR}")
_pybind11_message(STATUS " - version: ${pybind11_VERSION}")
endif()
# ==============================================================================
mark_as_advanced(pybind11_DIR)
# ==============================================================================