From 61502d395609672cd101da74d290895d20b0e7c7 Mon Sep 17 00:00:00 2001
From: Marc Chevrier <marc.chevrier@gmail.com>
Date: Tue, 7 Jan 2020 16:42:15 +0100
Subject: FindPython: Add variable Python_SOABI

This variable holds the standard extension suffix for modules.

Fixes: #20150
---
 Help/release/dev/FindPython-SOABI.rst |  6 +++++
 Modules/FindPython.cmake              |  7 +++++
 Modules/FindPython/Support.cmake      | 50 ++++++++++++++++++++++++++++++-----
 Modules/FindPython3.cmake             |  7 +++++
 Tests/FindPython/CMakeLists.txt       | 28 ++++++++++++++++++++
 Tests/FindPython/SOABI/CMakeLists.txt | 12 +++++++++
 6 files changed, 104 insertions(+), 6 deletions(-)
 create mode 100644 Help/release/dev/FindPython-SOABI.rst
 create mode 100644 Tests/FindPython/SOABI/CMakeLists.txt

diff --git a/Help/release/dev/FindPython-SOABI.rst b/Help/release/dev/FindPython-SOABI.rst
new file mode 100644
index 0000000..29f7292
--- /dev/null
+++ b/Help/release/dev/FindPython-SOABI.rst
@@ -0,0 +1,6 @@
+FindPython-SOABI
+----------------
+
+* The :module:`FindPython3` and :module:`FindPython` modules gained,
+  respectively, variable ``Python3_SOABI`` and ``Python_SOABI`` giving
+  the standard extension suffix for modules.
diff --git a/Modules/FindPython.cmake b/Modules/FindPython.cmake
index 177ed58..be272e1 100644
--- a/Modules/FindPython.cmake
+++ b/Modules/FindPython.cmake
@@ -93,6 +93,13 @@ This module will set the following variables in your project
 
   Information returned by
   ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)``.
+``Python_SOABI``
+  Extension suffix for modules.
+
+  Information returned by
+  ``distutils.sysconfig.get_config_flag('SOABI')`` or computed from
+  ``distutils.sysconfig.get_config_flag('EXT_SUFFIX')`` or
+  ``python-config --extension-suffix``.
 ``Python_Compiler_FOUND``
   System has the Python compiler.
 ``Python_COMPILER``
diff --git a/Modules/FindPython/Support.cmake b/Modules/FindPython/Support.cmake
index 0163d56..086b229 100644
--- a/Modules/FindPython/Support.cmake
+++ b/Modules/FindPython/Support.cmake
@@ -244,12 +244,16 @@ 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)$")
+  if (NOT NAME MATCHES "^(PREFIX|ABIFLAGS|CONFIGDIR|INCLUDES|LIBS|SOABI)$")
     return()
   endif()
 
   if (_${_PYTHON_PREFIX}_CONFIG)
-    set (config_flag "--${NAME}")
+    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}" ${config_flag}
                      RESULT_VARIABLE _result
@@ -264,6 +268,9 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
         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
+        string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\.(so|pyd))$" "\\1" _values "${_values}")
       endif()
     endif()
   endif()
@@ -289,6 +296,25 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
       if (_result)
         unset (_values)
       endif()
+    elseif (NAME STREQUAL "SOABI")
+      execute_process (COMMAND "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c "import sys; from distutils import sysconfig;sys.stdout.write(';'.join([sysconfig.get_config_var('SOABI') or '',sysconfig.get_config_var('EXT_SUFFIX') or '']))"
+                       RESULT_VARIABLE _result
+                       OUTPUT_VARIABLE _soabi
+                       ERROR_QUIET
+                       OUTPUT_STRIP_TRAILING_WHITESPACE)
+      if (_result)
+        unset (_values)
+      else()
+        list (GET _soabi 0 _values)
+        if (NOT _values)
+          # try to compute SOABI from EXT_SUFFIX
+          list (GET _soabi 1 _values)
+          if (_values)
+            # clean-up: remove prefix character and suffix
+            string (REGEX REPLACE "^[.-](.+)(${CMAKE_SHARED_LIBRARY_SUFFIX}|\.(so|pyd))$" "\\1" _values "${_values}")
+          endif()
+        endif()
+      endif()
     else()
       set (config_flag "${NAME}")
       if (NAME STREQUAL "CONFIGDIR")
@@ -745,6 +771,7 @@ else()
     _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS)
   endif()
 endif()
+unset (${_PYTHON_PREFIX}_SOABI)
 
 # Define lookup strategy
 if (_${_PYTHON_PREFIX}_LOOKUP_POLICY STREQUAL "NEW")
@@ -1267,7 +1294,6 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
 
     # 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)
@@ -1282,6 +1308,10 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       unset (${_PYTHON_PREFIX}_SITELIB)
       unset (${_PYTHON_PREFIX}_SITEARCH)
     endif()
+
+    if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_GREATER_EQUAL 3)
+      _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI)
+    endif()
   else()
     unset (_${_PYTHON_PREFIX}_INTERPRETER_SIGNATURE CACHE)
     unset (${_PYTHON_PREFIX}_INTERPRETER_ID)
@@ -1522,9 +1552,13 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
       unset (_${_PYTHON_PREFIX}_LIBRARY_RELEASE CACHE)
       unset (_${_PYTHON_PREFIX}_LIBRARY_DEBUG CACHE)
       unset (_${_PYTHON_PREFIX}_INCLUDE_DIR CACHE)
-      unset (_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE CACHE)
     endif()
   endif()
+  if (NOT _${_PYTHON_PREFIX}_LIBRARY_RELEASE OR NOT _${_PYTHON_PREFIX}_INCLUDE_DIR)
+    unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
+    unset (_${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE CACHE)
+  endif()
+
   if (DEFINED ${_PYTHON_PREFIX}_LIBRARY
       AND IS_ABSOLUTE "${${_PYTHON_PREFIX}_LIBRARY}")
     set (_${_PYTHON_PREFIX}_LIBRARY_RELEASE "${${_PYTHON_PREFIX}_LIBRARY}" CACHE INTERNAL "")
@@ -2148,6 +2182,11 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
     endif()
   endif()
 
+  if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_GREATER_EQUAL 3
+      AND NOT DEFINED ${_PYTHON_PREFIX}_SOABI)
+    _python_get_config_var (${_PYTHON_PREFIX}_SOABI SOABI)
+  endif()
+
   if (${_PYTHON_PREFIX}_Development_FOUND)
     # compute and save development signature
     string (MD5 __${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE "${_${_PYTHON_PREFIX}_SIGNATURE}:${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}:${_${_PYTHON_PREFIX}_INCLUDE_DIR}")
@@ -2166,6 +2205,7 @@ if ("Development" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
                     ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_RELEASE
                     ${_PYTHON_PREFIX}_RUNTIME_LIBRARY_DEBUG
                     _${_PYTHON_PREFIX}_INCLUDE_DIR
+                    _${_PYTHON_PREFIX}_CONFIG
                     _${_PYTHON_PREFIX}_DEVELOPMENT_SIGNATURE)
 endif()
 
@@ -2425,5 +2465,3 @@ if (DEFINED _${_PYTHON_PREFIX}_CMAKE_FIND_FRAMEWORK)
 else()
   unset (CMAKE_FIND_FRAMEWORK)
 endif()
-
-unset (_${_PYTHON_PREFIX}_CONFIG CACHE)
diff --git a/Modules/FindPython3.cmake b/Modules/FindPython3.cmake
index 0a96fad..00c354e 100644
--- a/Modules/FindPython3.cmake
+++ b/Modules/FindPython3.cmake
@@ -94,6 +94,13 @@ This module will set the following variables in your project
 
   Information returned by
   ``distutils.sysconfig.get_python_lib(plat_specific=True,standard_lib=False)``.
+``Python3_SOABI``
+  Extension suffix for modules.
+
+  Information returned by
+  ``distutils.sysconfig.get_config_flag('SOABI')`` or computed from
+  ``distutils.sysconfig.get_config_flag('EXT_SUFFIX')`` or
+  ``python3-config --extension-suffix``.
 ``Python3_Compiler_FOUND``
   System has the Python 3 compiler.
 ``Python3_COMPILER``
diff --git a/Tests/FindPython/CMakeLists.txt b/Tests/FindPython/CMakeLists.txt
index 5e20dd3..bfec986 100644
--- a/Tests/FindPython/CMakeLists.txt
+++ b/Tests/FindPython/CMakeLists.txt
@@ -148,6 +148,34 @@ if(CMake_TEST_FindPython)
     --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
     )
 
+  if (CMAKE_SYSTEM_NAME MATCHES "Linux|Darwin")
+    add_test(NAME FindPython.Interpreter.SOABI COMMAND
+      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/FindPython/SOABI"
+      "${CMake_BINARY_DIR}/Tests/FindPython/SOABI.Interpreter"
+      ${build_generator_args}
+      --build-project TestSOABI
+      --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
+      "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
+      "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
+      "-DCMake_TEST_FindPython_COMPONENT=Interpreter"
+      --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+      )
+    add_test(NAME FindPython.Development.SOABI COMMAND
+      ${CMAKE_CTEST_COMMAND} -C $<CONFIGURATION>
+      --build-and-test
+      "${CMake_SOURCE_DIR}/Tests/FindPython/SOABI"
+      "${CMake_BINARY_DIR}/Tests/FindPython/SOABI.Development"
+      ${build_generator_args}
+      --build-project TestSOABI
+      --build-options ${build_options} "-Dbuild_generator_args=${build_generator_args}"
+      "-DCMake_SOURCE_DIR=${CMake_SOURCE_DIR}"
+      "-DCMake_BINARY_DIR=${CMake_BINARY_DIR}"
+      "-DCMake_TEST_FindPython_COMPONENT=Development"
+      --test-command ${CMAKE_CTEST_COMMAND} -V -C $<CONFIGURATION>
+      )
+  endif()
 endif()
 
 if(CMake_TEST_FindPython_NumPy)
diff --git a/Tests/FindPython/SOABI/CMakeLists.txt b/Tests/FindPython/SOABI/CMakeLists.txt
new file mode 100644
index 0000000..aea2baf
--- /dev/null
+++ b/Tests/FindPython/SOABI/CMakeLists.txt
@@ -0,0 +1,12 @@
+cmake_minimum_required(VERSION 3.1)
+
+project(TestSOABI C)
+
+find_package(Python3 COMPONENTS ${CMake_TEST_FindPython_COMPONENT})
+if (NOT Python3_FOUND)
+  message (FATAL_ERROR "Fail to found Python 3")
+endif()
+
+if(NOT DEFINED Python3_SOABI)
+  message(FATAL_ERROR "Python3_SOABI for ${CMake_TEST_FindPython_COMPONENT} not found")
+endif()
-- 
cgit v0.12