[CMake] FindPython.cmake in older CMake version

Bram de Greve bram at cocamware.com
Sat Jul 14 16:15:03 EDT 2018


Hi,

I'm testing out CMake 3.12, and I very much like the new 
FindPython.cmake. Being able to use targets for the Python libraries 
means I can much easier package my own library without hardcoded paths.

The only thing is that I want to be compatible with older CMakes >= 
3.0.  Therefore I came up with following approach: write a 
FindPythonCompat.cmake that forwards to FindPython.cmake if CMake >= 
3.12, and uses FindPythonInterp.cmake and FindPythonLibs.cmake to mimic 
the new FindPython.cmake if CMake < 3.12.

You can find that file attached.

Instead of calling this:

find_package(Python 2.7 EXACT REQUIRED COMPONENTS Interpreter 
OPTIONAL_COMPONENTS Development)

I now do this:

list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # 
contains FindPythonInterp.cmake
find_package(PythonCompat 2.7 EXACT REQUIRED COMPONENTS Interpreter 
OPTIONAL_COMPONENTS Development)

The resulting targets and variables are the same as the ones from 
FindPython.cmake.

Another approached I consider is a bit more cunning: add a 
PythonConfig.cmake and PythonConfigVersion.cmake to CMAKE_PREFIX_PATH.  
PythonConfig.cmake would not contain the forward to FindPython.cmake, 
but it would instead rely on the fallback from MODULE to CONFIG mode 
when FindPython.cmake cannot be found in CMake < 3.12. I would then 
still be calling find_package(Python) but I extend CMAKE_PREFIX_PATH:

list(APPEND CMAKE_PREFIX_PATH ${CMAKE_CURRENT_LIST_DIR}/cmake) # 
contains PythonConfig.cmake
find_package(Python 2.7 EXACT REQUIRED COMPONENTS Interpreter 
OPTIONAL_COMPONENTS Development)

In both cases it is of course required that I install this 
FindPythonCompat.cmake or PythonConfig.cmake next to my own library's 
FooConfig.cmake.

Also, in the compatibility mode, it's not my goal to get a 1-on-1 match 
with FindPython.cmake. For my needs, I only require what 
FindInterp.cmake and FindLibs.cmake can deliver.

Now my questions to you are:
- What do you think of these approaches?
- What's the most CMake-esque approach?
- How can I improve this?
- Any better names for FindPythonCompat.cmake? FindPythonShim.cmake?  
FindPythonForward.cmake?

Thanks for your feedback,
Bram


-------------- next part --------------
unset(_PythonCompat_ARGS)
if(PythonCompat_FIND_VERSION)
    list(APPEND _PythonCompat_ARGS ${PythonCompat_FIND_VERSION})
endif()
if(PythonCompat_FIND_VERSION_EXACT)
    list(APPEND _PythonCompat_ARGS EXACT)
endif()
if(PythonCompat_FIND_QUIETLY)
    list(APPEND _PythonCompat_ARGS QUIET)
endif()

if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12)
    # Cmake 3.12 has FindPython.cmake, so we can simply forward to it
    unset(_PythonCompat_COMPONENTS)
    unset(_PythonCompat_OPTIONAL_COMPONENTS)
    foreach(_PythonCompat_COMPONENT ${PythonCompat_FIND_COMPONENTS})
        if(PythonCompat_FIND_REQUIRED_${_PythonCompat_COMPONENT})
            list(APPEND _PythonCompat_COMPONENTS "${_PythonCompat_COMPONENT}")
        else()
            list(APPEND _PythonCompat_OPTIONAL_COMPONENTS "${_PythonCompat_COMPONENT}")
        endif()
    endforeach()

    find_package(Python ${_PythonCompat_ARGS}
        COMPONENTS ${_PythonCompat_COMPONENTS}
        OPTIONAL_COMPONENTS ${_PythonCompat_OPTIONAL_COMPONENTS})

    set(PythonCompat_FOUND ${Python_FOUND})
    return()
endif()


if(NOT PythonCompat_FIND_COMPONENTS)
  set(PythonCompat_FIND_COMPONENTS Interpreter)
  set(PythonCompat_FIND_REQUIRED_Interpreter TRUE)
endif()

set(_PythonCompat_REQUIRED_VARS)

if(DEFINED PythonCompat_FIND_REQUIRED_Interpreter)
    if(Python_EXECUTABLE AND NOT PYTHON_EXECUTABLE)
        set(PYTHON_EXECUTABLE ${Python_EXECUTABLE} CACHE FILEPATH
            "Path to a program." FORCE)
    endif()

    find_package(PythonInterp ${_PythonCompat_ARGS})

    set(Python_Interpreter_FOUND ${PYTHONINTERP_FOUND})
    set(Python_EXECUTABLE ${PYTHON_EXECUTABLE})
    set(Python_VERSION ${PYTHON_VERSION_STRING})
    set(Python_VERSION_MAJOR ${PYTHON_VERSION_MAJOR})
    set(Python_VERSION_MINOR ${PYTHON_VERSION_MINOR})
    set(Python_VERSION_PATCH ${PYTHON_VERSION_PATCH})

    if(TARGET Python::Interpreter AND PYTHONINTERP_FOUND)
        add_executable (Python::Interpreter IMPORTED)
        set_property(TARGET Python::Interpreter PROPERTY
            IMPORTED_LOCATION "${PYTHON_EXECUTABLE}")
    endif()

    if(PythonCompat_FIND_REQUIRED_Interpreter)
        list(APPEND _PythonCompat_REQUIRED_VARS PYTHON_EXECUTABLE)
    endif()
endif()

if(DEFINED PythonCompat_FIND_REQUIRED_Development)
    find_package(PythonLibs ${_PythonCompat_ARGS})

    set(Python_Development_FOUND ${PYTHONLIBS_FOUND})
    set(Python_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS})
    set(Python_LIBRARIES ${PYTHON_LIBRARIES})
    #set(Python_LIBRARY_DIRS ${PYTHON_EXECUTABLE})
    #set(Python_RUNTIME_LIBRARY_DIRS ${PYTHON_EXECUTABLE})

    set(Python_INCLUDE_DIR ${PYTHON_INCLUDE_DIR})
    set(Python_LIBRARY_RELEASE ${PYTHON_LIBRARY_RELEASE})
    set(Python_LIBRARY_DEBUG ${PYTHON_DEBUG_LIBRARY})

    if(NOT TARGET Python::Python AND PYTHONLIBS_FOUND)
        if(PYTHON_LIBRARY MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$" OR
           PYTHON_DEBUG_LIBRARY MATCHES "${CMAKE_SHARED_LIBRARY_SUFFIX}$")
            set(_PythonCompat_LIBRARY_TYPE SHARED)
        else()
            set(_PythonCompat_LIBRARY_TYPE UNKNOWN)
        endif()
        add_library(Python::Python "${_PythonCompat_LIBRARY_TYPE}" IMPORTED)
        set_property(TARGET Python::Python PROPERTY
            INTERFACE_INCLUDE_DIRECTORIES "${PYTHON_INCLUDE_DIRS}")
        if(PYTHON_DEBUG_LIBRARY)
            set_property(TARGET Python::Python APPEND PROPERTY
                IMPORTED_CONFIGURATIONS RELEASE)
            set_target_properties(Python::Python PROPERTIES
                IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "C"
                IMPORTED_LOCATION_RELEASE "${PYTHON_LIBRARY_RELEASE}")
            set_property(TARGET Python::Python APPEND PROPERTY
                IMPORTED_CONFIGURATIONS DEBUG)
            set_target_properties(Python::Python PROPERTIES
                IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "C"
                IMPORTED_LOCATION_DEBUG "${PYTHON_DEBUG_LIBRARY}")
        else()
            set_target_properties(Python::Python PROPERTIES
                IMPORTED_LINK_INTERFACE_LANGUAGES "C"
                IMPORTED_LOCATION "${PYTHON_LIBRARY}")
        endif()
    endif()

    if(PythonCompat_FIND_REQUIRED_Development)
        list(APPEND _PythonCompat_REQUIRED_VARS PYTHON_LIBRARIES
                                                PYTHON_INCLUDE_DIRS)
    endif()
endif()

include(FindPackageHandleStandardArgs)
set(Python_FIND_COMPONENTS ${PythonCompat_FIND_COMPONENTS})
find_package_handle_standard_args(Python
    REQUIRED_VARS ${_PythonCompat_REQUIRED_VARS}
    VERSION_VAR Python_VERSION
    HANDLE_COMPONENTS)
set(PythonCompat_FOUND ${Python_FOUND})


More information about the CMake mailing list