Просмотр исходного кода

Merge topic 'cmuvprocesschain-external-stream-fd'

b65d54e876 cmUVStreambuf: Update URL for example code
0878306386 cmUVStream: Add cmUVStreamRead() function
b8fd273ed7 cmUVProcessChain: Return output and error streams as file descriptors
ec81d40be4 cmUVPipeIStream: Add cmUVPipeIStream
3b6c5efc08 cm::append: Add support for std::basic_string on SPARC/SunPro

Acked-by: Kitware Robot <[email protected]>
Tested-by: buildbot <[email protected]>
Merge-request: !8559
Brad King 2 лет назад
Родитель
Сommit
a84c996947

+ 1 - 0
Source/CMakeLists.txt

@@ -438,6 +438,7 @@ add_library(
   cmUVHandlePtr.h
   cmUVProcessChain.cxx
   cmUVProcessChain.h
+  cmUVStream.h
   cmUVStreambuf.h
   cmUVSignalHackRAII.h
   cmVariableWatch.cxx

+ 3 - 1
Source/cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool.cxx

@@ -10,6 +10,7 @@
 #include "cmRuntimeDependencyArchive.h"
 #include "cmSystemTools.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::
   cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool(
@@ -46,7 +47,8 @@ bool cmBinUtilsLinuxELFObjdumpGetRuntimeDependenciesTool::GetFileInfo(
   static const cmsys::RegularExpression neededRegex("^ *NEEDED *([^\n]*)$");
   static const cmsys::RegularExpression rpathRegex("^ *RPATH *([^\n]*)$");
   static const cmsys::RegularExpression runpathRegex("^ *RUNPATH *([^\n]*)$");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (neededRegex.find(line.c_str(), match)) {
       needed.push_back(match.match(1));

+ 7 - 5
Source/cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool.cxx

@@ -9,6 +9,7 @@
 
 #include "cmRuntimeDependencyArchive.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::
   cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool(
@@ -49,11 +50,12 @@ bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo(
     "^ *path (.*) \\(offset [0-9]+\\)$");
   static const cmsys::RegularExpression nameRegex(
     "^ *name (.*) \\(offset [0-9]+\\)$");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch cmdMatch;
     if (rpathRegex.find(line.c_str(), cmdMatch)) {
-      if (!std::getline(*process.OutputStream(), line) ||
-          !std::getline(*process.OutputStream(), line)) {
+      // NOLINTNEXTLINE(misc-redundant-expression)
+      if (!std::getline(output, line) || !std::getline(output, line)) {
         this->SetError("Invalid output from otool");
         return false;
       }
@@ -66,8 +68,8 @@ bool cmBinUtilsMacOSMachOOToolGetRuntimeDependenciesTool::GetFileInfo(
         return false;
       }
     } else if (loadDylibRegex.find(line.c_str(), cmdMatch)) {
-      if (!std::getline(*process.OutputStream(), line) ||
-          !std::getline(*process.OutputStream(), line)) {
+      // NOLINTNEXTLINE(misc-redundant-expression)
+      if (!std::getline(output, line) || !std::getline(output, line)) {
         this->SetError("Invalid output from otool");
         return false;
       }

+ 3 - 1
Source/cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool.cxx

@@ -9,6 +9,7 @@
 
 #include "cmRuntimeDependencyArchive.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::
   cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool(
@@ -43,7 +44,8 @@ bool cmBinUtilsWindowsPEDumpbinGetRuntimeDependenciesTool::GetFileInfo(
   std::string line;
   static const cmsys::RegularExpression regex(
     "^    ([^\n]*\\.[Dd][Ll][Ll])\r$");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (regex.find(line.c_str(), match)) {
       needed.push_back(match.match(1));

+ 3 - 1
Source/cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool.cxx

@@ -10,6 +10,7 @@
 #include "cmRuntimeDependencyArchive.h"
 #include "cmSystemTools.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::
   cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool(
@@ -44,7 +45,8 @@ bool cmBinUtilsWindowsPEObjdumpGetRuntimeDependenciesTool::GetFileInfo(
   std::string line;
   static const cmsys::RegularExpression regex(
     "^\t*DLL Name: ([^\n]*\\.[Dd][Ll][Ll])$");
-  while (cmSystemTools::GetLineFromStream(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (cmSystemTools::GetLineFromStream(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (regex.find(line.c_str(), match)) {
       needed.push_back(match.match(1));

+ 3 - 1
Source/cmLDConfigLDConfigTool.cxx

@@ -14,6 +14,7 @@
 #include "cmRuntimeDependencyArchive.h"
 #include "cmSystemTools.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 
 cmLDConfigLDConfigTool::cmLDConfigLDConfigTool(
   cmRuntimeDependencyArchive* archive)
@@ -50,7 +51,8 @@ bool cmLDConfigLDConfigTool::GetLDConfigPaths(std::vector<std::string>& paths)
 
   std::string line;
   static const cmsys::RegularExpression regex("^([^\t:]*):");
-  while (std::getline(*process.OutputStream(), line)) {
+  cmUVPipeIStream output(process.GetLoop(), process.OutputStream());
+  while (std::getline(output, line)) {
     cmsys::RegularExpressionMatch match;
     if (regex.find(line.c_str(), match)) {
       paths.push_back(match.match(1));

+ 12 - 48
Source/cmUVProcessChain.cxx

@@ -17,36 +17,15 @@
 
 #include "cmGetPipes.h"
 #include "cmUVHandlePtr.h"
-#include "cmUVStreambuf.h"
 
 struct cmUVProcessChain::InternalData
 {
-  struct BasicStreamData
+  struct StreamData
   {
-    cmUVStreambuf Streambuf;
-    cm::uv_pipe_ptr BuiltinStream;
+    int BuiltinStream = -1;
     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;
@@ -64,9 +43,9 @@ struct cmUVProcessChain::InternalData
 
   cm::uv_loop_ptr Loop;
 
-  StreamData<std::ostream> InputStreamData;
-  StreamData<std::istream> OutputStreamData;
-  StreamData<std::istream> ErrorStreamData;
+  StreamData InputStreamData;
+  StreamData OutputStreamData;
+  StreamData ErrorStreamData;
   cm::uv_pipe_ptr TempOutputPipe;
   cm::uv_pipe_ptr TempErrorPipe;
 
@@ -215,12 +194,7 @@ bool cmUVProcessChain::InternalData::Prepare(
         return false;
       }
 
-      if (errorData.BuiltinStream.init(*this->Loop, 0) < 0) {
-        return false;
-      }
-      if (uv_pipe_open(errorData.BuiltinStream, pipeFd[0]) < 0) {
-        return false;
-      }
+      errorData.BuiltinStream = pipeFd[0];
       errorData.Stdio.flags = UV_INHERIT_FD;
       errorData.Stdio.data.fd = pipeFd[1];
 
@@ -231,7 +205,6 @@ bool cmUVProcessChain::InternalData::Prepare(
         return false;
       }
 
-      errorData.Streambuf.open(errorData.BuiltinStream);
       break;
     }
 
@@ -251,6 +224,7 @@ bool cmUVProcessChain::InternalData::Prepare(
 
     case cmUVProcessChainBuilder::Builtin:
       if (this->Builder->MergedBuiltinStreams) {
+        outputData.BuiltinStream = errorData.BuiltinStream;
         outputData.Stdio.flags = UV_INHERIT_FD;
         outputData.Stdio.data.fd = errorData.Stdio.data.fd;
       } else {
@@ -259,12 +233,7 @@ bool cmUVProcessChain::InternalData::Prepare(
           return false;
         }
 
-        if (outputData.BuiltinStream.init(*this->Loop, 0) < 0) {
-          return false;
-        }
-        if (uv_pipe_open(outputData.BuiltinStream, pipeFd[0]) < 0) {
-          return false;
-        }
+        outputData.BuiltinStream = pipeFd[0];
         outputData.Stdio.flags = UV_INHERIT_FD;
         outputData.Stdio.data.fd = pipeFd[1];
 
@@ -274,8 +243,6 @@ bool cmUVProcessChain::InternalData::Prepare(
         if (uv_pipe_open(this->TempOutputPipe, outputData.Stdio.data.fd) < 0) {
           return false;
         }
-
-        outputData.Streambuf.open(outputData.BuiltinStream);
       }
       break;
 
@@ -411,17 +378,14 @@ uv_loop_t& cmUVProcessChain::GetLoop()
   return *this->Data->Loop;
 }
 
-std::istream* cmUVProcessChain::OutputStream()
+int cmUVProcessChain::OutputStream()
 {
-  if (this->Data->Builder->MergedBuiltinStreams) {
-    return this->Data->ErrorStreamData.GetBuiltinStream();
-  }
-  return this->Data->OutputStreamData.GetBuiltinStream();
+  return this->Data->OutputStreamData.BuiltinStream;
 }
 
-std::istream* cmUVProcessChain::ErrorStream()
+int cmUVProcessChain::ErrorStream()
 {
-  return this->Data->ErrorStreamData.GetBuiltinStream();
+  return this->Data->ErrorStreamData.BuiltinStream;
 }
 
 bool cmUVProcessChain::Valid() const

+ 2 - 3
Source/cmUVProcessChain.h

@@ -5,7 +5,6 @@
 #include <array>
 #include <cstddef> // IWYU pragma: keep
 #include <cstdint>
-#include <iosfwd>
 #include <memory>
 #include <string>
 #include <utility>
@@ -99,8 +98,8 @@ public:
   uv_loop_t& GetLoop();
 
   // FIXME: Add stdin support
-  std::istream* OutputStream();
-  std::istream* ErrorStream();
+  int OutputStream();
+  int ErrorStream();
 
   bool Valid() const;
   bool Wait(int64_t milliseconds = -1);

+ 140 - 0
Source/cmUVStream.h

@@ -0,0 +1,140 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#pragma once
+
+#include <cassert>
+#include <istream>
+
+#include <cm3p/uv.h>
+
+#include "cmUVHandlePtr.h"
+#include "cmUVStreambuf.h"
+
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+class cmBasicUVIStream : public std::basic_istream<CharT>
+{
+public:
+  cmBasicUVIStream();
+  cmBasicUVIStream(uv_stream_t* stream);
+
+  bool is_open() const;
+
+  void open(uv_stream_t* stream);
+
+  void close();
+
+private:
+  cmBasicUVStreambuf<CharT, Traits> Buffer;
+};
+
+template <typename CharT, typename Traits>
+cmBasicUVIStream<CharT, Traits>::cmBasicUVIStream()
+  : std::basic_istream<CharT, Traits>(&this->Buffer)
+{
+}
+
+template <typename CharT, typename Traits>
+cmBasicUVIStream<CharT, Traits>::cmBasicUVIStream(uv_stream_t* stream)
+  : cmBasicUVIStream()
+{
+  this->open(stream);
+}
+
+template <typename CharT, typename Traits>
+bool cmBasicUVIStream<CharT, Traits>::is_open() const
+{
+  return this->Buffer.is_open();
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVIStream<CharT, Traits>::open(uv_stream_t* stream)
+{
+  this->Buffer.open(stream);
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVIStream<CharT, Traits>::close()
+{
+  this->Buffer.close();
+}
+
+using cmUVIStream = cmBasicUVIStream<char>;
+
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+class cmBasicUVPipeIStream : public cmBasicUVIStream<CharT, Traits>
+{
+public:
+  cmBasicUVPipeIStream();
+  cmBasicUVPipeIStream(uv_loop_t& loop, int fd);
+
+  using cmBasicUVIStream<CharT, Traits>::is_open;
+
+  void open(uv_loop_t& loop, int fd);
+
+  void close();
+
+private:
+  cm::uv_pipe_ptr Pipe;
+};
+
+template <typename CharT, typename Traits>
+cmBasicUVPipeIStream<CharT, Traits>::cmBasicUVPipeIStream() = default;
+
+template <typename CharT, typename Traits>
+cmBasicUVPipeIStream<CharT, Traits>::cmBasicUVPipeIStream(uv_loop_t& loop,
+                                                          int fd)
+{
+  this->open(loop, fd);
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVPipeIStream<CharT, Traits>::open(uv_loop_t& loop, int fd)
+{
+  this->Pipe.init(loop, 0);
+  uv_pipe_open(this->Pipe, fd);
+  this->cmBasicUVIStream<CharT, Traits>::open(this->Pipe);
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVPipeIStream<CharT, Traits>::close()
+{
+  this->cmBasicUVIStream<CharT, Traits>::close();
+  this->Pipe.reset();
+}
+
+using cmUVPipeIStream = cmBasicUVPipeIStream<char>;
+
+template <typename ReadCallback, typename FinishCallback>
+void cmUVStreamRead(uv_stream_t* stream, ReadCallback onRead,
+                    FinishCallback onFinish)
+{
+  struct ReadData
+  {
+    std::vector<char> Buffer;
+    ReadCallback OnRead;
+    FinishCallback OnFinish;
+  };
+
+  stream->data = new ReadData{ {}, std::move(onRead), std::move(onFinish) };
+  uv_read_start(
+    stream,
+    [](uv_handle_t* s, std::size_t suggestedSize, uv_buf_t* buffer) {
+      auto* data = static_cast<ReadData*>(s->data);
+      data->Buffer.resize(suggestedSize);
+      buffer->base = data->Buffer.data();
+      buffer->len = suggestedSize;
+    },
+    [](uv_stream_t* s, ssize_t nread, const uv_buf_t* buffer) {
+      auto* data = static_cast<ReadData*>(s->data);
+      if (nread > 0) {
+        (void)buffer;
+        assert(buffer->base == data->Buffer.data());
+        data->Buffer.resize(nread);
+        data->OnRead(std::move(data->Buffer));
+      } else if (nread < 0 /*|| nread == UV_EOF*/) {
+        data->OnFinish();
+        uv_read_stop(s);
+        delete data;
+      }
+    });
+}

+ 2 - 1
Source/cmUVStreambuf.h

@@ -14,7 +14,8 @@
 /*
  * This file is based on example code from:
  *
- * http://www.voidcn.com/article/p-vjnlygmc-gy.html
+ * https://web.archive.org/web/20170515211805/
+ *     http://www.mr-edd.co.uk/blog/beginners_guide_streambuf
  *
  * The example code was distributed under the following license:
  *

+ 5 - 8
Source/cmcmd.cxx

@@ -28,6 +28,7 @@
 #include "cmSystemTools.h"
 #include "cmTransformDepfile.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmUtils.hxx"
 #include "cmValue.h"
 #include "cmVersion.h"
@@ -2017,10 +2018,8 @@ int cmcmd::RunPreprocessor(const std::vector<std::string>& command,
     return 1;
   }
   if (process.GetStatus(0).ExitStatus != 0) {
-    auto* errorStream = process.ErrorStream();
-    if (errorStream) {
-      std::cerr << errorStream->rdbuf();
-    }
+    cmUVPipeIStream errorStream(process.GetLoop(), process.ErrorStream());
+    std::cerr << errorStream.rdbuf();
 
     return 1;
   }
@@ -2144,10 +2143,8 @@ int cmcmd::RunLLVMRC(std::vector<std::string> const& args)
     return result;
   }
   if (process.GetStatus(0).ExitStatus != 0) {
-    auto* errorStream = process.ErrorStream();
-    if (errorStream) {
-      std::cerr << errorStream->rdbuf();
-    }
+    cmUVPipeIStream errorStream(process.GetLoop(), process.ErrorStream());
+    std::cerr << errorStream.rdbuf();
     return 1;
   }
 

+ 30 - 21
Tests/CMakeLib/testUVProcessChain.cxx

@@ -17,6 +17,7 @@
 #include "cmStringAlgorithms.h"
 #include "cmUVHandlePtr.h"
 #include "cmUVProcessChain.h"
+#include "cmUVStream.h"
 #include "cmUVStreambuf.h"
 
 struct ExpectedStatus
@@ -301,16 +302,19 @@ bool testUVProcessChainBuiltin(const char* helperCommand)
     return false;
   }
 
-  if (!chain->OutputStream()) {
-    std::cout << "OutputStream() was null, expecting not null" << std::endl;
+  if (chain->OutputStream() < 0) {
+    std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
     return false;
   }
-  if (!chain->ErrorStream()) {
-    std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+  if (chain->ErrorStream() < 0) {
+    std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
     return false;
   }
 
-  if (!checkOutput(*chain->OutputStream(), *chain->ErrorStream())) {
+  cmUVPipeIStream output(chain->GetLoop(), chain->OutputStream());
+  cmUVPipeIStream error(chain->GetLoop(), chain->ErrorStream());
+
+  if (!checkOutput(output, error)) {
     return false;
   }
 
@@ -330,12 +334,12 @@ bool testUVProcessChainBuiltinMerged(const char* helperCommand)
     return false;
   }
 
-  if (!chain->OutputStream()) {
-    std::cout << "OutputStream() was null, expecting not null" << std::endl;
+  if (chain->OutputStream() < 0) {
+    std::cout << "OutputStream() was invalid, expecting valid" << std::endl;
     return false;
   }
-  if (!chain->ErrorStream()) {
-    std::cout << "ErrorStream() was null, expecting not null" << std::endl;
+  if (chain->ErrorStream() < 0) {
+    std::cout << "ErrorStream() was invalid, expecting valid" << std::endl;
     return false;
   }
   if (chain->OutputStream() != chain->ErrorStream()) {
@@ -344,7 +348,9 @@ bool testUVProcessChainBuiltinMerged(const char* helperCommand)
     return false;
   }
 
-  std::string merged = getInput(*chain->OutputStream());
+  cmUVPipeIStream mergedStream(chain->GetLoop(), chain->OutputStream());
+
+  std::string merged = getInput(mergedStream);
   auto qemuErrorPos = merged.find("qemu:");
   if (qemuErrorPos != std::string::npos) {
     merged.resize(qemuErrorPos);
@@ -398,12 +404,12 @@ bool testUVProcessChainExternal(const char* helperCommand)
     return false;
   }
 
-  if (chain->OutputStream()) {
-    std::cout << "OutputStream() was not null, expecting null" << std::endl;
+  if (chain->OutputStream() >= 0) {
+    std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
     return false;
   }
-  if (chain->ErrorStream()) {
-    std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+  if (chain->ErrorStream() >= 0) {
+    std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
     return false;
   }
 
@@ -446,12 +452,12 @@ bool testUVProcessChainNone(const char* helperCommand)
     return false;
   }
 
-  if (chain->OutputStream()) {
-    std::cout << "OutputStream() was not null, expecting null" << std::endl;
+  if (chain->OutputStream() >= 0) {
+    std::cout << "OutputStream() was valid, expecting invalid" << std::endl;
     return false;
   }
-  if (chain->ErrorStream()) {
-    std::cout << "ErrorStream() was not null, expecting null" << std::endl;
+  if (chain->ErrorStream() >= 0) {
+    std::cout << "ErrorStream() was valid, expecting invalid" << std::endl;
     return false;
   }
 
@@ -473,7 +479,8 @@ bool testUVProcessChainCwdUnchanged(const char* helperCommand)
     return false;
   }
 
-  auto cwd = getInput(*chain.OutputStream());
+  cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
+  auto cwd = getInput(output);
   if (!cmHasLiteralSuffix(cwd, "/Tests/CMakeLib")) {
     std::cout << "Working directory was \"" << cwd
               << "\", expected to end in \"/Tests/CMakeLib\"" << std::endl;
@@ -499,7 +506,8 @@ bool testUVProcessChainCwdChanged(const char* helperCommand)
     return false;
   }
 
-  auto cwd = getInput(*chain.OutputStream());
+  cmUVPipeIStream output(chain.GetLoop(), chain.OutputStream());
+  auto cwd = getInput(output);
   if (!cmHasLiteralSuffix(cwd, "/Tests")) {
     std::cout << "Working directory was \"" << cwd
               << "\", expected to end in \"/Tests\"" << std::endl;
@@ -633,7 +641,8 @@ bool testUVProcessChainInputFile(const char* helperCommand)
     return false;
   }
 
-  std::string output = getInput(*chain.OutputStream());
+  cmUVPipeIStream stream(chain.GetLoop(), chain.OutputStream());
+  std::string output = getInput(stream);
   if (output != "HELO WRD!") {
     std::cout << "Output was \"" << output << "\", expected \"HELO WRD!\""
               << std::endl;

+ 97 - 0
Tests/CMakeLib/testUVStreambuf.cxx

@@ -3,11 +3,14 @@
 #include <string>
 #include <vector>
 
+#include <cmext/algorithm>
+
 #include <cm3p/uv.h>
 #include <stdint.h>
 
 #include "cmGetPipes.h"
 #include "cmUVHandlePtr.h"
+#include "cmUVStream.h"
 #include "cmUVStreambuf.h"
 
 #define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
@@ -437,6 +440,90 @@ end:
   return success;
 }
 
+bool testUVPipeIStream()
+{
+  int pipe[] = { -1, -1 };
+  if (cmGetPipes(pipe) < 0) {
+    std::cout << "cmGetPipes() returned an error" << std::endl;
+    return false;
+  }
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+  cm::uv_pipe_ptr pipeSink;
+  pipeSink.init(*loop, 0);
+  uv_pipe_open(pipeSink, pipe[1]);
+
+  std::string str = "Hello world!\n";
+  uv_write_t writeReq;
+  uv_buf_t buf;
+  buf.base = &str.front();
+  buf.len = str.length();
+  uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  cmUVPipeIStream pin(*loop, pipe[0]);
+  std::string line;
+  std::getline(pin, line);
+  if (line != "Hello world!") {
+    std::cout << "Line was \"" << line << "\", should be \"Hello world!\""
+              << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVStreamRead()
+{
+  int pipe[] = { -1, -1 };
+  if (cmGetPipes(pipe) < 0) {
+    std::cout << "cmGetPipes() returned an error" << std::endl;
+    return false;
+  }
+
+  cm::uv_loop_ptr loop;
+  loop.init();
+  cm::uv_pipe_ptr pipeSink;
+  pipeSink.init(*loop, 0);
+  uv_pipe_open(pipeSink, pipe[1]);
+
+  std::string str = "Hello world!";
+  uv_write_t writeReq;
+  uv_buf_t buf;
+  buf.base = &str.front();
+  buf.len = str.length();
+  uv_write(&writeReq, pipeSink, &buf, 1, nullptr);
+  uv_run(loop, UV_RUN_DEFAULT);
+  pipeSink.reset();
+
+  cm::uv_pipe_ptr pipeSource;
+  pipeSource.init(*loop, 0);
+  uv_pipe_open(pipeSource, pipe[0]);
+
+  std::string output;
+  bool finished = false;
+  cmUVStreamRead(
+    pipeSource,
+    [&output](std::vector<char> data) { cm::append(output, data); },
+    [&output, &finished]() {
+      if (output != "Hello world!") {
+        std::cout << "Output was \"" << output
+                  << "\", should be \"Hello world!\"" << std::endl;
+        return;
+      }
+      finished = true;
+    });
+  uv_run(loop, UV_RUN_DEFAULT);
+
+  if (!finished) {
+    std::cout << "finished was not set" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
 int testUVStreambuf(int argc, char** const argv)
 {
   if (argc < 2) {
@@ -454,5 +541,15 @@ int testUVStreambuf(int argc, char** const argv)
     return -1;
   }
 
+  if (!testUVPipeIStream()) {
+    std::cout << "While executing testUVPipeIStream().\n";
+    return -1;
+  }
+
+  if (!testUVStreamRead()) {
+    std::cout << "While executing testUVPipeIStream().\n";
+    return -1;
+  }
+
   return 0;
 }

+ 7 - 2
Utilities/std/cmext/algorithm

@@ -16,6 +16,7 @@
 
 #if defined(__SUNPRO_CC) && defined(__sparc)
 #  include <list>
+#  include <string>
 #  include <vector>
 #endif
 
@@ -67,11 +68,15 @@ namespace cm {
     APPEND_TWO(C1, C2)                                                        \
     APPEND_TWO(C2, C1)
 
-// For now, manage only support for std::vector and std::list.
-// Other sequential container support can be added if needed.
+// For now, manage only support for std::vector, std::list, and
+// std::basic_string. Other sequential container support can be added if
+// needed.
 APPEND(std::vector)
 APPEND(std::list)
+APPEND(std::basic_string)
 APPEND_MIX(std::vector, std::list)
+APPEND_MIX(std::vector, std::basic_string)
+APPEND_MIX(std::list, std::basic_string)
 
 #  undef APPEND
 #  undef APPEND_MIX