1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
|
##########################################################################
# Copyright (C) 2019 by Kneib Francois #
# #
# This program is free software; it is licensed under the terms of the #
# GNU General Public License v2 or later. See file LICENSE for details. #
##########################################################################
# Inspired by http://www.cmake.org/pipermail/cmake/2011-January/041666.html
#
# - Find Python Module
FUNCTION(FIND_PYTHON_MODULE module)
#Set a variable with the module name in upper case
STRING(TOUPPER ${module} module_upper)
#Reset result value as this function can be called multiple times while testing different python versions:
UNSET(PY_${module_upper} CACHE)
UNSET(${module_upper}_INCLUDE_DIR CACHE)
IF(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
SET(${module_upper}_FIND_REQUIRED TRUE)
ENDIF(ARGC GREATER 1 AND ARGV1 STREQUAL "REQUIRED")
EXECUTE_PROCESS(COMMAND "${PYTHON_EXECUTABLE}" "-c"
#Since tkinter does not have __version__ attribute, use TkVersion attribute instead to get the version
"import re, ${module}; \
location = re.compile('/__init__.py.*').sub('', ${module}.__file__); \
include_dir = ${module}.get_include() if hasattr(${module}, 'get_include') else None; \
version = ${module}.TkVersion if hasattr(${module}, 'TkVersion') else ${module}.__version__; \
print(location, include_dir, version);"
RESULT_VARIABLE _${module}_status
ERROR_VARIABLE _${module}_error
OUTPUT_VARIABLE _${module}_output
ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE)
IF(_${module}_status MATCHES 0)
#Split the _${module}_output into a list
STRING(REPLACE " " ";" _${module}_output_list ${_${module}_output})
#Get location from the first element of the list
LIST(GET _${module}_output_list 0 _${module}_location)
#Get include dir from the second element of the list
LIST(GET _${module}_output_list 1 _${module}_include_dir)
#Set MODULE_FOUND variable
IF(_${module}_include_dir MATCHES "None")
SET(PY_${module_upper} ${_${module}_location} CACHE STRING "Location of Python module ${module}")
FIND_PACKAGE_HANDLE_STANDARD_ARGS(${module_upper} DEFAULT_MSG PY_${module_upper})
ELSEIF(_${module}_include_dir MATCHES "Traceback") # Safeguard for mpi4py
SET(${module_upper}_FOUND False)
IF(${module_upper}_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "${module} include directory not found : matches Traceback")
RETURN()
ELSE()
MESSAGE("${module} include directory not found : matches Traceback")
ENDIF()
ELSE() # for numpy, mpi4py or other libraries with get_include() method
SET(${module_upper}_INCLUDE_DIR ${_${module}_include_dir} CACHE STRING "${module} include directory")
FIND_PACKAGE_HANDLE_STANDARD_ARGS(${module_upper} DEFAULT_MSG ${module_upper}_INCLUDE_DIR)
INCLUDE_DIRECTORIES(${${module_upper}_INCLUDE_DIR})
ENDIF()
#Concatenate the rest of the list to create the version in case there is space in the python __version__
#Can't use LIST(SUBLIST ...) as it is not compatible with old versions of cmake
SET(_${module}_version)
LIST(LENGTH _${module}_output_list len)
MATH(EXPR end_index "${len} - 1")
FOREACH(i RANGE 2 ${end_index})
LIST(GET _${module}_output_list ${i} item)
LIST(APPEND _${module}_version ${item})
ENDFOREACH()
STRING(REPLACE ";" " " ${module_upper}_VERSION ${_${module}_version})
#Get the version major, minor and patch depending of the version format
#Feel free to add other formats, for now, only x.x.x, x.x or (x, x) formats are supported
IF(${module_upper}_VERSION MATCHES "^([0-9]+)\\.([0-9]+)\\.([0-9]+)$")
SET(${module_upper}_VERSION_MAJOR "${CMAKE_MATCH_1}" )
SET(${module_upper}_VERSION_MINOR "${CMAKE_MATCH_2}")
SET(${module_upper}_VERSION_PATCH "${CMAKE_MATCH_3}")
ELSEIF(${module_upper}_VERSION MATCHES "^([0-9]+)\\.([0-9]+)$")
SET(${module_upper}_VERSION_MAJOR "${CMAKE_MATCH_1}")
SET(${module_upper}_VERSION_MINOR "${CMAKE_MATCH_2}")
SET(${module_upper}_VERSION_PATCH "0")
ELSEIF(${module_upper}_VERSION MATCHES "^\\(([0-9]+),([0-9]+)\\)$")
SET(${module_upper}_VERSION_MAJOR "${CMAKE_MATCH_1}")
SET(${module_upper}_VERSION_MINOR "${CMAKE_MATCH_2}")
SET(${module_upper}_VERSION_PATCH "0")
ENDIF()
ELSE(_${module}_status MATCHES 0)
SET(${module_upper}_FOUND FALSE)
IF(${module_upper}_FIND_REQUIRED)
MESSAGE(FATAL_ERROR "${module} import failure:\n${_${module}_error}")
RETURN()
ENDIF()
ENDIF(_${module}_status MATCHES 0)
#Set the variables with PARENT_SCOPE in order to access them outside of the function
SET(${module_upper}_FOUND ${${module_upper}_FOUND} PARENT_SCOPE)
IF(${module_upper}_FOUND)
SET(${module_upper}_VERSION ${${module_upper}_VERSION} PARENT_SCOPE)
SET(${module_upper}_VERSION_MAJOR ${${module_upper}_VERSION_MAJOR} PARENT_SCOPE)
SET(${module_upper}_VERSION_MINOR ${${module_upper}_VERSION_MINOR} PARENT_SCOPE)
SET(${module_upper}_VERSION_PATCH ${${module_upper}_VERSION_PATCH} PARENT_SCOPE)
ELSE()
UNSET(${module_upper}_VERSION PARENT_SCOPE)
UNSET(${module_upper}_VERSION_MAJOR PARENT_SCOPE)
UNSET(${module_upper}_VERSION_MINOR PARENT_SCOPE)
UNSET(${module_upper}_VERSION_PATCH PARENT_SCOPE)
ENDIF()
ENDFUNCTION(FIND_PYTHON_MODULE)
# Find PythonLibs, all Python packages needed by yade, and libboost-python. Must be used after a call to FIND_PACKAGE(PythonInterp)
FUNCTION(FIND_PYTHON_PACKAGES)
SET(ALL_PYTHON_DEPENDENCIES_FOUND FALSE PARENT_SCOPE)
SET(fail_message "Failed to import dependencies for Python version ${PYTHON_VERSION_STRING}. NOT FOUND:")
UNSET(PYTHON_LIBRARY CACHE)
UNSET(PYTHON_INCLUDE_DIR CACHE)
FIND_PACKAGE(PythonLibs QUIET)
IF(NOT PYTHONLIBS_FOUND)
MESSAGE(${fail_message} PythonLibs)
RETURN()
ENDIF()
# BEGIN find Boost for py_version
IF ( NOT LocalBoost )
SET(LocalBoost "1.47.0") # Minimal required Boost version
ENDIF ( NOT LocalBoost )
# Next loop is due to libboost-pythonXXX naming mismatch between ubuntu versions and debian versions, so try three possibilities that cover all distros.
FOREACH(PYTHON_PREFIX python python-py python${PYTHON_VERSION_MAJOR}-py) #boost>1.67 should pick-up the first one.
IF(ENABLE_LOGGER)
FIND_PACKAGE(Boost ${LocalBoost} QUIET COMPONENTS ${PYTHON_PREFIX}${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} thread filesystem iostreams regex serialization system date_time log)
ELSE(ENABLE_LOGGER)
FIND_PACKAGE(Boost ${LocalBoost} QUIET COMPONENTS ${PYTHON_PREFIX}${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} thread filesystem iostreams regex serialization system date_time)
ENDIF(ENABLE_LOGGER)
IF(Boost_FOUND)
# for some reason boost_python37 is found but not linked with boost 1.71, we add it here (is it a specific issue within NIX?)
IF (${Boost_VERSION} GREATER 107100 OR ${Boost_VERSION} EQUAL 107100) #maybe it should start at boost 1.67?
MESSAGE("Boost_VERSION=${Boost_VERSION}, adding boost_python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR} lib")
SET(Boost_LIBRARIES "${Boost_LIBRARIES};libboost_python${PYTHON_VERSION_MAJOR}${PYTHON_VERSION_MINOR}.so")
ENDIF()
BREAK()
ENDIF()
ENDFOREACH()
IF(NOT Boost_FOUND) # for opensuze
IF(ENABLE_LOGGER)
FIND_PACKAGE(Boost ${LocalBoost} QUIET COMPONENTS python-py${PYTHON_VERSION_MAJOR} thread filesystem iostreams regex serialization system date_time log)
ELSE(ENABLE_LOGGER)
FIND_PACKAGE(Boost ${LocalBoost} QUIET COMPONENTS python-py${PYTHON_VERSION_MAJOR} thread filesystem iostreams regex serialization system date_time)
ENDIF(ENABLE_LOGGER)
ENDIF()
IF(NOT Boost_FOUND) #as we try multiple python prefixes we have to handle manually the required behavior: fail if we didn't found boost
MESSAGE(${fail_message} libboost-python)
RETURN()
ENDIF()
# END find Boost for py_version
# Find Python modules and set the version variable in the parent scope which is CMakeLists.txt
FOREACH(module IN ITEMS IPython numpy matplotlib pygraphviz Xlib sphinx tkinter)
IF(${module} MATCHES "tkinter" AND ${PYTHON_VERSION_MAJOR} EQUAL 2)
SET(module "Tkinter")
ENDIF()
FIND_PYTHON_MODULE(${module} REQUIRED)
STRING(TOUPPER ${module} module_upper)
SET(${module_upper}_FOUND ${${module_upper}_FOUND} PARENT_SCOPE)
IF(${module_upper}_FOUND)
MESSAGE(STATUS "${module} version found: ${${module_upper}_VERSION}")
SET(${module_upper}_VERSION ${${module_upper}_VERSION} PARENT_SCOPE)
SET(${module_upper}_VERSION_MAJOR ${${module_upper}_VERSION_MAJOR} PARENT_SCOPE)
SET(${module_upper}_VERSION_MINOR ${${module_upper}_VERSION_MINOR} PARENT_SCOPE)
SET(${module_upper}_VERSION_PATCH ${${module_upper}_VERSION_PATCH} PARENT_SCOPE)
ELSE()
MESSAGE(${fail_message} ${module})
UNSET(${module_upper}_VERSION PARENT_SCOPE)
UNSET(${module_upper}_VERSION_MAJOR PARENT_SCOPE)
UNSET(${module_upper}_VERSION_MINOR PARENT_SCOPE)
UNSET(${module_upper}_VERSION_PATCH PARENT_SCOPE)
RETURN()
ENDIF()
ENDFOREACH()
# NOTE: If we are here, we found a suitable Python version with all packages needed.
SET(ALL_PYTHON_DEPENDENCIES_FOUND TRUE PARENT_SCOPE)
#Export findpythonlibs vars to global parent scope:
FOREACH(pythonlibs_var PYTHONLIBS_FOUND PYTHON_LIBRARIES PYTHON_INCLUDE_PATH PYTHON_INCLUDE_DIRS PYTHONLIBS_VERSION_STRING NUMPY_VERSION_MAJOR NUMPY_VERSION_MINOR)
SET(${pythonlibs_var} ${${pythonlibs_var}} PARENT_SCOPE)
ENDFOREACH()
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH})
INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_DIRS})
INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIRS})
#Export findboost vars to global parent scope:
FOREACH(boost_var boost_FOUND Boost_INCLUDE_DIRS Boost_LIBRARY_DIRS Boost_LIBRARIES Boost_<C>_FOUND Boost_<C>_LIBRARY Boost_VERSION Boost_LIB_VERSION Boost_MAJOR_VERSION Boost_MINOR_VERSION Boost_SUBMINOR_VERSION)
SET(${boost_var} ${${boost_var}} PARENT_SCOPE)
ENDFOREACH()
# for checking purpose
MESSAGE("-- Boost_VERSION: " ${Boost_VERSION})
MESSAGE("-- Boost_LIB_VERSION: " ${Boost_LIB_VERSION})
MESSAGE("-- Boost_INCLUDE_DIRS: " ${Boost_INCLUDE_DIRS})
MESSAGE("-- Boost_LIBRARIES: " ${Boost_LIBRARIES})
ENDFUNCTION(FIND_PYTHON_PACKAGES)
# Did findpythoninterp found the python version we want ? Output in PYTHON_VERSION_MATCH.
FUNCTION(PYTHON_VERSION_MATCHES version_number)
SET(PYTHON_VERSION_MATCH FALSE PARENT_SCOPE)
string(REGEX MATCH "([0-9]+)\\.([0-9]+)" _ ${version_number})
set(ver_major ${CMAKE_MATCH_1})
set(ver_minor ${CMAKE_MATCH_2})
MESSAGE("Trying python version: " ${version_number} " parsed as " ${ver_major} " " ${ver_minor})
IF(NOT (${PYTHON_VERSION_MAJOR} EQUAL ${ver_major}))
RETURN()
ENDIF()
IF(NOT(${PYTHON_VERSION_MINOR} EQUAL ${ver_minor}))
RETURN()
ENDIF()
#if we are here we match major and minor
SET(PYTHON_VERSION_MATCH TRUE PARENT_SCOPE)
ENDFUNCTION(PYTHON_VERSION_MATCHES version_number)
|