mirror of
https://github.com/boostorg/process.git
synced 2026-01-20 04:42:24 +00:00
Compare commits
20 Commits
args-fix
...
boost-1.90
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2a41d0a0dc | ||
|
|
dc00bf81d6 | ||
|
|
47b5c3c191 | ||
|
|
60affa362c | ||
|
|
aa40c138ed | ||
|
|
c5986d7f57 | ||
|
|
1e572e1756 | ||
|
|
635c226066 | ||
|
|
773ac747d5 | ||
|
|
322f581d1f | ||
|
|
9df0ee099b | ||
|
|
01c9a5b60f | ||
|
|
ed7099687a | ||
|
|
7fb5049feb | ||
|
|
02e14e8fff | ||
|
|
484d6e7a90 | ||
|
|
878a9e6ee9 | ||
|
|
1bfe21baa3 | ||
|
|
1765cd57bb | ||
|
|
7212471b57 |
@@ -3,7 +3,7 @@
|
||||
# Distributed under the Boost Software License, Version 1.0.
|
||||
# https://www.boost.org/LICENSE_1_0.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.5...3.16)
|
||||
cmake_minimum_required(VERSION 3.8...3.31)
|
||||
|
||||
project(boost_process VERSION "${BOOST_SUPERPROJECT_VERSION}" LANGUAGES CXX)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ Boost.process is a library for comfortable management of processes, released wit
|
||||
|
||||
| Branches | Linux / Windows | Code coverage | Matrix |
|
||||
|----------|----------------------------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
|
||||
| Develop: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](http://www.boost.org/development/tests/develop/developer/process.html) |
|
||||
| Master: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](http://www.boost.org/development/tests/master/developer/process.html) |
|
||||
| Develop: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](https://regression.boost.io/develop/developer/process.html) |
|
||||
| Master: | [](https://drone.cpp.al/boostorg/process) | [](https://codecov.io/gh/boostorg/process) | [](https://regression.boost.io/master/developer/process.html) |
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -35,12 +35,57 @@ Valid initializers for any stdio are:
|
||||
|
||||
- `std::nullptr_t` assigning a null-device
|
||||
- `FILE*` any open file, including `stdin`, `stdout` and `stderr`
|
||||
- a filesystem::path, which will open a readable or writable depending on the direction of the stream
|
||||
- `native_handle` any native file handle (`HANDLE` on windows) or file descriptor (`int` on posix)
|
||||
- any io-object with a .native_handle() function that is compatible with the above. E.g. a asio::ip::tcp::socket
|
||||
- an asio::basic_writeable_pipe for stdin or asio::basic_readable_pipe for stderr/stdout.
|
||||
- any io-object with a `.native_handle()` function that is compatible with the above. E.g. a `asio::ip::tcp::socket`, or a pipe object.
|
||||
- a filesystem::path, which will open a readable or writable depending on the direction of the stream
|
||||
- an `asio::basic_writeable_pipe` for stdin or `asio::basic_readable_pipe` for stderr/stdout.
|
||||
|
||||
When passing a `FILE*`, a `native_handle` or an io-object with a `native_handle`,
|
||||
the initializer will assign the handle as is to the child process.
|
||||
That is the file descriptor/handle gets cloned into the subprocess and used without modification.
|
||||
|
||||
When passing a filesystem::path, the initializer will attempt to open the file and then pass the handle
|
||||
to the subprocess.
|
||||
|
||||
When passing a `readable_pipe` to stdout/stderr or a `writable_pipe` to stdin by reference,
|
||||
the initializer to create the other side of the pipe (`writable_pipe` for stdout/stderr, `readable_pipe` for `stdin`),
|
||||
connect the pair and pass the native_handle to the child process.
|
||||
|
||||
That is, these two are equivalent:
|
||||
|
||||
.Implicit construction of the readable pipe.
|
||||
[source,cpp]
|
||||
----
|
||||
asio::io_context ctx;
|
||||
asio::writable_pipe wp{ctx};
|
||||
// create a readable pipe internally and connect it to wp
|
||||
process proc{ctx, "/bin/bash", {}, process_stdio{.in=wp}};
|
||||
|
||||
// create it explicitly
|
||||
{
|
||||
// the pipe the child process reads from
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
// `rp.native_handle()` will be assigned to the child processes stdin
|
||||
process proc{ctx, "/bin/bash", {}, process_stdio{.in=rp}};
|
||||
rp.close(); // close it so the pipe closes when the `proc exits.
|
||||
}
|
||||
----
|
||||
|
||||
The explicit version allows you to assign the same `writable_pipe` to `stdout` and `stderr`:
|
||||
|
||||
[source,cpp]
|
||||
----
|
||||
// the pipe the parent process reads from and both
|
||||
// stderr & stdout of the child process write to
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::writable_pipe wp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
process proc{ctx, "/bin/bash", {}, process_stdio{.out=wp, .err=wp}};
|
||||
wp.close(); // close it so the pipe closes when the `proc exits.
|
||||
----
|
||||
|
||||
NOTE: If the child writes to a pipe, the parent reads from it et vice versa.
|
||||
|
||||
|
||||
[source,cpp]
|
||||
@@ -52,4 +97,4 @@ struct process_stdio
|
||||
__implementation_defined__ out;
|
||||
__implementation_defined__ err;
|
||||
};
|
||||
----
|
||||
----
|
||||
|
||||
@@ -162,7 +162,9 @@ struct exe_cmd_init : handler_base_ext
|
||||
}
|
||||
static exe_cmd_init<Char> exe_args_shell(string_type && exe, std::vector<string_type> && args)
|
||||
{
|
||||
std::vector<string_type> args_ = {c_arg(Char()), std::move(exe)};
|
||||
std::vector<string_type> args_ = {c_arg(Char())};
|
||||
if (!exe.empty())
|
||||
args_.emplace_back(std::move(exe));
|
||||
args_.insert(args_.end(), std::make_move_iterator(args.begin()), std::make_move_iterator(args.end()));
|
||||
string_type sh = get_shell(Char());
|
||||
|
||||
|
||||
@@ -69,6 +69,8 @@ struct basic_process_handle_fd
|
||||
basic_process_handle_fd(executor_type executor, pid_type pid)
|
||||
: pid_(pid), descriptor_(executor, syscall(SYS_pidfd_open, pid, 0))
|
||||
{
|
||||
if (descriptor_.native_handle() == -1)
|
||||
detail::throw_error(detail::get_last_error(), "wait(pid)");
|
||||
}
|
||||
|
||||
basic_process_handle_fd(executor_type executor, pid_type pid, native_handle_type process_handle)
|
||||
|
||||
@@ -44,9 +44,14 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
if (ec)
|
||||
detail::throw_error(ec, "size_as_utf8");
|
||||
|
||||
|
||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||
res.resize(req_size);
|
||||
|
||||
if (req_size == 0)
|
||||
return res;
|
||||
|
||||
|
||||
auto res_size = convert_to_utf8(data, size, &res.front(), req_size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "convert_to_utf8");
|
||||
@@ -70,6 +75,9 @@ std::basic_string<CharOut, Traits, Allocator> conv_string(
|
||||
std::basic_string<CharOut, Traits, Allocator> res(allocator);
|
||||
res.resize(req_size);
|
||||
|
||||
if (req_size == 0)
|
||||
return res;
|
||||
|
||||
auto res_size = convert_to_wide(data, size, &res.front(), req_size, ec);
|
||||
if (ec)
|
||||
detail::throw_error(ec, "convert_to_wide");
|
||||
|
||||
@@ -1762,9 +1762,12 @@ struct process_environment
|
||||
std::vector<environment::key_value_pair> env_buffer;
|
||||
std::vector<wchar_t> unicode_env;
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(windows::default_launcher & launcher,
|
||||
const filesystem::path &, const std::wstring &);
|
||||
const filesystem::path &, const std::wstring &)
|
||||
{
|
||||
return do_setup(launcher);
|
||||
}
|
||||
BOOST_PROCESS_V2_DECL error_code do_setup(windows::default_launcher & launcher);
|
||||
|
||||
#else
|
||||
|
||||
@@ -1813,7 +1816,13 @@ struct process_environment
|
||||
|
||||
BOOST_PROCESS_V2_DECL
|
||||
error_code on_setup(posix::default_launcher & launcher,
|
||||
const filesystem::path &, const char * const *);
|
||||
const filesystem::path &, const char * const *)
|
||||
{
|
||||
return do_setup(launcher);
|
||||
}
|
||||
|
||||
BOOST_PROCESS_V2_DECL error_code do_setup(posix::default_launcher & launcher);
|
||||
|
||||
|
||||
std::vector<environment::key_value_pair> env_buffer;
|
||||
std::vector<const char *> env;
|
||||
|
||||
@@ -352,9 +352,9 @@ struct default_launcher
|
||||
}
|
||||
fd_whitelist.push_back(pg.p[1]);
|
||||
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
auto & ctx = net::query(
|
||||
exec, net::execution::context);
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
ctx.notify_fork(net::execution_context::fork_prepare);
|
||||
#endif
|
||||
pid = ::fork();
|
||||
@@ -386,7 +386,7 @@ struct default_launcher
|
||||
ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
|
||||
@@ -115,7 +115,7 @@ struct fork_and_forget_launcher : default_launcher
|
||||
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
|
||||
@@ -136,7 +136,7 @@ struct pdfork_launcher : default_launcher
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
#if !defined(BOOST_PROCESS_V2_DISABLE_NOTIFY_FORK)
|
||||
|
||||
@@ -140,7 +140,7 @@ struct pipe_fork_launcher : default_launcher
|
||||
default_launcher::ignore_unused(::write(pg.p[1], &errno, sizeof(int)));
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, errno, system_category());
|
||||
detail::on_exec_error(*this, executable, argv, ec, inits...);
|
||||
::exit(EXIT_FAILURE);
|
||||
::_exit(EXIT_FAILURE);
|
||||
return basic_process<Executor>{exec};
|
||||
}
|
||||
ctx.notify_fork(net::execution_context::fork_parent);
|
||||
|
||||
@@ -184,7 +184,7 @@ struct process_io_binding
|
||||
process_io_binding & operator=(const process_io_binding &) = delete;
|
||||
|
||||
process_io_binding(process_io_binding && other) noexcept
|
||||
: fd(other.fd), fd_needs_closing(other.fd), ec(other.ec)
|
||||
: fd(other.fd), fd_needs_closing(other.fd_needs_closing), ec(other.ec)
|
||||
{
|
||||
other.fd = target;
|
||||
other.fd_needs_closing = false;
|
||||
|
||||
@@ -225,6 +225,9 @@ struct default_launcher
|
||||
INVALID_HANDLE_VALUE,
|
||||
INVALID_HANDLE_VALUE},
|
||||
nullptr};
|
||||
/// Allow batch files to be executed, which might pose a security threat.
|
||||
bool allow_batch_files = false;
|
||||
|
||||
/// The process_information that gets assigned after a call to CreateProcess
|
||||
PROCESS_INFORMATION process_information{nullptr, nullptr, 0,0};
|
||||
|
||||
@@ -293,6 +296,12 @@ struct default_launcher
|
||||
Args && args,
|
||||
Inits && ... inits ) -> enable_init<Executor, Inits...>
|
||||
{
|
||||
if (!allow_batch_files && ((executable.extension() == ".bat") || (executable.extension() == ".cmd")))
|
||||
{
|
||||
BOOST_PROCESS_V2_ASSIGN_EC(ec, ERROR_ACCESS_DENIED, system_category());
|
||||
return basic_process<Executor>(exec);
|
||||
}
|
||||
|
||||
auto command_line = this->build_command_line(executable, std::forward<Args>(args));
|
||||
|
||||
ec = detail::on_setup(*this, executable, command_line, inits...);
|
||||
@@ -352,7 +361,6 @@ struct default_launcher
|
||||
BOOST_PROCESS_V2_DECL static
|
||||
std::size_t escape_argv_string(wchar_t * itr, std::size_t max_size,
|
||||
basic_string_view<wchar_t> ws);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -395,6 +403,7 @@ struct default_launcher
|
||||
{
|
||||
return detail::conv_string<wchar_t>(arg.data(), arg.size());
|
||||
});
|
||||
|
||||
return build_command_line_impl(pt, argw, L"");
|
||||
}
|
||||
|
||||
@@ -406,10 +415,11 @@ struct default_launcher
|
||||
{
|
||||
std::wstring buffer;
|
||||
buffer.resize(escaped_argv_length(pt.native()));
|
||||
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
|
||||
|
||||
if (!buffer.empty())
|
||||
escape_argv_string(&buffer.front(), buffer.size(), pt.native());
|
||||
return buffer;
|
||||
}
|
||||
|
||||
return build_command_line_impl(pt, args, *std::begin(args));
|
||||
}
|
||||
|
||||
@@ -438,4 +448,4 @@ BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
|
||||
|
||||
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||
#endif //BOOST_PROCESS_V2_WINDOWS_DEFAULT_LAUNCHER_HPP
|
||||
|
||||
@@ -17,7 +17,7 @@ BOOST_PROCESS_V2_BEGIN_NAMESPACE
|
||||
|
||||
#if defined(BOOST_PROCESS_V2_WINDOWS)
|
||||
|
||||
error_code process_environment::on_setup(windows::default_launcher & launcher, const filesystem::path &, const std::wstring &)
|
||||
error_code process_environment::do_setup(windows::default_launcher & launcher)
|
||||
{
|
||||
if (!unicode_env.empty() && !ec)
|
||||
{
|
||||
@@ -30,7 +30,7 @@ error_code process_environment::on_setup(windows::default_launcher & launcher, c
|
||||
|
||||
#else
|
||||
|
||||
error_code process_environment::on_setup(posix::default_launcher & launcher, const filesystem::path &, const char * const *)
|
||||
error_code process_environment::do_setup(posix::default_launcher & launcher)
|
||||
{
|
||||
launcher.env = env.data();
|
||||
return error_code{};
|
||||
|
||||
@@ -24,54 +24,72 @@ namespace windows
|
||||
if (ws.empty())
|
||||
return 2u; // just quotes
|
||||
|
||||
constexpr static auto space = L' ';
|
||||
constexpr static auto quote = L'"';
|
||||
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
|
||||
|
||||
const auto has_space = ws.find(space) != basic_string_view<wchar_t>::npos;
|
||||
const auto quoted = (ws.front() == quote) && (ws.back() == quote);
|
||||
const auto needs_escape = has_space && !quoted ;
|
||||
|
||||
if (!needs_escape)
|
||||
return ws.size();
|
||||
else
|
||||
return ws.size() + std::count(ws.begin(), ws.end(), quote) + 2u;
|
||||
std::size_t needed_escapes = 0u;
|
||||
for (auto itr = ws.begin(); itr != ws.end(); itr ++)
|
||||
{
|
||||
if (*itr == quote)
|
||||
needed_escapes++;
|
||||
else if (*itr == L'\\')
|
||||
{
|
||||
auto nx = std::next(itr);
|
||||
if (nx != ws.end() && *nx == L'"')
|
||||
needed_escapes ++;
|
||||
else if (nx == ws.end())
|
||||
needed_escapes ++;
|
||||
}
|
||||
}
|
||||
|
||||
return ws.size() + needed_escapes + (needs_quotes ? 2u : 0u);
|
||||
}
|
||||
|
||||
|
||||
std::size_t default_launcher::escape_argv_string(wchar_t * itr, std::size_t max_size,
|
||||
basic_string_view<wchar_t> ws)
|
||||
{
|
||||
const auto sz = escaped_argv_length(ws);
|
||||
constexpr static auto quote = L'"';
|
||||
const auto needs_quotes = ws.find_first_of(L" \t") != basic_string_view<wchar_t>::npos;
|
||||
const auto needed_escapes = std::count(ws.begin(), ws.end(), quote);
|
||||
|
||||
const auto sz = ws.size() + needed_escapes + (needs_quotes ? 2u : 0u);
|
||||
|
||||
if (sz > max_size)
|
||||
return 0u;
|
||||
|
||||
if (ws.empty())
|
||||
{
|
||||
itr[0] = L'"';
|
||||
itr[1] = L'"';
|
||||
itr[0] = quote;
|
||||
itr[1] = quote;
|
||||
return 2u;
|
||||
}
|
||||
|
||||
const auto has_space = ws.find(L' ') != basic_string_view<wchar_t>::npos;
|
||||
const auto quoted = (ws.front() == L'"') && (ws.back() == L'"');
|
||||
const auto needs_escape = has_space && !quoted;
|
||||
|
||||
if (!needs_escape)
|
||||
return std::copy(ws.begin(), ws.end(), itr) - itr;
|
||||
|
||||
if (sz < (2u + ws.size()))
|
||||
return 0u;
|
||||
|
||||
const auto end = itr + sz;
|
||||
const auto begin = itr;
|
||||
*(itr ++) = L'"';
|
||||
for (auto wc : ws)
|
||||
if (needs_quotes)
|
||||
*(itr++) = quote;
|
||||
|
||||
for (auto it = ws.begin(); it != ws.end(); it ++)
|
||||
{
|
||||
if (wc == L'"')
|
||||
if (*it == quote) // makes it \"
|
||||
*(itr++) = L'\\';
|
||||
*(itr++) = wc;
|
||||
|
||||
if (*it == L'\\') // \" needs to become \\\"
|
||||
{
|
||||
auto nx = std::next(it);
|
||||
if (nx != ws.end() && *nx == L'"')
|
||||
*(itr++) = L'\\';
|
||||
else if (nx == ws.end())
|
||||
*(itr++) = L'\\';
|
||||
|
||||
}
|
||||
|
||||
*(itr++) = *it;
|
||||
}
|
||||
|
||||
*(itr ++) = L'"';
|
||||
if (needs_quotes)
|
||||
*(itr++) = quote;
|
||||
return itr - begin;
|
||||
}
|
||||
|
||||
@@ -108,13 +126,18 @@ namespace windows
|
||||
auto tl = get_thread_attribute_list(ec);
|
||||
if (ec)
|
||||
return;
|
||||
|
||||
auto itr = std::unique(inherited_handles.begin(), inherited_handles.end());
|
||||
auto size = std::distance(inherited_handles.begin(), itr);
|
||||
|
||||
if (!::UpdateProcThreadAttribute(
|
||||
tl, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
|
||||
inherited_handles.data(), inherited_handles.size() * sizeof(HANDLE), nullptr, nullptr))
|
||||
inherited_handles.data(), size * sizeof(HANDLE), nullptr, nullptr))
|
||||
BOOST_PROCESS_V2_ASSIGN_LAST_ERROR(ec);
|
||||
}
|
||||
|
||||
}
|
||||
BOOST_PROCESS_V2_END_NAMESPACE
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,18 +1,12 @@
|
||||
enable_testing()
|
||||
|
||||
add_library(boost_process_v2_test_impl test_impl.cpp)
|
||||
target_compile_definitions(boost_process_v2_test_impl PUBLIC -DBOOST_PROCESS_V2_SEPARATE_COMPILATION=1)
|
||||
|
||||
if (WIN32)
|
||||
target_compile_definitions(boost_process_v2_test_impl PUBLIC WIN32_LEAN_AND_MEAN=1)
|
||||
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process Ntdll)
|
||||
else()
|
||||
target_link_libraries(boost_process_v2_test_impl Boost::process Boost::unit_test_framework Boost::process)
|
||||
endif()
|
||||
|
||||
function(boost_process_v2_standalone_test name)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp test_impl.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem Boost::unit_test_framework )
|
||||
if (WIN32)
|
||||
target_compile_definitions(boost_process_v2_${name} PUBLIC WIN32_LEAN_AND_MEAN=1)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Ntdll)
|
||||
endif()
|
||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}> )
|
||||
endfunction()
|
||||
|
||||
@@ -27,13 +21,16 @@ target_link_libraries(boost_process_v2_test_target PUBLIC Boost::process Boost::
|
||||
|
||||
function(boost_process_v2_test_with_target name)
|
||||
add_executable(boost_process_v2_${name} ${name}.cpp)
|
||||
target_link_libraries(boost_process_v2_${name} Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Boost::process Boost::system Boost::filesystem boost_process_v2_test_impl)
|
||||
if (WIN32)
|
||||
target_compile_definitions(boost_process_v2_${name} PUBLIC WIN32_LEAN_AND_MEAN=1)
|
||||
target_link_libraries(boost_process_v2_${name} PUBLIC Ntdll)
|
||||
endif()
|
||||
add_dependencies(boost_process_v2_${name} boost_process_v2_test_target)
|
||||
add_test(NAME boost_process_v2_${name} COMMAND $<TARGET_FILE:boost_process_v2_${name}>
|
||||
-- $<TARGET_FILE:boost_process_v2_test_target>)
|
||||
|
||||
|
||||
endfunction()
|
||||
|
||||
boost_process_v2_test_with_target(process)
|
||||
boost_process_v2_test_with_target(ext)
|
||||
boost_process_v2_test_with_target(ext)
|
||||
|
||||
@@ -33,7 +33,6 @@ project : requirements
|
||||
<os>NT,<toolset>cw:<library>ws2_32
|
||||
<os>NT,<toolset>gcc:<library>ws2_32
|
||||
<os>NT,<toolset>gcc:<library>Bcrypt
|
||||
<define>BOOST_PROCESS_V2_SEPARATE_COMPILATION=1
|
||||
<library>/boost/test//included
|
||||
;
|
||||
|
||||
|
||||
@@ -245,6 +245,83 @@ BOOST_AUTO_TEST_CASE(print_args_out)
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_spec_out)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
asio::io_context ctx;
|
||||
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::writable_pipe wp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
|
||||
fprintf(stderr, "print_args_spec_out\n");
|
||||
|
||||
bpv::process proc(ctx, pth, {"print-args", "&foo", "&", "", "\"\"", "\\\"", "|bar", "\"", "#foobar"},
|
||||
bpv::process_stdio{/*in*/{},/*out*/wp, /*err*/ nullptr});
|
||||
BOOST_CHECK(proc.running());
|
||||
|
||||
wp.close();
|
||||
asio::streambuf st;
|
||||
std::istream is{&st};
|
||||
bpv::error_code ec;
|
||||
|
||||
auto sz = asio::read(rp, st, ec);
|
||||
while (ec == asio::error::interrupted)
|
||||
sz += asio::read(rp, st, ec);
|
||||
|
||||
BOOST_CHECK_NE(sz, 0u);
|
||||
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
|
||||
|
||||
std::string line;
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL(pth, line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("print-args", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("&foo", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("&", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("\"\"", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("\\\"", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("|bar", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("\"", line);
|
||||
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("#foobar", line);
|
||||
|
||||
|
||||
proc.wait();
|
||||
BOOST_CHECK(proc.exit_code() == 0);
|
||||
}
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_err)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
@@ -791,5 +868,70 @@ BOOST_AUTO_TEST_CASE(async_terminate_code)
|
||||
#endif
|
||||
|
||||
|
||||
BOOST_AUTO_TEST_CASE(print_args_combined)
|
||||
{
|
||||
using boost::unit_test::framework::master_test_suite;
|
||||
const auto pth = master_test_suite().argv[1];
|
||||
|
||||
asio::io_context ctx;
|
||||
|
||||
asio::readable_pipe rp{ctx};
|
||||
asio::writable_pipe wp{ctx};
|
||||
asio::connect_pipe(rp, wp);
|
||||
|
||||
bpv::process proc(ctx, pth, {"print-args", "bar", "foo"}, bpv::process_stdio{/*in*/{}, /*.out= */ wp, /* .err=*/ wp});
|
||||
wp.close();
|
||||
|
||||
asio::streambuf st;
|
||||
std::istream is{&st};
|
||||
bpv::error_code ec;
|
||||
|
||||
auto sz = asio::read(rp, st, ec);
|
||||
while (ec == asio::error::interrupted)
|
||||
sz += asio::read(rp, st, ec);
|
||||
|
||||
BOOST_CHECK_NE(sz , 0u);
|
||||
BOOST_CHECK_MESSAGE((ec == asio::error::broken_pipe) || (ec == asio::error::eof), ec.message());
|
||||
|
||||
std::string line;
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL(pth, line );
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL(pth, line );
|
||||
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("print-args", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("print-args", line);
|
||||
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("bar", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("bar", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("foo", line);
|
||||
|
||||
BOOST_CHECK(std::getline(is, line));
|
||||
trim_end(line);
|
||||
BOOST_CHECK_EQUAL("foo", line);
|
||||
|
||||
|
||||
proc.wait();
|
||||
BOOST_CHECK_EQUAL(proc.exit_code(), 0);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user