From fb93d6e03208934555f7467da45179ea62b65c4c Mon Sep 17 00:00:00 2001 From: Kefu Chai Date: Mon, 14 Aug 2023 23:27:42 +0800 Subject: [PATCH] cmake: update FindPython3 module * use the one shiped by the latest CMake (ab379e5054aa792df9572078dcf95bddd75f7661) * use the new policy to use the new find strategy. * accomodate the vanilla FindPython3 module to Ceph by: - dropping the `cmake_policy()` calls which set the policy not supported by 3.16. - `include (FindPackageHandleStandardArgs)` without specifying the relative path. - dropping the `HANDLE_VERSION_RANGE` from `FindPackageHandleStandardArgs()` call. this option was introduced by CMake v3.19, see https://cmake.org/cmake/help/latest/module/FindPackageHandleStandardArgs.html but Ubuntu focal comes with CMake 3.16, which is our minimal required CMake version. the new FindPython3 module from CMake: * enables us to find the recent Python intepreter and development files up to CPython 3.13. * finds intepreter with the new `Python_FIND_STRATEGY`. the old and default strategy always finds the most recent version with all specified name and in all locations. so, if /usr/bin/python exists, it would accept, even if it is a symlink to python3.9 and what we want is python3.6. while the new policy stops at the one which satisfies the constraints. simpler this way and less error prone. Fixes: https://tracker.ceph.com/issues/62428 Signed-off-by: Kefu Chai --- CMakeLists.txt | 1 + COPYING | 2 +- cmake/modules/FindPython/Support.cmake | 4476 +++++++++++++++++++----- cmake/modules/FindPython3.cmake | 432 ++- 4 files changed, 4045 insertions(+), 866 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1778a720264..78881cb0188 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,6 +14,7 @@ cmake_policy(SET CMP0065 NEW) cmake_policy(SET CMP0074 NEW) cmake_policy(SET CMP0075 NEW) cmake_policy(SET CMP0093 NEW) +cmake_policy(SET CMP0094 NEW) foreach(policy CMP0127 CMP0135) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) diff --git a/COPYING b/COPYING index 373b9976b3e..bd0b22f6bce 100644 --- a/COPYING +++ b/COPYING @@ -9,7 +9,7 @@ License: LGPL-2.1 or LGPL-3 (see COPYING-LGPL2.1 and COPYING-LGPL3) Files: cmake/modules/FindPython* Copyright: - Copyright 2000-2020 Kitware, Inc. and Contributors + Copyright 2000-2023 Kitware, Inc. and Contributors License: BSD 3-clause Files: cmake/modules/FindLTTngUST.cmake diff --git a/cmake/modules/FindPython/Support.cmake b/cmake/modules/FindPython/Support.cmake index fb362bfe21c..5e1d6f62ecc 100644 --- a/cmake/modules/FindPython/Support.cmake +++ b/cmake/modules/FindPython/Support.cmake @@ -5,31 +5,35 @@ # This file is a "template" file used by various FindPython modules. # -cmake_policy (VERSION 3.5) - # # Initial configuration # + +cmake_policy(PUSH) +# list supports empty elements +cmake_policy (SET CMP0007 NEW) +# numbers and boolean constants +cmake_policy (SET CMP0012 NEW) +# IN_LIST operator +cmake_policy (SET CMP0057 NEW) + if (NOT DEFINED _PYTHON_PREFIX) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() if (NOT DEFINED _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() -if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL 3) - set(_${_PYTHON_PREFIX}_VERSIONS 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) -elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL 2) +if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") + set(_${_PYTHON_PREFIX}_VERSIONS 3.12 3.11 3.10 3.9 3.8 3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0) +elseif (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "2") set(_${_PYTHON_PREFIX}_VERSIONS 2.7 2.6 2.5 2.4 2.3 2.2 2.1 2.0) else() message (FATAL_ERROR "FindPython: INTERNAL ERROR") endif() get_property(_${_PYTHON_PREFIX}_CMAKE_ROLE GLOBAL PROPERTY CMAKE_ROLE) -if (NOT DEFINED _${_PYTHON_PREFIX}_CMAKE_ROLE) - # CMake 3.14 introduced CMAKE_ROLE - set(_${_PYTHON_PREFIX}_CMAKE_ROLE "PROJECT") -endif() +include (FindPackageHandleStandardArgs) # # helper commands @@ -46,70 +50,868 @@ macro (_PYTHON_DISPLAY_FAILURE _PYTHON_MSG) set (${_PYTHON_PREFIX}_FOUND FALSE) string (TOUPPER "${_PYTHON_PREFIX}" _${_PYTHON_PREFIX}_UPPER_PREFIX) set (${_PYTHON_UPPER_PREFIX}_FOUND FALSE) - return() +endmacro() + + +function (_PYTHON_ADD_REASON_FAILURE module message) + if (_${_PYTHON_PREFIX}_${module}_REASON_FAILURE) + string (LENGTH "${_${_PYTHON_PREFIX}_${module}_REASON_FAILURE}" length) + math (EXPR length "${length} + 10") + string (REPEAT " " ${length} shift) + set_property (CACHE _${_PYTHON_PREFIX}_${module}_REASON_FAILURE PROPERTY VALUE "${_${_PYTHON_PREFIX}_${module}_REASON_FAILURE}\n${shift}${message}") + else() + set_property (CACHE _${_PYTHON_PREFIX}_${module}_REASON_FAILURE PROPERTY VALUE "${message}") + endif() +endfunction() + + +function (_PYTHON_MARK_AS_INTERNAL) + foreach (var IN LISTS ARGV) + if (DEFINED CACHE{${var}}) + set_property (CACHE ${var} PROPERTY TYPE INTERNAL) + endif() + endforeach() +endfunction() + + +macro (_PYTHON_SELECT_LIBRARY_CONFIGURATIONS _PYTHON_BASENAME) + if(NOT DEFINED ${_PYTHON_BASENAME}_LIBRARY_RELEASE) + set(${_PYTHON_BASENAME}_LIBRARY_RELEASE "${_PYTHON_BASENAME}_LIBRARY_RELEASE-NOTFOUND") + endif() + if(NOT DEFINED ${_PYTHON_BASENAME}_LIBRARY_DEBUG) + set(${_PYTHON_BASENAME}_LIBRARY_DEBUG "${_PYTHON_BASENAME}_LIBRARY_DEBUG-NOTFOUND") + endif() + + get_property(_PYTHON_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if (${_PYTHON_BASENAME}_LIBRARY_DEBUG AND ${_PYTHON_BASENAME}_LIBRARY_RELEASE AND + NOT ${_PYTHON_BASENAME}_LIBRARY_DEBUG STREQUAL ${_PYTHON_BASENAME}_LIBRARY_RELEASE AND + (_PYTHON_isMultiConfig OR CMAKE_BUILD_TYPE)) + # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for + # single-config generators, set optimized and debug libraries + set (${_PYTHON_BASENAME}_LIBRARIES "") + foreach (_PYTHON_libname IN LISTS ${_PYTHON_BASENAME}_LIBRARY_RELEASE) + list( APPEND ${_PYTHON_BASENAME}_LIBRARIES optimized "${_PYTHON_libname}") + endforeach() + foreach (_PYTHON_libname IN LISTS ${_PYTHON_BASENAME}_LIBRARY_DEBUG) + list( APPEND ${_PYTHON_BASENAME}_LIBRARIES debug "${_PYTHON_libname}") + endforeach() + elseif (${_PYTHON_BASENAME}_LIBRARY_RELEASE) + set (${_PYTHON_BASENAME}_LIBRARIES "${${_PYTHON_BASENAME}_LIBRARY_RELEASE}") + elseif (${_PYTHON_BASENAME}_LIBRARY_DEBUG) + set (${_PYTHON_BASENAME}_LIBRARIES "${${_PYTHON_BASENAME}_LIBRARY_DEBUG}") + else() + set (${_PYTHON_BASENAME}_LIBRARIES "${_PYTHON_BASENAME}_LIBRARY-NOTFOUND") + endif() endmacro() macro (_PYTHON_FIND_FRAMEWORKS) - set (${_PYTHON_PREFIX}_FRAMEWORKS) - if (APPLE) + if (CMAKE_HOST_APPLE OR APPLE) + file(TO_CMAKE_PATH "$ENV{CMAKE_FRAMEWORK_PATH}" _pff_CMAKE_FRAMEWORK_PATH) set (_pff_frameworks ${CMAKE_FRAMEWORK_PATH} - $ENV{CMAKE_FRAMEWORK_PATH} - ~/Library/Frameworks - /usr/local/Frameworks - ${CMAKE_SYSTEM_FRAMEWORK_PATH}) + ${_pff_CMAKE_FRAMEWORK_PATH} + ~/Library/Frameworks + /usr/local/Frameworks + /opt/homebrew/Frameworks + ${CMAKE_SYSTEM_FRAMEWORK_PATH}) list (REMOVE_DUPLICATES _pff_frameworks) - foreach (_pff_framework IN LISTS _pff_frameworks) - if (EXISTS ${_pff_framework}/Python.framework) - list (APPEND ${_PYTHON_PREFIX}_FRAMEWORKS ${_pff_framework}/Python.framework) + foreach (_pff_implementation IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + unset (_${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS) + if (_pff_implementation STREQUAL "CPython") + foreach (_pff_framework IN LISTS _pff_frameworks) + if (EXISTS ${_pff_framework}/Python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}.framework) + list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/Python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}.framework) + endif() + if (EXISTS ${_pff_framework}/Python.framework) + list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/Python.framework) + endif() + endforeach() + elseif (_pff_implementation STREQUAL "IronPython") + foreach (_pff_framework IN LISTS _pff_frameworks) + if (EXISTS ${_pff_framework}/IronPython.framework) + list (APPEND _${_PYTHON_PREFIX}_${_pff_implementation}_FRAMEWORKS ${_pff_framework}/IronPython.framework) + endif() + endforeach() endif() endforeach() + unset (_pff_implementation) unset (_pff_frameworks) unset (_pff_framework) endif() endmacro() -function (_PYTHON_GET_FRAMEWORKS _PYTHON_PGF_FRAMEWORK_PATHS _PYTHON_VERSION) - set (_PYTHON_FRAMEWORK_PATHS) - foreach (_PYTHON_FRAMEWORK IN LISTS ${_PYTHON_PREFIX}_FRAMEWORKS) - list (APPEND _PYTHON_FRAMEWORK_PATHS - "${_PYTHON_FRAMEWORK}/Versions/${_PYTHON_VERSION}") +function (_PYTHON_GET_FRAMEWORKS _PYTHON_PGF_FRAMEWORK_PATHS) + cmake_parse_arguments (PARSE_ARGV 1 _PGF "" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGF_IMPLEMENTATIONS) + set (_PGF_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + set (framework_paths) + + foreach (implementation IN LISTS _PGF_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + foreach (version IN LISTS _PGF_VERSION) + foreach (framework IN LISTS _${_PYTHON_PREFIX}_${implementation}_FRAMEWORKS) + if (EXISTS "${framework}/Versions/${version}") + list (APPEND framework_paths "${framework}/Versions/${version}") + endif() + endforeach() + endforeach() + elseif (implementation STREQUAL "IronPython") + foreach (version IN LISTS _PGF_VERSION) + foreach (framework IN LISTS _${_PYTHON_PREFIX}_${implementation}_FRAMEWORKS) + # pick-up all available versions + file (GLOB versions LIST_DIRECTORIES true RELATIVE "${framework}/Versions/" + "${framework}/Versions/${version}*") + list (SORT versions ORDER DESCENDING) + list (TRANSFORM versions PREPEND "${framework}/Versions/") + list (APPEND framework_paths ${versions}) + endforeach() + endforeach() + endif() endforeach() - set (${_PYTHON_PGF_FRAMEWORK_PATHS} ${_PYTHON_FRAMEWORK_PATHS} PARENT_SCOPE) + + set (${_PYTHON_PGF_FRAMEWORK_PATHS} ${framework_paths} PARENT_SCOPE) endfunction() +function (_PYTHON_GET_REGISTRIES _PYTHON_PGR_REGISTRY_PATHS) + cmake_parse_arguments (PARSE_ARGV 1 _PGR "" "" "IMPLEMENTATIONS;VERSION") -function (_PYTHON_VALIDATE_INTERPRETER) - if (NOT ${_PYTHON_PREFIX}_EXECUTABLE) + if (NOT _PGR_IMPLEMENTATIONS) + set (_PGR_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + set (registries) + + foreach (implementation IN LISTS _PGR_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + foreach (version IN LISTS _PGR_VERSION) + string (REPLACE "." "" version_no_dots ${version}) + list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) + list (APPEND registries ${reg_paths}) + if (version VERSION_GREATER_EQUAL "3.5") + # cmake_host_system_information is not usable in bootstrap + get_filename_component (arch "[HKEY_CURRENT_USER\\Software\\Python\\PythonCore\\${version};SysArchitecture]" NAME) + string (REPLACE "bit" "" arch "${arch}") + if (arch IN_LIST _${_PYTHON_PREFIX}_ARCH) + list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) + endif() + else() + list (APPEND registries [HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}/InstallPath]) + endif() + list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) + list (APPEND registries ${reg_paths}) + list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_CURRENT_USER/SOFTWARE/Python/PythonCore/${version}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) + list (APPEND registries ${reg_paths}) + list (APPEND registries [HKEY_LOCAL_MACHINE/SOFTWARE/Python/PythonCore/${version}/InstallPath]) + list (TRANSFORM _${_PYTHON_PREFIX}_ARCH REPLACE "^(.+)$" "[HKEY_LOCAL_MACHINE/SOFTWARE/Python/ContinuumAnalytics/Anaconda${version_no_dots}-\\1/InstallPath]" OUTPUT_VARIABLE reg_paths) + list (APPEND registries ${reg_paths}) + endforeach() + elseif (implementation STREQUAL "IronPython") + foreach (version IN LISTS _PGR_VERSION) + list (APPEND registries [HKEY_LOCAL_MACHINE/SOFTWARE/IronPython/${version}/InstallPath]) + endforeach() + endif() + endforeach() + + set (${_PYTHON_PGR_REGISTRY_PATHS} "${registries}" PARENT_SCOPE) +endfunction() + + +function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS) + set (abiflags) + list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug) + list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc) + list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode) + + if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY") + set (abiflags "mu" "m" "u" "") + elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON") + set (abiflags "mu" "u") + elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF") + set (abiflags "m" "") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") + set (abiflags "mu" "m") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON") + set (abiflags "mu") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF") + set (abiflags "m") + elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY") + set (abiflags "u" "") + elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON") + set (abiflags "u") + endif() + + if (pydebug STREQUAL "ON") + if (abiflags) + list (TRANSFORM abiflags PREPEND "d") + else() + set (abiflags "d") + endif() + elseif (pydebug STREQUAL "ANY") + if (abiflags) + set (flags "${abiflags}") + list (TRANSFORM flags PREPEND "d") + list (APPEND abiflags "${flags}") + else() + set (abiflags "" "d") + endif() + endif() + + set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES) + cmake_parse_arguments (PARSE_ARGV 1 _PGPS "INTERPRETER;COMPILER;LIBRARY;INCLUDE" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGPS_IMPLEMENTATIONS) + set (_PGPS_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + else() + set (abi "mu" "m" "u" "") + endif() + + set (path_suffixes) + + foreach (implementation IN LISTS _PGPS_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + if (_PGPS_INTERPRETER) + list (APPEND path_suffixes bin Scripts) + else() + foreach (version IN LISTS _PGPS_VERSION) + if (_PGPS_LIBRARY) + if (CMAKE_LIBRARY_ARCHITECTURE) + list (APPEND path_suffixes lib/${CMAKE_LIBRARY_ARCHITECTURE}) + endif() + list (APPEND path_suffixes lib libs) + + if (CMAKE_LIBRARY_ARCHITECTURE) + set (suffixes "${abi}") + if (suffixes) + list (TRANSFORM suffixes PREPEND "lib/python${version}/config-${version}") + list (TRANSFORM suffixes APPEND "-${CMAKE_LIBRARY_ARCHITECTURE}") + else() + set (suffixes "lib/python${version}/config-${version}-${CMAKE_LIBRARY_ARCHITECTURE}") + endif() + list (APPEND path_suffixes ${suffixes}) + endif() + set (suffixes "${abi}") + if (suffixes) + list (TRANSFORM suffixes PREPEND "lib/python${version}/config-${version}") + else() + set (suffixes "lib/python${version}/config-${version}") + endif() + list (APPEND path_suffixes ${suffixes}) + elseif (_PGPS_INCLUDE) + set (suffixes "${abi}") + if (suffixes) + list (TRANSFORM suffixes PREPEND "include/python${version}") + else() + set (suffixes "include/python${version}") + endif() + list (APPEND path_suffixes ${suffixes} include) + endif() + endforeach() + endif() + elseif (implementation STREQUAL "IronPython") + if (_PGPS_INTERPRETER OR _PGPS_COMPILER) + foreach (version IN LISTS _PGPS_VERSION) + list (APPEND path_suffixes "share/ironpython${version}") + endforeach() + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}) + endif() + elseif (implementation STREQUAL "PyPy") + if (_PGPS_INTERPRETER) + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_EXECUTABLE_PATH_SUFFIXES}) + elseif (_PGPS_LIBRARY) + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_LIBRARY_PATH_SUFFIXES}) + elseif (_PGPS_INCLUDE) + foreach (version IN LISTS _PGPS_VERSION) + list (APPEND path_suffixes lib/pypy${version}/include pypy${version}/include) + endforeach() + list (APPEND path_suffixes ${_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES}) + endif() + endif() + endforeach() + list (REMOVE_DUPLICATES path_suffixes) + + set (${_PYTHON_PGPS_PATH_SUFFIXES} ${path_suffixes} PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_NAMES _PYTHON_PGN_NAMES) + cmake_parse_arguments (PARSE_ARGV 1 _PGN "POSIX;INTERPRETER;COMPILER;CONFIG;LIBRARY;WIN32;DEBUG" "" "IMPLEMENTATIONS;VERSION") + + if (NOT _PGN_IMPLEMENTATIONS) + set (_PGN_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}) + endif() + + set (names) + + foreach (implementation IN LISTS _PGN_IMPLEMENTATIONS) + if (implementation STREQUAL "CPython") + if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "FIRST") + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + endif() + foreach (version IN LISTS _PGN_VERSION) + if (_PGN_WIN32) + string (REPLACE "." "" version_no_dots ${version}) + + set (name python${version_no_dots}) + if (_PGN_DEBUG) + string (APPEND name "_d") + endif() + + list (APPEND names "${name}") + endif() + + if (_PGN_POSIX) + if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS) + set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}") + else() + if (_PGN_INTERPRETER OR _PGN_CONFIG) + set (abi "") + else() + set (abi "mu" "m" "u" "") + endif() + endif() + + if (abi) + if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + set (abinames "${abi}") + list (TRANSFORM abinames PREPEND "${CMAKE_LIBRARY_ARCHITECTURE}-python${version}") + list (TRANSFORM abinames APPEND "-config") + list (APPEND names ${abinames}) + endif() + set (abinames "${abi}") + list (TRANSFORM abinames PREPEND "python${version}") + if (_PGN_CONFIG) + list (TRANSFORM abinames APPEND "-config") + endif() + list (APPEND names ${abinames}) + else() + unset (abinames) + if (_PGN_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + set (abinames "${CMAKE_LIBRARY_ARCHITECTURE}-python${version}") + endif() + list (APPEND abinames "python${version}") + if (_PGN_CONFIG) + list (TRANSFORM abinames APPEND "-config") + endif() + list (APPEND names ${abinames}) + endif() + endif() + endforeach() + if (_PGN_INTERPRETER AND _${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES STREQUAL "LAST") + list (APPEND names python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} python) + endif() + elseif (implementation STREQUAL "IronPython") + if (_PGN_INTERPRETER) + if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not use wrapper script on Linux because it is buggy: -c interpreter option cannot be used + foreach (version IN LISTS _PGN_VERSION) + list (APPEND names "ipy${version}") + endforeach() + endif() + list (APPEND names ${_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES}) + elseif (_PGN_COMPILER) + list (APPEND names ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES}) + endif() + elseif (implementation STREQUAL "PyPy") + if (_PGN_INTERPRETER) + list (APPEND names ${_${_PYTHON_PREFIX}_PYPY_NAMES}) + elseif (_PGN_LIBRARY) + if (_PGN_WIN32) + foreach (version IN LISTS _PGN_VERSION) + string (REPLACE "." "" version_no_dots ${version}) + set (name "python${version_no_dots}") + if (_PGN_DEBUG) + string (APPEND name "_d") + endif() + list (APPEND names "${name}") + endforeach() + endif() + + if (_PGN_POSIX) + foreach(version IN LISTS _PGN_VERSION) + list (APPEND names "pypy${version}-c") + endforeach() + endif() + + list (APPEND names ${_${_PYTHON_PREFIX}_PYPY_LIB_NAMES}) + endif() + endif() + endforeach() + + set (${_PYTHON_PGN_NAMES} ${names} PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME) + unset (${_PYTHON_PGCV_VALUE} PARENT_SCOPE) + + if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI|SOSABI)$") + return() + endif() + + if (NAME STREQUAL "SOSABI") + # assume some default + if (CMAKE_SYSTEM_NAME STREQUAL "Windows" OR CMAKE_SYSTEM_NAME MATCHES "MSYS|CYGWIN") + set (_values "") + else() + set (_values "abi${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") + endif() + elseif (_${_PYTHON_PREFIX}_CONFIG) + if (NAME STREQUAL "SOABI") + set (config_flag "--extension-suffix") + else() + set (config_flag "--${NAME}") + endif() + string (TOLOWER "${config_flag}" config_flag) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} ${config_flag} + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + if (NAME STREQUAL "INCLUDES") + # do some clean-up + string (REGEX MATCHALL "(-I|-iwithsysroot)[ ]*[^ ]+" _values "${_values}") + string (REGEX REPLACE "(-I|-iwithsysroot)[ ]*" "" _values "${_values}") + list (REMOVE_DUPLICATES _values) + elseif (NAME STREQUAL "SOABI") + # clean-up: remove prefix character and suffix + if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") + set(_values "") + else() + string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + endif() + endif() + endif() + endif() + + if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING) + if (NAME STREQUAL "PREFIX") + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\ntry:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_config_var('base') or '', sysconfig.get_config_var('installed_base') or '']))\nexcept Exception:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.PREFIX,sysconfig.EXEC_PREFIX,sysconfig.BASE_EXEC_PREFIX]))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + list (REMOVE_DUPLICATES _values) + endif() + elseif (NAME STREQUAL "INCLUDES") + if (WIN32) + set (_scheme "nt") + else() + set (_scheme "posix_prefix") + endif() + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_path('platinclude'),sysconfig.get_path('platinclude','${_scheme}'),sysconfig.get_path('include'),sysconfig.get_path('include','${_scheme}')]))\nexcept Exception:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_python_inc(plat_specific=True),sysconfig.get_python_inc(plat_specific=False)]))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + list (REMOVE_DUPLICATES _values) + endif() + elseif (NAME STREQUAL "SOABI") + # first step: compute SOABI form EXT_SUFFIX config variable + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n import sysconfig\n sys.stdout.write(sysconfig.get_config_var('EXT_SUFFIX') or '')\nexcept Exception:\n from distutils import sysconfig;sys.stdout.write(sysconfig.get_config_var('EXT_SUFFIX') or '')" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + if (_values) + # clean-up: remove prefix character and suffix + if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") + set(_values "") + else() + string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + endif() + endif() + endif() + + # second step: use SOABI or SO config variables as fallback + if (NOT _values) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('SO') or '']))\nexcept Exception:\n from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('SO') or '']))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _soabi + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + foreach (_item IN LISTS _soabi) + if (_item) + set (_values "${_item}") + break() + endif() + endforeach() + if (_values) + # clean-up: remove prefix character and suffix + if (_values MATCHES "^(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.so|\\.pyd)$") + set(_values "") + else() + string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\\.(so|pyd))$" "\\1" _values "${_values}") + endif() + endif() + endif() + endif() + elseif (NAME STREQUAL "SOSABI") + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys\nimport re\nimport importlib.machinery\nsys.stdout.write(next(filter(lambda x: re.search('^\\.abi', x), importlib.machinery.EXTENSION_SUFFIXES)))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + else() + string (REGEX REPLACE "^\\.(.+)\\.[^.]+$" "\\1" _values "${_values}") + endif() + else() + set (config_flag "${NAME}") + if (NAME STREQUAL "CONFIGDIR") + set (config_flag "LIBPL") + endif() + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry:\n import sysconfig\n sys.stdout.write(sysconfig.get_config_var('${config_flag}'))\nexcept Exception:\n from distutils import sysconfig\n sys.stdout.write(sysconfig.get_config_var('${config_flag}'))" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _values + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + unset (_values) + endif() + endif() + endif() + + if (NAME STREQUAL "ABIFLAGS" OR NAME STREQUAL "SOABI" OR NAME STREQUAL "SOSABI") + set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) + return() + endif() + + if (NOT _values OR _values STREQUAL "None") return() endif() - if (ARGC EQUAL 1) - set (expected_version ${ARGV0}) + if (NAME STREQUAL "LIBS") + # do some clean-up + string (REGEX MATCHALL "-(l|framework)[ ]*[^ ]+" _values "${_values}") + # remove elements relative to python library itself + list (FILTER _values EXCLUDE REGEX "-lpython") + list (REMOVE_DUPLICATES _values) + endif() + + if (WIN32 AND NAME MATCHES "^(PREFIX|CONFIGDIR|INCLUDES)$") + file (TO_CMAKE_PATH "${_values}" _values) + endif() + + set (${_PYTHON_PGCV_VALUE} "${_values}" PARENT_SCOPE) +endfunction() + +function (_PYTHON_GET_VERSION) + cmake_parse_arguments (PARSE_ARGV 0 _PGV "LIBRARY;SABI_LIBRARY;INCLUDE" "PREFIX" "") + + unset (${_PGV_PREFIX}VERSION PARENT_SCOPE) + unset (${_PGV_PREFIX}VERSION_MAJOR PARENT_SCOPE) + unset (${_PGV_PREFIX}VERSION_MINOR PARENT_SCOPE) + unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE) + unset (${_PGV_PREFIX}ABI PARENT_SCOPE) + + if (_PGV_LIBRARY) + # retrieve version and abi from library name + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + get_filename_component (library_name "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" NAME) + # extract version from library name + if (library_name MATCHES "python([23])([0-9]+)") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + elseif (library_name MATCHES "python([23])\\.([0-9]+)([dmu]*)") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_3}" PARENT_SCOPE) + elseif (library_name MATCHES "pypy([23])\\.([0-9]+)-c") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + elseif (library_name MATCHES "pypy(3)?-c") + set (version "${CMAKE_MATCH_1}") + # try to pick-up a more precise version from the path + get_filename_component (library_dir "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + if (library_dir MATCHES "/pypy([23])\\.([0-9]+)/") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE) + elseif (version EQUAL "3") + set (${_PGV_PREFIX}VERSION_MAJOR "3" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "3" PARENT_SCOPE) + else() + set (${_PGV_PREFIX}VERSION_MAJOR "2" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "2" PARENT_SCOPE) + endif() + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + endif() + endif() + elseif (_PGV_SABI_LIBRARY) + # retrieve version and abi from library name + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + get_filename_component (library_name "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" NAME) + # extract version from library name + if (library_name MATCHES "python([23])([dmu]*)") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_2}" PARENT_SCOPE) + elseif (library_name MATCHES "pypy([23])-c") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + elseif (library_name MATCHES "pypy-c") + # try to pick-up a more precise version from the path + get_filename_component (library_dir "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) + if (library_dir MATCHES "/pypy([23])\\.([0-9]+)/") + set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE) + endif() + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + endif() + endif() else() - unset (expected_version) + if (_${_PYTHON_PREFIX}_INCLUDE_DIR) + # retrieve version from header file + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" version + REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") + string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" + version "${version}") + string (REGEX MATCHALL "[0-9]+" versions "${version}") + list (GET versions 0 version_major) + list (GET versions 1 version_minor) + list (GET versions 2 version_patch) + + set (${_PGV_PREFIX}VERSION "${version_major}.${version_minor}.${version_patch}" PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MAJOR ${version_major} PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_MINOR ${version_minor} PARENT_SCOPE) + set (${_PGV_PREFIX}VERSION_PATCH ${version_patch} PARENT_SCOPE) + + # compute ABI flags + if (version_major VERSION_GREATER "2") + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(Py_DEBUG|WITH_PYMALLOC|Py_UNICODE_SIZE|MS_WIN32)") + set (abi) + if (config MATCHES "#[ ]*define[ ]+MS_WIN32") + # ABI not used on Windows + set (abi "") + else() + if (NOT config) + # pyconfig.h can be a wrapper to a platform specific pyconfig.h + # In this case, try to identify ABI from include directory + if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([dmu]*)") + set (abi "${CMAKE_MATCH_1}") + else() + set (abi "") + endif() + else() + if (config MATCHES "#[ ]*define[ ]+Py_DEBUG[ ]+1") + string (APPEND abi "d") + endif() + if (config MATCHES "#[ ]*define[ ]+WITH_PYMALLOC[ ]+1") + string (APPEND abi "m") + endif() + if (config MATCHES "#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4") + string (APPEND abi "u") + endif() + endif() + set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE) + endif() + else() + # ABI not supported + set (${_PGV_PREFIX}ABI "" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + +function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME) + cmake_parse_arguments (PARSE_ARGV 1 _PGL "INTERPRETER;COMPILER" "CONFIG" "") + + unset (${_PYTHON_PGL_NAME} PARENT_SCOPE) + + if ((_PGL_INTERPRETER AND NOT _${_PYTHON_PREFIX}_EXECUTABLE) + OR (_PGL_COMPILER AND NOT _${_PYTHON_PREFIX}_COMPILER) + OR (_PGL_CONFIG AND NOT _${_PYTHON_PREFIX}_CONFIG)) + return() + endif() + + if (_PGL_CONFIG) + # default config script can be launched directly + set (${_PYTHON_PGL_NAME} "${_${_PYTHON_PREFIX}_CONFIG}" PARENT_SCOPE) + + if (NOT MINGW) + return() + endif() + # on MINGW environment, python-config script may require bash to be launched + execute_process (COMMAND cygpath.exe -u "${_${_PYTHON_PREFIX}_CONFIG}" + RESULT_VARIABLE _result + OUTPUT_VARIABLE _config + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_result) + # impossible to convert path, keep default config + return() + endif() + execute_process (COMMAND bash.exe "${_config}" --prefix + RESULT_VARIABLE _result + OUTPUT_QUIET + ERROR_QUIET) + if (_result) + # fail to execute through bash, keep default config + return() + endif() + + set(${_PYTHON_PGL_NAME} bash.exe "${_config}" PARENT_SCOPE) + return() + endif() + + if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS + AND NOT SYSTEM_NAME MATCHES "Windows|Linux") + if (_PGL_INTERPRETER) + get_filename_component (name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME) + get_filename_component (ext "${_${_PYTHON_PREFIX}_EXECUTABLE}" LAST_EXT) + if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES + AND ext STREQUAL ".exe") + set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE) + endif() + elseif (_PGL_COMPILER) + get_filename_component (name "${_${_PYTHON_PREFIX}_COMPILER}" NAME) + get_filename_component (ext "${_${_PYTHON_PREFIX}_COMPILER}" LAST_EXT) + if (name IN_LIST _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES + AND ext STREQUAL ".exe") + set (${_PYTHON_PGL_NAME} "${${_PYTHON_PREFIX}_DOTNET_LAUNCHER}" PARENT_SCOPE) + endif() + endif() + endif() +endfunction() + + +function (_PYTHON_VALIDATE_INTERPRETER) + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVI "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVI_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_EXECUTABLE}") + # interpreter does not exist anymore + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot find the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() endif() - get_filename_component (python_name "${${_PYTHON_PREFIX}_EXECUTABLE}" NAME) + _python_get_launcher (launcher INTERPRETER) - if (expected_version AND NOT python_name STREQUAL "python${expected_version}${CMAKE_EXECUTABLE_SUFFIX}") - # executable found must have a specific version - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c - "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:2]]))" + # validate ABI compatibility + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI) + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(sys.abiflags)" + RESULT_VARIABLE result + OUTPUT_VARIABLE abi + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (result) + # assume ABI is not supported + set (abi "") + endif() + if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + + if (_PVI_IN_RANGE OR _PVI_VERSION) + # retrieve full version + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" RESULT_VARIABLE result OUTPUT_VARIABLE version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) - if (result OR NOT version EQUAL expected_version) - # interpreter not usable or has wrong major version - set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) + if (result) + # interpreter is not usable + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() + + if (_PVI_VERSION) + # check against specified version + ## compute number of components for version + string (REGEX REPLACE "[^.]" "" dots "${_PVI_VERSION}") + ## add one dot because there is one dot less than there are components + string (LENGTH "${dots}." count) + if (count GREATER 3) + set (count 3) + endif() + set (version_regex "^[0-9]+") + if (count EQUAL 3) + string (APPEND version_regex "\\.[0-9]+\\.[0-9]+") + elseif (count EQUAL 2) + string (APPEND version_regex "\\.[0-9]+") + endif() + # extract needed range + string (REGEX MATCH "${version_regex}" version "${version}") + + if (_PVI_EXACT AND NOT version VERSION_EQUAL _PVI_VERSION) + # interpreter has wrong version + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + else() + # check that version is OK + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVI_VERSION}") + if (NOT major_version VERSION_EQUAL expected_major_version + OR NOT version VERSION_GREATER_EQUAL _PVI_VERSION) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + endif() + + if (_PVI_IN_RANGE) + # check if version is in the requested range + find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # interpreter has invalid version + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() else() + get_filename_component (python_name "${_${_PYTHON_PREFIX}_EXECUTABLE}" NAME) if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}") # executable found do not have version in name # ensure major version is OK - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write(str(sys.version_info[0]))" RESULT_VARIABLE result OUTPUT_VARIABLE version @@ -117,16 +919,23 @@ function (_PYTHON_VALIDATE_INTERPRETER) OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR NOT version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) # interpreter not usable or has wrong major version - set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) + if (result) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + else() + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong major version for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + endif() + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") return() endif() endif() endif() - if (CMAKE_SIZEOF_VOID_P AND "Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + if (CMAKE_SIZEOF_VOID_P AND ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) AND NOT CMAKE_CROSSCOMPILING) # In this case, interpreter must have same architecture as environment - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))" RESULT_VARIABLE result OUTPUT_VARIABLE size @@ -134,36 +943,320 @@ function (_PYTHON_VALIDATE_INTERPRETER) OUTPUT_STRIP_TRAILING_WHITESPACE) if (result OR NOT size EQUAL CMAKE_SIZEOF_VOID_P) # interpreter not usable or has wrong architecture - set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) + if (result) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + else() + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + endif() + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + + if (WIN32) + # In this case, check if the interpreter is compatible with the target processor architecture + if (NOT CMAKE_GENERATOR_PLATFORM AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM" OR CMAKE_GENERATOR_PLATFORM MATCHES "ARM") + set(target_arm TRUE) + else() + set(target_arm FALSE) + endif() + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys, sysconfig; sys.stdout.write(sysconfig.get_platform())" + RESULT_VARIABLE result + OUTPUT_VARIABLE platform + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + string(TOUPPER "${platform}" platform) + if (result OR ((target_arm AND NOT platform MATCHES "ARM") OR + (NOT target_arm AND platform MATCHES "ARM"))) + # interpreter not usable or has wrong architecture + if (result) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot use the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + else() + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong architecture for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + endif() + set_property (CACHE _${_PYTHON_PREFIX}_EXECUTABLE PROPERTY VALUE "${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND") + return() + endif() + endif() + endif() +endfunction() + +function(_python_validate_find_interpreter status interpreter) + set(_${_PYTHON_PREFIX}_EXECUTABLE "${interpreter}" CACHE FILEPATH "" FORCE) + _python_validate_interpreter (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + set (${status} FALSE PARENT_SCOPE) + endif() +endfunction() + + +function (_PYTHON_VALIDATE_COMPILER) + if (NOT _${_PYTHON_PREFIX}_COMPILER) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVC "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVC_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_COMPILER}") + # Compiler does not exist anymore + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot find the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + + _python_get_launcher (launcher COMPILER) + + # retrieve python environment version from compiler + set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") + file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]])); sys.stdout.flush()\n") + execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_COMPILER}" + ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS} + /target:exe /embed "${working_dir}/version.py" + WORKING_DIRECTORY "${working_dir}" + OUTPUT_QUIET + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + get_filename_component (ir_dir "${_${_PYTHON_PREFIX}_COMPILER}" DIRECTORY) + execute_process (COMMAND "${CMAKE_COMMAND}" -E env "MONO_PATH=${ir_dir}" + ${${_PYTHON_PREFIX}_DOTNET_LAUNCHER} "${working_dir}/version.exe" + WORKING_DIRECTORY "${working_dir}" + RESULT_VARIABLE result + OUTPUT_VARIABLE version + ERROR_QUIET) + file (REMOVE_RECURSE "${working_dir}") + if (result) + # compiler is not usable + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot use the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + + if (_PVC_VERSION OR _PVC_IN_RANGE) + if (_PVC_VERSION) + # check against specified version + ## compute number of components for version + string (REGEX REPLACE "[^.]" "" dots "${_PVC_VERSION}") + ## add one dot because there is one dot less than there are components + string (LENGTH "${dots}." count) + if (count GREATER 3) + set (count 3) + endif() + set (version_regex "^[0-9]+") + if (count EQUAL 3) + string (APPEND version_regex "\\.[0-9]+\\.[0-9]+") + elseif (count EQUAL 2) + string (APPEND version_regex "\\.[0-9]+") + endif() + # extract needed range + string (REGEX MATCH "${version_regex}" version "${version}") + + if (_PVC_EXACT AND NOT version VERSION_EQUAL _PVC_VERSION) + # interpreter has wrong version + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + else() + # check that version is OK + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" expected_major_version "${_PVC_VERSION}") + if (NOT major_version VERSION_EQUAL expected_major_version + OR NOT version VERSION_GREATER_EQUAL _PVC_VERSION) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + endif() + endif() + + if (_PVC_IN_RANGE) + # check if version is in the requested range + find_package_check_version ("${version}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # interpreter has invalid version + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") + return() + endif() + endif() + else() + string(REGEX REPLACE "^([0-9]+)\\.?.*$" "\\1" major_version "${version}") + if (NOT major_version EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # Compiler has wrong major version + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Wrong major version for the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") + set_property (CACHE _${_PYTHON_PREFIX}_COMPILER PROPERTY VALUE "${_PYTHON_PREFIX}_COMPILER-NOTFOUND") return() endif() endif() endfunction() +function(_python_validate_find_compiler status compiler) + set(_${_PYTHON_PREFIX}_COMPILER "${compiler}" CACHE FILEPATH "" FORCE) + _python_validate_compiler (${_${_PYTHON_PREFIX}_VALIDATE_OPTIONS}) + if (NOT _${_PYTHON_PREFIX}_COMPILER) + set (${status} FALSE PARENT_SCOPE) + endif() +endfunction() + + +function (_PYTHON_VALIDATE_LIBRARY) + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVL "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + # library does not exist anymore + set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + if (WIN32) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") + endif() + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + return() + endif() + + # retrieve version and abi from library name + _python_get_version (LIBRARY PREFIX lib_) + + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + else() + if (_PVL_VERSION OR _PVL_IN_RANGE) + if (_PVL_VERSION) + # library have only major.minor information + string (REGEX MATCH "[0-9](\\.[0-9]+)?" version "${_PVL_VERSION}") + if ((_PVL_EXACT AND NOT lib_VERSION VERSION_EQUAL version) OR (lib_VERSION VERSION_LESS version)) + # library has wrong version + set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND _PVL_IN_RANGE) + # check if library version is in the requested range + find_package_check_version ("${lib_VERSION}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # library has wrong version + set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + else() + if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # library has wrong major version + set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if (WIN32) + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") + endif() + unset (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE CACHE) + unset (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG CACHE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() +endfunction() + + +function (_PYTHON_VALIDATE_SABI_LIBRARY) + if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) + return() + endif() + + cmake_parse_arguments (PARSE_ARGV 0 _PVL "CHECK_EXISTS" "" "") + + if (_PVL_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") + # library does not exist anymore + set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") + if (WIN32) + set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG-NOTFOUND") + endif() + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + return() + endif() + + # retrieve version and abi from library name + _python_get_version (SABI_LIBRARY PREFIX lib_) + + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT lib_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") + else() + if (NOT lib_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # library has wrong major version + set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Wrong major version for the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + if (WIN32) + set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_DEBUG-NOTFOUND") + endif() + unset (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE CACHE) + unset (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG CACHE) + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() +endfunction() + -function (_PYTHON_VALIDATE_COMPILER expected_version) - if (NOT ${_PYTHON_PREFIX}_COMPILER) +function (_PYTHON_VALIDATE_INCLUDE_DIR) + if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) return() endif() - # retrieve python environment version from compiler - set (working_dir "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") - file (WRITE "${working_dir}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:2]]))\n") - execute_process (COMMAND "${${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${working_dir}/version.py" - WORKING_DIRECTORY "${working_dir}" - OUTPUT_QUIET - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - execute_process (COMMAND "${working_dir}/version" - WORKING_DIRECTORY "${working_dir}" - RESULT_VARIABLE result - OUTPUT_VARIABLE version - ERROR_QUIET) - file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") + cmake_parse_arguments (PARSE_ARGV 0 _PVID "IN_RANGE;EXACT;CHECK_EXISTS" "VERSION" "") + + if (_PVID_CHECK_EXISTS AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") + # include file does not exist anymore + set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + return() + endif() + + # retrieve version from header file + _python_get_version (INCLUDE PREFIX inc_) + + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # incompatible ABI + set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + else() + if (_PVID_VERSION OR _PVID_IN_RANGE) + if (_PVID_VERSION) + if ((_PVID_EXACT AND NOT inc_VERSION VERSION_EQUAL expected_version) OR (inc_VERSION VERSION_LESS expected_version)) + # include dir has wrong version + set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + endif() - if (result OR NOT version EQUAL expected_version) - # Compiler not usable or has wrong major version - set (${_PYTHON_PREFIX}_COMPILER ${_PYTHON_PREFIX}_COMPILER-NOTFOUND CACHE INTERNAL "" FORCE) + if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND PVID_IN_RANGE) + # check if include dir is in the request range + find_package_check_version ("${inc_VERSION}" in_range HANDLE_VERSION_RANGE) + if (NOT in_range) + # include dir has wrong version + set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + endif() + else() + if (NOT inc_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # include dir has wrong major version + set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Wrong major version for the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + endif() + endif() endif() endfunction() @@ -186,25 +1279,70 @@ endfunction() function (_PYTHON_SET_LIBRARY_DIRS _PYTHON_SLD_RESULT) unset (_PYTHON_DIRS) - set (_PYTHON_LIBS ${ARGV}) - list (REMOVE_AT _PYTHON_LIBS 0) + set (_PYTHON_LIBS ${ARGN}) foreach (_PYTHON_LIB IN LISTS _PYTHON_LIBS) if (${_PYTHON_LIB}) get_filename_component (_PYTHON_DIR "${${_PYTHON_LIB}}" DIRECTORY) list (APPEND _PYTHON_DIRS "${_PYTHON_DIR}") endif() endforeach() - if (_PYTHON_DIRS) - list (REMOVE_DUPLICATES _PYTHON_DIRS) - endif() + list (REMOVE_DUPLICATES _PYTHON_DIRS) set (${_PYTHON_SLD_RESULT} ${_PYTHON_DIRS} PARENT_SCOPE) endfunction() -# If major version is specified, it must be the same as internal major version -if (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR - AND NOT ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) - _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong major version specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}\", but expected major version is \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") +function (_PYTHON_SET_DEVELOPMENT_MODULE_FOUND module) + if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + if (module STREQUAL "SABIModule" + AND "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_LESS "3.2") + # Stable API was introduced in version 3.2 + set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE PARENT_SCOPE) + _python_add_reason_failure ("Development" "SABIModule requires version 3.2 or upper.") + return() + endif() + + string(TOUPPER "${module}" id) + set (module_found TRUE) + + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + set (module_found FALSE) + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + set (module_found FALSE) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + set (module_found FALSE) + endif() + + set (${_PYTHON_PREFIX}_Development.${module}_FOUND ${module_found} PARENT_SCOPE) + endif() +endfunction() + + +if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + # range must include internal major version + if (${_PYTHON_PREFIX}_FIND_VERSION_MIN_MAJOR VERSION_GREATER _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR + OR ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND ${_PYTHON_PREFIX}_FIND_VERSION_MAX VERSION_LESS _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + OR (${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND ${_PYTHON_PREFIX}_FIND_VERSION_MAX VERSION_LESS_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR))) + _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong version range specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_RANGE}\", but expected version range must include major version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") + + cmake_policy(POP) + return() + endif() +else() + if (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR + AND NOT ${_PYTHON_PREFIX}_FIND_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + # If major version is specified, it must be the same as internal major version + _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Wrong major version specified is \"${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}\", but expected major version is \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") + + cmake_policy(POP) + return() + endif() endif() @@ -214,57 +1352,250 @@ if (NOT ${_PYTHON_PREFIX}_FIND_COMPONENTS) set (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter TRUE) endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) - list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Interpreter" "Development") - list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Interpreter" "Development.Module") endif() -foreach (_${_PYTHON_PREFIX}_COMPONENT IN LISTS ${_PYTHON_PREFIX}_FIND_COMPONENTS) +if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND ${_PYTHON_PREFIX}_FIND_COMPONENTS "Development.Module" "Development.Embed") +endif() +list (REMOVE_DUPLICATES ${_PYTHON_PREFIX}_FIND_COMPONENTS) +foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development Development.Module Development.SABIModule Development.Embed NumPy) set (${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_FOUND FALSE) endforeach() -unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development) + set (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Module TRUE) + set (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed TRUE) +endif() + +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) +unset (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) +if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "LIBRARY") + endif() + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS "INCLUDE_DIR") +endif() +if ("Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + if (CMAKE_SYSTEM_NAME MATCHES "^(Windows.*|CYGWIN|MSYS)$") + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS "SABI_LIBRARY") + endif() + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS "INCLUDE_DIR") +endif() +if ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + list (APPEND _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS "LIBRARY" "INCLUDE_DIR") +endif() +set (_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS} ${_${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS}) +list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) # Set versions to search ## default: search any version set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSIONS}) +unset (_${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + +if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) + if ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MIN STREQUAL "INCLUDE" + AND _${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION_MIN) + AND ((${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "INCLUDE" + AND _${_PYTHON_PREFIX}_VERSION VERSION_LESS_EQUAL ${_PYTHON_PREFIX}_FIND_VERSION_MAX) + OR (${_PYTHON_PREFIX}_FIND_VERSION_RANGE_MAX STREQUAL "EXCLUDE" + AND _${_PYTHON_PREFIX}_VERSION VERSION_LESS ${_PYTHON_PREFIX}_FIND_VERSION_MAX))) + list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) + endif() + endforeach() +else() + if (${_PYTHON_PREFIX}_FIND_VERSION_COUNT GREATER 1) + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + set (_${_PYTHON_PREFIX}_FIND_VERSION_EXACT "EXACT") + set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}) + else() + unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) + # add all compatible versions + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) + if (_${_PYTHON_PREFIX}_VERSION VERSION_GREATER_EQUAL "${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}") + list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) + endif() + endforeach() + endif() + endif() +endif() + +# Set ABIs to search +## default: search any ABI +if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS "3") + # ABI not supported + unset (_${_PYTHON_PREFIX}_FIND_ABI) + set (_${_PYTHON_PREFIX}_ABIFLAGS "") +else() + unset (_${_PYTHON_PREFIX}_FIND_ABI) + unset (_${_PYTHON_PREFIX}_ABIFLAGS) + if (DEFINED ${_PYTHON_PREFIX}_FIND_ABI) + # normalization + string (TOUPPER "${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_FIND_ABI) + list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(TRUE|Y(ES)?|1)$" "ON") + list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(FALSE|N(O)?|0)$" "OFF") + if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(ON|OFF|ANY);(ON|OFF|ANY)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_ABI}: invalid value for '${_PYTHON_PREFIX}_FIND_ABI'. Ignore it") + unset (_${_PYTHON_PREFIX}_FIND_ABI) + endif() + _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS) + endif() +endif() +unset (${_PYTHON_PREFIX}_SOABI) +unset (${_PYTHON_PREFIX}_SOSABI) -if (${_PYTHON_PREFIX}_FIND_VERSION_COUNT GREATER 1) - if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) - set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_FIND_VERSION_MAJOR}.${${_PYTHON_PREFIX}_FIND_VERSION_MINOR}) +# Define lookup strategy +cmake_policy (GET CMP0094 _${_PYTHON_PREFIX}_LOOKUP_POLICY) +if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW") + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "LOCATION") +else() + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") +endif() +if (DEFINED ${_PYTHON_PREFIX}_FIND_STRATEGY) + if (NOT ${_PYTHON_PREFIX}_FIND_STRATEGY MATCHES "^(VERSION|LOCATION)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_STRATEGY}: invalid value for '${_PYTHON_PREFIX}_FIND_STRATEGY'. 'VERSION' or 'LOCATION' expected.") + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "VERSION") else() - unset (_${_PYTHON_PREFIX}_FIND_VERSIONS) - # add all compatible versions - foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_VERSIONS) - if (NOT _${_PYTHON_PREFIX}_VERSION VERSION_LESS ${_PYTHON_PREFIX}_FIND_VERSION) - list (APPEND _${_PYTHON_PREFIX}_FIND_VERSIONS ${_${_PYTHON_PREFIX}_VERSION}) - endif() - endforeach() + set (_${_PYTHON_PREFIX}_FIND_STRATEGY "${${_PYTHON_PREFIX}_FIND_STRATEGY}") endif() endif() # Python and Anaconda distributions: define which architectures can be used +unset (_${_PYTHON_PREFIX}_REGISTRY_VIEW) if (CMAKE_SIZEOF_VOID_P) - # In this case, search only for 64bit or 32bit math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") - set (_${_PYTHON_PREFIX}_ARCH2 ${_${_PYTHON_PREFIX}_ARCH}) + if ("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + # In this case, search only for 64bit or 32bit + set (_${_PYTHON_PREFIX}_REGISTRY_VIEW REGISTRY_VIEW ${_${_PYTHON_PREFIX}_ARCH}) + if (WIN32 AND (NOT CMAKE_GENERATOR_PLATFORM AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM" + OR CMAKE_GENERATOR_PLATFORM MATCHES "ARM")) + # search exclusively ARM architecture: 64bit or 32bit + if (_${_PYTHON_PREFIX}_ARCH EQUAL 64) + set (_${_PYTHON_PREFIX}_ARCH ARM64) + else() + set (_${_PYTHON_PREFIX}_ARCH ARM) + endif() + endif() + else() + if (_${_PYTHON_PREFIX}_ARCH EQUAL "32") + if (CMAKE_SYSTEM_PROCESSOR MATCHES "ARM") + # search first ARM architectures: 32bit and then 64bit + list (PREPEND _${_PYTHON_PREFIX}_ARCH ARM ARM64) + endif() + list (APPEND _${_PYTHON_PREFIX}_ARCH 64) + else() + if (CMAKE_SYSTEM_PROCESSOR MATCHES "ARM") + # search first ARM architectures: 64bit and then 32bit + list (PREPEND _${_PYTHON_PREFIX}_ARCH ARM64 ARM) + endif() + list (APPEND _${_PYTHON_PREFIX}_ARCH 32) + endif() + endif() else() # architecture unknown, search for both 64bit and 32bit - set (_${_PYTHON_PREFIX}_ARCH 64) - set (_${_PYTHON_PREFIX}_ARCH2 32) + set (_${_PYTHON_PREFIX}_ARCH 64 32) + if (CMAKE_SYSTEM_PROCESSOR MATCHES "ARM") + list (PREPEND _${_PYTHON_PREFIX}_ARCH ARM64 ARM) + endif() endif() # IronPython support +unset (_${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES) +unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES) +unset (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS) if (CMAKE_SIZEOF_VOID_P) - # In this case, search only for 64bit or 32bit - math (EXPR _${_PYTHON_PREFIX}_ARCH "${CMAKE_SIZEOF_VOID_P} * 8") - set (_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES ipy${_${_PYTHON_PREFIX}_ARCH} ipy) + if (CMAKE_SIZEOF_VOID_P EQUAL "4") + set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x86") + else() + set (_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS "/platform:x64") + endif() +endif() +if (NOT CMAKE_SYSTEM_NAME STREQUAL "Linux") + # Do not use wrapper script on Linux because it is buggy: -c interpreter option cannot be used + list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES "ipy${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}" "ipy64" "ipy32" "ipy") + list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES "ipyc") +endif() +list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_INTERPRETER_NAMES "ipy.exe") +list (APPEND _${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_NAMES "ipyc.exe") +set (_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES net45 net40 bin) + +# PyPy support +if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR EQUAL "3") + set (_${_PYTHON_PREFIX}_PYPY_NAMES pypy3) + set (_${_PYTHON_PREFIX}_PYPY_LIB_NAMES pypy3-c) + if (WIN32) + # special name for runtime part + list (APPEND _${_PYTHON_PREFIX}_PYPY_LIB_NAMES libpypy3-c) + endif() + set (_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES lib/pypy3/include pypy3/include) +else() + set (_${_PYTHON_PREFIX}_PYPY_NAMES pypy) + set (_${_PYTHON_PREFIX}_PYPY_LIB_NAMES pypy-c) + if (WIN32) + # special name for runtime part + list (APPEND _${_PYTHON_PREFIX}_PYPY_LIB_NAMES libpypy-c) + endif() + set (_${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES lib/pypy/include pypy/include) +endif() +list (APPEND _${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES libexec/include) +set (_${_PYTHON_PREFIX}_PYPY_EXECUTABLE_PATH_SUFFIXES bin) +set (_${_PYTHON_PREFIX}_PYPY_LIBRARY_PATH_SUFFIXES lib libs bin) +list (APPEND _${_PYTHON_PREFIX}_PYPY_INCLUDE_PATH_SUFFIXES include) + +# Python Implementations handling +unset (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) +if (DEFINED ${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + foreach (_${_PYTHON_PREFIX}_IMPLEMENTATION IN LISTS ${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + if (NOT _${_PYTHON_PREFIX}_IMPLEMENTATION MATCHES "^(CPython|IronPython|PyPy)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${_${_PYTHON_PREFIX}_IMPLEMENTATION}: invalid value for '${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS'. 'CPython', 'IronPython' or 'PyPy' expected. Value will be ignored.") + else() + list (APPEND _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS ${_${_PYTHON_PREFIX}_IMPLEMENTATION}) + endif() + endforeach() else() - # architecture unknown, search for natural interpreter - set (_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES ipy) + if (WIN32) + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS CPython IronPython) + else() + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS CPython) + endif() endif() -set (_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES net45 net40) + +# compute list of names for header file +unset (_${_PYTHON_PREFIX}_INCLUDE_NAMES) +foreach (_${_PYTHON_PREFIX}_IMPLEMENTATION IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + if (_${_PYTHON_PREFIX}_IMPLEMENTATION STREQUAL "CPython") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_NAMES "Python.h") + elseif (_${_PYTHON_PREFIX}_IMPLEMENTATION STREQUAL "PyPy") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_NAMES "PyPy.h" "pypy_decl.h") + endif() +endforeach() + # Apple frameworks handling _python_find_frameworks () +set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK "FIRST") + +if (DEFINED ${_PYTHON_PREFIX}_FIND_FRAMEWORK) + if (NOT ${_PYTHON_PREFIX}_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}: invalid value for '${_PYTHON_PREFIX}_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") + else() + set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${${_PYTHON_PREFIX}_FIND_FRAMEWORK}) + endif() +elseif (DEFINED CMAKE_FIND_FRAMEWORK) + if (CMAKE_FIND_FRAMEWORK STREQUAL "ONLY") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: CMAKE_FIND_FRAMEWORK: 'ONLY' value is not supported. 'FIRST' will be used instead.") + elseif (NOT CMAKE_FIND_FRAMEWORK MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${CMAKE_FIND_FRAMEWORK}: invalid value for 'CMAKE_FIND_FRAMEWORK'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") + else() + set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) + endif() +endif() + # Save CMAKE_FIND_APPBUNDLE if (DEFINED CMAKE_FIND_APPBUNDLE) set (_${_PYTHON_PREFIX}_CMAKE_FIND_APPBUNDLE ${CMAKE_FIND_APPBUNDLE}) @@ -277,15 +1608,8 @@ set (CMAKE_FIND_APPBUNDLE "NEVER") # Save CMAKE_FIND_FRAMEWORK if (DEFINED CMAKE_FIND_FRAMEWORK) set (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) - if (CMAKE_FIND_FRAMEWORK STREQUAL "ONLY") - message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: CMAKE_FIND_FRAMEWORK: 'ONLY' value is not supported. 'FIRST' will be used instead.") - set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK "FIRST") - else() - set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK ${CMAKE_FIND_FRAMEWORK}) - endif() else() unset (_${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK) - set (_${_PYTHON_PREFIX}_FIND_FRAMEWORK "FIRST") endif() # To avoid framework lookup set (CMAKE_FIND_FRAMEWORK "NEVER") @@ -293,7 +1617,7 @@ set (CMAKE_FIND_FRAMEWORK "NEVER") # Windows Registry handling if (DEFINED ${_PYTHON_PREFIX}_FIND_REGISTRY) if (NOT ${_PYTHON_PREFIX}_FIND_REGISTRY MATCHES "^(FIRST|LAST|NEVER)$") - message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_REGISTRY}: invalid value for '${_PYTHON_PREFIX}_FIND_REGISTRY'. 'FIRST', 'LAST' or 'NEVER' expected.") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_REGISTRY}: invalid value for '${_PYTHON_PREFIX}_FIND_REGISTRY'. 'FIRST', 'LAST' or 'NEVER' expected. 'FIRST' will be used instead.") set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY ${${_PYTHON_PREFIX}_FIND_REGISTRY}) @@ -302,321 +1626,943 @@ else() set (_${_PYTHON_PREFIX}_FIND_REGISTRY "FIRST") endif() +# virtual environments recognition +if (DEFINED ENV{VIRTUAL_ENV} OR DEFINED ENV{CONDA_PREFIX}) + if (DEFINED ${_PYTHON_PREFIX}_FIND_VIRTUALENV) + if (NOT ${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY|STANDARD)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}: invalid value for '${_PYTHON_PREFIX}_FIND_VIRTUALENV'. 'FIRST', 'ONLY' or 'STANDARD' expected. 'FIRST' will be used instead.") + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV "FIRST") + else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV ${${_PYTHON_PREFIX}_FIND_VIRTUALENV}) + endif() + else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV FIRST) + endif() +else() + set (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STANDARD) +endif() + + +# Python naming handling +if (DEFINED ${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES) + if (NOT ${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES MATCHES "^(FIRST|LAST|NEVER)$") + message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}: invalid value for '${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES'. 'FIRST', 'LAST' or 'NEVER' expected. 'LAST' will be used instead.") + set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES LAST) + else() + set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES ${${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}) + endif() +else() + set (_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES LAST) +endif() + + +# Compute search signature +# This signature will be used to check validity of cached variables on new search +set (_${_PYTHON_PREFIX}_SIGNATURE "${${_PYTHON_PREFIX}_ROOT_DIR}:${_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS}:${_${_PYTHON_PREFIX}_FIND_STRATEGY}:${${_PYTHON_PREFIX}_FIND_VIRTUALENV}${_${_PYTHON_PREFIX}_FIND_UNVERSIONED_NAMES}") +if (NOT WIN32) + string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${${_PYTHON_PREFIX}_USE_STATIC_LIBS}:") +endif() +if (CMAKE_HOST_APPLE) + string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${_${_PYTHON_PREFIX}_FIND_FRAMEWORK}") +endif() +if (CMAKE_HOST_WIN32) + string (APPEND _${_PYTHON_PREFIX}_SIGNATURE ":${_${_PYTHON_PREFIX}_FIND_REGISTRY}") +endif() + +function (_PYTHON_CHECK_DEVELOPMENT_SIGNATURE module) + if ("Development.${module}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + string (TOUPPER "${module}" id) + set (signature "${_${_PYTHON_PREFIX}_SIGNATURE}:") + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:") + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}:") + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:") + endif() + string (MD5 signature "${signature}") + if (signature STREQUAL _${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_library (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_library (IN_RANGE CHECK_EXISTS) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_library (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_library (CHECK_EXISTS) + endif() + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + _python_validate_sabi_library (CHECK_EXISTS) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_include_dir (IN_RANGE CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_include_dir (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_include_dir (CHECK_EXISTS) + endif() + endif() + else() + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + unset (_${_PYTHON_PREFIX}_LIBRARY_RELEASE CACHE) + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE CACHE) + unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) + endif() + endif() + if (("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + OR ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + OR ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS + AND NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE CACHE) + endif() + endif() +endfunction() + +function (_PYTHON_COMPUTE_DEVELOPMENT_SIGNATURE module) + string (TOUPPER "${module}" id) + if (${_PYTHON_PREFIX}_Development.${module}_FOUND) + set (signature "${_${_PYTHON_PREFIX}_SIGNATURE}:") + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:") + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}:") + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${id}_ARTIFACTS) + list (APPEND signature "${_${_PYTHON_PREFIX}_INCLUDE_DIR}:") + endif() + string (MD5 signature "${signature}") + set (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE "${signature}" CACHE INTERNAL "") + else() + unset (_${_PYTHON_PREFIX}_DEVELOPMENT_${id}_SIGNATURE CACHE) + endif() +endfunction() unset (_${_PYTHON_PREFIX}_REQUIRED_VARS) unset (_${_PYTHON_PREFIX}_CACHED_VARS) +unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE INTERNAL "Interpreter reason failure") +unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE INTERNAL "Compiler reason failure") +foreach (artifact IN LISTS _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + unset (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE) + set (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE CACHE INTERNAL "Development ${artifact} reason failure") +endforeach() +unset (_${_PYTHON_PREFIX}_Development_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_Development_REASON_FAILURE CACHE INTERNAL "Development reason failure") +unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE) +set (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE INTERNAL "NumPy reason failure") + + +# preamble +## For IronPython on platforms other than Windows, search for the .Net interpreter +if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS + AND NOT WIN32) + find_program (${_PYTHON_PREFIX}_DOTNET_LAUNCHER + NAMES "mono") +endif() # first step, search for the interpreter if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) - list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_EXECUTABLE + _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Interpreter) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_EXECUTABLE) endif() - set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + if (DEFINED ${_PYTHON_PREFIX}_EXECUTABLE + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_EXECUTABLE}") + if (NOT ${_PYTHON_PREFIX}_EXECUTABLE STREQUAL _${_PYTHON_PREFIX}_EXECUTABLE) + # invalidate cache properties + unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) + endif() + set (_${_PYTHON_PREFIX}_EXECUTABLE "${${_PYTHON_PREFIX}_EXECUTABLE}" CACHE INTERNAL "") + elseif (DEFINED _${_PYTHON_PREFIX}_EXECUTABLE) + # compute interpreter signature and check validity of definition + string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") + if (__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) + # check version validity + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_interpreter (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_interpreter (IN_RANGE CHECK_EXISTS) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_interpreter (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_interpreter (CHECK_EXISTS) + endif() + else() + unset (_${_PYTHON_PREFIX}_EXECUTABLE CACHE) + endif() + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) + unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) + endif() + endif() - # look-up for various versions and locations - foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) - string (REPLACE "." "" _${_PYTHON_PREFIX}_VERSION_NO_DOTS ${_${_PYTHON_PREFIX}_VERSION}) + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) - _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION}) + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + # build all executable names + _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} POSIX INTERPRETER) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} INTERPRETER) - # Apple frameworks handling - if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - NAMES_PER_DIR - PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} - PATH_SUFFIXES bin - NO_CMAKE_PATH - NO_CMAKE_ENVIRONMENT_PATH - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - endif() + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + # Registry Paths + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) - # Windows registry - if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - python - ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATHS [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - endif() + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS ${_${_PYTHON_PREFIX}_FIND_VERSION_EXACT}) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${${_PYTHON_PREFIX}_FIND_VERSION}) + endif() - # try using HINTS - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - python - ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - # try using standard paths. - if (WIN32) - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - python - ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} - NAMES_PER_DIR) + while (TRUE) + # Virtual environments handling + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + break() + endif() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + # try using standard paths + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endif() + + break() + endwhile() else() - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - NAMES_PER_DIR) - endif() + # look-up for various versions and locations + set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE) + endif() - # Apple frameworks handling - if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - NAMES_PER_DIR - PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} - PATH_SUFFIXES bin - NO_DEFAULT_PATH) - endif() + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_names (_${_PYTHON_PREFIX}_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX INTERPRETER) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INTERPRETER) - # Windows registry - if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_VERSION} - python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - python - ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} - NAMES_PER_DIR - PATHS [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_DEFAULT_PATH) - endif() + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS}) - _python_validate_interpreter (${_${_PYTHON_PREFIX}_VERSION}) - if (${_PYTHON_PREFIX}_EXECUTABLE) - break() - endif() - endforeach() + # Virtual environments handling + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + continue() + endif() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + endif() + + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + endif() + + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # try using standard paths. + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_interpreter) + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) + endif() - if (NOT ${_PYTHON_PREFIX}_EXECUTABLE) - # No specific version found. Retry with generic names - # try using HINTS - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - python - ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES} - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES bin ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - # try using standard paths. - # NAMES_PER_DIR is not defined on purpose to have a chance to find - # expected version. - # For example, typical systems have 'python' for version 2.* and 'python3' - # for version 3.*. So looking for names per dir will find, potentially, - # systematically 'python' (i.e. version 2) even if version 3 is searched. - find_program (${_PYTHON_PREFIX}_EXECUTABLE - NAMES python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} - python - ${_${_PYTHON_PREFIX}_IRON_PYTHON_NAMES}) - - _python_validate_interpreter () + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_interpreter) + endif() + + if (_${_PYTHON_PREFIX}_EXECUTABLE) + break() + endif() + endforeach() + + if (NOT _${_PYTHON_PREFIX}_EXECUTABLE AND + NOT _${_PYTHON_PREFIX}_FIND_VIRTUALENV STREQUAL "ONLY") + # No specific version found. Retry with generic names and standard paths. + _python_get_names (_${_PYTHON_PREFIX}_NAMES POSIX INTERPRETER) + unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS) + find_program (_${_PYTHON_PREFIX}_EXECUTABLE + NAMES ${_${_PYTHON_PREFIX}_NAMES} + NAMES_PER_DIR + VALIDATOR _python_validate_find_interpreter) + endif() + endif() endif() + set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}") + _python_get_launcher (_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER INTERPRETER) + # retrieve exact version of executable found - if (${_PYTHON_PREFIX}_EXECUTABLE) - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c + if (_${_PYTHON_PREFIX}_EXECUTABLE) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE ${_PYTHON_PREFIX}_VERSION ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (NOT _${_PYTHON_PREFIX}_RESULT) + set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE TRUE) + else() + # Interpreter is not usable + set (_${_PYTHON_PREFIX}_EXECUTABLE_USABLE FALSE) + unset (${_PYTHON_PREFIX}_VERSION) + set_property (CACHE _${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE PROPERTY VALUE "Cannot run the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"") + endif() + endif() + + if (_${_PYTHON_PREFIX}_EXECUTABLE AND _${_PYTHON_PREFIX}_EXECUTABLE_USABLE) + list (LENGTH _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES _properties_length) + if (NOT _properties_length EQUAL "12") + # cache variable comes from some older Python module version: not usable + unset (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES CACHE) + endif() + unset (_properties_length) + + if (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES) + set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 0 ${_PYTHON_PREFIX}_INTERPRETER_ID) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 1 ${_PYTHON_PREFIX}_VERSION_MAJOR) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 2 ${_PYTHON_PREFIX}_VERSION_MINOR) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 3 ${_PYTHON_PREFIX}_VERSION_PATCH) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 4 _${_PYTHON_PREFIX}_ARCH) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 5 _${_PYTHON_PREFIX}_ABIFLAGS) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 6 ${_PYTHON_PREFIX}_SOABI) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 7 ${_PYTHON_PREFIX}_SOSABI) + + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 8 ${_PYTHON_PREFIX}_STDLIB) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 9 ${_PYTHON_PREFIX}_STDARCH) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 10 ${_PYTHON_PREFIX}_SITELIB) + list (GET _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES 11 ${_PYTHON_PREFIX}_SITEARCH) + else() string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 ${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 ${_PYTHON_PREFIX}_VERSION_MINOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 2 ${_PYTHON_PREFIX}_VERSION_PATCH) - else() - # Interpreter is not usable - set (${_PYTHON_PREFIX}_EXECUTABLE ${_PYTHON_PREFIX}_EXECUTABLE-NOTFOUND CACHE INTERNAL "" FORCE) - unset (${_PYTHON_PREFIX}_VERSION) - endif() - endif() - - if (${_PYTHON_PREFIX}_EXECUTABLE - AND ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) - set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) - # Use interpreter version for future searches to ensure consistency - set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) - endif() - if (${_PYTHON_PREFIX}_Interpreter_FOUND) - if (NOT CMAKE_SIZEOF_VOID_P) - # determine interpreter architecture - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.maxsize > 2**32)" - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE ${_PYTHON_PREFIX}_IS64BIT - ERROR_VARIABLE ${_PYTHON_PREFIX}_IS64BIT) - if (NOT _${_PYTHON_PREFIX}_RESULT) - if (${_PYTHON_PREFIX}_IS64BIT) - set (_${_PYTHON_PREFIX}_ARCH 64) - set (_${_PYTHON_PREFIX}_ARCH2 64) - else() - set (_${_PYTHON_PREFIX}_ARCH 32) - set (_${_PYTHON_PREFIX}_ARCH2 32) + if (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) + set (${_PYTHON_PREFIX}_Interpreter_FOUND TRUE) + + # Use interpreter version and ABI for future searches to ensure consistency + set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(sys.abiflags)" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_ABIFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assunme ABI is not supported + set (_${_PYTHON_PREFIX}_ABIFLAGS "") endif() endif() - endif() - # retrieve interpreter identity - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -V - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID - ERROR_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID) - if (NOT _${_PYTHON_PREFIX}_RESULT) - if (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Anaconda") - set (${_PYTHON_PREFIX}_INTERPRETER_ID "Anaconda") - elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Enthought") - set (${_PYTHON_PREFIX}_INTERPRETER_ID "Canopy") - else() - string (REGEX REPLACE "^([^ ]+).*" "\\1" ${_PYTHON_PREFIX}_INTERPRETER_ID "${${_PYTHON_PREFIX}_INTERPRETER_ID}") - if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "Python") - # try to get a more precise ID - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; print(sys.copyright)" + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + unset (_${_PYTHON_PREFIX}_Interpreter_REASON_FAILURE CACHE) + + # compute and save interpreter signature + string (MD5 __${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_EXECUTABLE}") + set (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE "${__${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}" CACHE INTERNAL "") + + if (NOT CMAKE_SIZEOF_VOID_P) + # determine interpreter architecture + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(str(sys.maxsize > 2**32))" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE ${_PYTHON_PREFIX}_COPYRIGHT - ERROR_QUIET) - if (${_PYTHON_PREFIX}_COPYRIGHT MATCHES "ActiveState") - set (${_PYTHON_PREFIX}_INTERPRETER_ID "ActivePython") + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_IS64BIT + ERROR_VARIABLE ${_PYTHON_PREFIX}_IS64BIT) + if (NOT _${_PYTHON_PREFIX}_RESULT) + if (${_PYTHON_PREFIX}_IS64BIT) + set (_${_PYTHON_PREFIX}_ARCH 64) + else() + set (_${_PYTHON_PREFIX}_ARCH 32) + endif() + endif() + + if (WIN32) + # check if architecture is Intel or ARM + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; import sysconfig; sys.stdout.write(sysconfig.get_platform())" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PLATFORM + ERROR_VARIABLE ${_PYTHON_PREFIX}_PLATFORM) + if (NOT _${_PYTHON_PREFIX}_RESULT) + string(TOUPPER "${_${_PYTHON_PREFIX}_PLATFORM}" _${_PYTHON_PREFIX}_PLATFORM) + if (_${_PYTHON_PREFIX}_PLATFORM MATCHES "ARM") + if (${_PYTHON_PREFIX}_IS64BIT) + set (_${_PYTHON_PREFIX}_ARCH ARM64) + else() + set (_${_PYTHON_PREFIX}_ARCH ARM) + endif() + endif() + endif() + endif() + endif() + + # retrieve interpreter identity + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -V + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID + ERROR_VARIABLE ${_PYTHON_PREFIX}_INTERPRETER_ID) + if (NOT _${_PYTHON_PREFIX}_RESULT) + if (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Anaconda") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "Anaconda") + elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "Enthought") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "Canopy") + elseif (${_PYTHON_PREFIX}_INTERPRETER_ID MATCHES "PyPy ([0-9.]+)") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "PyPy") + set (${_PYTHON_PREFIX}_PyPy_VERSION "${CMAKE_MATCH_1}") + else() + string (REGEX REPLACE "^([^ ]+).*" "\\1" ${_PYTHON_PREFIX}_INTERPRETER_ID "${${_PYTHON_PREFIX}_INTERPRETER_ID}") + if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "Python") + # try to get a more precise ID + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys; sys.stdout.write(sys.copyright)" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE ${_PYTHON_PREFIX}_COPYRIGHT + ERROR_QUIET) + if (${_PYTHON_PREFIX}_COPYRIGHT MATCHES "ActiveState") + set (${_PYTHON_PREFIX}_INTERPRETER_ID "ActivePython") + endif() + endif() endif() + else() + set (${_PYTHON_PREFIX}_INTERPRETER_ID Python) + endif() + + # retrieve various package installation directories + execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\nif sys.version_info >= (3,10):\n import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_path('stdlib'),sysconfig.get_path('platstdlib'),sysconfig.get_path('purelib'),sysconfig.get_path('platlib')]))\nelse:\n from distutils import sysconfig\n sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS + ERROR_QUIET) + if (NOT _${_PYTHON_PREFIX}_RESULT) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB) + list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH) + else() + unset (${_PYTHON_PREFIX}_STDLIB) + unset (${_PYTHON_PREFIX}_STDARCH) + unset (${_PYTHON_PREFIX}_SITELIB) + unset (${_PYTHON_PREFIX}_SITEARCH) endif() + + _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) + _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI) + + # store properties in the cache to speed-up future searches + set (_${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES + "${${_PYTHON_PREFIX}_INTERPRETER_ID};${${_PYTHON_PREFIX}_VERSION_MAJOR};${${_PYTHON_PREFIX}_VERSION_MINOR};${${_PYTHON_PREFIX}_VERSION_PATCH};${_${_PYTHON_PREFIX}_ARCH};${_${_PYTHON_PREFIX}_ABIFLAGS};${${_PYTHON_PREFIX}_SOABI};${${_PYTHON_PREFIX}_SOSABI};${${_PYTHON_PREFIX}_STDLIB};${${_PYTHON_PREFIX}_STDARCH};${${_PYTHON_PREFIX}_SITELIB};${${_PYTHON_PREFIX}_SITEARCH}" CACHE INTERNAL "${_PYTHON_PREFIX} Properties") + else() + unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE) + unset (${_PYTHON_PREFIX}_INTERPRETER_ID) endif() - else() - set (${_PYTHON_PREFIX}_INTERPRETER_ID Python) endif() - else() - unset (${_PYTHON_PREFIX}_INTERPRETER_ID) endif() - # retrieve various package installation directories - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_python_lib(plat_specific=False,standard_lib=True),sysconfig.get_python_lib(plat_specific=True,standard_lib=True),sysconfig.get_python_lib(plat_specific=False,standard_lib=False),sysconfig.get_python_lib(plat_specific=True,standard_lib=False)]))" - - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_LIBPATHS - ERROR_QUIET) - if (NOT _${_PYTHON_PREFIX}_RESULT) - list (GET _${_PYTHON_PREFIX}_LIBPATHS 0 ${_PYTHON_PREFIX}_STDLIB) - list (GET _${_PYTHON_PREFIX}_LIBPATHS 1 ${_PYTHON_PREFIX}_STDARCH) - list (GET _${_PYTHON_PREFIX}_LIBPATHS 2 ${_PYTHON_PREFIX}_SITELIB) - list (GET _${_PYTHON_PREFIX}_LIBPATHS 3 ${_PYTHON_PREFIX}_SITEARCH) - else() - unset (${_PYTHON_PREFIX}_STDLIB) - unset (${_PYTHON_PREFIX}_STDARCH) - unset (${_PYTHON_PREFIX}_SITELIB) - unset (${_PYTHON_PREFIX}_SITEARCH) + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + set (${_PYTHON_PREFIX}_EXECUTABLE "${_${_PYTHON_PREFIX}_EXECUTABLE}" CACHE FILEPATH "${_PYTHON_PREFIX} Interpreter") endif() - mark_as_advanced (${_PYTHON_PREFIX}_EXECUTABLE) + _python_mark_as_internal (_${_PYTHON_PREFIX}_EXECUTABLE + _${_PYTHON_PREFIX}_INTERPRETER_PROPERTIES + _${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE) endif() # second step, search for compiler (IronPython) if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) - list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_COMPILER) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_COMPILER) if (${_PYTHON_PREFIX}_FIND_REQUIRED_Compiler) list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_COMPILER) endif() - # IronPython specific artifacts - # If IronPython interpreter is found, use its path - unset (_${_PYTHON_PREFIX}_IRON_ROOT) - if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") - get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY) + if (NOT "IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + unset (_${_PYTHON_PREFIX}_COMPILER CACHE) + unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) + elseif (DEFINED ${_PYTHON_PREFIX}_COMPILER + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_COMPILER}") + set (_${_PYTHON_PREFIX}_COMPILER "${${_PYTHON_PREFIX}_COMPILER}" CACHE INTERNAL "") + elseif (DEFINED _${_PYTHON_PREFIX}_COMPILER) + # compute compiler signature and check validity of definition + string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") + if (__${_PYTHON_PREFIX}_COMPILER_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_COMPILER_SIGNATURE) + # check version validity + if (${_PYTHON_PREFIX}_FIND_VERSION_EXACT) + _python_validate_compiler (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} EXACT CHECK_EXISTS) + elseif (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + _python_validate_compiler (IN_RANGE CHECK_EXISTS) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + _python_validate_compiler (VERSION ${${_PYTHON_PREFIX}_FIND_VERSION} CHECK_EXISTS) + else() + _python_validate_compiler (CHECK_EXISTS) + endif() + else() + unset (_${_PYTHON_PREFIX}_COMPILER CACHE) + unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) + endif() endif() - # try using root dir and registry - foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) - if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") - find_program (${_PYTHON_PREFIX}_COMPILER - NAMES ipyc - HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} - PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) + if ("IronPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS + AND NOT _${_PYTHON_PREFIX}_COMPILER) + # IronPython specific artifacts + # If IronPython interpreter is found, use its path + unset (_${_PYTHON_PREFIX}_IRON_ROOT) + if (${_PYTHON_PREFIX}_Interpreter_FOUND AND ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") + get_filename_component (_${_PYTHON_PREFIX}_IRON_ROOT "${${_PYTHON_PREFIX}_EXECUTABLE}" DIRECTORY) endif() - find_program (${_PYTHON_PREFIX}_COMPILER - NAMES ipyc - HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS ${_${_PYTHON_PREFIX}_FIND_VERSION_EXACT}) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS IN_RANGE) + elseif (DEFINED ${_PYTHON_PREFIX}_FIND_VERSION) + list (APPEND _${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${${_PYTHON_PREFIX}_FIND_VERSION}) + endif() - if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") - find_program (${_PYTHON_PREFIX}_COMPILER - NAMES ipyc - PATHS [HKEY_LOCAL_MACHINE\\SOFTWARE\\IronPython\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES} - NO_DEFAULT_PATH) - endif() + while (TRUE) + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() - _python_validate_compiler (${_${_PYTHON_PREFIX}_VERSION}) - if (${_PYTHON_PREFIX}_COMPILER) - break() + # try using HINTS + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + + # try using standard paths + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + + break() + endwhile() + else() + # try using root dir and registry + set (_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS EXACT) + if (${_PYTHON_PREFIX}_FIND_VERSION_RANGE) + list (APPEND _${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS IN_RANGE) + endif() + + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSION} + COMPILER) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_VERSION}) + + set (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS VERSION ${_${_PYTHON_PREFIX}_VERSION} ${_${_PYTHON_PREFIX}_COMMON_VALIDATE_OPTIONS}) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + + # try using HINTS + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + # Windows registry + if (CMAKE_HOST_WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + ${_${_PYTHON_PREFIX}_REGISTRY_VIEW} + NO_DEFAULT_PATH + VALIDATOR _python_validate_find_compiler) + if (_${_PYTHON_PREFIX}_COMPILER) + break() + endif() + endif() + endforeach() + + # no specific version found, re-try in standard paths + _python_get_names (_${_PYTHON_PREFIX}_COMPILER_NAMES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES + IMPLEMENTATIONS IronPython + VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} + COMPILER) + unset (_${_PYTHON_PREFIX}_VALIDATE_OPTIONS) + find_program (_${_PYTHON_PREFIX}_COMPILER + NAMES ${_${_PYTHON_PREFIX}_COMPILER_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + VALIDATOR _python_validate_find_compiler) endif() - endforeach() + endif() - # no specific version found, re-try in standard paths - find_program (${_PYTHON_PREFIX}_COMPILER - NAMES ipyc - HINTS ${_${_PYTHON_PREFIX}_IRON_ROOT} ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES ${_${_PYTHON_PREFIX}_IRON_PYTHON_PATH_SUFFIXES}) + set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}") - if (${_PYTHON_PREFIX}_COMPILER) + if (_${_PYTHON_PREFIX}_COMPILER) # retrieve python environment version from compiler + _python_get_launcher (_${_PYTHON_PREFIX}_COMPILER_LAUNCHER COMPILER) set (_${_PYTHON_PREFIX}_VERSION_DIR "${CMAKE_CURRENT_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/PythonCompilerVersion.dir") - file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))\n") - execute_process (COMMAND "${${_PYTHON_PREFIX}_COMPILER}" /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" + file (WRITE "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]])); sys.stdout.flush()\n") + execute_process (COMMAND ${_${_PYTHON_PREFIX}_COMPILER_LAUNCHER} "${_${_PYTHON_PREFIX}_COMPILER}" + ${_${_PYTHON_PREFIX}_IRON_PYTHON_COMPILER_ARCH_FLAGS} + /target:exe /embed "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.py" WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" OUTPUT_QUIET ERROR_QUIET) - execute_process (COMMAND "${_${_PYTHON_PREFIX}_VERSION_DIR}/version" + get_filename_component (_${_PYTHON_PREFIX}_IR_DIR "${_${_PYTHON_PREFIX}_COMPILER}" DIRECTORY) + execute_process (COMMAND "${CMAKE_COMMAND}" -E env "MONO_PATH=${_${_PYTHON_PREFIX}_IR_DIR}" + ${${_PYTHON_PREFIX}_DOTNET_LAUNCHER} "${_${_PYTHON_PREFIX}_VERSION_DIR}/version.exe" WORKING_DIRECTORY "${_${_PYTHON_PREFIX}_VERSION_DIR}" RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT OUTPUT_VARIABLE _${_PYTHON_PREFIX}_VERSION ERROR_QUIET) if (NOT _${_PYTHON_PREFIX}_RESULT) + set (_${_PYTHON_PREFIX}_COMPILER_USABLE TRUE) string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}") list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR) list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR) @@ -631,47 +2577,124 @@ if ("Compiler" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) endif() else() # compiler not usable - set (${_PYTHON_PREFIX}_COMPILER ${_PYTHON_PREFIX}_COMPILER-NOTFOUND CACHE INTERNAL "" FORCE) + set (_${_PYTHON_PREFIX}_COMPILER_USABLE FALSE) + set_property (CACHE _${_PYTHON_PREFIX}_Compiler_REASON_FAILURE PROPERTY VALUE "Cannot run the compiler \"${_${_PYTHON_PREFIX}_COMPILER}\"") endif() file (REMOVE_RECURSE "${_${_PYTHON_PREFIX}_VERSION_DIR}") endif() - if (${_PYTHON_PREFIX}_COMPILER) + if (_${_PYTHON_PREFIX}_COMPILER AND _${_PYTHON_PREFIX}_COMPILER_USABLE) if (${_PYTHON_PREFIX}_Interpreter_FOUND) # Compiler must be compatible with interpreter - if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) + if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) endif() elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) set (${_PYTHON_PREFIX}_Compiler_FOUND TRUE) - # Use compiler version for future searches to ensure consistency - set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) + # Use compiler version for future searches to ensure consistency + set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) endif() endif() if (${_PYTHON_PREFIX}_Compiler_FOUND) + unset (_${_PYTHON_PREFIX}_Compiler_REASON_FAILURE CACHE) + + # compute and save compiler signature + string (MD5 __${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_COMPILER}") + set (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE "${__${_PYTHON_PREFIX}_COMPILER_SIGNATURE}" CACHE INTERNAL "") + set (${_PYTHON_PREFIX}_COMPILER_ID IronPython) else() + unset (_${_PYTHON_PREFIX}_COMPILER_SIGNATURE CACHE) unset (${_PYTHON_PREFIX}_COMPILER_ID) endif() - mark_as_advanced (${_PYTHON_PREFIX}_COMPILER) -endif() + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + set (${_PYTHON_PREFIX}_COMPILER "${_${_PYTHON_PREFIX}_COMPILER}" CACHE FILEPATH "${_PYTHON_PREFIX} Compiler") + endif() + _python_mark_as_internal (_${_PYTHON_PREFIX}_COMPILER + _${_PYTHON_PREFIX}_COMPILER_SIGNATURE) +endif() # third step, search for the development artifacts +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Module) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) + endif() +endif() +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.SABIModule) + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_SABI_LIBRARIES) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) + endif() +endif() +if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development.Embed) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARIES) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_EMBED_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_INCLUDE_DIRS) + endif() +endif() +list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_REQUIRED_VARS) ## Development environment is not compatible with IronPython interpreter -if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS - AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") - list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_LIBRARY - ${_PYTHON_PREFIX}_LIBRARY_RELEASE - ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE - ${_PYTHON_PREFIX}_LIBRARY_DEBUG - ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG - ${_PYTHON_PREFIX}_INCLUDE_DIR) - if (${_PYTHON_PREFIX}_FIND_REQUIRED_Development) - list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_LIBRARY - ${_PYTHON_PREFIX}_INCLUDE_DIR) +if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS) + AND ((${_PYTHON_PREFIX}_Interpreter_FOUND + AND NOT ${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "IronPython") + OR NOT ${_PYTHON_PREFIX}_Interpreter_FOUND)) + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + # reduce possible implementations to the interpreter one + if (${_PYTHON_PREFIX}_INTERPRETER_ID STREQUAL "PyPy") + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "PyPy") + else() + set (_${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "CPython") + endif() + else() + list (REMOVE_ITEM _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS "IronPython") + endif() + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_INCLUDE_DIR) + endif() + + _python_check_development_signature (Module) + _python_check_development_signature (SABIModule) + _python_check_development_signature (Embed) + + if (DEFINED ${_PYTHON_PREFIX}_LIBRARY + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_LIBRARY}") + set (_${_PYTHON_PREFIX}_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_LIBRARY}" CACHE INTERNAL "") + unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE) + unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) + endif() + if (DEFINED ${_PYTHON_PREFIX}_SABI_LIBRARY + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_SABI_LIBRARY}") + set (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_SABI_LIBRARY}" CACHE INTERNAL "") + unset (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG CACHE) + unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE) + endif() + if (DEFINED ${_PYTHON_PREFIX}_INCLUDE_DIR + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_INCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_INCLUDE_DIR "${${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE INTERNAL "") endif() # Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES @@ -683,345 +2706,804 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS else() list (REMOVE_ITEM CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_STATIC_LIBRARY_SUFFIX}) endif() - else() endif() - # if python interpreter is found, use its location and version to ensure consistency - # between interpreter and development environment - unset (_${_PYTHON_PREFIX}_PREFIX) - if (${_PYTHON_PREFIX}_Interpreter_FOUND) - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c - "import sys; from distutils import sysconfig; sys.stdout.write(sysconfig.PREFIX)" - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PREFIX - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (_${_PYTHON_PREFIX}_RESULT) - unset (_${_PYTHON_PREFIX}_PREFIX) + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + # if python interpreter is found, use it to look-up for artifacts + # to ensure consistency between interpreter and development environments. + # If not, try to locate a compatible config tool + if ((NOT ${_PYTHON_PREFIX}_Interpreter_FOUND OR CMAKE_CROSSCOMPILING) + AND "CPython" IN_LIST _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} POSIX CONFIG) + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS}) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES bin) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_DEFAULT_PATH) + endif() + + _python_get_launcher (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER CONFIG "${_${_PYTHON_PREFIX}_CONFIG}") + + if (_${_PYTHON_PREFIX}_CONFIG) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --prefix + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP + ERROR_VARIABLE __${_PYTHON_PREFIX}_HELP + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume config tool is not usable + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + endif() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --abiflags + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume ABI is not supported + set (__${_PYTHON_PREFIX}_ABIFLAGS "") + endif() + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # Wrong ABI + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + endif() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + # check that config tool match library architecture + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --configdir + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + else() + string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) + if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + endif() + endif() + endif() + else() + foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + # try to use pythonX.Y-config tool + _python_get_names (_${_PYTHON_PREFIX}_CONFIG_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} POSIX CONFIG) + + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES bin) + + # Apple frameworks handling + if (CMAKE_HOST_APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + find_program (_${_PYTHON_PREFIX}_CONFIG + NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} + NAMES_PER_DIR + PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES bin + NO_DEFAULT_PATH) + endif() + + unset (_${_PYTHON_PREFIX}_CONFIG_NAMES) + + _python_get_launcher (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER CONFIG "${_${_PYTHON_PREFIX}_CONFIG}") + + if (_${_PYTHON_PREFIX}_CONFIG) + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --prefix + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_HELP + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume config tool is not usable + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_CONFIG) + continue() + endif() + + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --abiflags + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE __${_PYTHON_PREFIX}_ABIFLAGS + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + # assume ABI is not supported + set (__${_PYTHON_PREFIX}_ABIFLAGS "") + endif() + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS) + # Wrong ABI + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + continue() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG AND DEFINED CMAKE_LIBRARY_ARCHITECTURE) + # check that config tool match library architecture + execute_process (COMMAND ${_${_PYTHON_PREFIX}_CONFIG_LAUNCHER} --configdir + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (_${_PYTHON_PREFIX}_RESULT) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + continue() + endif() + string (FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) + if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) + unset (_${_PYTHON_PREFIX}_CONFIG CACHE) + unset (_${_PYTHON_PREFIX}_CONFIG_LAUNCHER) + continue() + endif() + endif() + + if (_${_PYTHON_PREFIX}_CONFIG) + break() + endif() + endforeach() + endif() endif() endif() - set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}" "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) - foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) - string (REPLACE "." "" _${_PYTHON_PREFIX}_VERSION_NO_DOTS ${_${_PYTHON_PREFIX}_VERSION}) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) + # retrieve root install directory + _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX) - # try to use pythonX.Y-config tool - set (_${_PYTHON_PREFIX}_CONFIG_NAMES) - if (DEFINED CMAKE_LIBRARY_ARCHITECTURE) - set (_${_PYTHON_PREFIX}_CONFIG_NAMES "${CMAKE_LIBRARY_ARCHITECTURE}-python${_${_PYTHON_PREFIX}_VERSION}-config") - endif() - list (APPEND _${_PYTHON_PREFIX}_CONFIG_NAMES "python${_${_PYTHON_PREFIX}_VERSION}-config") - find_program (_${_PYTHON_PREFIX}_CONFIG - NAMES ${_${_PYTHON_PREFIX}_CONFIG_NAMES} - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES bin) - unset (_${_PYTHON_PREFIX}_CONFIG_NAMES) + # enforce current ABI + _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS) - if (NOT _${_PYTHON_PREFIX}_CONFIG) - continue() - endif() - if (DEFINED CMAKE_LIBRARY_ARCHITECTURE) - # check that config tool match library architecture - execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --configdir - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_CONFIGDIR - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (_${_PYTHON_PREFIX}_RESULT) - unset (_${_PYTHON_PREFIX}_CONFIG CACHE) - continue() + set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") + + # retrieve library + ## compute some paths and artifact names + if (_${_PYTHON_PREFIX}_CONFIG) + string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}") + else() + set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") + endif() + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY) + + _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR) + list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}") + + list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts + if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + # library names + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} WIN32 POSIX LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} WIN32 DEBUG) + # Paths suffixes + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} LIBRARY) + + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_FIND_VERSIONS}) + # Registry Paths + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} ) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search in HINTS locations + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + # search in all default paths + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + else() + foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 POSIX LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} WIN32 DEBUG) + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search in HINTS locations + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + # search in all default paths + find_library (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + break() + endif() + endforeach() + endif() endif() - string(FIND "${_${_PYTHON_PREFIX}_CONFIGDIR}" "${CMAKE_LIBRARY_ARCHITECTURE}" _${_PYTHON_PREFIX}_RESULT) - if (_${_PYTHON_PREFIX}_RESULT EQUAL -1) - unset (_${_PYTHON_PREFIX}_CONFIG CACHE) - continue() + endif() + + # finalize library version information + _python_get_version (LIBRARY PREFIX _${_PYTHON_PREFIX}_) + if (_${_PYTHON_PREFIX}_VERSION EQUAL "${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}") + # not able to extract full version from library name + if (${_PYTHON_PREFIX}_Interpreter_FOUND) + # update from interpreter + set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) endif() endif() - # retrieve root install directory - execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --prefix - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PREFIX - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (_${_PYTHON_PREFIX}_RESULT) - # python-config is not usable - unset (_${_PYTHON_PREFIX}_CONFIG CACHE) - continue() + set (${_PYTHON_PREFIX}_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}") + set_property (CACHE _${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_LIBRARY_RELEASE-NOTFOUND") + else() + unset (_${_PYTHON_PREFIX}_Development_LIBRARY_REASON_FAILURE CACHE) endif() - set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}" "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) - # retrieve library - execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT _${_PYTHON_PREFIX}_RESULT) - # retrieve library directory - string (REGEX MATCHALL "-L[^ ]+" _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_FLAGS}") - string (REPLACE "-L" "" _${_PYTHON_PREFIX}_LIB_DIRS "${_${_PYTHON_PREFIX}_LIB_DIRS}") - list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_LIB_DIRS) - # retrieve library name - string (REGEX MATCHALL "-lpython[^ ]+" _${_PYTHON_PREFIX}_LIB_NAMES "${_${_PYTHON_PREFIX}_FLAGS}") - string (REPLACE "-l" "" _${_PYTHON_PREFIX}_LIB_NAMES "${_${_PYTHON_PREFIX}_LIB_NAMES}") - list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_LIB_NAMES) - - find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE - NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # search for debug library + # use release library location as a hint + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} + NO_DEFAULT_PATH) + # second try including CMAKE variables to catch-up non conventional layouts + find_library (_${_PYTHON_PREFIX}_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} ${_${_PYTHON_PREFIX}_LIB_DIRS} - PATH_SUFFIXES lib NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) - # retrieve runtime library - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) - get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) - get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) - _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE - NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} - NAMES_PER_DIR - HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES bin - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - endif() - endif() - - # retrieve include directory - execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --includes - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT _${_PYTHON_PREFIX}_RESULT) - # retrieve include directory - string (REGEX MATCHALL "-I[^ ]+" _${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_FLAGS}") - string (REPLACE "-I" "" _${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIRS}") - list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_INCLUDE_DIRS) - - find_path (${_PYTHON_PREFIX}_INCLUDE_DIR - NAMES Python.h - HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS} - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) endif() - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_INCLUDE_DIR) - break() + # retrieve runtime libraries + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 POSIX LIBRARY) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) + _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" + "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES bin) endif() - endforeach() + if (_${_PYTHON_PREFIX}_LIBRARY_DEBUG) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_VERSION} WIN32 DEBUG) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) + _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" + "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES bin) + endif() + endif() - # Rely on HINTS and standard paths if config tool failed to locate artifacts - if (NOT (${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) OR NOT ${_PYTHON_PREFIX}_INCLUDE_DIR) - foreach (_${_PYTHON_PREFIX}_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) - string (REPLACE "." "" _${_PYTHON_PREFIX}_VERSION_NO_DOTS ${_${_PYTHON_PREFIX}_VERSION}) - - _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_VERSION}) - - set (_${_PYTHON_PREFIX}_REGISTRY_PATHS - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_CURRENT_USER\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\PythonCore\\${_${_PYTHON_PREFIX}_VERSION}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH}\\InstallPath] - [HKEY_LOCAL_MACHINE\\SOFTWARE\\Python\\ContinuumAnalytics\\Anaconda${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}-${_${_PYTHON_PREFIX}_ARCH2}\\InstallPath]) - - if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") - find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS} - python${_${_PYTHON_PREFIX}_VERSION}mu - python${_${_PYTHON_PREFIX}_VERSION}m - python${_${_PYTHON_PREFIX}_VERSION}u - python${_${_PYTHON_PREFIX}_VERSION} - NAMES_PER_DIR - PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} - PATH_SUFFIXES lib/${CMAKE_LIBRARY_ARCHITECTURE} lib libs - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}mu - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}m - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}u - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION} - lib/python${_${_PYTHON_PREFIX}_VERSION}/config - NO_CMAKE_PATH - NO_CMAKE_ENVIRONMENT_PATH - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + ## compute artifact names + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES VERSION ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} WIN32 POSIX LIBRARY) + _python_get_names (_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG VERSION ${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR} WIN32 DEBUG) - if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") - find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS} - python${_${_PYTHON_PREFIX}_VERSION}mu - python${_${_PYTHON_PREFIX}_VERSION}m - python${_${_PYTHON_PREFIX}_VERSION}u - python${_${_PYTHON_PREFIX}_VERSION} - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} - PATH_SUFFIXES lib/${CMAKE_LIBRARY_ARCHITECTURE} lib libs - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}mu - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}m - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}u - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION} - lib/python${_${_PYTHON_PREFIX}_VERSION}/config - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) - endif() + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS + AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # SABI_LIBRARY_RELEASE search is based on LIBRARY_RELEASE + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) - # search in HINTS locations - find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS} - python${_${_PYTHON_PREFIX}_VERSION}mu - python${_${_PYTHON_PREFIX}_VERSION}m - python${_${_PYTHON_PREFIX}_VERSION}u - python${_${_PYTHON_PREFIX}_VERSION} - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES lib/${CMAKE_LIBRARY_ARCHITECTURE} lib libs - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}mu - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}m - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}u - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION} - lib/python${_${_PYTHON_PREFIX}_VERSION}/config - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_SYSTEM_PATH) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) - if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") - set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} + NO_DEFAULT_PATH) else() - unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) - endif() + if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) + # retrieve root install directory + _python_get_config_var (_${_PYTHON_PREFIX}_PREFIX PREFIX) - if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") - set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) - else() - unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) - endif() + # enforce current ABI + _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS) - # search in all default paths - find_library (${_PYTHON_PREFIX}_LIBRARY_RELEASE - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS} - python${_${_PYTHON_PREFIX}_VERSION}mu - python${_${_PYTHON_PREFIX}_VERSION}m - python${_${_PYTHON_PREFIX}_VERSION}u - python${_${_PYTHON_PREFIX}_VERSION} - NAMES_PER_DIR - PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} - ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} - PATH_SUFFIXES lib/${CMAKE_LIBRARY_ARCHITECTURE} lib libs - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}mu - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}m - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION}u - lib/python${_${_PYTHON_PREFIX}_VERSION}/config-${_${_PYTHON_PREFIX}_VERSION} - lib/python${_${_PYTHON_PREFIX}_VERSION}/config) - # retrieve runtime library - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) - get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) - get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) - _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS} - python${_${_PYTHON_PREFIX}_VERSION}mu - python${_${_PYTHON_PREFIX}_VERSION}m - python${_${_PYTHON_PREFIX}_VERSION}u - python${_${_PYTHON_PREFIX}_VERSION} - NAMES_PER_DIR - HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES bin) - endif() + set (_${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") - if (WIN32) - # search for debug library - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE) - # use library location as a hint - get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) - find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}_d + # retrieve SABI library + ## compute some paths + if (_${_PYTHON_PREFIX}_CONFIG) + string (REGEX REPLACE "^.+python([0-9.]+)[a-z]*-config" "\\1" _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_CONFIG}") + else() + set (_${_PYTHON_PREFIX}_VERSION "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}") + endif() + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} LIBRARY) + + _python_get_config_var (_${_PYTHON_PREFIX}_CONFIGDIR CONFIGDIR) + list (APPEND _${_PYTHON_PREFIX}_HINTS "${_${_PYTHON_PREFIX}_CONFIGDIR}") + + list (APPEND _${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR - HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} - NO_DEFAULT_PATH) - else() - # search first in known locations - if (_${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") - find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}_d + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts + if (NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + set (_${_PYTHON_PREFIX}_HINTS "${${_PYTHON_PREFIX}_ROOT_DIR}" ENV ${_PYTHON_PREFIX}_ROOT_DIR) + + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() + + if (_${_PYTHON_PREFIX}_FIND_STRATEGY STREQUAL "LOCATION") + # Paths suffixes + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} LIBRARY) + + # Framework Paths + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_FIND_VERSIONS}) + # Registry Paths + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_FIND_VERSIONS} ) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search in HINTS locations + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} NAMES_PER_DIR HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} - PATH_SUFFIXES lib libs + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + # search in all default paths + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + else() + foreach (_${_PYTHON_PREFIX}_LIB_VERSION IN LISTS _${_PYTHON_PREFIX}_FIND_VERSIONS) + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION}) + + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_LIB_VERSION} LIBRARY) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_CMAKE_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # search in HINTS locations + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + # search in all default paths + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES}) + + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE) + break() + endif() + endforeach() endif() - # search in all default paths - find_library (${_PYTHON_PREFIX}_LIBRARY_DEBUG - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}_d - NAMES_PER_DIR - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATHS ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} - PATH_SUFFIXES lib libs) endif() - if (${_PYTHON_PREFIX}_LIBRARY_DEBUG) - get_filename_component (_${_PYTHON_PREFIX}_PATH "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" DIRECTORY) - get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) - _python_find_runtime_library (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG - NAMES python${_${_PYTHON_PREFIX}_VERSION_NO_DOTS}_d - NAMES_PER_DIR - HINTS "${_${_PYTHON_PREFIX}_PATH}" "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} - PATH_SUFFIXES bin) + endif() + endif() + + # finalize library version information + _python_get_version (SABI_LIBRARY PREFIX _${_PYTHON_PREFIX}_) + # ABI library does not have the full version information + if (${_PYTHON_PREFIX}_Interpreter_FOUND OR _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # update from interpreter or library + set (_${_PYTHON_PREFIX}_VERSION ${${_PYTHON_PREFIX}_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${${_PYTHON_PREFIX}_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${${_PYTHON_PREFIX}_VERSION_PATCH}) + endif() + + set (${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") + + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE AND NOT EXISTS "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}") + set_property (CACHE _${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE PROPERTY VALUE "Cannot find the library \"${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}\"") + set_property (CACHE _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE PROPERTY VALUE "${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE-NOTFOUND") + else() + unset (_${_PYTHON_PREFIX}_Development_SABI_LIBRARY_REASON_FAILURE CACHE) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + # search for debug library + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" ${_${_PYTHON_PREFIX}_HINTS} + NO_DEFAULT_PATH) + # second try including CMAKE variables to catch-up non conventional layouts + find_library (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) + endif() + + # retrieve runtime libraries + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) + _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" + "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES bin) + endif() + + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) + get_filename_component (_${_PYTHON_PREFIX}_PATH "${_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PATH2 "${_${_PYTHON_PREFIX}_PATH}" DIRECTORY) + _python_find_runtime_library (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG + NAMES ${_${_PYTHON_PREFIX}_LIB_NAMES_DEBUG} + NAMES_PER_DIR + HINTS "${_${_PYTHON_PREFIX}_PATH}" + "${_${_PYTHON_PREFIX}_PATH2}" ${_${_PYTHON_PREFIX}_HINTS} + PATH_SUFFIXES bin) + endif() + endif() + + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + while (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED FALSE) + set (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED FALSE) + foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Module SABIModule Embed) + string (TOUPPER "${_${_PYTHON_PREFIX}_COMPONENT}" _${_PYTHON_PREFIX}_ID) + if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND "LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS) + set (_${_PYTHON_PREFIX}_LIBRARY_REQUIRED TRUE) + endif() + if ("Development.${_${_PYTHON_PREFIX}_COMPONENT}" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND "SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_${_${_PYTHON_PREFIX}_ID}_ARTIFACTS) + set (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED TRUE) endif() + endforeach() + if ((_${_PYTHON_PREFIX}_LIBRARY_REQUIRED + AND NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + AND (_${_PYTHON_PREFIX}_SABI_LIBRARY_REQUIRED + AND NOT _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)) + # Don't search for include dir if no library was founded + break() + endif() + + if ((${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT CMAKE_CROSSCOMPILING) OR _${_PYTHON_PREFIX}_CONFIG) + _python_get_config_var (_${_PYTHON_PREFIX}_INCLUDE_DIRS INCLUDES) + + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} + HINTS ${_${_PYTHON_PREFIX}_INCLUDE_DIRS} + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_SYSTEM_PATH) endif() - # Don't search for include dir until library location is known - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) + # Rely on HINTS and standard paths if interpreter or config tool failed to locate artifacts + if (NOT _${_PYTHON_PREFIX}_INCLUDE_DIR) + unset (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS) + if (_${_PYTHON_PREFIX}_FIND_VIRTUALENV MATCHES "^(FIRST|ONLY)$") + set (_${_PYTHON_PREFIX}_VIRTUALENV_PATHS ENV VIRTUAL_ENV ENV CONDA_PREFIX) + endif() unset (_${_PYTHON_PREFIX}_INCLUDE_HINTS) - if (${_PYTHON_PREFIX}_EXECUTABLE) - # pick up include directory from configuration - execute_process (COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c - "import sys; import sysconfig; sys.stdout.write(sysconfig.get_path('include'))" - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_PATH - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT _${_PYTHON_PREFIX}_RESULT) - file (TO_CMAKE_PATH "${_${_PYTHON_PREFIX}_PATH}" _${_PYTHON_PREFIX}_PATH) - list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PATH}") - endif() - endif() - - foreach (_${_PYTHON_PREFIX}_LIB IN ITEMS ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - if (${_${_PYTHON_PREFIX}_LIB}) - # Use the library's install prefix as a hint - if (${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") - list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") - elseif (${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") - list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") - elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIB} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") - list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") - else() - # assume library is in a directory under root - get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${${_${_PYTHON_PREFIX}_LIB}}" DIRECTORY) - get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) - list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") - endif() + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS + AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + # Use the library's install prefix as a hint + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + elseif (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + else() + # assume library is in a directory under root + get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") endif() - endforeach() - list (REMOVE_DUPLICATES _${_PYTHON_PREFIX}_INCLUDE_HINTS) + elseif ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS + AND _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE) + # Use the library's install prefix as a hint + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "^(.+/Frameworks/Python.framework/Versions/[0-9.]+)") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + elseif (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "^(.+)/lib(64|32)?/python[0-9.]+/config") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + elseif (DEFINED CMAKE_LIBRARY_ARCHITECTURE AND ${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE} MATCHES "^(.+)/lib/${CMAKE_LIBRARY_ARCHITECTURE}") + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${CMAKE_MATCH_1}") + else() + # assume library is in a directory under root + get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY) + get_filename_component (_${_PYTHON_PREFIX}_PREFIX "${_${_PYTHON_PREFIX}_PREFIX}" DIRECTORY) + list (APPEND _${_PYTHON_PREFIX}_INCLUDE_HINTS "${_${_PYTHON_PREFIX}_PREFIX}") + endif() + endif() + + _python_get_frameworks (_${_PYTHON_PREFIX}_FRAMEWORK_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_registries (_${_PYTHON_PREFIX}_REGISTRY_PATHS VERSION ${_${_PYTHON_PREFIX}_VERSION}) + _python_get_path_suffixes (_${_PYTHON_PREFIX}_PATH_SUFFIXES VERSION ${_${_PYTHON_PREFIX}_VERSION} INCLUDE) if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "FIRST") - find_path (${_PYTHON_PREFIX}_INCLUDE_DIR - NAMES Python.h - HINTS ${_${_PYTHON_PREFIX}_HINTS} - PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} - PATH_SUFFIXES include/python${_${_PYTHON_PREFIX}_VERSION}mu - include/python${_${_PYTHON_PREFIX}_VERSION}m - include/python${_${_PYTHON_PREFIX}_VERSION}u - include/python${_${_PYTHON_PREFIX}_VERSION} - include + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} + HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_CMAKE_PATH NO_CMAKE_ENVIRONMENT_PATH NO_SYSTEM_ENVIRONMENT_PATH @@ -1029,159 +3511,361 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS endif() if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "FIRST") - find_path (${_PYTHON_PREFIX}_INCLUDE_DIR - NAMES Python.h + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} - PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} - PATH_SUFFIXES include/python${_${_PYTHON_PREFIX}_VERSION}mu - include/python${_${_PYTHON_PREFIX}_VERSION}m - include/python${_${_PYTHON_PREFIX}_VERSION}u - include/python${_${_PYTHON_PREFIX}_VERSION} - include + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${_${_PYTHON_PREFIX}_REGISTRY_PATHS} + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() - find_path (${_PYTHON_PREFIX}_INCLUDE_DIR - NAMES Python.h + if (APPLE AND _${_PYTHON_PREFIX}_FIND_FRAMEWORK STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS ${_${_PYTHON_PREFIX}_FRAMEWORK_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_FRAMEWORK_PATHS) + endif() + + if (WIN32 AND _${_PYTHON_PREFIX}_FIND_REGISTRY STREQUAL "LAST") + set (__${_PYTHON_PREFIX}_REGISTRY_PATHS ${_${_PYTHON_PREFIX}_REGISTRY_PATHS}) + else() + unset (__${_PYTHON_PREFIX}_REGISTRY_PATHS) + endif() + + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES} HINTS ${_${_PYTHON_PREFIX}_INCLUDE_HINTS} ${_${_PYTHON_PREFIX}_HINTS} - PATHS ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} + PATHS ${_${_PYTHON_PREFIX}_VIRTUALENV_PATHS} + ${__${_PYTHON_PREFIX}_FRAMEWORK_PATHS} ${__${_PYTHON_PREFIX}_REGISTRY_PATHS} - PATH_SUFFIXES include/python${_${_PYTHON_PREFIX}_VERSION}mu - include/python${_${_PYTHON_PREFIX}_VERSION}m - include/python${_${_PYTHON_PREFIX}_VERSION}u - include/python${_${_PYTHON_PREFIX}_VERSION} - include + PATH_SUFFIXES ${_${_PYTHON_PREFIX}_PATH_SUFFIXES} NO_SYSTEM_ENVIRONMENT_PATH NO_CMAKE_SYSTEM_PATH) endif() - if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) AND ${_PYTHON_PREFIX}_INCLUDE_DIR) - break() - endif() - endforeach() + # search header file in standard locations + find_path (_${_PYTHON_PREFIX}_INCLUDE_DIR + NAMES ${_${_PYTHON_PREFIX}_INCLUDE_NAMES}) - # search header file in standard locations - find_path (${_PYTHON_PREFIX}_INCLUDE_DIR - NAMES Python.h) - endif() + break() + endwhile() - if (${_PYTHON_PREFIX}_INCLUDE_DIR) - # retrieve version from header file - file (STRINGS "${${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" _${_PYTHON_PREFIX}_VERSION - REGEX "^#define[ \t]+PY_VERSION[ \t]+\"[^\"]+\"") - string (REGEX REPLACE "^#define[ \t]+PY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" - _${_PYTHON_PREFIX}_VERSION "${_${_PYTHON_PREFIX}_VERSION}") - string (REGEX MATCHALL "[0-9]+" _${_PYTHON_PREFIX}_VERSIONS "${_${_PYTHON_PREFIX}_VERSION}") - list (GET _${_PYTHON_PREFIX}_VERSIONS 0 _${_PYTHON_PREFIX}_VERSION_MAJOR) - list (GET _${_PYTHON_PREFIX}_VERSIONS 1 _${_PYTHON_PREFIX}_VERSION_MINOR) - list (GET _${_PYTHON_PREFIX}_VERSIONS 2 _${_PYTHON_PREFIX}_VERSION_PATCH) + set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") - if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND) - # set public version information - set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) - set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) - set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) - set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) + if (_${_PYTHON_PREFIX}_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}") + set_property (CACHE _${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND") + else() + unset (_${_PYTHON_PREFIX}_Development_INCLUDE_DIR_REASON_FAILURE CACHE) + endif() + + if (_${_PYTHON_PREFIX}_INCLUDE_DIR) + # retrieve version from header file + _python_get_version (INCLUDE PREFIX _${_PYTHON_PREFIX}_INC_) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS + AND _${_PYTHON_PREFIX}_LIBRARY_RELEASE) + if ("${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" + VERSION_EQUAL _${_PYTHON_PREFIX}_VERSION) + # update versioning + set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) + elseif (_${_PYTHON_PREFIX}_VERSION VERSION_EQUAL _${_PYTHON_PREFIX}_INC_VERSION_MAJOR) + # library specify only major version, use include file for full version information + set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) + endif() + else() + set (_${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_INC_VERSION}) + set (_${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}) + set (_${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}) + set (_${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_INC_VERSION_PATCH}) + endif() endif() endif() - # define public variables - include (SelectLibraryConfigurations) - select_library_configurations (${_PYTHON_PREFIX}) - if (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) - set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") - elseif (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) - set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") - else() - set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "$${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND") + if (NOT ${_PYTHON_PREFIX}_Interpreter_FOUND AND NOT ${_PYTHON_PREFIX}_Compiler_FOUND) + # set public version information + set (${_PYTHON_PREFIX}_VERSION ${_${_PYTHON_PREFIX}_VERSION}) + set (${_PYTHON_PREFIX}_VERSION_MAJOR ${_${_PYTHON_PREFIX}_VERSION_MAJOR}) + set (${_PYTHON_PREFIX}_VERSION_MINOR ${_${_PYTHON_PREFIX}_VERSION_MINOR}) + set (${_PYTHON_PREFIX}_VERSION_PATCH ${_${_PYTHON_PREFIX}_VERSION_PATCH}) endif() - _python_set_library_dirs (${_PYTHON_PREFIX}_LIBRARY_DIRS - ${_PYTHON_PREFIX}_LIBRARY_RELEASE ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - if (UNIX) - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" - OR ${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") - set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) + # define public variables + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_LIBRARY_DEBUG}") + _python_select_library_configurations (${_PYTHON_PREFIX}) + + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + + if (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") + elseif (_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + else() + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_LIBRARY-NOTFOUND") endif() - else() + + _python_set_library_dirs (${_PYTHON_PREFIX}_LIBRARY_DIRS + _${_PYTHON_PREFIX}_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_LIBRARY_DEBUG) + if (UNIX) + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") + set (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) + endif() + else() _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DIRS - ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) + endif() endif() - set (${_PYTHON_PREFIX}_INCLUDE_DIRS "${${_PYTHON_PREFIX}_INCLUDE_DIR}") + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG}") + _python_select_library_configurations (${_PYTHON_PREFIX}_SABI) + + set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE}") + set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG}") + + if (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE) + set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE}") + elseif (_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) + set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG}") + else() + set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY "${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY-NOTFOUND") + endif() - mark_as_advanced (${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE - ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG - ${_PYTHON_PREFIX}_INCLUDE_DIR) + _python_set_library_dirs (${_PYTHON_PREFIX}_SABI_LIBRARY_DIRS + _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG) + if (UNIX) + if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") + set (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DIRS ${${_PYTHON_PREFIX}_LIBRARY_DIRS}) + endif() + else() + _python_set_library_dirs (${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DIRS + _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG) + endif() + endif() - if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - AND ${_PYTHON_PREFIX}_INCLUDE_DIR) + if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE OR _${_PYTHON_PREFIX}_INCLUDE_DIR) if (${_PYTHON_PREFIX}_Interpreter_FOUND OR ${_PYTHON_PREFIX}_Compiler_FOUND) # development environment must be compatible with interpreter/compiler - if (${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR} VERSION_EQUAL ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}) - set (${_PYTHON_PREFIX}_Development_FOUND TRUE) + if ("${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}" VERSION_EQUAL "${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR}" + AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}") + _python_set_development_module_found (Module) + _python_set_development_module_found (SABIModule) + _python_set_development_module_found (Embed) endif() - elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) - set (${_PYTHON_PREFIX}_Development_FOUND TRUE) + elseif (${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR + AND "${_${_PYTHON_PREFIX}_INC_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_INC_VERSION_MINOR}" VERSION_EQUAL "${_${_PYTHON_PREFIX}_VERSION_MAJOR}.${_${_PYTHON_PREFIX}_VERSION_MINOR}") + _python_set_development_module_found (Module) + _python_set_development_module_found (SABIModule) + _python_set_development_module_found (Embed) endif() + if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND + (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS + OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)) + set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE) + set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE) + set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE) + endif() + endif() + + if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Module_FOUND + AND ${_PYTHON_PREFIX}_Development.Embed_FOUND) + set (${_PYTHON_PREFIX}_Development_FOUND TRUE) + endif() + + if ((${_PYTHON_PREFIX}_Development.Module_FOUND + OR ${_PYTHON_PREFIX}_Development.SABIModule_FOUND + OR ${_PYTHON_PREFIX}_Development.Embed_FOUND) + AND EXISTS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/PyPy.h") + # retrieve PyPy version + file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/patchlevel.h" ${_PYTHON_PREFIX}_PyPy_VERSION + REGEX "^#define[ \t]+PYPY_VERSION[ \t]+\"[^\"]+\"") + string (REGEX REPLACE "^#define[ \t]+PYPY_VERSION[ \t]+\"([^\"]+)\".*" "\\1" + ${_PYTHON_PREFIX}_PyPy_VERSION "${${_PYTHON_PREFIX}_PyPy_VERSION}") + endif() + + unset(${_PYTHON_PREFIX}_LINK_OPTIONS) + if (${_PYTHON_PREFIX}_Development.Embed_FOUND AND APPLE + AND ${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$") + # rpath must be specified if python is part of a framework + unset(_${_PYTHON_PREFIX}_is_prefix) + foreach (_${_PYTHON_PREFIX}_implementation IN LISTS _${_PYTHON_PREFIX}_FIND_IMPLEMENTATIONS) + foreach (_${_PYTHON_PREFIX}_framework IN LISTS _${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_implementation}_FRAMEWORKS) + cmake_path (IS_PREFIX _${_PYTHON_PREFIX}_framework "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" _${_PYTHON_PREFIX}_is_prefix) + if (_${_PYTHON_PREFIX}_is_prefix) + cmake_path (GET _${_PYTHON_PREFIX}_framework PARENT_PATH _${_PYTHON_PREFIX}_framework) + set (${_PYTHON_PREFIX}_LINK_OPTIONS "LINKER:-rpath,${_${_PYTHON_PREFIX}_framework}") + break() + endif() + endforeach() + if (_${_PYTHON_PREFIX}_is_prefix) + break() + endif() + endforeach() + unset(_${_PYTHON_PREFIX}_implementation) + unset(_${_PYTHON_PREFIX}_framework) + unset(_${_PYTHON_PREFIX}_is_prefix) + endif() + + if (NOT DEFINED ${_PYTHON_PREFIX}_SOABI) + _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI) endif() + if (NOT DEFINED ${_PYTHON_PREFIX}_SOSABI) + _python_get_config_var (${_PYTHON_PREFIX}_SOSABI SOSABI) + endif() + + _python_compute_development_signature (Module) + _python_compute_development_signature (SABIModule) + _python_compute_development_signature (Embed) + # Restore the original find library ordering if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES) set (CMAKE_FIND_LIBRARY_SUFFIXES ${_${_PYTHON_PREFIX}_CMAKE_FIND_LIBRARY_SUFFIXES}) endif() + + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_LIBRARY "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} Library") + endif() + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_SABI_LIBRARY "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" CACHE FILEPATH "${_PYTHON_PREFIX} ABI Library") + endif() + if ("INCLUDE_DIR" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + set (${_PYTHON_PREFIX}_INCLUDE_DIR "${_${_PYTHON_PREFIX}_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} Include Directory") + endif() + endif() + + _python_mark_as_internal (_${_PYTHON_PREFIX}_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_SABI_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_RELEASE + _${_PYTHON_PREFIX}_RUNTIME_SABI_LIBRARY_DEBUG + _${_PYTHON_PREFIX}_INCLUDE_DIR + _${_PYTHON_PREFIX}_CONFIG + _${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE + _${_PYTHON_PREFIX}_DEVELOPMENT_EMBED_SIGNATURE) endif() +if (${_PYTHON_PREFIX}_FIND_REQUIRED_NumPy) + list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS) +endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_Interpreter_FOUND) - list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) - if (${_PYTHON_PREFIX}_FIND_REQUIRED_NumPy) - list (APPEND _${_PYTHON_PREFIX}_REQUIRED_VARS ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) - endif() - execute_process( - COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c - "from __future__ import print_function\ntry: import numpy; print(numpy.get_include(), end='')\nexcept:pass\n" - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_PATH - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT _${_PYTHON_PREFIX}_RESULT) - find_path(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR - NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h" - HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}" - NO_DEFAULT_PATH) - endif() - if(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) - set(${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") - set(${_PYTHON_PREFIX}_NumPy_FOUND TRUE) - endif() - if(${_PYTHON_PREFIX}_NumPy_FOUND) - execute_process( - COMMAND "${${_PYTHON_PREFIX}_EXECUTABLE}" -c - "from __future__ import print_function\ntry: import numpy; print(numpy.__version__, end='')\nexcept:pass\n" - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_VERSION) + list (APPEND _${_PYTHON_PREFIX}_CACHED_VARS _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + + if (DEFINED ${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR + AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE INTERNAL "") + elseif (DEFINED _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + # compute numpy signature. Depends on interpreter and development signatures + string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + if (NOT __${_PYTHON_PREFIX}_NUMPY_SIGNATURE STREQUAL _${_PYTHON_PREFIX}_NUMPY_SIGNATURE + OR NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + unset (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR CACHE) + unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE) + endif() + endif() + + if (NOT _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + execute_process(COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry: import numpy; sys.stdout.write(numpy.get_include())\nexcept:pass\n" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_PATH + ERROR_QUIET + OUTPUT_STRIP_TRAILING_WHITESPACE) + + if (NOT _${_PYTHON_PREFIX}_RESULT) + find_path (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR + NAMES "numpy/arrayobject.h" "numpy/numpyconfig.h" + HINTS "${_${_PYTHON_PREFIX}_NumPy_PATH}" + NO_DEFAULT_PATH) + endif() + endif() + + set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + + if(_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR AND NOT EXISTS "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") + set_property (CACHE _${_PYTHON_PREFIX}_NumPy_REASON_FAILURE PROPERTY VALUE "Cannot find the directory \"${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}\"") + set_property (CACHE _${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR-NOTFOUND") + endif() + + if (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR) + execute_process (COMMAND ${${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c + "import sys\ntry: import numpy; sys.stdout.write(numpy.__version__)\nexcept:pass\n" + RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT + OUTPUT_VARIABLE _${_PYTHON_PREFIX}_NumPy_VERSION) if (NOT _${_PYTHON_PREFIX}_RESULT) - set(${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}") + set (${_PYTHON_PREFIX}_NumPy_VERSION "${_${_PYTHON_PREFIX}_NumPy_VERSION}") + else() + unset (${_PYTHON_PREFIX}_NumPy_VERSION) endif() + + # final step: set NumPy founded only if Development.Module component is founded as well + set(${_PYTHON_PREFIX}_NumPy_FOUND ${${_PYTHON_PREFIX}_Development.Module_FOUND}) + else() + set (${_PYTHON_PREFIX}_NumPy_FOUND FALSE) + endif() + + if (${_PYTHON_PREFIX}_NumPy_FOUND) + unset (_${_PYTHON_PREFIX}_NumPy_REASON_FAILURE CACHE) + + # compute and save numpy signature + string (MD5 __${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE}:${_${_PYTHON_PREFIX}_DEVELOPMENT_MODULE_SIGNATURE}:${${_PYTHON_PREFIX}_NumPyINCLUDE_DIR}") + set (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE "${__${_PYTHON_PREFIX}_NUMPY_SIGNATURE}" CACHE INTERNAL "") + else() + unset (_${_PYTHON_PREFIX}_NUMPY_SIGNATURE CACHE) endif() - # final step: set NumPy founded only if Development component is founded as well - if (NOT ${_PYTHON_PREFIX}_Development_FOUND) - set(${_PYTHON_PREFIX}_NumPy_FOUND FALSE) + + if (${_PYTHON_PREFIX}_ARTIFACTS_INTERACTIVE) + set (${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR "${_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}" CACHE FILEPATH "${_PYTHON_PREFIX} NumPy Include Directory") endif() + + _python_mark_as_internal (_${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR + _${_PYTHON_PREFIX}_NUMPY_SIGNATURE) endif() # final validation if (${_PYTHON_PREFIX}_VERSION_MAJOR AND NOT ${_PYTHON_PREFIX}_VERSION_MAJOR VERSION_EQUAL _${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR) _python_display_failure ("Could NOT find ${_PYTHON_PREFIX}: Found unsuitable major version \"${${_PYTHON_PREFIX}_VERSION_MAJOR}\", but required major version is exact version \"${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}\"") + + cmake_policy(POP) + return() endif() -include (FindPackageHandleStandardArgs) +unset (_${_PYTHON_PREFIX}_REASON_FAILURE) +foreach (_${_PYTHON_PREFIX}_COMPONENT IN ITEMS Interpreter Compiler Development NumPy) + if (_${_PYTHON_PREFIX}_COMPONENT STREQUAL "Development") + foreach (artifact IN LISTS _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_ARTIFACTS) + if (_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE) + _python_add_reason_failure ("Development" "${_${_PYTHON_PREFIX}_Development_${artifact}_REASON_FAILURE}") + endif() + endforeach() + endif() + if (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE) + string (APPEND _${_PYTHON_PREFIX}_REASON_FAILURE "\n ${_${_PYTHON_PREFIX}_COMPONENT}: ${_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE}") + unset (_${_PYTHON_PREFIX}_${_${_PYTHON_PREFIX}_COMPONENT}_REASON_FAILURE CACHE) + endif() +endforeach() + find_package_handle_standard_args (${_PYTHON_PREFIX} REQUIRED_VARS ${_${_PYTHON_PREFIX}_REQUIRED_VARS} VERSION_VAR ${_PYTHON_PREFIX}_VERSION - HANDLE_COMPONENTS) + HANDLE_VERSION_RANGE + HANDLE_COMPONENTS + REASON_FAILURE_MESSAGE "${_${_PYTHON_PREFIX}_REASON_FAILURE}") # Create imported targets and helper functions if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") @@ -1201,73 +3885,123 @@ if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") PROPERTY IMPORTED_LOCATION "${${_PYTHON_PREFIX}_COMPILER}") endif() - if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS - AND ${_PYTHON_PREFIX}_Development_FOUND AND NOT TARGET ${_PYTHON_PREFIX}::Python) + if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Module_FOUND) + OR ("Development.SABIModule" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.SABIModule_FOUND) + OR ("Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS + AND ${_PYTHON_PREFIX}_Development.Embed_FOUND)) - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" - OR ${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" - OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE OR ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG) - set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) - else() - set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) - endif() - - add_library (${_PYTHON_PREFIX}::Python ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) - - set_property (TARGET ${_PYTHON_PREFIX}::Python - PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIR}") - - if ((${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE) - OR (${_PYTHON_PREFIX}_LIBRARY_DEBUG AND ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG)) - # System manage shared libraries in two parts: import and runtime - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - set_property (TARGET ${_PYTHON_PREFIX}::Python PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" - IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}" - IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE}") - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" - IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}" - IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG}") + macro (__PYTHON_IMPORT_LIBRARY __name) + if (${ARGC} GREATER 1) + set (_PREFIX "${ARGV1}_") else() - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_LIBRARY}" - IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_LIBRARY}") + set (_PREFIX "") endif() - else() - if (${_PYTHON_PREFIX}_LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_LIBRARY_DEBUG) - set_property (TARGET ${_PYTHON_PREFIX}::Python PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" - IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_LIBRARY_RELEASE}") - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" - IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_LIBRARY_DEBUG}") + if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" + OR ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE) + set (_${_PYTHON_PREFIX}_LIBRARY_TYPE SHARED) + else() + set (_${_PYTHON_PREFIX}_LIBRARY_TYPE STATIC) + endif() + + if (NOT TARGET ${__name}) + add_library (${__name} ${_${_PYTHON_PREFIX}_LIBRARY_TYPE} IMPORTED) + endif() + + set_property (TARGET ${__name} + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") + + if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE) + # System manage shared libraries in two parts: import and runtime + if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG) + set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_IMPLIB_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}" + IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}RUNTIME_LIBRARY_RELEASE}") + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" + IMPORTED_IMPLIB_DEBUG "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG}" + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_DEBUG}") + else() + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_IMPLIB "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARIES}" + IMPORTED_LOCATION "${${_PYTHON_PREFIX}_RUNTIME_${_PREFIX}LIBRARY_RELEASE}") + endif() else() - set_target_properties (${_PYTHON_PREFIX}::Python - PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" - IMPORTED_LOCATION "${${_PYTHON_PREFIX}_LIBRARY}") + if (${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE AND ${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG) + set_property (TARGET ${__name} PROPERTY IMPORTED_CONFIGURATIONS RELEASE DEBUG) + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C" + IMPORTED_LOCATION_RELEASE "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}") + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C" + IMPORTED_LOCATION_DEBUG "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_DEBUG}") + else() + set_target_properties (${__name} + PROPERTIES IMPORTED_LINK_INTERFACE_LANGUAGES "C" + IMPORTED_LOCATION "${${_PYTHON_PREFIX}_${_PREFIX}LIBRARY_RELEASE}") + endif() endif() + + if (_${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") + # extend link information with dependent libraries + _python_get_config_var (_${_PYTHON_PREFIX}_LINK_LIBRARIES LIBS) + if (_${_PYTHON_PREFIX}_LINK_LIBRARIES) + set_property (TARGET ${__name} + PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) + endif() + endif() + + if (${_PYTHON_PREFIX}_LINK_OPTIONS + AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "SHARED") + set_property (TARGET ${__name} PROPERTY INTERFACE_LINK_OPTIONS "${${_PYTHON_PREFIX}_LINK_OPTIONS}") + endif() + endmacro() + + macro (__PYTHON_IMPORT_MODULE __name) + if (NOT TARGET ${__name}) + add_library (${__name} INTERFACE IMPORTED) + endif() + set_property (TARGET ${__name} + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_INCLUDE_DIRS}") + + # When available, enforce shared library generation with undefined symbols + if (APPLE) + set_property (TARGET ${__name} + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup") + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "SunOS") + set_property (TARGET ${__name} + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-z,nodefs") + endif() + if (CMAKE_SYSTEM_NAME STREQUAL "AIX") + set_property (TARGET ${__name} + PROPERTY INTERFACE_LINK_OPTIONS "LINKER:-b,erok") + endif() + endmacro() + + if (${_PYTHON_PREFIX}_Development.Embed_FOUND) + __python_import_library (${_PYTHON_PREFIX}::Python) endif() - if (_${_PYTHON_PREFIX}_CONFIG AND _${_PYTHON_PREFIX}_LIBRARY_TYPE STREQUAL "STATIC") - # extend link information with dependent libraries - execute_process (COMMAND "${_${_PYTHON_PREFIX}_CONFIG}" --ldflags - RESULT_VARIABLE _${_PYTHON_PREFIX}_RESULT - OUTPUT_VARIABLE _${_PYTHON_PREFIX}_FLAGS - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT _${_PYTHON_PREFIX}_RESULT) - string (REGEX MATCHALL "-[Ll][^ ]+" _${_PYTHON_PREFIX}_LINK_LIBRARIES "${_${_PYTHON_PREFIX}_FLAGS}") - # remove elements relative to python library itself - list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-lpython") - foreach (_${_PYTHON_PREFIX}_DIR IN LISTS ${_PYTHON_PREFIX}_LIBRARY_DIRS) - list (FILTER _${_PYTHON_PREFIX}_LINK_LIBRARIES EXCLUDE REGEX "-L${${_PYTHON_PREFIX}_DIR}") - endforeach() - set_property (TARGET ${_PYTHON_PREFIX}::Python - PROPERTY INTERFACE_LINK_LIBRARIES ${_${_PYTHON_PREFIX}_LINK_LIBRARIES}) + if (${_PYTHON_PREFIX}_Development.Module_FOUND) + if ("LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_MODULE_ARTIFACTS) + # On Windows/CYGWIN/MSYS, Python::Module is the same as Python::Python + # but ALIAS cannot be used because the imported library is not GLOBAL. + __python_import_library (${_PYTHON_PREFIX}::Module) + else() + __python_import_module (${_PYTHON_PREFIX}::Module) + endif() + endif() + + if (${_PYTHON_PREFIX}_Development.SABIModule_FOUND) + if ("SABI_LIBRARY" IN_LIST _${_PYTHON_PREFIX}_FIND_DEVELOPMENT_SABIMODULE_ARTIFACTS) + __python_import_library (${_PYTHON_PREFIX}::SABIModule SABI) + else() + __python_import_module (${_PYTHON_PREFIX}::SABIModule) endif() endif() @@ -1276,35 +4010,115 @@ if(_${_PYTHON_PREFIX}_CMAKE_ROLE STREQUAL "PROJECT") # It is used to build modules for python. # function (__${_PYTHON_PREFIX}_ADD_LIBRARY prefix name) - cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY - "STATIC;SHARED;MODULE" "" "") + cmake_parse_arguments (PARSE_ARGV 2 PYTHON_ADD_LIBRARY "STATIC;SHARED;MODULE;WITH_SOABI" "USE_SABI" "") - unset (type) - if (NOT (PYTHON_ADD_LIBRARY_STATIC - OR PYTHON_ADD_LIBRARY_SHARED - OR PYTHON_ADD_LIBRARY_MODULE)) + if (PYTHON_ADD_LIBRARY_STATIC) + set (type STATIC) + elseif (PYTHON_ADD_LIBRARY_SHARED) + set (type SHARED) + else() set (type MODULE) endif() - add_library (${name} ${type} ${ARGN}) - target_link_libraries (${name} PRIVATE ${prefix}::Python) - # customize library name to follow module name rules + if (PYTHON_ADD_LIBRARY_USE_SABI) + if (NOT type STREQUAL MODULE) + message (SEND_ERROR "${prefix}_ADD_LIBRARY: 'USE_SABI' option is only valid for 'MODULE' type.") + return() + endif() + if (NOT PYTHON_ADD_LIBRARY_USE_SABI MATCHES "^(3)(\\.([0-9]+))?$") + message (SEND_ERROR "${prefix}_ADD_LIBRARY: ${PYTHON_ADD_LIBRARY_USE_SABI}: wrong version specified for 'USE_SABI'.") + return() + endif() + # compute value for Py_LIMITED_API macro + set (major_version "${CMAKE_MATCH_1}") + unset (minor_version) + if (CMAKE_MATCH_3) + set (minor_version "${CMAKE_MATCH_3}") + endif() + if (major_version EQUAL "3" AND NOT minor_version) + set (Py_LIMITED_API "3") + elseif ("${major_version}.${minor_version}" VERSION_LESS "3.2") + message (SEND_ERROR "${prefix}_ADD_LIBRARY: ${PYTHON_ADD_LIBRARY_USE_SABI}: invalid version. Version must be '3.2' or upper.") + return() + else() + set (Py_LIMITED_API "0x0${major_version}") + if (NOT minor_version) + string (APPEND Py_LIMITED_API "00") + else() + if (minor_version LESS 16) + string (APPEND Py_LIMITED_API "0") + endif() + math (EXPR minor_version "${minor_version}" OUTPUT_FORMAT HEXADECIMAL) + string (REGEX REPLACE "^0x(.+)$" "\\1" minor_version "${minor_version}") + string (APPEND Py_LIMITED_API "${minor_version}") + endif() + string (APPEND Py_LIMITED_API "0000") + endif() + endif() + + if (type STREQUAL "MODULE") + if (PYTHON_ADD_LIBRARY_USE_SABI AND NOT TARGET ${prefix}::SABIModule) + message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::SABIModule' is not defined.\n Did you miss to request COMPONENT 'Development.SABIModule'?") + return() + endif() + if (NOT PYTHON_ADD_LIBRARY_USE_SABI AND NOT TARGET ${prefix}::Module) + message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Module' is not defined.\n Did you miss to request COMPONENT 'Development.Module'?") + return() + endif() + endif() + if (NOT type STREQUAL "MODULE" AND NOT TARGET ${prefix}::Python) + message (SEND_ERROR "${prefix}_ADD_LIBRARY: dependent target '${prefix}::Python' is not defined.\n Did you miss to request COMPONENT 'Development.Embed'?") + return() + endif() + + add_library (${name} ${type} ${PYTHON_ADD_LIBRARY_UNPARSED_ARGUMENTS}) + get_property (type TARGET ${name} PROPERTY TYPE) + if (type STREQUAL "MODULE_LIBRARY") + if (PYTHON_ADD_LIBRARY_USE_SABI) + target_compile_definitions (${name} PRIVATE Py_LIMITED_API=${Py_LIMITED_API}) + target_link_libraries (${name} PRIVATE ${prefix}::SABIModule) + else() + target_link_libraries (${name} PRIVATE ${prefix}::Module) + endif() + # customize library name to follow module name rules set_property (TARGET ${name} PROPERTY PREFIX "") if(CMAKE_SYSTEM_NAME STREQUAL "Windows") set_property (TARGET ${name} PROPERTY SUFFIX ".pyd") endif() + + if (PYTHON_ADD_LIBRARY_WITH_SOABI) + if (NOT PYTHON_ADD_LIBRARY_USE_SABI AND ${prefix}_SOABI) + get_property (suffix TARGET ${name} PROPERTY SUFFIX) + if (NOT suffix) + set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}") + endif() + set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOABI}${suffix}") + endif() + if (PYTHON_ADD_LIBRARY_USE_SABI AND ${prefix}_SOSABI) + get_property (suffix TARGET ${name} PROPERTY SUFFIX) + if (NOT suffix) + set (suffix "${CMAKE_SHARED_MODULE_SUFFIX}") + endif() + set_property (TARGET ${name} PROPERTY SUFFIX ".${${prefix}_SOSABI}${suffix}") + endif() + endif() + else() + if (PYTHON_ADD_LIBRARY_WITH_SOABI OR PYTHON_ADD_LIBRARY_USE_SABI) + message (AUTHOR_WARNING "Find${prefix}: Options 'WITH_SOABI' and 'USE_SABI' are only supported for `MODULE` library type.") + endif() + target_link_libraries (${name} PRIVATE ${prefix}::Python) endif() endfunction() endif() if ("NumPy" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS AND ${_PYTHON_PREFIX}_NumPy_FOUND - AND NOT TARGET ${_PYTHON_PREFIX}::NumPy AND TARGET ${_PYTHON_PREFIX}::Python) + AND NOT TARGET ${_PYTHON_PREFIX}::NumPy AND TARGET ${_PYTHON_PREFIX}::Module) add_library (${_PYTHON_PREFIX}::NumPy INTERFACE IMPORTED) set_property (TARGET ${_PYTHON_PREFIX}::NumPy - PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIR}") - target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Python) + PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${${_PYTHON_PREFIX}_NumPy_INCLUDE_DIRS}") + target_link_libraries (${_PYTHON_PREFIX}::NumPy INTERFACE ${_PYTHON_PREFIX}::Module) endif() endif() @@ -1325,4 +4139,4 @@ else() unset (CMAKE_FIND_FRAMEWORK) endif() -unset (_${_PYTHON_PREFIX}_CONFIG CACHE) +cmake_policy(POP) diff --git a/cmake/modules/FindPython3.cmake b/cmake/modules/FindPython3.cmake index c2f33846971..901565bdfac 100644 --- a/cmake/modules/FindPython3.cmake +++ b/cmake/modules/FindPython3.cmake @@ -5,21 +5,52 @@ FindPython3 ----------- +.. versionadded:: 3.12 + Find Python 3 interpreter, compiler and development environment (include directories and libraries). -Three components are supported: +.. versionadded:: 3.19 + When a version is requested, it can be specified as a simple value or as a + range. For a detailed description of version range usage and capabilities, + refer to the :command:`find_package` command. + +The following components are supported: * ``Interpreter``: search for Python 3 interpreter * ``Compiler``: search for Python 3 compiler. Only offered by IronPython. * ``Development``: search for development artifacts (include directories and - libraries) + libraries). + + .. versionadded:: 3.18 + This component includes two sub-components which can be specified + independently: + + * ``Development.Module``: search for artifacts for Python 3 module + developments. + * ``Development.Embed``: search for artifacts for Python 3 embedding + developments. + + .. versionadded:: 3.26 + + * ``Development.SABIModule``: search for artifacts for Python 3 module + developments using the + `Stable Application Binary Interface `_. + This component is available only for version ``3.2`` and upper. + * ``NumPy``: search for NumPy include directories. -If no ``COMPONENTS`` is specified, ``Interpreter`` is assumed. +.. versionadded:: 3.14 + Added the ``NumPy`` component. + +If no ``COMPONENTS`` are specified, ``Interpreter`` is assumed. + +If component ``Development`` is specified, it implies sub-components +``Development.Module`` and ``Development.Embed``. To ensure consistent versions between components ``Interpreter``, ``Compiler``, -``Development`` and ``NumPy``, specify all components at the same time:: +``Development`` (or one of its sub-components) and ``NumPy``, specify all +components at the same time:: find_package (Python3 COMPONENTS Interpreter Development) @@ -31,24 +62,45 @@ for you. .. note:: - If components ``Interpreter`` and ``Development`` are both specified, this - module search only for interpreter with same platform architecture as the one - defined by ``CMake`` configuration. This contraint does not apply if only - ``Interpreter`` component is specified. + If components ``Interpreter`` and ``Development`` (or one of its + sub-components) are both specified, this module search only for interpreter + with same platform architecture as the one defined by CMake + configuration. This constraint does not apply if only ``Interpreter`` + component is specified. Imported Targets ^^^^^^^^^^^^^^^^ -This module defines the following :ref:`Imported Targets ` -(when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``): +This module defines the following :ref:`Imported Targets `: + +.. versionchanged:: 3.14 + :ref:`Imported Targets ` are only created when + :prop_gbl:`CMAKE_ROLE` is ``PROJECT``. ``Python3::Interpreter`` Python 3 interpreter. Target defined if component ``Interpreter`` is found. ``Python3::Compiler`` Python 3 compiler. Target defined if component ``Compiler`` is found. + +``Python3::Module`` + .. versionadded:: 3.15 + + Python 3 library for Python module. Target defined if component + ``Development.Module`` is found. + +``Python3::SABIModule`` + .. versionadded:: 3.26 + + Python 3 library for Python module using the Stable Application Binary + Interface. Target defined if component ``Development.SABIModule`` is found. + ``Python3::Python`` - Python 3 library. Target defined if component ``Development`` is found. + Python 3 library for Python embedding. Target defined if component + ``Development.Embed`` is found. + ``Python3::NumPy`` + .. versionadded:: 3.14 + NumPy library for Python 3. Target defined if component ``NumPy`` is found. Result Variables @@ -70,26 +122,43 @@ This module will set the following variables in your project * Anaconda * Canopy * IronPython + * PyPy ``Python3_STDLIB`` Standard platform independent installation directory. - Information returned by - ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=True)``. + Information returned by ``sysconfig.get_path('stdlib')``. ``Python3_STDARCH`` Standard platform dependent installation directory. - Information returned by - ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=True)``. + Information returned by ``sysconfig.get_path('platstdlib')``. ``Python3_SITELIB`` Third-party platform independent installation directory. - Information returned by - ``distutils.sysconfig.get_python_lib(plat_specific=False,standard_lib=False)``. + Information returned by ``sysconfig.get_path('purelib')``. ``Python3_SITEARCH`` Third-party platform dependent installation directory. - Information returned by - ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)``. + Information returned by ``sysconfig.get_path('platlib')``. + +``Python3_SOABI`` + .. versionadded:: 3.17 + + Extension suffix for modules. + + Information computed from ``sysconfig.get_config_var('EXT_SUFFIX')`` or + ``sysconfig.get_config_var('SOABI')`` or + ``python3-config --extension-suffix``. + +``Python3_SOSABI`` + .. versionadded:: 3.26 + + Extension suffix for modules using the Stable Application Binary Interface. + + Information computed from ``importlib.machinery.EXTENSION_SUFFIXES`` if the + COMPONENT ``Interpreter`` was specified. Otherwise, the extension is ``abi3`` + except for ``Windows``, ``MSYS`` and ``CYGWIN`` for which this is an empty + string. + ``Python3_Compiler_FOUND`` System has the Python 3 compiler. ``Python3_COMPILER`` @@ -97,16 +166,60 @@ This module will set the following variables in your project ``Python3_COMPILER_ID`` A short string unique to the compiler. Possible values include: * IronPython + +``Python3_DOTNET_LAUNCHER`` + .. versionadded:: 3.18 + + The ``.Net`` interpreter. Only used by ``IronPython`` implementation. + ``Python3_Development_FOUND`` + System has the Python 3 development artifacts. + +``Python3_Development.Module_FOUND`` + .. versionadded:: 3.18 + + System has the Python 3 development artifacts for Python module. + +``Python3_Development.SABIModule_FOUND`` + .. versionadded:: 3.26 + + System has the Python 3 development artifacts for Python module using the + Stable Application Binary Interface. + +``Python3_Development.Embed_FOUND`` + .. versionadded:: 3.18 + + System has the Python 3 development artifacts for Python embedding. + ``Python3_INCLUDE_DIRS`` + The Python 3 include directories. + +``Python3_LINK_OPTIONS`` + .. versionadded:: 3.19 + + The Python 3 link options. Some configurations require specific link options + for a correct build and execution. + ``Python3_LIBRARIES`` The Python 3 libraries. ``Python3_LIBRARY_DIRS`` The Python 3 library directories. ``Python3_RUNTIME_LIBRARY_DIRS`` The Python 3 runtime library directories. +``Python3_SABI_LIBRARIES`` + .. versionadded:: 3.26 + + The Python 3 libraries for the Stable Application Binary Interface. +``Python3_SABI_LIBRARY_DIRS`` + .. versionadded:: 3.26 + + The Python 3 ``SABI`` library directories. +``Python3_RUNTIME_SABI_LIBRARY_DIRS`` + .. versionadded:: 3.26 + + The Python 3 runtime ``SABI`` library directories. ``Python3_VERSION`` Python 3 version. ``Python3_VERSION_MAJOR`` @@ -115,11 +228,25 @@ This module will set the following variables in your project Python 3 minor version. ``Python3_VERSION_PATCH`` Python 3 patch version. + +``Python3_PyPy_VERSION`` + .. versionadded:: 3.18 + + Python 3 PyPy version. + ``Python3_NumPy_FOUND`` + .. versionadded:: 3.14 + System has the NumPy. + ``Python3_NumPy_INCLUDE_DIRS`` - The NumPy include directries. + .. versionadded:: 3.14 + + The NumPy include directories. + ``Python3_NumPy_VERSION`` + .. versionadded:: 3.14 + The NumPy version. Hints @@ -134,43 +261,280 @@ Hints * If set to TRUE, search **only** for static libraries. * If set to FALSE, search **only** for shared libraries. + .. note:: + + This hint will be ignored on ``Windows`` because static libraries are not + available on this platform. + +``Python3_FIND_ABI`` + .. versionadded:: 3.16 + + This variable defines which ABIs, as defined in :pep:`3149`, should be + searched. + + .. note:: + + If ``Python3_FIND_ABI`` is not defined, any ABI will be searched. + + The ``Python3_FIND_ABI`` variable is a 3-tuple specifying, in that order, + ``pydebug`` (``d``), ``pymalloc`` (``m``) and ``unicode`` (``u``) flags. + Each element can be set to one of the following: + + * ``ON``: Corresponding flag is selected. + * ``OFF``: Corresponding flag is not selected. + * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched. + + From this 3-tuple, various ABIs will be searched starting from the most + specialized to the most general. Moreover, ``debug`` versions will be + searched **after** ``non-debug`` ones. + + For example, if we have:: + + set (Python3_FIND_ABI "ON" "ANY" "ANY") + + The following flags combinations will be appended, in that order, to the + artifact names: ``dmu``, ``dm``, ``du``, and ``d``. + + And to search any possible ABIs:: + + set (Python3_FIND_ABI "ANY" "ANY" "ANY") + + The following combinations, in that order, will be used: ``mu``, ``m``, + ``u``, ````, ``dmu``, ``dm``, ``du`` and ``d``. + + .. note:: + + This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems, + when ``Python3_FIND_ABI`` is defined, ``Python`` distributions from + `python.org `_ will be found only if value for + each flag is ``OFF`` or ``ANY``. + +``Python3_FIND_STRATEGY`` + .. versionadded:: 3.15 + + This variable defines how lookup will be done. + The ``Python3_FIND_STRATEGY`` variable can be set to one of the following: + + * ``VERSION``: Try to find the most recent version in all specified + locations. + This is the default if policy :policy:`CMP0094` is undefined or set to + ``OLD``. + * ``LOCATION``: Stops lookup as soon as a version satisfying version + constraints is founded. + This is the default if policy :policy:`CMP0094` is set to ``NEW``. + + See also ``Python3_FIND_UNVERSIONED_NAMES``. + ``Python3_FIND_REGISTRY`` + .. versionadded:: 3.13 + On Windows the ``Python3_FIND_REGISTRY`` variable determine the order of preference between registry and environment variables. - the ``Python3_FIND_REGISTRY`` variable can be set to empty or one of the - following: + The ``Python3_FIND_REGISTRY`` variable can be set to one of the following: * ``FIRST``: Try to use registry before environment variables. This is the default. * ``LAST``: Try to use registry after environment variables. * ``NEVER``: Never try to use registry. -``CMAKE_FIND_FRAMEWORK`` - On OS X the :variable:`CMAKE_FIND_FRAMEWORK` variable determine the order of +``Python3_FIND_FRAMEWORK`` + .. versionadded:: 3.15 + + On macOS the ``Python3_FIND_FRAMEWORK`` variable determine the order of preference between Apple-style and unix-style package components. + This variable can take same values as :variable:`CMAKE_FIND_FRAMEWORK` + variable. .. note:: Value ``ONLY`` is not supported so ``FIRST`` will be used instead. + If ``Python3_FIND_FRAMEWORK`` is not defined, :variable:`CMAKE_FIND_FRAMEWORK` + variable will be used, if any. + +``Python3_FIND_VIRTUALENV`` + .. versionadded:: 3.15 + + This variable defines the handling of virtual environments managed by + ``virtualenv`` or ``conda``. It is meaningful only when a virtual environment + is active (i.e. the ``activate`` script has been evaluated). In this case, it + takes precedence over ``Python3_FIND_REGISTRY`` and ``CMAKE_FIND_FRAMEWORK`` + variables. The ``Python3_FIND_VIRTUALENV`` variable can be set to one of the + following: + + * ``FIRST``: The virtual environment is used before any other standard + paths to look-up for the interpreter. This is the default. + * ``ONLY``: Only the virtual environment is used to look-up for the + interpreter. + * ``STANDARD``: The virtual environment is not used to look-up for the + interpreter but environment variable ``PATH`` is always considered. + In this case, variable ``Python3_FIND_REGISTRY`` (Windows) or + ``CMAKE_FIND_FRAMEWORK`` (macOS) can be set with value ``LAST`` or + ``NEVER`` to select preferably the interpreter from the virtual + environment. + + .. versionadded:: 3.17 + Added support for ``conda`` environments. + + .. note:: + + If the component ``Development`` is requested, it is **strongly** + recommended to also include the component ``Interpreter`` to get expected + result. + +``Python3_FIND_IMPLEMENTATIONS`` + .. versionadded:: 3.18 + + This variable defines, in an ordered list, the different implementations + which will be searched. The ``Python3_FIND_IMPLEMENTATIONS`` variable can + hold the following values: + + * ``CPython``: this is the standard implementation. Various products, like + ``Anaconda`` or ``ActivePython``, rely on this implementation. + * ``IronPython``: This implementation use the ``CSharp`` language for + ``.NET Framework`` on top of the `Dynamic Language Runtime` (``DLR``). + See `IronPython `_. + * ``PyPy``: This implementation use ``RPython`` language and + ``RPython translation toolchain`` to produce the python interpreter. + See `PyPy `_. + + The default value is: + + * Windows platform: ``CPython``, ``IronPython`` + * Other platforms: ``CPython`` + + .. note:: + + This hint has the lowest priority of all hints, so even if, for example, + you specify ``IronPython`` first and ``CPython`` in second, a python + product based on ``CPython`` can be selected because, for example with + ``Python3_FIND_STRATEGY=LOCATION``, each location will be search first for + ``IronPython`` and second for ``CPython``. + + .. note:: + + When ``IronPython`` is specified, on platforms other than ``Windows``, the + ``.Net`` interpreter (i.e. ``mono`` command) is expected to be available + through the ``PATH`` variable. + +``Python3_FIND_UNVERSIONED_NAMES`` + .. versionadded:: 3.20 + + This variable defines how the generic names will be searched. Currently, it + only applies to the generic names of the interpreter, namely, ``python3`` and + ``python``. + The ``Python3_FIND_UNVERSIONED_NAMES`` variable can be set to one of the + following values: + + * ``FIRST``: The generic names are searched before the more specialized ones + (such as ``python3.5`` for example). + * ``LAST``: The generic names are searched after the more specialized ones. + This is the default. + * ``NEVER``: The generic name are not searched at all. + + See also ``Python3_FIND_STRATEGY``. + +Artifacts Specification +^^^^^^^^^^^^^^^^^^^^^^^ + +.. versionadded:: 3.16 + +To solve special cases, it is possible to specify directly the artifacts by +setting the following variables: + +``Python3_EXECUTABLE`` + The path to the interpreter. + +``Python3_COMPILER`` + The path to the compiler. + +``Python3_DOTNET_LAUNCHER`` + .. versionadded:: 3.18 + + The ``.Net`` interpreter. Only used by ``IronPython`` implementation. + +``Python3_LIBRARY`` + The path to the library. It will be used to compute the + variables ``Python3_LIBRARIES``, ``Python3_LIBRARY_DIRS`` and + ``Python3_RUNTIME_LIBRARY_DIRS``. + +``Python3_SABI_LIBRARY`` + .. versionadded:: 3.26 + + The path to the library for Stable Application Binary Interface. It will be + used to compute the variables ``Python3_SABI_LIBRARIES``, + ``Python3_SABI_LIBRARY_DIRS`` and ``Python3_RUNTIME_SABI_LIBRARY_DIRS``. + +``Python3_INCLUDE_DIR`` + The path to the directory of the ``Python`` headers. It will be used to + compute the variable ``Python3_INCLUDE_DIRS``. + +``Python3_NumPy_INCLUDE_DIR`` + The path to the directory of the ``NumPy`` headers. It will be used to + compute the variable ``Python3_NumPy_INCLUDE_DIRS``. + +.. note:: + + All paths must be absolute. Any artifact specified with a relative path + will be ignored. + .. note:: - If a Python virtual environment is configured, set variable - ``Python_FIND_REGISTRY`` (Windows) or ``CMAKE_FIND_FRAMEWORK`` (macOS) with - value ``LAST`` or ``NEVER`` to select it preferably. + When an artifact is specified, all ``HINTS`` will be ignored and no search + will be performed for this artifact. + + If more than one artifact is specified, it is the user's responsibility to + ensure the consistency of the various artifacts. + +By default, this module supports multiple calls in different directories of a +project with different version/component requirements while providing correct +and consistent results for each call. To support this behavior, CMake cache +is not used in the traditional way which can be problematic for interactive +specification. So, to enable also interactive specification, module behavior +can be controlled with the following variable: + +``Python3_ARTIFACTS_INTERACTIVE`` + .. versionadded:: 3.18 + + Selects the behavior of the module. This is a boolean variable: + + * If set to ``TRUE``: Create CMake cache entries for the above artifact + specification variables so that users can edit them interactively. + This disables support for multiple version/component requirements. + * If set to ``FALSE`` or undefined: Enable multiple version/component + requirements. Commands ^^^^^^^^ This module defines the command ``Python3_add_library`` (when :prop_gbl:`CMAKE_ROLE` is ``PROJECT``), which has the same semantics as -:command:`add_library`, but takes care of Python module naming rules -(only applied if library is of type ``MODULE``), and adds a dependency to target -``Python3::Python``:: - - Python3_add_library (my_module MODULE src1.cpp) - -If library type is not specified, ``MODULE`` is assumed. +:command:`add_library` and adds a dependency to target ``Python3::Python`` or, +when library type is ``MODULE``, to target ``Python3::Module`` or +``Python3::SABIModule`` (when ``USE_SABI`` option is specified) and takes care +of Python module naming rules:: + + Python3_add_library ( [STATIC | SHARED | MODULE [USE_SABI ] [WITH_SOABI]] + [ ...]) + +If the library type is not specified, ``MODULE`` is assumed. + +.. versionadded:: 3.17 + For ``MODULE`` library type, if option ``WITH_SOABI`` is specified, the + module suffix will include the ``Python3_SOABI`` value, if any. + +.. versionadded:: 3.26 + For ``MODULE`` type, if the option ``USE_SABI`` is specified, the + preprocessor definition ``Py_LIMITED_API`` will be specified, as ``PRIVATE``, + for the target ```` with the value computed from ```` argument. + The expected format for ```` is ``major[.minor]``, where each + component is a numeric value. If ``minor`` component is specified, the + version should be, at least, ``3.2`` which is the version where the + `Stable Application Binary Interface `_ + was introduced. Specifying only major version ``3`` is equivalent to ``3.2``. + + When option ``WITH_SOABI`` is also specified, the module suffix will include + the ``Python3_SOSABI`` value, if any. #]=======================================================================] -- 2.47.3