From e5a4ffaad166de7a8b907919ca2d805a3ec72c39 Mon Sep 17 00:00:00 2001 From: Asit Dhal Date: Tue, 8 Dec 2020 09:51:00 +0100 Subject: execute_process: Improve COMMAND_ERROR_IS_FATAL error capture scenarios 1. COMMAND_ERROR_IS_FATAL ANY will capture errors if the exit code is non zero, there is a timeout or an abnormal exit. 2. COMMAND_ERROR_IS_FATAL LAST will capture if only the last process has an exit code non zero, there is a timeout or an abnormal exit. Fixes: #21562 --- Source/cmExecuteProcessCommand.cxx | 118 +++++++++++++++------ Tests/RunCMake/CMakeLists.txt | 5 +- .../AnyCommandAbnormalExit-result.txt | 1 + .../AnyCommandAbnormalExit-stderr.txt | 4 + .../execute_process/AnyCommandAbnormalExit.cmake | 5 + .../execute_process/AnyCommandError-stderr.txt | 5 +- .../RunCMake/execute_process/AnyCommandError.cmake | 10 +- .../RunCMake/execute_process/AnyCommandGood.cmake | 4 + .../execute_process/AnyCommandTimeout-result.txt | 1 + .../execute_process/AnyCommandTimeout-stderr.txt | 2 + .../execute_process/AnyCommandTimeout.cmake | 15 +++ .../LastCommandAbnormalExit-1.cmake | 13 +++ .../LastCommandAbnormalExit-2-result.txt | 1 + .../LastCommandAbnormalExit-2-stderr.txt | 2 + .../LastCommandAbnormalExit-2.cmake | 13 +++ .../execute_process/LastCommandError-stderr.txt | 2 +- .../execute_process/LastCommandError.cmake | 13 ++- .../RunCMake/execute_process/LastCommandGood.cmake | 15 +++ .../execute_process/LastCommandTimeout-result.txt | 1 + .../execute_process/LastCommandTimeout-stderr.txt | 2 + .../execute_process/LastCommandTimeout.cmake | 15 +++ Tests/RunCMake/execute_process/RunCMakeTest.cmake | 12 ++- 22 files changed, 217 insertions(+), 42 deletions(-) create mode 100644 Tests/RunCMake/execute_process/AnyCommandAbnormalExit-result.txt create mode 100644 Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt create mode 100644 Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake create mode 100644 Tests/RunCMake/execute_process/AnyCommandGood.cmake create mode 100644 Tests/RunCMake/execute_process/AnyCommandTimeout-result.txt create mode 100644 Tests/RunCMake/execute_process/AnyCommandTimeout-stderr.txt create mode 100644 Tests/RunCMake/execute_process/AnyCommandTimeout.cmake create mode 100644 Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake create mode 100644 Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-result.txt create mode 100644 Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt create mode 100644 Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake create mode 100644 Tests/RunCMake/execute_process/LastCommandGood.cmake create mode 100644 Tests/RunCMake/execute_process/LastCommandTimeout-result.txt create mode 100644 Tests/RunCMake/execute_process/LastCommandTimeout-stderr.txt create mode 100644 Tests/RunCMake/execute_process/LastCommandTimeout.cmake diff --git a/Source/cmExecuteProcessCommand.cxx b/Source/cmExecuteProcessCommand.cxx index 14147e0..5a85b7d 100644 --- a/Source/cmExecuteProcessCommand.cxx +++ b/Source/cmExecuteProcessCommand.cxx @@ -6,8 +6,10 @@ #include /* isspace */ #include #include +#include #include #include +#include #include #include @@ -375,47 +377,101 @@ bool cmExecuteProcessCommand(std::vector const& args, } } - if (arguments.CommandErrorIsFatal == "ANY"_s) { - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - std::vector failedIndexes; - for (int i = 0; i < static_cast(arguments.Commands.size()); ++i) { - if (cmsysProcess_GetStateByIndex(cp, i) == - kwsysProcess_StateByIndex_Exited) { - int exitCode = cmsysProcess_GetExitValueByIndex(cp, i); - if (exitCode) { - failedIndexes.push_back(i); - } + auto queryProcessStatusByIndex = [&cp](int index) -> std::string { + std::string processStatus; + switch (cmsysProcess_GetStateByIndex(cp, static_cast(index))) { + case kwsysProcess_StateByIndex_Exited: { + int exitCode = cmsysProcess_GetExitValueByIndex(cp, index); + if (exitCode) { + processStatus = "Child return code: " + std::to_string(exitCode); } + } break; + case kwsysProcess_StateByIndex_Exception: { + processStatus = cmStrCat( + "Abnormal exit with child return code: ", + cmsysProcess_GetExceptionStringByIndex(cp, static_cast(index))); + break; } - if (!failedIndexes.empty()) { - std::ostringstream oss; - oss << "failed command indexes: "; - for (auto i = 0u; i < failedIndexes.size(); i++) { - if (i == failedIndexes.size() - 1) { - oss << failedIndexes[i] + 1; - } else { - oss << failedIndexes[i] + 1 << ", "; + case kwsysProcess_StateByIndex_Error: + default: + processStatus = "Error getting the child return code"; + break; + } + return processStatus; + }; + + if (arguments.CommandErrorIsFatal == "ANY"_s) { + bool ret = true; + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { + std::map failureIndices; + for (int i = 0; i < static_cast(arguments.Commands.size()); ++i) { + std::string processStatus = queryProcessStatusByIndex(i); + if (!processStatus.empty()) { + failureIndices[i] = processStatus; + } + if (!failureIndices.empty()) { + std::ostringstream oss; + oss << "failed command indexes:\n"; + for (auto const& e : failureIndices) { + oss << " " << e.first + 1 << ": \"" << e.second << "\"\n"; + } + status.SetError(oss.str()); + ret = false; } } - status.SetError(oss.str()); - cmSystemTools::SetFatalErrorOccured(); - return false; - } + } break; + case cmsysProcess_State_Exception: + status.SetError( + cmStrCat("abnormal exit: ", cmsysProcess_GetExceptionString(cp))); + ret = false; + break; + case cmsysProcess_State_Error: + status.SetError(cmStrCat("error getting child return code: ", + cmsysProcess_GetErrorString(cp))); + ret = false; + break; + case cmsysProcess_State_Expired: + status.SetError("Process terminated due to timeout"); + ret = false; + break; + } + + if (!ret) { + cmSystemTools::SetFatalErrorOccured(); + return false; } } if (arguments.CommandErrorIsFatal == "LAST"_s) { - if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) { - int lastIndex = static_cast(arguments.Commands.size() - 1); - if (cmsysProcess_GetStateByIndex(cp, lastIndex) == - kwsysProcess_StateByIndex_Exited) { - int exitCode = cmsysProcess_GetExitValueByIndex(cp, lastIndex); - if (exitCode) { + bool ret = true; + switch (cmsysProcess_GetState(cp)) { + case cmsysProcess_State_Exited: { + int lastIndex = static_cast(arguments.Commands.size() - 1); + const std::string processStatus = queryProcessStatusByIndex(lastIndex); + if (!processStatus.empty()) { status.SetError("last command failed"); - cmSystemTools::SetFatalErrorOccured(); - return false; + ret = false; } - } + } break; + case cmsysProcess_State_Exception: + status.SetError( + cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp))); + ret = false; + break; + case cmsysProcess_State_Error: + status.SetError(cmStrCat("Error getting child return code: ", + cmsysProcess_GetErrorString(cp))); + ret = false; + break; + case cmsysProcess_State_Expired: + status.SetError("Process terminated due to timeout"); + ret = false; + break; + } + if (!ret) { + cmSystemTools::SetFatalErrorOccured(); + return false; } } diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt index 615bf20..98d5d01 100644 --- a/Tests/RunCMake/CMakeLists.txt +++ b/Tests/RunCMake/CMakeLists.txt @@ -299,7 +299,10 @@ add_RunCMake_test(add_subdirectory) add_RunCMake_test(add_test) add_RunCMake_test(build_command) add_executable(exit_code exit_code.c) -set(execute_process_ARGS -DEXIT_CODE_EXE=$) +set(execute_process_ARGS + -DEXIT_CODE_EXE=$ + -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} + ) if(NOT CMake_TEST_EXTERNAL_CMAKE) list(APPEND execute_process_ARGS -DTEST_ENCODING_EXE=$) endif() diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-result.txt b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt new file mode 100644 index 0000000..9627872 --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit-stderr.txt @@ -0,0 +1,4 @@ +CMake Error at .*AnyCommandAbnormalExit.cmake:[0-9]+ \(execute_process\): + execute_process failed command indexes: + + 1: "Abnormal exit with child return code: Segmentation fault diff --git a/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake new file mode 100644 index 0000000..5ac0c21 --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandAbnormalExit.cmake @@ -0,0 +1,5 @@ +execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c + "import os; os.kill(os.getpid(),11)" + COMMAND ${CMAKE_COMMAND} -E true + COMMAND_ERROR_IS_FATAL ANY + ) diff --git a/Tests/RunCMake/execute_process/AnyCommandError-stderr.txt b/Tests/RunCMake/execute_process/AnyCommandError-stderr.txt index 0380562..bf36391 100644 --- a/Tests/RunCMake/execute_process/AnyCommandError-stderr.txt +++ b/Tests/RunCMake/execute_process/AnyCommandError-stderr.txt @@ -1,2 +1,5 @@ CMake Error at .*AnyCommandError.cmake:1 \(execute_process\): - execute_process failed command indexes: 2, 3, 4 + execute_process failed command indexes: + + 2: "Child return code: 1" + 3: "Child return code: 1" diff --git a/Tests/RunCMake/execute_process/AnyCommandError.cmake b/Tests/RunCMake/execute_process/AnyCommandError.cmake index f8ec385..c9348cd 100644 --- a/Tests/RunCMake/execute_process/AnyCommandError.cmake +++ b/Tests/RunCMake/execute_process/AnyCommandError.cmake @@ -1,8 +1,6 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E true - COMMAND ${CMAKE_COMMAND} -E false - COMMAND ${CMAKE_COMMAND} -E false - COMMAND ${CMAKE_COMMAND} -E false - COMMAND ${CMAKE_COMMAND} -E true - COMMAND ${CMAKE_COMMAND} -E true - COMMAND_ERROR_IS_FATAL ANY + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E true + COMMAND_ERROR_IS_FATAL ANY ) diff --git a/Tests/RunCMake/execute_process/AnyCommandGood.cmake b/Tests/RunCMake/execute_process/AnyCommandGood.cmake new file mode 100644 index 0000000..27f0996 --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandGood.cmake @@ -0,0 +1,4 @@ +execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E true + COMMAND_ERROR_IS_FATAL ANY + ) diff --git a/Tests/RunCMake/execute_process/AnyCommandTimeout-result.txt b/Tests/RunCMake/execute_process/AnyCommandTimeout-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandTimeout-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/AnyCommandTimeout-stderr.txt b/Tests/RunCMake/execute_process/AnyCommandTimeout-stderr.txt new file mode 100644 index 0000000..10cc5f4 --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandTimeout-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at .*AnyCommandTimeout.cmake:9 \(execute_process\): + execute_process Process terminated due to timeout diff --git a/Tests/RunCMake/execute_process/AnyCommandTimeout.cmake b/Tests/RunCMake/execute_process/AnyCommandTimeout.cmake new file mode 100644 index 0000000..a5a53fd --- /dev/null +++ b/Tests/RunCMake/execute_process/AnyCommandTimeout.cmake @@ -0,0 +1,15 @@ +execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E sleep 10 + COMMAND ${CMAKE_COMMAND} -E true + TIMEOUT 1 + RESULT_VARIABLE result +) + +if(NOT result EQUAL "0") + execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E sleep 10 + COMMAND ${CMAKE_COMMAND} -E true + TIMEOUT 1 + COMMAND_ERROR_IS_FATAL ANY + ) +endif() diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake new file mode 100644 index 0000000..5a4574c --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-1.cmake @@ -0,0 +1,13 @@ +execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c + "import os; os.kill(os.getpid(),11)" + COMMAND ${CMAKE_COMMAND} -E true + RESULT_VARIABLE result + ) + +if(result EQUAL "0") + execute_process(COMMAND "${PYTHON_EXECUTABLE}" -c + "import os; os.kill(os.getpid(),11)" + COMMAND ${CMAKE_COMMAND} -E true + COMMAND_ERROR_IS_FATAL LAST + ) +endif() diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-result.txt b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt new file mode 100644 index 0000000..c915e58 --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at .*LastCommandAbnormalExit-2.cmake:[0-9]+ \(execute_process\): + execute_process Abnormal exit: Segmentation fault diff --git a/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake new file mode 100644 index 0000000..b87e0f7 --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandAbnormalExit-2.cmake @@ -0,0 +1,13 @@ +execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND "${PYTHON_EXECUTABLE}" -c + "import os; os.kill(os.getpid(),11)" + RESULT_VARIABLE result + ) + +if(NOT result EQUAL "0") + execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND "${PYTHON_EXECUTABLE}" -c + "import os; os.kill(os.getpid(),11)" + COMMAND_ERROR_IS_FATAL LAST + ) +endif() diff --git a/Tests/RunCMake/execute_process/LastCommandError-stderr.txt b/Tests/RunCMake/execute_process/LastCommandError-stderr.txt index ff191b3..335a771 100644 --- a/Tests/RunCMake/execute_process/LastCommandError-stderr.txt +++ b/Tests/RunCMake/execute_process/LastCommandError-stderr.txt @@ -1,2 +1,2 @@ -CMake Error at .*LastCommandError.cmake:1 \(execute_process\): +CMake Error at .*LastCommandError.cmake:11 \(execute_process\): execute_process last command failed diff --git a/Tests/RunCMake/execute_process/LastCommandError.cmake b/Tests/RunCMake/execute_process/LastCommandError.cmake index 6116a5c..9a925fe 100644 --- a/Tests/RunCMake/execute_process/LastCommandError.cmake +++ b/Tests/RunCMake/execute_process/LastCommandError.cmake @@ -1,8 +1,19 @@ execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E false + RESULT_VARIABLE result +) + +if(NOT result EQUAL "0") + execute_process(COMMAND ${CMAKE_COMMAND} -E true COMMAND ${CMAKE_COMMAND} -E false COMMAND ${CMAKE_COMMAND} -E false COMMAND ${CMAKE_COMMAND} -E false COMMAND ${CMAKE_COMMAND} -E true COMMAND ${CMAKE_COMMAND} -E false COMMAND_ERROR_IS_FATAL LAST -) + ) +endif() diff --git a/Tests/RunCMake/execute_process/LastCommandGood.cmake b/Tests/RunCMake/execute_process/LastCommandGood.cmake new file mode 100644 index 0000000..c22b49d --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandGood.cmake @@ -0,0 +1,15 @@ +execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E true + RESULT_VARIABLE result + ) + +if(result EQUAL "0") + execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E false + COMMAND ${CMAKE_COMMAND} -E true + COMMAND_ERROR_IS_FATAL LAST + ) +endif() diff --git a/Tests/RunCMake/execute_process/LastCommandTimeout-result.txt b/Tests/RunCMake/execute_process/LastCommandTimeout-result.txt new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandTimeout-result.txt @@ -0,0 +1 @@ +1 diff --git a/Tests/RunCMake/execute_process/LastCommandTimeout-stderr.txt b/Tests/RunCMake/execute_process/LastCommandTimeout-stderr.txt new file mode 100644 index 0000000..1cd1546 --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandTimeout-stderr.txt @@ -0,0 +1,2 @@ +CMake Error at .*LastCommandTimeout.cmake:9 \(execute_process\): + execute_process Process terminated due to timeout diff --git a/Tests/RunCMake/execute_process/LastCommandTimeout.cmake b/Tests/RunCMake/execute_process/LastCommandTimeout.cmake new file mode 100644 index 0000000..9c1f444 --- /dev/null +++ b/Tests/RunCMake/execute_process/LastCommandTimeout.cmake @@ -0,0 +1,15 @@ +execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E sleep 10 + COMMAND ${CMAKE_COMMAND} -E true + TIMEOUT 1 + RESULT_VARIABLE result +) + +if(NOT result EQUAL "0") + execute_process(COMMAND ${CMAKE_COMMAND} -E true + COMMAND ${CMAKE_COMMAND} -E sleep 10 + COMMAND ${CMAKE_COMMAND} -E true + TIMEOUT 1 + COMMAND_ERROR_IS_FATAL LAST + ) +endif() diff --git a/Tests/RunCMake/execute_process/RunCMakeTest.cmake b/Tests/RunCMake/execute_process/RunCMakeTest.cmake index f4c3d19..35712f6 100644 --- a/Tests/RunCMake/execute_process/RunCMakeTest.cmake +++ b/Tests/RunCMake/execute_process/RunCMakeTest.cmake @@ -27,6 +27,16 @@ run_cmake_command(EchoCommand3 ${CMAKE_COMMAND} run_cmake_command(EchoVariable ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/EchoVariable.cmake) +run_cmake_command(CommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/CommandError.cmake) run_cmake_command(AnyCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyCommandError.cmake) +run_cmake_command(AnyCommandTimeout ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyCommandTimeout.cmake) +run_cmake_command(AnyCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyCommandGood.cmake) run_cmake_command(LastCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandError.cmake) -run_cmake_command(CommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/CommandError.cmake) +run_cmake_command(LastCommandTimeout ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandTimeout.cmake) +run_cmake_command(LastCommandGood ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/LastCommandGood.cmake) + +if(UNIX AND PYTHON_EXECUTABLE) + run_cmake_command(AnyCommandAbnormalExit ${CMAKE_COMMAND} -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/AnyCommandAbnormalExit.cmake) + run_cmake_command(LastCommandAbnormalExit-1 ${CMAKE_COMMAND} -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-1.cmake) + run_cmake_command(LastCommandAbnormalExit-2 ${CMAKE_COMMAND} -DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE} -P ${RunCMake_SOURCE_DIR}/LastCommandAbnormalExit-2.cmake) +endif() -- cgit v0.12