Enable basic windows support (#33)

* windows: fix implementation error
- repair configure_pipe
- allow vector initialization
- add draft destructor
- close pipes correctly

* windows: add some test compatibility

* windows: update readme

* windows: add vector args to check_output
This commit is contained in:
xoviat 2020-01-18 08:31:03 -06:00 committed by Arun Muralidharan
parent 126ee2978f
commit 9c624ce4e3
4 changed files with 64 additions and 43 deletions

View File

@ -13,8 +13,10 @@ This library had these design goals:
## Supported Platforms
Unlike python2.7 subprocess module, this library currently only supports MAC OS and Linux.
It has no support for Windows in its current state.
This library supports MAC OS and Linux.
Support for Windows is limited at this time. Please report any specific use-cases that fail,
and they will be fixed as they are reported.
## Integration
Subprocess library has just a single source `subprocess.hpp` present at the top directory of this repository. All you need to do is add
@ -34,6 +36,7 @@ Checkout http://templated-thoughts.blogspot.in/2016/03/sub-processing-with-moder
## Compiler Support
Linux - g++ 4.8 and above
Mac OS - Clang 3.4 and later
Windows - MSVC 2015 and above
## Examples
Here are few examples to show how to get started:

View File

@ -272,11 +272,11 @@ namespace util
// Create a pipe for the child process's STDIN.
if (!CreatePipe(read_handle, write_handle, &saAttr,0))
throw OSError("Stdin CreatePipe", 0);
throw OSError("CreatePipe", 0);
// Ensure the write handle to the pipe for STDIN is not inherited.
if (!SetHandleInformation(child_handle, HANDLE_FLAG_INHERIT, 0))
throw OSError("Stdin SetHandleInformation", 0);
if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
throw OSError("SetHandleInformation", 0);
}
#endif
@ -1131,6 +1131,26 @@ public:
if (!defer_process_start_) execute_process();
}
template <typename... Args>
Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_)
{
init_args(std::forward<Args>(args)...);
// Setup the communication channels of the Popen class
stream_.setup_comm_channels();
if (!defer_process_start_) execute_process();
}
/*
~Popen()
{
#ifdef _MSC_VER
CloseHandle(this->process_handle_);
#endif
}
*/
void start_process() noexcept(false);
int pid() const noexcept { return child_pid_; }
@ -1409,43 +1429,21 @@ inline void Popen::execute_process() noexcept(false)
this->process_handle_ = piProcInfo.hProcess;
try {
char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
std::async(std::launch::async, [this] {
WaitForSingleObject(this->process_handle_, INFINITE);
int read_bytes = util::read_atmost_n(
this->error(),
err_buf,
SP_MAX_ERR_BUF_SIZ);
fclose(this->error());
if (read_bytes || strlen(err_buf)) {
// Throw whatever information we have about child failure
throw CalledProcessError(err_buf);
}
} catch (std::exception& exp) {
stream_.cleanup_fds();
throw;
}
CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
CloseHandle(this->stream_.g_hChildStd_IN_Rd);
});
/*
this->hExited_ =
std::shared_future<int>(std::async(std::launch::async, [this] {
WaitForSingleObject(this->hProcess_, INFINITE);
CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
CloseHandle(this->stream_.g_hChildStd_IN_Rd);
DWORD exit_code;
if (FALSE == GetExitCodeProcess(this->hProcess_, &exit_code))
throw OSError("GetExitCodeProcess", 0);
CloseHandle(this->hProcess_);
return (int)exit_code;
}));
NOTE: In the linux version, there is a check to make sure that the process
has been started. Here, we do nothing because CreateProcess will throw
if we fail to create the process.
*/
#else
int err_rd_pipe, err_wr_pipe;
@ -1683,7 +1681,7 @@ namespace detail {
inline void Streams::setup_comm_channels()
{
#ifdef _MSC_VER
util::configure_pipe(this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
this->write_to_child_ = _fileno(this->input());
@ -1935,6 +1933,12 @@ OutBuffer check_output(const std::string& arg, Args&&... args)
return (detail::check_output_impl(arg, std::forward<Args>(args)...));
}
template <typename... Args>
OutBuffer check_output(std::vector<std::string> plist, Args &&... args)
{
return (detail::check_output_impl(plist, std::forward<Args>(args)...));
}
/*!
* An easy way to pipeline easy commands.

View File

@ -8,7 +8,9 @@ void test_ret_code()
std::cout << "Test::test_poll_ret_code" << std::endl;
auto p = sp::Popen({"/usr/bin/false"});
while (p.poll() == -1) {
#ifndef _MSC_VER
usleep(1000 * 100);
#endif
}
assert (p.retcode() == 1);
}

View File

@ -5,7 +5,11 @@ using namespace subprocess;
void test_exename()
{
#ifdef _MSC_VER
auto ret = call({"--version"}, executable{"cmake"}, shell{false});
#else
auto ret = call({"-l"}, executable{"ls"}, shell{false});
#endif
std::cout << ret << std::endl;
}
@ -35,7 +39,11 @@ void test_easy_piping()
void test_shell()
{
auto obuf = check_output({"ls", "-l"}, shell{false});
#ifdef _MSC_VER
auto obuf = check_output({"cmake", "--version"}, shell{false});
#else
auto obuf = check_output({"ls", "-l"}, shell{false});
#endif
std::cout << obuf.buf.data() << std::endl;
}
@ -43,9 +51,13 @@ void test_sleep()
{
auto p = Popen({"sleep", "30"}, shell{true});
while (p.poll() == -1) {
while (p.poll() == -1)
{
std::cout << "Waiting..." << std::endl;
#ifdef _MSC_VER
#else
sleep(1);
#endif
}
std::cout << "Sleep ended: ret code = " << p.retcode() << std::endl;
@ -53,15 +65,15 @@ void test_sleep()
void test_read_all()
{
Popen p = Popen({"echo","12345678"}, output{PIPE});
Popen p = Popen({"echo", "12345678"}, output{PIPE});
std::vector<char> buf(6);
int rbytes = util::read_all(p.output(), buf);
std::string out(buf.begin(), buf.end());
assert(out == "12345678\n" && rbytes == 9); // echo puts a new line at the end of output
std::cout<<"read_all() succeeded"<<std::endl;
std::cout << "read_all() succeeded" << std::endl;
}
void test_simple_cmd()