mirror of
https://github.com/CHN-beta/nixos.git
synced 2026-01-12 04:19:22 +08:00
localPackages.biu: exec allow set time limit
This commit is contained in:
@@ -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)
|
||||
@@ -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>)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
9
local/pkgs/biu/test/process.cpp
Normal file
9
local/pkgs/biu/test/process.cpp
Normal 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);
|
||||
}
|
||||
Reference in New Issue
Block a user