Skip to content

Commit 9c624ce

Browse files
xoviatarun11299
authored andcommitted
Enable basic windows support (arun11299#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
1 parent 126ee29 commit 9c624ce

File tree

4 files changed

+64
-43
lines changed

4 files changed

+64
-43
lines changed

README.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ This library had these design goals:
1313

1414

1515
## Supported Platforms
16-
Unlike python2.7 subprocess module, this library currently only supports MAC OS and Linux.
17-
It has no support for Windows in its current state.
16+
This library supports MAC OS and Linux.
17+
18+
Support for Windows is limited at this time. Please report any specific use-cases that fail,
19+
and they will be fixed as they are reported.
1820

1921
## Integration
2022
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
3436
## Compiler Support
3537
Linux - g++ 4.8 and above
3638
Mac OS - Clang 3.4 and later
39+
Windows - MSVC 2015 and above
3740

3841
## Examples
3942
Here are few examples to show how to get started:

subprocess.hpp

Lines changed: 40 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -272,11 +272,11 @@ namespace util
272272

273273
// Create a pipe for the child process's STDIN.
274274
if (!CreatePipe(read_handle, write_handle, &saAttr,0))
275-
throw OSError("Stdin CreatePipe", 0);
275+
throw OSError("CreatePipe", 0);
276276

277277
// Ensure the write handle to the pipe for STDIN is not inherited.
278-
if (!SetHandleInformation(child_handle, HANDLE_FLAG_INHERIT, 0))
279-
throw OSError("Stdin SetHandleInformation", 0);
278+
if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
279+
throw OSError("SetHandleInformation", 0);
280280
}
281281
#endif
282282

@@ -1131,6 +1131,26 @@ class Popen
11311131
if (!defer_process_start_) execute_process();
11321132
}
11331133

1134+
template <typename... Args>
1135+
Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_)
1136+
{
1137+
init_args(std::forward<Args>(args)...);
1138+
1139+
// Setup the communication channels of the Popen class
1140+
stream_.setup_comm_channels();
1141+
1142+
if (!defer_process_start_) execute_process();
1143+
}
1144+
1145+
/*
1146+
~Popen()
1147+
{
1148+
#ifdef _MSC_VER
1149+
CloseHandle(this->process_handle_);
1150+
#endif
1151+
}
1152+
*/
1153+
11341154
void start_process() noexcept(false);
11351155

11361156
int pid() const noexcept { return child_pid_; }
@@ -1409,43 +1429,21 @@ inline void Popen::execute_process() noexcept(false)
14091429

14101430
this->process_handle_ = piProcInfo.hProcess;
14111431

1412-
try {
1413-
char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
1432+
std::async(std::launch::async, [this] {
1433+
WaitForSingleObject(this->process_handle_, INFINITE);
14141434

1415-
int read_bytes = util::read_atmost_n(
1416-
this->error(),
1417-
err_buf,
1418-
SP_MAX_ERR_BUF_SIZ);
1419-
fclose(this->error());
1420-
1421-
if (read_bytes || strlen(err_buf)) {
1422-
// Throw whatever information we have about child failure
1423-
throw CalledProcessError(err_buf);
1424-
}
1425-
} catch (std::exception& exp) {
1426-
stream_.cleanup_fds();
1427-
throw;
1428-
}
1435+
CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
1436+
CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
1437+
CloseHandle(this->stream_.g_hChildStd_IN_Rd);
1438+
});
14291439

14301440
/*
1431-
this->hExited_ =
1432-
std::shared_future<int>(std::async(std::launch::async, [this] {
1433-
WaitForSingleObject(this->hProcess_, INFINITE);
1434-
1435-
CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
1436-
CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
1437-
CloseHandle(this->stream_.g_hChildStd_IN_Rd);
1438-
1439-
DWORD exit_code;
1440-
if (FALSE == GetExitCodeProcess(this->hProcess_, &exit_code))
1441-
throw OSError("GetExitCodeProcess", 0);
1442-
1443-
CloseHandle(this->hProcess_);
1444-
1445-
return (int)exit_code;
1446-
}));
1441+
NOTE: In the linux version, there is a check to make sure that the process
1442+
has been started. Here, we do nothing because CreateProcess will throw
1443+
if we fail to create the process.
14471444
*/
14481445

1446+
14491447
#else
14501448

14511449
int err_rd_pipe, err_wr_pipe;
@@ -1683,7 +1681,7 @@ namespace detail {
16831681
inline void Streams::setup_comm_channels()
16841682
{
16851683
#ifdef _MSC_VER
1686-
util::configure_pipe(this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
1684+
util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
16871685
this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
16881686
this->write_to_child_ = _fileno(this->input());
16891687

@@ -1935,6 +1933,12 @@ OutBuffer check_output(const std::string& arg, Args&&... args)
19351933
return (detail::check_output_impl(arg, std::forward<Args>(args)...));
19361934
}
19371935

1936+
template <typename... Args>
1937+
OutBuffer check_output(std::vector<std::string> plist, Args &&... args)
1938+
{
1939+
return (detail::check_output_impl(plist, std::forward<Args>(args)...));
1940+
}
1941+
19381942

19391943
/*!
19401944
* An easy way to pipeline easy commands.

test/test_ret_code.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ void test_ret_code()
88
std::cout << "Test::test_poll_ret_code" << std::endl;
99
auto p = sp::Popen({"/usr/bin/false"});
1010
while (p.poll() == -1) {
11+
#ifndef _MSC_VER
1112
usleep(1000 * 100);
13+
#endif
1214
}
1315
assert (p.retcode() == 1);
1416
}

test/test_subprocess.cc

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ using namespace subprocess;
55

66
void test_exename()
77
{
8+
#ifdef _MSC_VER
9+
auto ret = call({"--version"}, executable{"cmake"}, shell{false});
10+
#else
811
auto ret = call({"-l"}, executable{"ls"}, shell{false});
12+
#endif
913
std::cout << ret << std::endl;
1014
}
1115

@@ -35,33 +39,41 @@ void test_easy_piping()
3539

3640
void test_shell()
3741
{
38-
auto obuf = check_output({"ls", "-l"}, shell{false});
42+
#ifdef _MSC_VER
43+
auto obuf = check_output({"cmake", "--version"}, shell{false});
44+
#else
45+
auto obuf = check_output({"ls", "-l"}, shell{false});
46+
#endif
3947
std::cout << obuf.buf.data() << std::endl;
4048
}
4149

4250
void test_sleep()
4351
{
4452
auto p = Popen({"sleep", "30"}, shell{true});
4553

46-
while (p.poll() == -1) {
54+
while (p.poll() == -1)
55+
{
4756
std::cout << "Waiting..." << std::endl;
57+
#ifdef _MSC_VER
58+
#else
4859
sleep(1);
60+
#endif
4961
}
5062

5163
std::cout << "Sleep ended: ret code = " << p.retcode() << std::endl;
5264
}
5365

5466
void test_read_all()
5567
{
56-
Popen p = Popen({"echo","12345678"}, output{PIPE});
57-
68+
Popen p = Popen({"echo", "12345678"}, output{PIPE});
69+
5870
std::vector<char> buf(6);
5971
int rbytes = util::read_all(p.output(), buf);
6072

6173
std::string out(buf.begin(), buf.end());
6274

6375
assert(out == "12345678\n" && rbytes == 9); // echo puts a new line at the end of output
64-
std::cout<<"read_all() succeeded"<<std::endl;
76+
std::cout << "read_all() succeeded" << std::endl;
6577
}
6678

6779
void test_simple_cmd()

0 commit comments

Comments
 (0)