localPackages.biu: exec allow use parent process std{in,out,err}

This commit is contained in:
2024-06-09 18:52:39 +08:00
parent 1949ea7d78
commit 8f09a18dc5
3 changed files with 81 additions and 42 deletions

View File

@@ -22,6 +22,8 @@ namespace biu
using int128_t = __int128_t;
using uint128_t = __uint128_t;
struct Empty {};
struct CaseInsensitiveStringLessComparator
{
template <typename String> constexpr bool operator()(const String& s1, const String& s2) const;
@@ -64,11 +66,31 @@ namespace biu
template <typename T, typename Fallback = void> using FallbackIfNoTypeDeclared
= typename detail_::FallbackIfNoTypeDeclaredHelper<T, Fallback>::Type;
namespace detail_ { struct ExecResult { int exit_code; std::string stdout, stderr; }; }
detail_::ExecResult exec
namespace detail_
{
template <bool DirectStdout, bool DirectStderr> struct ExecResult
{
int exit_code;
std::conditional_t<DirectStdout, Empty, std::string> stdout;
std::conditional_t<DirectStderr, Empty, std::string> stderr;
};
template <bool DirectStdin, bool DirectStdout, bool DirectStderr>
detail_::ExecResult<DirectStdout, DirectStderr> exec
(
std::filesystem::path program, std::vector<std::string> args, std::optional<std::string> stdin,
std::map<std::string, std::string> extra_env
);
}
template <bool DirectStdin = false, bool DirectStdout = false, bool DirectStderr = false> requires (!DirectStdin)
detail_::ExecResult<DirectStdout, DirectStderr> exec
(
std::filesystem::path program, std::vector<std::string> args, std::optional<std::string> stdin,
std::map<std::string, std::string> extra_env
std::filesystem::path program, std::vector<std::string> args, std::optional<std::string> stdin = {},
std::map<std::string, std::string> extra_env = {}
);
template <bool DirectStdin = false, bool DirectStdout = false, bool DirectStderr = false> requires DirectStdin
detail_::ExecResult<DirectStdout, DirectStderr> exec
(
std::filesystem::path program, std::vector<std::string> args, std::map<std::string, std::string> extra_env = {}
);
}
}

View File

@@ -1,6 +1,7 @@
# pragma once
# include <boost/functional/hash.hpp>
# include <biu/common.hpp>
# include <boost/process.hpp>
namespace biu::common
{
@@ -21,4 +22,56 @@ namespace biu::common
[](char c1, char c2){return std::tolower(c1) < std::tolower(c2);}
);
}
}
template <bool directStdin, bool directStdout, bool directStderr> detail_::ExecResult<directStdout, directStderr> exec
(
std::filesystem::path program, std::vector<std::string> args, std::optional<std::string> stdin,
std::map<std::string, std::string> extra_env
)
{
namespace bp = boost::process;
bp::ipstream stdout_stream, stderr_stream;
bp::opstream input_stream;
auto&& stdout =
[&]{ if constexpr (directStdout) return bp::std_out > ::stdout; else return bp::std_out > stdout_stream; }();
auto&& stderr =
[&]{ if constexpr (directStderr) return bp::std_err > ::stderr; else return bp::std_err > stderr_stream; }();
auto&& input = [&]
{
if constexpr (directStdin) return bp::std_in < ::stdin;
else if (stdin) return bp::std_in < input_stream; else return bp::std_in < bp::null;
}();
std::unique_ptr<bp::child> process;
bp::environment env = boost::this_process::environment();
for (const auto& [key, value] : extra_env) env[key] = value;
process = std::make_unique<bp::child>(program.string(), bp::args(args), stdout, stderr, input, env);
if (stdin) { input << *stdin; input.pipe().close(); }
process->wait();
return
{
process->exit_code(),
[&]
{
if constexpr (directStdout) return std::string{std::istreambuf_iterator<char>{stdout.rdbuf()}, {}};
else return Empty{};
}(),
[&]
{
if constexpr (directStderr) return std::string{std::istreambuf_iterator<char>{stderr.rdbuf()}, {}};
else return Empty{};
}()
};
}
template <bool DirectStdin, bool DirectStdout, bool DirectStderr> requires (!DirectStdin)
detail_::ExecResult<DirectStdout, DirectStderr> exec
(
std::filesystem::path program, std::vector<std::string> args, std::optional<std::string> stdin,
std::map<std::string, std::string> extra_env
)
{ return exec<DirectStdout, DirectStderr>(program, args, stdin, extra_env); }
template <bool DirectStdin, bool DirectStdout, bool DirectStderr> requires DirectStdin
detail_::ExecResult<DirectStdout, DirectStderr> exec
(
std::filesystem::path program, std::vector<std::string> args, std::map<std::string, std::string> extra_env
)
{ return exec<DirectStdin, DirectStdout, DirectStderr>(program, args, {}, extra_env); }
}

View File

@@ -1,45 +1,9 @@
# include <future>
# include <utility>
# include <biu.hpp>
# include <boost/process.hpp>
namespace biu
{
std::regex literals::operator""_re(const char* str, std::size_t len) { return std::regex{str, len}; }
namespace common
{
void block_forever() { std::promise<void>().get_future().wait(); std::unreachable(); }
detail_::ExecResult exec
(
std::filesystem::path program, std::vector<std::string> args, std::optional<std::string> stdin,
std::map<std::string, std::string> extra_env
)
{
namespace bp = boost::process;
bp::ipstream stdout, stderr;
bp::opstream input;
std::unique_ptr<bp::child> process;
bp::environment env = boost::this_process::environment();
for (const auto& [key, value] : extra_env) env[key] = value;
if (stdin)
{
process = std::make_unique<bp::child>
(program.string(), bp::args(args), bp::std_out > stdout, bp::std_err > stderr,
bp::std_in < input, env);
input << *stdin;
input.pipe().close();
}
else process = std::make_unique<bp::child>
(program.string(), bp::args(args), bp::std_out > stdout, bp::std_err > stderr,
bp::std_in < bp::null, env);
process->wait();
return
{
process->exit_code(),
std::string{std::istreambuf_iterator<char>{stdout.rdbuf()}, {}},
std::string{std::istreambuf_iterator<char>{stderr.rdbuf()}, {}}
};
}
}
namespace common { void block_forever() { std::promise<void>().get_future().wait(); std::unreachable(); } }
}