localPackages.sbatch-tui: 基本完成

This commit is contained in:
陈浩南 2024-06-08 20:35:25 +08:00
parent 64532717da
commit b5a4aee761
13 changed files with 205 additions and 17 deletions

View File

@ -197,9 +197,9 @@
packages = [ pkgs.clang-tools_17 ];
CMAKE_EXPORT_COMPILE_COMMANDS = "1";
};
sbatch-cli = pkgs.mkShell
sbatch-tui = pkgs.mkShell
{
packages = with pkgs; [ sbatch-cli ];
inputsFrom = with pkgs.localPackages; [ sbatch-tui ];
buildInputs = [ pkgs.clang-tools_17 ];
CMAKE_EXPORT_COMPILE_COMMANDS = "1";
};

View File

@ -78,7 +78,7 @@ inputs: rec
sqlite-orm = inputs.pkgs.callPackage ./sqlite-orm { src = inputs.topInputs.sqlite-orm; };
mkPnpmPackage = inputs.pkgs.callPackage ./mkPnpmPackage.nix {};
nodejs-with-pnpm9 = inputs.pkgs.callPackage ./nodejs-with-pnpm9.nix {};
sbatch-cli = inputs.pkgs.callPackage ./sbatch-cli {};
sbatch-tui = inputs.pkgs.callPackage ./sbatch-tui {};
fromYaml = content: builtins.fromJSON (builtins.readFile
(inputs.pkgs.runCommand "toJSON" {}

View File

@ -1 +0,0 @@
use flake .#sbatch-cli

View File

@ -1,7 +0,0 @@
{ stdenv, cmake, pkg-config }: stdenv.mkDerivation
{
name = "sbatch-cli";
src = ./.;
buildInputs = [];
nativeBuildInputs = [ cmake pkg-config ];
}

View File

@ -0,0 +1 @@
use flake .#sbatch-tui

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.14)
project(sbatch-cli VERSION 0.0.0 LANGUAGES CXX)
project(sbatch-tui VERSION 0.0.0 LANGUAGES CXX)
enable_testing()
include(GNUInstallDirs)
@ -9,13 +9,18 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo")
endif()
set(HPCSTAT_VERSION "unknown" CACHE STRING "Version of the hpcstat")
find_package(fmt REQUIRED)
find_package(ftxui REQUIRED)
find_package(Boost REQUIRED COMPONENTS filesystem)
find_package(range-v3 REQUIRED)
add_executable(sbatch-cli src/main.cpp)
target_compile_features(sbatch-cli PUBLIC cxx_std_23)
# target_link_libraries(sbatch-cli PRIVATE)
add_executable(sbatch-tui src/main.cpp src/device.cpp)
target_compile_features(sbatch-tui PUBLIC cxx_std_23)
target_include_directories(sbatch-tui PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_link_libraries(sbatch-tui PRIVATE fmt::fmt ftxui::screen ftxui::dom ftxui::component Boost::filesystem
range-v3::range-v3)
install(TARGETS sbatch-cli RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
install(TARGETS sbatch-tui RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
get_property(ImportedTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
message("Imported targets: ${ImportedTargets}")

View File

@ -0,0 +1,7 @@
{ stdenv, cmake, pkg-config, fmt, ftxui, boost, range-v3 }: stdenv.mkDerivation
{
name = "sbatch-tui";
src = ./.;
buildInputs = [ fmt ftxui boost range-v3 ];
nativeBuildInputs = [ cmake pkg-config ];
}

View File

@ -0,0 +1,10 @@
# pragma once
# include <string>
# include <vector>
struct Device_t
{
unsigned CpuMpiThreads, CpuOpenMPThreads;
std::vector<std::string> GpuIds;
};
extern Device_t Device;

View File

@ -0,0 +1,8 @@
# include <sbatch-tui/device.hpp>
Device_t Device
{
.CpuMpiThreads = 1,
.CpuOpenMPThreads = 1,
.GpuIds = { "4060" }
};

View File

@ -0,0 +1,8 @@
# include <sbatch-tui/device.hpp>
decltype(Device) Device
{
.CpuMpiThreads = @CpuMpiThreads@,
.CpuOpenMPThreads = @CpuOpenMPThreads@,
.GpuIds = { @GpuIds@ }
};

View File

@ -0,0 +1,157 @@
# include <map>
# include <filesystem>
# include <ftxui/component/component.hpp>
# include <ftxui/component/component_options.hpp>
# include <ftxui/component/screen_interactive.hpp>
# include <boost/process.hpp>
# include <boost/algorithm/string.hpp>
# include <fmt/format.h>
# include <range/v3/view.hpp>
# include <sbatch-tui/device.hpp>
using namespace fmt::literals;
using namespace std::literals;
int main()
{
// 需要绑定到界面上的变量
struct
{
int vasp_version_selected = 0;
std::vector<std::string> vasp_version_entries = { "std", "gam", "ncl" };
int device_selected = 0;
std::vector<std::string> device_entries = []
{
std::vector<std::string> devices(Device.GpuIds.size() + 2);
for (std::size_t i = 0; i < Device.GpuIds.size(); ++i)
devices[i + 1] = Device.GpuIds[i];
devices[0] = "any single GPU";
devices.back() = "CPU";
return devices;
}();
std::string user_command;
std::string submit_command;
} state;
// 为组件增加标题栏
auto component_with_title = [](std::string title, ftxui::Component component)
{
return ftxui::Renderer(component, [title, component]
{
return ftxui::vbox
({
ftxui::text(title) | ftxui::bgcolor(ftxui::Color::Blue),
component->Render(),
ftxui::separator()
});
});
};
// 构建界面, 需要至少 25 行 47 列
auto screen = ftxui::ScreenInteractive::Fullscreen();
auto request_interface = [&state, &screen, &component_with_title]
{
auto vasp_version = component_with_title
(
"Select VASP version:",
ftxui::Menu(&state.vasp_version_entries, &state.vasp_version_selected)
)
| ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 30);
auto device = component_with_title
(
"Select device:",
ftxui::Menu(&state.device_entries, &state.device_selected)
)
| ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 30);
auto continue_button = ftxui::Button("Continue",
[&]{ state.user_command = "continue"; screen.ExitLoopClosure()(); });
auto quit_button = ftxui::Button("Quit",
[&]{ state.user_command = "quit"; screen.ExitLoopClosure()(); });
return ftxui::Container::Vertical
({
vasp_version, device,
ftxui::Container::Horizontal({continue_button, quit_button})
}) | ftxui::borderHeavy
| ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 30)
| ftxui::size(ftxui::HEIGHT, ftxui::EQUAL, 18);
}();
auto confirm_interface = [&state, &screen, &component_with_title]
{
ftxui::InputOption input_option;
input_option.multiline = true;
return ftxui::Container::Vertical
({
component_with_title
(
"Double check & modify submit command:",
ftxui::Input(&state.submit_command, "", input_option)
)
| ftxui::size(ftxui::HEIGHT, ftxui::EQUAL, 7),
ftxui::Container::Horizontal
({
ftxui::Button("Submit",
[&]{state.user_command = "submit"; screen.ExitLoopClosure()();}),
ftxui::Button("Quit",
[&]{state.user_command = "quit"; screen.ExitLoopClosure()();}),
ftxui::Button("Back",
[&]{state.user_command = "back"; screen.ExitLoopClosure()();})
})
}) | ftxui::borderHeavy
| ftxui::size(ftxui::WIDTH, ftxui::EQUAL, 30)
| ftxui::size(ftxui::HEIGHT, ftxui::EQUAL, 18);
}();
// 实际投递任务
auto submit = [](std::string submit_command)
{
// replace \n with space
boost::replace_all(submit_command, "\n", " ");
auto process = boost::process::child
(
boost::process::search_path("sh"), "-c", submit_command,
boost::process::std_in.close(),
boost::process::std_out > stdout,
boost::process::std_err > stderr
);
process.wait();
};
// 进入事件循环
while (true)
{
screen.Loop(request_interface);
if (state.user_command == "quit")
return EXIT_FAILURE;
else if (state.user_command != "continue")
throw std::runtime_error("user_command is not recognized");
else if (state.device_selected < 0 || state.device_selected >= state.device_entries.size())
throw std::runtime_error("device_selected is out of range");
else if (state.device_selected == 0) state.submit_command = fmt::format
(
"sbatch --ntasks=1\n--gpus=1\n--job-name='{}'\n--output=output.txt\nvasp-nvidia-{}",
std::filesystem::current_path().filename().string(), state.vasp_version_entries[state.vasp_version_selected]
);
else if (state.device_selected == state.device_entries.size() - 1) state.submit_command = fmt::format
(
"sbatch --ntasks={}\n--cpus-per-task={}\n--hint=nomultithread\n--job-name='{}'\n--output=output.txt"
"\nvasp-intel-{}",
Device.CpuMpiThreads, Device.CpuOpenMPThreads, std::filesystem::current_path().filename().string(),
state.vasp_version_entries[state.vasp_version_selected]
);
else state.submit_command = fmt::format
(
"sbatch --ntasks=1\n--gpus={}:1\n--job-name='{}'\n--output=output.txt\nvasp-nvidia-{}",
state.device_entries[state.device_selected], std::filesystem::current_path().filename().string(),
state.vasp_version_entries[state.vasp_version_selected]
);
screen.Loop(confirm_interface);
if (state.user_command == "quit")
return EXIT_FAILURE;
else if (state.user_command == "back")
continue;
else if (state.user_command != "submit")
throw std::runtime_error("user_command is not recognized");
submit(state.submit_command);
break;
}
}