mirror of
https://github.com/CHN-beta/nixos.git
synced 2026-01-12 02:09:26 +08:00
packages.biu: migrate to process v2
This commit is contained in:
6
flake.lock
generated
6
flake.lock
generated
@@ -880,11 +880,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_2": {
|
"nixpkgs_2": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1751630609,
|
"lastModified": 1753842094,
|
||||||
"narHash": "sha256-mJ1XnKiLnNapGSUwyGdFD8tmQTzuJm8z3qaFC27guqE=",
|
"narHash": "sha256-1IAtQ0itZjT07XyUNfHJ4r8nO4+036T6scVUFs1NEko=",
|
||||||
"owner": "CHN-beta",
|
"owner": "CHN-beta",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b263408b62d74a1e7a298fc47135653a70c227aa",
|
"rev": "2ebe9e65f6f1223450f58bf0197fcbce3fb9bf0f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
13
flake/lib/buildNixpkgsConfig/boost188.patch
Normal file
13
flake/lib/buildNixpkgsConfig/boost188.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/boost/process/v2/stdio.hpp b/boost/process/v2/stdio.hpp
|
||||||
|
index 01d0216..4084e46 100644
|
||||||
|
--- a/boost/process/v2/stdio.hpp
|
||||||
|
+++ b/boost/process/v2/stdio.hpp
|
||||||
|
@@ -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;
|
||||||
@@ -69,6 +69,7 @@ in platformConfig //
|
|||||||
patches = prev.patches or [] ++ [ ./root.patch ];
|
patches = prev.patches or [] ++ [ ./root.patch ];
|
||||||
cmakeFlags = prev.cmakeFlags ++ [ "-DCMAKE_CXX_STANDARD=23" ];
|
cmakeFlags = prev.cmakeFlags ++ [ "-DCMAKE_CXX_STANDARD=23" ];
|
||||||
});
|
});
|
||||||
|
boost188 = prev.boost188.overrideAttrs (prev: { patches = prev.patches or [] ++ [ ./boost188.patch ]; });
|
||||||
inherit (final.pkgs-2411) iio-sensor-proxy;
|
inherit (final.pkgs-2411) iio-sensor-proxy;
|
||||||
inherit (final.pkgs-unstable) bees;
|
inherit (final.pkgs-unstable) bees;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ endif()
|
|||||||
|
|
||||||
find_package(magic_enum REQUIRED)
|
find_package(magic_enum REQUIRED)
|
||||||
find_package(fmt REQUIRED)
|
find_package(fmt REQUIRED)
|
||||||
find_package(Boost REQUIRED COMPONENTS headers iostreams filesystem system process stacktrace_from_exception)
|
find_package(Boost REQUIRED COMPONENTS headers iostreams filesystem system process stacktrace_from_exception
|
||||||
# stacktrace_backtrace
|
stacktrace_backtrace)
|
||||||
find_package(range-v3 REQUIRED)
|
find_package(range-v3 REQUIRED)
|
||||||
find_path(NAMEOF_INCLUDE_DIR nameof.hpp REQUIRED)
|
find_path(NAMEOF_INCLUDE_DIR nameof.hpp REQUIRED)
|
||||||
find_package(Eigen3 REQUIRED)
|
find_package(Eigen3 REQUIRED)
|
||||||
@@ -36,7 +36,7 @@ target_include_directories(biu PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_D
|
|||||||
${LIBBACKTRACE_INCLUDE_DIR} ${POCKETFFT_INCLUDE_DIR})
|
${LIBBACKTRACE_INCLUDE_DIR} ${POCKETFFT_INCLUDE_DIR})
|
||||||
target_link_libraries(biu PUBLIC magic_enum::magic_enum fmt::fmt Boost::headers Boost::iostreams Boost::filesystem
|
target_link_libraries(biu PUBLIC magic_enum::magic_enum fmt::fmt Boost::headers Boost::iostreams Boost::filesystem
|
||||||
range-v3::range-v3 Eigen3::Eigen HighFive TgBot::TgBot ${LIBBACKTRACE_LIBRARY} hdf5::hdf5 concurrencpp::concurrencpp
|
range-v3::range-v3 Eigen3::Eigen HighFive TgBot::TgBot ${LIBBACKTRACE_LIBRARY} hdf5::hdf5 concurrencpp::concurrencpp
|
||||||
yaml-cpp::yaml-cpp glaze::glaze Boost::process Boost::stacktrace_from_exception)
|
yaml-cpp::yaml-cpp glaze::glaze Boost::process Boost::stacktrace_from_exception Boost::stacktrace_backtrace)
|
||||||
target_compile_features(biu PUBLIC cxx_std_23)
|
target_compile_features(biu PUBLIC cxx_std_23)
|
||||||
target_compile_options(biu PUBLIC -Wno-gnu-string-literal-operator-template)
|
target_compile_options(biu PUBLIC -Wno-gnu-string-literal-operator-template)
|
||||||
install(TARGETS biu EXPORT biuTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
install(TARGETS biu EXPORT biuTargets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
include("${CMAKE_CURRENT_LIST_DIR}/biuTargets.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/biuTargets.cmake")
|
||||||
find_package(magic_enum REQUIRED)
|
find_package(magic_enum REQUIRED)
|
||||||
find_package(fmt REQUIRED)
|
find_package(fmt REQUIRED)
|
||||||
find_package(Boost REQUIRED COMPONENTS headers iostreams filesystem system process stacktrace_from_exception)
|
find_package(Boost REQUIRED COMPONENTS headers iostreams filesystem system process stacktrace_from_exception
|
||||||
|
stacktrace_backtrace)
|
||||||
find_package(range-v3 REQUIRED)
|
find_package(range-v3 REQUIRED)
|
||||||
find_path(NAMEOF_INCLUDE_DIR nameof.hpp REQUIRED)
|
find_path(NAMEOF_INCLUDE_DIR nameof.hpp REQUIRED)
|
||||||
find_package(Eigen3 REQUIRED)
|
find_package(Eigen3 REQUIRED)
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
src = ./.;
|
src = ./.;
|
||||||
buildInputs =
|
buildInputs =
|
||||||
[
|
[
|
||||||
magic-enum fmt boost range-v3 nameof zpp-bits eigen highfive tgbot-cpp libbacktrace hdf5
|
magic-enum fmt boost range-v3 nameof zpp-bits eigen libbacktrace hdf5
|
||||||
concurrencpp pocketfft yaml-cpp glaze
|
concurrencpp pocketfft yaml-cpp glaze (highfive.override { inherit boost; }) (tgbot-cpp.override { inherit boost; })
|
||||||
];
|
];
|
||||||
propagatedBuildInputs = buildInputs;
|
propagatedBuildInputs = buildInputs;
|
||||||
nativeBuildInputs = [ cmake ];
|
nativeBuildInputs = [ cmake ];
|
||||||
|
|||||||
@@ -17,3 +17,4 @@
|
|||||||
# include <biu/serialize.tpp>
|
# include <biu/serialize.tpp>
|
||||||
# include <biu/glaze.tpp>
|
# include <biu/glaze.tpp>
|
||||||
# include <range/v3/all.hpp>
|
# include <range/v3/all.hpp>
|
||||||
|
# include <biu/process.tpp>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace biu
|
|||||||
public: ValueType get(this auto&& self);
|
public: ValueType get(this auto&& self);
|
||||||
public: operator ValueType(this auto&& self);
|
public: operator ValueType(this auto&& self);
|
||||||
|
|
||||||
protected: template <bool Throw = false> auto lock_(this auto&& self, auto&& condition_function, auto timeout);
|
protected: template <bool Throw> auto lock_(this auto&& self, auto&& condition_function, auto timeout);
|
||||||
|
|
||||||
// Apply a function to stored value.
|
// Apply a function to stored value.
|
||||||
// Wait for some time (if provided) until condition funciton returns true (if provided)
|
// Wait for some time (if provided) until condition funciton returns true (if provided)
|
||||||
@@ -45,6 +45,7 @@ namespace biu
|
|||||||
// NoReturn: throw exception if timeout, ignore function result, and return *this, if true;
|
// NoReturn: throw exception if timeout, ignore function result, and return *this, if true;
|
||||||
// return bool or std::optional wrapped result of function, if false.
|
// return bool or std::optional wrapped result of function, if false.
|
||||||
// Useful when chaining multiple apply() calls.
|
// Useful when chaining multiple apply() calls.
|
||||||
|
// timeout 只考虑 condition_function 的超时,不考虑锁本身的超时。
|
||||||
public: template <bool NoReturn = false> decltype(auto) apply
|
public: template <bool NoReturn = false> decltype(auto) apply
|
||||||
(this auto&& self, auto&& function, auto&& condition_function, auto&& timeout);
|
(this auto&& self, auto&& function, auto&& condition_function, auto&& timeout);
|
||||||
public: template <bool NoReturn = false> decltype(auto) apply
|
public: template <bool NoReturn = false> decltype(auto) apply
|
||||||
@@ -54,8 +55,7 @@ namespace biu
|
|||||||
// Wait until condition funciton returns true or *this, with an optional timeout
|
// Wait until condition funciton returns true or *this, with an optional timeout
|
||||||
public: template <bool NoReturn = false> decltype(auto) wait
|
public: template <bool NoReturn = false> decltype(auto) wait
|
||||||
(this auto&& self, auto&& condition_function, auto timeout);
|
(this auto&& self, auto&& condition_function, auto timeout);
|
||||||
public: template <bool NoReturn = false> decltype(auto) wait
|
public: decltype(auto) wait(this auto&& self, auto&& condition_function);
|
||||||
(this auto&& self, auto&& condition_function);
|
|
||||||
|
|
||||||
// Attain lock from outside when constructing, and release when destructing.
|
// Attain lock from outside when constructing, and release when destructing.
|
||||||
// Throw: same effect as NoReturn.
|
// Throw: same effect as NoReturn.
|
||||||
|
|||||||
@@ -41,8 +41,11 @@ namespace biu
|
|||||||
(this auto&& self, auto&& condition_function, auto timeout)
|
(this auto&& self, auto&& condition_function, auto timeout)
|
||||||
{
|
{
|
||||||
if constexpr (Nullptr<decltype(condition_function)>)
|
if constexpr (Nullptr<decltype(condition_function)>)
|
||||||
|
{
|
||||||
|
static_assert(Nullptr<decltype(timeout)>);
|
||||||
return Guard<std::is_const_v<decltype(self)>>
|
return Guard<std::is_const_v<decltype(self)>>
|
||||||
(std::unique_lock{self.Mutex_}, std::experimental::make_observer(&self), {});
|
(std::unique_lock{self.Mutex_}, std::experimental::make_observer(&self), {});
|
||||||
|
}
|
||||||
else if constexpr (Nullptr<decltype(timeout)>)
|
else if constexpr (Nullptr<decltype(timeout)>)
|
||||||
{
|
{
|
||||||
std::unique_lock lock(self.Mutex_);
|
std::unique_lock lock(self.Mutex_);
|
||||||
@@ -70,28 +73,35 @@ namespace biu
|
|||||||
using function_return_type = std::invoke_result_t<decltype(function), MoveQualifiers<decltype(self), ValueType>>;
|
using function_return_type = std::invoke_result_t<decltype(function), MoveQualifiers<decltype(self), ValueType>>;
|
||||||
auto&& lock = std::forward<decltype(self)>(self).template lock_<NoReturn>
|
auto&& lock = std::forward<decltype(self)>(self).template lock_<NoReturn>
|
||||||
(std::forward<decltype(condition_function)>(condition_function), timeout);
|
(std::forward<decltype(condition_function)>(condition_function), timeout);
|
||||||
// 如果得到的是 optional
|
// 如果 lock 是 optional
|
||||||
if constexpr (SpecializationOf<std::remove_cvref_t<decltype(lock)>, std::optional>)
|
if constexpr (SpecializationOf<std::remove_cvref_t<decltype(lock)>, std::optional>)
|
||||||
// 如果超时了,返回 false 或者对应的 nullopt
|
// 如果超时了,这时 NoReturn 一定是 false,返回 false 或者对应的 nullopt
|
||||||
if (!lock)
|
if (!lock)
|
||||||
|
{
|
||||||
|
static_assert(!NoReturn);
|
||||||
if constexpr (std::is_void_v<function_return_type>) return false;
|
if constexpr (std::is_void_v<function_return_type>) return false;
|
||||||
else return std::optional<function_return_type>();
|
else return std::optional<function_return_type>();
|
||||||
|
}
|
||||||
// 否则,执行函数
|
// 否则,执行函数
|
||||||
else
|
else
|
||||||
|
// 如果函数本身返回 void,则返回 true 或者 *this
|
||||||
if constexpr (std::is_void_v<function_return_type>)
|
if constexpr (std::is_void_v<function_return_type>)
|
||||||
{
|
{
|
||||||
std::forward<decltype(function)>(function)
|
std::forward<decltype(function)>(function)
|
||||||
(std::forward<MoveQualifiers<decltype(self), ValueType>>(self.Value_));
|
(std::forward<MoveQualifiers<decltype(self), ValueType>>(self.Value_));
|
||||||
// 如果函数本身返回 void 并且不可能超时,返回 *this,否则返回 true
|
// 如果要求不返回结果,则返回 *this
|
||||||
if constexpr (Nullptr<decltype(condition_function)> || Nullptr<decltype(timeout)>)
|
if constexpr (NoReturn) return std::forward<decltype(self)>(self);
|
||||||
return std::forward<decltype(self)>(self);
|
// 否则,返回 true
|
||||||
else return true;
|
else return true;
|
||||||
}
|
}
|
||||||
|
// 否则,返回函数的返回值或者 std::optional 包装的结果
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto&& result = std::forward<decltype(function)>(function)
|
auto&& result = std::forward<decltype(function)>(function)
|
||||||
(std::forward<MoveQualifiers<decltype(self), ValueType>>(self.Value_));
|
(std::forward<MoveQualifiers<decltype(self), ValueType>>(self.Value_));
|
||||||
return std::make_optional(std::forward<decltype(result)>(result));
|
if constexpr (NoReturn)
|
||||||
|
return std::forward<decltype(self)>(self);
|
||||||
|
else return std::make_optional(std::forward<decltype(result)>(result));
|
||||||
}
|
}
|
||||||
// 否则,说明不可能超时,返回函数的返回值或者 *this
|
// 否则,说明不可能超时,返回函数的返回值或者 *this
|
||||||
else
|
else
|
||||||
@@ -125,15 +135,15 @@ namespace biu
|
|||||||
template <DecayedType ValueType> template <bool NoReturn> decltype(auto) Atomic<ValueType>::wait
|
template <DecayedType ValueType> template <bool NoReturn> decltype(auto) Atomic<ValueType>::wait
|
||||||
(this auto&& self, auto&& condition_function, auto timeout)
|
(this auto&& self, auto&& condition_function, auto timeout)
|
||||||
{
|
{
|
||||||
auto result = std::forward<decltype(self)>(self).template lock_<NoReturn>
|
auto&& result = std::forward<decltype(self)>(self).template lock_<NoReturn>
|
||||||
(std::forward<decltype(condition_function)>(condition_function), timeout);
|
(std::forward<decltype(condition_function)>(condition_function), timeout);
|
||||||
if constexpr (SpecializationOf<decltype(result), std::optional>) return result.has_value();
|
if constexpr (SpecializationOf<decltype(result), std::optional>) return result.has_value();
|
||||||
else return std::forward<decltype(result)>(result);
|
else return std::forward<decltype(self)>(self);
|
||||||
}
|
}
|
||||||
template <DecayedType ValueType> template <bool NoReturn> decltype(auto) Atomic<ValueType>::wait
|
template <DecayedType ValueType> decltype(auto) Atomic<ValueType>::wait
|
||||||
(this auto&& self, auto&& condition_function)
|
(this auto&& self, auto&& condition_function)
|
||||||
{
|
{
|
||||||
return std::forward<decltype(self)>(self).template wait<NoReturn>
|
return std::forward<decltype(self)>(self).template wait<false>
|
||||||
(std::forward<decltype(condition_function)>(condition_function), nullptr);
|
(std::forward<decltype(condition_function)>(condition_function), nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -74,27 +74,6 @@ namespace biu
|
|||||||
template <typename T, typename Fallback = void> using FallbackIfNoTypeDeclared
|
template <typename T, typename Fallback = void> using FallbackIfNoTypeDeclared
|
||||||
= typename detail_::FallbackIfNoTypeDeclaredHelper<T, Fallback>::Type;
|
= typename detail_::FallbackIfNoTypeDeclaredHelper<T, Fallback>::Type;
|
||||||
|
|
||||||
namespace detail_
|
|
||||||
{
|
|
||||||
struct ExecMode { bool DirectStdin = false, DirectStdout = false, DirectStderr = false, SearchPath = false; };
|
|
||||||
template <ExecMode Mode> struct ExecResult
|
|
||||||
{
|
|
||||||
int ExitCode;
|
|
||||||
std::conditional_t<Mode.DirectStdout, Empty, std::string> Stdout;
|
|
||||||
std::conditional_t<Mode.DirectStderr, Empty, std::string> Stderr;
|
|
||||||
operator bool() const;
|
|
||||||
};
|
|
||||||
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_::ExecMode Mode = {}> detail_::ExecResult<Mode> exec(detail_::ExecInput<Mode> input);
|
|
||||||
|
|
||||||
template <typename Array> concurrencpp::generator<std::pair<Array, std::size_t>> sequence(Array from, Array to);
|
template <typename Array> concurrencpp::generator<std::pair<Array, std::size_t>> sequence(Array from, Array to);
|
||||||
template <typename Array> concurrencpp::generator<std::pair<Array, std::size_t>> sequence(Array to);
|
template <typename Array> concurrencpp::generator<std::pair<Array, std::size_t>> sequence(Array to);
|
||||||
|
|
||||||
@@ -120,9 +99,11 @@ namespace biu
|
|||||||
constexpr detail_::ToLvalueHelper toLvalue;
|
constexpr detail_::ToLvalueHelper toLvalue;
|
||||||
|
|
||||||
template <typename Function, typename T, typename... Ts> void for_each(Function&& function, T&& arg, Ts&&... args);
|
template <typename Function, typename T, typename... Ts> void for_each(Function&& function, T&& arg, Ts&&... args);
|
||||||
|
|
||||||
|
template <typename T> decltype(auto) perfect_return(T&& obj);
|
||||||
}
|
}
|
||||||
using common::hash, common::unused, common::block_forever, common::is_interactive, common::env, common::int128_t,
|
using common::hash, common::unused, common::block_forever, common::is_interactive, common::env, common::int128_t,
|
||||||
common::uint128_t, common::Empty, common::CaseInsensitiveStringLessComparator, common::RemoveMemberPointer,
|
common::uint128_t, common::Empty, common::CaseInsensitiveStringLessComparator, common::RemoveMemberPointer,
|
||||||
common::MoveQualifiers, common::FallbackIfNoTypeDeclared, common::exec, common::sequence, common::read,
|
common::MoveQualifiers, common::FallbackIfNoTypeDeclared, common::sequence, common::read,
|
||||||
common::toLvalue, common::for_each;
|
common::toLvalue, common::for_each, common::perfect_return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,4 +75,18 @@ namespace biu::common
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename T> decltype(auto) perfect_return(T&& obj)
|
||||||
|
{
|
||||||
|
// 不允许返回右值引用
|
||||||
|
static_assert(!std::is_rvalue_reference_v<T>);
|
||||||
|
// 左值引用则返回左值引用
|
||||||
|
if constexpr (std::is_lvalue_reference_v<T>) return (obj);
|
||||||
|
// 否则,假定可以移动,并返回值
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static_assert(std::is_move_constructible_v<std::remove_reference_t<T>>);
|
||||||
|
return std::move(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
36
packages/biu/include/biu/process.hpp
Normal file
36
packages/biu/include/biu/process.hpp
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# pragma once
|
||||||
|
# include <biu/common.hpp>
|
||||||
|
|
||||||
|
namespace biu
|
||||||
|
{
|
||||||
|
namespace process
|
||||||
|
{
|
||||||
|
enum class IoType { Direct, Close, String };
|
||||||
|
namespace detail_
|
||||||
|
{
|
||||||
|
struct ExecMode
|
||||||
|
{
|
||||||
|
bool SearchPath = false, ModifyEnv = false, Timeout = false;
|
||||||
|
IoType Stdin = IoType::Direct, Stdout = IoType::Direct, Stderr = IoType::Direct;
|
||||||
|
};
|
||||||
|
template <ExecMode Mode> struct ExecResult
|
||||||
|
{
|
||||||
|
int ExitCode;
|
||||||
|
std::conditional_t<Mode.Stdout == IoType::String, std::string, Empty> Stdout;
|
||||||
|
std::conditional_t<Mode.Stderr == IoType::String, std::string, Empty> Stderr;
|
||||||
|
operator bool() const;
|
||||||
|
};
|
||||||
|
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.Stdin == IoType::String, std::string, Empty> Stdin = {};
|
||||||
|
std::conditional_t<Mode.ModifyEnv, std::map<std::string, std::string>, Empty> ExtraEnv = {};
|
||||||
|
std::conditional_t<Mode.Timeout, std::chrono::milliseconds, Empty> Timeout = {};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
template <detail_::ExecMode Mode = {}, typename... Ts>
|
||||||
|
detail_::ExecResult<Mode> exec(detail_::ExecInput<Mode> input, Ts&&... args);
|
||||||
|
}
|
||||||
|
using process::exec, process::IoType;
|
||||||
|
}
|
||||||
178
packages/biu/include/biu/process.tpp
Normal file
178
packages/biu/include/biu/process.tpp
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
# pragma once
|
||||||
|
# include <biu/process.hpp>
|
||||||
|
# include <biu/logger.hpp>
|
||||||
|
# include <biu/format.tpp>
|
||||||
|
# include <boost/process.hpp>
|
||||||
|
# include <boost/asio.hpp>
|
||||||
|
|
||||||
|
namespace biu::process
|
||||||
|
{
|
||||||
|
template <detail_::ExecMode Mode> detail_::ExecResult<Mode>::operator bool() const { return ExitCode == 0; }
|
||||||
|
|
||||||
|
template <detail_::ExecMode Mode, typename... Ts> detail_::ExecResult<Mode> exec
|
||||||
|
(detail_::ExecInput<Mode> input, Ts&&... args)
|
||||||
|
{
|
||||||
|
Logger::Guard log;
|
||||||
|
namespace bp = boost::process;
|
||||||
|
boost::asio::io_context context;
|
||||||
|
detail_::ExecResult<Mode> result;
|
||||||
|
using namespace biu::literals;
|
||||||
|
|
||||||
|
// 进程是在创建时就开始运行的,而不是在 io_context.run() 时才开始运行
|
||||||
|
|
||||||
|
// seach actual program
|
||||||
|
boost::filesystem::path actual_program = [&]
|
||||||
|
{
|
||||||
|
if constexpr (Mode.SearchPath) return bp::environment::find_executable(input.Program);
|
||||||
|
else return input.Program.string();
|
||||||
|
}();
|
||||||
|
log.debug("Searching for program: {} -> {}"_f(input.Program, actual_program.string()));
|
||||||
|
|
||||||
|
// env
|
||||||
|
auto env = [&]
|
||||||
|
{
|
||||||
|
if constexpr (Mode.ModifyEnv)
|
||||||
|
{
|
||||||
|
auto current = bp::environment::current();
|
||||||
|
std::unordered_map<bp::environment::key, bp::environment::value> env
|
||||||
|
(current.begin(), current.end());
|
||||||
|
for (const auto& [key, value] : input.ExtraEnv) env[key] = value;
|
||||||
|
}
|
||||||
|
else return bp::environment::current();
|
||||||
|
}();
|
||||||
|
log();
|
||||||
|
|
||||||
|
// prepare io pipes
|
||||||
|
std::unique_ptr<boost::asio::writable_pipe> stdin_pipe;
|
||||||
|
std::unique_ptr<boost::asio::readable_pipe> stdout_pipe, stderr_pipe;
|
||||||
|
std::function<void(boost::asio::readable_pipe& p, std::string& result)> read_some =
|
||||||
|
[&read_some](auto& p, auto& result)
|
||||||
|
{
|
||||||
|
auto buffer = std::make_shared<std::array<char, 1024>>();
|
||||||
|
p.async_read_some
|
||||||
|
(
|
||||||
|
boost::asio::buffer(*buffer),
|
||||||
|
[&, buffer](const boost::system::error_code& ec, std::size_t len)
|
||||||
|
{
|
||||||
|
Logger::Guard log;
|
||||||
|
if (!ec) { result.append(buffer->data(), len); read_some(p, result); log.debug("read {}"_f(len)); }
|
||||||
|
else log.debug("Error reading from pipe: {} {}"_f(ec.value(), ec.message()));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
std::function<void(boost::asio::writable_pipe& p, std::string& result)> write_some =
|
||||||
|
[&write_some](auto& p, auto& result)
|
||||||
|
{
|
||||||
|
if (result.empty()) { p.close(); return; }
|
||||||
|
auto buffer = std::make_shared<std::array<char, 1024>>();
|
||||||
|
std::copy(result.begin(), result.end(), buffer->begin());
|
||||||
|
p.async_write_some
|
||||||
|
(
|
||||||
|
boost::asio::buffer(*buffer),
|
||||||
|
[&, buffer](const auto& ec, std::size_t len)
|
||||||
|
{
|
||||||
|
Logger::Guard log;
|
||||||
|
if (!ec) { result.erase(0, len); write_some(p, result); log.debug("write {}"_f(len)); }
|
||||||
|
else log.debug("Error reading from pipe: {} {}"_f(ec.value(), ec.message()));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
bp::process_stdio stdio
|
||||||
|
{
|
||||||
|
.in = [&] -> decltype(auto)
|
||||||
|
{
|
||||||
|
if constexpr (Mode.Stdin == IoType::Close) return nullptr;
|
||||||
|
else if constexpr (Mode.Stdin == IoType::Direct)
|
||||||
|
return decltype(bp::process_stdio::in)();
|
||||||
|
else if constexpr (Mode.Stdin == IoType::String)
|
||||||
|
{
|
||||||
|
stdin_pipe = std::make_unique<boost::asio::writable_pipe>(context);
|
||||||
|
write_some(*stdin_pipe, input.Stdin);
|
||||||
|
return (*stdin_pipe);
|
||||||
|
}
|
||||||
|
else std::unreachable();
|
||||||
|
}(),
|
||||||
|
.out = [&] -> decltype(auto)
|
||||||
|
{
|
||||||
|
if constexpr (Mode.Stdout == IoType::Close) return nullptr;
|
||||||
|
else if constexpr (Mode.Stdout == IoType::Direct)
|
||||||
|
return decltype(bp::process_stdio::out)();
|
||||||
|
else if constexpr (Mode.Stdout == IoType::String)
|
||||||
|
{
|
||||||
|
stdout_pipe = std::make_unique<boost::asio::readable_pipe>(context);
|
||||||
|
read_some(*stdout_pipe, result.Stdout);
|
||||||
|
return (*stdout_pipe);
|
||||||
|
}
|
||||||
|
else std::unreachable();
|
||||||
|
}(),
|
||||||
|
.err = [&] -> decltype(auto)
|
||||||
|
{
|
||||||
|
if constexpr (Mode.Stderr == IoType::Close) return nullptr;
|
||||||
|
else if constexpr (Mode.Stderr == IoType::Direct)
|
||||||
|
return decltype(bp::process_stdio::err)();
|
||||||
|
else if constexpr (Mode.Stderr == IoType::String)
|
||||||
|
{
|
||||||
|
stderr_pipe = std::make_unique<boost::asio::readable_pipe>(context);
|
||||||
|
read_some(*stderr_pipe, result.Stderr);
|
||||||
|
return (*stderr_pipe);
|
||||||
|
}
|
||||||
|
else std::unreachable();
|
||||||
|
}()
|
||||||
|
};
|
||||||
|
log();
|
||||||
|
|
||||||
|
// start process
|
||||||
|
if constexpr (Mode.Timeout)
|
||||||
|
{
|
||||||
|
boost::asio::steady_timer timeout{context, input.Timeout};
|
||||||
|
boost::asio::cancellation_signal sig;
|
||||||
|
Atomic<bool> finished{false};
|
||||||
|
Logger::try_exec([&]
|
||||||
|
{
|
||||||
|
auto proc = bp::process
|
||||||
|
(context, actual_program, input.Args, std::move(stdio), std::move(env), std::forward<Ts>(args)...);
|
||||||
|
bp::async_execute
|
||||||
|
(
|
||||||
|
std::move(proc),
|
||||||
|
boost::asio::bind_cancellation_slot
|
||||||
|
(
|
||||||
|
sig.slot(),
|
||||||
|
[&](bp::v2::error_code ec, int exit_code)
|
||||||
|
{
|
||||||
|
result.ExitCode = exit_code;
|
||||||
|
timeout.cancel();
|
||||||
|
finished = true;
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
timeout.expires_after(input.Timeout);
|
||||||
|
timeout.async_wait([&](auto ec)
|
||||||
|
{
|
||||||
|
if (ec) return;
|
||||||
|
sig.emit(boost::asio::cancellation_type::partial);
|
||||||
|
timeout.expires_after(input.Timeout);
|
||||||
|
timeout.async_wait
|
||||||
|
([&](auto ec) { if (!ec) sig.emit(boost::asio::cancellation_type::terminal); });
|
||||||
|
});
|
||||||
|
context.run();
|
||||||
|
finished.wait([](auto& v) { return v; });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto thread = std::thread([&]
|
||||||
|
{
|
||||||
|
Logger::try_exec([&]
|
||||||
|
{
|
||||||
|
auto proc = bp::process
|
||||||
|
(context, actual_program, input.Args, std::move(stdio), std::move(env), std::forward<Ts>(args)...);
|
||||||
|
proc.wait();
|
||||||
|
result.ExitCode = proc.exit_code();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
context.run();
|
||||||
|
thread.join();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@
|
|||||||
# include <cstdio>
|
# include <cstdio>
|
||||||
# define BIU_INTERNAL
|
# define BIU_INTERNAL
|
||||||
# include <biu.hpp>
|
# include <biu.hpp>
|
||||||
# include <boost/process.hpp>
|
|
||||||
# include <boost/preprocessor.hpp>
|
|
||||||
|
|
||||||
namespace biu
|
namespace biu
|
||||||
{
|
{
|
||||||
@@ -19,56 +17,6 @@ namespace biu
|
|||||||
else return value;
|
else return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
// TODO: switch to v2
|
|
||||||
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 = [&]
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
# 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)
|
|
||||||
template<> std::vector<std::byte> read<std::byte>(const std::filesystem::path& path)
|
template<> std::vector<std::byte> read<std::byte>(const std::filesystem::path& path)
|
||||||
{
|
{
|
||||||
auto length = std::filesystem::file_size(path);
|
auto length = std::filesystem::file_size(path);
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
int main()
|
int main()
|
||||||
{
|
{
|
||||||
using namespace biu::literals;
|
using namespace biu::literals;
|
||||||
auto result = biu::exec<{.SearchPath = true}>({.Program = "sleep", .Args = {"10"}, .Timeout = 3s});
|
biu::Logger::Guard log;
|
||||||
|
auto result = biu::exec<{.SearchPath = true, .Timeout = true}>({.Program = "sleep", .Args = {"10"}, .Timeout = 3s});
|
||||||
std::cout << "{}\n"_f(result.ExitCode);
|
std::cout << "{}\n"_f(result.ExitCode);
|
||||||
assert(!result);
|
assert(!result);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,9 +76,8 @@ inputs: rec
|
|||||||
# TODO: report glaze bug to upstream
|
# TODO: report glaze bug to upstream
|
||||||
inherit (inputs.pkgs.pkgs-2411) glaze;
|
inherit (inputs.pkgs.pkgs-2411) glaze;
|
||||||
stdenv = inputs.pkgs.clang18Stdenv;
|
stdenv = inputs.pkgs.clang18Stdenv;
|
||||||
boost = inputs.pkgs.boost187;
|
boost = (inputs.pkgs.boost188.override { extraB2Args = [ "boost.stacktrace.backtrace=on" ]; }).overrideAttrs
|
||||||
# boost = (inputs.pkgs.boost188.override { extraB2Args = [ "boost.stacktrace.backtrace=on" ]; }).overrideAttrs
|
(prev: { buildInputs = prev.buildInputs ++ [(inputs.pkgs.libbacktrace.override { enableStatic = true; })]; });
|
||||||
# (prev: { buildInputs = prev.buildInputs ++ [(inputs.pkgs.libbacktrace.override { enableStatic = true; })]; });
|
|
||||||
fmt = inputs.pkgs.fmt_11.overrideAttrs (prev: { patches = prev.patches or [] ++ [ ./biu/fmt.patch ]; });
|
fmt = inputs.pkgs.fmt_11.overrideAttrs (prev: { patches = prev.patches or [] ++ [ ./biu/fmt.patch ]; });
|
||||||
};
|
};
|
||||||
hpcstat = inputs.pkgs.callPackage ./hpcstat
|
hpcstat = inputs.pkgs.callPackage ./hpcstat
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace hpcstat::disk
|
|||||||
{ std::cerr << "HPCSTAT_DATADIR not set\n"; return false; }
|
{ std::cerr << "HPCSTAT_DATADIR not set\n"; return false; }
|
||||||
else if
|
else if
|
||||||
(
|
(
|
||||||
auto result = biu::exec<{.DirectStdout = true, .DirectStderr = true}>
|
auto result = biu::exec
|
||||||
({
|
({
|
||||||
// duc index -d ./duc.db -p ~
|
// duc index -d ./duc.db -p ~
|
||||||
"{}/duc"_f(*ducbindir),
|
"{}/duc"_f(*ducbindir),
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ namespace hpcstat::lfs
|
|||||||
{
|
{
|
||||||
if
|
if
|
||||||
(
|
(
|
||||||
auto result = biu::exec<{.SearchPath = true}>
|
auto result = biu::exec<{.SearchPath = true, .ModifyEnv = true}>
|
||||||
({
|
({
|
||||||
.Program="bjobs",
|
.Program="bjobs",
|
||||||
.Args={ "-a", "-o", "jobid submit_time stat cpu_used job_name", "-json" },
|
.Args={ "-a", "-o", "jobid submit_time stat cpu_used job_name", "-json" },
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ namespace hpcstat::ssh
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
else if
|
else if
|
||||||
(
|
(
|
||||||
auto output = biu::exec
|
auto output = biu::exec<{.Timeout = true}>
|
||||||
({.Program=std::filesystem::path(*sshbindir) / "ssh-add", .Args{ "-l" }, .Timeout=10s});
|
({.Program=std::filesystem::path(*sshbindir) / "ssh-add", .Args{ "-l" }, .Timeout=10s});
|
||||||
!output
|
!output
|
||||||
)
|
)
|
||||||
@@ -41,7 +41,7 @@ namespace hpcstat::ssh
|
|||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
else if
|
else if
|
||||||
(
|
(
|
||||||
auto output = biu::exec
|
auto output = biu::exec<.Stdin = biu::IoType::String, .Timeout = true>
|
||||||
({
|
({
|
||||||
.Program=std::filesystem::path(*sshbindir) / "ssh-keygen",
|
.Program=std::filesystem::path(*sshbindir) / "ssh-keygen",
|
||||||
.Args={
|
.Args={
|
||||||
@@ -69,7 +69,7 @@ namespace hpcstat::ssh
|
|||||||
bf::create_directories(tempdir);
|
bf::create_directories(tempdir);
|
||||||
auto signaturefile = tempdir / "signature";
|
auto signaturefile = tempdir / "signature";
|
||||||
std::ofstream(signaturefile) << signature;
|
std::ofstream(signaturefile) << signature;
|
||||||
auto result = biu::exec
|
auto result = biu::exec<.Stdin = biu::IoType::String, .Timeout = true>
|
||||||
({
|
({
|
||||||
.Program=std::filesystem::path(*sshbindir) / "ssh-keygen",
|
.Program=std::filesystem::path(*sshbindir) / "ssh-keygen",
|
||||||
.Args={
|
.Args={
|
||||||
|
|||||||
@@ -149,7 +149,7 @@ int main()
|
|||||||
catch (...) {}
|
catch (...) {}
|
||||||
// 提交任务
|
// 提交任务
|
||||||
boost::replace_all(State.SubmitCommand, "\n", " ");
|
boost::replace_all(State.SubmitCommand, "\n", " ");
|
||||||
biu::exec<{.DirectStdout = true, .DirectStderr = true, .SearchPath = true}>
|
biu::exec<{.SearchPath = true}>
|
||||||
({"sh", { "-c", State.SubmitCommand }});
|
({"sh", { "-c", State.SubmitCommand }});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,8 +55,7 @@ vm force-reboot <vm>
|
|||||||
if (!config.vm.contains(uid)) throw std::runtime_error("No VM found for current user");
|
if (!config.vm.contains(uid)) throw std::runtime_error("No VM found for current user");
|
||||||
if (!config.vm[uid].contains(args[1]))
|
if (!config.vm[uid].contains(args[1]))
|
||||||
throw std::runtime_error("VM {} is not owned by current user"_f(args[1]));
|
throw std::runtime_error("VM {} is not owned by current user"_f(args[1]));
|
||||||
biu::exec<{.DirectStdout = true, .DirectStderr = true, .SearchPath = false}>
|
biu::exec({config.virsh, { vm_to_virsh[args[0]], args[1] }});
|
||||||
({config.virsh, { vm_to_virsh[args[0]], args[1] }});
|
|
||||||
}
|
}
|
||||||
else throw std::runtime_error("unknown command: {}"_f(args[0]));
|
else throw std::runtime_error("unknown command: {}"_f(args[0]));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user