FindPythonModules.cmake
Go to the documentation of this file.
1 ##############################################################################
2 # @file FindPythonModules.cmake
3 # @brief Find Python modules.
4 #
5 # @par Input/Output variables:
6 # <table border="0">
7 # <tr>
8 # @tp @b PythonModules_DIR @endtp
9 # <td>List of directories where Python modules are installed.</td>
10 # </tr>
11 # </table>
12 
13 # @par Input variables:
14 # <table border="0">
15 # <tr>
16 # @tp @b PythonModules_FIND_COMPONENTS @endtp
17 # <td>The @c COMPONENTS argument(s) of the find_package() command specifies
18 # the names of the Python modules to look for.</td>
19 # </tr>
20 # <tr>
21 # @tp @b PythonModules_FIND_OPTIONAL_COMPONENTS @endtp
22 # <td>The @c OPTIONAL_COMPONENTS argument(s) of the find_package() command
23 # specifies the names of the Python modules which are not necessarily
24 # required, but should be searched as well.</td>
25 # </tr>
26 # <tr>
27 # @tp @b PYTHON_EXECUTABLE @endtp
28 # <td>Path to the Python interpreter. Should be set by first looking
29 # for the Python interpreter, i.e., find_packages(PythonInterp).
30 # If set, this module first tries to execute the Python interpreter,
31 # import the respective Python module, and then derive the search path
32 # from the @c __file__ attribute of the loaded Python module.
33 # Otherwise, or if this fails, it looks either for a package
34 # @c __init__.py file inside a subdirectory named after the specified
35 # Python module or a @c .py module file in each directory listed in
36 # the @c PYTHONPATH.</td>
37 # </tr>
38 # <tr>
39 # @tp @b PYTHONPATH @endtp
40 # <td>Search path for Python modules. If this CMake variable is undefined,
41 # the corresponding environment variable is used instead if set.
42 # Only absolute paths in the @c PYTHONPATH are considered.</td>
43 # </tr>
44 # </table>
45 #
46 # @par Output variables:
47 # <table border="0">
48 # <tr>
49 # @tp @b PythonModules_FOUND @endtp
50 # <td>Whether all specified Python modules were found.</td>
51 # </tr>
52 # <tr>
53 # @tp @b PythonModules_&lt;module&gt;_FOUND @endtp
54 # <td>Whether the Python module &lt;module%gt; was found.</td>
55 # </tr>
56 # <tr>
57 # @tp @b PythonModules_&lt;module&gt;_PATH @endtp
58 # <td>Absolute path of the directory containing the Python module named &lt;module%gt;.</td>
59 # </tr>
60 # <tr>
61 # @tp @b PythonModules_&lt;module&gt; @endtp
62 # <td>Import target for module named &lt;module&gt;. The location of the
63 # target is @c PythonModules_&lt;module&gt_PATH.</tr>
64 # </tr>
65 # <tr>
66 # @tp @b PythonModules_PYTHONPATH @endtp
67 # <td>The @c PYTHONPATH setting required for the found Python module(s), i.e.,
68 # The directories that have to be added to the Python search path.
69 # To support the use of this CMake module more than once with different
70 # arguments to the find_package() command, e.g., with and without the
71 # @c REQUIRED argument, the directories containing the found Python
72 # modules are appended to any existing directories in
73 # @c PythonModules_PYTHONPATH if not already listed.</td>
74 # </tr>
75 # </table>
76 #
77 # @ingroup CMakeFindModules
78 ##############################################################################
79 
80 #=============================================================================
81 # Copyright 2011-2012 University of Pennsylvania
82 # Copyright 2013-2016 Andreas Schuh <andreas.schuh.84@gmail.com>
83 #
84 # Distributed under the OSI-approved BSD License (the "License");
85 # see accompanying file Copyright.txt for details.
86 #
87 # This software is distributed WITHOUT ANY WARRANTY; without even the
88 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
89 # See the License for more information.
90 #=============================================================================
91 # (To distribute this file outside of CMake, substitute the full
92 # License text for the above reference.)
93 
94 include (CMakeParseArguments)
95 
96 # ----------------------------------------------------------------------------
97 ## @brief Find Python module.
98 #
99 # If the @c PYTHON_EXECUTABLE variable is set, this function at first tries
100 # to launch the Python interpreter, import the named Python module, and then
101 # determines the search path for the Python module from the @c __file__
102 # attribute of the loaded module. Otherwise, or if this fails, it looks for
103 # the Python module in the directories listed in the @c PYTHONPATH variable
104 # if defined. If this variable is not defined, the @c PYTHONPATH environment
105 # variable is used instead.
106 #
107 # @param [in] CACHEVAR Name of CMake cache variable which stores path of
108 # directory of the Python module. If not set or if
109 # the cache entry contains space characters only or
110 # ends in the string NOTFOUND, this function looks for
111 # the specified Python module. Otherwise, it does nothing
112 # and leaves the cache entry unmodified.
113 # @param [in] ARGN The remaining arguments are parsed and the following
114 # options extract:
115 # @par
116 # <table border=0>
117 # <tr>
118 # @tp @b NAME module @endtp
119 # <td>Name of the Python module.</td>
120 # </tr>
121 # <tr>
122 # @tp @b PYTHON_EXECUTABLE python @endtp
123 # <td>Full path of the Python interpreter executable. If not specified
124 # the global PYTHON_EXECUTABLE CMake variable/cache entry is used.</td>
125 # </tr>
126 # <tr>
127 # @tp @b PATH dir1 [dir2...] @endtp
128 # <td>Directories where to look for Python module.</td>
129 # </tr>
130 # <tr>
131 # @tp @b NO_PYTHONPATH @endtp
132 # <td>Do not consider the @c PYTHONPATH environment variable.</td>
133 # </tr>
134 # <tr>
135 # @tp @b NO_DEFAULT_PATH @endtp
136 # <td>Do not look in any default path such as the directories listed by the
137 # @c PYTHONPATH environment variable.</td>
138 # </tr>
139 # </table>
140 #
141 # @returns Sets the named cache variable of type @c PATH to the absolute path
142 # of the directory containing the specified Python @p MODULE if found,
143 # or the string "&lt;MODULE%gt;-NOTFOUND" otherwise.
144 function (basis_find_python_module CACHEVAR)
145  # do nothing if path of module already known from previous run
146  if (DEFINED ${CACHEVAR} AND NOT ${CACHEVAR} MATCHES "NOTFOUND$")
147  return ()
148  endif ()
149  # parse arguments
150  CMAKE_PARSE_ARGUMENTS (
151  ARGN
152  "NO_DEFAULT_PATH;NO_PYTHONPATH"
153  "PYTHON_EXECUTABLE;NAME"
154  "PATH"
155  ${ARGN}
156  )
157  if (NOT ARGN_NAME)
158  message ("basis_find_python_module(): Missing NAME argument!")
159  endif ()
160  if (ARGN_UNPARSED_ARGUMENTS)
161  message ("basis_find_python_module(): Invalid arguments: ${ARGN_UNPARSED_ARGUMENTS}")
162  endif ()
163  if (NOT ARGN_PYTHON_EXECUTABLE)
164  set (ARGN_PYTHON_EXECUTABLE "${PYTHON_EXECUTABLE}")
165  endif ()
166  if (ARGN_NO_DEFAULT_PATH)
167  set (ARGN_NO_PYTHONPATH TRUE)
168  set (ARGN_PYTHON_EXECUTABLE)
169  endif ()
170  # set initial value of cache entry
171  if (${CACHEVAR} MATCHES "^ *$")
172  set (${CACHEVAR} "${ARGN_NAME}-NOTFOUND" CACHE PATH "Directory containing ${ARGN_NAME} Python module/package." FORCE)
173  else ()
174  set (${CACHEVAR} "${ARGN_NAME}-NOTFOUND" CACHE PATH "Directory containing ${ARGN_NAME} Python module/package.")
175  endif ()
176  # 1. search specified paths
177  foreach (P ${ARGN_PATH}) # ignore empty entries
178  if (IS_ABSOLUTE "${P}")
179  if (EXISTS "${P}/${ARGN_NAME}.py" OR EXISTS "${P}/${ARGN_NAME}/__init__.py" OR
180  EXISTS "${P}/${ARGN_NAME}.pyc" OR EXISTS "${P}/${ARGN_NAME}/__init__.pyc")
181  set_property (CACHE ${CACHEVAR} PROPERTY VALUE "${P}")
182  return ()
183  endif ()
184  endif ()
185  endforeach ()
186  # 2. get __file__ attribute of module loaded in Python
187  if (ARGN_PYTHON_EXECUTABLE)
188  set (IMPORT_SITE_ERROR FALSE)
189  # 2a. try it with -E option -- the preferred way to run Python
190  execute_process (
191  COMMAND "${ARGN_PYTHON_EXECUTABLE}" -E -c "import ${ARGN_NAME}; print ${ARGN_NAME}.__file__"
192  RESULT_VARIABLE STATUS
193  OUTPUT_VARIABLE P
194  ERROR_VARIABLE ERROR
195  OUTPUT_STRIP_TRAILING_WHITESPACE
196  )
197  if (ERROR MATCHES "'import site' failed|ImportError: No module named site")
198  set (IMPORT_SITE_ERROR TRUE)
199  endif ()
200  # 2b. try it without -E option
201  if (NOT STATUS EQUAL 0)
202  execute_process (
203  COMMAND "${ARGN_PYTHON_EXECUTABLE}" -c "import ${ARGN_NAME}; print ${ARGN_NAME}.__file__"
204  RESULT_VARIABLE STATUS
205  OUTPUT_VARIABLE P
206  ERROR_VARIABLE ERROR
207  OUTPUT_STRIP_TRAILING_WHITESPACE
208  )
209  if (ERROR MATCHES "'import site' failed|ImportError: No module named site")
210  set (IMPORT_SITE_ERROR TRUE)
211  endif ()
212  if (NOT STATUS EQUAL 0 AND ERROR MATCHES "ImportError: No module named site")
213  set (PYTHONHOME "$ENV{PYTHONHOME}")
214  unset (ENV{PYTHONHOME})
215  execute_process (
216  COMMAND "${ARGN_PYTHON_EXECUTABLE}" -c "import ${ARGN_NAME}; print ${ARGN_NAME}.__file__"
217  RESULT_VARIABLE STATUS
218  OUTPUT_VARIABLE P
219  ERROR_VARIABLE ERROR
220  OUTPUT_STRIP_TRAILING_WHITESPACE
221  )
222  if (ERROR MATCHES "'import site' failed|ImportError: No module named site")
223  set (IMPORT_SITE_ERROR TRUE)
224  endif ()
225  set (ENV{PYTHONHOME} "${PYTHONHOME}")
226  endif ()
227  endif ()
228  if (STATUS EQUAL 0)
229  if (P MATCHES "__init__\\.pyc?$")
230  get_filename_component (P "${P}" PATH)
231  get_filename_component (P "${P}" PATH)
232  else ()
233  get_filename_component (P "${P}" PATH)
234  endif ()
235  set_property (CACHE ${CACHEVAR} PROPERTY VALUE "${P}")
236  return ()
237  elseif (IMPORT_SITE_ERROR)
238  message (WARNING "Import of site module failed when running Python interpreter ${ARGN_PYTHON_EXECUTABLE}"
239  " with and without -E option. Make sure that the Python interpreter is installed properly"
240  " and that the PYTHONHOME environment variable is either not set (recommended) or at"
241  " least set correctly for this Python installation. Maybe you need to enable this Python"
242  " version first somehow if more than one version of Python is installed on your system?"
243  " Otherwise, set PYTHON_EXECUTABLE to the right Python interpreter executable (python).")
244  endif ()
245  endif ()
246  # 3. search PYTHONPATH
247  if (NOT ARGN_NO_PYTHONPATH)
248  string (REPLACE ":" ";" PYTHONPATH "$ENV{PYTHONPATH}")
249  foreach (P ${PYTHONPATH}) # ignore empty entries
250  if (IS_ABSOLUTE "${P}")
251  if (EXISTS "${P}/${ARGN_NAME}.py" OR EXISTS "${P}/${ARGN_NAME}/__init__.py" OR
252  EXISTS "${P}/${ARGN_NAME}.pyc" OR EXISTS "${P}/${ARGN_NAME}/__init__.pyc")
253  set_property (CACHE ${CACHEVAR} PROPERTY VALUE "${P}")
254  return ()
255  endif ()
256  endif ()
257  endforeach ()
258  endif ()
259 endfunction ()
260 
261 # ----------------------------------------------------------------------------
262 # find Python modules
263 if (NOT PythonModules_FIND_COMPONENTS AND NOT PythonModules_FIND_OPTIONAL_COMPONENTS)
264  message (FATAL_ERROR "PythonModules: No (OPTIONAL_)COMPONENTS (i.e., Python module names) specified!")
265 endif ()
266 if (NOT DEFINED PythonModules_PYTHONPATH)
267  set (PythonModules_PYTHONPATH) # PYTHONPATH of all found modules
268 endif ()
269 # helper macro
270 macro (_PythonModules_find_python_modules ALL_FOUND)
271  set (${ALL_FOUND} TRUE)
272  foreach (_PythonModules_MODULE ${ARGN})
273  set (_PythonModules_VAR "PythonModules_${_PythonModules_MODULE}_PATH")
274  set (_PythonModules_TGT "PythonModules_${_PythonModules_MODULE}")
275  if (PythonModules_DIR)
277  ${_PythonModules_VAR}
278  NAME "${_PythonModules_MODULE}"
279  PATH "${PythonModules_DIR}"
280  NO_DEFAULT_PATH
281  )
282  else ()
284  ${_PythonModules_VAR}
285  NAME "${_PythonModules_MODULE}"
286  PATH "${PythonModules_DIR}"
287  )
288  endif ()
289  mark_as_advanced (${_PythonModules_VAR})
290  if (${_PythonModules_VAR} MATCHES "(^ *|NOTFOUND)$")
291  set (${ALL_FOUND} FALSE)
292  set (PythonModules_${_PythonModules_MODULE}_FOUND FALSE)
293  else ()
294  if (NOT TARGET ${_PythonModules_TGT})
295  add_library (${_PythonModules_TGT} UNKNOWN IMPORTED)
297  ${_PythonModules_TGT}
298  PROPERTIES
299  BASIS_TYPE "SCRIPT_LIBRARY"
300  IMPORTED_LOCATION "${${_PythonModules_VAR}}"
301  )
302  endif ()
303  set (PythonModules_${_PythonModules_MODULE}_FOUND TRUE)
304  list (APPEND PythonModules_PYTHONPATH "${${_PythonModules_VAR}}")
305  endif ()
306  endforeach ()
307 endmacro ()
308 # optional first, as PythonModules_FOUND shall be reset to TRUE afterwards
309 _PythonModules_find_python_modules (PythonModules_FOUND ${PythonModules_FIND_OPTIONAL_COMPONENTS})
310 _PythonModules_find_python_modules (PythonModules_FOUND ${PythonModules_FIND_COMPONENTS})
311 # remove duplicate paths in PYTHONPATH
312 if (PythonModules_PYTHONPATH)
313  list (REMOVE_DUPLICATES PythonModules_PYTHONPATH)
314 endif ()
315 
316 # ----------------------------------------------------------------------------
317 # handle standard QUIET and REQUIRED arguments
318 if (NOT PythonModules_FOUND)
319  # list of modules that were not found
320  set (_PythonModules_MISSING)
321  foreach (_PythonModules_MODULE ${PythonModules_FIND_COMPONENTS})
322  if (NOT PythonModules_${_PythonModules_MODULE}_FOUND)
323  list (APPEND _PythonModules_MISSING "${_PythonModules_MODULE}")
324  endif ()
325  endforeach ()
326  # hint on how to help finding the Python modules
327  set (_PythonModules_HINT)
328  if (PYTHON_EXECUTABLE)
329  set (_PythonModules_HINT "Check if executing ${PYTHON_EXECUTABLE} -c \"import <module>\" works")
330  else ()
331  set (_PythonModules_HINT "Set PYTHON_EXECUTABLE, e.g., by searching for Python interpreter first")
332  endif ()
334  set (_PythonModules_HINT "${_PythonModules_HINT} or set")
335  else ()
336  set (_PythonModules_HINT "Set")
337  endif ()
338  set (_PythonModules_HINT "${_PythonModules_HINT} the PythonModules_DIR variable. Alternatively,")
339  set (_PythonModules_HINT "${_PythonModules_HINT} set the PythonModules_<module>_PATH variable(s)")
340  set (_PythonModules_HINT "${_PythonModules_HINT} instead or the PYTHONPATH environment variable.")
341  set (_PythonModules_HINT "${_PythonModules_HINT} Unset PythonModules_DIR if you chose an alternative")
342  set (_PythonModules_HINT "${_PythonModules_HINT} option and before rerunning CMake again.")
343  # error message
344  string (REPLACE ";" ", " ${_PythonModules_MISSING} "${_PythonModules_MISSING}")
345  message (FATAL_ERROR "Could NOT find the following Python modules:\n${_PythonModules_MISSING}\n${_PythonModules_HINT}")
346 endif ()
347 
348 # ----------------------------------------------------------------------------
349 # common <Pkg>_DIR variable
350 if (NOT PythonModules_DIR)
351  set (
352  PythonModules_DIR
353  "${PythonModules_PYTHONPATH}"
354  CACHE PATH
355  "Directory or list of directories separated by ; of installed Python modules."
356  FORCE
357  )
358 endif ()
359 
360 # ----------------------------------------------------------------------------
361 # clean up
362 unset (_PythonModules_MODULE)
363 unset (_PythonModules_VAR)
364 unset (_PythonModules_VARS)
function set_target_properties(in ARGN)
Set target property.
function is(in result, in expected, in name)
Test whether a given result is equal to the expected result.
function add_library(in TARGET_UID, in ARGN)
Add library target.
cmake NAME
cmake _PythonModules_HINT
function basis_find_python_module(in CACHEVAR, in ARGN)
Find Python module.
if(oldcoutbuf)