On 07/22/2010 09:18 AM, Alan W. Irwin wrote:
> On 2010-07-22 03:09+0200 Michael Hertling wrote:
> 
>> In summary, my point is: Even if the loops are swapped, we wouldn't get
>> a solution that works well in real-world scenarios so I doubt if it's
>> worth the effort [...].
> 
> Hi Michael:
> 
> I think find issues are a really important topic so I am glad you are
> adding so much to the discussion.
> 
> Although I agree with much of what you say, in this particular case I
> believe your above premise is too general, and you have therefore
> drawn an incorrect conclusion.  PLplot and other software packages
> with CMake-based build systems have run into a number of real-world
> Find issues that would be solved if 10718 were fixed. [...]

Wouldn't they be solved if the find modules were fixed instead of 10718?

> [...] Obviously, this
> fix does not deal with the same-directory case, but at least the fix
> limits find issues to just that case alone, and I believe that
> simplification is well worth having.
> 
> Assuming 10718 gets fixed, then one possibility for dealing with the
> same-directory case is to simply publicize this is a well-known find
> issue with current CMake find modules and leave it at that.  That
> "solution" certainly has the merit of simplicity and only fails for
> the rare case where an older version is desired that resides in the
> same directory as a later version (assuming the NAMES are ordered from
> newest to oldest version).

Particularly in regard to Python, I'm not sure if a solution to the
same-directory case should be dropped just like that. Thanks to the
systematically versioned vital components - interpreter, libraries,
library subdirectory and include directory - Python is predestined
to allow multiple installations under the same prefix. Now, from a
user's point of view, I would greatly appreciate an opportunity to
say FIND_PACKAGE(Python 2.5 EXACT ...), and vice versa, I would be
greatly displeased if this attempt fails, despite of 2.5 installed
and even mentioned after NAMES, because of the mere 2.6 presence.

>> [...] one should
>> prefer to improve the find modules and get rid of those non-equivalent
>> alternatives after the NAMES option, in particular hardcoded version
>> lists, and freshly developed modules should use FIND_PACKAGES()'s
>> version interface right from the beginning.
> 
> This certainly sounds promising for dealing with the same-directory
> case.  However, although the version interface appears in the usual
> documentation, I cannot find a single example of a
> <config-file>Version.cmake file.  Am I missing something or is there
> no practical demonstration of the version interface for any Find
> module included with CMake?

As MW already pointed out, these version files aren't relevant
to the search for Python until GvR switches over to CMake. ;)

> Until there is a practical demonstration of the version interface it
> is hard to be sure this is the solution we should recommend for all
> find modules. Therefore, would you be willing to create a simple
> project demonstrating your idea and share it with this list?
> 
> The simple project I have in mind would consist of a single Python
> find module (the usual boiler plate + three find commands) which did
> essentially nothing but find the (versioned) python interpreter,
> header file directory, and library under the version interface; the
> accompanying file mentioned in the documentation to support the
> version interface; and a single CMakeLists.txt file consisting of ~10
> lines which specified the location of that find module (and version
> interface support file); allowed the user the option of setting the
> python version to anything they liked (e.g., nothing, 2, 2.5, 2.6, 3,
> etc.); and used find_package to find and print out the appropriate
> Python interpreter, header file directory, and library for the
> user-specified version.

Look at CMakeLists.txt and FindPython.cmake in the attachment.

To prevent further references to John Ronald Reuel T.: This finder is
neither fully-featured nor production-ready, and it also doesn't claim
to be the most beautiful piece of code ever written; furthermore, it is
targeted at *nix and most probably won't run on another platform out of
the box. Instead, it's just meant to outline a possible approach how to
use FIND_PACKAGE()'s version interface to locate a Python installation
without relying on hardcoded version numbers or an interpreter which
possibly cannot run on the current system.

Remarks:

- PYTHON_NATIVE indicates whether the interpreter may by invoked.
- Requesting "Python" without version means searching for an
  unversioned interpreter and the highest versioned library unless
  PYTHON_NATIVE is true which means querying the interpreter for its
  accompanying library; the former may possibly result in inconsistent
  interpreter/library combinations.
- Requesting "Python 2" means searching for the highest Python 2 but
  not 3; EXACT doesn't matter here.
- Requesting "Python 2.5" means searching for the highest Python 2 but
  at least 2.5 and not 3.
- Requesting "Python 2.5 EXACT" means searching for the highest Python
  2.5 but not 2.6 or higher or 3.
- Requesting "Python 2.5.4" means searching for the highest Python 2
  but at least 2.5.4 and not 3; without PYTHON_NATIVE, this may fail
  as the interpreter is necessary to check for a three-digit version.
- Requesting "Python 2.5.4 EXACT" means searching for Python 2.5.4;
  without PYTHON_NATIVE, this fails for the same reason as above.
- GET_LIBRARY_PATH() is of general purpose and could be transferred
  to a module of its own, possibly along with a GET_PROGRAM_PATH() etc.
- Components aren't recognized, but this shouldn't be hard to implement.

Regards,

Michael
# CMakeLists.txt:
# Call with -DVERSION=[...] -DEXACT=[Y/N] -DPYTHON_NATIVE=[Y/N]
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(FINDPYTHON NONE)
SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}")
IF(EXACT)
    SET(EXACT "EXACT")
ELSE()
    SET(EXACT "")
ENDIF()
FIND_PACKAGE(Python ${VERSION} ${EXACT})
MESSAGE("PYTHON_EXECUTABLE: ${PYTHON_EXECUTABLE}")
MESSAGE("PYTHON_LIBRARY: ${PYTHON_LIBRARY}")
MESSAGE("PYTHON_INCLUDE_DIR: ${PYTHON_INCLUDE_DIR}")
MESSAGE("PYTHON_FOUND: ${PYTHON_FOUND}")
# FindPython.cmake:

# PYTHON_NATIVE indicates an executable Python interpreter.

# Get FIND_LIBRARY()'s search path:
FUNCTION(GET_LIBRARY_PATH PATH)
    UNSET(p)
    FOREACH(i IN LISTS CMAKE_PREFIX_PATH)
        LIST(APPEND p ${i}/lib)
    ENDFOREACH()
    FOREACH(i IN LISTS CMAKE_LIBRARY_PATH CMAKE_FRAMEWORK_PATH)
        LIST(APPEND p ${i})
    ENDFOREACH()
    SET(CMAKE_PREFIX_PATH_ENV $ENV{CMAKE_PREFIX_PATH})
    FOREACH(i IN LISTS CMAKE_PREFIX_PATH_ENV)
        LIST(APPEND p ${i}/lib)
    ENDFOREACH()
    SET(CMAKE_LIBRARY_PATH_ENV $ENV{CMAKE_LIBRARY_PATH})
    SET(CMAKE_FRAMEWORK_PATH_ENV $ENV{CMAKE_FRAMEWORK_PATH})
    FOREACH(i IN LISTS CMAKE_LIBRARY_PATH_ENV CMAKE_FRAMEWORK_PATH_ENV)
        LIST(APPEND p ${i})
    ENDFOREACH()
    SET(PATH_ENV $ENV{PATH})
    STRING(REGEX REPLACE ":" ";" PATH_ENV ${PATH_ENV})
    SET(LIB_ENV $ENV{LIB})
    FOREACH(i IN LISTS PATH_ENV LIB_ENV)
        LIST(APPEND p ${i})
    ENDFOREACH()
    FOREACH(i IN LISTS CMAKE_SYSTEM_PREFIX_PATH)
        LIST(APPEND p ${i}/lib)
    ENDFOREACH()
    FOREACH(i IN LISTS CMAKE_SYSTEM_LIBRARY_PATH CMAKE_SYSTEM_FRAMEWORK_PATH)
        LIST(APPEND p ${i})
    ENDFOREACH()
    # To be enhanced: HINTS/PATHS, NO_..._PATH, CMAKE_FIND_ROOT_PATH etc.
    SET(${PATH} ${p} PARENT_SCOPE)
ENDFUNCTION()

# Search the highest Python version according to GLOBEXPR:
FUNCTION(GET_PYTHON_VERSION VERSION GLOBEXPR)
    SET(v "0")
    FOREACH(i IN LISTS ARGN)
        FILE(GLOB j ${i}/python${GLOBEXPR})
        FOREACH(k IN LISTS j)
            IF(k MATCHES "python[0-9][0-9.]*\$")
                STRING(REGEX REPLACE "^.*python([0-9][0-9.]*)\$" "\\1" k ${k})
                IF(k VERSION_GREATER v)
                    SET(v ${k})
                ENDIF()
            ENDIF()
        ENDFOREACH()
    ENDFOREACH()
    SET(${VERSION} ${v} PARENT_SCOPE)
ENDFUNCTION()

# Query Python interpreter for library:
FUNCTION(QUERY_PYTHON_LIBRARY PATHS NAME INTERPRETER)
    EXECUTE_PROCESS(
        COMMAND ${INTERPRETER} -c "
import distutils.sysconfig;
print(distutils.sysconfig.get_config_var('LIBDIR')
      +';'+distutils.sysconfig.get_config_var('LIBPL'))"
        OUTPUT_VARIABLE p
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    EXECUTE_PROCESS(
        COMMAND ${INTERPRETER} -c "
import distutils.sysconfig;
print('python'+distutils.sysconfig.get_config_var('VERSION'))"
        OUTPUT_VARIABLE n
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    SET(${PATHS} ${p} PARENT_SCOPE)
    SET(${NAME} ${n} PARENT_SCOPE)
ENDFUNCTION()

# Query Python interpreter for include directory:
FUNCTION(QUERY_PYTHON_INCLUDE_DIR PATH INTERPRETER)
    EXECUTE_PROCESS(
        COMMAND ${INTERPRETER} -c "
import distutils.sysconfig;
print(distutils.sysconfig.get_config_var('INCLUDEPY'))"
        OUTPUT_VARIABLE p
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    SET(${PATH} ${p} PARENT_SCOPE)
ENDFUNCTION()

# Query Python interpreter for version:
FUNCTION(QUERY_PYTHON_VERSION VERSION INTERPRETER)
    EXECUTE_PROCESS(
        COMMAND ${INTERPRETER} -c "import sys; print(sys.version)"
        OUTPUT_VARIABLE v
        OUTPUT_STRIP_TRAILING_WHITESPACE
    )
    STRING(REGEX REPLACE "^([^ ]*) .*\$" "\\1" v ${v})
    SET(${VERSION} ${v} PARENT_SCOPE)
ENDFUNCTION()

# For convenience:
SET(MAJVER "${Python_FIND_VERSION_MAJOR}")
SET(MINVER "${Python_FIND_VERSION_MINOR}")

GET_LIBRARY_PATH(LIBPATH)

UNSET(EXEVER)  # Python interpreter version to search
UNSET(LIBVER)  # Python library version to search

# Set up EXEVER/LIBVER from Python_FIND_VERSION et al.:
IF(NOT DEFINED Python_FIND_VERSION_COUNT)
    SET(EXEVER "")  # Search for unversioned interpreter.
    IF(NOT PYTHON_NATIVE)
        # Search most recent libraries w/o querying:
        GET_PYTHON_VERSION(VERSION *.* ${LIBPATH})
        IF(VERSION)
            SET(LIBVER ${VERSION})
        ENDIF()
    ENDIF()
ELSEIF(Python_FIND_VERSION_COUNT EQUAL 1)
    # EXACT doesn't matter here.
    GET_PYTHON_VERSION(VERSION ${MAJVER}.* ${LIBPATH})
    IF(VERSION)
        SET(EXEVER ${VERSION})
        IF(NOT PYTHON_NATIVE)
            SET(LIBVER ${EXEVER})
        ENDIF()
    ENDIF()
ELSE()
    IF(Python_FIND_VERSION_EXACT)
        GET_PYTHON_VERSION(VERSION ${MAJVER}.${MINVER} ${LIBPATH})
        IF(VERSION AND (Python_FIND_VERSION_COUNT EQUAL 2 OR PYTHON_NATIVE))
            SET(EXEVER ${VERSION})
            IF(NOT PYTHON_NATIVE)
                SET(LIBVER ${EXEVER})
            ENDIF()
        # Else: Not found or unacceptable.
        ENDIF()
    ELSE()
        GET_PYTHON_VERSION(VERSION ${MAJVER}.* ${LIBPATH})
        IF(VERSION AND (NOT VERSION VERSION_LESS Python_FIND_VERSION
           OR (VERSION VERSION_EQUAL ${MAJVER}.${MINVER} AND PYTHON_NATIVE)))
            SET(EXEVER ${VERSION})
            IF(NOT PYTHON_NATIVE)
                SET(LIBVER ${EXEVER})
            ENDIF()
        # Else: Not found or unacceptable.
        ENDIF()
    ENDIF()
ENDIF()

# Search interpreter:
IF(DEFINED EXEVER)
    FIND_PROGRAM(PYTHON_EXECUTABLE python${EXEVER})
ENDIF()

# Search library and include directory:
IF(PYTHON_NATIVE AND PYTHON_EXECUTABLE)
    QUERY_PYTHON_LIBRARY(PATHS NAME ${PYTHON_EXECUTABLE})
    FIND_LIBRARY(PYTHON_LIBRARY ${NAME} PATHS ${PATHS} NO_DEFAULT_PATH)
    QUERY_PYTHON_INCLUDE_DIR(PATH ${PYTHON_EXECUTABLE})
    FIND_PATH(PYTHON_INCLUDE_DIR Python.h PATHS ${PATH} NO_DEFAULT_PATH)
ELSEIF(DEFINED LIBVER)
    FIND_LIBRARY(PYTHON_LIBRARY python${LIBVER})
    FIND_LIBRARY(PYTHON_LIBRARY python${LIBVER}
                 PATH_SUFFIXES python${LIBVER}/config)
    FIND_PATH(PYTHON_INCLUDE_DIR Python.h PATH_SUFFIXES python${LIBVER})
ENDIF()

# Check version:
IF(PYTHON_EXECUTABLE AND Python_FIND_VERSION_COUNT GREATER 1)
    IF(Python_FIND_VERSION_EXACT)
        IF(Python_FIND_VERSION_COUNT GREATER 2)
            QUERY_PYTHON_VERSION(VERSION ${PYTHON_EXECUTABLE})
            IF(NOT VERSION VERSION_EQUAL Python_FIND_VERSION)
                UNSET(PYTHON_EXECUTABLE CACHE)
                UNSET(PYTHON_EXECUTABLE)
            ENDIF()
        ENDIF()
    ELSE()
        IF(EXEVER VERSION_LESS Python_FIND_VERSION)
            QUERY_PYTHON_VERSION(VERSION ${PYTHON_EXECUTABLE})
            IF(VERSION VERSION_LESS Python_FIND_VERSION)
                UNSET(PYTHON_EXECUTABLE CACHE)
                UNSET(PYTHON_EXECUTABLE)
            ENDIF()
        ENDIF()
    ENDIF()
ENDIF()

# Finish:
INCLUDE(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(
    PYTHON
    DEFAULT_MSG
    PYTHON_EXECUTABLE PYTHON_LIBRARY PYTHON_INCLUDE_DIR
)
_______________________________________________
Powered by www.kitware.com

Visit other Kitware open-source projects at 
http://www.kitware.com/opensource/opensource.html

Please keep messages on-topic and check the CMake FAQ at: 
http://www.cmake.org/Wiki/CMake_FAQ

Follow this link to subscribe/unsubscribe:
http://www.cmake.org/mailman/listinfo/cmake

Reply via email to