فهرست منبع

Merge topic 'FindPython-free-threaded-python'

9a0dee7257 FindPython: Add support for free threaded python
4178ab928f FindPython: Fix spelling error

Acked-by: Kitware Robot <[email protected]>
Acked-by: buildbot <[email protected]>
Merge-request: !9578
Brad King 1 سال پیش
والد
کامیت
a67bb7f488

+ 5 - 0
Help/release/dev/FindPython-free_threaded_Python.rst

@@ -0,0 +1,5 @@
+FindPython-free_thread_Python
+-----------------------------
+
+* The :module:`FindPython`, :module:`FindPython3` modules gained the support
+  of the free threaded python version.

+ 25 - 14
Modules/FindPython.cmake

@@ -310,42 +310,53 @@ Hints
 
 
     This hint will be honored only when searched for ``Python`` version 3.
     This hint will be honored only when searched for ``Python`` version 3.
 
 
-  .. note::
+  The ``Python_FIND_ABI`` variable is a 4-tuple specifying, in that order,
+  ``pydebug`` (``d``), ``pymalloc`` (``m``), ``unicode`` (``u``) and
+  ``gil_disabled`` (``t``) flags.
 
 
-    If ``Python_FIND_ABI`` is not defined, any ABI will be searched.
+  .. versionadded:: 3.30
+    A fourth element, specifying the ``gil_disabled`` flag (i.e. free
+    threaded python), is added and is optional. If not specified, the value is
+    ``OFF``.
 
 
-  The ``Python_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:
   Each element can be set to one of the following:
 
 
   * ``ON``: Corresponding flag is selected.
   * ``ON``: Corresponding flag is selected.
   * ``OFF``: Corresponding flag is not selected.
   * ``OFF``: Corresponding flag is not selected.
   * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched.
   * ``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.
+  .. note::
+
+    If ``Python_FIND_ABI`` is not defined, any ABI will be searched.
+
+  From this 4-tuple, various ABIs will be searched starting from the most
+  specialized to the most general. Moreover, when ``ANY`` is specified for
+  ``pydebug`` and ``gil_disabled``, ``debug`` and ``free threaded`` versions
+  will be searched **after** ``non-debug`` and ``non-gil-disabled`` ones.
 
 
   For example, if we have::
   For example, if we have::
 
 
-    set (Python_FIND_ABI "ON" "ANY" "ANY")
+    set (Python_FIND_ABI "ON" "ANY" "ANY" "ON")
 
 
   The following flags combinations will be appended, in that order, to the
   The following flags combinations will be appended, in that order, to the
-  artifact names: ``dmu``, ``dm``, ``du``, and ``d``.
+  artifact names: ``tdmu``, ``tdm``, ``tdu``, and ``td``.
 
 
   And to search any possible ABIs::
   And to search any possible ABIs::
 
 
-    set (Python_FIND_ABI "ANY" "ANY" "ANY")
+    set (Python_FIND_ABI "ANY" "ANY" "ANY" "ANY")
 
 
   The following combinations, in that order, will be used: ``mu``, ``m``,
   The following combinations, in that order, will be used: ``mu``, ``m``,
-  ``u``, ``<empty>``, ``dmu``, ``dm``, ``du`` and ``d``.
+  ``u``, ``<empty>``, ``dmu``, ``dm``, ``du``, ``d``, ``tmu``, ``tm``, ``tu``,
+  ``t``, ``tdmu``, ``tdm``, ``tdu``, and ``td``.
 
 
   .. note::
   .. note::
 
 
-    This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems,
+    This hint is useful only on ``POSIX`` systems except for the
+    ``gil_disabled`` flag. So, on ``Windows`` systems,
     when ``Python_FIND_ABI`` is defined, ``Python`` distributions from
     when ``Python_FIND_ABI`` is defined, ``Python`` distributions from
-    `python.org <https://www.python.org/>`_ will be found only if value for
-    each flag is ``OFF`` or ``ANY``.
+    `python.org <https://www.python.org/>`_ will be found only if the value for
+    each flag is ``OFF`` or ``ANY`` except for the fourth one
+    (``gil_disabled``).
 
 
 ``Python_FIND_STRATEGY``
 ``Python_FIND_STRATEGY``
   .. versionadded:: 3.15
   .. versionadded:: 3.15

+ 115 - 68
Modules/FindPython/Support.cmake

@@ -226,17 +226,28 @@ endfunction()
 
 
 
 
 function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS)
 function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS)
-  set (abiflags)
+  if (NOT DEFINED _${_PYTHON_PREFIX}_FIND_ABI)
+    set (${_PGABIFLAGS} "<none>" PARENT_SCOPE)
+    return()
+  endif()
+
+  set (abiflags "<none>")
   list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug)
   list (GET _${_PYTHON_PREFIX}_FIND_ABI 0 pydebug)
   list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc)
   list (GET _${_PYTHON_PREFIX}_FIND_ABI 1 pymalloc)
   list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode)
   list (GET _${_PYTHON_PREFIX}_FIND_ABI 2 unicode)
+  list (LENGTH _${_PYTHON_PREFIX}_FIND_ABI find_abi_length)
+  if (find_abi_length GREATER 3)
+    list (GET _${_PYTHON_PREFIX}_FIND_ABI 3 gil)
+  else()
+    set (gil "OFF")
+  endif()
 
 
   if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY")
   if (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ANY")
-    set (abiflags "mu" "m" "u" "")
+    set (abiflags "mu" "m" "u" "<none>")
   elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON")
   elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "ON")
     set (abiflags "mu" "u")
     set (abiflags "mu" "u")
   elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF")
   elseif (pymalloc STREQUAL "ANY" AND unicode STREQUAL "OFF")
-    set (abiflags "m" "")
+    set (abiflags "m" "<none>")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY")
     set (abiflags "mu" "m")
     set (abiflags "mu" "m")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ON")
@@ -244,7 +255,7 @@ function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS)
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "OFF")
     set (abiflags "m")
     set (abiflags "m")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY")
   elseif (pymalloc STREQUAL "ON" AND unicode STREQUAL "ANY")
-    set (abiflags "u" "")
+    set (abiflags "u" "<none>")
   elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON")
   elseif (pymalloc STREQUAL "OFF" AND unicode STREQUAL "ON")
     set (abiflags "u")
     set (abiflags "u")
   endif()
   endif()
@@ -261,10 +272,26 @@ function (_PYTHON_GET_ABIFLAGS _PGABIFLAGS)
       list (TRANSFORM flags PREPEND "d")
       list (TRANSFORM flags PREPEND "d")
       list (APPEND abiflags "${flags}")
       list (APPEND abiflags "${flags}")
     else()
     else()
-      set (abiflags "" "d")
+      set (abiflags "<none>" "d")
     endif()
     endif()
   endif()
   endif()
 
 
+  if (gil STREQUAL "ON")
+    if (abiflags)
+      list (TRANSFORM abiflags PREPEND "t")
+    else()
+      set (abiflags "t")
+    endif()
+  elseif (gil STREQUAL "ANY")
+    if (abiflags)
+      set (flags "${abiflags}")
+      list (TRANSFORM flags PREPEND "t")
+      list (APPEND abiflags "${flags}")
+    else()
+      set (abiflags "<none>" "t")
+    endif()
+  endif()
+  list (TRANSFORM abiflags REPLACE "^(.+)<none>$" "\\1")
   set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE)
   set (${_PGABIFLAGS} "${abiflags}" PARENT_SCOPE)
 endfunction()
 endfunction()
 
 
@@ -277,6 +304,7 @@ function (_PYTHON_GET_PATH_SUFFIXES _PYTHON_PGPS_PATH_SUFFIXES)
 
 
   if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
   if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
     set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
     set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
+    list (TRANSFORM abi REPLACE "^<none>$" "")
   else()
   else()
     set (abi "mu" "m" "u" "")
     set (abi "mu" "m" "u" "")
   endif()
   endif()
@@ -368,19 +396,33 @@ function (_PYTHON_GET_NAMES _PYTHON_PGN_NAMES)
       endif()
       endif()
       foreach (version IN LISTS _PGN_VERSION)
       foreach (version IN LISTS _PGN_VERSION)
         if (_PGN_WIN32)
         if (_PGN_WIN32)
-          string (REPLACE "." "" version_no_dots ${version})
-
-          set (name python${version_no_dots})
+          if (_PGN_INTERPRETER)
+            set (name_version ${version})
+          else()
+            string (REPLACE "." "" name_version ${version})
+          endif()
+          if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
+            set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
+            list (TRANSFORM abi REPLACE "^<none>$" "")
+          else()
+            set (abi "")
+          endif()
+          if (abi)
+            set (abinames "${abi}")
+            list (TRANSFORM abinames PREPEND "python${name_version}")
+          else()
+            set (abinames "python${name_version}")
+          endif()
           if (_PGN_DEBUG)
           if (_PGN_DEBUG)
-            string (APPEND name "_d")
+            list (TRANSFORM abinames APPEND "_d")
           endif()
           endif()
-
-          list (APPEND names "${name}")
+          list (APPEND names ${abinames})
         endif()
         endif()
 
 
         if (_PGN_POSIX)
         if (_PGN_POSIX)
           if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
           if (DEFINED _${_PYTHON_PREFIX}_ABIFLAGS)
             set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
             set (abi "${_${_PYTHON_PREFIX}_ABIFLAGS}")
+            list (TRANSFORM abi REPLACE "^<none>$" "")
           else()
           else()
             if (_PGN_INTERPRETER OR _PGN_CONFIG)
             if (_PGN_INTERPRETER OR _PGN_CONFIG)
               set (abi "")
               set (abi "")
@@ -506,6 +548,9 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
         endif()
         endif()
       endif()
       endif()
     endif()
     endif()
+    if (NAME STREQUAL "ABIFLAGS" AND NOT _values)
+      set (_values "<none>")
+    endif()
   endif()
   endif()
 
 
   if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING)
   if (_${_PYTHON_PREFIX}_EXECUTABLE AND NOT CMAKE_CROSSCOMPILING)
@@ -600,6 +645,20 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
       if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "_d${CMAKE_IMPORT_LIBRARY_SUFFIX}$")
       if (WIN32 AND _${_PYTHON_PREFIX}_LIBRARY_DEBUG MATCHES "_d${CMAKE_IMPORT_LIBRARY_SUFFIX}$")
         set (_values "_d")
         set (_values "_d")
       endif()
       endif()
+    elseif (NAME STREQUAL "ABIFLAGS" AND WIN32)
+      # config var ABIFLAGS does not exist, check GIL specific variable
+      execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+                               "import sys; import sysconfig; sys.stdout.write(str(sysconfig.get_config_var('Py_GIL_DISABLED')))"
+                     RESULT_VARIABLE _result
+                     OUTPUT_VARIABLE _values
+                     ERROR_QUIET
+                     OUTPUT_STRIP_TRAILING_WHITESPACE)
+      if (result OR NOT _values EQUAL "1")
+        # assume ABI is not supported or GIL is set
+        set (_values "<none>")
+      else()
+        set (_values "t")
+      endif()
     else()
     else()
       set (config_flag "${NAME}")
       set (config_flag "${NAME}")
       if (NAME STREQUAL "CONFIGDIR")
       if (NAME STREQUAL "CONFIGDIR")
@@ -614,6 +673,9 @@ function (_PYTHON_GET_CONFIG_VAR _PYTHON_PGCV_VALUE NAME)
       if (_result)
       if (_result)
         unset (_values)
         unset (_values)
       endif()
       endif()
+      if (NAME STREQUAL "ABIFLAGS" AND NOT _values)
+        set (_values "<none>")
+      endif()
     endif()
     endif()
   endif()
   endif()
 
 
@@ -650,26 +712,27 @@ function (_PYTHON_GET_VERSION)
   unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE)
   unset (${_PGV_PREFIX}VERSION_PATCH PARENT_SCOPE)
   unset (${_PGV_PREFIX}ABI PARENT_SCOPE)
   unset (${_PGV_PREFIX}ABI PARENT_SCOPE)
 
 
+  unset (abi)
+
   if (_PGV_LIBRARY)
   if (_PGV_LIBRARY)
     # retrieve version and abi from library name
     # retrieve version and abi from library name
     if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE)
     if (_${_PYTHON_PREFIX}_LIBRARY_RELEASE)
       get_filename_component (library_name "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" NAME)
       get_filename_component (library_name "${_${_PYTHON_PREFIX}_LIBRARY_RELEASE}" NAME)
       # extract version from library name
       # extract version from library name
-      if (library_name MATCHES "python([23])([0-9]+)")
+      if (library_name MATCHES "python([23])([0-9]+)([tdmu]*)")
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" 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}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 (abi "${CMAKE_MATCH_3}")
+      elseif (library_name MATCHES "python([23])\\.([0-9]+)([tdmu]*)")
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" 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}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE)
-        set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_3}" PARENT_SCOPE)
+        set (abi "${CMAKE_MATCH_3}")
       elseif (library_name MATCHES "pypy([23])\\.([0-9]+)-c")
       elseif (library_name MATCHES "pypy([23])\\.([0-9]+)-c")
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MINOR "${CMAKE_MATCH_2}" 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}VERSION "${CMAKE_MATCH_1}.${CMAKE_MATCH_2}" PARENT_SCOPE)
-        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
       elseif (library_name MATCHES "pypy(3)?-c")
       elseif (library_name MATCHES "pypy(3)?-c")
         set (version "${CMAKE_MATCH_1}")
         set (version "${CMAKE_MATCH_1}")
         # try to pick-up a more precise version from the path
         # try to pick-up a more precise version from the path
@@ -685,7 +748,6 @@ function (_PYTHON_GET_VERSION)
           set (${_PGV_PREFIX}VERSION_MAJOR "2" PARENT_SCOPE)
           set (${_PGV_PREFIX}VERSION_MAJOR "2" PARENT_SCOPE)
           set (${_PGV_PREFIX}VERSION "2" PARENT_SCOPE)
           set (${_PGV_PREFIX}VERSION "2" PARENT_SCOPE)
         endif()
         endif()
-        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
       endif()
       endif()
     endif()
     endif()
   elseif (_PGV_SABI_LIBRARY)
   elseif (_PGV_SABI_LIBRARY)
@@ -693,14 +755,13 @@ function (_PYTHON_GET_VERSION)
     if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
     if (_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE)
       get_filename_component (library_name "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" NAME)
       get_filename_component (library_name "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" NAME)
       # extract version from library name
       # extract version from library name
-      if (library_name MATCHES "python([23])([dmu]*)")
+      if (library_name MATCHES "python([23])([tdmu]*)")
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
-        set (${_PGV_PREFIX}ABI "${CMAKE_MATCH_2}" PARENT_SCOPE)
+        set (abi "${CMAKE_MATCH_2}")
       elseif (library_name MATCHES "pypy([23])-c")
       elseif (library_name MATCHES "pypy([23])-c")
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
         set (${_PGV_PREFIX}VERSION "${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")
       elseif (library_name MATCHES "pypy-c")
         # try to pick-up a more precise version from the path
         # try to pick-up a more precise version from the path
         get_filename_component (library_dir "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY)
         get_filename_component (library_dir "${_${_PYTHON_PREFIX}_SABI_LIBRARY_RELEASE}" DIRECTORY)
@@ -708,7 +769,6 @@ function (_PYTHON_GET_VERSION)
           set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
           set (${_PGV_PREFIX}VERSION_MAJOR "${CMAKE_MATCH_1}" PARENT_SCOPE)
           set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
           set (${_PGV_PREFIX}VERSION "${CMAKE_MATCH_1}" PARENT_SCOPE)
         endif()
         endif()
-        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
       endif()
       endif()
     endif()
     endif()
   else()
   else()
@@ -730,39 +790,44 @@ function (_PYTHON_GET_VERSION)
 
 
       # compute ABI flags
       # compute ABI flags
       if (version_major VERSION_GREATER "2")
       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 "")
+        set (config_flags "Py_GIL_DISABLED|Py_DEBUG|Py_UNICODE_SIZE|MS_WIN32")
+        if ("${version_major}.${version_minor}" VERSION_LESS "3.8")
+          # pymalloc is not the default. Must be part of the name signature
+          string (APPEND config_flags "|WITH_PYMALLOC")
+        endif()
+        file (STRINGS "${_${_PYTHON_PREFIX}_INCLUDE_DIR}/pyconfig.h" config REGEX "(${config_flags})")
+        if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+MS_WIN32")
+          # same header is used regardless abi, so set is as <none>
+          set (abi "<none>")
         else()
         else()
           if (NOT config)
           if (NOT config)
             # pyconfig.h can be a wrapper to a platform specific pyconfig.h
             # pyconfig.h can be a wrapper to a platform specific pyconfig.h
             # In this case, try to identify ABI from include directory
             # In this case, try to identify ABI from include directory
-            if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([dmu]*)")
+            if (_${_PYTHON_PREFIX}_INCLUDE_DIR MATCHES "python${version_major}\\.${version_minor}+([tdmu]*)")
               set (abi "${CMAKE_MATCH_1}")
               set (abi "${CMAKE_MATCH_1}")
-            else()
-              set (abi "")
             endif()
             endif()
           else()
           else()
-            if (config MATCHES "#[ ]*define[ ]+Py_DEBUG[ ]+1")
+            if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+Py_GIL_DISABLED[ ]+1")
+              string (APPEND abi "t")
+            endif()
+            if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+Py_DEBUG[ ]+1")
               string (APPEND abi "d")
               string (APPEND abi "d")
             endif()
             endif()
-            if (config MATCHES "#[ ]*define[ ]+WITH_PYMALLOC[ ]+1")
+            if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+WITH_PYMALLOC[ ]+1")
               string (APPEND abi "m")
               string (APPEND abi "m")
             endif()
             endif()
-            if (config MATCHES "#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4")
+            if (config MATCHES "(^|;)[ ]*#[ ]*define[ ]+Py_UNICODE_SIZE[ ]+4")
               string (APPEND abi "u")
               string (APPEND abi "u")
             endif()
             endif()
           endif()
           endif()
-          set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE)
         endif()
         endif()
-      else()
-        # ABI not supported
-        set (${_PGV_PREFIX}ABI "" PARENT_SCOPE)
       endif()
       endif()
     endif()
     endif()
   endif()
   endif()
+  if (NOT abi)
+    set (abi "<none>")
+  endif()
+  set (${_PGV_PREFIX}ABI "${abi}" PARENT_SCOPE)
 endfunction()
 endfunction()
 
 
 function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME)
 function (_PYTHON_GET_LAUNCHER _PYTHON_PGL_NAME)
@@ -845,20 +910,11 @@ function (_PYTHON_VALIDATE_INTERPRETER)
     return()
     return()
   endif()
   endif()
 
 
-  _python_get_launcher (launcher INTERPRETER)
+  _python_get_launcher (_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER INTERPRETER)
 
 
   # validate ABI compatibility
   # validate ABI compatibility
   if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI)
   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()
+    _python_get_config_var (abi ABIFLAGS)
     if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
     if (NOT abi IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
       # incompatible ABI
       # 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}_Interpreter_REASON_FAILURE PROPERTY VALUE "Wrong ABI for the interpreter \"${_${_PYTHON_PREFIX}_EXECUTABLE}\"")
@@ -869,7 +925,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
 
 
   if (_PVI_IN_RANGE OR _PVI_VERSION)
   if (_PVI_IN_RANGE OR _PVI_VERSION)
     # retrieve full version
     # retrieve full version
-    execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+    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]]))"
                              "import sys; sys.stdout.write('.'.join([str(x) for x in sys.version_info[:3]]))"
                      RESULT_VARIABLE result
                      RESULT_VARIABLE result
                      OUTPUT_VARIABLE version
                      OUTPUT_VARIABLE version
@@ -936,7 +992,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
     if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}")
     if (NOT python_name STREQUAL "python${_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR}${CMAKE_EXECUTABLE_SUFFIX}")
       # executable found do not have version in name
       # executable found do not have version in name
       # ensure major version is OK
       # ensure major version is OK
-      execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+      execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                                "import sys; sys.stdout.write(str(sys.version_info[0]))"
                                "import sys; sys.stdout.write(str(sys.version_info[0]))"
                        RESULT_VARIABLE result
                        RESULT_VARIABLE result
                        OUTPUT_VARIABLE version
                        OUTPUT_VARIABLE version
@@ -960,7 +1016,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
         OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
         OR "Development.Embed" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
       AND NOT CMAKE_CROSSCOMPILING)
       AND NOT CMAKE_CROSSCOMPILING)
     # In this case, interpreter must have same architecture as environment
     # In this case, interpreter must have same architecture as environment
-    execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+    execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
                              "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))"
                              "import sys, struct; sys.stdout.write(str(struct.calcsize(\"P\")))"
                      RESULT_VARIABLE result
                      RESULT_VARIABLE result
                      OUTPUT_VARIABLE size
                      OUTPUT_VARIABLE size
@@ -984,7 +1040,7 @@ function (_PYTHON_VALIDATE_INTERPRETER)
       else()
       else()
         set(target_arm FALSE)
         set(target_arm FALSE)
       endif()
       endif()
-      execute_process (COMMAND ${launcher} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
+      execute_process (COMMAND ${_${_PYTHON_PREFIX}_INTERPRETER_LAUNCHER} "${_${_PYTHON_PREFIX}_EXECUTABLE}" -c
         "import sys, sysconfig; sys.stdout.write(sysconfig.get_platform())"
         "import sys, sysconfig; sys.stdout.write(sysconfig.get_platform())"
         RESULT_VARIABLE result
         RESULT_VARIABLE result
         OUTPUT_VARIABLE platform
         OUTPUT_VARIABLE platform
@@ -1252,7 +1308,7 @@ function (_PYTHON_VALIDATE_INCLUDE_DIR)
   # retrieve version from header file
   # retrieve version from header file
   _python_get_version (INCLUDE PREFIX inc_)
   _python_get_version (INCLUDE PREFIX inc_)
 
 
-  if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
+  if (NOT WIN32 AND DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT inc_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
     # incompatible ABI
     # 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}_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")
     set_property (CACHE _${_PYTHON_PREFIX}_INCLUDE_DIR PROPERTY VALUE "${_PYTHON_PREFIX}_INCLUDE_DIR-NOTFOUND")
@@ -1452,7 +1508,7 @@ endif()
 if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS "3")
 if (_${_PYTHON_PREFIX}_REQUIRED_VERSION_MAJOR VERSION_LESS "3")
   # ABI not supported
   # ABI not supported
   unset (_${_PYTHON_PREFIX}_FIND_ABI)
   unset (_${_PYTHON_PREFIX}_FIND_ABI)
-  set (_${_PYTHON_PREFIX}_ABIFLAGS "")
+  set (_${_PYTHON_PREFIX}_ABIFLAGS "<none>")
 else()
 else()
   unset (_${_PYTHON_PREFIX}_FIND_ABI)
   unset (_${_PYTHON_PREFIX}_FIND_ABI)
   unset (_${_PYTHON_PREFIX}_ABIFLAGS)
   unset (_${_PYTHON_PREFIX}_ABIFLAGS)
@@ -1461,12 +1517,12 @@ else()
     string (TOUPPER "${${_PYTHON_PREFIX}_FIND_ABI}" _${_PYTHON_PREFIX}_FIND_ABI)
     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 "^(TRUE|Y(ES)?|1)$" "ON")
     list (TRANSFORM _${_PYTHON_PREFIX}_FIND_ABI REPLACE "^(FALSE|N(O)?|0)$" "OFF")
     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)$")
+    if (NOT _${_PYTHON_PREFIX}_FIND_ABI MATCHES "^(ON|OFF|ANY);(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")
       message (AUTHOR_WARNING "Find${_PYTHON_PREFIX}: ${${_PYTHON_PREFIX}_FIND_ABI}: invalid value for '${_PYTHON_PREFIX}_FIND_ABI'. Ignore it")
       unset (_${_PYTHON_PREFIX}_FIND_ABI)
       unset (_${_PYTHON_PREFIX}_FIND_ABI)
     endif()
     endif()
-    _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS)
   endif()
   endif()
+  _python_get_abiflags (_${_PYTHON_PREFIX}_ABIFLAGS)
 endif()
 endif()
 unset (${_PYTHON_PREFIX}_SOABI)
 unset (${_PYTHON_PREFIX}_SOABI)
 unset (${_PYTHON_PREFIX}_SOSABI)
 unset (${_PYTHON_PREFIX}_SOSABI)
@@ -2168,16 +2224,7 @@ if ("Interpreter" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS)
 
 
         # Use interpreter version and ABI for future searches to ensure consistency
         # Use interpreter version and ABI for future searches to ensure consistency
         set (_${_PYTHON_PREFIX}_FIND_VERSIONS ${${_PYTHON_PREFIX}_VERSION_MAJOR}.${${_PYTHON_PREFIX}_VERSION_MINOR})
         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()
+        _python_get_config_var (_${_PYTHON_PREFIX}_ABIFLAGS ABIFLAGS)
       endif()
       endif()
 
 
       if (${_PYTHON_PREFIX}_Interpreter_FOUND)
       if (${_PYTHON_PREFIX}_Interpreter_FOUND)
@@ -2835,7 +2882,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
           if (_${_PYTHON_PREFIX}_RESULT)
           if (_${_PYTHON_PREFIX}_RESULT)
             # assume ABI is not supported
             # assume ABI is not supported
-            set (__${_PYTHON_PREFIX}_ABIFLAGS "")
+            set (__${_PYTHON_PREFIX}_ABIFLAGS "<none>")
           endif()
           endif()
           if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
           if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
             # Wrong ABI
             # Wrong ABI
@@ -2930,7 +2977,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
                            OUTPUT_STRIP_TRAILING_WHITESPACE)
           if (_${_PYTHON_PREFIX}_RESULT)
           if (_${_PYTHON_PREFIX}_RESULT)
             # assume ABI is not supported
             # assume ABI is not supported
-            set (__${_PYTHON_PREFIX}_ABIFLAGS "")
+            set (__${_PYTHON_PREFIX}_ABIFLAGS "<none>")
           endif()
           endif()
           if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
           if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND NOT __${_PYTHON_PREFIX}_ABIFLAGS IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)
             # Wrong ABI
             # Wrong ABI
@@ -3723,7 +3770,7 @@ if (("Development.Module" IN_LIST ${_PYTHON_PREFIX}_FIND_COMPONENTS
     endif()
     endif()
     if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND
     if (DEFINED _${_PYTHON_PREFIX}_FIND_ABI AND
         (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS
         (NOT _${_PYTHON_PREFIX}_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS
-          OR NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS))
+          OR (NOT WIN32 AND NOT _${_PYTHON_PREFIX}_INC_ABI IN_LIST _${_PYTHON_PREFIX}_ABIFLAGS)))
       set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE)
       set (${_PYTHON_PREFIX}_Development.Module_FOUND FALSE)
       set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE)
       set (${_PYTHON_PREFIX}_Development.SABIModule_FOUND FALSE)
       set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE)
       set (${_PYTHON_PREFIX}_Development.Embed_FOUND FALSE)

+ 26 - 15
Modules/FindPython3.cmake

@@ -308,42 +308,53 @@ Hints
   This variable defines which ABIs, as defined in :pep:`3149`, should be
   This variable defines which ABIs, as defined in :pep:`3149`, should be
   searched.
   searched.
 
 
-  .. note::
+  The ``Python3_FIND_ABI`` variable is a 4-tuple specifying, in that order,
+  ``pydebug`` (``d``), ``pymalloc`` (``m``), ``unicode`` (``u``) and
+  ``gil_disabled`` (``t``) flags.
 
 
-    If ``Python3_FIND_ABI`` is not defined, any ABI will be searched.
+  .. versionadded:: 3.30
+    A fourth element, specifying the ``gil_disabled`` flag (i.e. free
+    threaded python), is added and is optional. If not specified, the value is
+    ``OFF``.
 
 
-  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:
   Each element can be set to one of the following:
 
 
   * ``ON``: Corresponding flag is selected.
   * ``ON``: Corresponding flag is selected.
   * ``OFF``: Corresponding flag is not selected.
   * ``OFF``: Corresponding flag is not selected.
   * ``ANY``: The two possibilities (``ON`` and ``OFF``) will be searched.
   * ``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.
+  .. note::
+
+    If ``Python3_FIND_ABI`` is not defined, any ABI will be searched.
+
+  From this 4-tuple, various ABIs will be searched starting from the most
+  specialized to the most general. Moreover, when ``ANY`` is specified for
+  ``pydebug`` and ``gil_disabled``, ``debug`` and ``free threaded`` versions
+  will be searched **after** ``non-debug`` and ``non-gil-disabled`` ones.
 
 
   For example, if we have::
   For example, if we have::
 
 
-    set (Python3_FIND_ABI "ON" "ANY" "ANY")
+    set (Python3_FIND_ABI "ON" "ANY" "ANY" "ON")
 
 
   The following flags combinations will be appended, in that order, to the
   The following flags combinations will be appended, in that order, to the
-  artifact names: ``dmu``, ``dm``, ``du``, and ``d``.
+  artifact names: ``tdmu``, ``tdm``, ``tdu``, and ``td``.
 
 
   And to search any possible ABIs::
   And to search any possible ABIs::
 
 
-    set (Python3_FIND_ABI "ANY" "ANY" "ANY")
+    set (Python3_FIND_ABI "ANY" "ANY" "ANY" "ANY")
 
 
   The following combinations, in that order, will be used: ``mu``, ``m``,
   The following combinations, in that order, will be used: ``mu``, ``m``,
-  ``u``, ``<empty>``, ``dmu``, ``dm``, ``du`` and ``d``.
+  ``u``, ``<empty>``, ``dmu``, ``dm``, ``du``, ``d``, ``tmu``, ``tm``, ``tu``,
+  ``t``, ``tdmu``, ``tdm``, ``tdu``, and ``td``.
 
 
   .. note::
   .. note::
 
 
-    This hint is useful only on ``POSIX`` systems. So, on ``Windows`` systems,
-    when ``Python3_FIND_ABI`` is defined, ``Python`` distributions from
-    `python.org <https://www.python.org/>`_ will be found only if value for
-    each flag is ``OFF`` or ``ANY``.
+    This hint is useful only on ``POSIX`` systems except for the
+    ``gil_disabled`` flag. So, on ``Windows`` systems,
+    when ``Python_FIND_ABI`` is defined, ``Python`` distributions from
+    `python.org <https://www.python.org/>`_ will be found only if the value for
+    each flag is ``OFF`` or ``ANY`` except for the fourth one
+    (``gil_disabled``).
 
 
 ``Python3_FIND_STRATEGY``
 ``Python3_FIND_STRATEGY``
   .. versionadded:: 3.15
   .. versionadded:: 3.15

+ 1 - 1
Tests/CMakeLists.txt

@@ -1552,7 +1552,7 @@ if(BUILD_TESTING)
     set(CMake_TEST_FindPython2 TRUE)
     set(CMake_TEST_FindPython2 TRUE)
     set(CMake_TEST_FindPython3 TRUE)
     set(CMake_TEST_FindPython3 TRUE)
   endif()
   endif()
-  if(CMake_TEST_FindPython_SABIMOdule)
+  if(CMake_TEST_FindPython_SABIModule)
     set(CMake_TEST_FindPython2_SABIModule TRUE)
     set(CMake_TEST_FindPython2_SABIModule TRUE)
     set(CMake_TEST_FindPython3_SABIModule TRUE)
     set(CMake_TEST_FindPython3_SABIModule TRUE)
   endif()
   endif()

+ 5 - 0
Tests/FindPython/Python3/CMakeLists.txt

@@ -76,6 +76,11 @@ if (abi MATCHES "u")
 else()
 else()
   list (APPEND Python3_VALID_ABI "OFF")
   list (APPEND Python3_VALID_ABI "OFF")
 endif()
 endif()
+if (abi MATCHES "t")
+  list (APPEND Python3_VALID_ABI "ON")
+else()
+  list (APPEND Python3_VALID_ABI "OFF")
+endif()
 # build an invalid pattern for ABI
 # build an invalid pattern for ABI
 set (Python3_INVALID_ABI)
 set (Python3_INVALID_ABI)
 foreach (abi IN LISTS Python3_VALID_ABI)
 foreach (abi IN LISTS Python3_VALID_ABI)