Преглед на файлове

execute_process: add options for fatal errors on subprocess failure

Fixes: #19930
Asit Dhal преди 5 години
родител
ревизия
116a427eb1

+ 7 - 1
Help/command/execute_process.rst

@@ -23,7 +23,8 @@ Execute one or more child processes.
                   [ERROR_STRIP_TRAILING_WHITESPACE]
                   [ENCODING <name>]
                   [ECHO_OUTPUT_VARIABLE]
-                  [ECHO_ERROR_VARIABLE])
+                  [ECHO_ERROR_VARIABLE]
+                  [COMMAND_ERROR_IS_FATAL <ANY|LAST>])
 
 Runs the given sequence of one or more commands.
 
@@ -116,6 +117,11 @@ Options:
 
   This is analogous to the ``tee`` Unix command.
 
+``COMMAND_ERROR_IS_FATAL <ANY|LAST>``
+  ``COMMAND_ERROR_IS_FATAL ANY`` option stops processing if any command fails.
+  ``COMMAND_ERROR_IS_FATAL LAST`` option stops processing if the last command
+  in the command list fails.
+
 If more than one ``OUTPUT_*`` or ``ERROR_*`` option is given for the
 same pipe the precedence is not specified.
 If no ``OUTPUT_*`` or ``ERROR_*`` options are given the output will

+ 5 - 0
Help/release/dev/execute-process-command-error-is-fatal.rst

@@ -0,0 +1,5 @@
+execute-process-command-error-is-fatal
+--------------------------------------
+
+* The :command:`execute_process` command gained a ``COMMAND_ERROR_IS_FATAL``
+  option to specify a fatal error.

+ 57 - 1
Source/cmExecuteProcessCommand.cxx

@@ -7,8 +7,10 @@
 #include <cstdio>
 #include <iostream>
 #include <memory>
+#include <sstream>
 #include <vector>
 
+#include <cm/string_view>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
@@ -63,6 +65,7 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     bool EchoOutputVariable = false;
     bool EchoErrorVariable = false;
     std::string Encoding;
+    std::string CommandErrorIsFatal;
   };
 
   static auto const parser =
@@ -86,7 +89,8 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
             &Arguments::ErrorStripTrailingWhitespace)
       .Bind("ENCODING"_s, &Arguments::Encoding)
       .Bind("ECHO_OUTPUT_VARIABLE"_s, &Arguments::EchoOutputVariable)
-      .Bind("ECHO_ERROR_VARIABLE"_s, &Arguments::EchoErrorVariable);
+      .Bind("ECHO_ERROR_VARIABLE"_s, &Arguments::EchoErrorVariable)
+      .Bind("COMMAND_ERROR_IS_FATAL"_s, &Arguments::CommandErrorIsFatal);
 
   std::vector<std::string> unparsedArguments;
   std::vector<std::string> keywordsMissingValue;
@@ -131,6 +135,14 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
       return false;
     }
   }
+
+  if (!arguments.CommandErrorIsFatal.empty()) {
+    if (arguments.CommandErrorIsFatal != "ANY"_s &&
+        arguments.CommandErrorIsFatal != "LAST"_s) {
+      status.SetError("COMMAND_ERROR_IS_FATAL option can be ANY or LAST");
+      return false;
+    }
+  }
   // Create a process instance.
   std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
     cmsysProcess_New(), cmsysProcess_Delete);
@@ -363,6 +375,50 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     }
   }
 
+  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);
+          }
+        }
+      }
+      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;
+      }
+    }
+  }
+
+  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) {
+          status.SetError("last command failed");
+          cmSystemTools::SetFatalErrorOccured();
+          return false;
+        }
+      }
+    }
+  }
+
   return true;
 }
 

+ 1 - 0
Tests/RunCMake/execute_process/AnyCommandError-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/execute_process/AnyCommandError-stderr.txt

@@ -0,0 +1,2 @@
+CMake Error at .*AnyCommandError.cmake:1 \(execute_process\):
+  execute_process failed command indexes: 2, 3, 4

+ 8 - 0
Tests/RunCMake/execute_process/AnyCommandError.cmake

@@ -0,0 +1,8 @@
+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
+)

+ 1 - 0
Tests/RunCMake/execute_process/CommandError-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/execute_process/CommandError-stderr.txt

@@ -0,0 +1,2 @@
+CMake Error at .*CommandError.cmake:1 \(execute_process\):
+  execute_process COMMAND_ERROR_IS_FATAL option can be ANY or LAST

+ 3 - 0
Tests/RunCMake/execute_process/CommandError.cmake

@@ -0,0 +1,3 @@
+execute_process(COMMAND ${CMAKE_COMMAND} -E true
+    COMMAND_ERROR_IS_FATAL ALL
+)

+ 1 - 0
Tests/RunCMake/execute_process/LastCommandError-result.txt

@@ -0,0 +1 @@
+1

+ 2 - 0
Tests/RunCMake/execute_process/LastCommandError-stderr.txt

@@ -0,0 +1,2 @@
+CMake Error at .*LastCommandError.cmake:1 \(execute_process\):
+  execute_process last command failed

+ 8 - 0
Tests/RunCMake/execute_process/LastCommandError.cmake

@@ -0,0 +1,8 @@
+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
+)

+ 4 - 0
Tests/RunCMake/execute_process/RunCMakeTest.cmake

@@ -26,3 +26,7 @@ run_cmake_command(EchoCommand3 ${CMAKE_COMMAND}
   ${RunCMake_SOURCE_DIR}/EchoCommand.cmake)
 
 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)