BASIS  r3148
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 # unset environment variables that may cause problems otherwise
00081 unset (ENV{PYTHONHOME})
00082 unset (ENV{PYTHONPATH})
00083 
00084 # ----------------------------------------------------------------------------
00085 # initialize arguments
00086 set (CONFIGURED_COMMAND "@COMMAND@")
00087 if (CONFIGURED_COMMAND)
00088   set (COMMAND "${CONFIGURED_COMMAND}")
00089 elseif (NOT COMMAND)
00090   message (FATAL_ERROR "No command specified for execute_process(): use \"-DCOMMAND=cmd\"")
00091 endif ()
00092 
00093 if (NOT ARGS_SEPARATOR)
00094   set (ARGS_SEPARATOR ";")
00095 endif ()
00096 
00097 if (ARGS_FILE)
00098   include ("${ARGS_FILE}" OPTIONAL)
00099 
00100   if (ARGS AND DEFINED ${ARGS})
00101     set (ARGS "${${ARGS}}")
00102   else ()
00103     set (ARGS "")
00104   endif ()
00105 endif ()
00106 
00107 if ("${COMMAND}" MATCHES "ARGS")
00108   string (REPLACE ";" "${ARGS_SEPARATOR}" TMP "${ARGS}")
00109   string (REPLACE "ARGS" "${ARGS_SEPARATOR}${TMP}" COMMAND "${COMMAND}")
00110   set (ARGS)
00111 endif ()
00112 
00113 set (EXECUTE_PROCESS_ARGS "COMMAND" "${COMMAND}")
00114 
00115 if (ARGS)
00116   list (APPEND EXECUTE_PROCESS_ARGS "ARGS" "${ARGS}")
00117 endif ()
00118 
00119 list (APPEND EXECUTE_PROCESS_ARGS "RESULT_VARIABLE" "RETVAL")
00120 
00121 if (TIMEOUT)
00122   list (APPEND EXECUTE_PROCESS_ARGS "TIMEOUT" "${TIMEOUT}")
00123 endif ()
00124 
00125 if (WORKING_DIRECTORY)
00126   list (APPEND EXECUTE_PROCESS_ARGS "WORKING_DIRECTORY" "${WORKING_DIRECTORY}")
00127 endif ()
00128 
00129 if (OUTPUT_FILE)
00130   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_FILE" "${OUTPUT_FILE}")
00131 endif ()
00132 
00133 if (ERROR_FILE)
00134   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_FILE" "${ERROR_FILE}")
00135 endif ()
00136 
00137 if (OUTPUT_QUIET)
00138   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_QUIET")
00139 endif ()
00140 
00141 if (ERROR_QUIET)
00142   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_QUIET")
00143 endif ()
00144 
00145 if (OUTPUT_STRIP_TRAILING_WHITESPACE)
00146   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_STRIP_TRAILING_WHITESPACE")
00147 endif ()
00148 
00149 if (ERROR_STRIP_TRAILING_WHITESPACE)
00150   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_STRIP_TRAILING_WHITESPACE")
00151 endif ()
00152 
00153 if (NOT OUTPUT_FILE)
00154   list (APPEND EXECUTE_PROCESS_ARGS "OUTPUT_VARIABLE" "STDOUT")
00155 endif ()
00156 
00157 if (NOT ERROR_FILE)
00158   list (APPEND EXECUTE_PROCESS_ARGS "ERROR_VARIABLE"  "STDERR")
00159 endif ()
00160 
00161 if (NOT RETRY_ATTEMPTS)
00162   set (RETRY_ATTEMPTS 0)
00163 endif ()
00164 
00165 if (NOT RETRY_DELAY)
00166   set (RETRY_DELAY 60)
00167 endif ()
00168 
00169 # --------------------------------------------------------------------------
00170 # verbose message of command to execute
00171 set (CMD)
00172 foreach (ARG ${COMMAND})
00173   if (CMD)
00174     set (CMD "${CMD} ")
00175   endif ()
00176   if (ARG MATCHES " ")
00177     set (CMD "${CMD}\"${ARG}\"")
00178   else ()
00179     set (CMD "${CMD}${ARG}")
00180   endif ()
00181 endforeach ()
00182 
00183 if (VERBOSE)
00184   message ("${CMD}")
00185 endif ()
00186 
00187 # ----------------------------------------------------------------------------
00188 # execute command
00189 set (RETRY TRUE) # execute process at least once
00190 while (RETRY)
00191   # --------------------------------------------------------------------------
00192   # whether retry is required is decided upon after process execution
00193   set (RETRY FALSE)
00194 
00195   # --------------------------------------------------------------------------
00196   # execute process
00197   execute_process (${EXECUTE_PROCESS_ARGS})
00198 
00199   # --------------------------------------------------------------------------
00200   # read in output from log files
00201   if (OUTPUT_FILE)
00202     file (READ "${OUTPUT_FILE}" STDOUT)
00203   endif ()
00204 
00205   if (ERROR_FILE)
00206     file (READ "${ERROR_FILE}" STDERR)
00207   endif ()
00208 
00209   # --------------------------------------------------------------------------
00210   # parse output for recoverable errors
00211   foreach (EXPR IN LISTS RETRY_EXPRESSION)
00212     if (STDOUT MATCHES "${EXPR}" OR STDERR MATCHES "${EXPR}")
00213       if (RETRY_ATTEMPTS GREATER 0)
00214         message ("Process output matches \"${EXPR}\", retry in ${RETRY_DELAY} seconds")
00215         # retry
00216         math (EXPR RETRY_ATTEMPTS "${RETRY_ATTEMPTS} - 1")
00217         set (RETRY TRUE)
00218       else ()
00219         # no retries left
00220         set (RETVAL 1)
00221       endif ()
00222       break ()
00223     endif ()
00224   endforeach ()
00225 
00226   # --------------------------------------------------------------------------
00227   # sleep for given amount of seconds before retrying to execute process
00228   if (RETRY)
00229     # use sleep command if available, i.e., on Unix and also on Windows
00230     # if the Windows 2003 Resource Kit is installed
00231     find_program (SLEEP sleep)
00232     if (SLEEP)
00233       execute_process (
00234         COMMAND "${SLEEP}" ${RETRY_DELAY}
00235         TIMEOUT ${RETRY_DELAY}
00236         ERROR_QUIET OUTPUT_QUIET
00237       )
00238     else ()
00239       # work-around using ping command if sleep command not available, i.e.,
00240       # on Windows where the Windows 2003 Resource Kit is not installed
00241       # See http://malektips.com/dos0017.html
00242       find_program (PING ping)
00243       if (WIN32)
00244         execute_process (
00245           COMMAND ${PING} 127.0.0.1 -n ${RETRY_DELAY} -w 1000
00246           TIMEOUT ${RETRY_DELAY}
00247           ERROR_QUIET OUTPUT_QUIET
00248         )
00249       else ()
00250         # usually not required as sleep command should be available
00251         execute_process (
00252           COMMAND ${PING} 127.0.0.1 -c ${RETRY_DELAY} -W 1000
00253           TIMEOUT ${RETRY_DELAY}
00254           ERROR_QUIET OUTPUT_QUIET
00255         )
00256       endif ()
00257     else ()
00258       message (WARNING "Cannot delay retries as neither sleep nor ping command is available!")
00259     endif ()
00260   endif ()
00261 endwhile ()
00262 
00263 # ----------------------------------------------------------------------------
00264 # parse output for errors
00265 foreach (EXPR IN LISTS ERROR_EXPRESSION)
00266   if (STDOUT MATCHES "${EXPR}" OR STDERR MATCHES "${EXPR}")
00267     set (RETVAL 1)
00268     break ()
00269   endif ()
00270 endforeach ()
00271 
00272 # ----------------------------------------------------------------------------
00273 # give some hints for known error messages
00274 if (NOT RETVAL EQUAL 0)
00275   set (HINTS)
00276   # MATLAB Compiler
00277   if (CMD MATCHES "mcc")
00278     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.'")
00279       set (HINTS "${HINTS}
00280 
00281   Note: The error that a MEX-file would shadow a M-file can be caused by a shared library that
00282         is required by the MEX-file but not found in the search path of the dynamic loader.
00283 ")
00284     endif ()
00285   endif ()
00286   # append hints to output
00287   if ("${STDOUT}" STREQUAL "${STDERR}")
00288     set (STDERR "${STDERR}${HINTS}")
00289     set (STDOUT "${STDOUT}${HINTS}")
00290   else ()
00291     set (STDERR "${STDERR}${HINTS}")
00292   endif ()
00293 endif ()
00294 
00295 # ----------------------------------------------------------------------------
00296 # prepand command to log file
00297 if (LOG_ARGS)
00298   if (OUTPUT_FILE)
00299     set (TMP "Command: ${CMD}\n\nWorking directory: ${WORKING_DIRECTORY}\n\n")
00300     set (TMP "${TMP}Timeout: ${TIMEOUT}\n\nOutput:\n\n${STDOUT}")
00301     file (WRITE "${OUTPUT_FILE}" "${TMP}")
00302     unset (TMP)
00303   endif ()
00304 
00305   if (ERROR_FILE AND NOT "${ERROR_FILE}" STREQUAL "${OUTPUT_FILE}")
00306     set (TMP "Command: ${CMD}\n\nWorking directory: ${WORKING_DIRECTORY}\n\n")
00307     set (TMP "${TMP}Timeout: ${TIMEOUT}\n\nOutput:\n\n${STDERR}")
00308     file (WRITE "${ERROR_FILE}" "${TMP}")
00309     unset (TMP)
00310   endif ()
00311 endif ()
00312 
00313 # ----------------------------------------------------------------------------
00314 # print error message (and exit with exit code 1) on error
00315 if (NOT RETVAL EQUAL 0)
00316   
00317   # print error
00318   if ("${STDOUT}" STREQUAL "${STDERR}")
00319     message (
00320       FATAL_ERROR "
00321 Command: ${CMD}
00322 Working directory: ${WORKING_DIRECTORY}
00323 Timeout: ${TIMEOUT}
00324 Output:
00325 ${STDOUT}")
00326   else ()
00327     message (
00328       FATAL_ERROR "
00329 Command: ${CMD}
00330 Working directory: ${WORKING_DIRECTORY}
00331 Timeout: ${TIMEOUT}
00332 Output (stdout):
00333 ${STDOUT}
00334 Output (stderr):
00335 ${STDERR}")
00336   endif ()
00337 endif ()