Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use FindPython3 instead of FindPythonInterp #7

Merged
merged 11 commits into from
Feb 11, 2024
228 changes: 51 additions & 177 deletions cmake/Modules/FindPythonExtra.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -17,211 +17,85 @@
#
# CMake module for providing extra information about the Python interpreter.
#
# Output variables:
# Output Targets:
# - Python3::Interpreter - an executable target that will invoke a version of Python
# matching CMAKE_BUILD_TYPE on platforms where that matters
#
# Output variables:
# - PythonExtra_FOUND: True if a Python executable was found
# - PythonExtra_EXTENSION_SUFFIX: The suffix for a Python extension, according
# to PEP-3149: https://www.python.org/dev/peps/pep-3149/
# - PythonExtra_EXTENSION_EXTENSION: The extension for a Python extension. On
# Linux and Mac OSX equals to ".so", on Windows to ".pyd"
# - PythonExtra_INCLUDE_DIRS: The paths to the directories where the Python
# headers are installed.
# - PythonExtra_LIBRARIES: The paths to the Python libraries.
# - PYTHON_SOABI: The shared library file name tag according to PEP-3149:
# https://www.python.org/dev/peps/pep-3149/
#
# Conditional output variables
# Advanced Output variables
# - PYTHON_EXECUTABLE: a path to a python interpreter (deprecated; new code should use Python3::Interpreter)
# - PYTHON_EXECUTABLE_DEBUG: If the CMAKE_BUILD_TYPE is Debug and WIN32 is true
clalancette marked this conversation as resolved.
Show resolved Hide resolved
# then this will be a path to a debug build of the Python interpreter.
# then this will be a path to a debug build of the Python interpreter (deprecated; new code should use Python3::Interpreter)
# - PythonExtra_POSTFIX: a postfix that downstream consumers can use as the DEBUG_POSTFIX property to a target
#
# Example usage:
#
# find_package(python_cmake_module REQUIRED)
# find_package(PythonExtra MODULE)
# # use PythonExtra_* variables
# # use Python3::Interpreter
#
# Note on FindPython3
# This module will `find_package(Python3 REQUIRED COMPONENTS Interpreter)`
# If more components from FindPython3.cmake are needed, then find them manually
# after finding this module.
#
###############################################################################

# lint_cmake: -convention/filename, -package/stdargs

set(PythonExtra_FOUND FALSE)
if(PythonExtra_FOUND)
return()
endif()

# Prevent find_package(PythonLibs) from getting confused.
unset(PYTHON_LIBRARY)
find_package(Python3 REQUIRED COMPONENTS Interpreter)

find_package(PythonInterp 3.6 REQUIRED)
get_target_property(PYTHON_EXECUTABLE Python3::Interpreter LOCATION)
sloretz marked this conversation as resolved.
Show resolved Hide resolved

if(PYTHONINTERP_FOUND)
if(APPLE)
find_program(PYTHON_CONFIG_EXECUTABLE NAMES "python3-config")
if(NOT PYTHON_CONFIG_EXECUTABLE)
message(FATAL_ERROR "Cannot find python3-config executable")
endif()
set(PYTHON_EXECUTABLE_DEBUG "${PYTHON_EXECUTABLE}")

if(NOT DEFINED PythonExtra_INCLUDE_DIRS)
execute_process(
COMMAND
"${PYTHON_CONFIG_EXECUTABLE}"
"--includes"
OUTPUT_VARIABLE _output
RESULT_VARIABLE _result
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"execute_process(${PYTHON_CONFIG_EXECUTABLE} --includes) returned "
"error code ${_result}")
endif()

string(REPLACE " " ";" _output_list ${_output})

foreach(_includedir ${_output_list})
string(SUBSTRING "${_includedir}" 2 -1 _includedir)
list(APPEND PythonExtra_INCLUDE_DIRS "${_includedir}")
endforeach()
endif()
set(PythonExtra_INCLUDE_DIRS
${PythonExtra_INCLUDE_DIRS}
CACHE INTERNAL
"The paths to the Python include directories.")
message(STATUS "Using PythonExtra_INCLUDE_DIRS: ${PythonExtra_INCLUDE_DIRS}")

if(NOT DEFINED PythonExtra_LIBRARIES)
execute_process(
COMMAND
"${PYTHON_CONFIG_EXECUTABLE}"
"--ldflags"
OUTPUT_VARIABLE _output
RESULT_VARIABLE _result
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"execute_process(${PYTHON_CONFIG_EXECUTABLE} --ldflags) returned "
"error code ${_result}")
endif()

string(REPLACE " " ";" _output_list "${_output}")
set(PythonExtra_LIBRARIES
""
CACHE INTERNAL
"The libraries that need to be linked against for Python extensions.")

set(_library_paths "")
foreach(_item ${_output_list})
string(REGEX MATCH "-L(.*)" _regex_match ${_item})
if(NOT _regex_match STREQUAL "")
string(SUBSTRING "${_regex_match}" 2 -1 _library_path)
list(APPEND _library_paths "${_library_path}")
endif()
endforeach()

set(_python_version_no_dots
"${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}")
set(_python_version
"${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}")

find_library(PYTHON_LIBRARY
NAMES
python${_python_version_no_dots}
python${_python_version}mu
python${_python_version}m
python${_python_version}u
python${_python_version}
PATHS
${_library_paths}
NO_SYSTEM_ENVIRONMENT_PATH
)
endif()
set(PythonExtra_POSTFIX "")

set(PythonExtra_LIBRARIES "${PYTHON_LIBRARY}")
message(STATUS "Using PythonExtra_LIBRARIES: ${PythonExtra_LIBRARIES}")
set(_required_vars
PYTHON_EXECUTABLE
PYTHON_EXECUTABLE_DEBUG)

# Set the location to the debug interpreter on Windows if it exists
if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
get_filename_component(_python_executable_dir "${PYTHON_EXECUTABLE}" DIRECTORY)
get_filename_component(_python_executable_name "${PYTHON_EXECUTABLE}" NAME_WE)
get_filename_component(_python_executable_ext "${PYTHON_EXECUTABLE}" EXT)
if(_python_executable_name MATCHES ".*_d")
# The interpreter name we found already includes _d. We don't need to reconstruct it.
set(PYTHON_EXECUTABLE_DEBUG "${PYTHON_EXECUTABLE}")
else()
find_package(PythonLibs 3.5 REQUIRED)
if(WIN32 AND CMAKE_BUILD_TYPE STREQUAL "Debug")
get_filename_component(_python_executable_dir "${PYTHON_EXECUTABLE}" DIRECTORY)
get_filename_component(_python_executable_name "${PYTHON_EXECUTABLE}" NAME_WE)
get_filename_component(_python_executable_ext "${PYTHON_EXECUTABLE}" EXT)
set(_python_executable_debug "${_python_executable_dir}/${_python_executable_name}_d${_python_executable_ext}")
if(EXISTS "${_python_executable_debug}")
set(PYTHON_EXECUTABLE_DEBUG "${_python_executable_debug}")
else()
message(FATAL_ERROR "${_python_executable_debug} doesn't exist")
endif()
set(PYTHON_EXECUTABLE_DEBUG "${_python_executable_dir}/${_python_executable_name}_d${_python_executable_ext}")
if(NOT EXISTS "${PYTHON_EXECUTABLE_DEBUG}")
message(WARNING "${PYTHON_EXECUTABLE_DEBUG} doesn't exist but a Windows Debug build requires it")
unset(PYTHON_EXECUTABLE_DEBUG)
endif()
message(STATUS "Using PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}")
message(STATUS "Using PYTHON_INCLUDE_DIRS: ${PYTHON_INCLUDE_DIRS}")
message(STATUS "Using PYTHON_LIBRARIES: ${PYTHON_LIBRARIES}")
set(PythonExtra_INCLUDE_DIRS "${PYTHON_INCLUDE_DIRS}")
set(PythonExtra_LIBRARIES "${PYTHON_LIBRARIES}")
endif()

if(NOT DEFINED PYTHON_SOABI)
set(_python_code
"from sysconfig import get_config_var"
"print(get_config_var('SOABI'))"
)
execute_process(
COMMAND
"${PYTHON_EXECUTABLE}"
"-c"
"${_python_code}"
OUTPUT_VARIABLE _output
RESULT_VARIABLE _result
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT _result EQUAL 0)
message(FATAL_ERROR
"execute_process(${PYTHON_EXECUTABLE} -c '${_python_code}') returned "
"error code ${_result}")
endif()
set(Python3_EXECUTABLE "${PYTHON_EXECUTABLE_DEBUG}")
set_property(TARGET Python3::Interpreter PROPERTY IMPORTED_LOCATION "${PYTHON_EXECUTABLE_DEBUG}")

set(PYTHON_SOABI
"${_output}"
CACHE INTERNAL
"The SOABI suffix for Python native extensions. See PEP-3149: https://www.python.org/dev/peps/pep-3149/.")
endif()
set(PythonExtra_POSTFIX "_d")

if(PYTHON_SOABI STREQUAL "" OR PYTHON_SOABI STREQUAL "None")
set(PythonExtra_EXTENSION_SUFFIX
""
CACHE INTERNAL
"The full suffix for Python native extensions. See PEP-3149: https://www.python.org/dev/peps/pep-3149/."
)
else()
set(PythonExtra_EXTENSION_SUFFIX
".${PYTHON_SOABI}"
CACHE INTERNAL
"The full suffix for Python native extensions. See PEP-3149: https://www.python.org/dev/peps/pep-3149/."
)
endif()

if(WIN32)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(PythonExtra_EXTENSION_EXTENSION "_d.pyd")
else()
set(PythonExtra_EXTENSION_EXTENSION ".pyd")
endif()
else()
# Also use .so for OSX, not dylib
set(PythonExtra_EXTENSION_EXTENSION ".so")
endif()

set(PythonExtra_FOUND TRUE)
unset(_python_executable_dir)
unset(_python_executable_name)
unset(_python_executable_ext)
endif()

# Downstream users should use Python3::Interpreter instead of these variables
mark_as_advanced(
PYTHON_EXECUTABLE
PYTHON_EXECUTABLE_DEBUG
PythonExtra_POSTFIX)

include(FindPackageHandleStandardArgs)
set(_required_vars
PythonExtra_EXTENSION_EXTENSION
PythonExtra_INCLUDE_DIRS
PythonExtra_LIBRARIES
PYTHON_SOABI)
if(NOT WIN32)
list(APPEND _required_vars PythonExtra_EXTENSION_SUFFIX)
elseif("${CMAKE_BUILD_TYPE}" STREQUAL "Debug")
list(APPEND _required_vars PYTHON_EXECUTABLE_DEBUG)
endif()
find_package_handle_standard_args(PythonExtra
FOUND_VAR PythonExtra_FOUND
REQUIRED_VARS ${_required_vars}
)

unset(_required_vars)