ExecuteProcess.cmake
Go to the documentation of this file.
1 # ============================================================================
2 # Copyright (c) 2011-2012 University of Pennsylvania
3 # Copyright (c) 2013-2016 Andreas Schuh
4 # All rights reserved.
5 #
6 # See COPYING file for license information or visit
7 # https://cmake-basis.github.io/download.html#license
8 # ============================================================================
9 
10 ##############################################################################
11 # @file ExecuteProcess.cmake
12 # @brief Execute process using CMake script mode.
13 #
14 # This CMake script can be used as argument for the -P option of cmake, when
15 # another command shall be executed by CMake, for example, as custom build
16 # command. The advantage of using this script is that all options of the
17 # CMake command execute_process() can be used, i.e., a timeout can be
18 # specified.
19 #
20 # The arguments of the execute_process() command have to be specified via
21 # the -D option on the command line of cmake before the -P \<this script\>
22 # option is given. The name of the CMake variables must be equal the
23 # name of the arguments to the execute_process() command.
24 #
25 # Arguments of execute_process() which are considered:
26 #
27 # - @b COMMAND
28 # - @b WORKING_DIRECTORY
29 # - @b TIMEOUT
30 # - @b OUTPUT_FILE
31 # - @b ERROR_FILE
32 # - @b OUTPUT_QUIET
33 # - @b ERROR_QUIET
34 # - @b OUTPUT_STRIP_TRAILING_WHITESPACE
35 # - @b ERROR_STRIP_TRAILING_WHITESPACE
36 #
37 # Additionally, matching expressions (separated by ';') to identify error messages
38 # in the output streams stdout and stderr can be specified by the input argument
39 # ERROR_EXPRESSION. When the output of the executed command matches one of
40 # the error expressions, a fatal error message is displayed causing CMake to
41 # return the exit code 1.
42 #
43 # Setting VERBOSE to true enables verbose output messages.
44 #
45 # When the input argument LOG_ARGS evaluates to true, the values of COMMAND,
46 # WORKING_DIRECTORY, and TIMEOUT are added to the top of the output files
47 # specified by OUTPUT_FILE and ERROR_FILE.
48 #
49 # The arguments ARGS and ARGS_FILE can be used to specify (additional) command
50 # arguments. The content of the text file ARGS_FILE is read when it this file
51 # exists. Separate lines of this file are considered single arguments.
52 # The arguments specified by ARGS and ARGS_FILE are concatenated where the
53 # arguments given by ARGS follow after the ones read from the ARGS_FILE.
54 # All occurences of the string 'ARGS' in the COMMAND are replaced by these
55 # arguments. If no such string is present, the arguments are simply passed
56 # to the execute_process() command as its ARGS argument.
57 # The argument ARGS_SEPARATOR specifies the separator used to separate the
58 # arguments given by ARGS and ARGS_FILE when the 'ARGS' string in COMMAND
59 # is replaced. By default, it is set to ';'.
60 #
61 # Example:
62 # @code
63 # cmake -DCOMMAND='ls;-l' -DWORKING_DIRECTORY='/' -DTIMEOUT=60
64 # -P ExecuteProcess.cmake
65 # @endcode
66 #
67 # The output of the executed process can further be searched for error expressions
68 # specified by the ERROR_EXPRESSION variable. If the process output matches
69 # this expression, a fatal CMake error is raised.
70 #
71 # Certain errors may be temporary such as in particular a license checkout
72 # error of the MATLAB Compiler. Such errors can be filtered out using the
73 # RETRY_EXPRESSION variable. If such error is detected, this script sleeps for
74 # RETRY_DELAY seconds and then executes the process again. This is done
75 # at maximum RETRY_ATTEMPTS times. If the retry attempts are exceeded, a
76 # fatal CMake error is raised instead.
77 #
78 # @sa http://www.cmake.org/cmake/help/cmake2.6docs.html#command:execute_process
79 #
80 # @ingroup CMakeUtilities
81 ##############################################################################
82 
83 if (POLICY CMP0053)
84  cmake_policy (SET CMP0053 NEW)
85 endif ()
86 
87 # ----------------------------------------------------------------------------
88 # unset environment variables that may cause problems otherwise
89 unset (ENV{PYTHONHOME})
90 unset (ENV{PYTHONPATH})
91 
92 # ----------------------------------------------------------------------------
93 # initialize arguments
94 set (CONFIGURED_COMMAND "@BUILD_CMD@")
95 
96 string (REPLACE "#" "@" AT_BUILD_CMD_AT "#BUILD_CMD#")
97 if (CONFIGURED_COMMAND AND NOT CONFIGURED_COMMAND STREQUAL "${AT_BUILD_CMD_AT}")
98  set (COMMAND "${CONFIGURED_COMMAND}")
99 elseif (NOT COMMAND)
100  message (FATAL_ERROR "No command specified for execute_process(): use \"-DCOMMAND=cmd\"")
101 endif ()
102 
103 if (NOT ARGS_SEPARATOR)
104  set (ARGS_SEPARATOR ";")
105 endif ()
106 
107 if (ARGS_FILE)
108  include ("${ARGS_FILE}" OPTIONAL)
109 
110  if (ARGS AND DEFINED ${ARGS})
111  set (ARGS "${${ARGS}}")
112  else ()
113  set (ARGS "")
114  endif ()
115 endif ()
116 
117 if ("${COMMAND}" MATCHES "ARGS")
118  string (REPLACE ";" "${ARGS_SEPARATOR}" TMP "${ARGS}")
119  string (REPLACE "ARGS" "${ARGS_SEPARATOR}${TMP}" COMMAND "${COMMAND}")
120  set (ARGS)
121 endif ()
122 
123 set (EXECUTE_PROCESS_ARGS "COMMAND" "${COMMAND}")
124 
125 if (ARGS)
126  list (APPEND EXECUTE_PROCESS_ARGS "ARGS" "${ARGS}")
127 endif ()
128 
129 list (APPEND EXECUTE_PROCESS_ARGS "RESULT_VARIABLE" "RETVAL")
130 
131 if (TIMEOUT)
132  list (APPEND EXECUTE_PROCESS_ARGS "TIMEOUT" "${TIMEOUT}")
133 endif ()
134 
135 if (WORKING_DIRECTORY)
136  list (APPEND EXECUTE_PROCESS_ARGS "WORKING_DIRECTORY" "${WORKING_DIRECTORY}")
137 endif ()
138 
139 if (OUTPUT_FILE)
140  list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_FILE" "${OUTPUT_FILE}")
141 endif ()
142 
143 if (ERROR_FILE)
144  list (APPEND EXECUTE_PROCESS_ARGS "ERROR_FILE" "${ERROR_FILE}")
145 endif ()
146 
147 if (OUTPUT_QUIET)
148  list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_QUIET")
149 endif ()
150 
151 if (ERROR_QUIET)
152  list (APPEND EXECUTE_PROCESS_ARGS "ERROR_QUIET")
153 endif ()
154 
155 if (OUTPUT_STRIP_TRAILING_WHITESPACE)
156  list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_STRIP_TRAILING_WHITESPACE")
157 endif ()
158 
159 if (ERROR_STRIP_TRAILING_WHITESPACE)
160  list (APPEND EXECUTE_PROCESS_ARGS "ERROR_STRIP_TRAILING_WHITESPACE")
161 endif ()
162 
163 if (NOT OUTPUT_FILE)
164  list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_VARIABLE" "STDOUT")
165 endif ()
166 
167 if (NOT ERROR_FILE)
168  list (APPEND EXECUTE_PROCESS_ARGS "ERROR_VARIABLE" "STDERR")
169 endif ()
170 
171 if (NOT RETRY_ATTEMPTS)
172  set (RETRY_ATTEMPTS 0)
173 endif ()
174 
175 if (NOT RETRY_DELAY)
176  set (RETRY_DELAY 60)
177 endif ()
178 
179 # --------------------------------------------------------------------------
180 # verbose message of command to execute
181 set (CMD)
182 foreach (ARG ${COMMAND})
183  if (CMD)
184  set (CMD "${CMD} ")
185  endif ()
186  if (ARG MATCHES " ")
187  set (CMD "${CMD}\"${ARG}\"")
188  else ()
189  set (CMD "${CMD}${ARG}")
190  endif ()
191 endforeach ()
192 
193 if (VERBOSE)
194  message ("${CMD}")
195 endif ()
196 
197 # ----------------------------------------------------------------------------
198 # execute command
199 set (RETRY TRUE) # execute process at least once
200 while (RETRY)
201  # --------------------------------------------------------------------------
202  # whether retry is required is decided upon after process execution
203  set (RETRY FALSE)
204 
205  # --------------------------------------------------------------------------
206  # execute process
207  execute_process (${EXECUTE_PROCESS_ARGS})
208 
209  # --------------------------------------------------------------------------
210  # read in output from log files
211  if (OUTPUT_FILE)
212  file (READ "${OUTPUT_FILE}" STDOUT)
213  endif ()
214 
215  if (ERROR_FILE)
216  file (READ "${ERROR_FILE}" STDERR)
217  endif ()
218 
219  # --------------------------------------------------------------------------
220  # parse output for recoverable errors
221  foreach (EXPR IN LISTS RETRY_EXPRESSION)
222  if (STDOUT MATCHES "${EXPR}" OR STDERR MATCHES "${EXPR}")
223  if (RETRY_ATTEMPTS GREATER 0)
224  message ("Process output matches \"${EXPR}\", retry in ${RETRY_DELAY} seconds")
225  # retry
226  math (EXPR RETRY_ATTEMPTS "${RETRY_ATTEMPTS} - 1")
227  set (RETRY TRUE)
228  else ()
229  # no retries left
230  set (RETVAL 1)
231  endif ()
232  break ()
233  endif ()
234  endforeach ()
235 
236  # --------------------------------------------------------------------------
237  # sleep for given amount of seconds before retrying to execute process
238  if (RETRY)
239  # use sleep command if available, i.e., on Unix and also on Windows
240  # if the Windows 2003 Resource Kit is installed
241  find_program (SLEEP sleep)
242  if (SLEEP)
243  execute_process (
244  COMMAND "${SLEEP}" ${RETRY_DELAY}
245  TIMEOUT ${RETRY_DELAY}
246  ERROR_QUIET OUTPUT_QUIET
247  )
248  else ()
249  # work-around using ping command if sleep command not available, i.e.,
250  # on Windows where the Windows 2003 Resource Kit is not installed
251  # See http://malektips.com/dos0017.html
252  find_program (PING ping)
253  if (WIN32)
254  execute_process (
255  COMMAND ${PING} 127.0.0.1 -n ${RETRY_DELAY} -w 1000
256  TIMEOUT ${RETRY_DELAY}
257  ERROR_QUIET OUTPUT_QUIET
258  )
259  else ()
260  # usually not required as sleep command should be available
261  execute_process (
262  COMMAND ${PING} 127.0.0.1 -c ${RETRY_DELAY} -W 1000
263  TIMEOUT ${RETRY_DELAY}
264  ERROR_QUIET OUTPUT_QUIET
265  )
266  endif ()
267  else ()
268  message (WARNING "Cannot delay retries as neither sleep nor ping command is available!")
269  endif ()
270  endif ()
271 endwhile ()
272 
273 # ----------------------------------------------------------------------------
274 # parse output for errors
275 foreach (EXPR IN LISTS ERROR_EXPRESSION)
276  if (STDOUT MATCHES "${EXPR}" OR STDERR MATCHES "${EXPR}")
277  set (RETVAL 1)
278  break ()
279  endif ()
280 endforeach ()
281 
282 # ----------------------------------------------------------------------------
283 # give some hints for known error messages
284 if (NOT RETVAL EQUAL 0)
285  set (HINTS)
286  # MATLAB Compiler
287  if (CMD MATCHES "mcc")
288  if (STDERR MATCHES "The file.*appears to be a MEX-file.[ ]+It shadows the M-file.*but will not execute properly at runtime, as it does not export a function.*named 'mexFunction.'")
289  set (HINTS "${HINTS}
290 
291  Note: The error that a MEX-file would shadow a M-file can be caused by a shared library that
292  is required by the MEX-file but not found in the search path of the dynamic loader.
293 ")
294  endif ()
295  endif ()
296  # append hints to output
297  if ("${STDOUT}" STREQUAL "${STDERR}")
298  set (STDERR "${STDERR}${HINTS}")
299  set (STDOUT "${STDOUT}${HINTS}")
300  else ()
301  set (STDERR "${STDERR}${HINTS}")
302  endif ()
303 endif ()
304 
305 # ----------------------------------------------------------------------------
306 # prepand command to log file
307 if (LOG_ARGS)
308  if (OUTPUT_FILE)
309  set (TMP "Command: ${CMD}\n\nWorking directory: ${WORKING_DIRECTORY}\n\n")
310  set (TMP "${TMP}Timeout: ${TIMEOUT}\n\nOutput:\n\n${STDOUT}")
311  file (WRITE "${OUTPUT_FILE}" "${TMP}")
312  unset (TMP)
313  endif ()
314 
315  if (ERROR_FILE AND NOT "${ERROR_FILE}" STREQUAL "${OUTPUT_FILE}")
316  set (TMP "Command: ${CMD}\n\nWorking directory: ${WORKING_DIRECTORY}\n\n")
317  set (TMP "${TMP}Timeout: ${TIMEOUT}\n\nOutput:\n\n${STDERR}")
318  file (WRITE "${ERROR_FILE}" "${TMP}")
319  unset (TMP)
320  endif ()
321 endif ()
322 
323 # ----------------------------------------------------------------------------
324 # print error message (and exit with exit code 1) on error
325 if (NOT RETVAL EQUAL 0)
326 
327  # print error
328  if ("${STDOUT}" STREQUAL "${STDERR}")
329  message (
330  FATAL_ERROR "
331 Command: ${CMD}
332 Working directory: ${WORKING_DIRECTORY}
333 Timeout: ${TIMEOUT}
334 Output:
335 ${STDOUT}")
336  else ()
337  message (
338  FATAL_ERROR "
339 Command: ${CMD}
340 Working directory: ${WORKING_DIRECTORY}
341 Timeout: ${TIMEOUT}
342 Output (stdout):
343 ${STDOUT}
344 Output (stderr):
345 ${STDERR}")
346  endif ()
347 endif ()
cmake CMD
cmake STDERR
cmake RETRY_ATTEMPTS
cmake RETVAL
cmake COMMAND
cmake CONFIGURED_COMMAND
cmake EXECUTE_PROCESS_ARGS
cmake RETRY
cmake STDOUT
cmake TMP
cmake ARGS_SEPARATOR
if(oldcoutbuf)
cmake RETRY_DELAY
cmake cmake ARGS
function execute(in options, in cmd, in args)
Execute command as subprocess.