mirror of
https://github.com/arun11299/cpp-subprocess.git
synced 2025-08-05 04:46:21 -04:00
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:
parent
126ee2978f
commit
9c624ce4e3
@ -13,8 +13,10 @@ This library had these design goals:
|
|||||||
|
|
||||||
|
|
||||||
## Supported Platforms
|
## Supported Platforms
|
||||||
Unlike python2.7 subprocess module, this library currently only supports MAC OS and Linux.
|
This library supports MAC OS and Linux.
|
||||||
It has no support for Windows in its current state.
|
|
||||||
|
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
|
## 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
|
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
|
## Compiler Support
|
||||||
Linux - g++ 4.8 and above
|
Linux - g++ 4.8 and above
|
||||||
Mac OS - Clang 3.4 and later
|
Mac OS - Clang 3.4 and later
|
||||||
|
Windows - MSVC 2015 and above
|
||||||
|
|
||||||
## Examples
|
## Examples
|
||||||
Here are few examples to show how to get started:
|
Here are few examples to show how to get started:
|
||||||
|
@ -272,11 +272,11 @@ namespace util
|
|||||||
|
|
||||||
// Create a pipe for the child process's STDIN.
|
// Create a pipe for the child process's STDIN.
|
||||||
if (!CreatePipe(read_handle, write_handle, &saAttr,0))
|
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.
|
// Ensure the write handle to the pipe for STDIN is not inherited.
|
||||||
if (!SetHandleInformation(child_handle, HANDLE_FLAG_INHERIT, 0))
|
if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
|
||||||
throw OSError("Stdin SetHandleInformation", 0);
|
throw OSError("SetHandleInformation", 0);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1131,6 +1131,26 @@ public:
|
|||||||
if (!defer_process_start_) execute_process();
|
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);
|
void start_process() noexcept(false);
|
||||||
|
|
||||||
int pid() const noexcept { return child_pid_; }
|
int pid() const noexcept { return child_pid_; }
|
||||||
@ -1409,43 +1429,21 @@ inline void Popen::execute_process() noexcept(false)
|
|||||||
|
|
||||||
this->process_handle_ = piProcInfo.hProcess;
|
this->process_handle_ = piProcInfo.hProcess;
|
||||||
|
|
||||||
try {
|
std::async(std::launch::async, [this] {
|
||||||
char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
|
WaitForSingleObject(this->process_handle_, INFINITE);
|
||||||
|
|
||||||
int read_bytes = util::read_atmost_n(
|
CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
|
||||||
this->error(),
|
CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
|
||||||
err_buf,
|
CloseHandle(this->stream_.g_hChildStd_IN_Rd);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
this->hExited_ =
|
NOTE: In the linux version, there is a check to make sure that the process
|
||||||
std::shared_future<int>(std::async(std::launch::async, [this] {
|
has been started. Here, we do nothing because CreateProcess will throw
|
||||||
WaitForSingleObject(this->hProcess_, INFINITE);
|
if we fail to create the process.
|
||||||
|
|
||||||
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;
|
|
||||||
}));
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
int err_rd_pipe, err_wr_pipe;
|
int err_rd_pipe, err_wr_pipe;
|
||||||
@ -1683,7 +1681,7 @@ namespace detail {
|
|||||||
inline void Streams::setup_comm_channels()
|
inline void Streams::setup_comm_channels()
|
||||||
{
|
{
|
||||||
#ifdef _MSC_VER
|
#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->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
|
||||||
this->write_to_child_ = _fileno(this->input());
|
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)...));
|
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.
|
* An easy way to pipeline easy commands.
|
||||||
|
@ -8,7 +8,9 @@ void test_ret_code()
|
|||||||
std::cout << "Test::test_poll_ret_code" << std::endl;
|
std::cout << "Test::test_poll_ret_code" << std::endl;
|
||||||
auto p = sp::Popen({"/usr/bin/false"});
|
auto p = sp::Popen({"/usr/bin/false"});
|
||||||
while (p.poll() == -1) {
|
while (p.poll() == -1) {
|
||||||
|
#ifndef _MSC_VER
|
||||||
usleep(1000 * 100);
|
usleep(1000 * 100);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
assert (p.retcode() == 1);
|
assert (p.retcode() == 1);
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,11 @@ using namespace subprocess;
|
|||||||
|
|
||||||
void test_exename()
|
void test_exename()
|
||||||
{
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
auto ret = call({"--version"}, executable{"cmake"}, shell{false});
|
||||||
|
#else
|
||||||
auto ret = call({"-l"}, executable{"ls"}, shell{false});
|
auto ret = call({"-l"}, executable{"ls"}, shell{false});
|
||||||
|
#endif
|
||||||
std::cout << ret << std::endl;
|
std::cout << ret << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +39,11 @@ void test_easy_piping()
|
|||||||
|
|
||||||
void test_shell()
|
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;
|
std::cout << obuf.buf.data() << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,9 +51,13 @@ void test_sleep()
|
|||||||
{
|
{
|
||||||
auto p = Popen({"sleep", "30"}, shell{true});
|
auto p = Popen({"sleep", "30"}, shell{true});
|
||||||
|
|
||||||
while (p.poll() == -1) {
|
while (p.poll() == -1)
|
||||||
|
{
|
||||||
std::cout << "Waiting..." << std::endl;
|
std::cout << "Waiting..." << std::endl;
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#else
|
||||||
sleep(1);
|
sleep(1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "Sleep ended: ret code = " << p.retcode() << std::endl;
|
std::cout << "Sleep ended: ret code = " << p.retcode() << std::endl;
|
||||||
@ -53,15 +65,15 @@ void test_sleep()
|
|||||||
|
|
||||||
void test_read_all()
|
void test_read_all()
|
||||||
{
|
{
|
||||||
Popen p = Popen({"echo","12345678"}, output{PIPE});
|
Popen p = Popen({"echo", "12345678"}, output{PIPE});
|
||||||
|
|
||||||
std::vector<char> buf(6);
|
std::vector<char> buf(6);
|
||||||
int rbytes = util::read_all(p.output(), buf);
|
int rbytes = util::read_all(p.output(), buf);
|
||||||
|
|
||||||
std::string out(buf.begin(), buf.end());
|
std::string out(buf.begin(), buf.end());
|
||||||
|
|
||||||
assert(out == "12345678\n" && rbytes == 9); // echo puts a new line at the end of output
|
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()
|
void test_simple_cmd()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user