From b5a4aee76133f59c7b83ed5c788706d83b0b2640 Mon Sep 17 00:00:00 2001 From: chn Date: Sat, 8 Jun 2024 20:35:25 +0800 Subject: [PATCH] =?UTF-8?q?localPackages.sbatch-tui:=20=E5=9F=BA=E6=9C=AC?= =?UTF-8?q?=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flake.nix | 4 +- local/pkgs/default.nix | 2 +- local/pkgs/sbatch-cli/.envrc | 1 - local/pkgs/sbatch-cli/default.nix | 7 - local/pkgs/sbatch-cli/src/main.cpp | 0 local/pkgs/{sbatch-cli => sbatch-tui}/.clangd | 0 local/pkgs/sbatch-tui/.envrc | 1 + .../{sbatch-cli => sbatch-tui}/CMakeLists.txt | 17 +- local/pkgs/sbatch-tui/default.nix | 7 + .../sbatch-tui/include/sbatch-tui/device.hpp | 10 ++ local/pkgs/sbatch-tui/src/device.cpp | 8 + local/pkgs/sbatch-tui/src/device.cpp.template | 8 + local/pkgs/sbatch-tui/src/main.cpp | 157 ++++++++++++++++++ 13 files changed, 205 insertions(+), 17 deletions(-) delete mode 100644 local/pkgs/sbatch-cli/.envrc delete mode 100644 local/pkgs/sbatch-cli/default.nix delete mode 100644 local/pkgs/sbatch-cli/src/main.cpp rename local/pkgs/{sbatch-cli => sbatch-tui}/.clangd (100%) create mode 100644 local/pkgs/sbatch-tui/.envrc rename local/pkgs/{sbatch-cli => sbatch-tui}/CMakeLists.txt (51%) create mode 100644 local/pkgs/sbatch-tui/default.nix create mode 100644 local/pkgs/sbatch-tui/include/sbatch-tui/device.hpp create mode 100644 local/pkgs/sbatch-tui/src/device.cpp create mode 100644 local/pkgs/sbatch-tui/src/device.cpp.template create mode 100644 local/pkgs/sbatch-tui/src/main.cpp diff --git a/flake.nix b/flake.nix index 28145349..d6e07bc8 100644 --- a/flake.nix +++ b/flake.nix @@ -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"; }; diff --git a/local/pkgs/default.nix b/local/pkgs/default.nix index 1008cd90..7cee9046 100644 --- a/local/pkgs/default.nix +++ b/local/pkgs/default.nix @@ -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" {} diff --git a/local/pkgs/sbatch-cli/.envrc b/local/pkgs/sbatch-cli/.envrc deleted file mode 100644 index c17dec4f..00000000 --- a/local/pkgs/sbatch-cli/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake .#sbatch-cli diff --git a/local/pkgs/sbatch-cli/default.nix b/local/pkgs/sbatch-cli/default.nix deleted file mode 100644 index f92ec713..00000000 --- a/local/pkgs/sbatch-cli/default.nix +++ /dev/null @@ -1,7 +0,0 @@ -{ stdenv, cmake, pkg-config }: stdenv.mkDerivation -{ - name = "sbatch-cli"; - src = ./.; - buildInputs = []; - nativeBuildInputs = [ cmake pkg-config ]; -} diff --git a/local/pkgs/sbatch-cli/src/main.cpp b/local/pkgs/sbatch-cli/src/main.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/local/pkgs/sbatch-cli/.clangd b/local/pkgs/sbatch-tui/.clangd similarity index 100% rename from local/pkgs/sbatch-cli/.clangd rename to local/pkgs/sbatch-tui/.clangd diff --git a/local/pkgs/sbatch-tui/.envrc b/local/pkgs/sbatch-tui/.envrc new file mode 100644 index 00000000..6073828d --- /dev/null +++ b/local/pkgs/sbatch-tui/.envrc @@ -0,0 +1 @@ +use flake .#sbatch-tui diff --git a/local/pkgs/sbatch-cli/CMakeLists.txt b/local/pkgs/sbatch-tui/CMakeLists.txt similarity index 51% rename from local/pkgs/sbatch-cli/CMakeLists.txt rename to local/pkgs/sbatch-tui/CMakeLists.txt index d5971198..302bd55a 100644 --- a/local/pkgs/sbatch-cli/CMakeLists.txt +++ b/local/pkgs/sbatch-tui/CMakeLists.txt @@ -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}") diff --git a/local/pkgs/sbatch-tui/default.nix b/local/pkgs/sbatch-tui/default.nix new file mode 100644 index 00000000..7829bbc1 --- /dev/null +++ b/local/pkgs/sbatch-tui/default.nix @@ -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 ]; +} diff --git a/local/pkgs/sbatch-tui/include/sbatch-tui/device.hpp b/local/pkgs/sbatch-tui/include/sbatch-tui/device.hpp new file mode 100644 index 00000000..595b365c --- /dev/null +++ b/local/pkgs/sbatch-tui/include/sbatch-tui/device.hpp @@ -0,0 +1,10 @@ +# pragma once +# include +# include + +struct Device_t +{ + unsigned CpuMpiThreads, CpuOpenMPThreads; + std::vector GpuIds; +}; +extern Device_t Device; diff --git a/local/pkgs/sbatch-tui/src/device.cpp b/local/pkgs/sbatch-tui/src/device.cpp new file mode 100644 index 00000000..5c62b051 --- /dev/null +++ b/local/pkgs/sbatch-tui/src/device.cpp @@ -0,0 +1,8 @@ +# include + +Device_t Device +{ + .CpuMpiThreads = 1, + .CpuOpenMPThreads = 1, + .GpuIds = { "4060" } +}; diff --git a/local/pkgs/sbatch-tui/src/device.cpp.template b/local/pkgs/sbatch-tui/src/device.cpp.template new file mode 100644 index 00000000..1c87b6ec --- /dev/null +++ b/local/pkgs/sbatch-tui/src/device.cpp.template @@ -0,0 +1,8 @@ +# include + +decltype(Device) Device +{ + .CpuMpiThreads = @CpuMpiThreads@, + .CpuOpenMPThreads = @CpuOpenMPThreads@, + .GpuIds = { @GpuIds@ } +}; diff --git a/local/pkgs/sbatch-tui/src/main.cpp b/local/pkgs/sbatch-tui/src/main.cpp new file mode 100644 index 00000000..081b0398 --- /dev/null +++ b/local/pkgs/sbatch-tui/src/main.cpp @@ -0,0 +1,157 @@ +# include +# include +# include +# include +# include +# include +# include +# include +# include +# include + +using namespace fmt::literals; +using namespace std::literals; + +int main() +{ + // 需要绑定到界面上的变量 + struct + { + int vasp_version_selected = 0; + std::vector vasp_version_entries = { "std", "gam", "ncl" }; + int device_selected = 0; + std::vector device_entries = [] + { + std::vector 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; + } +}