mirror of
https://github.com/Kitware/CMake.git
synced 2026-01-08 06:40:48 -06:00
Merge topic 'cmuvstreambuf'
c74698cb75cmUVStreambuf: Add std::streambuf implementation for uv_stream_t8cfd25db71cmUVHandlePtr: Add cm::uv_loop_ptrc0e6b22d0aRefactor: Move/rename cmProcessGetPipes() to cmGetPipes() Acked-by: Kitware Robot <kwrobot@kitware.com> Merge-request: !3240
This commit is contained in:
@@ -264,6 +264,8 @@ set(SRCS
|
||||
cmGeneratorExpression.h
|
||||
cmGeneratorTarget.cxx
|
||||
cmGeneratorTarget.h
|
||||
cmGetPipes.cxx
|
||||
cmGetPipes.h
|
||||
cmGlobalCommonGenerator.cxx
|
||||
cmGlobalCommonGenerator.h
|
||||
cmGlobalGenerator.cxx
|
||||
@@ -386,6 +388,7 @@ set(SRCS
|
||||
cmUuid.cxx
|
||||
cmUVHandlePtr.cxx
|
||||
cmUVHandlePtr.h
|
||||
cmUVStreambuf.h
|
||||
cmUVSignalHackRAII.h
|
||||
cmVariableWatch.cxx
|
||||
cmVariableWatch.h
|
||||
|
||||
@@ -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)
|
||||
|
||||
48
Source/cmGetPipes.cxx
Normal file
48
Source/cmGetPipes.cxx
Normal file
@@ -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
|
||||
8
Source/cmGetPipes.h
Normal file
8
Source/cmGetPipes.h
Normal file
@@ -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
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
219
Source/cmUVStreambuf.h
Normal file
219
Source/cmUVStreambuf.h
Normal file
@@ -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
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
457
Tests/CMakeLib/testUVStreambuf.cxx
Normal file
457
Tests/CMakeLib/testUVStreambuf.cxx
Normal file
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user