BASIS  version 1.2.3 (revision 2104)
ExecuteProcess.cmake
Go to the documentation of this file.
00001 ##############################################################################
00002 # @file  ExecuteProcess.cmake
00003 # @brief Execute process using CMake script mode.
00004 #
00005 # This CMake script can be used as argument for the -P option of cmake, when
00006 # another command shall be executed by CMake, for example, as custom build
00007 # command. The advantage of using this script is that all options of the
00008 # CMake command execute_process() can be used, i.e., a timeout can be
00009 # specified.
00010 #
00011 # The arguments of the execute_process() command have to specified via
00012 # the -D option on the command line of cmake before the -P \<this script\>
00013 # option is given. The name of the CMake variables must be equal the
00014 # name of the arguments to the execute_process() command.
00015 #
00016 # Arguments of execute_process() which are considered:
00017 #
00018 # - @b COMMAND
00019 # - @b WORKING_DIRECTORY
00020 # - @b TIMEOUT
00021 # - @b OUTPUT_FILE
00022 # - @b ERROR_FILE
00023 # - @b OUTPUT_QUIET
00024 # - @b ERROR_QUIET
00025 # - @b OUTPUT_STRIP_TRAILING_WHITESPACE
00026 # - @b ERROR_STRIP_TRAILING_WHITESPACE
00027 #
00028 # Additionally, matching expressions (separated by ';') to identify error messages
00029 # in the output streams stdout and stderr can be specified by the input argument
00030 # ERROR_EXPRESSION. When the output of the executed command matches one of
00031 # the error expressions, a fatal error message is displayed causing CMake to
00032 # return the exit code 1.
00033 #
00034 # Setting VERBOSE to true enables verbose output messages.
00035 #
00036 # When the input argument LOG_ARGS evaluates to true, the values of COMMAND,
00037 # WORKING_DIRECTORY, and TIMEOUT are added to the top of the output files
00038 # specified by OUTPUT_FILE and ERROR_FILE.
00039 #
00040 # The arguments ARGS and ARGS_FILE can be used to specify (additional) command
00041 # arguments. The content of the text file ARGS_FILE is read when it this file
00042 # exists. Separate lines of this file are considered single arguments.
00043 # The arguments specified by ARGS and ARGS_FILE are concatenated where the
00044 # arguments given by ARGS follow after the ones read from the ARGS_FILE.
00045 # All occurences of the string 'ARGS' in the COMMAND are replaced by these
00046 # arguments. If no such string is present, the arguments are simply passed
00047 # to the execute_process() command as its ARGS argument.
00048 # The argument ARGS_SEPARATOR specifies the separator used to separate the
00049 # arguments given by ARGS and ARGS_FILE when the 'ARGS' string in COMMAND
00050 # is replaced. By default, it is set to ';'.
00051 #
00052 # Example:
00053 # @code
00054 # cmake -DCOMMAND='ls;-l' -DWORKING_DIRECTORY='/' -DTIMEOUT=60
00055 #       -P ExecuteProcess.cmake
00056 # @endcode
00057 #
00058 # The output of the executed process can further be searched for error expressions
00059 # specified by the ERROR_EXPRESSION variable. If the process output matches
00060 # this expression, a fatal CMake error is raised.
00061 #
00062 # Certain errors may be temporary such as in particular a license checkout
00063 # error of the MATLAB Compiler. Such errors can be filtered out using the
00064 # RETRY_EXPRESSION variable. If such error is detected, this script sleeps for
00065 # RETRY_DELAY seconds and then executes the process again. This is done
00066 # at maximum RETRY_ATTEMPTS times. If the retry attempts are exceeded, a
00067 # fatal CMake error is raised instead.
00068 #
00069 # @sa http://www.cmake.org/cmake/help/cmake2.6docs.html#command:execute_process
00070 #
00071 # Copyright (c) 2011, 2012 University of Pennsylvania. All rights reserved.<br />
00072 # See https://www.cbica.upenn.edu/sbia/software/license.html or COPYING file.
00073 #
00074 # Contact: SBIA Group <sbia-software at uphs.upenn.edu>
00075 #
00076 # @ingroup CMakeUtilities
00077 ##############################################################################
00078 
00079 # ----------------------------------------------------------------------------
00080 # initialize arguments
00081 if (NOT COMMAND)
00082   message (FATAL_ERROR "No command specified for execute_process (): use -DCOMMAND='cmd'")
00083 endif ()
00084 
00085 if (NOT ARGS_SEPARATOR)
00086   set (ARGS_SEPARATOR ";")
00087 endif ()
00088 
00089 if (ARGS_FILE)
00090   include ("${ARGS_FILE}" OPTIONAL)
00091 
00092   if (ARGS AND DEFINED ${ARGS})
00093     set (ARGS "${${ARGS}}")
00094   else ()
00095     set (ARGS "")
00096   endif ()
00097 endif ()
00098 
00099 if ("${COMMAND}" MATCHES "ARGS")
00100   string (REPLACE ";" "${ARGS_SEPARATOR}" TMP "${ARGS}")
00101   string (REPLACE "ARGS" "${ARGS_SEPARATOR}${TMP}" COMMAND "${COMMAND}")
00102   set (ARGS)
00103 endif ()
00104 
00105 set (EXECUTE_PROCESS_ARGS "COMMAND" "${COMMAND}")
00106 
00107 if (ARGS)
00108   list (APPEND EXECUTE_PROCESS_ARGS "ARGS" "${ARGS}")
00109 endif ()
00110 
00111 list (APPEND EXECUTE_PROCESS_ARGS "RESULT_VARIABLE" "RETVAL")
00112 
00113 if (TIMEOUT)
00114   list (APPEND EXECUTE_PROCESS_ARGS "TIMEOUT" "${TIMEOUT}")
00115 endif ()
00116 
00117 if (WORKING_DIRECTORY)
00118   list (APPEND EXECUTE_PROCESS_ARGS "WORKING_DIRECTORY" "${WORKING_DIRECTORY}")
00119 endif ()
00120 
00121 if (OUTPUT_FILE)
00122   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_FILE" "${OUTPUT_FILE}")
00123 endif ()
00124 
00125 if (ERROR_FILE)
00126   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_FILE" "${ERROR_FILE}")
00127 endif ()
00128 
00129 if (OUTPUT_QUIET)
00130   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_QUIET")
00131 endif ()
00132 
00133 if (ERROR_QUIET)
00134   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_QUIET")
00135 endif ()
00136 
00137 if (OUTPUT_STRIP_TRAILING_WHITESPACE)
00138   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_STRIP_TRAILING_WHITESPACE")
00139 endif ()
00140 
00141 if (ERROR_STRIP_TRAILING_WHITESPACE)
00142   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_STRIP_TRAILING_WHITESPACE")
00143 endif ()
00144 
00145 if (NOT OUTPUT_FILE)
00146   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_VARIABLE" "STDOUT")
00147 endif ()
00148 
00149 if (NOT ERROR_FILE)
00150   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_VARIABLE"  "STDERR")
00151 endif ()
00152 
00153 if (NOT RETRY_ATTEMPTS)
00154   set (RETRY_ATTEMPTS 0)
00155 endif ()
00156 
00157 if (NOT RETRY_DELAY)
00158   set (RETRY_DELAY 60)
00159 endif ()
00160 
00161 # --------------------------------------------------------------------------
00162 # verbose message of command to execute
00163 set (CMD)
00164 foreach (ARG ${COMMAND})
00165   if (CMD)
00166     set (CMD "${CMD} ")
00167   endif ()
00168   if (ARG MATCHES " ")
00169     set (CMD "${CMD}\"${ARG}\"")
00170   else ()
00171     set (CMD "${CMD}${ARG}")
00172   endif ()
00173 endforeach ()
00174 
00175 if (VERBOSE)
00176   message ("${CMD}")
00177 endif ()
00178 
00179 # ----------------------------------------------------------------------------
00180 # execute command
00181 set (RETRY TRUE) # execute process at least once
00182 while (RETRY)
00183   # --------------------------------------------------------------------------
00184   # whether retry is required is decided upon after process execution
00185   set (RETRY FALSE)
00186 
00187   # --------------------------------------------------------------------------
00188   # execute process
00189   execute_process (${EXECUTE_PROCESS_ARGS})
00190 
00191   # --------------------------------------------------------------------------
00192   # read in output from log files
00193   if (OUTPUT_FILE)
00194     file (READ "${OUTPUT_FILE}" STDOUT)
00195   endif ()
00196 
00197   if (ERROR_FILE)
00198     file (READ "${ERROR_FILE}" STDERR)
00199   endif ()
00200 
00201   # --------------------------------------------------------------------------
00202   # parse output for recoverable errors
00203   foreach (EXPR IN LISTS RETRY_EXPRESSION)
00204     if (STDOUT MATCHES "${EXPR}" OR STDERR MATCHES "${EXPR}")
00205       if (RETRY_ATTEMPTS GREATER 0)
00206         message ("Process output matches \"${EXPR}\", retry in ${RETRY_DELAY} seconds")
00207         # retry
00208         math (EXPR RETRY_ATTEMPTS "${RETRY_ATTEMPTS} - 1")
00209         set (RETRY TRUE)
00210       else ()
00211         # no retries left
00212         set (RETVAL 1)
00213       endif ()
00214       break ()
00215     endif ()
00216   endforeach ()
00217 
00218   # --------------------------------------------------------------------------
00219   # sleep for given amount of seconds before retrying to execute process
00220   if (RETRY)
00221     # use sleep command if available, i.e., on Unix and also on Windows
00222     # if the Windows 2003 Resource Kit is installed
00223     find_program (SLEEP sleep)
00224     if (SLEEP)
00225       execute_process (
00226         COMMAND "${SLEEP}" ${RETRY_DELAY}
00227         TIMEOUT ${RETRY_DELAY}
00228         ERROR_QUIET OUTPUT_QUIET
00229       )
00230     else ()
00231       # work-around using ping command if sleep command not available, i.e.,
00232       # on Windows where the Windows 2003 Resource Kit is not installed
00233       # See http://malektips.com/dos0017.html
00234       find_program (PING ping)
00235       if (WINDOWS)
00236         execute_process (
00237           COMMAND ${PING} 127.0.0.1 -n ${RETRY_DELAY} -w 1000
00238           TIMEOUT ${RETRY_DELAY}
00239           ERROR_QUIET OUTPUT_QUIET
00240         )
00241       else ()
00242         # usually not required as sleep command should be available
00243         execute_process (
00244           COMMAND ${PING} 127.0.0.1 -c ${RETRY_DELAY} -W 1000
00245           TIMEOUT ${RETRY_DELAY}
00246           ERROR_QUIET OUTPUT_QUIET
00247         )
00248       endif ()
00249     else ()
00250       message (WARNING "Cannot delay retries as neither sleep nor ping command is available!")
00251     endif ()
00252   endif ()
00253 endwhile ()
00254 
00255 # ----------------------------------------------------------------------------
00256 # parse output for errors
00257 foreach (EXPR IN LISTS ERROR_EXPRESSION)
00258   if (STDOUT MATCHES "${EXPR}" OR STDERR MATCHES "${EXPR}")
00259     set (RETVAL 1)
00260     break ()
00261   endif ()
00262 endforeach ()
00263 
00264 # ----------------------------------------------------------------------------
00265 # prepand command to log file
00266 if (LOG_ARGS)
00267   if (OUTPUT_FILE)
00268     set (TMP "Command: ${CMD}\n\nWorking directory: ${WORKING_DIRECTORY}\n\n")
00269     set (TMP "${TMP}Timeout: ${TIMEOUT}\n\nOutput:\n\n${STDOUT}")
00270     file (WRITE "${OUTPUT_FILE}" "${TMP}")
00271     unset (TMP)
00272   endif ()
00273 
00274   if (ERROR_FILE AND NOT "${ERROR_FILE}" STREQUAL "${OUTPUT_FILE}")
00275     set (TMP "Command: ${CMD}\n\nWorking directory: ${WORKING_DIRECTORY}\n\n")
00276     set (TMP "${TMP}Timeout: ${TIMEOUT}\n\nOutput:\n\n${STDERR}")
00277     file (WRITE "${ERROR_FILE}" "${TMP}")
00278     unset (TMP)
00279   endif ()
00280 endif ()
00281 
00282 # ----------------------------------------------------------------------------
00283 # print error message (and exit with exit code 1) on error
00284 if (NOT RETVAL EQUAL 0)
00285   if ("${STDOUT}" STREQUAL "${STDERR}")
00286     message (
00287       FATAL_ERROR "
00288 Command: ${CMD}
00289 Working directory: ${WORKING_DIRECTORY}
00290 Timeout: ${TIMEOUT}
00291 Output:
00292 ${STDOUT}")
00293   else ()
00294     message (
00295       FATAL_ERROR "
00296 Command: ${CMD}
00297 Working directory: ${WORKING_DIRECTORY}
00298 Timeout: ${TIMEOUT}
00299 Output (stdout):
00300 ${STDOUT}
00301 Output (stderr):
00302 ${STDERR}")
00303   endif ()
00304 endif ()