[Cmake-commits] CMake branch, master, updated. v3.14.3-794-gea026fb

Kitware Robot kwrobot at kitware.com
Tue Apr 30 10:43:08 EDT 2019


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  ea026fb2198ebd47353270461059183bbff867bd (commit)
       via  c74698cb75b9923517f7f87eacf04ca0eefc8e72 (commit)
       via  8cfd25db711c22f9478e565a496145577df24d77 (commit)
       via  c0e6b22d0a81c0e0dc1ee87366790882a3d54667 (commit)
      from  40852eed8e304dffd7254d6790850e0a73766d9e (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=ea026fb2198ebd47353270461059183bbff867bd
commit ea026fb2198ebd47353270461059183bbff867bd
Merge: 40852ee c74698c
Author:     Brad King <brad.king at kitware.com>
AuthorDate: Tue Apr 30 14:34:59 2019 +0000
Commit:     Kitware Robot <kwrobot at kitware.com>
CommitDate: Tue Apr 30 10:35:11 2019 -0400

    Merge topic 'cmuvstreambuf'
    
    c74698cb75 cmUVStreambuf: Add std::streambuf implementation for uv_stream_t
    8cfd25db71 cmUVHandlePtr: Add cm::uv_loop_ptr
    c0e6b22d0a Refactor: Move/rename cmProcessGetPipes() to cmGetPipes()
    
    Acked-by: Kitware Robot <kwrobot at kitware.com>
    Merge-request: !3240


https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c74698cb75b9923517f7f87eacf04ca0eefc8e72
commit c74698cb75b9923517f7f87eacf04ca0eefc8e72
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Tue Apr 16 17:58:02 2019 -0400
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Thu Apr 25 12:14:00 2019 -0400

    cmUVStreambuf: Add std::streambuf implementation for uv_stream_t
    
    This will allow std::istream/std::ostream-based interaction with
    processes spawned by libuv.

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 6fa046c..01c6cd7 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -388,6 +388,7 @@ set(SRCS
   cmUuid.cxx
   cmUVHandlePtr.cxx
   cmUVHandlePtr.h
+  cmUVStreambuf.h
   cmUVSignalHackRAII.h
   cmVariableWatch.cxx
   cmVariableWatch.h
diff --git a/Source/cmUVStreambuf.h b/Source/cmUVStreambuf.h
new file mode 100644
index 0000000..29e4fde
--- /dev/null
+++ b/Source/cmUVStreambuf.h
@@ -0,0 +1,219 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmUVStreambuf_h
+#define cmUVStreambuf_h
+
+#include "cmUVHandlePtr.h"
+
+#include "cm_uv.h"
+
+#include <algorithm>
+#include <cstring>
+#include <streambuf>
+#include <vector>
+
+/*
+ * This file is based on example code from:
+ *
+ * http://www.voidcn.com/article/p-vjnlygmc-gy.html
+ *
+ * The example code was distributed under the following license:
+ *
+ * Copyright 2007 Edd Dawson.
+ * Distributed under the Boost Software License, Version 1.0.
+ *
+ * Boost Software License - Version 1.0 - August 17th, 2003
+ *
+ * Permission is hereby granted, free of charge, to any person or organization
+ * obtaining a copy of the software and accompanying documentation covered by
+ * this license (the "Software") to use, reproduce, display, distribute,
+ * execute, and transmit the Software, and to prepare derivative works of the
+ * Software, and to permit third-parties to whom the Software is furnished to
+ * do so, all subject to the following:
+ *
+ * The copyright notices in the Software and this entire statement, including
+ * the above license grant, this restriction and the following disclaimer,
+ * must be included in all copies of the Software, in whole or in part, and
+ * all derivative works of the Software, unless such copies or derivative
+ * works are solely in the form of machine-executable object code generated by
+ * a source language processor.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+ * SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+ * FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+template <typename CharT, typename Traits = std::char_traits<CharT>>
+class cmBasicUVStreambuf : public std::basic_streambuf<CharT, Traits>
+{
+public:
+  cmBasicUVStreambuf(std::size_t bufSize = 256, std::size_t putBack = 8);
+  ~cmBasicUVStreambuf() override;
+
+  bool is_open() const;
+
+  cmBasicUVStreambuf* open(uv_stream_t* stream);
+
+  cmBasicUVStreambuf* close();
+
+protected:
+  typename cmBasicUVStreambuf::int_type underflow() override;
+  std::streamsize showmanyc() override;
+
+  // FIXME: Add write support
+
+private:
+  uv_stream_t* Stream = nullptr;
+  void* OldStreamData;
+  const std::size_t PutBack;
+  std::vector<CharT> InputBuffer;
+  bool EndOfFile;
+
+  void StreamReadStartStop();
+
+  void StreamRead(ssize_t nread);
+  void HandleAlloc(uv_buf_t* buf);
+};
+
+template <typename CharT, typename Traits>
+cmBasicUVStreambuf<CharT, Traits>::cmBasicUVStreambuf(std::size_t bufSize,
+                                                      std::size_t putBack)
+  : PutBack(std::max<std::size_t>(putBack, 1))
+  , InputBuffer(std::max<std::size_t>(this->PutBack, bufSize) + this->PutBack)
+{
+  this->close();
+}
+
+template <typename CharT, typename Traits>
+cmBasicUVStreambuf<CharT, Traits>::~cmBasicUVStreambuf()
+{
+  this->close();
+}
+
+template <typename CharT, typename Traits>
+bool cmBasicUVStreambuf<CharT, Traits>::is_open() const
+{
+  return this->Stream != nullptr;
+}
+
+template <typename CharT, typename Traits>
+cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::open(
+  uv_stream_t* stream)
+{
+  this->close();
+  this->Stream = stream;
+  this->EndOfFile = false;
+  if (this->Stream) {
+    this->OldStreamData = this->Stream->data;
+    this->Stream->data = this;
+  }
+  this->StreamReadStartStop();
+  return this;
+}
+
+template <typename CharT, typename Traits>
+cmBasicUVStreambuf<CharT, Traits>* cmBasicUVStreambuf<CharT, Traits>::close()
+{
+  if (this->Stream) {
+    uv_read_stop(this->Stream);
+    this->Stream->data = this->OldStreamData;
+  }
+  this->Stream = nullptr;
+  CharT* readEnd = this->InputBuffer.data() + this->InputBuffer.size();
+  this->setg(readEnd, readEnd, readEnd);
+  return this;
+}
+
+template <typename CharT, typename Traits>
+typename cmBasicUVStreambuf<CharT, Traits>::int_type
+cmBasicUVStreambuf<CharT, Traits>::underflow()
+{
+  if (!this->is_open()) {
+    return Traits::eof();
+  }
+
+  if (this->gptr() < this->egptr()) {
+    return Traits::to_int_type(*this->gptr());
+  }
+
+  this->StreamReadStartStop();
+  while (this->in_avail() == 0) {
+    uv_run(this->Stream->loop, UV_RUN_ONCE);
+  }
+  if (this->in_avail() == -1) {
+    return Traits::eof();
+  }
+  return Traits::to_int_type(*this->gptr());
+}
+
+template <typename CharT, typename Traits>
+std::streamsize cmBasicUVStreambuf<CharT, Traits>::showmanyc()
+{
+  if (!this->is_open() || this->EndOfFile) {
+    return -1;
+  }
+  return 0;
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVStreambuf<CharT, Traits>::StreamReadStartStop()
+{
+  if (this->Stream) {
+    uv_read_stop(this->Stream);
+    if (this->gptr() >= this->egptr()) {
+      uv_read_start(
+        this->Stream,
+        [](uv_handle_t* handle, size_t /* unused */, uv_buf_t* buf) {
+          auto streambuf =
+            static_cast<cmBasicUVStreambuf<CharT, Traits>*>(handle->data);
+          streambuf->HandleAlloc(buf);
+        },
+        [](uv_stream_t* stream2, ssize_t nread, const uv_buf_t* /* unused */) {
+          auto streambuf =
+            static_cast<cmBasicUVStreambuf<CharT, Traits>*>(stream2->data);
+          streambuf->StreamRead(nread);
+        });
+    }
+  }
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVStreambuf<CharT, Traits>::HandleAlloc(uv_buf_t* buf)
+{
+  auto size = this->egptr() - this->gptr();
+  std::memmove(this->InputBuffer.data(), this->gptr(),
+               this->egptr() - this->gptr());
+  this->setg(this->InputBuffer.data(), this->InputBuffer.data(),
+             this->InputBuffer.data() + size);
+  buf->base = this->egptr();
+#ifdef _WIN32
+#  define BUF_LEN_TYPE ULONG
+#else
+#  define BUF_LEN_TYPE size_t
+#endif
+  buf->len = BUF_LEN_TYPE(
+    (this->InputBuffer.data() + this->InputBuffer.size() - this->egptr()) *
+    sizeof(CharT));
+#undef BUF_LEN_TYPE
+}
+
+template <typename CharT, typename Traits>
+void cmBasicUVStreambuf<CharT, Traits>::StreamRead(ssize_t nread)
+{
+  if (nread > 0) {
+    this->setg(this->eback(), this->gptr(),
+               this->egptr() + nread / sizeof(CharT));
+    uv_read_stop(this->Stream);
+  } else if (nread < 0 || nread == UV_EOF) {
+    this->EndOfFile = true;
+    uv_read_stop(this->Stream);
+  }
+}
+
+using cmUVStreambuf = cmBasicUVStreambuf<char>;
+
+#endif
diff --git a/Tests/CMakeLib/CMakeLists.txt b/Tests/CMakeLib/CMakeLists.txt
index 031ab01..e04bba2 100644
--- a/Tests/CMakeLib/CMakeLists.txt
+++ b/Tests/CMakeLib/CMakeLists.txt
@@ -16,9 +16,11 @@ set(CMakeLib_TESTS
   testXMLSafe.cxx
   testFindPackageCommand.cxx
   testUVRAII.cxx
+  testUVStreambuf.cxx
   )
 
 set(testRST_ARGS ${CMAKE_CURRENT_SOURCE_DIR})
+set(testUVStreambuf_ARGS $<TARGET_FILE:cmake>)
 
 if(WIN32)
   list(APPEND CMakeLib_TESTS
@@ -43,7 +45,7 @@ target_link_libraries(testEncoding cmsys)
 
 foreach(testfile ${CMakeLib_TESTS})
   get_filename_component(test "${testfile}" NAME_WE)
-  add_test(CMakeLib.${test} CMakeLibTests ${test} ${${test}_ARGS})
+  add_test(NAME CMakeLib.${test} COMMAND CMakeLibTests ${test} ${${test}_ARGS})
 endforeach()
 
 if(TEST_CompileCommandOutput)
diff --git a/Tests/CMakeLib/testUVStreambuf.cxx b/Tests/CMakeLib/testUVStreambuf.cxx
new file mode 100644
index 0000000..39655f3
--- /dev/null
+++ b/Tests/CMakeLib/testUVStreambuf.cxx
@@ -0,0 +1,457 @@
+#include "cmUVStreambuf.h"
+
+#include "cmGetPipes.h"
+#include "cmUVHandlePtr.h"
+
+#include "cm_uv.h"
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <cstring>
+
+#include <stdint.h>
+
+#define TEST_STR_LINE_1 "This string must be exactly 128 characters long so"
+#define TEST_STR_LINE_2 "that we can test CMake's std::streambuf integration"
+#define TEST_STR_LINE_3 "with libuv's uv_stream_t."
+#define TEST_STR TEST_STR_LINE_1 "\n" TEST_STR_LINE_2 "\n" TEST_STR_LINE_3
+
+bool writeDataToStreamPipe(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe,
+                           char* outputData, unsigned int outputDataLength,
+                           const char* /* unused */)
+{
+  int err;
+
+  // Create the pipe
+  int pipeHandles[2];
+  if (cmGetPipes(pipeHandles) < 0) {
+    std::cout << "Could not open pipe" << std::endl;
+    return false;
+  }
+
+  cm::uv_pipe_ptr outputPipe;
+  inputPipe.init(loop, 0);
+  outputPipe.init(loop, 0);
+  uv_pipe_open(inputPipe, pipeHandles[0]);
+  uv_pipe_open(outputPipe, pipeHandles[1]);
+
+  // Write data for reading
+  uv_write_t writeReq;
+  struct WriteCallbackData
+  {
+    bool Finished = false;
+    int Status;
+  } writeData;
+  writeReq.data = &writeData;
+  uv_buf_t outputBuf;
+  outputBuf.base = outputData;
+  outputBuf.len = outputDataLength;
+  if ((err = uv_write(&writeReq, outputPipe, &outputBuf, 1,
+                      [](uv_write_t* req, int status) {
+                        auto data = static_cast<WriteCallbackData*>(req->data);
+                        data->Finished = true;
+                        data->Status = status;
+                      })) < 0) {
+    std::cout << "Could not write to pipe: " << uv_strerror(err) << std::endl;
+    return false;
+  }
+  while (!writeData.Finished) {
+    uv_run(&loop, UV_RUN_ONCE);
+  }
+  if (writeData.Status < 0) {
+    std::cout << "Status is " << uv_strerror(writeData.Status)
+              << ", should be 0" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool writeDataToStreamProcess(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe,
+                              char* outputData, unsigned int /* unused */,
+                              const char* cmakeCommand)
+{
+  int err;
+
+  inputPipe.init(loop, 0);
+  std::vector<std::string> arguments = { cmakeCommand, "-E", "echo_append",
+                                         outputData };
+  std::vector<const char*> processArgs;
+  for (auto const& arg : arguments) {
+    processArgs.push_back(arg.c_str());
+  }
+  processArgs.push_back(nullptr);
+  std::vector<uv_stdio_container_t> stdio(3);
+  stdio[0].flags = UV_IGNORE;
+  stdio[0].data.stream = nullptr;
+  stdio[1].flags =
+    static_cast<uv_stdio_flags>(UV_CREATE_PIPE | UV_WRITABLE_PIPE);
+  stdio[1].data.stream = inputPipe;
+  stdio[2].flags = UV_IGNORE;
+  stdio[2].data.stream = nullptr;
+
+  struct ProcessExitData
+  {
+    bool Finished = false;
+    int64_t ExitStatus;
+    int TermSignal;
+  } exitData;
+  cm::uv_process_ptr process;
+  auto options = uv_process_options_t();
+  options.file = cmakeCommand;
+  options.args = const_cast<char**>(processArgs.data());
+  options.flags = UV_PROCESS_WINDOWS_HIDE;
+  options.stdio = stdio.data();
+  options.stdio_count = static_cast<int>(stdio.size());
+  options.exit_cb = [](uv_process_t* handle, int64_t exitStatus,
+                       int termSignal) {
+    auto data = static_cast<ProcessExitData*>(handle->data);
+    data->Finished = true;
+    data->ExitStatus = exitStatus;
+    data->TermSignal = termSignal;
+  };
+  if ((err = process.spawn(loop, options, &exitData)) < 0) {
+    std::cout << "Could not spawn process: " << uv_strerror(err) << std::endl;
+    return false;
+  }
+  while (!exitData.Finished) {
+    uv_run(&loop, UV_RUN_ONCE);
+  }
+  if (exitData.ExitStatus != 0) {
+    std::cout << "Process exit status is " << exitData.ExitStatus
+              << ", should be 0" << std::endl;
+    return false;
+  }
+  if (exitData.TermSignal != 0) {
+    std::cout << "Process term signal is " << exitData.TermSignal
+              << ", should be 0" << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool testUVStreambufRead(
+  bool (*cb)(uv_loop_t& loop, cm::uv_pipe_ptr& inputPipe, char* outputData,
+             unsigned int outputDataLength, const char* cmakeCommand),
+  const char* cmakeCommand)
+{
+  char outputData[] = TEST_STR;
+  bool success = false;
+  cm::uv_loop_ptr loop;
+  loop.init();
+  cm::uv_pipe_ptr inputPipe;
+  std::vector<char> inputData(128);
+  std::streamsize readLen;
+  std::string line;
+  cm::uv_timer_ptr timer;
+
+  // Create the streambuf
+  cmUVStreambuf inputBuf(64);
+  std::istream inputStream(&inputBuf);
+  if (inputBuf.is_open()) {
+    std::cout << "is_open() is true, should be false" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+
+  // Perform first read test - read all the data
+  if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
+    goto end;
+  }
+  inputBuf.open(inputPipe);
+  if (!inputBuf.is_open()) {
+    std::cout << "is_open() is false, should be true" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) {
+    std::cout << "sgetn() returned " << readLen << ", should be 128"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if (std::memcmp(inputData.data(), outputData, 128)) {
+    std::cout << "Read data does not match write data" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+  inputData.assign(128, char{});
+  inputBuf.close();
+  if (inputBuf.is_open()) {
+    std::cout << "is_open() is true, should be false" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+
+  // Perform second read test - read some data and then close
+  if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
+    goto end;
+  }
+  inputBuf.open(inputPipe);
+  if (!inputBuf.is_open()) {
+    std::cout << "is_open() is false, should be true" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 64)) != 64) {
+    std::cout << "sgetn() returned " << readLen << ", should be 64"
+              << std::endl;
+    goto end;
+  }
+  if (std::memcmp(inputData.data(), outputData, 64)) {
+    std::cout << "Read data does not match write data" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 8) {
+    std::cout << "in_avail() returned " << readLen << ", should be 8"
+              << std::endl;
+    goto end;
+  }
+  inputData.assign(128, char{});
+  inputBuf.close();
+  if (inputBuf.is_open()) {
+    std::cout << "is_open() is true, should be false" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+
+  // Perform third read test - read line by line
+  if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
+    goto end;
+  }
+  inputBuf.open(inputPipe);
+  if (!inputBuf.is_open()) {
+    std::cout << "is_open() is false, should be true" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+
+  if (!std::getline(inputStream, line)) {
+    std::cout << "getline returned false, should be true" << std::endl;
+    goto end;
+  }
+  if (line != TEST_STR_LINE_1) {
+    std::cout << "Line 1 is \"" << line
+              << "\", should be \"" TEST_STR_LINE_1 "\"" << std::endl;
+    goto end;
+  }
+
+  if (!std::getline(inputStream, line)) {
+    std::cout << "getline returned false, should be true" << std::endl;
+    goto end;
+  }
+  if (line != TEST_STR_LINE_2) {
+    std::cout << "Line 2 is \"" << line
+              << "\", should be \"" TEST_STR_LINE_2 "\"" << std::endl;
+    goto end;
+  }
+
+  if (!std::getline(inputStream, line)) {
+    std::cout << "getline returned false, should be true" << std::endl;
+    goto end;
+  }
+  if (line != TEST_STR_LINE_3) {
+    std::cout << "Line 3 is \"" << line
+              << "\", should be \"" TEST_STR_LINE_3 "\"" << std::endl;
+    goto end;
+  }
+
+  if (std::getline(inputStream, line)) {
+    std::cout << "getline returned true, should be false" << std::endl;
+    goto end;
+  }
+
+  inputBuf.close();
+  if (inputBuf.is_open()) {
+    std::cout << "is_open() is true, should be false" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+
+  // Perform fourth read test - run the event loop outside of underflow()
+  if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
+    goto end;
+  }
+  inputBuf.open(inputPipe);
+  if (!inputBuf.is_open()) {
+    std::cout << "is_open() is false, should be true" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  uv_run(loop, UV_RUN_DEFAULT);
+  if ((readLen = inputBuf.in_avail()) != 72) {
+    std::cout << "in_avail() returned " << readLen << ", should be 72"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 128) {
+    std::cout << "sgetn() returned " << readLen << ", should be 128"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 128"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+
+  inputBuf.close();
+  if (inputBuf.is_open()) {
+    std::cout << "is_open() is true, should be false" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+
+  // Perform fifth read test - close the streambuf in the middle of a read
+  timer.init(*loop, &inputBuf);
+  if (!cb(*loop, inputPipe, outputData, 128, cmakeCommand)) {
+    goto end;
+  }
+  inputBuf.open(inputPipe);
+  if (!inputBuf.is_open()) {
+    std::cout << "is_open() is false, should be true" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != 0) {
+    std::cout << "in_avail() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  uv_timer_start(timer,
+                 [](uv_timer_t* handle) {
+                   auto buf = static_cast<cmUVStreambuf*>(handle->data);
+                   buf->close();
+                 },
+                 0, 0);
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+  if (inputBuf.is_open()) {
+    std::cout << "is_open() is true, should be false" << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.in_avail()) != -1) {
+    std::cout << "in_avail() returned " << readLen << ", should be -1"
+              << std::endl;
+    goto end;
+  }
+  if ((readLen = inputBuf.sgetn(inputData.data(), 128)) != 0) {
+    std::cout << "sgetn() returned " << readLen << ", should be 0"
+              << std::endl;
+    goto end;
+  }
+
+  success = true;
+
+end:
+  return success;
+}
+
+int testUVStreambuf(int argc, char** const argv)
+{
+  if (argc < 2) {
+    std::cout << "Invalid arguments.\n";
+    return -1;
+  }
+
+  if (!testUVStreambufRead(writeDataToStreamPipe, argv[1])) {
+    std::cout << "While executing testUVStreambufRead() with pipe.\n";
+    return -1;
+  }
+
+  if (!testUVStreambufRead(writeDataToStreamProcess, argv[1])) {
+    std::cout << "While executing testUVStreambufRead() with process.\n";
+    return -1;
+  }
+
+  return 0;
+}

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=8cfd25db711c22f9478e565a496145577df24d77
commit 8cfd25db711c22f9478e565a496145577df24d77
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Tue Apr 23 13:37:26 2019 -0400
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Thu Apr 25 12:03:08 2019 -0400

    cmUVHandlePtr: Add cm::uv_loop_ptr

diff --git a/Source/cmUVHandlePtr.cxx b/Source/cmUVHandlePtr.cxx
index fd07d2d..27069ee 100644
--- a/Source/cmUVHandlePtr.cxx
+++ b/Source/cmUVHandlePtr.cxx
@@ -11,19 +11,59 @@
 
 namespace cm {
 
-static void close_delete(uv_handle_t* h)
+struct uv_loop_deleter
 {
-  free(h);
+  void operator()(uv_loop_t* loop) const;
+};
+
+void uv_loop_deleter::operator()(uv_loop_t* loop) const
+{
+  uv_run(loop, UV_RUN_DEFAULT);
+  int result = uv_loop_close(loop);
+  (void)result;
+  assert(result >= 0);
+  free(loop);
+}
+
+int uv_loop_ptr::init(void* data)
+{
+  this->reset();
+
+  this->loop.reset(static_cast<uv_loop_t*>(calloc(1, sizeof(uv_loop_t))),
+                   uv_loop_deleter());
+  this->loop->data = data;
+
+  return uv_loop_init(this->loop.get());
+}
+
+void uv_loop_ptr::reset()
+{
+  this->loop.reset();
+}
+
+uv_loop_ptr::operator uv_loop_t*()
+{
+  return this->loop.get();
+}
+
+uv_loop_t* uv_loop_ptr::operator->() const noexcept
+{
+  return this->loop.get();
+}
+
+uv_loop_t* uv_loop_ptr::get() const
+{
+  return this->loop.get();
 }
 
 template <typename T>
-static void default_delete(T* type_handle)
+static void handle_default_delete(T* type_handle)
 {
   auto handle = reinterpret_cast<uv_handle_t*>(type_handle);
   if (handle) {
     assert(!uv_is_closing(handle));
     if (!uv_is_closing(handle)) {
-      uv_close(handle, &close_delete);
+      uv_close(handle, [](uv_handle_t* h) { free(h); });
     }
   }
 }
@@ -34,7 +74,7 @@ static void default_delete(T* type_handle)
 template <typename T>
 struct uv_handle_deleter
 {
-  void operator()(T* type_handle) const { default_delete(type_handle); }
+  void operator()(T* type_handle) const { handle_default_delete(type_handle); }
 };
 
 template <typename T>
@@ -107,7 +147,7 @@ struct uv_handle_deleter<uv_async_t>
   void operator()(uv_async_t* handle)
   {
     std::lock_guard<std::mutex> lock(*handleMutex);
-    default_delete(handle);
+    handle_default_delete(handle);
   }
 };
 
@@ -136,7 +176,7 @@ struct uv_handle_deleter<uv_signal_t>
   {
     if (handle) {
       uv_signal_stop(handle);
-      default_delete(handle);
+      handle_default_delete(handle);
     }
   }
 };
diff --git a/Source/cmUVHandlePtr.h b/Source/cmUVHandlePtr.h
index 992c429..c09e4bf 100644
--- a/Source/cmUVHandlePtr.h
+++ b/Source/cmUVHandlePtr.h
@@ -30,7 +30,45 @@
 namespace cm {
 
 /***
- * RAII class to simplify and insure the safe usage of uv_*_t types. This
+ * RAII class to simplify and ensure the safe usage of uv_loop_t. This includes
+ * making sure resources are properly freed.
+ */
+class uv_loop_ptr
+{
+protected:
+  std::shared_ptr<uv_loop_t> loop;
+
+public:
+  uv_loop_ptr(uv_loop_ptr const&) = delete;
+  uv_loop_ptr& operator=(uv_loop_ptr const&) = delete;
+  uv_loop_ptr(uv_loop_ptr&&) noexcept;
+  uv_loop_ptr& operator=(uv_loop_ptr&&) noexcept;
+
+  // Dtor and ctor need to be inline defined like this for default ctors and
+  // dtors to work.  Some compilers do not like '= default' here.
+  uv_loop_ptr() {} // NOLINT(modernize-use-equals-default)
+  uv_loop_ptr(std::nullptr_t) {}
+  ~uv_loop_ptr() { this->reset(); }
+
+  int init(void* data = nullptr);
+
+  /**
+   * Properly close the handle if needed and sets the inner handle to nullptr
+   */
+  void reset();
+
+  /**
+   * Allow less verbose calling of uv_loop_* functions
+   * @return reinterpreted handle
+   */
+  operator uv_loop_t*();
+
+  uv_loop_t* get() const;
+  uv_loop_t* operator->() const noexcept;
+};
+
+/***
+ * RAII class to simplify and ensure the safe usage of uv_*_t types. This
  * includes making sure resources are properly freed and contains casting
  * operators which allow for passing into relevant uv_* functions.
  *
diff --git a/Tests/CMakeLib/testUVRAII.cxx b/Tests/CMakeLib/testUVRAII.cxx
index 1c1da76..2aeaf2c 100644
--- a/Tests/CMakeLib/testUVRAII.cxx
+++ b/Tests/CMakeLib/testUVRAII.cxx
@@ -171,11 +171,59 @@ static bool testAllMoves()
   return true;
 };
 
+static bool testLoopReset()
+{
+  bool closed = false;
+  cm::uv_loop_ptr loop;
+  loop.init();
+
+  uv_timer_t timer;
+  uv_timer_init(loop, &timer);
+  timer.data = &closed;
+  uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) {
+    auto closedPtr = static_cast<bool*>(handle->data);
+    *closedPtr = true;
+  });
+
+  loop.reset();
+  if (!closed) {
+    std::cerr << "uv_loop_ptr did not finish" << std::endl;
+    return false;
+  }
+
+  return true;
+};
+
+static bool testLoopDestructor()
+{
+  bool closed = false;
+
+  uv_timer_t timer;
+  {
+    cm::uv_loop_ptr loop;
+    loop.init();
+
+    uv_timer_init(loop, &timer);
+    timer.data = &closed;
+    uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) {
+      auto closedPtr = static_cast<bool*>(handle->data);
+      *closedPtr = true;
+    });
+  }
+
+  if (!closed) {
+    std::cerr << "uv_loop_ptr did not finish" << std::endl;
+    return false;
+  }
+
+  return true;
+};
+
 int testUVRAII(int, char** const)
 {
   if ((testAsyncShutdown() &&
        testAsyncDtor() & testAsyncMove() & testCrossAssignment() &
-         testAllMoves()) == 0) {
+         testAllMoves() & testLoopReset() & testLoopDestructor()) == 0) {
     return -1;
   }
   return 0;

https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=c0e6b22d0a81c0e0dc1ee87366790882a3d54667
commit c0e6b22d0a81c0e0dc1ee87366790882a3d54667
Author:     Kyle Edwards <kyle.edwards at kitware.com>
AuthorDate: Tue Apr 16 17:55:39 2019 -0400
Commit:     Kyle Edwards <kyle.edwards at kitware.com>
CommitDate: Thu Apr 25 12:03:08 2019 -0400

    Refactor: Move/rename cmProcessGetPipes() to cmGetPipes()

diff --git a/Source/CMakeLists.txt b/Source/CMakeLists.txt
index 49f237f..6fa046c 100644
--- a/Source/CMakeLists.txt
+++ b/Source/CMakeLists.txt
@@ -264,6 +264,8 @@ set(SRCS
   cmGeneratorExpression.h
   cmGeneratorTarget.cxx
   cmGeneratorTarget.h
+  cmGetPipes.cxx
+  cmGetPipes.h
   cmGlobalCommonGenerator.cxx
   cmGlobalCommonGenerator.h
   cmGlobalGenerator.cxx
diff --git a/Source/CTest/cmProcess.cxx b/Source/CTest/cmProcess.cxx
index bfe8d70..a2c30bb 100644
--- a/Source/CTest/cmProcess.cxx
+++ b/Source/CTest/cmProcess.cxx
@@ -5,61 +5,19 @@
 #include "cmCTest.h"
 #include "cmCTestRunTest.h"
 #include "cmCTestTestHandler.h"
+#include "cmGetPipes.h"
 #include "cmsys/Process.h"
 
-#include <fcntl.h>
 #include <iostream>
 #include <signal.h>
 #include <string>
 #if defined(_WIN32)
 #  include "cm_kwiml.h"
-#else
-#  include <unistd.h>
 #endif
 #include <utility>
 
 #define CM_PROCESS_BUF_SIZE 65536
 
-#if defined(_WIN32) && !defined(__CYGWIN__)
-#  include <io.h>
-
-static int cmProcessGetPipes(int* fds)
-{
-  SECURITY_ATTRIBUTES attr;
-  HANDLE readh, writeh;
-  attr.nLength = sizeof(attr);
-  attr.lpSecurityDescriptor = nullptr;
-  attr.bInheritHandle = FALSE;
-  if (!CreatePipe(&readh, &writeh, &attr, 0))
-    return uv_translate_sys_error(GetLastError());
-  fds[0] = _open_osfhandle((intptr_t)readh, 0);
-  fds[1] = _open_osfhandle((intptr_t)writeh, 0);
-  if (fds[0] == -1 || fds[1] == -1) {
-    CloseHandle(readh);
-    CloseHandle(writeh);
-    return uv_translate_sys_error(GetLastError());
-  }
-  return 0;
-}
-#else
-#  include <errno.h>
-
-static int cmProcessGetPipes(int* fds)
-{
-  if (pipe(fds) == -1) {
-    return uv_translate_sys_error(errno);
-  }
-
-  if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
-      fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
-    close(fds[0]);
-    close(fds[1]);
-    return uv_translate_sys_error(errno);
-  }
-  return 0;
-}
-#endif
-
 cmProcess::cmProcess(cmCTestRunTest& runner)
   : Runner(runner)
   , Conv(cmProcessOutput::UTF8, CM_PROCESS_BUF_SIZE)
@@ -120,7 +78,7 @@ bool cmProcess::StartProcess(uv_loop_t& loop, std::vector<size_t>* affinity)
   pipe_reader.init(loop, 0, this);
 
   int fds[2] = { -1, -1 };
-  status = cmProcessGetPipes(fds);
+  status = cmGetPipes(fds);
   if (status != 0) {
     cmCTestLog(this->Runner.GetCTest(), ERROR_MESSAGE,
                "Error initializing pipe: " << uv_strerror(status)
diff --git a/Source/cmGetPipes.cxx b/Source/cmGetPipes.cxx
new file mode 100644
index 0000000..ad323f7
--- /dev/null
+++ b/Source/cmGetPipes.cxx
@@ -0,0 +1,48 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#include "cmGetPipes.h"
+
+#include "cm_uv.h"
+
+#include <fcntl.h>
+
+#if defined(_WIN32) && !defined(__CYGWIN__)
+#  include <io.h>
+
+int cmGetPipes(int* fds)
+{
+  SECURITY_ATTRIBUTES attr;
+  HANDLE readh, writeh;
+  attr.nLength = sizeof(attr);
+  attr.lpSecurityDescriptor = nullptr;
+  attr.bInheritHandle = FALSE;
+  if (!CreatePipe(&readh, &writeh, &attr, 0))
+    return uv_translate_sys_error(GetLastError());
+  fds[0] = _open_osfhandle((intptr_t)readh, 0);
+  fds[1] = _open_osfhandle((intptr_t)writeh, 0);
+  if (fds[0] == -1 || fds[1] == -1) {
+    CloseHandle(readh);
+    CloseHandle(writeh);
+    return uv_translate_sys_error(GetLastError());
+  }
+  return 0;
+}
+#else
+#  include <errno.h>
+#  include <unistd.h>
+
+int cmGetPipes(int* fds)
+{
+  if (pipe(fds) == -1) {
+    return uv_translate_sys_error(errno);
+  }
+
+  if (fcntl(fds[0], F_SETFD, FD_CLOEXEC) == -1 ||
+      fcntl(fds[1], F_SETFD, FD_CLOEXEC) == -1) {
+    close(fds[0]);
+    close(fds[1]);
+    return uv_translate_sys_error(errno);
+  }
+  return 0;
+}
+#endif
diff --git a/Source/cmGetPipes.h b/Source/cmGetPipes.h
new file mode 100644
index 0000000..2a46b51
--- /dev/null
+++ b/Source/cmGetPipes.h
@@ -0,0 +1,8 @@
+/* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
+   file Copyright.txt or https://cmake.org/licensing for details.  */
+#ifndef cmGetPipes_h
+#define cmGetPipes_h
+
+int cmGetPipes(int* fds);
+
+#endif

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

Summary of changes:
 Source/CMakeLists.txt                              |   3 +
 Source/CTest/cmProcess.cxx                         |  46 +--
 Source/cmGetPipes.cxx                              |  48 +++
 .../{CPack/cmCPackConfigure.h.in => cmGetPipes.h}  |   6 +
 Source/cmUVHandlePtr.cxx                           |  54 ++-
 Source/cmUVHandlePtr.h                             |  40 +-
 Source/cmUVStreambuf.h                             | 219 ++++++++++
 Tests/CMakeLib/CMakeLists.txt                      |   4 +-
 Tests/CMakeLib/testUVRAII.cxx                      |  50 ++-
 Tests/CMakeLib/testUVStreambuf.cxx                 | 457 +++++++++++++++++++++
 10 files changed, 873 insertions(+), 54 deletions(-)
 create mode 100644 Source/cmGetPipes.cxx
 copy Source/{CPack/cmCPackConfigure.h.in => cmGetPipes.h} (65%)
 create mode 100644 Source/cmUVStreambuf.h
 create mode 100644 Tests/CMakeLib/testUVStreambuf.cxx


hooks/post-receive
-- 
CMake


More information about the Cmake-commits mailing list