localPackages.biu: exec allow set time limit

This commit is contained in:
2024-07-03 14:54:06 +08:00
parent 0e4f1d06a9
commit fef4d06de1
4 changed files with 71 additions and 94 deletions

View File

@@ -41,3 +41,7 @@ add_executable(test-serialize test/serialize.cpp)
target_link_libraries(test-serialize PRIVATE biu)
set_property(TARGET test-serialize PROPERTY CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON)
add_test(NAME test-serialize COMMAND test-serialize)
add_executable(test-process test/process.cpp)
target_link_libraries(test-process PRIVATE biu)
set_property(TARGET test-process PROPERTY CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON)
add_test(NAME test-process COMMAND test-process)

View File

@@ -72,27 +72,24 @@ namespace biu
namespace detail_
{
template <bool DirectStdout, bool DirectStderr> struct ExecResult
struct ExecMode { bool DirectStdin = false, DirectStdout = false, DirectStderr = false, SearchPath = false; };
template <ExecMode Mode> struct ExecResult
{
int ExitCode;
std::conditional_t<DirectStdout, Empty, std::string> Stdout;
std::conditional_t<DirectStderr, Empty, std::string> Stderr;
std::conditional_t<Mode.DirectStdout, Empty, std::string> Stdout;
std::conditional_t<Mode.DirectStderr, Empty, std::string> Stderr;
operator bool() const;
};
struct ExecInput { bool DirectStdin = false, DirectStdout = false, DirectStderr = false, SearchPath = false; };
template <ExecMode Mode> struct ExecInput
{
std::conditional_t<Mode.SearchPath, std::string, std::filesystem::path> Program;
std::vector<std::string> Args;
std::conditional_t<Mode.DirectStdin, Empty, std::string> Stdin = {};
std::map<std::string, std::string> ExtraEnv = {};
std::optional<std::chrono::milliseconds> Timeout;
};
}
template <detail_::ExecInput Input = {}> requires (!Input.DirectStdin)
detail_::ExecResult<Input.DirectStdout, Input.DirectStderr> exec
(
std::conditional_t<Input.SearchPath, std::string, std::filesystem::path> program, std::vector<std::string> args,
std::optional<std::string> stdin_string = {}, std::map<std::string, std::string> extra_env = {}
);
template <detail_::ExecInput Input = {}> requires (Input.DirectStdin)
detail_::ExecResult<Input.DirectStdout, Input.DirectStderr> exec
(
std::conditional_t<Input.SearchPath, std::string, std::filesystem::path> program, std::vector<std::string> args,
std::map<std::string, std::string> extra_env = {}
);
template <detail_::ExecMode Mode = {}> detail_::ExecResult<Mode> exec(detail_::ExecInput<Mode> input);
static_assert(sizeof(char) == sizeof(std::byte));
template <typename Char = std::byte, typename T> requires (std::same_as<Char, std::byte>)

View File

@@ -18,87 +18,54 @@ namespace biu
else return value;
}
template <bool DirectStdout, bool DirectStderr>
detail_::ExecResult<DirectStdout, DirectStderr>::operator bool() const
{ return ExitCode == 0; }
# define BIU_EXECRESULT_PRED(r, state) BOOST_PP_NOT_EQUAL(state, 4)
# define BIU_EXECRESULT_OP(r, state) BOOST_PP_INC(state)
# define BIU_EXECRESULT_MACRO(r, state) \
template detail_::ExecResult<(state & 1) != 0, (state & 2) != 0>::operator bool() const;
BOOST_PP_FOR(0, BIU_EXECRESULT_PRED, BIU_EXECRESULT_OP, BIU_EXECRESULT_MACRO)
namespace detail_
template <detail_::ExecMode Mode> detail_::ExecResult<Mode>::operator bool() const { return ExitCode == 0; }
template <detail_::ExecMode Mode> detail_::ExecResult<Mode> exec(detail_::ExecInput<Mode> input)
{
template <ExecInput Input>
detail_::ExecResult<Input.DirectStdout, Input.DirectStderr> exec
(
std::conditional_t<Input.SearchPath, std::string, std::filesystem::path> program, std::vector<std::string> args,
std::optional<std::string> stdin_string, std::map<std::string, std::string> extra_env
)
namespace bp = boost::process;
// decide input/output format, prepare environment, seach actual program
bp::ipstream stdout_stream, stderr_stream;
bp::opstream input_stream;
auto&& stdin_format = [&]
{ if constexpr (Mode.DirectStdin) return bp::std_in < stdin; else return bp::std_in < input_stream; }();
auto&& stdout_format = [&]
{ if constexpr (Mode.DirectStdout) return bp::std_out > stdout; else return bp::std_out > stdout_stream; }();
auto&& stderr_format = [&]
{ if constexpr (Mode.DirectStderr) return bp::std_err > stderr; else return bp::std_err > stderr_stream; }();
auto&& actual_program = [&]
{
namespace bp = boost::process;
bp::ipstream stdout_stream, stderr_stream;
bp::opstream input_stream;
auto&& stdout_format = [&]
{ if constexpr (Input.DirectStdout) return bp::std_out > stdout; else return bp::std_out > stdout_stream; }();
auto&& stderr_format = [&]
{ if constexpr (Input.DirectStderr) return bp::std_err > stderr; else return bp::std_err > stderr_stream; }();
auto&& actual_program =
[&]{ if constexpr (Input.SearchPath) return bp::search_path(program); else return program.string(); }();
std::unique_ptr<bp::child> process;
bp::environment env = boost::this_process::environment();
for (const auto& [key, value] : extra_env) env[key] = value;
process = [&]
{
if constexpr (Input.DirectStdin) return std::make_unique<bp::child>
(actual_program, bp::args(args), stdout_format, stderr_format, bp::std_in < stdin, env);
else if (stdin_string) return std::make_unique<bp::child>
(actual_program, bp::args(args), stdout_format, stderr_format, bp::std_in < input_stream, env);
else return std::make_unique<bp::child>
(actual_program, bp::args(args), stdout_format, stderr_format, bp::std_in < bp::null, env);
}();
if (stdin_string) { input_stream << *stdin_string; input_stream.pipe().close(); }
process->wait();
return
{
.ExitCode = process->exit_code(),
.Stdout = [&]
{
if constexpr (Input.DirectStdout) return Empty{};
else return std::string{std::istreambuf_iterator<char>{stdout_stream.rdbuf()}, {}};
}(),
.Stderr = [&]
{
if constexpr (Input.DirectStderr) return Empty{};
else return std::string{std::istreambuf_iterator<char>{stderr_stream.rdbuf()}, {}};
}()
};
}
if constexpr (Mode.SearchPath) return bp::search_path(input.Program);
else return input.Program.string();
}();
bp::environment env = boost::this_process::environment();
for (const auto& [key, value] : input.ExtraEnv) env[key] = value;
// start
auto process = bp::child
(actual_program, bp::args(input.Args), stdout_format, stderr_format, stdin_format, env);
if constexpr (!Mode.DirectStdin) { input_stream << input.Stdin; input_stream.pipe().close(); }
// wait for exit
if (input.Timeout) { if (!process.wait_for(*input.Timeout)) process.terminate(); }
else process.wait();
// collect output
detail_::ExecResult<Mode> result;
result.ExitCode = process.exit_code();
if constexpr (!Mode.DirectStdout) result.Stdout = {std::istreambuf_iterator<char>{stdout_stream.rdbuf()}, {}};
if constexpr (!Mode.DirectStderr) result.Stderr = {std::istreambuf_iterator<char>{stderr_stream.rdbuf()}, {}};
return result;
}
template <detail_::ExecInput Input> requires (!Input.DirectStdin)
detail_::ExecResult<Input.DirectStdout, Input.DirectStderr> exec
(
std::conditional_t<Input.SearchPath, std::string, std::filesystem::path> program, std::vector<std::string> args,
std::optional<std::string> stdin_string, std::map<std::string, std::string> extra_env
)
{ return detail_::exec<Input>(program, args, stdin_string, extra_env); }
template <detail_::ExecInput Input> requires (Input.DirectStdin)
detail_::ExecResult<Input.DirectStdout, Input.DirectStderr> exec
(
std::conditional_t<Input.SearchPath, std::string, std::filesystem::path> program, std::vector<std::string> args,
std::map<std::string, std::string> extra_env
)
{ return detail_::exec<Input>(program, args, {}, extra_env); }
# define BIU_EXEC_PRED(r, state) BOOST_PP_NOT_EQUAL(state, 8)
# define BIU_EXEC_OP(r, state) BOOST_PP_INC(state)
# define BIU_EXEC_MACRO(r, state) \
template detail_::ExecResult<(state & 1) != 0, (state & 2) != 0> \
exec<{false, (state & 1) != 0, (state & 2) != 0, (state & 4) != 0}> \
(std::conditional_t<(state & 4) != 0, std::string, std::filesystem::path>, std::vector<std::string>, \
std::optional<std::string>, std::map<std::string, std::string>); \
template detail_::ExecResult<(state & 1) != 0, (state & 2) != 0> \
exec<{true, (state & 1) != 0, (state & 2) != 0, (state & 4) != 0}> \
(std::conditional_t<(state & 4) != 0, std::string, std::filesystem::path>, std::vector<std::string>, \
std::map<std::string, std::string>);
# define BIU_EXEC_PRED(r, i) BOOST_PP_NOT_EQUAL(i, 16)
# define BIU_EXEC_OP(r, i) BOOST_PP_INC(i)
# define BIU_EXEC_MACRO(r, i) \
namespace detail_ \
{ constexpr ExecMode ExecMode##i {(i & 1) != 0, (i & 2) != 0, (i & 4) != 0, (i & 8) != 0}; } \
template detail_::ExecResult<detail_::ExecMode##i>::operator bool() const; \
template detail_::ExecResult<detail_::ExecMode##i> \
exec<detail_::ExecMode##i>(detail_::ExecInput<detail_::ExecMode##i>);
BOOST_PP_FOR(0, BIU_EXEC_PRED, BIU_EXEC_OP, BIU_EXEC_MACRO)
}
}

View File

@@ -0,0 +1,9 @@
# include <biu.hpp>
int main()
{
using namespace biu::literals;
auto result = biu::exec<{.SearchPath = true}>({.Program = "sleep", .Args = {"10"}, .Timeout = 3s});
std::cout << "{}\n"_f(result.ExitCode);
assert(!result);
}