-
Notifications
You must be signed in to change notification settings - Fork 16
/
FindPythonModules.cmake
359 lines (351 loc) · 14.6 KB
/
FindPythonModules.cmake
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# ============================================================================
# Copyright (c) 2011-2012 University of Pennsylvania
# Copyright (c) 2013-2014 Andreas Schuh
# All rights reserved.
#
# See COPYING file for license information or visit
# http://opensource.andreasschuh.com/cmake-basis/download.html#license
# ============================================================================
##############################################################################
# @file FindPythonModules.cmake
# @brief Find Python modules.
#
# @par Input/Output variables:
# <table border="0">
# <tr>
# @tp @b PythonModules_DIR @endtp
# <td>List of directories where Python modules are installed.</td>
# </tr>
# </table>
# @par Input variables:
# <table border="0">
# <tr>
# @tp @b PythonModules_FIND_COMPONENTS @endtp
# <td>The @c COMPONENTS argument(s) of the find_package() command specifies
# the names of the Python modules to look for.</td>
# </tr>
# <tr>
# @tp @b PythonModules_FIND_OPTIONAL_COMPONENTS @endtp
# <td>The @c OPTIONAL_COMPONENTS argument(s) of the find_package() command
# specifies the names of the Python modules which are not necessarily
# required, but should be searched as well.</td>
# </tr>
# <tr>
# @tp @b PYTHON_EXECUTABLE @endtp
# <td>Path to the Python interpreter. Should be set by first looking
# for the Python interpreter, i.e., find_packages(PythonInterp).
# If set, this module first tries to execute the Python interpreter,
# import the respective Python module, and then derive the search path
# from the @c __file__ attribute of the loaded Python module.
# Otherwise, or if this fails, it looks either for a package
# @c __init__.py file inside a subdirectory named after the specified
# Python module or a @c .py module file in each directory listed in
# the @c PYTHONPATH.</td>
# </tr>
# <tr>
# @tp @b PYTHONPATH @endtp
# <td>Search path for Python modules. If this CMake variable is undefined,
# the corresponding environment variable is used instead if set.
# Only absolute paths in the @c PYTHONPATH are considered.</td>
# </tr>
# </table>
#
# @par Output variables:
# <table border="0">
# <tr>
# @tp @b PythonModules_FOUND @endtp
# <td>Whether all specified Python modules were found.</td>
# </tr>
# <tr>
# @tp @b PythonModules_<module>_FOUND @endtp
# <td>Whether the Python module <module%gt; was found.</td>
# </tr>
# <tr>
# @tp @b PythonModules_<module>_PATH @endtp
# <td>Absolute path of the directory containing the Python module named <module%gt;.</td>
# </tr>
# <tr>
# @tp @b PythonModules_<module> @endtp
# <td>Import target for module named <module>. The location of the
# target is @c PythonModules_<module>_PATH.</tr>
# </tr>
# <tr>
# @tp @b PythonModules_PYTHONPATH @endtp
# <td>The @c PYTHONPATH setting required for the found Python module(s), i.e.,
# The directories that have to be added to the Python search path.
# To support the use of this CMake module more than once with different
# arguments to the find_package() command, e.g., with and without the
# @c REQUIRED argument, the directories containing the found Python
# modules are appended to any existing directories in
# @c PythonModules_PYTHONPATH if not already listed.</td>
# </tr>
# </table>
#
# @ingroup CMakeFindModules
##############################################################################
include (CMakeParseArguments)
# ----------------------------------------------------------------------------
## @brief Find Python module.
#
# If the @c PYTHON_EXECUTABLE variable is set, this function at first tries
# to launch the Python interpreter, import the named Python module, and then
# determines the search path for the Python module from the @c __file__
# attribute of the loaded module. Otherwise, or if this fails, it looks for
# the Python module in the directories listed in the @c PYTHONPATH variable
# if defined. If this variable is not defined, the @c PYTHONPATH environment
# variable is used instead.
#
# @param [in] CACHEVAR Name of CMake cache variable which stores path of
# directory of the Python module. If not set or if
# the cache entry contains space characters only or
# ends in the string NOTFOUND, this function looks for
# the specified Python module. Otherwise, it does nothing
# and leaves the cache entry unmodified.
# @param [in] ARGN The remaining arguments are parsed and the following
# options extract:
# @par
# <table border=0>
# <tr>
# @tp @b NAME module @endtp
# <td>Name of the Python module.</td>
# </tr>
# <tr>
# @tp @b PYTHON_EXECUTABLE python @endtp
# <td>Full path of the Python interpreter executable. If not specified
# the global PYTHON_EXECUTABLE CMake variable/cache entry is used.</td>
# </tr>
# <tr>
# @tp @b PATH dir1 [dir2...] @endtp
# <td>Directories where to look for Python module.</td>
# </tr>
# <tr>
# @tp @b NO_PYTHONPATH @endtp
# <td>Do not consider the @c PYTHONPATH environment variable.</td>
# </tr>
# <tr>
# @tp @b NO_DEFAULT_PATH @endtp
# <td>Do not look in any default path such as the directories listed by the
# @c PYTHONPATH environment variable.</td>
# </tr>
# </table>
#
# @returns Sets the named cache variable of type @c PATH to the absolute path
# of the directory containing the specified Python @p MODULE if found,
# or the string "<MODULE%gt;-NOTFOUND" otherwise.
function (basis_find_python_module CACHEVAR)
# do nothing if path of module already known from previous run
if (DEFINED ${CACHEVAR} AND NOT ${CACHEVAR} MATCHES "NOTFOUND$")
return ()
endif ()
# parse arguments
CMAKE_PARSE_ARGUMENTS (
ARGN
"NO_DEFAULT_PATH;NO_PYTHONPATH"
"PYTHON_EXECUTABLE;NAME"
"PATH"
${ARGN}
)
if (NOT ARGN_NAME)
message ("basis_find_python_module(): Missing NAME argument!")
endif ()
if (ARGN_UNPARSED_ARGUMENTS)
message ("basis_find_python_module(): Invalid arguments: ${ARGN_UNPARSED_ARGUMENTS}")
endif ()
if (NOT ARGN_PYTHON_EXECUTABLE)
set (ARGN_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}")
endif ()
if (ARGN_NO_DEFAULT_PATH)
set (ARGN_NO_PYTHONPATH TRUE)
set (ARGN_PYTHON_EXECUTABLE)
endif ()
# set initial value of cache entry
if (${CACHEVAR} MATCHES "^ *$")
set (${CACHEVAR} "${ARGN_NAME}-NOTFOUND" CACHE PATH "Directory containing ${ARGN_NAME} Python module/package." FORCE)
else ()
set (${CACHEVAR} "${ARGN_NAME}-NOTFOUND" CACHE PATH "Directory containing ${ARGN_NAME} Python module/package.")
endif ()
# 1. search specified paths
foreach (P ${ARGN_PATH}) # ignore empty entries
if (IS_ABSOLUTE "${P}")
if (EXISTS "${P}/${ARGN_NAME}.py" OR EXISTS "${P}/${ARGN_NAME}/__init__.py" OR
EXISTS "${P}/${ARGN_NAME}.pyc" OR EXISTS "${P}/${ARGN_NAME}/__init__.pyc")
set_property (CACHE ${CACHEVAR} PROPERTY VALUE "${P}")
return ()
endif ()
endif ()
endforeach ()
# 2. get __file__ attribute of module loaded in Python
if (ARGN_PYTHON_EXECUTABLE)
set (IMPORT_SITE_ERROR FALSE)
# 2a. try it with -E option -- the preferred way to run Python
execute_process (
COMMAND "${ARGN_PYTHON_EXECUTABLE}" -E -c "import ${ARGN_NAME}; print ${ARGN_NAME}.__file__"
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE P
ERROR_VARIABLE ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (ERROR MATCHES "'import site' failed|ImportError: No module named site")
set (IMPORT_SITE_ERROR TRUE)
endif ()
# 2b. try it without -E option
if (NOT STATUS EQUAL 0)
execute_process (
COMMAND "${ARGN_PYTHON_EXECUTABLE}" -c "import ${ARGN_NAME}; print ${ARGN_NAME}.__file__"
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE P
ERROR_VARIABLE ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (ERROR MATCHES "'import site' failed|ImportError: No module named site")
set (IMPORT_SITE_ERROR TRUE)
endif ()
if (NOT STATUS EQUAL 0 AND ERROR MATCHES "ImportError: No module named site")
set (PYTHONHOME "$ENV{PYTHONHOME}")
unset (ENV{PYTHONHOME})
execute_process (
COMMAND "${ARGN_PYTHON_EXECUTABLE}" -c "import ${ARGN_NAME}; print ${ARGN_NAME}.__file__"
RESULT_VARIABLE STATUS
OUTPUT_VARIABLE P
ERROR_VARIABLE ERROR
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if (ERROR MATCHES "'import site' failed|ImportError: No module named site")
set (IMPORT_SITE_ERROR TRUE)
endif ()
set (ENV{PYTHONHOME} "${PYTHONHOME}")
endif ()
endif ()
if (STATUS EQUAL 0)
if (P MATCHES "__init__\\.pyc?$")
get_filename_component (P "${P}" PATH)
get_filename_component (P "${P}" PATH)
else ()
get_filename_component (P "${P}" PATH)
endif ()
set_property (CACHE ${CACHEVAR} PROPERTY VALUE "${P}")
return ()
elseif (IMPORT_SITE_ERROR)
message (WARNING "Import of site module failed when running Python interpreter ${ARGN_PYTHON_EXECUTABLE}"
" with and without -E option. Make sure that the Python interpreter is installed properly"
" and that the PYTHONHOME environment variable is either not set (recommended) or at"
" least set correctly for this Python installation. Maybe you need to enable this Python"
" version first somehow if more than one version of Python is installed on your system?"
" Otherwise, set PYTHON_EXECUTABLE to the right Python interpreter executable (python).")
endif ()
endif ()
# 3. search PYTHONPATH
if (NOT ARGN_NO_PYTHONPATH)
string (REPLACE ":" ";" PYTHONPATH "$ENV{PYTHONPATH}")
foreach (P ${PYTHONPATH}) # ignore empty entries
if (IS_ABSOLUTE "${P}")
if (EXISTS "${P}/${ARGN_NAME}.py" OR EXISTS "${P}/${ARGN_NAME}/__init__.py" OR
EXISTS "${P}/${ARGN_NAME}.pyc" OR EXISTS "${P}/${ARGN_NAME}/__init__.pyc")
set_property (CACHE ${CACHEVAR} PROPERTY VALUE "${P}")
return ()
endif ()
endif ()
endforeach ()
endif ()
endfunction ()
# ----------------------------------------------------------------------------
# find Python modules
if (NOT PythonModules_FIND_COMPONENTS AND NOT PythonModules_FIND_OPTIONAL_COMPONENTS)
message (FATAL_ERROR "PythonModules: No (OPTIONAL_)COMPONENTS (i.e., Python module names) specified!")
endif ()
if (NOT DEFINED PythonModules_PYTHONPATH)
set (PythonModules_PYTHONPATH) # PYTHONPATH of all found modules
endif ()
# helper macro
macro (_PythonModules_find_python_modules ALL_FOUND)
set (${ALL_FOUND} TRUE)
foreach (_PythonModules_MODULE ${ARGN})
set (_PythonModules_VAR "PythonModules_${_PythonModules_MODULE}_PATH")
set (_PythonModules_TGT "PythonModules_${_PythonModules_MODULE}")
if (PythonModules_DIR)
basis_find_python_module (
${_PythonModules_VAR}
NAME "${_PythonModules_MODULE}"
PATH "${PythonModules_DIR}"
NO_DEFAULT_PATH
)
else ()
basis_find_python_module (
${_PythonModules_VAR}
NAME "${_PythonModules_MODULE}"
PATH "${PythonModules_DIR}"
)
endif ()
mark_as_advanced (${_PythonModules_VAR})
if (${_PythonModules_VAR} MATCHES "(^ *|NOTFOUND)$")
set (${ALL_FOUND} FALSE)
set (PythonModules_${_PythonModules_MODULE}_FOUND FALSE)
else ()
if (NOT TARGET ${_PythonModules_TGT})
add_library (${_PythonModules_TGT} UNKNOWN IMPORTED)
set_target_properties (
${_PythonModules_TGT}
PROPERTIES
BASIS_TYPE "SCRIPT_LIBRARY"
IMPORTED_LOCATION "${${_PythonModules_VAR}}"
)
endif ()
set (PythonModules_${_PythonModules_MODULE}_FOUND TRUE)
list (APPEND PythonModules_PYTHONPATH "${${_PythonModules_VAR}}")
endif ()
endforeach ()
endmacro ()
# optional first, as PythonModules_FOUND shall be reset to TRUE afterwards
_PythonModules_find_python_modules (PythonModules_FOUND ${PythonModules_FIND_OPTIONAL_COMPONENTS})
_PythonModules_find_python_modules (PythonModules_FOUND ${PythonModules_FIND_COMPONENTS})
# remove duplicate paths in PYTHONPATH
if (PythonModules_PYTHONPATH)
list (REMOVE_DUPLICATES PythonModules_PYTHONPATH)
endif ()
# ----------------------------------------------------------------------------
# handle standard QUIET and REQUIRED arguments
if (NOT PythonModules_FOUND)
# list of modules that were not found
set (_PythonModules_MISSING)
foreach (_PythonModules_MODULE ${PythonModules_FIND_COMPONENTS})
if (NOT PythonModules_${_PythonModules_MODULE}_FOUND)
list (APPEND _PythonModules_MISSING "${_PythonModules_MODULE}")
endif ()
endforeach ()
# hint on how to help finding the Python modules
set (_PythonModules_HINT)
if (PYTHON_EXECUTABLE)
set (_PythonModules_HINT "Check if executing ${PYTHON_EXECUTABLE} -c \"import <module>\" works")
else ()
set (_PythonModules_HINT "Set PYTHON_EXECUTABLE, e.g., by searching for Python interpreter first")
endif ()
if (_PythonModules_HINT)
set (_PythonModules_HINT "${_PythonModules_HINT} or set")
else ()
set (_PythonModules_HINT "Set")
endif ()
set (_PythonModules_HINT "${_PythonModules_HINT} the PythonModules_DIR variable. Alternatively,")
set (_PythonModules_HINT "${_PythonModules_HINT} set the PythonModules_<module>_PATH variable(s)")
set (_PythonModules_HINT "${_PythonModules_HINT} instead or the PYTHONPATH environment variable.")
set (_PythonModules_HINT "${_PythonModules_HINT} Unset PythonModules_DIR if you chose an alternative")
set (_PythonModules_HINT "${_PythonModules_HINT} option and before rerunning CMake again.")
# error message
string (REPLACE ";" ", " ${_PythonModules_MISSING} "${_PythonModules_MISSING}")
message (FATAL_ERROR "Could NOT find the following Python modules:\n${_PythonModules_MISSING}\n${_PythonModules_HINT}")
endif ()
# ----------------------------------------------------------------------------
# common <Pkg>_DIR variable
if (NOT PythonModules_DIR)
set (
PythonModules_DIR
"${PythonModules_PYTHONPATH}"
CACHE PATH
"Directory or list of directories separated by ; of installed Python modules."
FORCE
)
endif ()
# ----------------------------------------------------------------------------
# clean up
unset (_PythonModules_MODULE)
unset (_PythonModules_VAR)
unset (_PythonModules_VARS)