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