Browse Source

Revert use of libuv for process execution for 3.28

Wide use of CMake 3.28.{1,0[-rcN]} has uncovered some hangs and crashes
in libuv SIGCHLD handling on some platforms, particularly in virtualization
environments on macOS hosts.  Although the bug does not seem to be in CMake,
we can restore stability in the CMake 3.28 release series for users of such
platforms by reverting our new uses of libuv for process execution.

Revert implementation changes merged by commit 4771544386 (Merge topic
'replace-cmsysprocess-with-cmuvprocesschain', 2023-09-06, v3.28.0-rc1~138),
but keep test suite updates.

Issue: #25414, #25500, #25562, #25589
Brad King 1 năm trước cách đây
mục cha
commit
bcbb212df7

+ 0 - 2
Source/CPack/cmCPackSTGZGenerator.cxx

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

+ 18 - 13
Source/CTest/cmCTestBZR.cxx

@@ -135,14 +135,14 @@ private:
 std::string cmCTestBZR::LoadInfo()
 std::string cmCTestBZR::LoadInfo()
 {
 {
   // Run "bzr info" to get the repository info from the work tree.
   // 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> ");
   InfoParser iout(this, "info-out> ");
   OutputLogger ierr(this->Log, "info-err> ");
   OutputLogger ierr(this->Log, "info-err> ");
   this->RunChild(bzr_info, &iout, &ierr);
   this->RunChild(bzr_info, &iout, &ierr);
 
 
   // Run "bzr revno" to get the repository revision number from the work tree.
   // 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;
   std::string rev;
   RevnoParser rout(this, "revno-out> ", rev);
   RevnoParser rout(this, "revno-out> ", rev);
   OutputLogger rerr(this->Log, "revno-err> ");
   OutputLogger rerr(this->Log, "revno-err> ");
@@ -372,18 +372,22 @@ bool cmCTestBZR::UpdateImpl()
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
   // TODO: if(this->CTest->GetTestModel() == cmCTest::NIGHTLY)
 
 
   // Use "bzr pull" to update the working tree.
   // 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");
   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.
   // For some reason bzr uses stderr to display the update status.
   OutputLogger out(this->Log, "pull-out> ");
   OutputLogger out(this->Log, "pull-out> ");
   UpdateParser err(this, "pull-err> ");
   UpdateParser err(this, "pull-err> ");
-  return this->RunUpdateCommand(bzr_update, &out, &err);
+  return this->RunUpdateCommand(bzr_update.data(), &out, &err);
 }
 }
 
 
 bool cmCTestBZR::LoadRevisions()
 bool cmCTestBZR::LoadRevisions()
@@ -404,9 +408,10 @@ bool cmCTestBZR::LoadRevisions()
   }
   }
 
 
   // Run "bzr log" to get all global revisions of interest.
   // 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> ");
     LogParser out(this, "log-out> ");
     OutputLogger err(this->Log, "log-err> ");
     OutputLogger err(this->Log, "log-err> ");
@@ -462,8 +467,8 @@ private:
 bool cmCTestBZR::LoadModifications()
 bool cmCTestBZR::LoadModifications()
 {
 {
   // Run "bzr status" which reports local modifications.
   // 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> ");
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(bzr_status, &out, &err);
   this->RunChild(bzr_status, &out, &err);

+ 9 - 6
Source/CTest/cmCTestBuildAndTestHandler.cxx

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

+ 134 - 157
Source/CTest/cmCTestBuildHandler.cxx

@@ -3,17 +3,15 @@
 #include "cmCTestBuildHandler.h"
 #include "cmCTestBuildHandler.h"
 
 
 #include <cstdlib>
 #include <cstdlib>
-#include <memory>
 #include <ratio>
 #include <ratio>
 #include <set>
 #include <set>
 #include <utility>
 #include <utility>
 
 
 #include <cmext/algorithm>
 #include <cmext/algorithm>
 
 
-#include <cm3p/uv.h>
-
 #include "cmsys/Directory.hxx"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 
 
 #include "cmCTest.h"
 #include "cmCTest.h"
 #include "cmCTestLaunchReporter.h"
 #include "cmCTestLaunchReporter.h"
@@ -26,9 +24,6 @@
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringReplaceHelper.h"
 #include "cmStringReplaceHelper.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 #include "cmXMLWriter.h"
 
 
@@ -425,7 +420,7 @@ int cmCTestBuildHandler::ProcessHandler()
   cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
   cmStringReplaceHelper colorRemover("\x1b\\[[0-9;]*m", "", nullptr);
   this->ColorRemover = &colorRemover;
   this->ColorRemover = &colorRemover;
   int retVal = 0;
   int retVal = 0;
-  bool res = true;
+  int res = cmsysProcess_State_Exited;
   if (!this->CTest->GetShowOnly()) {
   if (!this->CTest->GetShowOnly()) {
     res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
     res = this->RunMakeCommand(makeCommand, &retVal, buildDirectory.c_str(), 0,
                                ofs);
                                ofs);
@@ -480,7 +475,7 @@ int cmCTestBuildHandler::ProcessHandler()
   }
   }
   this->GenerateXMLFooter(xml, elapsed_build_time);
   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,
     cmCTestLog(this->CTest, ERROR_MESSAGE,
                "Error(s) when building project" << std::endl);
                "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
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -781,9 +776,19 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
     return false;
     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,
   cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                      "Run command:", this->Quiet);
                      "Run command:", this->Quiet);
-  for (auto const& arg : args) {
+  for (char const* arg : argv) {
+    if (!arg) {
+      break;
+    }
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        " \"" << arg << "\"", this->Quiet);
                        " \"" << arg << "\"", this->Quiet);
   }
   }
@@ -795,20 +800,21 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
   static_cast<void>(launchHelper);
   static_cast<void>(launchHelper);
 
 
   // Now create process object
   // 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
   // Initialize tick's
   std::string::size_type tick = 0;
   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);
   cmProcessOutput processOutput(encoding);
+  std::string strdata;
   cmCTestOptionalLog(
   cmCTestOptionalLog(
     this->CTest, HANDLER_PROGRESS_OUTPUT,
     this->CTest, HANDLER_PROGRESS_OUTPUT,
     "   Each symbol represents "
     "   Each symbol represents "
@@ -830,65 +836,39 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
   this->WarningQuotaReached = false;
   this->WarningQuotaReached = false;
   this->ErrorQuotaReached = 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
   // 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->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
                       &this->BuildProcessingQueue);
                       &this->BuildProcessingQueue);
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
   this->ProcessBuffer(nullptr, 0, tick, tick_len, ofs,
@@ -899,93 +879,90 @@ bool cmCTestBuildHandler::RunMakeCommand(const std::string& command,
                        << std::endl,
                        << std::endl,
                      this->Quiet);
                      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,
     cmCTestOptionalLog(this->CTest, WARNING,
                        "There was a timeout" << std::endl, this->Quiet);
                        "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
   //! Run command specialized for make and configure. Returns process status
   // and retVal is return value or exception.
   // 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
   enum
   {
   {

+ 11 - 7
Source/CTest/cmCTestCVS.cxx

@@ -5,7 +5,6 @@
 #include <utility>
 #include <utility>
 
 
 #include <cm/string_view>
 #include <cm/string_view>
-#include <cmext/algorithm>
 
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
@@ -90,15 +89,18 @@ bool cmCTestCVS::UpdateImpl()
   }
   }
 
 
   // Run "cvs update" to update the work tree.
   // 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("-z3");
   cvs_update.push_back("update");
   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 out(this, "up-out> ");
   UpdateParser err(this, "up-err> ");
   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
 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);
   cmCTestLog(this->CTest, HANDLER_OUTPUT, "." << std::flush);
 
 
   // Run "cvs log" to get revisions of this file on this branch.
   // 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);
   LogParser out(this, "log-out> ", revisions);
   OutputLogger err(this->Log, "log-err> ");
   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();
   auto elapsed_time_start = std::chrono::steady_clock::now();
   std::string output;
   std::string output;
   int retVal = 0;
   int retVal = 0;
-  bool res = false;
+  int res = 0;
   if (!this->CTest->GetShowOnly()) {
   if (!this->CTest->GetShowOnly()) {
     cmGeneratedFileStream os;
     cmGeneratedFileStream os;
     if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {
     if (!this->StartResultingXML(cmCTest::PartConfigure, "Configure", os)) {

+ 93 - 16
Source/CTest/cmCTestCoverageHandler.cxx

@@ -9,7 +9,6 @@
 #include <cstring>
 #include <cstring>
 #include <iomanip>
 #include <iomanip>
 #include <iterator>
 #include <iterator>
-#include <memory>
 #include <ratio>
 #include <ratio>
 #include <sstream>
 #include <sstream>
 #include <type_traits>
 #include <type_traits>
@@ -19,6 +18,7 @@
 
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/Glob.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 
 #include "cmCTest.h"
 #include "cmCTest.h"
@@ -33,7 +33,6 @@
 #include "cmParsePHPCoverage.h"
 #include "cmParsePHPCoverage.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVProcessChain.h"
 #include "cmWorkingDirectory.h"
 #include "cmWorkingDirectory.h"
 #include "cmXMLWriter.h"
 #include "cmXMLWriter.h"
 
 
@@ -41,6 +40,85 @@ class cmMakefile;
 
 
 #define SAFEDIV(x, y) (((y) != 0) ? ((x) / (y)) : (0))
 #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;
 cmCTestCoverageHandler::cmCTestCoverageHandler() = default;
 
 
 void cmCTestCoverageHandler::Initialize()
 void cmCTestCoverageHandler::Initialize()
@@ -1862,35 +1940,34 @@ int cmCTestCoverageHandler::RunBullseyeCommand(
     cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
     cmCTestLog(this->CTest, ERROR_MESSAGE, "Cannot find :" << cmd << "\n");
     return 0;
     return 0;
   }
   }
-  std::vector<std::string> args{ cmd };
   if (arg) {
   if (arg) {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << " " << arg << "\n", this->Quiet);
                        "Run : " << program << " " << arg << "\n", this->Quiet);
-    args.emplace_back(arg);
   } else {
   } else {
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
     cmCTestOptionalLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                        "Run : " << program << "\n", this->Quiet);
                        "Run : " << program << "\n", this->Quiet);
   }
   }
   // create a process object and start it
   // create a process object and start it
-  cmUVProcessChainBuilder builder;
+  cmCTestRunProcess runCoverageSrc;
+  runCoverageSrc.SetCommand(program.c_str());
+  runCoverageSrc.AddArgument(arg);
   std::string stdoutFile =
   std::string stdoutFile =
     cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
     cmStrCat(cont->BinaryDir, "/Testing/Temporary/",
              this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
              this->GetCTestInstance()->GetCurrentTag(), '-', cmd);
   std::string stderrFile = stdoutFile;
   std::string stderrFile = stdoutFile;
   stdoutFile += ".stdout";
   stdoutFile += ".stdout";
   stderrFile += ".stderr";
   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
   // since we set the output file names wait for it to end
-  auto chain = builder.Start();
-  chain.Wait();
+  runCoverageSrc.WaitForExit();
   outputFile = stdoutFile;
   outputFile = stdoutFile;
   return 1;
   return 1;
 }
 }

+ 66 - 64
Source/CTest/cmCTestGIT.cxx

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

+ 21 - 14
Source/CTest/cmCTestHG.cxx

@@ -95,8 +95,8 @@ private:
 std::string cmCTestHG::GetWorkingRevision()
 std::string cmCTestHG::GetWorkingRevision()
 {
 {
   // Run plumbing "hg identify" to get work tree revision.
   // 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;
   std::string rev;
   IdentifyParser out(this, "rev-out> ", rev);
   IdentifyParser out(this, "rev-out> ", rev);
   OutputLogger err(this->Log, "rev-err> ");
   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.
   // 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 out(this->Log, "pull-out> ");
     OutputLogger err(this->Log, "pull-err> ");
     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)
   // 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(this->CommandLineTool.c_str());
   hg_update.push_back("update");
   hg_update.push_back("update");
   hg_update.push_back("-v");
   hg_update.push_back("-v");
@@ -147,11 +147,16 @@ bool cmCTestHG::UpdateImpl()
     opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
     opts = this->CTest->GetCTestConfiguration("HGUpdateOptions");
   }
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
   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 out(this->Log, "update-out> ");
   OutputLogger err(this->Log, "update-err> ");
   OutputLogger err(this->Log, "update-err> ");
-  return this->RunUpdateCommand(hg_update, &out, &err);
+  return this->RunUpdateCommand(hg_update.data(), &out, &err);
 }
 }
 
 
 class cmCTestHG::LogParser
 class cmCTestHG::LogParser
@@ -272,8 +277,8 @@ bool cmCTestHG::LoadRevisions()
   // the project has spaces in the path.  Also, they may not have
   // the project has spaces in the path.  Also, they may not have
   // proper XML escapes.
   // proper XML escapes.
   std::string range = this->OldRevision + ":" + this->NewRevision;
   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"
                               "   revision=\"{node|short}\">\n"
                               "  <author>{author|person}</author>\n"
                               "  <author>{author|person}</author>\n"
                               "  <email>{author|email}</email>\n"
                               "  <email>{author|email}</email>\n"
@@ -283,8 +288,10 @@ bool cmCTestHG::LoadRevisions()
                               "  <file_adds>{file_adds}</file_adds>\n"
                               "  <file_adds>{file_adds}</file_adds>\n"
                               "  <file_dels>{file_dels}</file_dels>\n"
                               "  <file_dels>{file_dels}</file_dels>\n"
                               "</logentry>\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> ");
   LogParser out(this, "log-out> ");
   out.Process("<?xml version=\"1.0\"?>\n"
   out.Process("<?xml version=\"1.0\"?>\n"
@@ -298,8 +305,8 @@ bool cmCTestHG::LoadRevisions()
 bool cmCTestHG::LoadModifications()
 bool cmCTestHG::LoadModifications()
 {
 {
   // Use 'hg status' to get modified files.
   // 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> ");
   StatusParser out(this, "status-out> ");
   OutputLogger err(this->Log, "status-err> ");
   OutputLogger err(this->Log, "status-err> ");
   this->RunChild(hg_status, &out, &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.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunch.h"
 #include "cmCTestLaunch.h"
 
 
-#include <cstdio>
 #include <cstring>
 #include <cstring>
 #include <iostream>
 #include <iostream>
-#include <memory>
-#include <utility>
-
-#include <cm3p/uv.h>
 
 
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 
 #include "cmCTestLaunchReporter.h"
 #include "cmCTestLaunchReporter.h"
@@ -21,9 +17,6 @@
 #include "cmStateSnapshot.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmake.h"
 #include "cmake.h"
 
 
 #ifdef _WIN32
 #ifdef _WIN32
@@ -35,6 +28,8 @@
 
 
 cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
 cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
 {
 {
+  this->Process = nullptr;
+
   if (!this->ParseArguments(argc, argv)) {
   if (!this->ParseArguments(argc, argv)) {
     return;
     return;
   }
   }
@@ -45,9 +40,13 @@ cmCTestLaunch::cmCTestLaunch(int argc, const char* const* argv)
   this->ScrapeRulesLoaded = false;
   this->ScrapeRulesLoaded = false;
   this->HaveOut = false;
   this->HaveOut = false;
   this->HaveErr = 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)
 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.
   // Extract the real command line.
   if (arg0) {
   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;
     return true;
   }
   }
+  this->RealArgC = 0;
+  this->RealArgV = nullptr;
   std::cerr << "No launch/command separator ('--') found!\n";
   std::cerr << "No launch/command separator ('--') found!\n";
   return false;
   return false;
 }
 }
@@ -149,19 +151,17 @@ void cmCTestLaunch::RunChild()
   }
   }
 
 
   // Prepare to run the real command.
   // 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 fout;
   cmsys::ofstream ferr;
   cmsys::ofstream ferr;
   if (this->Reporter.Passthru) {
   if (this->Reporter.Passthru) {
     // In passthru mode we just share the output pipes.
     // 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 {
   } else {
     // In full mode we record the child output pipes to log files.
     // 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);
     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);
     ferr.open(this->Reporter.LogErr.c_str(), std::ios::out | std::ios::binary);
   }
   }
@@ -174,65 +174,51 @@ void cmCTestLaunch::RunChild()
 #endif
 #endif
 
 
   // Run the real command.
   // Run the real command.
-  auto chain = builder.Start();
+  cmsysProcess_Execute(cp);
 
 
   // Record child stdout and stderr if necessary.
   // 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) {
   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.
   // 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()
 int cmCTestLaunch::Run()
 {
 {
+  if (!this->Process) {
+    std::cerr << "Could not allocate cmsysProcess instance!\n";
+    return -1;
+  }
+
   this->RunChild();
   this->RunChild();
 
 
   if (this->CheckResults()) {
   if (this->CheckResults()) {
@@ -240,6 +226,7 @@ int cmCTestLaunch::Run()
   }
   }
 
 
   this->LoadConfig();
   this->LoadConfig();
+  this->Reporter.Process = this->Process;
   this->Reporter.WriteXML();
   this->Reporter.WriteXML();
 
 
   return this->Reporter.ExitCode;
   return this->Reporter.ExitCode;

+ 4 - 1
Source/CTest/cmCTestLaunch.h

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

+ 30 - 20
Source/CTest/cmCTestLaunchReporter.cxx

@@ -2,9 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmCTestLaunchReporter.h"
 #include "cmCTestLaunchReporter.h"
 
 
-#include <utility>
-
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 
 
 #include "cmCryptoHash.h"
 #include "cmCryptoHash.h"
@@ -23,7 +22,6 @@
 cmCTestLaunchReporter::cmCTestLaunchReporter()
 cmCTestLaunchReporter::cmCTestLaunchReporter()
 {
 {
   this->Passthru = true;
   this->Passthru = true;
-  this->Status.Finished = true;
   this->ExitCode = 1;
   this->ExitCode = 1;
   this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
   this->CWD = cmSystemTools::GetCurrentWorkingDirectory();
 
 
@@ -233,23 +231,35 @@ void cmCTestLaunchReporter::WriteXMLResult(cmXMLElement& e2)
 
 
   // ExitCondition
   // ExitCondition
   cmXMLElement e4(e3, "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 "cmsys/RegularExpression.hxx"
 
 
-#include "cmUVProcessChain.h"
-
 class cmXMLElement;
 class cmXMLElement;
 
 
 /** \class cmCTestLaunchReporter
 /** \class cmCTestLaunchReporter
@@ -50,7 +48,7 @@ public:
   void ComputeFileNames();
   void ComputeFileNames();
 
 
   bool Passthru;
   bool Passthru;
-  cmUVProcessChain::Status Status;
+  struct cmsysProcess_s* Process;
   int ExitCode;
   int ExitCode;
 
 
   // Temporary log files for stdout and stderr of real command.
   // 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);
   auto it = this->Users.find(username);
 
 
   if (it == this->Users.end()) {
   if (it == this->Users.end()) {
-    std::vector<std::string> p4_users;
+    std::vector<char const*> p4_users;
     this->SetP4Options(p4_users);
     this->SetP4Options(p4_users);
     p4_users.push_back("users");
     p4_users.push_back("users");
     p4_users.push_back("-m");
     p4_users.push_back("-m");
     p4_users.push_back("1");
     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> ");
     UserParser out(this, "users-out> ");
     OutputLogger err(this->Log, "users-err> ");
     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.
     // The user should now be added to the map. Search again.
     it = this->Users.find(username);
     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()) {
   if (this->P4Options.empty()) {
-    std::string p4 = this->CommandLineTool;
+    const char* p4 = this->CommandLineTool.c_str();
     this->P4Options.emplace_back(p4);
     this->P4Options.emplace_back(p4);
 
 
     // The CTEST_P4_CLIENT variable sets the P4 client used when issuing
     // 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));
     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::string cmCTestP4::GetWorkingRevision()
 {
 {
-  std::vector<std::string> p4_identify;
+  std::vector<char const*> p4_identify;
   this->SetP4Options(p4_identify);
   this->SetP4Options(p4_identify);
 
 
   p4_identify.push_back("changes");
   p4_identify.push_back("changes");
@@ -341,13 +345,14 @@ std::string cmCTestP4::GetWorkingRevision()
   p4_identify.push_back("-t");
   p4_identify.push_back("-t");
 
 
   std::string source = this->SourceDirectory + "/...#have";
   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;
   std::string rev;
   IdentifyParser out(this, "p4_changes-out> ", rev);
   IdentifyParser out(this, "p4_changes-out> ", rev);
   OutputLogger err(this->Log, "p4_changes-err> ");
   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 there was a problem contacting the server return "<unknown>"
   if (!result) {
   if (!result) {
@@ -383,7 +388,7 @@ bool cmCTestP4::NoteNewRevision()
 
 
 bool cmCTestP4::LoadRevisions()
 bool cmCTestP4::LoadRevisions()
 {
 {
-  std::vector<std::string> p4_changes;
+  std::vector<char const*> p4_changes;
   this->SetP4Options(p4_changes);
   this->SetP4Options(p4_changes);
 
 
   // Use 'p4 changes ...@old,new' to get a list of changelists
   // Use 'p4 changes ...@old,new' to get a list of changelists
@@ -404,36 +409,38 @@ bool cmCTestP4::LoadRevisions()
     .append(this->NewRevision);
     .append(this->NewRevision);
 
 
   p4_changes.push_back("changes");
   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> ");
   ChangesParser out(this, "p4_changes-out> ");
   OutputLogger err(this->Log, "p4_changes-err> ");
   OutputLogger err(this->Log, "p4_changes-err> ");
 
 
   this->ChangeLists.clear();
   this->ChangeLists.clear();
-  this->RunChild(p4_changes, &out, &err);
+  this->RunChild(p4_changes.data(), &out, &err);
 
 
   if (this->ChangeLists.empty()) {
   if (this->ChangeLists.empty()) {
     return true;
     return true;
   }
   }
 
 
   // p4 describe -s ...@1111111,2222222
   // 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)) {
   for (std::string const& i : cmReverseRange(this->ChangeLists)) {
     this->SetP4Options(p4_describe);
     this->SetP4Options(p4_describe);
     p4_describe.push_back("describe");
     p4_describe.push_back("describe");
     p4_describe.push_back("-s");
     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> ");
     DescribeParser outDescribe(this, "p4_describe-out> ");
     OutputLogger errDescribe(this->Log, "p4_describe-err> ");
     OutputLogger errDescribe(this->Log, "p4_describe-err> ");
-    this->RunChild(p4_describe, &outDescribe, &errDescribe);
+    this->RunChild(p4_describe.data(), &outDescribe, &errDescribe);
   }
   }
   return true;
   return true;
 }
 }
 
 
 bool cmCTestP4::LoadModifications()
 bool cmCTestP4::LoadModifications()
 {
 {
-  std::vector<std::string> p4_diff;
+  std::vector<char const*> p4_diff;
   this->SetP4Options(p4_diff);
   this->SetP4Options(p4_diff);
 
 
   p4_diff.push_back("diff");
   p4_diff.push_back("diff");
@@ -441,11 +448,12 @@ bool cmCTestP4::LoadModifications()
   // Ideally we would use -Od but not all clients support it
   // Ideally we would use -Od but not all clients support it
   p4_diff.push_back("-dn");
   p4_diff.push_back("-dn");
   std::string source = this->SourceDirectory + "/...";
   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> ");
   DiffParser out(this, "p4_diff-out> ");
   OutputLogger err(this->Log, "p4_diff-err> ");
   OutputLogger err(this->Log, "p4_diff-err> ");
-  this->RunChild(p4_diff, &out, &err);
+  this->RunChild(p4_diff.data(), &out, &err);
   return true;
   return true;
 }
 }
 
 
@@ -453,14 +461,17 @@ bool cmCTestP4::UpdateCustom(const std::string& custom)
 {
 {
   cmList p4_custom_command{ custom, cmList::EmptyElements::Yes };
   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_out(this->Log, "p4_customsync-out> ");
   OutputLogger custom_err(this->Log, "p4_customsync-err> ");
   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()
 bool cmCTestP4::UpdateImpl()
@@ -477,7 +488,7 @@ bool cmCTestP4::UpdateImpl()
     return false;
     return false;
   }
   }
 
 
-  std::vector<std::string> p4_sync;
+  std::vector<char const*> p4_sync;
   this->SetP4Options(p4_sync);
   this->SetP4Options(p4_sync);
 
 
   p4_sync.push_back("sync");
   p4_sync.push_back("sync");
@@ -488,7 +499,9 @@ bool cmCTestP4::UpdateImpl()
     opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
     opts = this->CTest->GetCTestConfiguration("P4UpdateOptions");
   }
   }
   std::vector<std::string> args = cmSystemTools::ParseArguments(opts);
   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 + "/...";
   std::string source = this->SourceDirectory + "/...";
 
 
@@ -502,10 +515,11 @@ bool cmCTestP4::UpdateImpl()
     source.append("@\"").append(date).append("\"");
     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 out(this->Log, "p4_sync-out> ");
   OutputLogger err(this->Log, "p4_sync-err> ");
   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;
   std::vector<std::string> P4Options;
 
 
   User GetUserData(const std::string& username);
   User GetUserData(const std::string& username);
-  void SetP4Options(std::vector<std::string>& options);
+  void SetP4Options(std::vector<char const*>& options);
 
 
   std::string GetWorkingRevision();
   std::string GetWorkingRevision();
   bool NoteOldRevision() override;
   bool NoteOldRevision() override;

+ 21 - 15
Source/CTest/cmCTestSVN.cxx

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

+ 1 - 1
Source/CTest/cmCTestSVN.h

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

+ 41 - 43
Source/CTest/cmCTestScriptHandler.cxx

@@ -11,9 +11,8 @@
 
 
 #include <cm/memory>
 #include <cm/memory>
 
 
-#include <cm3p/uv.h>
-
 #include "cmsys/Directory.hxx"
 #include "cmsys/Directory.hxx"
+#include "cmsys/Process.h"
 
 
 #include "cmCTest.h"
 #include "cmCTest.h"
 #include "cmCTestBuildCommand.h"
 #include "cmCTestBuildCommand.h"
@@ -41,8 +40,6 @@
 #include "cmStateSnapshot.h"
 #include "cmStateSnapshot.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmValue.h"
 #include "cmake.h"
 #include "cmake.h"
 
 
@@ -151,65 +148,66 @@ int cmCTestScriptHandler::ExecuteScript(const std::string& total_script_arg)
   // now pass through all the other arguments
   // now pass through all the other arguments
   std::vector<std::string>& initArgs =
   std::vector<std::string>& initArgs =
     this->CTest->GetInitialCommandLineArguments();
     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
   // 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> out;
   std::vector<char> err;
   std::vector<char> err;
   std::string line;
   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,
     cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT,
                "Output: " << line << "\n");
                "Output: " << line << "\n");
-    if (pipe == cmSystemTools::WaitForLineResult::STDERR) {
+    if (pipe == cmsysProcess_Pipe_STDERR) {
       cmCTestLog(this->CTest, ERROR_MESSAGE, line << "\n");
       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");
       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
   // 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;
   int retVal = 0;
   bool failed = false;
   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) {
   if (failed) {
     std::ostringstream message;
     std::ostringstream message;
     message << "Error running command: [";
     message << "Error running command: [";
-    message << static_cast<int>(result.first) << "] ";
+    message << result << "] ";
     for (const char* arg : argv) {
     for (const char* arg : argv) {
       if (arg) {
       if (arg) {
         message << arg << " ";
         message << arg << " ";

+ 26 - 19
Source/CTest/cmCTestVC.cxx

@@ -7,9 +7,10 @@
 #include <sstream>
 #include <sstream>
 #include <vector>
 #include <vector>
 
 
+#include "cmsys/Process.h"
+
 #include "cmCTest.h"
 #include "cmCTest.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVProcessChain.h"
 #include "cmValue.h"
 #include "cmValue.h"
 #include "cmXMLWriter.h"
 #include "cmXMLWriter.h"
 
 
@@ -54,12 +55,18 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
 
 
   // Construct the initial checkout command line.
   // Construct the initial checkout command line.
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
   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.
   // Run the initial checkout command and log its output.
   this->Log << "--- Begin Initial Checkout ---\n";
   this->Log << "--- Begin Initial Checkout ---\n";
   OutputLogger out(this->Log, "co-out> ");
   OutputLogger out(this->Log, "co-out> ");
   OutputLogger err(this->Log, "co-err> ");
   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";
   this->Log << "--- End Initial Checkout ---\n";
   if (!result) {
   if (!result) {
     cmCTestLog(this->CTest, ERROR_MESSAGE,
     cmCTestLog(this->CTest, ERROR_MESSAGE,
@@ -68,35 +75,35 @@ bool cmCTestVC::InitialCheckout(const std::string& command)
   return result;
   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";
   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;
   std::ostringstream line;
   const char* sep = "";
   const char* sep = "";
-  for (auto const& arg : cmd) {
-    line << sep << "\"" << arg << "\"";
+  for (const char* const* arg = cmd; *arg; ++arg) {
+    line << sep << "\"" << *arg << "\"";
     sep = " ";
     sep = " ";
   }
   }
   return line.str();
   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.
   // Report the command line.
   this->UpdateCommandLine = this->ComputeCommandLine(cmd);
   this->UpdateCommandLine = this->ComputeCommandLine(cmd);
@@ -106,7 +113,7 @@ bool cmCTestVC::RunUpdateCommand(const std::vector<std::string>& cmd,
   }
   }
 
 
   // Run the command.
   // Run the command.
-  return this->RunChild(cmd, out, err, "", encoding);
+  return this->RunChild(cmd, out, err, nullptr, encoding);
 }
 }
 
 
 std::string cmCTestVC::GetNightlyTime()
 std::string cmCTestVC::GetNightlyTime()

+ 4 - 5
Source/CTest/cmCTestVC.h

@@ -6,7 +6,6 @@
 
 
 #include <iosfwd>
 #include <iosfwd>
 #include <string>
 #include <string>
-#include <vector>
 
 
 #include "cmProcessOutput.h"
 #include "cmProcessOutput.h"
 #include "cmProcessTools.h"
 #include "cmProcessTools.h"
@@ -109,15 +108,15 @@ protected:
   };
   };
 
 
   /** Convert a list of arguments to a human-readable command line.  */
   /** 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.  */
   /** 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);
                 Encoding encoding = cmProcessOutput::Auto);
 
 
   /** Run VC update command line and send output to given parsers.  */
   /** 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,
                         OutputParser* err = nullptr,
                         Encoding encoding = cmProcessOutput::Auto);
                         Encoding encoding = cmProcessOutput::Auto);
 
 

+ 0 - 4
Source/LexerParser/cmCTestResourceGroupsLexer.cxx

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

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

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

+ 198 - 231
Source/cmCTest.cxx

@@ -5,7 +5,6 @@
 #include <algorithm>
 #include <algorithm>
 #include <cctype>
 #include <cctype>
 #include <chrono>
 #include <chrono>
-#include <cstdint>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
 #include <cstring>
 #include <cstring>
@@ -25,13 +24,13 @@
 #include <cmext/string_view>
 #include <cmext/string_view>
 
 
 #include <cm3p/curl/curl.h>
 #include <cm3p/curl/curl.h>
-#include <cm3p/uv.h>
 #include <cm3p/zlib.h>
 #include <cm3p/zlib.h>
 
 
 #include "cmsys/Base64.h"
 #include "cmsys/Base64.h"
 #include "cmsys/Directory.hxx"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/Glob.hxx"
 #include "cmsys/Glob.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/SystemInformation.hxx"
 #include "cmsys/SystemInformation.hxx"
 #if defined(_WIN32)
 #if defined(_WIN32)
@@ -65,9 +64,6 @@
 #include "cmStateTypes.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmValue.h"
 #include "cmVersion.h"
 #include "cmVersion.h"
 #include "cmVersionConfig.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
   // First generate the command and arguments
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
   std::vector<std::string> args = cmSystemTools::ParseArguments(command);
@@ -1088,107 +1084,107 @@ bool cmCTest::RunMakeCommand(const std::string& command, std::string& output,
     return false;
     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();
   output.clear();
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, "Run command:");
   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, " \"" << arg << "\"");
   }
   }
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT, std::endl);
 
 
   // Now create process object
   // 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
   // Initialize tick's
   std::string::size_type tick = 0;
   std::string::size_type tick = 0;
   std::string::size_type tick_len = 1024;
   std::string::size_type tick_len = 1024;
   std::string::size_type tick_line_len = 50;
   std::string::size_type tick_line_len = 50;
 
 
+  char* data;
+  int length;
   cmProcessOutput processOutput(encoding);
   cmProcessOutput processOutput(encoding);
+  std::string strdata;
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              "   Each . represents " << tick_len
              "   Each . represents " << tick_len
                                      << " bytes of output\n"
                                      << " bytes of output\n"
                                         "    "
                                         "    "
                                      << std::flush);
                                      << 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,
   cmCTestLog(this, HANDLER_PROGRESS_OUTPUT,
              " Size of output: " << int(double(output.size()) / 1024.0) << "K"
              " Size of output: " << int(double(output.size()) / 1024.0) << "K"
                                  << std::endl);
                                  << 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);
     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());
   bool modifyEnv = (environment && !environment->empty());
 
 
@@ -1238,16 +1233,19 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
     inst.SetStreams(&oss, &oss);
     inst.SetStreams(&oss, &oss);
 
 
     std::vector<std::string> args;
     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) {
     if (log) {
       *log << "* Run internal CTest" << std::endl;
       *log << "* Run internal CTest" << std::endl;
@@ -1273,7 +1271,7 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
                                                              << std::endl);
                                                              << std::endl);
     }
     }
 
 
-    return true;
+    return cmsysProcess_State_Exited;
   }
   }
   std::vector<char> tempOutput;
   std::vector<char> tempOutput;
   if (output) {
   if (output) {
@@ -1286,43 +1284,41 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
     cmSystemTools::AppendEnv(*environment);
     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);
   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);
   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);
   processOutput.DecodeText(tempOutput, tempOutput);
   if (output && tempOutput.begin() != tempOutput.end()) {
   if (output && tempOutput.begin() != tempOutput.end()) {
     output->append(tempOutput.data(), tempOutput.size());
     output->append(tempOutput.data(), tempOutput.size());
@@ -1330,41 +1326,33 @@ bool cmCTest::RunTest(const std::vector<std::string>& argv,
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
   cmCTestLog(this, HANDLER_VERBOSE_OUTPUT,
              "-- Process completed" << std::endl);
              "-- 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;
   return result;
 }
 }
@@ -3482,70 +3470,49 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
   stdOut->clear();
   stdOut->clear();
   stdErr->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;
   std::vector<char> tempOutput;
-  bool outFinished = false;
-  cm::uv_pipe_ptr outStream;
   std::vector<char> tempError;
   std::vector<char> tempError;
-  bool errFinished = false;
-  cm::uv_pipe_ptr errStream;
+  char* data;
+  int length;
   cmProcessOutput processOutput(encoding);
   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) {
   if (this->Impl->ExtraVerbose) {
-    std::string strdata;
     processOutput.DecodeText(std::string(), strdata);
     processOutput.DecodeText(std::string(), strdata);
     if (!strdata.empty()) {
     if (!strdata.empty()) {
       cmSystemTools::Stdout(strdata);
       cmSystemTools::Stdout(strdata);
     }
     }
   }
   }
 
 
-  while (!timedOut && !chain.Finished()) {
-    uv_run(&chain.GetLoop(), UV_RUN_ONCE);
-  }
+  cmsysProcess_WaitForExit(cp, nullptr);
   if (!tempOutput.empty()) {
   if (!tempOutput.empty()) {
     processOutput.DecodeText(tempOutput, tempOutput);
     processOutput.DecodeText(tempOutput, tempOutput);
     stdOut->append(tempOutput.data(), tempOutput.size());
     stdOut->append(tempOutput.data(), tempOutput.size());
@@ -3556,32 +3523,32 @@ bool cmCTest::RunCommand(std::vector<std::string> const& args,
   }
   }
 
 
   bool result = true;
   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";
     const char* error_str = "Process terminated due to timeout\n";
     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
     cmCTestLog(this, ERROR_MESSAGE, error_str << std::endl);
     stdErr->append(error_str, strlen(error_str));
     stdErr->append(error_str, strlen(error_str));
     result = false;
     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;
   return result;
 }
 }
 
 

+ 8 - 8
Source/cmCTest.h

@@ -254,10 +254,10 @@ public:
    * Run command specialized for make and configure. Returns process status
    * Run command specialized for make and configure. Returns process status
    * and retVal is return value or exception.
    * 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 */
   /** Return the current tag */
   std::string GetCurrentTag();
   std::string GetCurrentTag();
@@ -303,10 +303,10 @@ public:
    * environment variables prior to running the test. After running the test,
    * environment variables prior to running the test. After running the test,
    * environment variables are restored to their previous values.
    * 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
    * Get the handler object

+ 195 - 204
Source/cmExecuteProcessCommand.cxx

@@ -2,8 +2,8 @@
    file Copyright.txt or https://cmake.org/licensing for details.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmExecuteProcessCommand.h"
 #include "cmExecuteProcessCommand.h"
 
 
+#include <algorithm>
 #include <cctype> /* isspace */
 #include <cctype> /* isspace */
-#include <cstdint>
 #include <cstdio>
 #include <cstdio>
 #include <iostream>
 #include <iostream>
 #include <map>
 #include <map>
@@ -16,7 +16,7 @@
 #include <cmext/algorithm>
 #include <cmext/algorithm>
 #include <cmext/string_view>
 #include <cmext/string_view>
 
 
-#include <cm3p/uv.h>
+#include "cmsys/Process.h"
 
 
 #include "cmArgumentParser.h"
 #include "cmArgumentParser.h"
 #include "cmExecutionStatus.h"
 #include "cmExecutionStatus.h"
@@ -26,9 +26,6 @@
 #include "cmProcessOutput.h"
 #include "cmProcessOutput.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
 #include "cmSystemTools.h"
 #include "cmSystemTools.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 
 
 namespace {
 namespace {
 bool cmExecuteProcessCommandIsWhitespace(char c)
 bool cmExecuteProcessCommandIsWhitespace(char c)
@@ -39,7 +36,7 @@ bool cmExecuteProcessCommandIsWhitespace(char c)
 void cmExecuteProcessCommandFixText(std::vector<char>& output,
 void cmExecuteProcessCommandFixText(std::vector<char>& output,
                                     bool strip_trailing_whitespace);
                                     bool strip_trailing_whitespace);
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   std::size_t length);
+                                   int length);
 }
 }
 
 
 // cmExecuteProcessCommand
 // cmExecuteProcessCommand
@@ -164,68 +161,57 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     }
     }
   }
   }
   // Create a process instance.
   // 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.
   // Set the command sequence.
   for (std::vector<std::string> const& cmd : arguments.Commands) {
   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.
   // Set the process working directory.
   if (!arguments.WorkingDirectory.empty()) {
   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 {
     } 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.
   // 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_stdout = false;
   bool echo_stderr = false;
   bool echo_stderr = false;
@@ -273,86 +259,36 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
     }
     }
   }
   }
   // Start the process.
   // 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.
   // 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 processOutput(
     cmProcessOutput::FindEncoding(arguments.Encoding));
     cmProcessOutput::FindEncoding(arguments.Encoding));
   std::string strdata;
   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 &&
   if (!arguments.OutputQuiet &&
       (arguments.OutputVariable.empty() || arguments.EchoOutputVariable)) {
       (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.
   // Fix the text in the output strings.
-  cmExecuteProcessCommandFixText(outputData.Output,
+  cmExecuteProcessCommandFixText(tempOutput,
                                  arguments.OutputStripTrailingWhitespace);
                                  arguments.OutputStripTrailingWhitespace);
-  cmExecuteProcessCommandFixText(errorData.Output,
+  cmExecuteProcessCommandFixText(tempError,
                                  arguments.ErrorStripTrailingWhitespace);
                                  arguments.ErrorStripTrailingWhitespace);
 
 
   // Store the output obtained.
   // Store the output obtained.
-  if (!arguments.OutputVariable.empty() && !outputData.Output.empty()) {
+  if (!arguments.OutputVariable.empty() && !tempOutput.empty()) {
     status.GetMakefile().AddDefinition(arguments.OutputVariable,
     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,
     status.GetMakefile().AddDefinition(arguments.ErrorVariable,
-                                       errorData.Output.data());
+                                       tempError.data());
   }
   }
 
 
   // Store the result of running the process.
   // Store the result of running the process.
   if (!arguments.ResultVariable.empty()) {
   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(
         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,
         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.
   // Store the result of running the processes.
   if (!arguments.ResultsVariable.empty()) {
   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) {
   if (arguments.CommandErrorIsFatal == "ANY"_s) {
     bool ret = true;
     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;
         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) {
     if (!ret) {
@@ -475,23 +460,29 @@ bool cmExecuteProcessCommand(std::vector<std::string> const& args,
 
 
   if (arguments.CommandErrorIsFatal == "LAST"_s) {
   if (arguments.CommandErrorIsFatal == "LAST"_s) {
     bool ret = true;
     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);
         int lastIndex = static_cast<int>(arguments.Commands.size() - 1);
         const std::string processStatus = queryProcessStatusByIndex(lastIndex);
         const std::string processStatus = queryProcessStatusByIndex(lastIndex);
         if (!processStatus.empty()) {
         if (!processStatus.empty()) {
           status.SetError("last command failed");
           status.SetError("last command failed");
           ret = false;
           ret = false;
         }
         }
-      }
+      } break;
+      case cmsysProcess_State_Exception:
+        status.SetError(
+          cmStrCat("Abnormal exit: ", cmsysProcess_GetExceptionString(cp)));
+        ret = false;
+        break;
+      case cmsysProcess_State_Error:
+        status.SetError(cmStrCat("Error getting child return code: ",
+                                 cmsysProcess_GetErrorString(cp)));
+        ret = false;
+        break;
+      case cmsysProcess_State_Expired:
+        status.SetError("Process terminated due to timeout");
+        ret = false;
+        break;
     }
     }
     if (!ret) {
     if (!ret) {
       cmSystemTools::SetFatalErrorOccurred();
       cmSystemTools::SetFatalErrorOccurred();
@@ -534,7 +525,7 @@ void cmExecuteProcessCommandFixText(std::vector<char>& output,
 }
 }
 
 
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
 void cmExecuteProcessCommandAppend(std::vector<char>& output, const char* data,
-                                   std::size_t length)
+                                   int length)
 {
 {
 #if defined(__APPLE__)
 #if defined(__APPLE__)
   // HACK on Apple to work around bug with inserting at the
   // 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.  */
    file Copyright.txt or https://cmake.org/licensing for details.  */
 #include "cmProcessTools.h"
 #include "cmProcessTools.h"
 
 
-#include <algorithm>
-#include <iterator>
 #include <ostream>
 #include <ostream>
 
 
-#include <cm3p/uv.h>
+#include "cmsys/Process.h"
 
 
 #include "cmProcessOutput.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);
   cmProcessOutput processOutput(encoding);
-
-  builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
-
-  auto chain = builder.Start();
-
   std::string strdata;
   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)
 cmProcessTools::LineParser::LineParser(char sep, bool ignoreCR)

+ 3 - 5
Source/cmProcessTools.h

@@ -7,10 +7,8 @@
 #include <cstring>
 #include <cstring>
 #include <iosfwd>
 #include <iosfwd>
 #include <string>
 #include <string>
-#include <vector>
 
 
 #include "cmProcessOutput.h"
 #include "cmProcessOutput.h"
-#include "cmUVProcessChain.h"
 
 
 /** \class cmProcessTools
 /** \class cmProcessTools
  * \brief Helper classes for process output parsing
  * \brief Helper classes for process output parsing
@@ -83,7 +81,7 @@ public:
   };
   };
 
 
   /** Run a process and send output to given parsers.  */
   /** 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 "cmProcessOutput.h"
 #include "cmRange.h"
 #include "cmRange.h"
 #include "cmStringAlgorithms.h"
 #include "cmStringAlgorithms.h"
-#include "cmUVHandlePtr.h"
-#include "cmUVProcessChain.h"
-#include "cmUVStream.h"
 #include "cmValue.h"
 #include "cmValue.h"
 
 
 #if !defined(CMAKE_BOOTSTRAP)
 #if !defined(CMAKE_BOOTSTRAP)
@@ -62,14 +59,12 @@
 #include <cassert>
 #include <cassert>
 #include <cctype>
 #include <cctype>
 #include <cerrno>
 #include <cerrno>
-#include <cstdint>
 #include <cstdio>
 #include <cstdio>
 #include <cstdlib>
 #include <cstdlib>
 #include <cstring>
 #include <cstring>
 #include <ctime>
 #include <ctime>
 #include <functional>
 #include <functional>
 #include <iostream>
 #include <iostream>
-#include <memory>
 #include <sstream>
 #include <sstream>
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
@@ -573,111 +568,85 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
                                      const char* dir, OutputOption outputflag,
                                      const char* dir, OutputOption outputflag,
                                      cmDuration timeout, Encoding encoding)
                                      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) {
   if (outputflag == OUTPUT_PASSTHROUGH) {
+    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDOUT, 1);
+    cmsysProcess_SetPipeShared(cp, cmsysProcess_Pipe_STDERR, 1);
     captureStdOut = nullptr;
     captureStdOut = nullptr;
     captureStdErr = nullptr;
     captureStdErr = nullptr;
-    builder.SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, stdout)
-      .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, stderr);
   } else if (outputflag == OUTPUT_MERGE ||
   } else if (outputflag == OUTPUT_MERGE ||
              (captureStdErr && captureStdErr == captureStdOut)) {
              (captureStdErr && captureStdErr == captureStdOut)) {
-    builder.SetMergedBuiltinStreams();
+    cmsysProcess_SetOption(cp, cmsysProcess_Option_MergeOutput, 1);
     captureStdErr = nullptr;
     captureStdErr = nullptr;
-  } else {
-    builder.SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
-      .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
   }
   }
   assert(!captureStdErr || captureStdErr != captureStdOut);
   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> tempStdOut;
   std::vector<char> tempStdErr;
   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);
   cmProcessOutput processOutput(encoding);
-  std::unique_ptr<cmUVStreamReadHandle> outputHandle;
-  std::unique_ptr<cmUVStreamReadHandle> errorHandle;
+  std::string strdata;
   if (outputflag != OUTPUT_PASSTHROUGH &&
   if (outputflag != OUTPUT_PASSTHROUGH &&
       (captureStdOut || captureStdErr || outputflag != OUTPUT_NONE)) {
       (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) {
   if (captureStdOut) {
     captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
     captureStdOut->assign(tempStdOut.begin(), tempStdOut.end());
@@ -689,43 +658,48 @@ bool cmSystemTools::RunSingleCommand(std::vector<std::string> const& command,
   }
   }
 
 
   bool result = true;
   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) {
     if (outputflag != OUTPUT_NONE) {
       std::cerr << error_str << std::endl;
       std::cerr << error_str << std::endl;
     }
     }
     if (captureStdErr) {
     if (captureStdErr) {
       captureStdErr->append(error_str, strlen(error_str));
       captureStdErr->append(error_str, strlen(error_str));
+    } else if (captureStdOut) {
+      captureStdOut->append(error_str, strlen(error_str));
     }
     }
     result = false;
     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;
   return result;
 }
 }
 
 
@@ -2239,10 +2213,9 @@ bool cmSystemTools::ListTar(const std::string& outFileName,
 #endif
 #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();
   line.clear();
   auto outiter = out.begin();
   auto outiter = out.begin();
@@ -2264,7 +2237,7 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
           line.append(out.data(), length);
           line.append(out.data(), length);
         }
         }
         out.erase(out.begin(), outiter + 1);
         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);
           line.append(err.data(), length);
         }
         }
         err.erase(err.begin(), erriter + 1);
         err.erase(err.begin(), erriter + 1);
-        return WaitForLineResult::STDERR;
+        return cmsysProcess_Pipe_STDERR;
       }
       }
     }
     }
 
 
     // No newlines found.  Wait for more data from the process.
     // 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.
       // 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.
       // Append to the stdout buffer.
       std::vector<char>::size_type size = out.size();
       std::vector<char>::size_type size = out.size();
       cm::append(out, strdata);
       cm::append(out, strdata);
       outiter = out.begin() + size;
       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.
       // Append to the stderr buffer.
       std::vector<char>::size_type size = err.size();
       std::vector<char>::size_type size = err.size();
       cm::append(err, strdata);
       cm::append(err, strdata);
       erriter = err.begin() + size;
       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.
       // Both stdout and stderr pipes have broken.  Return leftover data.
       processOutput.DecodeText(std::string(), strdata, 1);
       processOutput.DecodeText(std::string(), strdata, 1);
       if (!strdata.empty()) {
       if (!strdata.empty()) {
@@ -2358,20 +2298,14 @@ cmSystemTools::WaitForLineResult cmSystemTools::WaitForLine(
       if (!out.empty()) {
       if (!out.empty()) {
         line.append(out.data(), outiter - out.begin());
         line.append(out.data(), outiter - out.begin());
         out.erase(out.begin(), out.end());
         out.erase(out.begin(), out.end());
-        return WaitForLineResult::STDOUT;
+        return cmsysProcess_Pipe_STDOUT;
       }
       }
       if (!err.empty()) {
       if (!err.empty()) {
         line.append(err.data(), erriter - err.begin());
         line.append(err.data(), erriter - err.begin());
         err.erase(err.begin(), err.end());
         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/optional>
 #include <cm/string_view>
 #include <cm/string_view>
 
 
-#include <cm3p/uv.h>
-
+#include "cmsys/Process.h"
 #include "cmsys/Status.hxx"      // IWYU pragma: export
 #include "cmsys/Status.hxx"      // IWYU pragma: export
 #include "cmsys/SystemTools.hxx" // IWYU pragma: export
 #include "cmsys/SystemTools.hxx" // IWYU pragma: export
 
 
@@ -340,20 +339,10 @@ public:
    */
    */
   static void ReportLastSystemError(const char* m);
   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 void SetForceUnixPaths(bool v) { s_ForceUnixPaths = v; }
   static bool GetForceUnixPaths() { return s_ForceUnixPaths; }
   static bool GetForceUnixPaths() { return s_ForceUnixPaths; }

+ 58 - 39
Source/cmcmd.cxx

@@ -72,6 +72,7 @@
 
 
 #include "cmsys/Directory.hxx"
 #include "cmsys/Directory.hxx"
 #include "cmsys/FStream.hxx"
 #include "cmsys/FStream.hxx"
+#include "cmsys/Process.h"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/RegularExpression.hxx"
 #include "cmsys/Terminal.h"
 #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());
   cmsys::ofstream fout(depFile.c_str());
   if (!fout) {
   if (!fout) {
@@ -306,18 +313,22 @@ int CLCompileAndDependencies(const std::vector<std::string>& args)
   CLOutputLogger errLogger(std::cerr);
   CLOutputLogger errLogger(std::cerr);
 
 
   // Start the process.
   // Start the process.
-  auto result =
-    cmProcessTools::RunProcess(builder, &includeParser, &errLogger);
-  auto const& subStatus = result.front();
+  cmProcessTools::RunProcess(cp.get(), &includeParser, &errLogger);
 
 
   int status = 0;
   int status = 0;
   // handle status of process
   // 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) {
   if (status != 0) {
@@ -1105,8 +1116,7 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
 
 
       int ret = 0;
       int ret = 0;
       auto time_start = std::chrono::steady_clock::now();
       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();
       auto time_finish = std::chrono::steady_clock::now();
 
 
       std::chrono::duration<double> time_elapsed = time_finish - time_start;
       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.
   // Read command lines from the script.
   cmsys::ifstream fin(args[2].c_str());
   cmsys::ifstream fin(args[2].c_str());
   if (!fin) {
   if (!fin) {
@@ -1897,21 +1922,9 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
       continue;
       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.
     // 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.
     // Report the command if verbose output is enabled.
     if (verbose) {
     if (verbose) {
@@ -1919,29 +1932,35 @@ int cmcmd::ExecuteLinkScript(std::vector<std::string> const& args)
     }
     }
 
 
     // Run the command and wait for it to exit.
     // 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.
     // 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;
         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;
         result = 2;
         break;
         break;
       default:
       default:
-        std::cerr << "Error running link command: " << exception.second;
-        result = 1;
         break;
         break;
     }
     }
   }
   }
 
 
+  // Free the process instance.
+  cmsysProcess_Delete(cp);
+
   // Return the final resulting return value.
   // Return the final resulting return value.
   return result;
   return result;
 }
 }

+ 0 - 4
Utilities/IWYU/mapping.imp

@@ -75,10 +75,6 @@
   { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] },
   { include: [ "<ostream>", public, "\"cmsys/FStream.hxx\"", public ] },
   { include: [ "<fstream>", 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: [ "<filesystem>", public, "<cm/filesystem>", public ] },
   { include: [ "<optional>", public, "<cm/optional>", public ] },
   { include: [ "<optional>", public, "<cm/optional>", public ] },
   { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] },
   { include: [ "<shared_mutex>", public, "<cm/shared_mutex>", public ] },