Bladeren bron

cmCTestScriptHandler: Replace cmsysProcess with cmUVProcessChain

And update cmSystemTools::WaitForLine() to use cmUVProcessChain.
Kyle Edwards 2 jaren geleden
bovenliggende
commit
49a37d5a97
4 gewijzigde bestanden met toevoegingen van 123 en 69 verwijderingen
  1. 43 41
      Source/CTest/cmCTestScriptHandler.cxx
  2. 64 20
      Source/cmSystemTools.cxx
  3. 16 4
      Source/cmSystemTools.h
  4. 0 4
      Tests/CMakeLib/testUVProcessChainHelper.cxx

+ 43 - 41
Source/CTest/cmCTestScriptHandler.cxx

@@ -11,8 +11,9 @@
 
 #include <cm/memory>
 
+#include <cm3p/uv.h>
+
 #include "cmsys/Directory.hxx"
-#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestBuildCommand.h"
@@ -40,6 +41,8 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -148,66 +151,65 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg)
   // now pass through all the other arguments
   std::vector<std::string>& initArgs =
     this->CTest->GetInitialCommandLineArguments();
-  //*** need to make sure this does not have the current script ***
-  for (size_t i = 1; i < initArgs.size(); ++i) {
-    argv.push_back(initArgs[i].c_str());
-  }
-  argv.push_back(nullptr);
 
   // Now create process object
-  cmsysProcess* cp = cmsysProcess_New();
-  cmsysProcess_SetCommand(cp, argv.data());
-  // cmsysProcess_SetWorkingDirectory(cp, dir);
-  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
-  // cmsysProcess_SetTimeout(cp, timeout);
-  cmsysProcess_Execute(cp);
+  cmUVProcessChainBuilder builder;
+  builder.AddCommand(initArgs)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  auto process = builder.Start();
+  cm::uv_pipe_ptr outPipe;
+  outPipe.init(process.GetLoop(), 0);
+  uv_pipe_open(outPipe, process.OutputStream());
+  cm::uv_pipe_ptr errPipe;
+  errPipe.init(process.GetLoop(), 0);
+  uv_pipe_open(errPipe, process.ErrorStream());
 
   std::vector<char> out;
   std::vector<char> err;
   std::string line;
-  int pipe =
-    cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
-  while (pipe != cmsysProcess_Pipe_None) {
+  auto pipe =
+    cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
+                               std::chrono::seconds(100), out, err);
+  while (pipe != cmSystemTools::WaitForLineResult::None) {
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                "Output: " << line << "\n");
-    if (pipe == cmsysProcess_Pipe_STDERR) {
+    if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
       cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
-    } else if (pipe == cmsysProcess_Pipe_STDOUT) {
+    } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) {
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
     }
-    pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
-                                      err);
+    pipe =
+      cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
+                                 std::chrono::seconds(100), out, err);
   }
 
   // Properly handle output of the build command
-  cmsysProcess_WaitForExit(cp, nullptr);
-  int result = cmsysProcess_GetState(cp);
+  process.Wait();
+  auto const& status = process.GetStatus(0);
+  auto result = status.GetException();
   int retVal = 0;
   bool failed = false;
-  if (result == cmsysProcess_State_Exited) {
-    retVal = cmsysProcess_GetExitValue(cp);
-  } else if (result == cmsysProcess_State_Exception) {
-    retVal = cmsysProcess_GetExitException(cp);
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tThere was an exception: "
-                 << cmsysProcess_GetExceptionString(cp) << " " << retVal
-                 << std::endl);
-    failed = true;
-  } else if (result == cmsysProcess_State_Expired) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tThere was a timeout" << std::endl);
-    failed = true;
-  } else if (result == cmsysProcess_State_Error) {
-    cmCTestLog(this->CTest, ERROR_MESSAGE,
-               "\tError executing ctest: " << cmsysProcess_GetErrorString(cp)
-                                           << std::endl);
-    failed = true;
+  switch (result.first) {
+    case cmUVProcessChain::ExceptionCode::None:
+      retVal = static_cast<int>(status.ExitStatus);
+      break;
+    case cmUVProcessChain::ExceptionCode::Spawn:
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "\tError executing ctest: " << result.second << std::endl);
+      failed = true;
+      break;
+    default:
+      retVal = status.TermSignal;
+      cmCTestLog(this->CTest, ERROR_MESSAGE,
+                 "\tThere was an exception: " << result.second << " " << retVal
+                                              << std::endl);
+      failed = true;
   }
-  cmsysProcess_Delete(cp);
   if (failed) {
     std::ostringstream message;
     message << "Error running command: [";
-    message << result << "] ";
+    message << static_cast<int>(result.first) << "] ";
     for (const char* arg : argv) {
       if (arg) {
         message << arg << " ";

+ 64 - 20
Source/cmSystemTools.cxx

@@ -31,6 +31,8 @@
 #include "cmProcessOutput.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVStream.h"
 #include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
@@ -59,12 +61,14 @@
 #include <cassert>
 #include <cctype>
 #include <cerrno>
+#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
 #include <ctime>
 #include <functional>
 #include <iostream>
+#include <memory>
 #include <sstream>
 #include <utility>
 #include <vector>
@@ -2213,9 +2217,10 @@ bool cmSystemTools::ListTar(const std::string& outFileName,
 #endif
 }
 
-int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
-                               cmDuration timeout, std::vector<char>& out,
-                               std::vector<char>& err)
+cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
+  uv_loop_t* loop, uv_stream_t* outPipe, uv_stream_t* errPipe,
+  std::string& line, cmDuration timeout, std::vector<char>& out,
+  std::vector<char>& err)
 {
   line.clear();
   auto outiter = out.begin();
@@ -2237,7 +2242,7 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
           line.append(out.data(), length);
         }
         out.erase(out.begin(), outiter + 1);
-        return cmsysProcess_Pipe_STDOUT;
+        return WaitForLineResult::STDOUT;
       }
     }
 
@@ -2255,33 +2260,66 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
           line.append(err.data(), length);
         }
         err.erase(err.begin(), erriter + 1);
-        return cmsysProcess_Pipe_STDERR;
+        return WaitForLineResult::STDERR;
       }
     }
 
     // No newlines found.  Wait for more data from the process.
-    int length;
-    char* data;
-    double timeoutAsDbl = timeout.count();
-    int pipe =
-      cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
-    if (pipe == cmsysProcess_Pipe_Timeout) {
+    struct ReadData
+    {
+      uv_stream_t* Stream;
+      std::vector<char> Buffer;
+      bool Read = false;
+      bool Finished = false;
+    };
+    auto startRead =
+      [](uv_stream_t* stream,
+         ReadData& data) -> std::unique_ptr<cmUVStreamReadHandle> {
+      data.Stream = stream;
+      return cmUVStreamRead(
+        stream,
+        [&data](std::vector<char> buf) {
+          data.Buffer = std::move(buf);
+          data.Read = true;
+          uv_read_stop(data.Stream);
+        },
+        [&data]() { data.Finished = true; });
+    };
+    ReadData outData;
+    auto outHandle = startRead(outPipe, outData);
+    ReadData errData;
+    auto errHandle = startRead(errPipe, errData);
+
+    cm::uv_timer_ptr timer;
+    bool timedOut = false;
+    timer.init(*loop, &timedOut);
+    timer.start(
+      [](uv_timer_t* handle) {
+        auto* timedOutPtr = static_cast<bool*>(handle->data);
+        *timedOutPtr = true;
+      },
+      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+
+    uv_run(loop, UV_RUN_ONCE);
+    if (timedOut) {
       // Timeout has been exceeded.
-      return pipe;
+      return WaitForLineResult::Timeout;
     }
-    if (pipe == cmsysProcess_Pipe_STDOUT) {
-      processOutput.DecodeText(data, length, strdata, 1);
+    if (outData.Read) {
+      processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
+                               strdata, 1);
       // Append to the stdout buffer.
       std::vector<char>::size_type size = out.size();
       cm::append(out, strdata);
       outiter = out.begin() + size;
-    } else if (pipe == cmsysProcess_Pipe_STDERR) {
-      processOutput.DecodeText(data, length, strdata, 2);
+    } else if (errData.Read) {
+      processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
+                               strdata, 2);
       // Append to the stderr buffer.
       std::vector<char>::size_type size = err.size();
       cm::append(err, strdata);
       erriter = err.begin() + size;
-    } else if (pipe == cmsysProcess_Pipe_None) {
+    } else if (outData.Finished && errData.Finished) {
       // Both stdout and stderr pipes have broken.  Return leftover data.
       processOutput.DecodeText(std::string(), strdata, 1);
       if (!strdata.empty()) {
@@ -2298,14 +2336,20 @@ int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
       if (!out.empty()) {
         line.append(out.data(), outiter - out.begin());
         out.erase(out.begin(), out.end());
-        return cmsysProcess_Pipe_STDOUT;
+        return WaitForLineResult::STDOUT;
       }
       if (!err.empty()) {
         line.append(err.data(), erriter - err.begin());
         err.erase(err.begin(), err.end());
-        return cmsysProcess_Pipe_STDERR;
+        return WaitForLineResult::STDERR;
       }
-      return cmsysProcess_Pipe_None;
+      return WaitForLineResult::None;
+    }
+    if (!outData.Finished) {
+      uv_read_stop(outPipe);
+    }
+    if (!errData.Finished) {
+      uv_read_stop(errPipe);
     }
   }
 }

+ 16 - 4
Source/cmSystemTools.h

@@ -18,6 +18,8 @@
 #include <cm/optional>
 #include <cm/string_view>
 
+#include <cm3p/uv.h>
+
 #include "cmsys/Process.h"
 #include "cmsys/Status.hxx"      // IWYU pragma: export
 #include "cmsys/SystemTools.hxx" // IWYU pragma: export
@@ -339,10 +341,20 @@ public:
    */
   static void ReportLastSystemError(const char* m);
 
-  /** a general output handler for cmsysProcess  */
-  static int WaitForLine(cmsysProcess* process, std::string& line,
-                         cmDuration timeout, std::vector<char>& out,
-                         std::vector<char>& err);
+  enum class WaitForLineResult
+  {
+    None,
+    STDOUT,
+    STDERR,
+    Timeout,
+  };
+
+  /** a general output handler for libuv  */
+  static WaitForLineResult WaitForLine(uv_loop_t* loop, uv_stream_t* outPipe,
+                                       uv_stream_t* errPipe, std::string& line,
+                                       cmDuration timeout,
+                                       std::vector<char>& out,
+                                       std::vector<char>& err);
 
   static void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; }
   static bool GetForceUnixPaths() { return s_ForceUnixPaths; }

+ 0 - 4
Tests/CMakeLib/testUVProcessChainHelper.cxx

@@ -7,10 +7,6 @@
 #include <string>
 #include <thread>
 
-#ifdef _WIN32
-#  include <windows.h>
-#endif
-
 #include "cmSystemTools.h"
 
 static std::string getStdin()