Browse Source

Merge topic 'revert-replace-cmsysprocess-with-cmuvprocesschain' into release-3.28

bcbb212df7 Revert use of libuv for process execution for 3.28

Acked-by: Kitware Robot <[email protected]>
Merge-request: !9176
Brad King 1 year ago
parent
commit
151601c2e3

+ 0 - 2
Source/CPack/cmCPackSTGZGenerator.cxx

@@ -7,8 +7,6 @@
 #include <string>
 #include <vector>
 
-#include <fcntl.h>
-
 #include "cmsys/FStream.hxx"
 
 #include "cm_sys_stat.h"

+ 18 - 13
Source/CTest/cmCTestBZR.cxx

@@ -135,14 +135,14 @@ private:
 std::string cmCTestBZR::LoadInfo()
 {
   // Run "bzr info" to get the repository info from the work tree.
-  std::string bzr = this->CommandLineTool;
-  std::vector<std::string> bzr_info = { bzr, "info" };
+  const char* bzr = this->CommandLineTool.c_str();
+  const char* bzr_info[] = { bzr, "info", nullptr };
   InfoParser iout(this, "info-out> ");
   OutputLogger ierr(this->Log, "info-err> ");
   this->RunChild(bzr_info, &iout, &ierr);
 
   // Run "bzr revno" to get the repository revision number from the work tree.
-  std::vector<std::string> bzr_revno = { bzr, "revno" };
+  const char* bzr_revno[] = { bzr, "revno", nullptr };
   std::string rev;
   RevnoParser rout(this, "revno-out> ", rev);
   OutputLogger rerr(this->Log, "revno-err> ");
@@ -372,18 +372,22 @@ bool cmCTestBZR::UpdateImpl()
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
   // Use "bzr pull" to update the working tree.
-  std::vector<std::string> bzr_update;
-  bzr_update.push_back(this->CommandLineTool);
+  std::vector<char const*> bzr_update;
+  bzr_update.push_back(this->CommandLineTool.c_str());
   bzr_update.push_back("pull");
 
-  cm::append(bzr_update, args);
+  for (std::string const& arg : args) {
+    bzr_update.push_back(arg.c_str());
+  }
+
+  bzr_update.push_back(this->URL.c_str());
 
-  bzr_update.push_back(this->URL);
+  bzr_update.push_back(nullptr);
 
   // For some reason bzr uses stderr to display the update status.
   OutputLogger out(this->Log, "pull-out> ");
   UpdateParser err(this, "pull-err> ");
-  return this->RunUpdateCommand(bzr_update, &out, &err);
+  return this->RunUpdateCommand(bzr_update.data(), &out, &err);
 }
 
 bool cmCTestBZR::LoadRevisions()
@@ -404,9 +408,10 @@ bool cmCTestBZR::LoadRevisions()
   }
 
   // Run "bzr log" to get all global revisions of interest.
-  std::string bzr = this->CommandLineTool;
-  std::vector<std::string> bzr_log = { bzr,  "log",   "-v",     "-r",
-                                       revs, "--xml", this->URL };
+  const char* bzr = this->CommandLineTool.c_str();
+  const char* bzr_log[] = {
+    bzr, "log", "-v", "-r", revs.c_str(), "--xml", this->URL.c_str(), nullptr
+  };
   {
     LogParser out(this, "log-out> ");
     OutputLogger err(this->Log, "log-err> ");
@@ -462,8 +467,8 @@ private:
 bool cmCTestBZR::LoadModifications()
 {
   // Run "bzr status" which reports local modifications.
-  std::string bzr = this->CommandLineTool;
-  std::vector<std::string> bzr_status = { bzr, "status", "-SV" };
+  const char* bzr = this->CommandLineTool.c_str();
+  const char* bzr_status[] = { bzr, "status", "-SV", nullptr };
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(bzr_status, &out, &err);

+ 9 - 6
Source/CTest/cmCTestBuildAndTestHandler.cxx

@@ -7,6 +7,8 @@
 #include <cstring>
 #include <ratio>
 
+#include "cmsys/Process.h"
+
 #include "cmBuildOptions.h"
 #include "cmCTest.h"
 #include "cmCTestTestHandler.h"
@@ -306,11 +308,12 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
     return 1;
   }
 
-  std::vector<std::string> testCommand;
-  testCommand.push_back(fullPath);
+  std::vector<const char*> testCommand;
+  testCommand.push_back(fullPath.c_str());
   for (std::string const& testCommandArg : this->TestCommandArgs) {
-    testCommand.push_back(testCommandArg);
+    testCommand.push_back(testCommandArg.c_str());
   }
+  testCommand.push_back(nullptr);
   std::string outs;
   int retval = 0;
   // run the test from the this->BuildRunDir if set
@@ -346,10 +349,10 @@ int cmCTestBuildAndTestHandler::RunCMakeAndTest(std::string* outstring)
     }
   }
 
-  bool runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
-                                         remainingTime, nullptr);
+  int runTestRes = this->CTest->RunTest(testCommand, &outs, &retval, nullptr,
+                                        remainingTime, nullptr);
 
-  if (!runTestRes || retval != 0) {
+  if (runTestRes != cmsysProcess_State_Exited || retval != 0) {
     out << "Test command failed: " << testCommand[0] << "\n";
     retval = 1;
   }

+ 134 - 157
Source/CTest/cmCTestBuildHandler.cxx

@@ -3,17 +3,15 @@
 #include "cmCTestBuildHandler.h"
 
 #include <cstdlib>
-#include <memory>
 #include <ratio>
 #include <set>
 #include <utility>
 
 #include <cmext/algorithm>
 
-#include <cm3p/uv.h>
-
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestLaunchReporter.h"
@@ -26,9 +24,6 @@
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
@@ -425,7 +420,7 @@ int cmCTestBuildHandler::ProcessHandler()
   cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
   this->ColorRemover = &colorRemover;
   int retVal = 0;
-  bool res = true;
+  int res = cmsysProcess_State_Exited;
   if (!this->CTest->GetShowOnly()) {
     res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
                                ofs);
@@ -480,7 +475,7 @@ int cmCTestBuildHandler::ProcessHandler()
   }
   this->GenerateXMLFooter(xml, elapsed_build_time);
 
-  if (!res || retVal || this->TotalErrors > 0) {
+  if (res != cmsysProcess_State_Exited || retVal || this->TotalErrors > 0) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Error(s) when building project" << std::endl);
   }
@@ -769,10 +764,10 @@ void cmCTestBuildHandler::LaunchHelper::WriteScrapeMatchers(
   }
 }
 
-bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
-                                         int* retVal, const char* dir,
-                                         int timeout, std::ostream& ofs,
-                                         Encoding encoding)
+int cmCTestBuildHandler::RunMakeCommand(const std::string& command,
+                                        int* retVal, const char* dir,
+                                        int timeout, std::ostream& ofs,
+                                        Encoding encoding)
 {
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -781,9 +776,19 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
     return false;
   }
 
+  std::vector<const char*> argv;
+  argv.reserve(args.size() + 1);
+  for (std::string const& arg : args) {
+    argv.push_back(arg.c_str());
+  }
+  argv.push_back(nullptr);
+
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Run command:", this->Quiet);
-  for (auto const& arg : args) {
+  for (char const* arg : argv) {
+    if (!arg) {
+      break;
+    }
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        " \"" << arg << "\"", this->Quiet);
   }
@@ -795,20 +800,21 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
   static_cast<void>(launchHelper);
 
   // Now create process object
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(args)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
-  if (dir) {
-    builder.SetWorkingDirectory(dir);
-  }
-  auto chain = builder.Start();
+  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);
 
   // Initialize tick's
   std::string::size_type tick = 0;
-  static constexpr std::string::size_type tick_len = 1024;
+  const std::string::size_type tick_len = 1024;
 
+  char* data;
+  int length;
   cmProcessOutput processOutput(encoding);
+  std::string strdata;
   cmCTestOptionalLog(
     this->CTest, HANDLER_PROGRESS_OUTPUT,
     "   Each symbol represents "
@@ -830,65 +836,39 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
   this->WarningQuotaReached = false;
   this->ErrorQuotaReached = false;
 
-  cm::uv_timer_ptr timer;
-  bool timedOut = false;
-  timer.init(chain.GetLoop(), &timedOut);
-  if (timeout > 0) {
-    timer.start(
-      [](uv_timer_t* t) {
-        auto* timedOutPtr = static_cast<bool*>(t->data);
-        *timedOutPtr = true;
-      },
-      timeout * 1000, 0);
-  }
-
   // For every chunk of data
-  cm::uv_pipe_ptr outputStream;
-  bool outFinished = false;
-  cm::uv_pipe_ptr errorStream;
-  bool errFinished = false;
-  auto startRead = [this, &chain, &processOutput, &tick,
-                    &ofs](cm::uv_pipe_ptr& pipe, int stream,
-                          t_BuildProcessingQueueType& queue, bool& finished,
-                          int id) -> std::unique_ptr<cmUVStreamReadHandle> {
-    pipe.init(chain.GetLoop(), 0);
-    uv_pipe_open(pipe, stream);
-    return cmUVStreamRead(
-      pipe,
-      [this, &processOutput, &queue, id, &tick, &ofs](std::vector<char> data) {
-        // Replace '\0' with '\n', since '\0' does not really make sense. This
-        // is for Visual Studio output
-        for (auto& c : data) {
-          if (c == 0) {
-            c = '\n';
-          }
-        }
+  int res;
+  while ((res = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
+    // Replace '\0' with '\n', since '\0' does not really make sense. This is
+    // for Visual Studio output
+    for (int cc = 0; cc < length; ++cc) {
+      if (data[cc] == 0) {
+        data[cc] = '\n';
+      }
+    }
 
-        // Process the chunk of data
-        std::string strdata;
-        processOutput.DecodeText(data.data(), data.size(), strdata, id);
-        this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
-                            ofs, &queue);
-      },
-      [this, &processOutput, &queue, id, &tick, &ofs, &finished]() {
-        std::string strdata;
-        processOutput.DecodeText(std::string(), strdata, id);
-        if (!strdata.empty()) {
-          this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len,
-                              ofs, &queue);
-        }
-        finished = true;
-      });
-  };
-  auto outputHandle = startRead(outputStream, chain.OutputStream(),
-                                this->BuildProcessingQueue, outFinished, 1);
-  auto errorHandle =
-    startRead(errorStream, chain.ErrorStream(),
-              this->BuildProcessingErrorQueue, errFinished, 2);
-
-  while (!timedOut && !(outFinished && errFinished && chain.Finished())) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+    // Process the chunk of data
+    if (res == cmsysProcess_Pipe_STDERR) {
+      processOutput.DecodeText(data, length, strdata, 1);
+      this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
+                          &this->BuildProcessingErrorQueue);
+    } else {
+      processOutput.DecodeText(data, length, strdata, 2);
+      this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
+                          &this->BuildProcessingQueue);
+    }
+  }
+  processOutput.DecodeText(std::string(), strdata, 1);
+  if (!strdata.empty()) {
+    this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
+                        &this->BuildProcessingErrorQueue);
   }
+  processOutput.DecodeText(std::string(), strdata, 2);
+  if (!strdata.empty()) {
+    this->ProcessBuffer(strdata.c_str(), strdata.size(), tick, tick_len, ofs,
+                        &this->BuildProcessingQueue);
+  }
+
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
                       &this->BuildProcessingQueue);
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
@@ -899,93 +879,90 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
                        << std::endl,
                      this->Quiet);
 
-  if (chain.Finished()) {
-    auto const& status = chain.GetStatus(0);
-    auto exception = status.GetException();
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        if (retVal) {
-          *retVal = static_cast<int>(status.ExitStatus);
-          cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
-                             "Command exited with the value: " << *retVal
-                                                               << std::endl,
-                             this->Quiet);
-          // if a non zero return value
-          if (*retVal) {
-            // If there was an error running command, report that on the
-            // dashboard.
-            if (this->UseCTestLaunch) {
-              // For launchers, do not record this top-level error if other
-              // more granular build errors have already been captured.
-              bool launcherXMLFound = false;
-              cmsys::Directory launchDir;
-              launchDir.Load(this->CTestLaunchDir);
-              unsigned long n = launchDir.GetNumberOfFiles();
-              for (unsigned long i = 0; i < n; ++i) {
-                const char* fname = launchDir.GetFile(i);
-                if (cmHasLiteralSuffix(fname, ".xml")) {
-                  launcherXMLFound = true;
-                  break;
-                }
-              }
-              if (!launcherXMLFound) {
-                cmCTestLaunchReporter reporter;
-                reporter.RealArgs = args;
-                reporter.ComputeFileNames();
-                reporter.ExitCode = *retVal;
-                reporter.Status = status;
-                // Use temporary BuildLog file to populate this error for
-                // CDash.
-                ofs.flush();
-                reporter.LogOut = this->LogFileNames["Build"];
-                reporter.LogOut += ".tmp";
-                reporter.WriteXML();
-              }
-            } else {
-              cmCTestBuildErrorWarning errorwarning;
-              errorwarning.LineNumber = 0;
-              errorwarning.LogLine = 1;
-              errorwarning.Text = cmStrCat(
-                "*** WARNING non-zero return value in ctest from: ", args[0]);
-              errorwarning.PreContext.clear();
-              errorwarning.PostContext.clear();
-              errorwarning.Error = false;
-              this->ErrorsAndWarnings.push_back(std::move(errorwarning));
-              this->TotalWarnings++;
+  // Properly handle output of the build command
+  cmsysProcess_WaitForExit(cp, nullptr);
+  int result = cmsysProcess_GetState(cp);
+
+  if (result == cmsysProcess_State_Exited) {
+    if (retVal) {
+      *retVal = cmsysProcess_GetExitValue(cp);
+      cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
+                         "Command exited with the value: " << *retVal
+                                                           << std::endl,
+                         this->Quiet);
+      // if a non zero return value
+      if (*retVal) {
+        // If there was an error running command, report that on the
+        // dashboard.
+        if (this->UseCTestLaunch) {
+          // For launchers, do not record this top-level error if other
+          // more granular build errors have already been captured.
+          bool launcherXMLFound = false;
+          cmsys::Directory launchDir;
+          launchDir.Load(this->CTestLaunchDir);
+          unsigned long n = launchDir.GetNumberOfFiles();
+          for (unsigned long i = 0; i < n; ++i) {
+            const char* fname = launchDir.GetFile(i);
+            if (cmHasLiteralSuffix(fname, ".xml")) {
+              launcherXMLFound = true;
+              break;
             }
           }
+          if (!launcherXMLFound) {
+            cmCTestLaunchReporter reporter;
+            reporter.RealArgs = args;
+            reporter.ComputeFileNames();
+            reporter.ExitCode = *retVal;
+            reporter.Process = cp;
+            // Use temporary BuildLog file to populate this error for CDash.
+            ofs.flush();
+            reporter.LogOut = this->LogFileNames["Build"];
+            reporter.LogOut += ".tmp";
+            reporter.WriteXML();
+          }
+        } else {
+          cmCTestBuildErrorWarning errorwarning;
+          errorwarning.LineNumber = 0;
+          errorwarning.LogLine = 1;
+          errorwarning.Text = cmStrCat(
+            "*** WARNING non-zero return value in ctest from: ", argv[0]);
+          errorwarning.PreContext.clear();
+          errorwarning.PostContext.clear();
+          errorwarning.Error = false;
+          this->ErrorsAndWarnings.push_back(std::move(errorwarning));
+          this->TotalWarnings++;
         }
-        break;
-      case cmUVProcessChain::ExceptionCode::Spawn: {
-        // If there was an error running command, report that on the dashboard.
-        cmCTestBuildErrorWarning errorwarning;
-        errorwarning.LineNumber = 0;
-        errorwarning.LogLine = 1;
-        errorwarning.Text =
-          cmStrCat("*** ERROR executing: ", exception.second);
-        errorwarning.PreContext.clear();
-        errorwarning.PostContext.clear();
-        errorwarning.Error = true;
-        this->ErrorsAndWarnings.push_back(std::move(errorwarning));
-        this->TotalErrors++;
-        cmCTestLog(this->CTest, ERROR_MESSAGE,
-                   "There was an error: " << exception.second << std::endl);
-      } break;
-      default:
-        if (retVal) {
-          *retVal = status.TermSignal;
-          cmCTestOptionalLog(
-            this->CTest, WARNING,
-            "There was an exception: " << *retVal << std::endl, this->Quiet);
-        }
-        break;
+      }
     }
-  } else {
+  } else if (result == cmsysProcess_State_Exception) {
+    if (retVal) {
+      *retVal = cmsysProcess_GetExitException(cp);
+      cmCTestOptionalLog(this->CTest, WARNING,
+                         "There was an exception: " << *retVal << std::endl,
+                         this->Quiet);
+    }
+  } else if (result == cmsysProcess_State_Expired) {
     cmCTestOptionalLog(this->CTest, WARNING,
                        "There was a timeout" << std::endl, this->Quiet);
+  } else if (result == cmsysProcess_State_Error) {
+    // If there was an error running command, report that on the dashboard.
+    cmCTestBuildErrorWarning errorwarning;
+    errorwarning.LineNumber = 0;
+    errorwarning.LogLine = 1;
+    errorwarning.Text =
+      cmStrCat("*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
+    errorwarning.PreContext.clear();
+    errorwarning.PostContext.clear();
+    errorwarning.Error = true;
+    this->ErrorsAndWarnings.push_back(std::move(errorwarning));
+    this->TotalErrors++;
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "There was an error: " << cmsysProcess_GetErrorString(cp)
+                                      << std::endl);
   }
 
-  return true;
+  cmsysProcess_Delete(cp);
+  return result;
 }
 
 // ######################################################################

+ 3 - 3
Source/CTest/cmCTestBuildHandler.h

@@ -53,9 +53,9 @@ private:
 
   //! Run command specialized for make and configure. Returns process status
   // and retVal is return value or exception.
-  bool RunMakeCommand(const std::string& command, int* retVal, const char* dir,
-                      int timeout, std::ostream& ofs,
-                      Encoding encoding = cmProcessOutput::Auto);
+  int RunMakeCommand(const std::string& command, int* retVal, const char* dir,
+                     int timeout, std::ostream& ofs,
+                     Encoding encoding = cmProcessOutput::Auto);
 
   enum
   {

+ 11 - 7
Source/CTest/cmCTestCVS.cxx

@@ -5,7 +5,6 @@
 #include <utility>
 
 #include <cm/string_view>
-#include <cmext/algorithm>
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
@@ -90,15 +89,18 @@ bool cmCTestCVS::UpdateImpl()
   }
 
   // Run "cvs update" to update the work tree.
-  std::vector<std::string> cvs_update;
-  cvs_update.push_back(this->CommandLineTool);
+  std::vector<char const*> cvs_update;
+  cvs_update.push_back(this->CommandLineTool.c_str());
   cvs_update.push_back("-z3");
   cvs_update.push_back("update");
-  cm::append(cvs_update, args);
+  for (std::string const& arg : args) {
+    cvs_update.push_back(arg.c_str());
+  }
+  cvs_update.push_back(nullptr);
 
   UpdateParser out(this, "up-out> ");
   UpdateParser err(this, "up-err> ");
-  return this->RunUpdateCommand(cvs_update, &out, &err);
+  return this->RunUpdateCommand(cvs_update.data(), &out, &err);
 }
 
 class cmCTestCVS::LogParser : public cmCTestVC::LineParser
@@ -219,8 +221,10 @@ void cmCTestCVS::LoadRevisions(std::string const& file, const char* branchFlag,
   cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
 
   // Run "cvs log" to get revisions of this file on this branch.
-  std::string cvs = this->CommandLineTool;
-  std::vector<std::string> cvs_log = { cvs, "log", "-N", branchFlag, file };
+  const char* cvs = this->CommandLineTool.c_str();
+  const char* cvs_log[] = {
+    cvs, "log", "-N", branchFlag, file.c_str(), nullptr
+  };
 
   LogParser out(this, "log-out> ", revisions);
   OutputLogger err(this->Log, "log-err> ");

+ 1 - 1
Source/CTest/cmCTestConfigureHandler.cxx

@@ -45,7 +45,7 @@ int cmCTestConfigureHandler::ProcessHandler()
   auto elapsed_time_start = std::chrono::steady_clock::now();
   std::string output;
   int retVal = 0;
-  bool res = false;
+  int res = 0;
   if (!this->CTest->GetShowOnly()) {
     cmGeneratedFileStream os;
     if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {

+ 93 - 16
Source/CTest/cmCTestCoverageHandler.cxx

@@ -9,7 +9,6 @@
 #include <cstring>
 #include <iomanip>
 #include <iterator>
-#include <memory>
 #include <ratio>
 #include <sstream>
 #include <type_traits>
@@ -19,6 +18,7 @@
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCTest.h"
@@ -33,7 +33,6 @@
 #include "cmParsePHPCoverage.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVProcessChain.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
 
@@ -41,6 +40,85 @@ class cmMakefile;
 
 #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
 
+class cmCTestRunProcess
+{
+public:
+  cmCTestRunProcess()
+  {
+    this->Process = cmsysProcess_New();
+    this->PipeState = -1;
+    this->TimeOut = cmDuration(-1);
+  }
+  ~cmCTestRunProcess()
+  {
+    if (this->PipeState != -1 && this->PipeState != cmsysProcess_Pipe_None &&
+        this->PipeState != cmsysProcess_Pipe_Timeout) {
+      this->WaitForExit();
+    }
+    cmsysProcess_Delete(this->Process);
+  }
+  cmCTestRunProcess(const cmCTestRunProcess&) = delete;
+  cmCTestRunProcess& operator=(const cmCTestRunProcess&) = delete;
+  void SetCommand(const char* command)
+  {
+    this->CommandLineStrings.clear();
+    this->CommandLineStrings.emplace_back(command);
+  }
+  void AddArgument(const char* arg)
+  {
+    if (arg) {
+      this->CommandLineStrings.emplace_back(arg);
+    }
+  }
+  void SetWorkingDirectory(const char* dir) { this->WorkingDirectory = dir; }
+  void SetTimeout(cmDuration t) { this->TimeOut = t; }
+  bool StartProcess()
+  {
+    std::vector<const char*> args;
+    args.reserve(this->CommandLineStrings.size());
+    for (std::string const& cl : this->CommandLineStrings) {
+      args.push_back(cl.c_str());
+    }
+    args.push_back(nullptr); // null terminate
+    cmsysProcess_SetCommand(this->Process, args.data());
+    if (!this->WorkingDirectory.empty()) {
+      cmsysProcess_SetWorkingDirectory(this->Process,
+                                       this->WorkingDirectory.c_str());
+    }
+
+    cmsysProcess_SetOption(this->Process, cmsysProcess_Option_HideWindow, 1);
+    if (this->TimeOut >= cmDuration::zero()) {
+      cmsysProcess_SetTimeout(this->Process, this->TimeOut.count());
+    }
+    cmsysProcess_Execute(this->Process);
+    this->PipeState = cmsysProcess_GetState(this->Process);
+    // if the process is running or exited return true
+    return this->PipeState == cmsysProcess_State_Executing ||
+      this->PipeState == cmsysProcess_State_Exited;
+  }
+  void SetStdoutFile(const char* fname)
+  {
+    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDOUT, fname);
+  }
+  void SetStderrFile(const char* fname)
+  {
+    cmsysProcess_SetPipeFile(this->Process, cmsysProcess_Pipe_STDERR, fname);
+  }
+  int WaitForExit(double* timeout = nullptr)
+  {
+    this->PipeState = cmsysProcess_WaitForExit(this->Process, timeout);
+    return this->PipeState;
+  }
+  int GetProcessState() const { return this->PipeState; }
+
+private:
+  int PipeState;
+  cmsysProcess* Process;
+  std::vector<std::string> CommandLineStrings;
+  std::string WorkingDirectory;
+  cmDuration TimeOut;
+};
+
 cmCTestCoverageHandler::cmCTestCoverageHandler() = default;
 
 void cmCTestCoverageHandler::Initialize()
@@ -1862,35 +1940,34 @@ int cmCTestCoverageHandler::RunBullseyeCommand(
     cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
     return 0;
   }
-  std::vector<std::string> args{ cmd };
   if (arg) {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << " " << arg << "\n", this->Quiet);
-    args.emplace_back(arg);
   } else {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << "\n", this->Quiet);
   }
   // create a process object and start it
-  cmUVProcessChainBuilder builder;
+  cmCTestRunProcess runCoverageSrc;
+  runCoverageSrc.SetCommand(program.c_str());
+  runCoverageSrc.AddArgument(arg);
   std::string stdoutFile =
     cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
              this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
   std::string stderrFile = stdoutFile;
   stdoutFile += ".stdout";
   stderrFile += ".stderr";
-  std::unique_ptr<FILE, int (*)(FILE*)> stdoutHandle(
-    cmsys::SystemTools::Fopen(stdoutFile, "w"), fclose);
-  std::unique_ptr<FILE, int (*)(FILE*)> stderrHandle(
-    cmsys::SystemTools::Fopen(stderrFile, "w"), fclose);
-  builder.AddCommand(args)
-    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
-                       stdoutHandle.get())
-    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
-                       stderrHandle.get());
+  runCoverageSrc.SetStdoutFile(stdoutFile.c_str());
+  runCoverageSrc.SetStderrFile(stderrFile.c_str());
+  if (!runCoverageSrc.StartProcess()) {
+    cmCTestLog(this->CTest, ERROR_MESSAGE,
+               "Could not run : " << program << " " << arg << "\n"
+                                  << "kwsys process state : "
+                                  << runCoverageSrc.GetProcessState());
+    return 0;
+  }
   // since we set the output file names wait for it to end
-  auto chain = builder.Start();
-  chain.Wait();
+  runCoverageSrc.WaitForExit();
   outputFile = stdoutFile;
   return 1;
 }

+ 66 - 64
Source/CTest/cmCTestGIT.cxx

@@ -9,9 +9,8 @@
 #include <utility>
 #include <vector>
 
-#include <cmext/algorithm>
-
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestVC.h"
@@ -19,7 +18,6 @@
 #include "cmProcessOutput.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVProcessChain.h"
 #include "cmValue.h"
 
 static unsigned int cmCTestGITVersion(unsigned int epic, unsigned int major,
@@ -60,9 +58,9 @@ private:
 std::string cmCTestGIT::GetWorkingRevision()
 {
   // Run plumbing "git rev-list" to get work tree revision.
-  std::string git = this->CommandLineTool;
-  std::vector<std::string> git_rev_list = { git, "rev-list", "-n",
-                                            "1", "HEAD",     "--" };
+  const char* git = this->CommandLineTool.c_str();
+  const char* git_rev_list[] = { git,    "rev-list", "-n",   "1",
+                                 "HEAD", "--",       nullptr };
   std::string rev;
   OneLineParser out(this, "rl-out> ", rev);
   OutputLogger err(this->Log, "rl-err> ");
@@ -94,13 +92,13 @@ std::string cmCTestGIT::FindGitDir()
   std::string git_dir;
 
   // Run "git rev-parse --git-dir" to locate the real .git directory.
-  std::string git = this->CommandLineTool;
-  std::vector<std::string> git_rev_parse = { git, "rev-parse", "--git-dir" };
+  const char* git = this->CommandLineTool.c_str();
+  char const* git_rev_parse[] = { git, "rev-parse", "--git-dir", nullptr };
   std::string git_dir_line;
   OneLineParser rev_parse_out(this, "rev-parse-out> ", git_dir_line);
   OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
-  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err,
-                     std::string{}, cmProcessOutput::UTF8)) {
+  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
+                     cmProcessOutput::UTF8)) {
     git_dir = git_dir_line;
   }
   if (git_dir.empty()) {
@@ -119,10 +117,11 @@ std::string cmCTestGIT::FindGitDir()
     std::string cygpath_exe =
       cmStrCat(cmSystemTools::GetFilenamePath(git), "/cygpath.exe");
     if (cmSystemTools::FileExists(cygpath_exe)) {
-      std::vector<std::string> cygpath = { cygpath_exe, "-w", git_dir };
+      char const* cygpath[] = { cygpath_exe.c_str(), "-w", git_dir.c_str(),
+                                0 };
       OneLineParser cygpath_out(this, "cygpath-out> ", git_dir_line);
       OutputLogger cygpath_err(this->Log, "cygpath-err> ");
-      if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, std::string{},
+      if (this->RunChild(cygpath, &cygpath_out, &cygpath_err, nullptr,
                          cmProcessOutput::UTF8)) {
         git_dir = git_dir_line;
       }
@@ -137,12 +136,12 @@ std::string cmCTestGIT::FindTopDir()
   std::string top_dir = this->SourceDirectory;
 
   // Run "git rev-parse --show-cdup" to locate the top of the tree.
-  std::string git = this->CommandLineTool;
-  std::vector<std::string> git_rev_parse = { git, "rev-parse", "--show-cdup" };
+  const char* git = this->CommandLineTool.c_str();
+  char const* git_rev_parse[] = { git, "rev-parse", "--show-cdup", nullptr };
   std::string cdup;
   OneLineParser rev_parse_out(this, "rev-parse-out> ", cdup);
   OutputLogger rev_parse_err(this->Log, "rev-parse-err> ");
-  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, "",
+  if (this->RunChild(git_rev_parse, &rev_parse_out, &rev_parse_err, nullptr,
                      cmProcessOutput::UTF8) &&
       !cdup.empty()) {
     top_dir += "/";
@@ -154,10 +153,10 @@ std::string cmCTestGIT::FindTopDir()
 
 bool cmCTestGIT::UpdateByFetchAndReset()
 {
-  std::string git = this->CommandLineTool;
+  const char* git = this->CommandLineTool.c_str();
 
   // Use "git fetch" to get remote commits.
-  std::vector<std::string> git_fetch;
+  std::vector<char const*> git_fetch;
   git_fetch.push_back(git);
   git_fetch.push_back("fetch");
 
@@ -167,12 +166,17 @@ bool cmCTestGIT::UpdateByFetchAndReset()
     opts = this->CTest->GetCTestConfiguration("GITUpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  cm::append(git_fetch, args);
+  for (std::string const& arg : args) {
+    git_fetch.push_back(arg.c_str());
+  }
+
+  // Sentinel argument.
+  git_fetch.push_back(nullptr);
 
   // Fetch upstream refs.
   OutputLogger fetch_out(this->Log, "fetch-out> ");
   OutputLogger fetch_err(this->Log, "fetch-err> ");
-  if (!this->RunUpdateCommand(git_fetch, &fetch_out, &fetch_err)) {
+  if (!this->RunUpdateCommand(git_fetch.data(), &fetch_out, &fetch_err)) {
     return false;
   }
 
@@ -203,22 +207,25 @@ bool cmCTestGIT::UpdateByFetchAndReset()
   }
 
   // Reset the local branch to point at that tracked from upstream.
-  std::vector<std::string> git_reset = { git, "reset", "--hard", sha1 };
+  char const* git_reset[] = { git, "reset", "--hard", sha1.c_str(), nullptr };
   OutputLogger reset_out(this->Log, "reset-out> ");
   OutputLogger reset_err(this->Log, "reset-err> ");
-  return this->RunChild(git_reset, &reset_out, &reset_err);
+  return this->RunChild(&git_reset[0], &reset_out, &reset_err);
 }
 
 bool cmCTestGIT::UpdateByCustom(std::string const& custom)
 {
   cmList git_custom_command{ custom, cmList::EmptyElements::Yes };
-  std::vector<std::string> git_custom;
-  git_custom.reserve(git_custom_command.size());
-  cm::append(git_custom, git_custom_command);
+  std::vector<char const*> git_custom;
+  git_custom.reserve(git_custom_command.size() + 1);
+  for (std::string const& i : git_custom_command) {
+    git_custom.push_back(i.c_str());
+  }
+  git_custom.push_back(nullptr);
 
   OutputLogger custom_out(this->Log, "custom-out> ");
   OutputLogger custom_err(this->Log, "custom-err> ");
-  return this->RunUpdateCommand(git_custom, &custom_out, &custom_err);
+  return this->RunUpdateCommand(git_custom.data(), &custom_out, &custom_err);
 }
 
 bool cmCTestGIT::UpdateInternal()
@@ -237,14 +244,13 @@ bool cmCTestGIT::UpdateImpl()
   }
 
   std::string top_dir = this->FindTopDir();
-  std::string git = this->CommandLineTool;
-  std::string recursive = "--recursive";
-  std::string sync_recursive = "--recursive";
+  const char* git = this->CommandLineTool.c_str();
+  const char* recursive = "--recursive";
+  const char* sync_recursive = "--recursive";
 
   // Git < 1.6.5 did not support submodule --recursive
-  bool support_recursive = true;
   if (this->GetGitVersion() < cmCTestGITVersion(1, 6, 5, 0)) {
-    support_recursive = false;
+    recursive = nullptr;
     // No need to require >= 1.6.5 if there are no submodules.
     if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
       this->Log << "Git < 1.6.5 cannot update submodules recursively\n";
@@ -252,9 +258,8 @@ bool cmCTestGIT::UpdateImpl()
   }
 
   // Git < 1.8.1 did not support sync --recursive
-  bool support_sync_recursive = true;
   if (this->GetGitVersion() < cmCTestGITVersion(1, 8, 1, 0)) {
-    support_sync_recursive = false;
+    sync_recursive = nullptr;
     // No need to require >= 1.8.1 if there are no submodules.
     if (cmSystemTools::FileExists(top_dir + "/.gitmodules")) {
       this->Log << "Git < 1.8.1 cannot synchronize submodules recursively\n";
@@ -269,39 +274,35 @@ bool cmCTestGIT::UpdateImpl()
   std::string init_submodules =
     this->CTest->GetCTestConfiguration("GITInitSubmodules");
   if (cmIsOn(init_submodules)) {
-    std::vector<std::string> git_submodule_init = { git, "submodule", "init" };
+    char const* git_submodule_init[] = { git, "submodule", "init", nullptr };
     ret = this->RunChild(git_submodule_init, &submodule_out, &submodule_err,
-                         top_dir);
+                         top_dir.c_str());
 
     if (!ret) {
       return false;
     }
   }
 
-  std::vector<std::string> git_submodule_sync = { git, "submodule", "sync" };
-  if (support_sync_recursive) {
-    git_submodule_sync.push_back(sync_recursive);
-  }
+  char const* git_submodule_sync[] = { git, "submodule", "sync",
+                                       sync_recursive, nullptr };
   ret = this->RunChild(git_submodule_sync, &submodule_out, &submodule_err,
-                       top_dir);
+                       top_dir.c_str());
 
   if (!ret) {
     return false;
   }
 
-  std::vector<std::string> git_submodule = { git, "submodule", "update" };
-  if (support_recursive) {
-    git_submodule.push_back(recursive);
-  }
+  char const* git_submodule[] = { git, "submodule", "update", recursive,
+                                  nullptr };
   return this->RunChild(git_submodule, &submodule_out, &submodule_err,
-                        top_dir);
+                        top_dir.c_str());
 }
 
 unsigned int cmCTestGIT::GetGitVersion()
 {
   if (!this->CurrentGitVersion) {
-    std::string git = this->CommandLineTool;
-    std::vector<std::string> git_version = { git, "--version" };
+    const char* git = this->CommandLineTool.c_str();
+    char const* git_version[] = { git, "--version", nullptr };
     std::string version;
     OneLineParser version_out(this, "version-out> ", version);
     OutputLogger version_err(this->Log, "version-err> ");
@@ -604,49 +605,50 @@ bool cmCTestGIT::LoadRevisions()
 {
   // Use 'git rev-list ... | git diff-tree ...' to get revisions.
   std::string range = this->OldRevision + ".." + this->NewRevision;
-  std::string git = this->CommandLineTool;
-  std::vector<std::string> git_rev_list = { git, "rev-list", "--reverse",
-                                            range, "--" };
-  std::vector<std::string> git_diff_tree = {
-    git,  "diff-tree", "--stdin",      "--always",
-    "-z", "-r",        "--pretty=raw", "--encoding=utf-8"
+  const char* git = this->CommandLineTool.c_str();
+  const char* git_rev_list[] = { git,           "rev-list", "--reverse",
+                                 range.c_str(), "--",       nullptr };
+  const char* git_diff_tree[] = {
+    git,  "diff-tree",    "--stdin",          "--always", "-z",
+    "-r", "--pretty=raw", "--encoding=utf-8", nullptr
   };
   this->Log << cmCTestGIT::ComputeCommandLine(git_rev_list) << " | "
             << cmCTestGIT::ComputeCommandLine(git_diff_tree) << "\n";
 
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(git_rev_list)
-    .AddCommand(git_diff_tree)
-    .SetWorkingDirectory(this->SourceDirectory);
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_AddCommand(cp, git_rev_list);
+  cmsysProcess_AddCommand(cp, git_diff_tree);
+  cmsysProcess_SetWorkingDirectory(cp, this->SourceDirectory.c_str());
 
   CommitParser out(this, "dt-out> ");
   OutputLogger err(this->Log, "dt-err> ");
-  cmCTestGIT::RunProcess(builder, &out, &err, cmProcessOutput::UTF8);
+  cmCTestGIT::RunProcess(cp, &out, &err, cmProcessOutput::UTF8);
 
   // Send one extra zero-byte to terminate the last record.
   out.Process("", 1);
 
+  cmsysProcess_Delete(cp);
   return true;
 }
 
 bool cmCTestGIT::LoadModifications()
 {
-  std::string git = this->CommandLineTool;
+  const char* git = this->CommandLineTool.c_str();
 
   // Use 'git update-index' to refresh the index w.r.t. the work tree.
-  std::vector<std::string> git_update_index = { git, "update-index",
-                                                "--refresh" };
+  const char* git_update_index[] = { git, "update-index", "--refresh",
+                                     nullptr };
   OutputLogger ui_out(this->Log, "ui-out> ");
   OutputLogger ui_err(this->Log, "ui-err> ");
-  this->RunChild(git_update_index, &ui_out, &ui_err, "",
+  this->RunChild(git_update_index, &ui_out, &ui_err, nullptr,
                  cmProcessOutput::UTF8);
 
   // Use 'git diff-index' to get modified files.
-  std::vector<std::string> git_diff_index = { git, "diff-index", "-z", "HEAD",
-                                              "--" };
+  const char* git_diff_index[] = { git,    "diff-index", "-z",
+                                   "HEAD", "--",         nullptr };
   DiffParser out(this, "di-out> ");
   OutputLogger err(this->Log, "di-err> ");
-  this->RunChild(git_diff_index, &out, &err, "", cmProcessOutput::UTF8);
+  this->RunChild(git_diff_index, &out, &err, nullptr, cmProcessOutput::UTF8);
 
   for (Change const& c : out.Changes) {
     this->DoModification(PathModified, c.Path);

+ 21 - 14
Source/CTest/cmCTestHG.cxx

@@ -95,8 +95,8 @@ private:
 std::string cmCTestHG::GetWorkingRevision()
 {
   // Run plumbing "hg identify" to get work tree revision.
-  std::string hg = this->CommandLineTool;
-  std::vector<std::string> hg_identify = { hg, "identify", "-i" };
+  const char* hg = this->CommandLineTool.c_str();
+  const char* hg_identify[] = { hg, "identify", "-i", nullptr };
   std::string rev;
   IdentifyParser out(this, "rev-out> ", rev);
   OutputLogger err(this->Log, "rev-err> ");
@@ -127,16 +127,16 @@ bool cmCTestHG::UpdateImpl()
 {
   // Use "hg pull" followed by "hg update" to update the working tree.
   {
-    std::string hg = this->CommandLineTool;
-    std::vector<std::string> hg_pull = { hg, "pull", "-v" };
+    const char* hg = this->CommandLineTool.c_str();
+    const char* hg_pull[] = { hg, "pull", "-v", nullptr };
     OutputLogger out(this->Log, "pull-out> ");
     OutputLogger err(this->Log, "pull-err> ");
-    this->RunChild(hg_pull, &out, &err);
+    this->RunChild(&hg_pull[0], &out, &err);
   }
 
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
-  std::vector<std::string> hg_update;
+  std::vector<char const*> hg_update;
   hg_update.push_back(this->CommandLineTool.c_str());
   hg_update.push_back("update");
   hg_update.push_back("-v");
@@ -147,11 +147,16 @@ bool cmCTestHG::UpdateImpl()
     opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  cm::append(hg_update, args);
+  for (std::string const& arg : args) {
+    hg_update.push_back(arg.c_str());
+  }
+
+  // Sentinel argument.
+  hg_update.push_back(nullptr);
 
   OutputLogger out(this->Log, "update-out> ");
   OutputLogger err(this->Log, "update-err> ");
-  return this->RunUpdateCommand(hg_update, &out, &err);
+  return this->RunUpdateCommand(hg_update.data(), &out, &err);
 }
 
 class cmCTestHG::LogParser
@@ -272,8 +277,8 @@ bool cmCTestHG::LoadRevisions()
   // the project has spaces in the path.  Also, they may not have
   // proper XML escapes.
   std::string range = this->OldRevision + ":" + this->NewRevision;
-  std::string hg = this->CommandLineTool;
-  std::string hgXMLTemplate = "<logentry\n"
+  const char* hg = this->CommandLineTool.c_str();
+  const char* hgXMLTemplate = "<logentry\n"
                               "   revision=\"{node|short}\">\n"
                               "  <author>{author|person}</author>\n"
                               "  <email>{author|email}</email>\n"
@@ -283,8 +288,10 @@ bool cmCTestHG::LoadRevisions()
                               "  <file_adds>{file_adds}</file_adds>\n"
                               "  <file_dels>{file_dels}</file_dels>\n"
                               "</logentry>\n";
-  std::vector<std::string> hg_log = { hg,    "log",        "--removed",  "-r",
-                                      range, "--template", hgXMLTemplate };
+  const char* hg_log[] = {
+    hg,           "log",         "--removed", "-r", range.c_str(),
+    "--template", hgXMLTemplate, nullptr
+  };
 
   LogParser out(this, "log-out> ");
   out.Process("<?xml version=\"1.0\"?>\n"
@@ -298,8 +305,8 @@ bool cmCTestHG::LoadRevisions()
 bool cmCTestHG::LoadModifications()
 {
   // Use 'hg status' to get modified files.
-  std::string hg = this->CommandLineTool;
-  std::vector<std::string> hg_status = { hg, "status" };
+  const char* hg = this->CommandLineTool.c_str();
+  const char* hg_status[] = { hg, "status", nullptr };
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(hg_status, &out, &err);

+ 54 - 67
Source/CTest/cmCTestLaunch.cxx

@@ -2,15 +2,11 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunch.h"
 
-#include <cstdio>
 #include <cstring>
 #include <iostream>
-#include <memory>
-#include <utility>
-
-#include <cm3p/uv.h>
 
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCTestLaunchReporter.h"
@@ -21,9 +17,6 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmake.h"
 
 #ifdef _WIN32
@@ -35,6 +28,8 @@
 
 cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
 {
+  this->Process = nullptr;
+
   if (!this->ParseArguments(argc, argv)) {
     return;
   }
@@ -45,9 +40,13 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
   this->ScrapeRulesLoaded = false;
   this->HaveOut = false;
   this->HaveErr = false;
+  this->Process = cmsysProcess_New();
 }
 
-cmCTestLaunch::~cmCTestLaunch() = default;
+cmCTestLaunch::~cmCTestLaunch()
+{
+  cmsysProcess_Delete(this->Process);
+}
 
 bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
 {
@@ -114,12 +113,15 @@ bool cmCTestLaunch::ParseArguments(int argc, const char* const* argv)
 
   // Extract the real command line.
   if (arg0) {
-    for (int i = 0; i < argc - arg0; ++i) {
-      this->RealArgV.emplace_back((argv + arg0)[i]);
-      this->HandleRealArg((argv + arg0)[i]);
+    this->RealArgC = argc - arg0;
+    this->RealArgV = argv + arg0;
+    for (int i = 0; i < this->RealArgC; ++i) {
+      this->HandleRealArg(this->RealArgV[i]);
     }
     return true;
   }
+  this->RealArgC = 0;
+  this->RealArgV = nullptr;
   std::cerr << "No launch/command separator ('--') found!\n";
   return false;
 }
@@ -149,19 +151,17 @@ void cmCTestLaunch::RunChild()
   }
 
   // Prepare to run the real command.
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(this->RealArgV);
+  cmsysProcess* cp = this->Process;
+  cmsysProcess_SetCommand(cp, this->RealArgV);
 
   cmsys::ofstream fout;
   cmsys::ofstream ferr;
   if (this->Reporter.Passthru) {
     // In passthru mode we just share the output pipes.
-    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
-      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
+    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
+    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
   } else {
     // In full mode we record the child output pipes to log files.
-    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
     fout.open(this->Reporter.LogOut.c_str(), std::ios::out | std::ios::binary);
     ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
   }
@@ -174,65 +174,51 @@ void cmCTestLaunch::RunChild()
 #endif
 
   // Run the real command.
-  auto chain = builder.Start();
+  cmsysProcess_Execute(cp);
 
   // Record child stdout and stderr if necessary.
-  cm::uv_pipe_ptr outPipe;
-  cm::uv_pipe_ptr errPipe;
-  bool outFinished = true;
-  bool errFinished = true;
-  cmProcessOutput processOutput;
-  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
-  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
   if (!this->Reporter.Passthru) {
-    auto beginRead = [&chain, &processOutput](
-                       cm::uv_pipe_ptr& pipe, int stream, std::ostream& out,
-                       cmsys::ofstream& file, bool& haveData, bool& finished,
-                       int id) -> std::unique_ptr<cmUVStreamReadHandle> {
-      pipe.init(chain.GetLoop(), 0);
-      uv_pipe_open(pipe, stream);
-      finished = false;
-      return cmUVStreamRead(
-        pipe,
-        [&processOutput, &out, &file, id, &haveData](std::vector<char> data) {
-          std::string strdata;
-          processOutput.DecodeText(data.data(), data.size(), strdata, id);
-          file.write(strdata.c_str(), strdata.size());
-          out.write(strdata.c_str(), strdata.size());
-          haveData = true;
-        },
-        [&processOutput, &out, &file, &finished, id]() {
-          std::string strdata;
-          processOutput.DecodeText(std::string(), strdata, id);
-          if (!strdata.empty()) {
-            file.write(strdata.c_str(), strdata.size());
-            out.write(strdata.c_str(), strdata.size());
-          }
-          finished = true;
-        });
-    };
-    outputHandle = beginRead(outPipe, chain.OutputStream(), std::cout, fout,
-                             this->HaveOut, outFinished, 1);
-    errorHandle = beginRead(errPipe, chain.ErrorStream(), std::cerr, ferr,
-                            this->HaveErr, errFinished, 2);
+    char* data = nullptr;
+    int length = 0;
+    cmProcessOutput processOutput;
+    std::string strdata;
+    while (int p = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
+      if (p == cmsysProcess_Pipe_STDOUT) {
+        processOutput.DecodeText(data, length, strdata, 1);
+        fout.write(strdata.c_str(), strdata.size());
+        std::cout.write(strdata.c_str(), strdata.size());
+        this->HaveOut = true;
+      } else if (p == cmsysProcess_Pipe_STDERR) {
+        processOutput.DecodeText(data, length, strdata, 2);
+        ferr.write(strdata.c_str(), strdata.size());
+        std::cerr.write(strdata.c_str(), strdata.size());
+        this->HaveErr = true;
+      }
+    }
+    processOutput.DecodeText(std::string(), strdata, 1);
+    if (!strdata.empty()) {
+      fout.write(strdata.c_str(), strdata.size());
+      std::cout.write(strdata.c_str(), strdata.size());
+    }
+    processOutput.DecodeText(std::string(), strdata, 2);
+    if (!strdata.empty()) {
+      ferr.write(strdata.c_str(), strdata.size());
+      std::cerr.write(strdata.c_str(), strdata.size());
+    }
   }
 
   // Wait for the real command to finish.
-  while (!(chain.Finished() && outFinished && errFinished)) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
-  }
-  this->Reporter.Status = chain.GetStatus(0);
-  if (this->Reporter.Status.GetException().first ==
-      cmUVProcessChain::ExceptionCode::Spawn) {
-    this->Reporter.ExitCode = 1;
-  } else {
-    this->Reporter.ExitCode =
-      static_cast<int>(this->Reporter.Status.ExitStatus);
-  }
+  cmsysProcess_WaitForExit(cp, nullptr);
+  this->Reporter.ExitCode = cmsysProcess_GetExitValue(cp);
 }
 
 int cmCTestLaunch::Run()
 {
+  if (!this->Process) {
+    std::cerr << "Could not allocate cmsysProcess instance!\n";
+    return -1;
+  }
+
   this->RunChild();
 
   if (this->CheckResults()) {
@@ -240,6 +226,7 @@ int cmCTestLaunch::Run()
   }
 
   this->LoadConfig();
+  this->Reporter.Process = this->Process;
   this->Reporter.WriteXML();
 
   return this->Reporter.ExitCode;

+ 4 - 1
Source/CTest/cmCTestLaunch.h

@@ -43,12 +43,15 @@ private:
   bool ParseArguments(int argc, const char* const* argv);
 
   // The real command line appearing after launcher arguments.
-  std::vector<std::string> RealArgV;
+  int RealArgC;
+  const char* const* RealArgV;
 
   // The real command line after response file expansion.
   std::vector<std::string> RealArgs;
   void HandleRealArg(const char* arg);
 
+  struct cmsysProcess_s* Process;
+
   // Whether or not any data have been written to stdout or stderr.
   bool HaveOut;
   bool HaveErr;

+ 30 - 20
Source/CTest/cmCTestLaunchReporter.cxx

@@ -2,9 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunchReporter.h"
 
-#include <utility>
-
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 
 #include "cmCryptoHash.h"
@@ -23,7 +22,6 @@
 cmCTestLaunchReporter::cmCTestLaunchReporter()
 {
   this->Passthru = true;
-  this->Status.Finished = true;
   this->ExitCode = 1;
   this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
 
@@ -233,23 +231,35 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2)
 
   // ExitCondition
   cmXMLElement e4(e3, "ExitCondition");
-  if (this->Status.Finished) {
-    auto exception = this->Status.GetException();
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        e4.Content(this->ExitCode);
-        break;
-      case cmUVProcessChain::ExceptionCode::Spawn:
-        e4.Content("Error administrating child process: ");
-        e4.Content(exception.second);
-        break;
-      default:
-        e4.Content("Terminated abnormally: ");
-        e4.Content(exception.second);
-        break;
-    }
-  } else {
-    e4.Content("Killed when timeout expired");
+  cmsysProcess* cp = this->Process;
+  switch (cmsysProcess_GetState(cp)) {
+    case cmsysProcess_State_Starting:
+      e4.Content("No process has been executed");
+      break;
+    case cmsysProcess_State_Executing:
+      e4.Content("The process is still executing");
+      break;
+    case cmsysProcess_State_Disowned:
+      e4.Content("Disowned");
+      break;
+    case cmsysProcess_State_Killed:
+      e4.Content("Killed by parent");
+      break;
+
+    case cmsysProcess_State_Expired:
+      e4.Content("Killed when timeout expired");
+      break;
+    case cmsysProcess_State_Exited:
+      e4.Content(this->ExitCode);
+      break;
+    case cmsysProcess_State_Exception:
+      e4.Content("Terminated abnormally: ");
+      e4.Content(cmsysProcess_GetExceptionString(cp));
+      break;
+    case cmsysProcess_State_Error:
+      e4.Content("Error administrating child process: ");
+      e4.Content(cmsysProcess_GetErrorString(cp));
+      break;
   }
 }
 

+ 1 - 3
Source/CTest/cmCTestLaunchReporter.h

@@ -10,8 +10,6 @@
 
 #include "cmsys/RegularExpression.hxx"
 
-#include "cmUVProcessChain.h"
-
 class cmXMLElement;
 
 /** \class cmCTestLaunchReporter
@@ -50,7 +48,7 @@ public:
   void ComputeFileNames();
 
   bool Passthru;
-  cmUVProcessChain::Status Status;
+  struct cmsysProcess_s* Process;
   int ExitCode;
 
   // Temporary log files for stdout and stderr of real command.

+ 40 - 26
Source/CTest/cmCTestP4.cxx

@@ -149,16 +149,17 @@ cmCTestP4::User cmCTestP4::GetUserData(const std::string& username)
   auto it = this->Users.find(username);
 
   if (it == this->Users.end()) {
-    std::vector<std::string> p4_users;
+    std::vector<char const*> p4_users;
     this->SetP4Options(p4_users);
     p4_users.push_back("users");
     p4_users.push_back("-m");
     p4_users.push_back("1");
-    p4_users.push_back(username);
+    p4_users.push_back(username.c_str());
+    p4_users.push_back(nullptr);
 
     UserParser out(this, "users-out> ");
     OutputLogger err(this->Log, "users-err> ");
-    this->RunChild(p4_users, &out, &err);
+    this->RunChild(p4_users.data(), &out, &err);
 
     // The user should now be added to the map. Search again.
     it = this->Users.find(username);
@@ -302,10 +303,10 @@ private:
   }
 };
 
-void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions)
+void cmCTestP4::SetP4Options(std::vector<char const*>& CommandOptions)
 {
   if (this->P4Options.empty()) {
-    std::string p4 = this->CommandLineTool;
+    const char* p4 = this->CommandLineTool.c_str();
     this->P4Options.emplace_back(p4);
 
     // The CTEST_P4_CLIENT variable sets the P4 client used when issuing
@@ -327,12 +328,15 @@ void cmCTestP4::SetP4Options(std::vector<std::string>& CommandOptions)
     cm::append(this->P4Options, cmSystemTools::ParseArguments(opts));
   }
 
-  CommandOptions = this->P4Options;
+  CommandOptions.clear();
+  for (std::string const& o : this->P4Options) {
+    CommandOptions.push_back(o.c_str());
+  }
 }
 
 std::string cmCTestP4::GetWorkingRevision()
 {
-  std::vector<std::string> p4_identify;
+  std::vector<char const*> p4_identify;
   this->SetP4Options(p4_identify);
 
   p4_identify.push_back("changes");
@@ -341,13 +345,14 @@ std::string cmCTestP4::GetWorkingRevision()
   p4_identify.push_back("-t");
 
   std::string source = this->SourceDirectory + "/...#have";
-  p4_identify.push_back(source);
+  p4_identify.push_back(source.c_str());
+  p4_identify.push_back(nullptr);
 
   std::string rev;
   IdentifyParser out(this, "p4_changes-out> ", rev);
   OutputLogger err(this->Log, "p4_changes-err> ");
 
-  bool result = this->RunChild(p4_identify, &out, &err);
+  bool result = this->RunChild(p4_identify.data(), &out, &err);
 
   // If there was a problem contacting the server return "<unknown>"
   if (!result) {
@@ -383,7 +388,7 @@ bool cmCTestP4::NoteNewRevision()
 
 bool cmCTestP4::LoadRevisions()
 {
-  std::vector<std::string> p4_changes;
+  std::vector<char const*> p4_changes;
   this->SetP4Options(p4_changes);
 
   // Use 'p4 changes ...@old,new' to get a list of changelists
@@ -404,36 +409,38 @@ bool cmCTestP4::LoadRevisions()
     .append(this->NewRevision);
 
   p4_changes.push_back("changes");
-  p4_changes.push_back(range);
+  p4_changes.push_back(range.c_str());
+  p4_changes.push_back(nullptr);
 
   ChangesParser out(this, "p4_changes-out> ");
   OutputLogger err(this->Log, "p4_changes-err> ");
 
   this->ChangeLists.clear();
-  this->RunChild(p4_changes, &out, &err);
+  this->RunChild(p4_changes.data(), &out, &err);
 
   if (this->ChangeLists.empty()) {
     return true;
   }
 
   // p4 describe -s ...@1111111,2222222
-  std::vector<std::string> p4_describe;
+  std::vector<char const*> p4_describe;
   for (std::string const& i : cmReverseRange(this->ChangeLists)) {
     this->SetP4Options(p4_describe);
     p4_describe.push_back("describe");
     p4_describe.push_back("-s");
-    p4_describe.push_back(i);
+    p4_describe.push_back(i.c_str());
+    p4_describe.push_back(nullptr);
 
     DescribeParser outDescribe(this, "p4_describe-out> ");
     OutputLogger errDescribe(this->Log, "p4_describe-err> ");
-    this->RunChild(p4_describe, &outDescribe, &errDescribe);
+    this->RunChild(p4_describe.data(), &outDescribe, &errDescribe);
   }
   return true;
 }
 
 bool cmCTestP4::LoadModifications()
 {
-  std::vector<std::string> p4_diff;
+  std::vector<char const*> p4_diff;
   this->SetP4Options(p4_diff);
 
   p4_diff.push_back("diff");
@@ -441,11 +448,12 @@ bool cmCTestP4::LoadModifications()
   // Ideally we would use -Od but not all clients support it
   p4_diff.push_back("-dn");
   std::string source = this->SourceDirectory + "/...";
-  p4_diff.push_back(source);
+  p4_diff.push_back(source.c_str());
+  p4_diff.push_back(nullptr);
 
   DiffParser out(this, "p4_diff-out> ");
   OutputLogger err(this->Log, "p4_diff-err> ");
-  this->RunChild(p4_diff, &out, &err);
+  this->RunChild(p4_diff.data(), &out, &err);
   return true;
 }
 
@@ -453,14 +461,17 @@ bool cmCTestP4::UpdateCustom(const std::string& custom)
 {
   cmList p4_custom_command{ custom, cmList::EmptyElements::Yes };
 
-  std::vector<std::string> p4_custom;
-  p4_custom.reserve(p4_custom_command.size());
-  cm::append(p4_custom, p4_custom_command);
+  std::vector<char const*> p4_custom;
+  p4_custom.reserve(p4_custom_command.size() + 1);
+  for (std::string const& i : p4_custom_command) {
+    p4_custom.push_back(i.c_str());
+  }
+  p4_custom.push_back(nullptr);
 
   OutputLogger custom_out(this->Log, "p4_customsync-out> ");
   OutputLogger custom_err(this->Log, "p4_customsync-err> ");
 
-  return this->RunUpdateCommand(p4_custom, &custom_out, &custom_err);
+  return this->RunUpdateCommand(p4_custom.data(), &custom_out, &custom_err);
 }
 
 bool cmCTestP4::UpdateImpl()
@@ -477,7 +488,7 @@ bool cmCTestP4::UpdateImpl()
     return false;
   }
 
-  std::vector<std::string> p4_sync;
+  std::vector<char const*> p4_sync;
   this->SetP4Options(p4_sync);
 
   p4_sync.push_back("sync");
@@ -488,7 +499,9 @@ bool cmCTestP4::UpdateImpl()
     opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
-  cm::append(p4_sync, args);
+  for (std::string const& arg : args) {
+    p4_sync.push_back(arg.c_str());
+  }
 
   std::string source = this->SourceDirectory + "/...";
 
@@ -502,10 +515,11 @@ bool cmCTestP4::UpdateImpl()
     source.append("@\"").append(date).append("\"");
   }
 
-  p4_sync.push_back(source);
+  p4_sync.push_back(source.c_str());
+  p4_sync.push_back(nullptr);
 
   OutputLogger out(this->Log, "p4_sync-out> ");
   OutputLogger err(this->Log, "p4_sync-err> ");
 
-  return this->RunUpdateCommand(p4_sync, &out, &err);
+  return this->RunUpdateCommand(p4_sync.data(), &out, &err);
 }

+ 1 - 1
Source/CTest/cmCTestP4.h

@@ -39,7 +39,7 @@ private:
   std::vector<std::string> P4Options;
 
   User GetUserData(const std::string& username);
-  void SetP4Options(std::vector<std::string>& options);
+  void SetP4Options(std::vector<char const*>& options);
 
   std::string GetWorkingRevision();
   bool NoteOldRevision() override;

+ 21 - 15
Source/CTest/cmCTestSVN.cxx

@@ -33,7 +33,7 @@ cmCTestSVN::~cmCTestSVN() = default;
 
 void cmCTestSVN::CleanupImpl()
 {
-  std::vector<std::string> svn_cleanup;
+  std::vector<const char*> svn_cleanup;
   svn_cleanup.push_back("cleanup");
   OutputLogger out(this->Log, "cleanup-out> ");
   OutputLogger err(this->Log, "cleanup-err> ");
@@ -88,9 +88,9 @@ static bool cmCTestSVNPathStarts(std::string const& p1, std::string const& p2)
 std::string cmCTestSVN::LoadInfo(SVNInfo& svninfo)
 {
   // Run "svn info" to get the repository info from the work tree.
-  std::vector<std::string> svn_info;
+  std::vector<const char*> svn_info;
   svn_info.push_back("info");
-  svn_info.push_back(svninfo.LocalPath);
+  svn_info.push_back(svninfo.LocalPath.c_str());
   std::string rev;
   InfoParser out(this, "info-out> ", rev, svninfo);
   OutputLogger err(this->Log, "info-err> ");
@@ -251,24 +251,26 @@ bool cmCTestSVN::UpdateImpl()
     args.push_back("-r{" + this->GetNightlyTime() + " +0000}");
   }
 
-  std::vector<std::string> svn_update;
+  std::vector<char const*> svn_update;
   svn_update.push_back("update");
-  cm::append(svn_update, args);
+  for (std::string const& arg : args) {
+    svn_update.push_back(arg.c_str());
+  }
 
   UpdateParser out(this, "up-out> ");
   OutputLogger err(this->Log, "up-err> ");
   return this->RunSVNCommand(svn_update, &out, &err);
 }
 
-bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
+bool cmCTestSVN::RunSVNCommand(std::vector<char const*> const& parameters,
                                OutputParser* out, OutputParser* err)
 {
   if (parameters.empty()) {
     return false;
   }
 
-  std::vector<std::string> args;
-  args.push_back(this->CommandLineTool);
+  std::vector<char const*> args;
+  args.push_back(this->CommandLineTool.c_str());
   cm::append(args, parameters);
   args.push_back("--non-interactive");
 
@@ -276,12 +278,16 @@ bool cmCTestSVN::RunSVNCommand(std::vector<std::string> const& parameters,
 
   std::vector<std::string> parsedUserOptions =
     cmSystemTools::ParseArguments(userOptions);
-  cm::append(args, parsedUserOptions);
+  for (std::string const& opt : parsedUserOptions) {
+    args.push_back(opt.c_str());
+  }
+
+  args.push_back(nullptr);
 
-  if (parameters[0] == "update") {
-    return this->RunUpdateCommand(args, out, err);
+  if (strcmp(parameters[0], "update") == 0) {
+    return this->RunUpdateCommand(args.data(), out, err);
   }
-  return this->RunChild(args, out, err);
+  return this->RunChild(args.data(), out, err);
 }
 
 class cmCTestSVN::LogParser
@@ -387,7 +393,7 @@ bool cmCTestSVN::LoadRevisions(SVNInfo& svninfo)
   }
 
   // Run "svn log" to get all global revisions of interest.
-  std::vector<std::string> svn_log;
+  std::vector<const char*> svn_log;
   svn_log.push_back("log");
   svn_log.push_back("--xml");
   svn_log.push_back("-v");
@@ -466,7 +472,7 @@ private:
 bool cmCTestSVN::LoadModifications()
 {
   // Run "svn status" which reports local modifications.
-  std::vector<std::string> svn_status;
+  std::vector<const char*> svn_status;
   svn_status.push_back("status");
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
@@ -528,7 +534,7 @@ bool cmCTestSVN::LoadRepositories()
   this->RootInfo = &(this->Repositories.back());
 
   // Run "svn status" to get the list of external repositories
-  std::vector<std::string> svn_status;
+  std::vector<const char*> svn_status;
   svn_status.push_back("status");
   ExternalParser out(this, "external-out> ");
   OutputLogger err(this->Log, "external-err> ");

+ 1 - 1
Source/CTest/cmCTestSVN.h

@@ -33,7 +33,7 @@ private:
   bool NoteNewRevision() override;
   bool UpdateImpl() override;
 
-  bool RunSVNCommand(std::vector<std::string> const& parameters,
+  bool RunSVNCommand(std::vector<char const*> const& parameters,
                      OutputParser* out, OutputParser* err);
 
   // Information about an SVN repository (root repository or external)

+ 41 - 43
Source/CTest/cmCTestScriptHandler.cxx

@@ -11,9 +11,8 @@
 
 #include <cm/memory>
 
-#include <cm3p/uv.h>
-
 #include "cmsys/Directory.hxx"
+#include "cmsys/Process.h"
 
 #include "cmCTest.h"
 #include "cmCTestBuildCommand.h"
@@ -41,8 +40,6 @@
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmake.h"
 
@@ -151,65 +148,66 @@ 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
-  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());
+  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);
 
   std::vector<char> out;
   std::vector<char> err;
   std::string line;
-  auto pipe =
-    cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
-                               std::chrono::seconds(100), out, err);
-  while (pipe != cmSystemTools::WaitForLineResult::None) {
+  int pipe =
+    cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out, err);
+  while (pipe != cmsysProcess_Pipe_None) {
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                "Output: " << line << "\n");
-    if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
+    if (pipe == cmsysProcess_Pipe_STDERR) {
       cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
-    } else if (pipe == cmSystemTools::WaitForLineResult::STDOUT) {
+    } else if (pipe == cmsysProcess_Pipe_STDOUT) {
       cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, line << "\n");
     }
-    pipe =
-      cmSystemTools::WaitForLine(&process.GetLoop(), outPipe, errPipe, line,
-                                 std::chrono::seconds(100), out, err);
+    pipe = cmSystemTools::WaitForLine(cp, line, std::chrono::seconds(100), out,
+                                      err);
   }
 
   // Properly handle output of the build command
-  process.Wait();
-  auto const& status = process.GetStatus(0);
-  auto result = status.GetException();
+  cmsysProcess_WaitForExit(cp, nullptr);
+  int result = cmsysProcess_GetState(cp);
   int retVal = 0;
   bool failed = false;
-  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;
+  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;
   }
+  cmsysProcess_Delete(cp);
   if (failed) {
     std::ostringstream message;
     message << "Error running command: [";
-    message << static_cast<int>(result.first) << "] ";
+    message << result << "] ";
     for (const char* arg : argv) {
       if (arg) {
         message << arg << " ";

+ 26 - 19
Source/CTest/cmCTestVC.cxx

@@ -7,9 +7,10 @@
 #include <sstream>
 #include <vector>
 
+#include "cmsys/Process.h"
+
 #include "cmCTest.h"
 #include "cmSystemTools.h"
-#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 
@@ -54,12 +55,18 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
 
   // Construct the initial checkout command line.
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
+  std::vector<char const*> vc_co;
+  vc_co.reserve(args.size() + 1);
+  for (std::string const& arg : args) {
+    vc_co.push_back(arg.c_str());
+  }
+  vc_co.push_back(nullptr);
 
   // Run the initial checkout command and log its output.
   this->Log << "--- Begin Initial Checkout ---\n";
   OutputLogger out(this->Log, "co-out> ");
   OutputLogger err(this->Log, "co-err> ");
-  bool result = this->RunChild(args, &out, &err, parent);
+  bool result = this->RunChild(vc_co.data(), &out, &err, parent.c_str());
   this->Log << "--- End Initial Checkout ---\n";
   if (!result) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
@@ -68,35 +75,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
   return result;
 }
 
-bool cmCTestVC::RunChild(const std::vector<std::string>& cmd,
-                         OutputParser* out, OutputParser* err,
-                         std::string workDir, Encoding encoding)
+bool cmCTestVC::RunChild(char const* const* cmd, OutputParser* out,
+                         OutputParser* err, const char* workDir,
+                         Encoding encoding)
 {
   this->Log << cmCTestVC::ComputeCommandLine(cmd) << "\n";
 
-  cmUVProcessChainBuilder builder;
-  if (workDir.empty()) {
-    workDir = this->SourceDirectory;
-  }
-  builder.AddCommand(cmd).SetWorkingDirectory(workDir);
-  auto status = cmCTestVC::RunProcess(builder, out, err, encoding);
-  return status.front().SpawnResult == 0 && status.front().ExitStatus == 0;
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_SetCommand(cp, cmd);
+  workDir = workDir ? workDir : this->SourceDirectory.c_str();
+  cmsysProcess_SetWorkingDirectory(cp, workDir);
+  cmCTestVC::RunProcess(cp, out, err, encoding);
+  int result = cmsysProcess_GetExitValue(cp);
+  cmsysProcess_Delete(cp);
+  return result == 0;
 }
 
-std::string cmCTestVC::ComputeCommandLine(const std::vector<std::string>& cmd)
+std::string cmCTestVC::ComputeCommandLine(char const* const* cmd)
 {
   std::ostringstream line;
   const char* sep = "";
-  for (auto const& arg : cmd) {
-    line << sep << "\"" << arg << "\"";
+  for (const char* const* arg = cmd; *arg; ++arg) {
+    line << sep << "\"" << *arg << "\"";
     sep = " ";
   }
   return line.str();
 }
 
-bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
-                                 OutputParser* out, OutputParser* err,
-                                 Encoding encoding)
+bool cmCTestVC::RunUpdateCommand(char const* const* cmd, OutputParser* out,
+                                 OutputParser* err, Encoding encoding)
 {
   // Report the command line.
   this->UpdateCommandLine = this->ComputeCommandLine(cmd);
@@ -106,7 +113,7 @@ bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
   }
 
   // Run the command.
-  return this->RunChild(cmd, out, err, "", encoding);
+  return this->RunChild(cmd, out, err, nullptr, encoding);
 }
 
 std::string cmCTestVC::GetNightlyTime()

+ 4 - 5
Source/CTest/cmCTestVC.h

@@ -6,7 +6,6 @@
 
 #include <iosfwd>
 #include <string>
-#include <vector>
 
 #include "cmProcessOutput.h"
 #include "cmProcessTools.h"
@@ -109,15 +108,15 @@ protected:
   };
 
   /** Convert a list of arguments to a human-readable command line.  */
-  static std::string ComputeCommandLine(const std::vector<std::string>& cmd);
+  static std::string ComputeCommandLine(char const* const* cmd);
 
   /** Run a command line and send output to given parsers.  */
-  bool RunChild(const std::vector<std::string>& cmd, OutputParser* out,
-                OutputParser* err, std::string workDir = {},
+  bool RunChild(char const* const* cmd, OutputParser* out, OutputParser* err,
+                const char* workDir = nullptr,
                 Encoding encoding = cmProcessOutput::Auto);
 
   /** Run VC update command line and send output to given parsers.  */
-  bool RunUpdateCommand(const std::vector<std::string>& cmd, OutputParser* out,
+  bool RunUpdateCommand(char const* const* cmd, OutputParser* out,
                         OutputParser* err = nullptr,
                         Encoding encoding = cmProcessOutput::Auto);
 

+ 0 - 4
Source/LexerParser/cmCTestResourceGroupsLexer.cxx

@@ -667,10 +667,6 @@ Modify cmCTestResourceGroupsLexer.cxx:
 
 #include <cstddef>
 
-#ifndef _WIN32
-#  include <termios.h>
-#endif
-
 /*--------------------------------------------------------------------------*/
 
 #define INITIAL 0

+ 0 - 4
Source/LexerParser/cmCTestResourceGroupsLexer.in.l

@@ -26,10 +26,6 @@ Modify cmCTestResourceGroupsLexer.cxx:
 
 #include <cstddef>
 
-#ifndef _WIN32
-#  include <termios.h>
-#endif
-
 /*--------------------------------------------------------------------------*/
 %}
 

+ 198 - 231
Source/cmCTest.cxx

@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <cctype>
 #include <chrono>
-#include <cstdint>
 #include <cstdio>
 #include <cstdlib>
 #include <cstring>
@@ -25,13 +24,13 @@
 #include <cmext/string_view>
 
 #include <cm3p/curl/curl.h>
-#include <cm3p/uv.h>
 #include <cm3p/zlib.h>
 
 #include "cmsys/Base64.h"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/SystemInformation.hxx"
 #if defined(_WIN32)
@@ -65,9 +64,6 @@
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.h"
@@ -1077,9 +1073,9 @@ int cmCTest::GetTestModelFromString(const std::string& str)
 // ######################################################################
 // ######################################################################
 
-bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
-                             int* retVal, const char* dir, cmDuration timeout,
-                             std::ostream& ofs, Encoding encoding)
+int cmCTest::RunMakeCommand(const std::string& command, std::string& output,
+                            int* retVal, const char* dir, cmDuration timeout,
+                            std::ostream& ofs, Encoding encoding)
 {
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -1088,107 +1084,107 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
     return false;
   }
 
+  std::vector<const char*> argv;
+  argv.reserve(args.size() + 1);
+  for (std::string const& a : args) {
+    argv.push_back(a.c_str());
+  }
+  argv.push_back(nullptr);
+
   output.clear();
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
-  for (auto const& arg : args) {
+  for (char const* arg : argv) {
+    if (!arg) {
+      break;
+    }
     cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, " \"" << arg << "\"");
   }
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
 
   // Now create process object
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(args).SetMergedBuiltinStreams();
-  if (dir) {
-    builder.SetWorkingDirectory(dir);
-  }
-  auto chain = builder.Start();
-  cm::uv_pipe_ptr outputStream;
-  outputStream.init(chain.GetLoop(), 0);
-  uv_pipe_open(outputStream, chain.OutputStream());
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_SetCommand(cp, argv.data());
+  cmsysProcess_SetWorkingDirectory(cp, dir);
+  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+  cmsysProcess_SetTimeout(cp, timeout.count());
+  cmsysProcess_Execute(cp);
 
   // Initialize tick's
   std::string::size_type tick = 0;
   std::string::size_type tick_len = 1024;
   std::string::size_type tick_line_len = 50;
 
+  char* data;
+  int length;
   cmProcessOutput processOutput(encoding);
+  std::string strdata;
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              "   Each . represents " << tick_len
                                      << " bytes of output\n"
                                         "    "
                                      << std::flush);
-  auto outputHandle = cmUVStreamRead(
-    outputStream,
-    [this, &processOutput, &output, &tick, &tick_len, &tick_line_len,
-     &ofs](std::vector<char> data) {
-      std::string strdata;
-      processOutput.DecodeText(data.data(), data.size(), strdata);
-      for (char& cc : strdata) {
-        if (cc == 0) {
-          cc = '\n';
-        }
+  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
+    processOutput.DecodeText(data, length, strdata);
+    for (char& cc : strdata) {
+      if (cc == 0) {
+        cc = '\n';
       }
-      output.append(strdata);
-      while (output.size() > (tick * tick_len)) {
-        tick++;
-        cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
-        if (tick % tick_line_len == 0 && tick > 0) {
-          cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
-                     "  Size: " << int((double(output.size()) / 1024.0) + 1)
-                                << "K\n    " << std::flush);
-        }
-      }
-      cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-                 cmCTestLogWrite(strdata.c_str(), strdata.size()));
-      if (ofs) {
-        ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
-      }
-    },
-    [this, &processOutput, &output, &ofs]() {
-      std::string strdata;
-      processOutput.DecodeText(std::string(), strdata);
-      if (!strdata.empty()) {
-        output.append(strdata);
-        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-                   cmCTestLogWrite(strdata.c_str(), strdata.size()));
-        if (ofs) {
-          ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
-        }
+    }
+    output.append(strdata);
+    while (output.size() > (tick * tick_len)) {
+      tick++;
+      cmCTestLog(this, HANDLER_PROGRESS_OUTPUT, "." << std::flush);
+      if (tick % tick_line_len == 0 && tick > 0) {
+        cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
+                   "  Size: " << int((double(output.size()) / 1024.0) + 1)
+                              << "K\n    " << std::flush);
       }
-    });
-
-  bool finished = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
+    }
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+               cmCTestLogWrite(strdata.c_str(), strdata.size()));
+    if (ofs) {
+      ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
+    }
+  }
+  processOutput.DecodeText(std::string(), strdata);
+  if (!strdata.empty()) {
+    output.append(strdata);
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+               cmCTestLogWrite(strdata.c_str(), strdata.size()));
+    if (ofs) {
+      ofs << cmCTestLogWrite(strdata.c_str(), strdata.size());
+    }
+  }
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              " Size of output: " << int(double(output.size()) / 1024.0) << "K"
                                  << std::endl);
 
-  if (finished) {
-    auto const& status = chain.GetStatus(0);
-    auto exception = status.GetException();
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        *retVal = static_cast<int>(status.ExitStatus);
-        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-                   "Command exited with the value: " << *retVal << std::endl);
-        break;
-      case cmUVProcessChain::ExceptionCode::Spawn:
-        output += "\n*** ERROR executing: ";
-        output += exception.second;
-        output += "\n***The build process failed.";
-        cmCTestLog(this, ERROR_MESSAGE,
-                   "There was an error: " << exception.second << std::endl);
-        break;
-      default:
-        *retVal = static_cast<int>(exception.first);
-        cmCTestLog(this, WARNING,
-                   "There was an exception: " << *retVal << std::endl);
-        break;
-    }
-  } else {
+  cmsysProcess_WaitForExit(cp, nullptr);
+
+  int result = cmsysProcess_GetState(cp);
+
+  if (result == cmsysProcess_State_Exited) {
+    *retVal = cmsysProcess_GetExitValue(cp);
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+               "Command exited with the value: " << *retVal << std::endl);
+  } else if (result == cmsysProcess_State_Exception) {
+    *retVal = cmsysProcess_GetExitException(cp);
+    cmCTestLog(this, WARNING,
+               "There was an exception: " << *retVal << std::endl);
+  } else if (result == cmsysProcess_State_Expired) {
     cmCTestLog(this, WARNING, "There was a timeout" << std::endl);
+  } else if (result == cmsysProcess_State_Error) {
+    output += "\n*** ERROR executing: ";
+    output += cmsysProcess_GetErrorString(cp);
+    output += "\n***The build process failed.";
+    cmCTestLog(this, ERROR_MESSAGE,
+               "There was an error: " << cmsysProcess_GetErrorString(cp)
+                                      << std::endl);
   }
 
-  return true;
+  cmsysProcess_Delete(cp);
+
+  return result;
 }
 
 // ######################################################################
@@ -1196,10 +1192,9 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
 // ######################################################################
 // ######################################################################
 
-bool cmCTest::RunTest(const std::vector<std::string>& argv,
-                      std::string* output, int* retVal, std::ostream* log,
-                      cmDuration testTimeOut,
-                      std::vector<std::string>* environment, Encoding encoding)
+int cmCTest::RunTest(std::vector<const char*> argv, std::string* output,
+                     int* retVal, std::ostream* log, cmDuration testTimeOut,
+                     std::vector<std::string>* environment, Encoding encoding)
 {
   bool modifyEnv = (environment && !environment->empty());
 
@@ -1238,16 +1233,19 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
     inst.SetStreams(&oss, &oss);
 
     std::vector<std::string> args;
-    for (auto const& i : argv) {
-      // make sure we pass the timeout in for any build and test
-      // invocations. Since --build-generator is required this is a
-      // good place to check for it, and to add the arguments in
-      if (i == "--build-generator" && timeout != cmCTest::MaxDuration() &&
-          timeout > cmDuration::zero()) {
-        args.emplace_back("--test-timeout");
-        args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
+    for (char const* i : argv) {
+      if (i) {
+        // make sure we pass the timeout in for any build and test
+        // invocations. Since --build-generator is required this is a
+        // good place to check for it, and to add the arguments in
+        if (strcmp(i, "--build-generator") == 0 &&
+            timeout != cmCTest::MaxDuration() &&
+            timeout > cmDuration::zero()) {
+          args.emplace_back("--test-timeout");
+          args.push_back(std::to_string(cmDurationTo<unsigned int>(timeout)));
+        }
+        args.emplace_back(i);
       }
-      args.emplace_back(i);
     }
     if (log) {
       *log << "* Run internal CTest" << std::endl;
@@ -1273,7 +1271,7 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
                                                              << std::endl);
     }
 
-    return true;
+    return cmsysProcess_State_Exited;
   }
   std::vector<char> tempOutput;
   if (output) {
@@ -1286,43 +1284,41 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
     cmSystemTools::AppendEnv(*environment);
   }
 
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(argv).SetMergedBuiltinStreams();
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_SetCommand(cp, argv.data());
   cmCTestLog(this, DEBUG, "Command is: " << argv[0] << std::endl);
-  auto chain = builder.Start();
+  if (cmSystemTools::GetRunCommandHideConsole()) {
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
+  }
 
+  cmsysProcess_SetTimeout(cp, timeout.count());
+  cmsysProcess_Execute(cp);
+
+  char* data;
+  int length;
   cmProcessOutput processOutput(encoding);
-  cm::uv_pipe_ptr outputStream;
-  outputStream.init(chain.GetLoop(), 0);
-  uv_pipe_open(outputStream, chain.OutputStream());
-  auto outputHandle = cmUVStreamRead(
-    outputStream,
-    [this, &processOutput, &output, &tempOutput,
-     &log](std::vector<char> data) {
-      std::string strdata;
-      processOutput.DecodeText(data.data(), data.size(), strdata);
-      if (output) {
-        cm::append(tempOutput, data.data(), data.data() + data.size());
-      }
-      cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-                 cmCTestLogWrite(strdata.c_str(), strdata.size()));
-      if (log) {
-        log->write(strdata.c_str(), strdata.size());
-      }
-    },
-    [this, &processOutput, &log]() {
-      std::string strdata;
-      processOutput.DecodeText(std::string(), strdata);
-      if (!strdata.empty()) {
-        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
-                   cmCTestLogWrite(strdata.c_str(), strdata.size()));
-        if (log) {
-          log->write(strdata.c_str(), strdata.size());
-        }
-      }
-    });
+  std::string strdata;
+  while (cmsysProcess_WaitForData(cp, &data, &length, nullptr)) {
+    processOutput.DecodeText(data, length, strdata);
+    if (output) {
+      cm::append(tempOutput, data, data + length);
+    }
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+               cmCTestLogWrite(strdata.c_str(), strdata.size()));
+    if (log) {
+      log->write(strdata.c_str(), strdata.size());
+    }
+  }
+  processOutput.DecodeText(std::string(), strdata);
+  if (!strdata.empty()) {
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
+               cmCTestLogWrite(strdata.c_str(), strdata.size()));
+    if (log) {
+      log->write(strdata.c_str(), strdata.size());
+    }
+  }
 
-  bool complete = chain.Wait(static_cast<uint64_t>(timeout.count() * 1000.0));
+  cmsysProcess_WaitForExit(cp, nullptr);
   processOutput.DecodeText(tempOutput, tempOutput);
   if (output && tempOutput.begin() != tempOutput.end()) {
     output->append(tempOutput.data(), tempOutput.size());
@@ -1330,41 +1326,33 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
              "-- Process completed" << std::endl);
 
-  bool result = false;
+  int result = cmsysProcess_GetState(cp);
 
-  if (complete) {
-    auto const& status = chain.GetStatus(0);
-    auto exception = status.GetException();
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        *retVal = static_cast<int>(status.ExitStatus);
-        if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
-          this->OutputTestErrors(tempOutput);
-        }
-        result = true;
-        break;
-      case cmUVProcessChain::ExceptionCode::Spawn: {
-        std::string outerr =
-          cmStrCat("\n*** ERROR executing: ", exception.second);
-        if (output) {
-          *output += outerr;
-        }
-        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
-      } break;
-      default: {
-        if (this->Impl->OutputTestOutputOnTestFailure) {
-          this->OutputTestErrors(tempOutput);
-        }
-        *retVal = status.TermSignal;
-        std::string outerr =
-          cmStrCat("\n*** Exception executing: ", exception.second);
-        if (output) {
-          *output += outerr;
-        }
-        cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
-      } break;
+  if (result == cmsysProcess_State_Exited) {
+    *retVal = cmsysProcess_GetExitValue(cp);
+    if (*retVal != 0 && this->Impl->OutputTestOutputOnTestFailure) {
+      this->OutputTestErrors(tempOutput);
+    }
+  } else if (result == cmsysProcess_State_Exception) {
+    if (this->Impl->OutputTestOutputOnTestFailure) {
+      this->OutputTestErrors(tempOutput);
+    }
+    *retVal = cmsysProcess_GetExitException(cp);
+    std::string outerr = cmStrCat("\n*** Exception executing: ",
+                                  cmsysProcess_GetExceptionString(cp));
+    if (output) {
+      *output += outerr;
+    }
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
+  } else if (result == cmsysProcess_State_Error) {
+    std::string outerr =
+      cmStrCat("\n*** ERROR executing: ", cmsysProcess_GetErrorString(cp));
+    if (output) {
+      *output += outerr;
     }
+    cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, outerr << std::endl);
   }
+  cmsysProcess_Delete(cp);
 
   return result;
 }
@@ -3482,70 +3470,49 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
   stdOut->clear();
   stdErr->clear();
 
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(args)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
-  if (dir) {
-    builder.SetWorkingDirectory(dir);
-  }
-  auto chain = builder.Start();
-
-  cm::uv_timer_ptr timer;
-  bool timedOut = false;
-  if (timeout.count()) {
-    timer.init(chain.GetLoop(), &timedOut);
-    timer.start(
-      [](uv_timer_t* t) {
-        auto* timedOutPtr = static_cast<bool*>(t->data);
-        *timedOutPtr = true;
-      },
-      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_SetCommand(cp, argv.data());
+  cmsysProcess_SetWorkingDirectory(cp, dir);
+  if (cmSystemTools::GetRunCommandHideConsole()) {
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
   }
+  cmsysProcess_SetTimeout(cp, timeout.count());
+  cmsysProcess_Execute(cp);
 
   std::vector<char> tempOutput;
-  bool outFinished = false;
-  cm::uv_pipe_ptr outStream;
   std::vector<char> tempError;
-  bool errFinished = false;
-  cm::uv_pipe_ptr errStream;
+  char* data;
+  int length;
   cmProcessOutput processOutput(encoding);
-  auto startRead = [this, &chain, &processOutput](
-                     cm::uv_pipe_ptr& pipe, int stream,
-                     std::vector<char>& temp,
-                     bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
-    pipe.init(chain.GetLoop(), 0);
-    uv_pipe_open(pipe, stream);
-    return cmUVStreamRead(
-      pipe,
-      [this, &temp, &processOutput](std::vector<char> data) {
-        cm::append(temp, data);
-        if (this->Impl->ExtraVerbose) {
-          std::string strdata;
-          processOutput.DecodeText(data.data(), data.size(), strdata);
-          cmSystemTools::Stdout(strdata);
-        }
-      },
-      [&finished]() { finished = true; });
-  };
-  auto outputHandle =
-    startRead(outStream, chain.OutputStream(), tempOutput, outFinished);
-  auto errorHandle =
-    startRead(errStream, chain.ErrorStream(), tempError, errFinished);
-  while (!timedOut && !(outFinished && errFinished)) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  std::string strdata;
+  int res;
+  bool done = false;
+  while (!done) {
+    res = cmsysProcess_WaitForData(cp, &data, &length, nullptr);
+    switch (res) {
+      case cmsysProcess_Pipe_STDOUT:
+        cm::append(tempOutput, data, data + length);
+        break;
+      case cmsysProcess_Pipe_STDERR:
+        cm::append(tempError, data, data + length);
+        break;
+      default:
+        done = true;
+    }
+    if ((res == cmsysProcess_Pipe_STDOUT || res == cmsysProcess_Pipe_STDERR) &&
+        this->Impl->ExtraVerbose) {
+      processOutput.DecodeText(data, length, strdata);
+      cmSystemTools::Stdout(strdata);
+    }
   }
   if (this->Impl->ExtraVerbose) {
-    std::string strdata;
     processOutput.DecodeText(std::string(), strdata);
     if (!strdata.empty()) {
       cmSystemTools::Stdout(strdata);
     }
   }
 
-  while (!timedOut && !chain.Finished()) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
-  }
+  cmsysProcess_WaitForExit(cp, nullptr);
   if (!tempOutput.empty()) {
     processOutput.DecodeText(tempOutput, tempOutput);
     stdOut->append(tempOutput.data(), tempOutput.size());
@@ -3556,32 +3523,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
   }
 
   bool result = true;
-  if (timedOut) {
+  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
+    if (retVal) {
+      *retVal = cmsysProcess_GetExitValue(cp);
+    } else {
+      if (cmsysProcess_GetExitValue(cp) != 0) {
+        result = false;
+      }
+    }
+  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
+    const char* exception_str = cmsysProcess_GetExceptionString(cp);
+    cmCTestLog(this, ERROR_MESSAGE, exception_str << std::endl);
+    stdErr->append(exception_str, strlen(exception_str));
+    result = false;
+  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
+    const char* error_str = cmsysProcess_GetErrorString(cp);
+    cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
+    stdErr->append(error_str, strlen(error_str));
+    result = false;
+  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
     const char* error_str = "Process terminated due to timeout\n";
     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
     stdErr->append(error_str, strlen(error_str));
     result = false;
-  } else {
-    auto const& status = chain.GetStatus(0);
-    auto exception = status.GetException();
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        if (retVal) {
-          *retVal = static_cast<int>(status.ExitStatus);
-        } else {
-          if (status.ExitStatus != 0) {
-            result = false;
-          }
-        }
-        break;
-      default: {
-        cmCTestLog(this, ERROR_MESSAGE, exception.second << std::endl);
-        stdErr->append(exception.second);
-        result = false;
-      } break;
-    }
   }
 
+  cmsysProcess_Delete(cp);
   return result;
 }
 

+ 8 - 8
Source/cmCTest.h

@@ -254,10 +254,10 @@ public:
    * Run command specialized for make and configure. Returns process status
    * and retVal is return value or exception.
    */
-  bool RunMakeCommand(const std::string& command, std::string& output,
-                      int* retVal, const char* dir, cmDuration timeout,
-                      std::ostream& ofs,
-                      Encoding encoding = cmProcessOutput::Auto);
+  int RunMakeCommand(const std::string& command, std::string& output,
+                     int* retVal, const char* dir, cmDuration timeout,
+                     std::ostream& ofs,
+                     Encoding encoding = cmProcessOutput::Auto);
 
   /** Return the current tag */
   std::string GetCurrentTag();
@@ -303,10 +303,10 @@ public:
    * environment variables prior to running the test. After running the test,
    * environment variables are restored to their previous values.
    */
-  bool RunTest(const std::vector<std::string>& args, std::string* output,
-               int* retVal, std::ostream* logfile, cmDuration testTimeOut,
-               std::vector<std::string>* environment,
-               Encoding encoding = cmProcessOutput::Auto);
+  int RunTest(std::vector<const char*> args, std::string* output, int* retVal,
+              std::ostream* logfile, cmDuration testTimeOut,
+              std::vector<std::string>* environment,
+              Encoding encoding = cmProcessOutput::Auto);
 
   /**
    * Get the handler object

+ 195 - 204
Source/cmExecuteProcessCommand.cxx

@@ -2,8 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExecuteProcessCommand.h"
 
+#include <algorithm>
 #include <cctype> /* isspace */
-#include <cstdint>
 #include <cstdio>
 #include <iostream>
 #include <map>
@@ -16,7 +16,7 @@
 #include <cmext/algorithm>
 #include <cmext/string_view>
 
-#include <cm3p/uv.h>
+#include "cmsys/Process.h"
 
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
@@ -26,9 +26,6 @@
 #include "cmProcessOutput.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 
 namespace {
 bool cmExecuteProcessCommandIsWhitespace(char c)
@@ -39,7 +36,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c)
 void cmExecuteProcessCommandFixText(std::vector<char>& output,
                                     bool strip_trailing_whitespace);
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   std::size_t length);
+                                   int length);
 }
 
 // cmExecuteProcessCommand
@@ -164,68 +161,57 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     }
   }
   // Create a process instance.
-  cmUVProcessChainBuilder builder;
+  std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp_ptr(
+    cmsysProcess_New(), cmsysProcess_Delete);
+  cmsysProcess* cp = cp_ptr.get();
 
   // Set the command sequence.
   for (std::vector<std::string> const& cmd : arguments.Commands) {
-    builder.AddCommand(cmd);
+    std::vector<const char*> argv(cmd.size() + 1);
+    std::transform(cmd.begin(), cmd.end(), argv.begin(),
+                   [](std::string const& s) { return s.c_str(); });
+    argv.back() = nullptr;
+    cmsysProcess_AddCommand(cp, argv.data());
   }
 
   // Set the process working directory.
   if (!arguments.WorkingDirectory.empty()) {
-    builder.SetWorkingDirectory(arguments.WorkingDirectory);
+    cmsysProcess_SetWorkingDirectory(cp, arguments.WorkingDirectory.c_str());
   }
 
-  // Check the output variables.
-  std::unique_ptr<FILE, int (*)(FILE*)> inputFile(nullptr, fclose);
-  if (!inputFilename.empty()) {
-    inputFile.reset(cmsys::SystemTools::Fopen(inputFilename, "rb"));
-    if (inputFile) {
-      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT,
-                                inputFile.get());
-    }
-  } else {
-    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin);
-  }
+  // Always hide the process window.
+  cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
 
-  std::unique_ptr<FILE, int (*)(FILE*)> outputFile(nullptr, fclose);
-  if (!outputFilename.empty()) {
-    outputFile.reset(cmsys::SystemTools::Fopen(outputFilename, "wb"));
-    if (outputFile) {
-      builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT,
-                                outputFile.get());
-    }
-  } else {
-    if (arguments.OutputVariable == arguments.ErrorVariable &&
-        !arguments.ErrorVariable.empty()) {
-      builder.SetMergedBuiltinStreams();
+  // Check the output variables.
+  bool merge_output = false;
+  if (!arguments.InputFile.empty()) {
+    cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDIN,
+                             arguments.InputFile.c_str());
+  }
+  if (!arguments.OutputFile.empty()) {
+    cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDOUT,
+                             arguments.OutputFile.c_str());
+  }
+  if (!arguments.ErrorFile.empty()) {
+    if (arguments.ErrorFile == arguments.OutputFile) {
+      merge_output = true;
     } else {
-      builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT);
+      cmsysProcess_SetPipeFile(cp, cmsysProcess_Pipe_STDERR,
+                               arguments.ErrorFile.c_str());
     }
   }
-
-  std::unique_ptr<FILE, int (*)(FILE*)> errorFile(nullptr, fclose);
-  if (!errorFilename.empty()) {
-    if (errorFilename == outputFilename) {
-      if (outputFile) {
-        builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
-                                  outputFile.get());
-      }
-    } else {
-      errorFile.reset(cmsys::SystemTools::Fopen(errorFilename, "wb"));
-      if (errorFile) {
-        builder.SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR,
-                                  errorFile.get());
-      }
-    }
-  } else if (arguments.ErrorVariable.empty() ||
-             (!arguments.ErrorVariable.empty() &&
-              arguments.OutputVariable != arguments.ErrorVariable)) {
-    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+  if (!arguments.OutputVariable.empty() &&
+      arguments.OutputVariable == arguments.ErrorVariable) {
+    merge_output = true;
+  }
+  if (merge_output) {
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
   }
 
   // Set the timeout if any.
-  int64_t timeoutMillis = static_cast<int64_t>(timeout * 1000.0);
+  if (timeout >= 0) {
+    cmsysProcess_SetTimeout(cp, timeout);
+  }
 
   bool echo_stdout = false;
   bool echo_stderr = false;
@@ -273,86 +259,36 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     }
   }
   // Start the process.
-  auto chain = builder.Start();
-
-  bool timedOut = false;
-  cm::uv_timer_ptr timer;
-
-  if (timeoutMillis >= 0) {
-    timer.init(chain.GetLoop(), &timedOut);
-    timer.start(
-      [](uv_timer_t* handle) {
-        auto* timeoutPtr = static_cast<bool*>(handle->data);
-        *timeoutPtr = true;
-      },
-      timeoutMillis, 0);
-  }
+  cmsysProcess_Execute(cp);
 
   // Read the process output.
-  struct ReadData
-  {
-    bool Finished = false;
-    std::vector<char> Output;
-    cm::uv_pipe_ptr Stream;
-  };
-  ReadData outputData;
-  ReadData errorData;
+  std::vector<char> tempOutput;
+  std::vector<char> tempError;
+  int length;
+  char* data;
+  int p;
   cmProcessOutput processOutput(
     cmProcessOutput::FindEncoding(arguments.Encoding));
   std::string strdata;
-
-  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
-  if (chain.OutputStream() >= 0) {
-    outputData.Stream.init(chain.GetLoop(), 0);
-    uv_pipe_open(outputData.Stream, chain.OutputStream());
-    outputHandle = cmUVStreamRead(
-      outputData.Stream,
-      [&arguments, &processOutput, &outputData,
-       &strdata](std::vector<char> data) {
-        if (!arguments.OutputQuiet) {
-          if (arguments.OutputVariable.empty() ||
-              arguments.EchoOutputVariable) {
-            processOutput.DecodeText(data.data(), data.size(), strdata, 1);
-            cmSystemTools::Stdout(strdata);
-          }
-          if (!arguments.OutputVariable.empty()) {
-            cmExecuteProcessCommandAppend(outputData.Output, data.data(),
-                                          data.size());
-          }
-        }
-      },
-      [&outputData]() { outputData.Finished = true; });
-  } else {
-    outputData.Finished = true;
-  }
-  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
-  if (chain.ErrorStream() >= 0 &&
-      chain.ErrorStream() != chain.OutputStream()) {
-    errorData.Stream.init(chain.GetLoop(), 0);
-    uv_pipe_open(errorData.Stream, chain.ErrorStream());
-    errorHandle = cmUVStreamRead(
-      errorData.Stream,
-      [&arguments, &processOutput, &errorData,
-       &strdata](std::vector<char> data) {
-        if (!arguments.ErrorQuiet) {
-          if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
-            processOutput.DecodeText(data.data(), data.size(), strdata, 2);
-            cmSystemTools::Stderr(strdata);
-          }
-          if (!arguments.ErrorVariable.empty()) {
-            cmExecuteProcessCommandAppend(errorData.Output, data.data(),
-                                          data.size());
-          }
-        }
-      },
-      [&errorData]() { errorData.Finished = true; });
-  } else {
-    errorData.Finished = true;
-  }
-
-  while (chain.Valid() && !timedOut &&
-         !(chain.Finished() && outputData.Finished && errorData.Finished)) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+  while ((p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
+    // Put the output in the right place.
+    if (p == cmsysProcess_Pipe_STDOUT && !arguments.OutputQuiet) {
+      if (arguments.OutputVariable.empty() || arguments.EchoOutputVariable) {
+        processOutput.DecodeText(data, length, strdata, 1);
+        cmSystemTools::Stdout(strdata);
+      }
+      if (!arguments.OutputVariable.empty()) {
+        cmExecuteProcessCommandAppend(tempOutput, data, length);
+      }
+    } else if (p == cmsysProcess_Pipe_STDERR && !arguments.ErrorQuiet) {
+      if (arguments.ErrorVariable.empty() || arguments.EchoErrorVariable) {
+        processOutput.DecodeText(data, length, strdata, 2);
+        cmSystemTools::Stderr(strdata);
+      }
+      if (!arguments.ErrorVariable.empty()) {
+        cmExecuteProcessCommandAppend(tempError, data, length);
+      }
+    }
   }
   if (!arguments.OutputQuiet &&
       (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
@@ -369,102 +305,151 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     }
   }
 
-  // All output has been read.
-  processOutput.DecodeText(outputData.Output, outputData.Output);
-  processOutput.DecodeText(errorData.Output, errorData.Output);
+  // All output has been read.  Wait for the process to exit.
+  cmsysProcess_WaitForExit(cp, nullptr);
+  processOutput.DecodeText(tempOutput, tempOutput);
+  processOutput.DecodeText(tempError, tempError);
 
   // Fix the text in the output strings.
-  cmExecuteProcessCommandFixText(outputData.Output,
+  cmExecuteProcessCommandFixText(tempOutput,
                                  arguments.OutputStripTrailingWhitespace);
-  cmExecuteProcessCommandFixText(errorData.Output,
+  cmExecuteProcessCommandFixText(tempError,
                                  arguments.ErrorStripTrailingWhitespace);
 
   // Store the output obtained.
-  if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) {
+  if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
     status.GetMakefile().AddDefinition(arguments.OutputVariable,
-                                       outputData.Output.data());
+                                       tempOutput.data());
   }
-  if (arguments.ErrorVariable != arguments.OutputVariable &&
-      !arguments.ErrorVariable.empty() && !errorData.Output.empty()) {
+  if (!merge_output && !arguments.ErrorVariable.empty() &&
+      !tempError.empty()) {
     status.GetMakefile().AddDefinition(arguments.ErrorVariable,
-                                       errorData.Output.data());
+                                       tempError.data());
   }
 
   // Store the result of running the process.
   if (!arguments.ResultVariable.empty()) {
-    if (timedOut) {
-      status.GetMakefile().AddDefinition(arguments.ResultVariable,
-                                         "Process terminated due to timeout");
-    } else {
-      auto const* lastStatus = chain.GetStatus().back();
-      auto exception = lastStatus->GetException();
-      if (exception.first == cmUVProcessChain::ExceptionCode::None) {
+    switch (cmsysProcess_GetState(cp)) {
+      case cmsysProcess_State_Exited: {
+        int v = cmsysProcess_GetExitValue(cp);
+        char buf[16];
+        snprintf(buf, sizeof(buf), "%d", v);
+        status.GetMakefile().AddDefinition(arguments.ResultVariable, buf);
+      } break;
+      case cmsysProcess_State_Exception:
         status.GetMakefile().AddDefinition(
-          arguments.ResultVariable,
-          std::to_string(static_cast<int>(lastStatus->ExitStatus)));
-      } else {
+          arguments.ResultVariable, cmsysProcess_GetExceptionString(cp));
+        break;
+      case cmsysProcess_State_Error:
         status.GetMakefile().AddDefinition(arguments.ResultVariable,
-                                           exception.second);
-      }
+                                           cmsysProcess_GetErrorString(cp));
+        break;
+      case cmsysProcess_State_Expired:
+        status.GetMakefile().AddDefinition(
+          arguments.ResultVariable, "Process terminated due to timeout");
+        break;
     }
   }
   // Store the result of running the processes.
   if (!arguments.ResultsVariable.empty()) {
-    if (timedOut) {
-      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
-                                         "Process terminated due to timeout");
-    } else {
-      std::vector<std::string> res;
-      for (auto const* processStatus : chain.GetStatus()) {
-        auto exception = processStatus->GetException();
-        if (exception.first == cmUVProcessChain::ExceptionCode::None) {
-          res.emplace_back(
-            std::to_string(static_cast<int>(processStatus->ExitStatus)));
-        } else {
-          res.emplace_back(exception.second);
+    switch (cmsysProcess_GetState(cp)) {
+      case cmsysProcess_State_Exited: {
+        std::vector<std::string> res;
+        for (size_t i = 0; i < arguments.Commands.size(); ++i) {
+          switch (cmsysProcess_GetStateByIndex(cp, static_cast<int>(i))) {
+            case kwsysProcess_StateByIndex_Exited: {
+              int exitCode =
+                cmsysProcess_GetExitValueByIndex(cp, static_cast<int>(i));
+              char buf[16];
+              snprintf(buf, sizeof(buf), "%d", exitCode);
+              res.emplace_back(buf);
+            } break;
+            case kwsysProcess_StateByIndex_Exception:
+              res.emplace_back(cmsysProcess_GetExceptionStringByIndex(
+                cp, static_cast<int>(i)));
+              break;
+            case kwsysProcess_StateByIndex_Error:
+            default:
+              res.emplace_back("Error getting the child return code");
+              break;
+          }
         }
-      }
-      status.GetMakefile().AddDefinition(arguments.ResultsVariable,
-                                         cmList::to_string(res));
+        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                           cmList::to_string(res));
+      } break;
+      case cmsysProcess_State_Exception:
+        status.GetMakefile().AddDefinition(
+          arguments.ResultsVariable, cmsysProcess_GetExceptionString(cp));
+        break;
+      case cmsysProcess_State_Error:
+        status.GetMakefile().AddDefinition(arguments.ResultsVariable,
+                                           cmsysProcess_GetErrorString(cp));
+        break;
+      case cmsysProcess_State_Expired:
+        status.GetMakefile().AddDefinition(
+          arguments.ResultsVariable, "Process terminated due to timeout");
+        break;
     }
   }
 
-  auto queryProcessStatusByIndex = [&chain](std::size_t index) -> std::string {
-    auto const& processStatus = chain.GetStatus(index);
-    auto exception = processStatus.GetException();
-    if (exception.first == cmUVProcessChain::ExceptionCode::None) {
-      if (processStatus.ExitStatus) {
-        return cmStrCat("Child return code: ", processStatus.ExitStatus);
+  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;
       }
-      return "";
+      case kwsysProcess_StateByIndex_Error:
+      default:
+        processStatus = "Error getting the child return code";
+        break;
     }
-    return cmStrCat("Abnormal exit with child return code: ",
-                    exception.second);
+    return processStatus;
   };
 
   if (arguments.CommandErrorIsFatal == "ANY"_s) {
     bool ret = true;
-    if (timedOut) {
-      status.SetError("Process terminated due to timeout");
-      ret = false;
-    } else {
-      std::map<std::size_t, std::string> failureIndices;
-      auto statuses = chain.GetStatus();
-      for (std::size_t i = 0; i < statuses.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";
+    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;
+          }
         }
-        status.SetError(oss.str());
+      } 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) {
@@ -475,23 +460,29 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
 
   if (arguments.CommandErrorIsFatal == "LAST"_s) {
     bool ret = true;
-    if (timedOut) {
-      status.SetError("Process terminated due to timeout");
-      ret = false;
-    } else {
-      auto const& lastStatus = chain.GetStatus(arguments.Commands.size() - 1);
-      auto exception = lastStatus.GetException();
-      if (exception.first != cmUVProcessChain::ExceptionCode::None) {
-        status.SetError(cmStrCat("Abnormal exit: ", exception.second));
-        ret = false;
-      } else {
+    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");
           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::SetFatalErrorOccurred();
@@ -534,7 +525,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output,
 }
 
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   std::size_t length)
+                                   int length)
 {
 #if defined(__APPLE__)
   // HACK on Apple to work around bug with inserting at the

+ 31 - 51
Source/cmProcessTools.cxx

@@ -2,68 +2,48 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProcessTools.h"
 
-#include <algorithm>
-#include <iterator>
 #include <ostream>
 
-#include <cm3p/uv.h>
+#include "cmsys/Process.h"
 
 #include "cmProcessOutput.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVStream.h"
 
-std::vector<cmUVProcessChain::Status> cmProcessTools::RunProcess(
-  cmUVProcessChainBuilder& builder, OutputParser* out, OutputParser* err,
-  Encoding encoding)
+void cmProcessTools::RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
+                                OutputParser* err, Encoding encoding)
 {
+  cmsysProcess_Execute(cp);
+  char* data = nullptr;
+  int length = 0;
+  int p;
   cmProcessOutput processOutput(encoding);
-
-  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
-
-  auto chain = builder.Start();
-
   std::string strdata;
-  cm::uv_pipe_ptr outputPipe;
-  outputPipe.init(chain.GetLoop(), 0);
-  uv_pipe_open(outputPipe, chain.OutputStream());
-  auto outputHandle = cmUVStreamRead(
-    outputPipe,
-    [&out, &processOutput, &strdata](std::vector<char> data) {
-      if (out) {
-        processOutput.DecodeText(data.data(), data.size(), strdata, 1);
-        if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
-          out = nullptr;
-        }
+  while ((out || err) &&
+         (p = cmsysProcess_WaitForData(cp, &data, &length, nullptr))) {
+    if (out && p == cmsysProcess_Pipe_STDOUT) {
+      processOutput.DecodeText(data, length, strdata, 1);
+      if (!out->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
+        out = nullptr;
       }
-    },
-    [&out]() { out = nullptr; });
-  cm::uv_pipe_ptr errorPipe;
-  errorPipe.init(chain.GetLoop(), 0);
-  uv_pipe_open(errorPipe, chain.ErrorStream());
-  auto errorHandle = cmUVStreamRead(
-    errorPipe,
-    [&err, &processOutput, &strdata](std::vector<char> data) {
-      if (err) {
-        processOutput.DecodeText(data.data(), data.size(), strdata, 2);
-        if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
-          err = nullptr;
-        }
+    } else if (err && p == cmsysProcess_Pipe_STDERR) {
+      processOutput.DecodeText(data, length, strdata, 2);
+      if (!err->Process(strdata.c_str(), static_cast<int>(strdata.size()))) {
+        err = nullptr;
       }
-    },
-    [&err]() { err = nullptr; });
-  while (out || err || !chain.Finished()) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
+    }
   }
-
-  std::vector<cmUVProcessChain::Status> result;
-  auto status = chain.GetStatus();
-  std::transform(
-    status.begin(), status.end(), std::back_inserter(result),
-    [](const cmUVProcessChain::Status* s) -> cmUVProcessChain::Status {
-      return *s;
-    });
-  return result;
+  if (out) {
+    processOutput.DecodeText(std::string(), strdata, 1);
+    if (!strdata.empty()) {
+      out->Process(strdata.c_str(), static_cast<int>(strdata.size()));
+    }
+  }
+  if (err) {
+    processOutput.DecodeText(std::string(), strdata, 2);
+    if (!strdata.empty()) {
+      err->Process(strdata.c_str(), static_cast<int>(strdata.size()));
+    }
+  }
+  cmsysProcess_WaitForExit(cp, nullptr);
 }
 
 cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR)

+ 3 - 5
Source/cmProcessTools.h

@@ -7,10 +7,8 @@
 #include <cstring>
 #include <iosfwd>
 #include <string>
-#include <vector>
 
 #include "cmProcessOutput.h"
-#include "cmUVProcessChain.h"
 
 /** \class cmProcessTools
  * \brief Helper classes for process output parsing
@@ -83,7 +81,7 @@ public:
   };
 
   /** Run a process and send output to given parsers.  */
-  static std::vector<cmUVProcessChain::Status> RunProcess(
-    cmUVProcessChainBuilder& builder, OutputParser* out,
-    OutputParser* err = nullptr, Encoding encoding = cmProcessOutput::Auto);
+  static void RunProcess(struct cmsysProcess_s* cp, OutputParser* out,
+                         OutputParser* err = nullptr,
+                         Encoding encoding = cmProcessOutput::Auto);
 };

+ 109 - 175
Source/cmSystemTools.cxx

@@ -31,9 +31,6 @@
 #include "cmProcessOutput.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmValue.h"
 
 #if !defined(CMAKE_BOOTSTRAP)
@@ -62,14 +59,12 @@
 #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>
@@ -573,111 +568,85 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
                                      const char* dir, OutputOption outputflag,
                                      cmDuration timeout, Encoding encoding)
 {
-  cmUVProcessChainBuilder builder;
-  builder.SetExternalStream(cmUVProcessChainBuilder::Stream_INPUT, stdin)
-    .AddCommand(command);
-  if (dir) {
-    builder.SetWorkingDirectory(dir);
+  std::vector<const char*> argv;
+  argv.reserve(command.size() + 1);
+  for (std::string const& cmd : command) {
+    argv.push_back(cmd.c_str());
+  }
+  argv.push_back(nullptr);
+
+  cmsysProcess* cp = cmsysProcess_New();
+  cmsysProcess_SetCommand(cp, argv.data());
+  cmsysProcess_SetWorkingDirectory(cp, dir);
+  if (cmSystemTools::GetRunCommandHideConsole()) {
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_HideWindow, 1);
   }
 
   if (outputflag == OUTPUT_PASSTHROUGH) {
+    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
+    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
     captureStdOut = nullptr;
     captureStdErr = nullptr;
-    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
-      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
   } else if (outputflag == OUTPUT_MERGE ||
              (captureStdErr && captureStdErr == captureStdOut)) {
-    builder.SetMergedBuiltinStreams();
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
     captureStdErr = nullptr;
-  } else {
-    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
   }
   assert(!captureStdErr || captureStdErr != captureStdOut);
 
-  auto chain = builder.Start();
-  bool timedOut = false;
-  cm::uv_timer_ptr timer;
-  if (timeout.count()) {
-    timer.init(chain.GetLoop(), &timedOut);
-    timer.start(
-      [](uv_timer_t* t) {
-        auto* timedOutPtr = static_cast<bool*>(t->data);
-        *timedOutPtr = true;
-      },
-      static_cast<uint64_t>(timeout.count() * 1000.0), 0);
-  }
+  cmsysProcess_SetTimeout(cp, timeout.count());
+  cmsysProcess_Execute(cp);
 
   std::vector<char> tempStdOut;
   std::vector<char> tempStdErr;
-  cm::uv_pipe_ptr outStream;
-  bool outFinished = true;
-  cm::uv_pipe_ptr errStream;
-  bool errFinished = true;
+  char* data;
+  int length;
+  int pipe;
   cmProcessOutput processOutput(encoding);
-  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
-  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
+  std::string strdata;
   if (outputflag != OUTPUT_PASSTHROUGH &&
       (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
-    auto startRead =
-      [&outputflag, &processOutput,
-       &chain](cm::uv_pipe_ptr& pipe, int stream, std::string* captureStd,
-               std::vector<char>& tempStd, int id,
-               void (*outputFunc)(const std::string&),
-               bool& finished) -> std::unique_ptr<cmUVStreamReadHandle> {
-      if (stream < 0) {
-        return nullptr;
-      }
-
-      pipe.init(chain.GetLoop(), 0);
-      uv_pipe_open(pipe, stream);
-
-      finished = false;
-      return cmUVStreamRead(
-        pipe,
-        [outputflag, &processOutput, captureStd, &tempStd, id,
-         outputFunc](std::vector<char> data) {
-          // Translate NULL characters in the output into valid text.
-          for (auto& c : data) {
-            if (c == '\0') {
-              c = ' ';
-            }
-          }
+    while ((pipe = cmsysProcess_WaitForData(cp, &data, &length, nullptr)) >
+           0) {
+      // Translate NULL characters in the output into valid text.
+      for (int i = 0; i < length; ++i) {
+        if (data[i] == '\0') {
+          data[i] = ' ';
+        }
+      }
 
-          if (outputflag != OUTPUT_NONE) {
-            std::string strdata;
-            processOutput.DecodeText(data.data(), data.size(), strdata, id);
-            outputFunc(strdata);
-          }
-          if (captureStd) {
-            cm::append(tempStd, data.data(), data.data() + data.size());
-          }
-        },
-        [&finished, outputflag, &processOutput, id, outputFunc]() {
-          finished = true;
-          if (outputflag != OUTPUT_NONE) {
-            std::string strdata;
-            processOutput.DecodeText(std::string(), strdata, id);
-            if (!strdata.empty()) {
-              outputFunc(strdata);
-            }
-          }
-        });
-    };
+      if (pipe == cmsysProcess_Pipe_STDOUT) {
+        if (outputflag != OUTPUT_NONE) {
+          processOutput.DecodeText(data, length, strdata, 1);
+          cmSystemTools::Stdout(strdata);
+        }
+        if (captureStdOut) {
+          cm::append(tempStdOut, data, data + length);
+        }
+      } else if (pipe == cmsysProcess_Pipe_STDERR) {
+        if (outputflag != OUTPUT_NONE) {
+          processOutput.DecodeText(data, length, strdata, 2);
+          cmSystemTools::Stderr(strdata);
+        }
+        if (captureStdErr) {
+          cm::append(tempStdErr, data, data + length);
+        }
+      }
+    }
 
-    outputHandle =
-      startRead(outStream, chain.OutputStream(), captureStdOut, tempStdOut, 1,
-                cmSystemTools::Stdout, outFinished);
-    if (chain.OutputStream() != chain.ErrorStream()) {
-      errorHandle =
-        startRead(errStream, chain.ErrorStream(), captureStdErr, tempStdErr, 2,
-                  cmSystemTools::Stderr, errFinished);
+    if (outputflag != OUTPUT_NONE) {
+      processOutput.DecodeText(std::string(), strdata, 1);
+      if (!strdata.empty()) {
+        cmSystemTools::Stdout(strdata);
+      }
+      processOutput.DecodeText(std::string(), strdata, 2);
+      if (!strdata.empty()) {
+        cmSystemTools::Stderr(strdata);
+      }
     }
   }
 
-  while (!timedOut && !(chain.Finished() && outFinished && errFinished)) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
-  }
+  cmsysProcess_WaitForExit(cp, nullptr);
 
   if (captureStdOut) {
     captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
@@ -689,43 +658,48 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
   }
 
   bool result = true;
-  if (timedOut) {
-    const char* error_str = "Process terminated due to timeout\n";
+  if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exited) {
+    if (retVal) {
+      *retVal = cmsysProcess_GetExitValue(cp);
+    } else {
+      if (cmsysProcess_GetExitValue(cp) != 0) {
+        result = false;
+      }
+    }
+  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Exception) {
+    const char* exception_str = cmsysProcess_GetExceptionString(cp);
+    if (outputflag != OUTPUT_NONE) {
+      std::cerr << exception_str << std::endl;
+    }
+    if (captureStdErr) {
+      captureStdErr->append(exception_str, strlen(exception_str));
+    } else if (captureStdOut) {
+      captureStdOut->append(exception_str, strlen(exception_str));
+    }
+    result = false;
+  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Error) {
+    const char* error_str = cmsysProcess_GetErrorString(cp);
     if (outputflag != OUTPUT_NONE) {
       std::cerr << error_str << std::endl;
     }
     if (captureStdErr) {
       captureStdErr->append(error_str, strlen(error_str));
+    } else if (captureStdOut) {
+      captureStdOut->append(error_str, strlen(error_str));
     }
     result = false;
-  } else {
-    auto const& status = chain.GetStatus(0);
-    auto exception = status.GetException();
-
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        if (retVal) {
-          *retVal = static_cast<int>(status.ExitStatus);
-        } else {
-          if (status.ExitStatus != 0) {
-            result = false;
-          }
-        }
-        break;
-      default: {
-        if (outputflag != OUTPUT_NONE) {
-          std::cerr << exception.second << std::endl;
-        }
-        if (captureStdErr) {
-          captureStdErr->append(exception.second);
-        } else if (captureStdOut) {
-          captureStdOut->append(exception.second);
-        }
-        result = false;
-      } break;
+  } else if (cmsysProcess_GetState(cp) == cmsysProcess_State_Expired) {
+    const char* error_str = "Process terminated due to timeout\n";
+    if (outputflag != OUTPUT_NONE) {
+      std::cerr << error_str << std::endl;
+    }
+    if (captureStdErr) {
+      captureStdErr->append(error_str, strlen(error_str));
     }
+    result = false;
   }
 
+  cmsysProcess_Delete(cp);
   return result;
 }
 
@@ -2239,10 +2213,9 @@ bool cmSystemTools::ListTar(const std::string& outFileName,
 #endif
 }
 
-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)
+int cmSystemTools::WaitForLine(cmsysProcess* process, std::string& line,
+                               cmDuration timeout, std::vector<char>& out,
+                               std::vector<char>& err)
 {
   line.clear();
   auto outiter = out.begin();
@@ -2264,7 +2237,7 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
           line.append(out.data(), length);
         }
         out.erase(out.begin(), outiter + 1);
-        return WaitForLineResult::STDOUT;
+        return cmsysProcess_Pipe_STDOUT;
       }
     }
 
@@ -2282,66 +2255,33 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
           line.append(err.data(), length);
         }
         err.erase(err.begin(), erriter + 1);
-        return WaitForLineResult::STDERR;
+        return cmsysProcess_Pipe_STDERR;
       }
     }
 
     // No newlines found.  Wait for more data from the process.
-    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) {
+    int length;
+    char* data;
+    double timeoutAsDbl = timeout.count();
+    int pipe =
+      cmsysProcess_WaitForData(process, &data, &length, &timeoutAsDbl);
+    if (pipe == cmsysProcess_Pipe_Timeout) {
       // Timeout has been exceeded.
-      return WaitForLineResult::Timeout;
+      return pipe;
     }
-    if (outData.Read) {
-      processOutput.DecodeText(outData.Buffer.data(), outData.Buffer.size(),
-                               strdata, 1);
+    if (pipe == cmsysProcess_Pipe_STDOUT) {
+      processOutput.DecodeText(data, length, 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 (errData.Read) {
-      processOutput.DecodeText(errData.Buffer.data(), errData.Buffer.size(),
-                               strdata, 2);
+    } else if (pipe == cmsysProcess_Pipe_STDERR) {
+      processOutput.DecodeText(data, length, 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 (outData.Finished && errData.Finished) {
+    } else if (pipe == cmsysProcess_Pipe_None) {
       // Both stdout and stderr pipes have broken.  Return leftover data.
       processOutput.DecodeText(std::string(), strdata, 1);
       if (!strdata.empty()) {
@@ -2358,20 +2298,14 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
       if (!out.empty()) {
         line.append(out.data(), outiter - out.begin());
         out.erase(out.begin(), out.end());
-        return WaitForLineResult::STDOUT;
+        return cmsysProcess_Pipe_STDOUT;
       }
       if (!err.empty()) {
         line.append(err.data(), erriter - err.begin());
         err.erase(err.begin(), err.end());
-        return WaitForLineResult::STDERR;
+        return cmsysProcess_Pipe_STDERR;
       }
-      return WaitForLineResult::None;
-    }
-    if (!outData.Finished) {
-      uv_read_stop(outPipe);
-    }
-    if (!errData.Finished) {
-      uv_read_stop(errPipe);
+      return cmsysProcess_Pipe_None;
     }
   }
 }

+ 5 - 16
Source/cmSystemTools.h

@@ -18,8 +18,7 @@
 #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
 
@@ -340,20 +339,10 @@ public:
    */
   static void ReportLastSystemError(const char* m);
 
-  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);
+  /** a general output handler for cmsysProcess  */
+  static int WaitForLine(cmsysProcess* process, 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; }

+ 58 - 39
Source/cmcmd.cxx

@@ -72,6 +72,7 @@
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/Terminal.h"
 
@@ -294,8 +295,14 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
     }
   }
 
-  cmUVProcessChainBuilder builder;
-  builder.AddCommand(command).SetWorkingDirectory(currentBinaryDir);
+  std::unique_ptr<cmsysProcess, void (*)(cmsysProcess*)> cp(
+    cmsysProcess_New(), cmsysProcess_Delete);
+  std::vector<const char*> argv(command.size() + 1);
+  std::transform(command.begin(), command.end(), argv.begin(),
+                 [](std::string const& s) { return s.c_str(); });
+  argv.back() = nullptr;
+  cmsysProcess_SetCommand(cp.get(), argv.data());
+  cmsysProcess_SetWorkingDirectory(cp.get(), currentBinaryDir.c_str());
 
   cmsys::ofstream fout(depFile.c_str());
   if (!fout) {
@@ -306,18 +313,22 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
   CLOutputLogger errLogger(std::cerr);
 
   // Start the process.
-  auto result =
-    cmProcessTools::RunProcess(builder, &includeParser, &errLogger);
-  auto const& subStatus = result.front();
+  cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger);
 
   int status = 0;
   // handle status of process
-  if (subStatus.SpawnResult != 0) {
-    status = 2;
-  } else if (subStatus.TermSignal != 0) {
-    status = 1;
-  } else {
-    status = static_cast<int>(subStatus.ExitStatus);
+  switch (cmsysProcess_GetState(cp.get())) {
+    case cmsysProcess_State_Exited:
+      status = cmsysProcess_GetExitValue(cp.get());
+      break;
+    case cmsysProcess_State_Exception:
+      status = 1;
+      break;
+    case cmsysProcess_State_Error:
+      status = 2;
+      break;
+    default:
+      break;
   }
 
   if (status != 0) {
@@ -1105,8 +1116,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
 
       int ret = 0;
       auto time_start = std::chrono::steady_clock::now();
-      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret, nullptr,
-                                      cmSystemTools::OUTPUT_PASSTHROUGH);
+      cmSystemTools::RunSingleCommand(command, nullptr, nullptr, &ret);
       auto time_finish = std::chrono::steady_clock::now();
 
       std::chrono::duration<double> time_elapsed = time_finish - time_start;
@@ -1880,6 +1890,21 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
     }
   }
 
+  // Allocate a process instance.
+  cmsysProcess* cp = cmsysProcess_New();
+  if (!cp) {
+    std::cerr << "Error allocating process instance in link script."
+              << std::endl;
+    return 1;
+  }
+
+  // Children should share stdout and stderr with this process.
+  cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
+  cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
+
+  // Run the command lines verbatim.
+  cmsysProcess_SetOption(cp, cmsysProcess_Option_Verbatim, 1);
+
   // Read command lines from the script.
   cmsys::ifstream fin(args[2].c_str());
   if (!fin) {
@@ -1897,21 +1922,9 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
       continue;
     }
 
-    // Allocate a process instance.
-    cmUVProcessChainBuilder builder;
-
-    // Children should share stdout and stderr with this process.
-    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
-      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
-
     // Setup this command line.
-    std::vector<std::string> args2;
-#ifdef _WIN32
-    cmSystemTools::ParseWindowsCommandLine(command.c_str(), args2);
-#else
-    cmSystemTools::ParseUnixCommandLine(command.c_str(), args2);
-#endif
-    builder.AddCommand(args2);
+    const char* cmd[2] = { command.c_str(), nullptr };
+    cmsysProcess_SetCommand(cp, cmd);
 
     // Report the command if verbose output is enabled.
     if (verbose) {
@@ -1919,29 +1932,35 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
     }
 
     // Run the command and wait for it to exit.
-    auto chain = builder.Start();
-    chain.Wait();
+    cmsysProcess_Execute(cp);
+    cmsysProcess_WaitForExit(cp, nullptr);
 
     // Report failure if any.
-    auto const& status = chain.GetStatus(0);
-    auto exception = status.GetException();
-    switch (exception.first) {
-      case cmUVProcessChain::ExceptionCode::None:
-        if (status.ExitStatus != 0) {
-          result = static_cast<int>(status.ExitStatus);
+    switch (cmsysProcess_GetState(cp)) {
+      case cmsysProcess_State_Exited: {
+        int value = cmsysProcess_GetExitValue(cp);
+        if (value != 0) {
+          result = value;
         }
+      } break;
+      case cmsysProcess_State_Exception:
+        std::cerr << "Error running link command: "
+                  << cmsysProcess_GetExceptionString(cp) << std::endl;
+        result = 1;
         break;
-      case cmUVProcessChain::ExceptionCode::Spawn:
-        std::cerr << "Error running link command: " << exception.second;
+      case cmsysProcess_State_Error:
+        std::cerr << "Error running link command: "
+                  << cmsysProcess_GetErrorString(cp) << std::endl;
         result = 2;
         break;
       default:
-        std::cerr << "Error running link command: " << exception.second;
-        result = 1;
         break;
     }
   }
 
+  // Free the process instance.
+  cmsysProcess_Delete(cp);
+
   // Return the final resulting return value.
   return result;
 }

+ 0 - 4
Utilities/IWYU/mapping.imp

@@ -75,10 +75,6 @@
   { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] },
   { include: [ "<fstream>", public, "\"cmsys/FStream.hxx\"", public ] },
 
-  { symbol: [ "mode_t", private, "\"cm_sys_stat.h\"", public ] },
-  { symbol: [ "S_IWUSR", private, "\"cm_sys_stat.h\"", public ] },
-  { symbol: [ "S_IWGRP", private, "\"cm_sys_stat.h\"", public ] },
-
   { include: [ "<filesystem>", public, "<cm/filesystem>", public ] },
   { include: [ "<optional>", public, "<cm/optional>", public ] },
   { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] },