mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-16 05:26:58 +08:00
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
This commit is contained in:
@@ -6,8 +6,10 @@
|
||||
#include <cctype> /* isspace */
|
||||
#include <cstdio>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <cm/string_view>
|
||||
@@ -375,47 +377,101 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
|
||||
}
|
||||
}
|
||||
|
||||
auto queryProcessStatusByIndex = [&cp](int index) -> std::string {
|
||||
std::string processStatus;
|
||||
switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(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<int>(index)));
|
||||
break;
|
||||
}
|
||||
case kwsysProcess_StateByIndex_Error:
|
||||
default:
|
||||
processStatus = "Error getting the child return code";
|
||||
break;
|
||||
}
|
||||
return processStatus;
|
||||
};
|
||||
|
||||
if (arguments.CommandErrorIsFatal == "ANY"_s) {
|
||||
if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
|
||||
std::vector<int> failedIndexes;
|
||||
for (int i = 0; i < static_cast<int>(arguments.Commands.size()); ++i) {
|
||||
if (cmsysProcess_GetStateByIndex(cp, i) ==
|
||||
kwsysProcess_StateByIndex_Exited) {
|
||||
int exitCode = cmsysProcess_GetExitValueByIndex(cp, i);
|
||||
if (exitCode) {
|
||||
failedIndexes.push_back(i);
|
||||
bool ret = true;
|
||||
switch (cmsysProcess_GetState(cp)) {
|
||||
case cmsysProcess_State_Exited: {
|
||||
std::map<int, std::string> failureIndices;
|
||||
for (int i = 0; i < static_cast<int>(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
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 << ", ";
|
||||
}
|
||||
}
|
||||
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<int>(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<int>(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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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=$<TARGET_FILE:exit_code>)
|
||||
set(execute_process_ARGS
|
||||
-DEXIT_CODE_EXE=$<TARGET_FILE:exit_code>
|
||||
-DPYTHON_EXECUTABLE=${PYTHON_EXECUTABLE}
|
||||
)
|
||||
if(NOT CMake_TEST_EXTERNAL_CMAKE)
|
||||
list(APPEND execute_process_ARGS -DTEST_ENCODING_EXE=$<TARGET_FILE:testEncoding>)
|
||||
endif()
|
||||
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -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
|
@@ -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
|
||||
)
|
@@ -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"
|
||||
|
@@ -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
|
||||
)
|
||||
|
4
Tests/RunCMake/execute_process/AnyCommandGood.cmake
Normal file
4
Tests/RunCMake/execute_process/AnyCommandGood.cmake
Normal file
@@ -0,0 +1,4 @@
|
||||
execute_process(COMMAND ${CMAKE_COMMAND} -E true
|
||||
COMMAND ${CMAKE_COMMAND} -E true
|
||||
COMMAND_ERROR_IS_FATAL ANY
|
||||
)
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1,2 @@
|
||||
CMake Error at .*AnyCommandTimeout.cmake:9 \(execute_process\):
|
||||
execute_process Process terminated due to timeout
|
15
Tests/RunCMake/execute_process/AnyCommandTimeout.cmake
Normal file
15
Tests/RunCMake/execute_process/AnyCommandTimeout.cmake
Normal file
@@ -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()
|
@@ -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()
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1,2 @@
|
||||
CMake Error at .*LastCommandAbnormalExit-2.cmake:[0-9]+ \(execute_process\):
|
||||
execute_process Abnormal exit: Segmentation fault
|
@@ -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()
|
@@ -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
|
||||
|
@@ -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()
|
||||
|
15
Tests/RunCMake/execute_process/LastCommandGood.cmake
Normal file
15
Tests/RunCMake/execute_process/LastCommandGood.cmake
Normal file
@@ -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()
|
@@ -0,0 +1 @@
|
||||
1
|
@@ -0,0 +1,2 @@
|
||||
CMake Error at .*LastCommandTimeout.cmake:9 \(execute_process\):
|
||||
execute_process Process terminated due to timeout
|
15
Tests/RunCMake/execute_process/LastCommandTimeout.cmake
Normal file
15
Tests/RunCMake/execute_process/LastCommandTimeout.cmake
Normal file
@@ -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()
|
@@ -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(AnyCommandError ${CMAKE_COMMAND} -P ${RunCMake_SOURCE_DIR}/AnyCommandError.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(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(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()
|
||||
|
Reference in New Issue
Block a user