diff --git a/README.md b/README.md index d7a0b78..ab21bf2 100644 --- a/README.md +++ b/README.md @@ -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: diff --git a/subprocess.hpp b/subprocess.hpp index c500033..fb93766 100755 --- a/subprocess.hpp +++ b/subprocess.hpp @@ -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 @@ class Popen if (!defer_process_start_) execute_process(); } + template + Popen(std::vector vargs_, Args &&... args) : vargs_(vargs_) + { + init_args(std::forward(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(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)...)); } +template +OutBuffer check_output(std::vector plist, Args &&... args) +{ + return (detail::check_output_impl(plist, std::forward(args)...)); +} + /*! * An easy way to pipeline easy commands. diff --git a/test/test_ret_code.cc b/test/test_ret_code.cc index e695b2e..1db9e87 100644 --- a/test/test_ret_code.cc +++ b/test/test_ret_code.cc @@ -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); } diff --git a/test/test_subprocess.cc b/test/test_subprocess.cc index a335a18..64ef998 100755 --- a/test/test_subprocess.cc +++ b/test/test_subprocess.cc @@ -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 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"<