This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "CMake".

The branch, master has been updated
       via  f9feab40a195716a6bba11b39a809a18830df956 (commit)
       via  df6c4afa777098c6a2fcc01e4513ab5a6e444bc7 (commit)
       via  15004e431923035227bf502fe07c6bef2beb2d74 (commit)
       via  bd6c3f8609b87f6995acb2aef21aa572f0f73fa7 (commit)
       via  54903af84bc8656344e7fe1ea0a11d5f09e94f86 (commit)
       via  081104fb003a56bb3d8330363795ace5b71bc3f8 (commit)
       via  eff6e622d6cd66186ad42c9ec2ae6b2055008707 (commit)
       via  26025d6e106ffd4cd777fdc3a4343b33c8554c15 (commit)
       via  6b04d1cdc281b9b0dee5f59394a1c41d8b96c4a1 (commit)
      from  342936963479d59400a5f6ba8ef4de339212d303 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=f9feab40a195716a6bba11b39a809a18830df956
commit f9feab40a195716a6bba11b39a809a18830df956
Merge: df6c4af 15004e4
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Mon May 13 14:41:50 2019 +0000
Commit:     Kitware Robot <kwro...@kitware.com>
CommitDate: Mon May 13 10:42:02 2019 -0400

    Merge topic 'autorcc_timestamp'
    
    15004e4319 AutoRcc: Simplify error logging with utility lambda
    bd6c3f8609 AutoRcc: Rebuild if the rcc executable is newer than its output
    54903af84b AutoRcc: Don't read the info file time again
    081104fb00 AutoRcc: Write re-generation reason and rcc command as one string
    eff6e622d6 Autogen: A missing info file is a critical error
    
    Acked-by: Kitware Robot <kwro...@kitware.com>
    Merge-request: !3311


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=df6c4afa777098c6a2fcc01e4513ab5a6e444bc7
commit df6c4afa777098c6a2fcc01e4513ab5a6e444bc7
Merge: 3429369 26025d6
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Mon May 13 14:37:56 2019 +0000
Commit:     Kitware Robot <kwro...@kitware.com>
CommitDate: Mon May 13 10:38:09 2019 -0400

    Merge topic 'cmuvprocesschain'
    
    26025d6e10 cmUVProcessChain: Add cmUVProcessChain
    6b04d1cdc2 cmUVStreambuf: Initialize all members on construction
    
    Acked-by: Kitware Robot <kwro...@kitware.com>
    Merge-request: !3275


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=15004e431923035227bf502fe07c6bef2beb2d74
commit 15004e431923035227bf502fe07c6bef2beb2d74
Author:     Sebastian Holtermann <sebh...@xwmw.org>
AuthorDate: Sat May 11 21:39:41 2019 +0200
Commit:     Sebastian Holtermann <sebh...@xwmw.org>
CommitDate: Sat May 11 22:15:47 2019 +0200

    AutoRcc: Simplify error logging with utility lambda

diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index 922767d..7ac7339 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -3,6 +3,8 @@
 #include "cmQtAutoRcc.h"
 #include "cmQtAutoGen.h"
 
+#include <sstream>
+
 #include "cmAlgorithms.h"
 #include "cmCryptoHash.h"
 #include "cmDuration.h"
@@ -49,11 +51,16 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile)
     cmSystemTools::ExpandListArgument(InfoGetConfig(key), list);
     return list;
   };
+  auto LogInfoError = [this](std::string const& msg) -> bool {
+    std::ostringstream err;
+    err << "In " << Quoted(this->InfoFile()) << ":\n" << msg;
+    this->Log().Error(GenT::RCC, err.str());
+    return false;
+  };
 
   // -- Read info file
   if (!makefile->ReadListFile(InfoFile())) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "File processing failed.");
-    return false;
+    return LogInfoError("File processing failed.");
   }
 
   // - Configurations
@@ -63,14 +70,12 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile)
   // - Directories
   AutogenBuildDir_ = InfoGet("ARCC_BUILD_DIR");
   if (AutogenBuildDir_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Build directory empty.");
-    return false;
+    return LogInfoError("Build directory empty.");
   }
 
   IncludeDir_ = InfoGetConfig("ARCC_INCLUDE_DIR");
   if (IncludeDir_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Include directory empty.");
-    return false;
+    return LogInfoError("Include directory empty.");
   }
 
   // - Rcc executable
@@ -79,8 +84,7 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile)
     std::string error = "The rcc executable ";
     error += Quoted(RccExecutable_);
     error += " does not exist.";
-    Log().ErrorFile(GenT::RCC, InfoFile(), error);
-    return false;
+    return LogInfoError(error);
   }
   RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
 
@@ -99,28 +103,22 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile)
 
   // - Validity checks
   if (LockFile_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Lock file name missing.");
-    return false;
+    return LogInfoError("Lock file name missing.");
   }
   if (SettingsFile_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Settings file name missing.");
-    return false;
+    return LogInfoError("Settings file name missing.");
   }
   if (AutogenBuildDir_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "Autogen build directory missing.");
-    return false;
+    return LogInfoError("Autogen build directory missing.");
   }
   if (RccExecutable_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc executable missing.");
-    return false;
+    return LogInfoError("rcc executable missing.");
   }
   if (QrcFile_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc input file missing.");
-    return false;
+    return LogInfoError("rcc input file missing.");
   }
   if (RccFileName_.empty()) {
-    Log().ErrorFile(GenT::RCC, InfoFile(), "rcc output file missing.");
-    return false;
+    return LogInfoError("rcc output file missing.");
   }
 
   // Init derived information

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=bd6c3f8609b87f6995acb2aef21aa572f0f73fa7
commit bd6c3f8609b87f6995acb2aef21aa572f0f73fa7
Author:     Sebastian Holtermann <sebh...@xwmw.org>
AuthorDate: Sat May 11 21:30:20 2019 +0200
Commit:     Sebastian Holtermann <sebh...@xwmw.org>
CommitDate: Sat May 11 22:15:47 2019 +0200

    AutoRcc: Rebuild if the rcc executable is newer than its output
    
    In AUTORCC add a test if the rcc executable is newer that the rcc output.
    If the rcc executable is newer, rebuild the output.

diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index 7063f6a..922767d 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -75,6 +75,13 @@ bool cmQtAutoRcc::Init(cmMakefile* makefile)
 
   // - Rcc executable
   RccExecutable_ = InfoGet("ARCC_RCC_EXECUTABLE");
+  if (!RccExecutableTime_.Load(RccExecutable_)) {
+    std::string error = "The rcc executable ";
+    error += Quoted(RccExecutable_);
+    error += " does not exist.";
+    Log().ErrorFile(GenT::RCC, InfoFile(), error);
+    return false;
+  }
   RccListOptions_ = InfoGetList("ARCC_RCC_LIST_OPTIONS");
 
   // - Job
@@ -336,6 +343,18 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate)
     return true;
   }
 
+  // Test if the rcc output file is older than the rcc executable
+  if (RccFileTime_.Older(RccExecutableTime_)) {
+    if (Log().Verbose()) {
+      Reason = "Generating ";
+      Reason += Quoted(RccFileOutput_);
+      Reason += ", because it is older than the rcc executable, from ";
+      Reason += Quoted(QrcFile_);
+    }
+    generate = true;
+    return true;
+  }
+
   return true;
 }
 
diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h
index 2dde574..636a667 100644
--- a/Source/cmQtAutoRcc.h
+++ b/Source/cmQtAutoRcc.h
@@ -54,6 +54,7 @@ private:
   std::string IncludeDir_;
   // -- Qt environment
   std::string RccExecutable_;
+  cmFileTime RccExecutableTime_;
   std::vector<std::string> RccListOptions_;
   // -- Job
   std::string LockFile_;

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=54903af84bc8656344e7fe1ea0a11d5f09e94f86
commit 54903af84bc8656344e7fe1ea0a11d5f09e94f86
Author:     Sebastian Holtermann <sebh...@xwmw.org>
AuthorDate: Sat May 11 22:13:39 2019 +0200
Commit:     Sebastian Holtermann <sebh...@xwmw.org>
CommitDate: Sat May 11 22:15:47 2019 +0200

    AutoRcc: Don't read the info file time again
    
    In `AUTORCC` use the info file time that's available already instead of
    reading it again.

diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index f841260..7063f6a 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -383,18 +383,7 @@ bool cmQtAutoRcc::TestResources(bool& generate)
 bool cmQtAutoRcc::TestInfoFile()
 {
   // Test if the rcc output file is older than the info file
-
-  cmFileTime infoFileTime;
-  if (!infoFileTime.Load(InfoFile())) {
-    std::string error;
-    error = "Could not find the info file ";
-    error += Quoted(InfoFile());
-    error += '\n';
-    Log().ErrorFile(GenT::RCC, QrcFile_, error);
-    return false;
-  }
-  if (RccFileTime_.Older(infoFileTime)) {
-
+  if (RccFileTime_.Older(InfoFileTime())) {
     if (Log().Verbose()) {
       std::string reason = "Touching ";
       reason += Quoted(RccFileOutput_);

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=081104fb003a56bb3d8330363795ace5b71bc3f8
commit 081104fb003a56bb3d8330363795ace5b71bc3f8
Author:     Sebastian Holtermann <sebh...@xwmw.org>
AuthorDate: Sat May 11 21:23:29 2019 +0200
Commit:     Sebastian Holtermann <sebh...@xwmw.org>
CommitDate: Sat May 11 21:23:29 2019 +0200

    AutoRcc: Write re-generation reason and rcc command as one string
    
    In AUTORCC with verbose output write the rcc re-generation reason and
    the rcc command as on single string to avoid message chopping in concurrent
    builds.

diff --git a/Source/cmQtAutoRcc.cxx b/Source/cmQtAutoRcc.cxx
index bb40c39..f841260 100644
--- a/Source/cmQtAutoRcc.cxx
+++ b/Source/cmQtAutoRcc.cxx
@@ -301,12 +301,10 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate)
   // Test if the rcc output file exists
   if (!RccFileTime_.Load(RccFileOutput_)) {
     if (Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(RccFileOutput_);
-      reason += " from its source file ";
-      reason += Quoted(QrcFile_);
-      reason += " because it doesn't exist";
-      Log().Info(GenT::RCC, reason);
+      Reason = "Generating ";
+      Reason += Quoted(RccFileOutput_);
+      Reason += ", because it doesn't exist, from ";
+      Reason += Quoted(QrcFile_);
     }
     generate = true;
     return true;
@@ -315,12 +313,10 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate)
   // Test if the settings changed
   if (SettingsChanged_) {
     if (Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(RccFileOutput_);
-      reason += " from ";
-      reason += Quoted(QrcFile_);
-      reason += " because the RCC settings changed";
-      Log().Info(GenT::RCC, reason);
+      Reason = "Generating ";
+      Reason += Quoted(RccFileOutput_);
+      Reason += ", because the rcc settings changed, from ";
+      Reason += Quoted(QrcFile_);
     }
     generate = true;
     return true;
@@ -329,11 +325,12 @@ bool cmQtAutoRcc::TestQrcRccFiles(bool& generate)
   // Test if the rcc output file is older than the .qrc file
   if (RccFileTime_.Older(QrcFileTime_)) {
     if (Log().Verbose()) {
-      std::string reason = "Generating ";
-      reason += Quoted(RccFileOutput_);
-      reason += " because it is older than ";
-      reason += Quoted(QrcFile_);
-      Log().Info(GenT::RCC, reason);
+      Reason = "Generating ";
+      Reason += Quoted(RccFileOutput_);
+      Reason += ", because it is older than ";
+      Reason += Quoted(QrcFile_);
+      Reason += ", from ";
+      Reason += Quoted(QrcFile_);
     }
     generate = true;
     return true;
@@ -354,6 +351,7 @@ bool cmQtAutoRcc::TestResources(bool& generate)
     }
   }
 
+  // Check if any resource file is newer than the rcc output file
   for (std::string const& resFile : Inputs_) {
     // Check if the resource file exists
     cmFileTime fileTime;
@@ -365,16 +363,15 @@ bool cmQtAutoRcc::TestResources(bool& generate)
       Log().ErrorFile(GenT::RCC, QrcFile_, error);
       return false;
     }
-    // Check if the resource file is newer than the build file
+    // Check if the resource file is newer than the rcc output file
     if (RccFileTime_.Older(fileTime)) {
       if (Log().Verbose()) {
-        std::string reason = "Generating ";
-        reason += Quoted(RccFileOutput_);
-        reason += " from ";
-        reason += Quoted(QrcFile_);
-        reason += " because it is older than ";
-        reason += Quoted(resFile);
-        Log().Info(GenT::RCC, reason);
+        Reason = "Generating ";
+        Reason += Quoted(RccFileOutput_);
+        Reason += ", because it is older than ";
+        Reason += Quoted(resFile);
+        Reason += ", from ";
+        Reason += Quoted(QrcFile_);
       }
       generate = true;
       break;
@@ -397,6 +394,7 @@ bool cmQtAutoRcc::TestInfoFile()
     return false;
   }
   if (RccFileTime_.Older(infoFileTime)) {
+
     if (Log().Verbose()) {
       std::string reason = "Touching ";
       reason += Quoted(RccFileOutput_);
@@ -424,7 +422,7 @@ bool cmQtAutoRcc::GenerateRcc()
     return false;
   }
 
-  // Start a rcc process
+  // Compose rcc command
   std::vector<std::string> cmd;
   cmd.push_back(RccExecutable_);
   cmd.insert(cmd.end(), Options_.begin(), Options_.end());
@@ -432,12 +430,15 @@ bool cmQtAutoRcc::GenerateRcc()
   cmd.push_back(RccFileOutput_);
   cmd.push_back(QrcFile_);
 
-  // Log command
+  // Log reason and command
   if (Log().Verbose()) {
-    std::string msg = "Running command:\n";
+    std::string msg = Reason;
+    if (!msg.empty() && (msg.back() != '\n')) {
+      msg += '\n';
+    }
     msg += QuotedCommand(cmd);
     msg += '\n';
-    cmSystemTools::Stdout(msg);
+    Log().Info(GenT::RCC, msg);
   }
 
   std::string rccStdOut;
diff --git a/Source/cmQtAutoRcc.h b/Source/cmQtAutoRcc.h
index 01c3fb9..2dde574 100644
--- a/Source/cmQtAutoRcc.h
+++ b/Source/cmQtAutoRcc.h
@@ -67,6 +67,7 @@ private:
   std::string RccFileOutput_;
   std::string RccFilePublic_;
   cmFileTime RccFileTime_;
+  std::string Reason;
   std::vector<std::string> Options_;
   std::vector<std::string> Inputs_;
   // -- Settings file

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=eff6e622d6cd66186ad42c9ec2ae6b2055008707
commit eff6e622d6cd66186ad42c9ec2ae6b2055008707
Author:     Sebastian Holtermann <sebh...@xwmw.org>
AuthorDate: Sat May 11 21:17:05 2019 +0200
Commit:     Sebastian Holtermann <sebh...@xwmw.org>
CommitDate: Sat May 11 21:17:05 2019 +0200

    Autogen: A missing info file is a critical error

diff --git a/Source/cmQtAutoGenerator.cxx b/Source/cmQtAutoGenerator.cxx
index f7e377d..e1c435b 100644
--- a/Source/cmQtAutoGenerator.cxx
+++ b/Source/cmQtAutoGenerator.cxx
@@ -279,10 +279,11 @@ bool cmQtAutoGenerator::Run(std::string const& infoFile,
   InfoFile_ = infoFile;
   cmSystemTools::ConvertToUnixSlashes(InfoFile_);
   if (!InfoFileTime_.Load(InfoFile_)) {
-    std::string msg = "Autogen: The info file ";
+    std::string msg = "AutoGen: The info file ";
     msg += Quoted(InfoFile_);
     msg += " is not readable\n";
     cmSystemTools::Stderr(msg);
+    return false;
   }
   InfoDir_ = cmSystemTools::GetFilenamePath(infoFile);
   InfoConfig_ = config;

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=26025d6e106ffd4cd777fdc3a4343b33c8554c15
commit 26025d6e106ffd4cd777fdc3a4343b33c8554c15
Author:     Kyle Edwards <kyle.edwa...@kitware.com>
AuthorDate: Tue Apr 30 11:29:30 2019 -0400
Commit:     Kyle Edwards <kyle.edwa...@kitware.com>
CommitDate: Tue May 7 13:40:06 2019 -0400

    cmUVProcessChain: Add cmUVProcessChain
    
    This class is ultimately intended as a replacement for cmsys::Process.
    It spawns a series of processes using libuv, piping the output of each
    command into the next.
    
    Note: input support has not yet been implemented because write
    support has not yet been implemented on cmUVStreambuf.

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 01c6cd7..42eed4d 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -388,6 +388,8 @@ set(SRCS
   cmUuid.cxx
   cmUVHandlePtr.cxx
   cmUVHandlePtr.h
+  cmUVProcessChain.cxx
+  cmUVProcessChain.h
   cmUVStreambuf.h
   cmUVSignalHackRAII.h
   cmVariableWatch.cxx
diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
index 27069ee..db67463 100644
--- a/Source/cmUVHandlePtr.cxx
+++ b/Source/cmUVHandlePtr.cxx
@@ -211,7 +211,6 @@ uv_pipe_ptr::operator uv_stream_t*() const
   return reinterpret_cast<uv_stream_t*>(handle.get());
 }
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
 int uv_process_ptr::spawn(uv_loop_t& loop, uv_process_options_t const& options,
                           void* data)
 {
@@ -231,6 +230,7 @@ int uv_timer_ptr::start(uv_timer_cb cb, uint64_t timeout, 
uint64_t repeat)
   return uv_timer_start(*this, cb, timeout, repeat);
 }
 
+#ifdef CMAKE_BUILD_WITH_CMAKE
 uv_tty_ptr::operator uv_stream_t*() const
 {
   return reinterpret_cast<uv_stream_t*>(handle.get());
@@ -255,13 +255,13 @@ UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(pipe)
 
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(stream)
 
-#ifdef CMAKE_BUILD_WITH_CMAKE
-UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async)
-
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(process)
 
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(timer)
 
+#ifdef CMAKE_BUILD_WITH_CMAKE
+UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(async)
+
 UV_HANDLE_PTR_INSTANTIATE_EXPLICIT(tty)
 #endif
 }
diff --git a/Source/cmUVProcessChain.cxx b/Source/cmUVProcessChain.cxx
new file mode 100644
index 0000000..c4e30d4
--- /dev/null
+++ b/Source/cmUVProcessChain.cxx
@@ -0,0 +1,392 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmUVProcessChain.h"
+
+#include "cmAlgorithms.h"
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVStreambuf.h"
+#include "cm_uv.h"
+
+#include <iterator>
+#include <memory>
+#include <utility>
+
+struct cmUVProcessChain::InternalData
+{
+  struct BasicStreamData
+  {
+    cmUVStreambuf Streambuf;
+    cm::uv_pipe_ptr BuiltinStream;
+    uv_stdio_container_t Stdio;
+  };
+
+  template <typename IOStream>
+  struct StreamData : public BasicStreamData
+  {
+    StreamData()
+      : BuiltinIOStream(&this->Streambuf)
+    {
+    }
+
+    IOStream BuiltinIOStream;
+
+    IOStream* GetBuiltinStream()
+    {
+      if (this->BuiltinStream.get()) {
+        return &this->BuiltinIOStream;
+      }
+      return nullptr;
+    }
+  };
+
+  struct ProcessData
+  {
+    cmUVProcessChain::InternalData* Data;
+    cm::uv_process_ptr Process;
+    cm::uv_pipe_ptr OutputPipe;
+    bool Finished = false;
+    Status ProcessStatus;
+  };
+
+  const cmUVProcessChainBuilder* Builder = nullptr;
+
+  bool Valid = false;
+
+  cm::uv_loop_ptr Loop;
+
+  StreamData<std::istream> OutputStreamData;
+  StreamData<std::istream> ErrorStreamData;
+
+  unsigned int ProcessesCompleted = 0;
+  std::vector<std::unique_ptr<ProcessData>> Processes;
+
+  bool Prepare(const cmUVProcessChainBuilder* builder);
+  bool AddCommand(const cmUVProcessChainBuilder::ProcessConfiguration& config,
+                  bool first, bool last);
+  bool Finish();
+
+  static const Status* GetStatus(const ProcessData& data);
+};
+
+cmUVProcessChainBuilder::cmUVProcessChainBuilder()
+{
+  this->SetNoStream(Stream_INPUT)
+    .SetNoStream(Stream_OUTPUT)
+    .SetNoStream(Stream_ERROR);
+}
+
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::AddCommand(
+  const std::vector<std::string>& arguments)
+{
+  if (!arguments.empty()) {
+    this->Processes.emplace_back();
+    this->Processes.back().Arguments = arguments;
+  }
+  return *this;
+}
+
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetNoStream(Stream stdio)
+{
+  switch (stdio) {
+    case Stream_INPUT:
+    case Stream_OUTPUT:
+    case Stream_ERROR: {
+      auto& streamData = this->Stdio[stdio];
+      streamData.Type = None;
+      break;
+    }
+  }
+  return *this;
+}
+
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetBuiltinStream(
+  Stream stdio)
+{
+  switch (stdio) {
+    case Stream_INPUT:
+      // FIXME
+      break;
+
+    case Stream_OUTPUT:
+    case Stream_ERROR: {
+      auto& streamData = this->Stdio[stdio];
+      streamData.Type = Builtin;
+      break;
+    }
+  }
+  return *this;
+}
+
+cmUVProcessChainBuilder& cmUVProcessChainBuilder::SetExternalStream(
+  Stream stdio, int fd)
+{
+  switch (stdio) {
+    case Stream_INPUT:
+      // FIXME
+      break;
+
+    case Stream_OUTPUT:
+    case Stream_ERROR: {
+      auto& streamData = this->Stdio[stdio];
+      streamData.Type = External;
+      streamData.FileDescriptor = fd;
+      break;
+    }
+  }
+  return *this;
+}
+
+cmUVProcessChain cmUVProcessChainBuilder::Start() const
+{
+  cmUVProcessChain chain;
+
+  if (!chain.Data->Prepare(this)) {
+    return chain;
+  }
+
+  for (auto it = this->Processes.begin(); it != this->Processes.end(); ++it) {
+    if (!chain.Data->AddCommand(*it, it == this->Processes.begin(),
+                                it == std::prev(this->Processes.end()))) {
+      return chain;
+    }
+  }
+
+  chain.Data->Finish();
+
+  return chain;
+}
+
+const cmUVProcessChain::Status* cmUVProcessChain::InternalData::GetStatus(
+  const cmUVProcessChain::InternalData::ProcessData& data)
+{
+  if (data.Finished) {
+    return &data.ProcessStatus;
+  }
+  return nullptr;
+}
+
+bool cmUVProcessChain::InternalData::Prepare(
+  const cmUVProcessChainBuilder* builder)
+{
+  this->Builder = builder;
+
+  auto const& output =
+    this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT];
+  auto& outputData = this->OutputStreamData;
+  switch (output.Type) {
+    case cmUVProcessChainBuilder::None:
+      outputData.Stdio.flags = UV_IGNORE;
+      break;
+
+    case cmUVProcessChainBuilder::Builtin:
+      outputData.BuiltinStream.init(*this->Loop, 0);
+      outputData.Stdio.flags =
+        static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+      outputData.Stdio.data.stream = outputData.BuiltinStream;
+      break;
+
+    case cmUVProcessChainBuilder::External:
+      outputData.Stdio.flags = UV_INHERIT_FD;
+      outputData.Stdio.data.fd = output.FileDescriptor;
+      break;
+  }
+
+  auto const& error =
+    this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR];
+  auto& errorData = this->ErrorStreamData;
+  switch (error.Type) {
+    case cmUVProcessChainBuilder::None:
+      errorData.Stdio.flags = UV_IGNORE;
+      break;
+
+    case cmUVProcessChainBuilder::Builtin: {
+      int pipeFd[2];
+      if (cmGetPipes(pipeFd) < 0) {
+        return false;
+      }
+
+      errorData.BuiltinStream.init(*this->Loop, 0);
+      if (uv_pipe_open(errorData.BuiltinStream, pipeFd[0]) < 0) {
+        return false;
+      }
+      errorData.Stdio.flags = UV_INHERIT_FD;
+      errorData.Stdio.data.fd = pipeFd[1];
+      break;
+    }
+
+    case cmUVProcessChainBuilder::External:
+      errorData.Stdio.flags = UV_INHERIT_FD;
+      errorData.Stdio.data.fd = error.FileDescriptor;
+      break;
+  }
+
+  return true;
+}
+
+bool cmUVProcessChain::InternalData::AddCommand(
+  const cmUVProcessChainBuilder::ProcessConfiguration& config, bool first,
+  bool last)
+{
+  this->Processes.emplace_back(cm::make_unique<ProcessData>());
+  auto& process = *this->Processes.back();
+  process.Data = this;
+
+  auto options = uv_process_options_t();
+
+  // Bounds were checked at add time, first element is guaranteed to exist
+  options.file = config.Arguments[0].c_str();
+
+  std::vector<const char*> arguments;
+  for (auto const& arg : config.Arguments) {
+    arguments.push_back(arg.c_str());
+  }
+  arguments.push_back(nullptr);
+  options.args = const_cast<char**>(arguments.data());
+  options.flags = UV_PROCESS_WINDOWS_HIDE;
+
+  std::array<uv_stdio_container_t, 3> stdio;
+  stdio[0] = uv_stdio_container_t();
+  if (first) {
+    stdio[0].flags = UV_IGNORE;
+  } else {
+    auto& prev = **std::prev(this->Processes.end(), 2);
+    stdio[0].flags = UV_INHERIT_STREAM;
+    stdio[0].data.stream = prev.OutputPipe;
+  }
+  if (last) {
+    stdio[1] = this->OutputStreamData.Stdio;
+  } else {
+    if (process.OutputPipe.init(*this->Loop, 0) < 0) {
+      return false;
+    }
+    stdio[1] = uv_stdio_container_t();
+    stdio[1].flags =
+      static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+    stdio[1].data.stream = process.OutputPipe;
+  }
+  stdio[2] = this->ErrorStreamData.Stdio;
+
+  options.stdio = stdio.data();
+  options.stdio_count = 3;
+  options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
+                       int termSignal) {
+    auto* processData = static_cast<ProcessData*>(handle->data);
+    processData->Finished = true;
+    processData->ProcessStatus.ExitStatus = exitStatus;
+    processData->ProcessStatus.TermSignal = termSignal;
+    processData->Data->ProcessesCompleted++;
+  };
+
+  return process.Process.spawn(*this->Loop, options, &process) >= 0;
+}
+
+bool cmUVProcessChain::InternalData::Finish()
+{
+  if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_OUTPUT].Type ==
+      cmUVProcessChainBuilder::Builtin) {
+    this->OutputStreamData.Streambuf.open(
+      this->OutputStreamData.BuiltinStream);
+  }
+
+  if (this->Builder->Stdio[cmUVProcessChainBuilder::Stream_ERROR].Type ==
+      cmUVProcessChainBuilder::Builtin) {
+    cm::uv_pipe_ptr tmpPipe;
+    if (tmpPipe.init(*this->Loop, 0) < 0) {
+      return false;
+    }
+    if (uv_pipe_open(tmpPipe, this->ErrorStreamData.Stdio.data.fd) < 0) {
+      return false;
+    }
+    tmpPipe.reset();
+
+    this->ErrorStreamData.Streambuf.open(this->ErrorStreamData.BuiltinStream);
+  }
+
+  this->Valid = true;
+  return true;
+}
+
+cmUVProcessChain::cmUVProcessChain()
+  : Data(cm::make_unique<InternalData>())
+{
+  this->Data->Loop.init();
+}
+
+cmUVProcessChain::cmUVProcessChain(cmUVProcessChain&& other) noexcept
+  : Data(std::move(other.Data))
+{
+}
+
+cmUVProcessChain::~cmUVProcessChain() = default;
+
+cmUVProcessChain& cmUVProcessChain::operator=(
+  cmUVProcessChain&& other) noexcept
+{
+  this->Data = std::move(other.Data);
+  return *this;
+}
+
+uv_loop_t& cmUVProcessChain::GetLoop()
+{
+  return *this->Data->Loop;
+}
+
+std::istream* cmUVProcessChain::OutputStream()
+{
+  return this->Data->OutputStreamData.GetBuiltinStream();
+}
+
+std::istream* cmUVProcessChain::ErrorStream()
+{
+  return this->Data->ErrorStreamData.GetBuiltinStream();
+}
+
+bool cmUVProcessChain::Valid() const
+{
+  return this->Data->Valid;
+}
+
+bool cmUVProcessChain::Wait(int64_t milliseconds)
+{
+  bool timeout = false;
+  cm::uv_timer_ptr timer;
+
+  if (milliseconds >= 0) {
+    timer.init(*this->Data->Loop, &timeout);
+    timer.start(
+      [](uv_timer_t* handle) {
+        auto* timeoutPtr = static_cast<bool*>(handle->data);
+        *timeoutPtr = true;
+      },
+      milliseconds, 0);
+  }
+
+  while (!timeout &&
+         this->Data->ProcessesCompleted < this->Data->Processes.size()) {
+    uv_run(this->Data->Loop, UV_RUN_ONCE);
+  }
+
+  return !timeout;
+}
+
+std::vector<const cmUVProcessChain::Status*> cmUVProcessChain::GetStatus()
+  const
+{
+  std::vector<const cmUVProcessChain::Status*> statuses(
+    this->Data->Processes.size(), nullptr);
+  for (std::size_t i = 0; i < statuses.size(); i++) {
+    statuses[i] = this->GetStatus(i);
+  }
+  return statuses;
+}
+
+const cmUVProcessChain::Status* cmUVProcessChain::GetStatus(
+  std::size_t index) const
+{
+  auto const& process = *this->Data->Processes[index];
+  if (process.Finished) {
+    return &process.ProcessStatus;
+  }
+  return nullptr;
+}
diff --git a/Source/cmUVProcessChain.h b/Source/cmUVProcessChain.h
new file mode 100644
index 0000000..2b33520
--- /dev/null
+++ b/Source/cmUVProcessChain.h
@@ -0,0 +1,100 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmUVProcessChain_h
+#define cmUVProcessChain_h
+
+#include "cm_uv.h"
+
+#include <array>
+#include <iosfwd>
+#include <memory> // IWYU pragma: keep
+#include <string>
+#include <vector>
+
+#include <stdint.h>
+
+class cmUVProcessChain;
+
+class cmUVProcessChainBuilder
+{
+public:
+  enum Stream
+  {
+    Stream_INPUT = 0,
+    Stream_OUTPUT = 1,
+    Stream_ERROR = 2,
+  };
+
+  cmUVProcessChainBuilder();
+
+  cmUVProcessChainBuilder& AddCommand(
+    const std::vector<std::string>& arguments);
+  cmUVProcessChainBuilder& SetNoStream(Stream stdio);
+  cmUVProcessChainBuilder& SetBuiltinStream(Stream stdio);
+  cmUVProcessChainBuilder& SetExternalStream(Stream stdio, int fd);
+
+  cmUVProcessChain Start() const;
+
+private:
+  enum StdioType
+  {
+    None,
+    Builtin,
+    External,
+  };
+
+  friend class cmUVProcessChain;
+
+  struct StdioConfiguration
+  {
+    StdioType Type;
+    int FileDescriptor;
+  };
+
+  struct ProcessConfiguration
+  {
+    std::vector<std::string> Arguments;
+  };
+
+  std::array<StdioConfiguration, 3> Stdio;
+  std::vector<ProcessConfiguration> Processes;
+};
+
+class cmUVProcessChain
+{
+public:
+  struct Status
+  {
+    int64_t ExitStatus;
+    int TermSignal;
+  };
+
+  cmUVProcessChain(const cmUVProcessChain& other) = delete;
+  cmUVProcessChain(cmUVProcessChain&& other) noexcept;
+
+  ~cmUVProcessChain();
+
+  cmUVProcessChain& operator=(const cmUVProcessChain& other) = delete;
+  cmUVProcessChain& operator=(cmUVProcessChain&& other) noexcept;
+
+  uv_loop_t& GetLoop();
+
+  // FIXME: Add stdin support
+  std::istream* OutputStream();
+  std::istream* ErrorStream();
+
+  bool Valid() const;
+  bool Wait(int64_t milliseconds = -1);
+  std::vector<const Status*> GetStatus() const;
+  const Status* GetStatus(std::size_t index) const;
+
+private:
+  friend class cmUVProcessChainBuilder;
+
+  cmUVProcessChain();
+
+  struct InternalData;
+  std::unique_ptr<InternalData> Data;
+};
+
+#endif
diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h
index 0ae532b..873352b 100644
--- a/Source/cmUVStreambuf.h
+++ b/Source/cmUVStreambuf.h
@@ -208,7 +208,7 @@ void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t 
nread)
     this->setg(this->eback(), this->gptr(),
                this->egptr() + nread / sizeof(CharT));
     uv_read_stop(this->Stream);
-  } else if (nread < 0 || nread == UV_EOF) {
+  } else if (nread < 0 /*|| nread == UV_EOF*/) {
     this->EndOfFile = true;
     uv_read_stop(this->Stream);
   }
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index e04bba2..a25f25a 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -15,11 +15,15 @@ set(CMakeLib_TESTS
   testXMLParser.cxx
   testXMLSafe.cxx
   testFindPackageCommand.cxx
+  testUVProcessChain.cxx
   testUVRAII.cxx
   testUVStreambuf.cxx
   )
 
+add_executable(testUVProcessChainHelper testUVProcessChainHelper.cxx)
+
 set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
+set(testUVProcessChain_ARGS $<TARGET_FILE:testUVProcessChainHelper>)
 set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>)
 
 if(WIN32)
diff --git a/Tests/CMakeLib/testUVProcessChain.cxx 
b/Tests/CMakeLib/testUVProcessChain.cxx
new file mode 100644
index 0000000..72ae602
--- /dev/null
+++ b/Tests/CMakeLib/testUVProcessChain.cxx
@@ -0,0 +1,335 @@
+#include "cmUVProcessChain.h"
+
+#include "cmAlgorithms.h"
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+#include "cmUVStreambuf.h"
+
+#include "cm_uv.h"
+
+#include <algorithm>
+#include <functional>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <csignal>
+
+struct ExpectedStatus
+{
+  bool Finished;
+  bool MatchExitStatus;
+  bool MatchTermSignal;
+  cmUVProcessChain::Status Status;
+};
+
+static const std::vector<ExpectedStatus> status1 = {
+  { false, false, false, { 0, 0 } },
+  { false, false, false, { 0, 0 } },
+  { false, false, false, { 0, 0 } },
+};
+
+static const std::vector<ExpectedStatus> status2 = {
+  { true, true, true, { 0, 0 } },
+  { false, false, false, { 0, 0 } },
+  { false, false, false, { 0, 0 } },
+};
+
+static const std::vector<ExpectedStatus> status3 = {
+  { true, true, true, { 0, 0 } },
+  { true, true, true, { 1, 0 } },
+#ifdef _WIN32
+  { true, true, true, { 2, 0 } },
+#else
+  { true, false, true, { 0, SIGABRT } },
+#endif
+};
+
+bool operator==(const cmUVProcessChain::Status* actual,
+                const ExpectedStatus& expected)
+{
+  if (!expected.Finished) {
+    return !actual;
+  } else if (!actual) {
+    return false;
+  }
+  if (expected.MatchExitStatus &&
+      expected.Status.ExitStatus != actual->ExitStatus) {
+    return false;
+  }
+  if (expected.MatchTermSignal &&
+      expected.Status.TermSignal != actual->TermSignal) {
+    return false;
+  }
+  return true;
+}
+
+bool resultsMatch(const std::vector<const cmUVProcessChain::Status*>& actual,
+                  const std::vector<ExpectedStatus>& expected)
+{
+  return actual.size() == expected.size() &&
+    std::equal(actual.begin(), actual.end(), expected.begin());
+}
+
+std::string getInput(std::istream& input)
+{
+  char buffer[1024];
+  std::ostringstream str;
+  do {
+    input.read(buffer, 1024);
+    str.write(buffer, input.gcount());
+  } while (input.gcount() > 0);
+  return str.str();
+}
+
+template <typename T>
+std::function<std::ostream&(std::ostream&)> printExpected(bool match,
+                                                          const T& value)
+{
+  return [match, value](std::ostream& stream) -> std::ostream& {
+    if (match) {
+      stream << value;
+    } else {
+      stream << "*";
+    }
+    return stream;
+  };
+}
+
+std::ostream& operator<<(
+  std::ostream& stream,
+  const std::function<std::ostream&(std::ostream&)>& func)
+{
+  return func(stream);
+}
+
+void printResults(const std::vector<const cmUVProcessChain::Status*>& actual,
+                  const std::vector<ExpectedStatus>& expected)
+{
+  std::cout << "Expected: " << std::endl;
+  for (auto const& e : expected) {
+    if (e.Finished) {
+      std::cout << "  ExitStatus: "
+                << printExpected(e.MatchExitStatus, e.Status.ExitStatus)
+                << ", TermSignal: "
+                << printExpected(e.MatchTermSignal, e.Status.TermSignal)
+                << std::endl;
+    } else {
+      std::cout << "  null" << std::endl;
+    }
+  }
+  std::cout << "Actual:" << std::endl;
+  for (auto const& a : actual) {
+    if (a) {
+      std::cout << "  ExitStatus: " << a->ExitStatus
+                << ", TermSignal: " << a->TermSignal << std::endl;
+    } else {
+      std::cout << "  null" << std::endl;
+    }
+  }
+}
+
+bool checkExecution(cmUVProcessChainBuilder& builder,
+                    std::unique_ptr<cmUVProcessChain>& chain)
+{
+  std::vector<const cmUVProcessChain::Status*> status;
+
+  chain = cm::make_unique<cmUVProcessChain>(builder.Start());
+  if (!chain->Valid()) {
+    std::cout << "Valid() returned false, should be true" << std::endl;
+    return false;
+  }
+  status = chain->GetStatus();
+  if (!resultsMatch(status, status1)) {
+    std::cout << "GetStatus() did not produce expected output" << std::endl;
+    printResults(status, status1);
+    return false;
+  }
+
+  if (chain->Wait(6000)) {
+    std::cout << "Wait() returned true, should be false" << std::endl;
+    return false;
+  }
+  status = chain->GetStatus();
+  if (!resultsMatch(status, status2)) {
+    std::cout << "GetStatus() did not produce expected output" << std::endl;
+    printResults(status, status2);
+    return false;
+  }
+
+  if (!chain->Wait()) {
+    std::cout << "Wait() returned false, should be true" << std::endl;
+    return false;
+  }
+  status = chain->GetStatus();
+  if (!resultsMatch(status, status3)) {
+    std::cout << "GetStatus() did not produce expected output" << std::endl;
+    printResults(status, status3);
+    return false;
+  }
+
+  return true;
+}
+
+bool checkOutput(std::istream& outputStream, std::istream& errorStream)
+{
+  std::string output = getInput(outputStream);
+  if (output != "HELO WRD!") {
+    std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
+              << std::endl;
+    return false;
+  }
+
+  std::string error = getInput(errorStream);
+  if (error.length() != 3 || error.find('1') == std::string::npos ||
+      error.find('2') == std::string::npos ||
+      error.find('3') == std::string::npos) {
+    std::cout << "Error was \"" << error << "\", expected \"123\""
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVProcessChainBuiltin(const char* helperCommand)
+{
+  cmUVProcessChainBuilder builder;
+  std::unique_ptr<cmUVProcessChain> chain;
+  builder.AddCommand({ helperCommand, "echo" })
+    .AddCommand({ helperCommand, "capitalize" })
+    .AddCommand({ helperCommand, "dedup" })
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_OUTPUT)
+    .SetBuiltinStream(cmUVProcessChainBuilder::Stream_ERROR);
+
+  if (!checkExecution(builder, chain)) {
+    return false;
+  }
+
+  if (!chain->OutputStream()) {
+    std::cout << "OutputStream() was null, expecting not null" << std::endl;
+    return false;
+  }
+  if (!chain->ErrorStream()) {
+    std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+    return false;
+  }
+
+  if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) {
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVProcessChainExternal(const char* helperCommand)
+{
+  cmUVProcessChainBuilder builder;
+  std::unique_ptr<cmUVProcessChain> chain;
+  int outputPipe[2], errorPipe[2];
+  cm::uv_pipe_ptr outputInPipe, outputOutPipe, errorInPipe, errorOutPipe;
+
+  if (cmGetPipes(outputPipe) < 0) {
+    std::cout << "Error creating pipes" << std::endl;
+    return false;
+  }
+  if (cmGetPipes(errorPipe) < 0) {
+    std::cout << "Error creating pipes" << std::endl;
+    return false;
+  }
+
+  builder.AddCommand({ helperCommand, "echo" })
+    .AddCommand({ helperCommand, "capitalize" })
+    .AddCommand({ helperCommand, "dedup" })
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_OUTPUT, outputPipe[1])
+    .SetExternalStream(cmUVProcessChainBuilder::Stream_ERROR, errorPipe[1]);
+
+  if (!checkExecution(builder, chain)) {
+    return false;
+  }
+
+  if (chain->OutputStream()) {
+    std::cout << "OutputStream() was not null, expecting null" << std::endl;
+    return false;
+  }
+  if (chain->ErrorStream()) {
+    std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+    return false;
+  }
+
+  outputOutPipe.init(chain->GetLoop(), 0);
+  uv_pipe_open(outputOutPipe, outputPipe[1]);
+  outputOutPipe.reset();
+
+  errorOutPipe.init(chain->GetLoop(), 0);
+  uv_pipe_open(errorOutPipe, errorPipe[1]);
+  errorOutPipe.reset();
+
+  outputInPipe.init(chain->GetLoop(), 0);
+  uv_pipe_open(outputInPipe, outputPipe[0]);
+  cmUVStreambuf outputBuf;
+  outputBuf.open(outputInPipe);
+  std::istream outputStream(&outputBuf);
+
+  errorInPipe.init(chain->GetLoop(), 0);
+  uv_pipe_open(errorInPipe, errorPipe[0]);
+  cmUVStreambuf errorBuf;
+  errorBuf.open(errorInPipe);
+  std::istream errorStream(&errorBuf);
+
+  if (!checkOutput(outputStream, errorStream)) {
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVProcessChainNone(const char* helperCommand)
+{
+  cmUVProcessChainBuilder builder;
+  std::unique_ptr<cmUVProcessChain> chain;
+  builder.AddCommand({ helperCommand, "echo" })
+    .AddCommand({ helperCommand, "capitalize" })
+    .AddCommand({ helperCommand, "dedup" });
+
+  if (!checkExecution(builder, chain)) {
+    return false;
+  }
+
+  if (chain->OutputStream()) {
+    std::cout << "OutputStream() was not null, expecting null" << std::endl;
+    return false;
+  }
+  if (chain->ErrorStream()) {
+    std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+int testUVProcessChain(int argc, char** const argv)
+{
+  if (argc < 2) {
+    std::cout << "Invalid arguments.\n";
+    return -1;
+  }
+
+  if (!testUVProcessChainBuiltin(argv[1])) {
+    std::cout << "While executing testUVProcessChainBuiltin().\n";
+    return -1;
+  }
+
+  if (!testUVProcessChainExternal(argv[1])) {
+    std::cout << "While executing testUVProcessChainExternal().\n";
+    return -1;
+  }
+
+  if (!testUVProcessChainNone(argv[1])) {
+    std::cout << "While executing testUVProcessChainNone().\n";
+    return -1;
+  }
+
+  return 0;
+}
diff --git a/Tests/CMakeLib/testUVProcessChainHelper.cxx 
b/Tests/CMakeLib/testUVProcessChainHelper.cxx
new file mode 100644
index 0000000..263665d
--- /dev/null
+++ b/Tests/CMakeLib/testUVProcessChainHelper.cxx
@@ -0,0 +1,72 @@
+#include <chrono>
+#include <iostream>
+#include <set>
+#include <sstream>
+#include <string>
+#include <thread>
+
+#include <cctype>
+#include <cstdlib>
+
+std::string getStdin()
+{
+  char buffer[1024];
+  std::ostringstream str;
+  do {
+    std::cin.read(buffer, 1024);
+    str.write(buffer, std::cin.gcount());
+  } while (std::cin.gcount() > 0);
+  return str.str();
+}
+
+int main(int argc, char** argv)
+{
+  if (argc < 2) {
+    return -1;
+  }
+
+  std::string command = argv[1];
+  if (command == "echo") {
+    std::this_thread::sleep_for(std::chrono::milliseconds(3000));
+    std::cout << "HELLO world!" << std::flush;
+    std::cerr << "1" << std::flush;
+    return 0;
+  }
+  if (command == "capitalize") {
+    std::this_thread::sleep_for(std::chrono::milliseconds(9000));
+    std::string input = getStdin();
+    for (auto& c : input) {
+      c = static_cast<char>(std::toupper(c));
+    }
+    std::cout << input << std::flush;
+    std::cerr << "2" << std::flush;
+    return 1;
+  }
+  if (command == "dedup") {
+    // Use a nested scope to free all resources before aborting below.
+    {
+      std::string input = getStdin();
+      std::set<char> seen;
+      std::string output;
+      for (auto c : input) {
+        if (!seen.count(c)) {
+          seen.insert(c);
+          output += c;
+        }
+      }
+      std::cout << output << std::flush;
+      std::cerr << "3" << std::flush;
+    }
+
+    // On Windows, the exit code of abort() is different between debug and
+    // release builds, and does not yield a term_signal in libuv in either
+    // case. For the sake of simplicity, we just return another non-zero code.
+#ifdef _WIN32
+    return 2;
+#else
+    std::abort();
+#endif
+  }
+
+  return -1;
+}
diff --git a/bootstrap b/bootstrap
index 8b9c404..c5274ce 100755
--- a/bootstrap
+++ b/bootstrap
@@ -329,6 +329,7 @@ CMAKE_CXX_SOURCES="\
   cmGetCMakePropertyCommand \
   cmGetDirectoryPropertyCommand \
   cmGetFilenameComponentCommand \
+  cmGetPipes \
   cmGetPropertyCommand \
   cmGetSourceFilePropertyCommand \
   cmGetTargetPropertyCommand \
@@ -427,6 +428,7 @@ CMAKE_CXX_SOURCES="\
   cmUnexpectedCommand \
   cmUnsetCommand \
   cmUVHandlePtr \
+  cmUVProcessChain \
   cmVersion \
   cmWhileCommand \
   cmWorkingDirectory \

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=6b04d1cdc281b9b0dee5f59394a1c41d8b96c4a1
commit 6b04d1cdc281b9b0dee5f59394a1c41d8b96c4a1
Author:     Brad King <brad.k...@kitware.com>
AuthorDate: Thu May 2 10:15:30 2019 -0400
Commit:     Kyle Edwards <kyle.edwa...@kitware.com>
CommitDate: Tue May 7 12:35:22 2019 -0400

    cmUVStreambuf: Initialize all members on construction
    
    Avoid leaving any members uninitialized after construction even if they
    are later initialized before use by methods.  This helps convince static
    analysis tools that the members are not used uninitialized.

diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h
index 29e4fde..0ae532b 100644
--- a/Source/cmUVStreambuf.h
+++ b/Source/cmUVStreambuf.h
@@ -68,10 +68,10 @@ protected:
 
 private:
   uv_stream_t* Stream = nullptr;
-  void* OldStreamData;
-  const std::size_t PutBack;
+  void* OldStreamData = nullptr;
+  const std::size_t PutBack = 0;
   std::vector<CharT> InputBuffer;
-  bool EndOfFile;
+  bool EndOfFile = false;
 
   void StreamReadStartStop();
 

-----------------------------------------------------------------------

Summary of changes:
 Source/CMakeLists.txt                       |   2 +
 Source/cmQtAutoGenerator.cxx                |   3 +-
 Source/cmQtAutoRcc.cxx                      | 123 +++++----
 Source/cmQtAutoRcc.h                        |   2 +
 Source/cmUVHandlePtr.cxx                    |   8 +-
 Source/cmUVProcessChain.cxx                 | 392 ++++++++++++++++++++++++++++
 Source/cmUVProcessChain.h                   | 100 +++++++
 Source/cmUVStreambuf.h                      |   8 +-
 Tests/CMakeLib/CMakeLists.txt               |   4 +
 Tests/CMakeLib/testUVProcessChain.cxx       | 335 ++++++++++++++++++++++++
 Tests/CMakeLib/testUVProcessChainHelper.cxx |  72 +++++
 bootstrap                                   |   2 +
 12 files changed, 984 insertions(+), 67 deletions(-)
 create mode 100644 Source/cmUVProcessChain.cxx
 create mode 100644 Source/cmUVProcessChain.h
 create mode 100644 Tests/CMakeLib/testUVProcessChain.cxx
 create mode 100644 Tests/CMakeLib/testUVProcessChainHelper.cxx


hooks/post-receive
-- 
CMake
_______________________________________________
Cmake-commits mailing list
Cmake-commits@cmake.org
https://cmake.org/mailman/listinfo/cmake-commits

Reply via email to