Introduce config

The config can be used to automatically install
the CachyOS on the target
This commit is contained in:
Vladislav Nepogodin 2022-06-11 04:18:31 +04:00
parent 7fa573d83a
commit 5357735671
No known key found for this signature in database
GPG Key ID: B62C3D10C54D5DA9
15 changed files with 577 additions and 209 deletions

1
.gitignore vendored
View File

@ -13,6 +13,7 @@ subprojects/ftxui
subprojects/nlohmann_json-*
subprojects/spdlog
subprojects/simdjson
subprojects/rapidjson
subprojects/range-v3
subprojects/packagecache

View File

@ -48,9 +48,9 @@ CPMAddPackage(
EXCLUDE_FROM_ALL YES
)
CPMAddPackage(
NAME simdjson
GITHUB_REPOSITORY simdjson/simdjson
GIT_TAG fbe955e9a449c6bbeae69aacfa995795f2100791
NAME rapidjson
GITHUB_REPOSITORY Tencent/rapidjson
GIT_TAG 232389d4f1012dddec4ef84861face2d2ba85709
EXCLUDE_FROM_ALL YES
)
CPMAddPackage(
@ -94,6 +94,7 @@ add_executable(${PROJECT_NAME}
src/follow_process_log.hpp src/follow_process_log.cpp
src/crypto.cpp src/crypto.hpp
src/misc.cpp src/misc.hpp
src/simple_tui.cpp src/simple_tui.hpp
src/tui.cpp src/tui.hpp
src/main.cpp
)
@ -105,6 +106,7 @@ add_executable(test-exec-interactive
src/widgets.cpp src/widgets.hpp
src/follow_process_log.hpp src/follow_process_log.cpp
src/crypto.cpp src/crypto.hpp
src/simple_tui.cpp src/simple_tui.hpp
src/tui.cpp src/tui.hpp
src/main_test.cpp
)
@ -116,6 +118,7 @@ add_executable(test-process-tailing
src/widgets.cpp src/widgets.hpp
src/follow_process_log.hpp src/follow_process_log.cpp
src/crypto.cpp src/crypto.hpp
src/simple_tui.cpp src/simple_tui.hpp
src/tui.cpp src/tui.hpp
src/test_proccess_tailing.cpp
)
@ -132,9 +135,9 @@ enable_sanitizers(project_options)
include_directories(${CMAKE_SOURCE_DIR}/src)
target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::screen ftxui::dom ftxui::component cpr::cpr range-v3::range-v3)#simdjson::simdjson)
target_link_libraries(test-exec-interactive PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component cpr::cpr range-v3::range-v3)#simdjson::simdjson)
target_link_libraries(test-process-tailing PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component cpr::cpr)#simdjson::simdjson)
target_link_libraries(${PROJECT_NAME} PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::screen ftxui::dom ftxui::component cpr::cpr range-v3::range-v3)
target_link_libraries(test-exec-interactive PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component cpr::cpr range-v3::range-v3)
target_link_libraries(test-process-tailing PRIVATE project_warnings project_options spdlog::spdlog fmt::fmt ftxui::component cpr::cpr)
option(ENABLE_UNITY "Enable Unity builds of projects" OFF)
if(ENABLE_UNITY)

View File

@ -36,7 +36,7 @@ endif
spdlog = dependency('spdlog', version : ['>=1.9.2'], fallback : ['spdlog', 'spdlog_dep'])
fmt = dependency('fmt', version : ['>=8.0.0'], fallback : ['fmt', 'fmt_dep'])
ftxui = dependency('ftxui', modules : ['ftxui::screen', 'ftxui::dom', 'ftxui::component'], fallback : ['ftxui', 'ftxui_dep'])
simdjson = dependency('simdjson', version : ['>=1.0.2'], fallback : ['simdjson', 'simdjson_dep'])
rapidjson = dependency('rapidjson', version : ['>=1.1.0'], fallback : ['rapidjson', 'rapidjson_dep'])
cpr = dependency('cpr', version : ['>=1.7.0'], fallback : ['cpr', 'cpr_dep'])
ranges = dependency('range-v3', version : ['>=0.11.0'], fallback : ['range-v3', 'range_dep'])
#glibmm = dependency('glibmm-2.4', version : ['>=2.56.0'])
@ -53,6 +53,7 @@ src_files = files(
'src/follow_process_log.cpp', 'src/follow_process_log.hpp',
'src/crypto.cpp', 'src/crypto.hpp',
'src/misc.cpp', 'src/misc.hpp',
'src/simple_tui.cpp', 'src/simple_tui.hpp',
'src/tui.cpp', 'src/tui.hpp',
'src/main.cpp',
)
@ -122,7 +123,7 @@ endif
add_project_arguments(cc.get_supported_arguments(possible_cc_flags), language : 'cpp')
deps = [fmt, spdlog, ftxui, cpr, ranges]
deps = [fmt, spdlog, ftxui, cpr, ranges, rapidjson]
executable(
'cachyos-installer',

13
settings.json Normal file
View File

@ -0,0 +1,13 @@
{
"menus": -1,
"fs_name": "btrfs",
"device": "/dev/nvme0n1",
"locale": "en_US",
"user_name": "testuser",
"user_pass": "test",
"user_shell": "/bin/bash",
"root_pass": "secure",
"hostname": "cachyos",
"xkbmap": "us",
"timezone": "America/New_York"
}

View File

@ -16,6 +16,7 @@ bool Config::initialize() noexcept {
s_config = std::make_unique<Config>();
if (s_config) {
s_config->m_data["hostcache"] = static_cast<std::int32_t>(!fs::exists("/run/miso/bootmnt"));
s_config->m_data["menus"] = -1;
s_config->m_data["H_INIT"] = "openrc";
s_config->m_data["SYSTEM"] = "BIOS";

View File

@ -1,4 +1,5 @@
#include "disk.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "widgets.hpp"
@ -148,7 +149,7 @@ std::string zfs_list_devs() noexcept {
for (const auto& device : devices) {
// add the device
list_of_devices += fmt::format("{}\n", device);
// now lets add any other forms of those devices
// now let's add any other forms of those devices
list_of_devices += utils::exec(fmt::format("find -L /dev/ -xtype l -samefile {} 2>/dev/null", device));
}
return list_of_devices;
@ -164,4 +165,49 @@ std::string zfs_list_datasets(const std::string_view& type) noexcept {
return utils::exec("zfs list -H -o name 2>/dev/null | grep \"/\"");
}
// Other filesystems
void select_filesystem(const std::string_view& file_sys) noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
config_data["FILESYSTEM_NAME"] = std::string{file_sys.data()};
if (file_sys == "btrfs") {
config_data["FILESYSTEM"] = "mkfs.btrfs -f";
config_data["fs_opts"] = std::vector<std::string>{"autodefrag", "compress=zlib", "compress=lzo", "compress=zstd", "compress=no", "compress-force=zlib", "compress-force=lzo", "compress-force=zstd", "discard", "noacl", "noatime", "nodatasum", "nospace_cache", "recovery", "skip_balance", "space_cache", "nossd", "ssd", "ssd_spread", "commit=120"};
#ifdef NDEVENV
utils::exec("modprobe btrfs");
#endif
} else if (file_sys == "ext2") {
config_data["FILESYSTEM"] = "mkfs.ext2 -q";
} else if (file_sys == "ext3") {
config_data["FILESYSTEM"] = "mkfs.ext3 -q";
} else if (file_sys == "ext4") {
config_data["FILESYSTEM"] = "mkfs.ext4 -q";
config_data["fs_opts"] = std::vector<std::string>{"data=journal", "data=writeback", "dealloc", "discard", "noacl", "noatime", "nobarrier", "nodelalloc"};
} else if (file_sys == "f2fs") {
config_data["FILESYSTEM"] = "mkfs.f2fs -q";
config_data["fs_opts"] = std::vector<std::string>{"data_flush", "disable_roll_forward", "disable_ext_identify", "discard", "fastboot", "flush_merge", "inline_xattr", "inline_data", "inline_dentry", "no_heap", "noacl", "nobarrier", "noextent_cache", "noinline_data", "norecovery"};
#ifdef NDEVENV
utils::exec("modprobe f2fs");
#endif
} else if (file_sys == "jfs") {
config_data["FILESYSTEM"] = "mkfs.jfs -q";
config_data["fs_opts"] = std::vector<std::string>{"discard", "errors=continue", "errors=panic", "nointegrity"};
} else if (file_sys == "nilfs2") {
config_data["FILESYSTEM"] = "mkfs.nilfs2 -fq";
config_data["fs_opts"] = std::vector<std::string>{"discard", "nobarrier", "errors=continue", "errors=panic", "order=relaxed", "order=strict", "norecovery"};
} else if (file_sys == "ntfs") {
config_data["FILESYSTEM"] = "mkfs.ntfs -q";
} else if (file_sys == "reiserfs") {
config_data["FILESYSTEM"] = "mkfs.reiserfs -q";
config_data["fs_opts"] = std::vector<std::string>{"acl", "nolog", "notail", "replayonly", "user_xattr"};
} else if (file_sys == "vfat") {
config_data["FILESYSTEM"] = "mkfs.vfat -F32";
} else if (file_sys == "xfs") {
config_data["FILESYSTEM"] = "mkfs.xfs -f";
config_data["fs_opts"] = std::vector<std::string>{"discard", "filestreams", "ikeep", "largeio", "noalign", "nobarrier", "norecovery", "noquota", "wsync"};
}
}
} // namespace utils

View File

@ -22,6 +22,9 @@ void zfs_create_dataset(const std::string_view& zpath, const std::string_view& z
std::string zfs_list_devs() noexcept;
std::string zfs_list_datasets(const std::string_view& type = "none") noexcept;
// Other filesystems
void select_filesystem(const std::string_view& fs) noexcept;
} // namespace utils
#endif // DISK_HPP

173
src/simple_tui.cpp Normal file
View File

@ -0,0 +1,173 @@
#include "simple_tui.hpp"
#include "config.hpp"
#include "disk.hpp"
#include "tui.hpp"
#include "utils.hpp"
#include "widgets.hpp"
/* clang-format off */
#include <cstdlib> // for setenv
#include <sys/mount.h> // for mount
#include <fstream> // for ofstream
#include <algorithm> // for copy
#include <thread> // for thread
#include <filesystem> // for exists, is_directory
#include <string> // for basic_string
#include <ftxui/component/component.hpp> // for Renderer, Button
#include <ftxui/component/screen_interactive.hpp> // for Component, ScreenI...
#include <ftxui/dom/elements.hpp> // for operator|, size
/* clang-format on */
#include <fmt/ranges.h>
using namespace ftxui;
namespace {
// Set static list of filesystems rather than on-the-fly. Partially as most require additional flags, and
// partially because some don't seem to be viable.
// Set static list of filesystems rather than on-the-fly.
void select_filesystem() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
// prep variables
config_data["fs_opts"] = std::vector<std::string>{};
const std::vector<std::string> menu_entries = {
"btrfs",
"ext3",
"ext4",
"f2fs",
"jfs",
"nilfs2",
"ntfs",
"reiserfs",
"vfat",
"xfs",
};
auto screen = ScreenInteractive::Fullscreen();
std::int32_t selected{};
bool success{};
auto ok_callback = [&] {
const auto& src = menu_entries[static_cast<std::size_t>(selected)];
const auto& lines = utils::make_multiline(src, false, " ");
const auto& file_sys = lines[0];
utils::select_filesystem(file_sys.c_str());
success = true;
screen.ExitLoopClosure()();
};
static constexpr auto filesystem_body = "\nSelect your filesystem\n";
const auto& content_size = size(HEIGHT, LESS_THAN, 10) | size(WIDTH, GREATER_THAN, 40);
tui::detail::menu_widget(menu_entries, ok_callback, &selected, &screen, filesystem_body, {size(HEIGHT, LESS_THAN, 18), content_size});
if (!success) {
utils::select_filesystem("btrfs");
}
}
} // namespace
namespace tui {
void menu_simple() noexcept {
// Prepare
utils::umount_partitions();
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
const auto& device_info = std::get<std::string>(config_data["DEVICE"]);
const auto& fs_name = std::get<std::string>(config_data["FILESYSTEM_NAME"]);
const auto& mount_opts_info = std::get<std::string>(config_data["MOUNT_OPTS"]);
const auto& hostname = std::get<std::string>(config_data["HOSTNAME"]);
const auto& locale = std::get<std::string>(config_data["LOCALE"]);
const auto& xkbmap = std::get<std::string>(config_data["XKBMAP"]);
const auto& timezone = std::get<std::string>(config_data["TIMEZONE"]);
const auto& user_name = std::get<std::string>(config_data["USER_NAME"]);
const auto& user_pass = std::get<std::string>(config_data["USER_PASS"]);
const auto& user_shell = std::get<std::string>(config_data["USER_SHELL"]);
const auto& root_pass = std::get<std::string>(config_data["ROOT_PASS"]);
if (device_info.empty()) {
tui::select_device();
}
tui::auto_partition(false);
// Target FS
if (fs_name.empty()) {
select_filesystem();
} else {
utils::select_filesystem(fs_name);
}
tui::mount_current_partition(true);
/* clang-format on */
utils::generate_fstab("genfstab -U");
if (hostname.empty()) {
tui::set_hostname();
} else {
utils::set_hostname(hostname);
}
if (locale.empty()) {
tui::set_locale();
} else {
utils::set_locale(locale);
}
if (xkbmap.empty()) {
tui::set_xkbmap();
} else {
utils::set_xkbmap(xkbmap);
}
if (timezone.empty()) {
tui::set_timezone();
} else {
utils::set_timezone(timezone);
}
utils::set_hw_clock("utc");
if (root_pass.empty()) {
tui::set_root_password();
} else {
utils::set_root_password(root_pass);
}
if (user_name.empty()) {
tui::create_new_user();
} else {
utils::create_new_user(user_name, user_pass, user_shell);
}
fmt::print("┌{0:─^{5}}┐\n"
"│{1: ^{5}}│\n"
"│{2: ^{5}}│\n"
"│{3: ^{5}}│\n"
"│{4: ^{5}}│\n"
"└{0:─^{5}}┘\n",
"",
fmt::format("Mountpoint: {}", mountpoint),
fmt::format("Your device: {}", device_info),
fmt::format("Filesystem: {}", fs_name),
fmt::format("Filesystem opts: {}", mount_opts_info), 80);
// tui::mount_partitions();
//
// // Install process
// if (!utils::check_mount()) {
// spdlog::error("Your partitions are not mounted");
// }
// tui::install_base();
// tui::install_desktop();
// if (!utils::check_base()) {
// spdlog::error("Base is not installed");
// }
// tui::install_bootloader();
// tui::config_base_menu();
}
} // namespace tui

8
src/simple_tui.hpp Normal file
View File

@ -0,0 +1,8 @@
#ifndef SIMPLE_TUI_HPP
#define SIMPLE_TUI_HPP
namespace tui {
void menu_simple() noexcept;
} // namespace tui
#endif // SIMPLE_TUI_HPP

View File

@ -5,6 +5,7 @@
#include "disk.hpp"
#include "drivers.hpp"
#include "misc.hpp"
#include "simple_tui.hpp"
#include "utils.hpp"
#include "widgets.hpp"
@ -127,6 +128,7 @@ void generate_fstab() noexcept {
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
auto screen = ScreenInteractive::Fullscreen();
std::string fstab_cmd{};
std::int32_t selected{};
auto ok_callback = [&] {
if (system_info == "BIOS" && selected == 3) {
@ -134,36 +136,17 @@ void generate_fstab() noexcept {
detail::msgbox_widget(FstabErr);
return;
}
#ifdef NDEVENV
const auto& src = menu_entries[static_cast<std::size_t>(selected)];
utils::exec(fmt::format(FMT_COMPILE("{0} {1} > {1}/etc/fstab"), src, mountpoint));
spdlog::info("Created fstab file:\n");
utils::exec(fmt::format(FMT_COMPILE("cat {}/etc/fstab >> /tmp/cachyos-install.log"), mountpoint));
#endif
const auto& swap_file = fmt::format(FMT_COMPILE("{}/swapfile"), mountpoint);
if (fs::exists(swap_file) && fs::is_regular_file(swap_file)) {
spdlog::info("appending swapfile to the fstab..");
#ifdef NDEVENV
utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/\\\\{0}//\" {0}/etc/fstab"), mountpoint));
#endif
}
fstab_cmd = menu_entries[static_cast<std::size_t>(selected)];
screen.ExitLoopClosure()();
};
/* clang-format off */
if (fstab_cmd.empty()) { return; }
/* clang-format on */
utils::generate_fstab(fstab_cmd);
static constexpr auto fstab_body = "\nThe FSTAB file (File System TABle) sets what storage devices\nand partitions are to be mounted, and how they are to be used.\n\nUUID (Universally Unique IDentifier) is recommended.\n\nIf no labels were set for the partitions earlier,\ndevice names will be used for the label option.\n";
detail::menu_widget(menu_entries, ok_callback, &selected, &screen, fstab_body);
#ifdef NDEVENV
// Edit fstab in case of btrfs subvolumes
utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/subvolid=.*,subvol=\\/.*,//g\" {}/etc/fstab"), mountpoint));
// remove any zfs datasets that are mounted by zfs
const auto& msource_list = utils::make_multiline(utils::exec(fmt::format(FMT_COMPILE("cat {}/etc/fstab | grep \"^[a-z,A-Z]\" | {}"), mountpoint, "awk '{print $1}'")));
for (const auto& msource : msource_list) {
if (utils::exec(fmt::format(FMT_COMPILE("zfs list -H -o mountpoint,name | grep \"^/\" | {} | grep \"^{}$\""), "awk '{print $2}'", msource), true) == "0")
utils::exec(fmt::format(FMT_COMPILE("sed -e \"\\|^{}[[:space:]]| s/^#*/#/\" -i {}/etc/fstab"), msource, mountpoint));
}
#endif
}
// Set system hostname
@ -178,14 +161,7 @@ void set_hostname() noexcept {
if (hostname.empty()) { return; }
/* clang-format on */
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" > {}/etc/hostname"), hostname, mountpoint));
const auto& cmd = fmt::format(FMT_COMPILE("echo -e \"#<ip-address>\\t<hostname.domain.org>\\t<hostname>\\n127.0.0.1\\tlocalhost.localdomain\\tlocalhost\\t{0}\\n::1\\tlocalhost.localdomain\\tlocalhost\\t{0}\">{1}/etc/hosts"), hostname, mountpoint);
utils::exec(cmd);
#endif
utils::set_hostname(hostname);
}
// Set system language
@ -210,33 +186,7 @@ void set_locale() noexcept {
if (locale.empty()) { return; }
/* clang-format on */
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
const auto& locale_config_path = fmt::format(FMT_COMPILE("{}/etc/locale.conf"), mountpoint);
const auto& locale_gen_path = fmt::format(FMT_COMPILE("{}/etc/locale.gen"), mountpoint);
static constexpr auto locale_config_part = R"(LANG="{0}"
LC_NUMERIC="{0}"
LC_TIME="{0}"
LC_MONETARY="{0}"
LC_PAPER="{0}"
LC_NAME="{0}"
LC_ADDRESS="{0}"
LC_TELEPHONE="{0}"
LC_MEASUREMENT="{0}"
LC_IDENTIFICATION="{0}"
LC_MESSAGES="{0}")";
std::ofstream locale_config_file{locale_config_path};
locale_config_file << fmt::format(locale_config_part, locale);
utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/#{0}/{0}/\" {1}"), locale, locale_gen_path));
// Generate locales
utils::arch_chroot("locale-gen", false);
#endif
utils::set_locale(locale);
}
// Set keymap for X11
@ -263,13 +213,7 @@ void set_xkbmap() noexcept {
if (!success) { return; }
/* clang-format on */
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
utils::exec(fmt::format(FMT_COMPILE("echo -e \"Section \"\\\"InputClass\"\\\"\\nIdentifier \"\\\"system-keyboard\"\\\"\\nMatchIsKeyboard \"\\\"on\"\\\"\\nOption \"\\\"XkbLayout\"\\\" \"\\\"{0}\"\\\"\\nEndSection\" > {1}/etc/X11/xorg.conf.d/00-keyboard.conf"), xkbmap_choice, mountpoint));
#endif
utils::set_xkbmap(xkbmap_choice);
}
void select_keymap() noexcept {
@ -347,10 +291,8 @@ bool set_timezone() noexcept {
if (!do_set_timezone) { return false; }
/* clang-format on */
#ifdef NDEVENV
utils::arch_chroot(fmt::format(FMT_COMPILE("ln -sf /usr/share/zoneinfo/{}/{} /etc/localtime"), zone, subzone), false);
#endif
spdlog::info("Timezone is set to {}/{}", zone, subzone);
utils::set_timezone(fmt::format(FMT_COMPILE("{}/{}"), zone, subzone));
return true;
}
@ -360,10 +302,8 @@ void set_hw_clock() noexcept {
const std::vector<std::string> menu_entries{"utc", "localtime"};
std::int32_t selected{};
auto ok_callback = [&] {
#ifdef NDEVENV
const auto& clock_type = menu_entries[static_cast<std::size_t>(selected)];
utils::arch_chroot(fmt::format(FMT_COMPILE("hwclock --systohc --{}"), clock_type), false);
#endif
utils::set_hw_clock(clock_type);
screen.ExitLoopClosure()();
};
@ -390,17 +330,7 @@ void set_root_password() noexcept {
detail::msgbox_widget(PassErrBody);
tui::set_root_password();
}
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
std::error_code err{};
utils::exec(fmt::format(FMT_COMPILE("echo -e \"{}\n{}\" > /tmp/.passwd"), pass, confirm));
utils::exec(fmt::format(FMT_COMPILE("arch-chroot {} passwd root < /tmp/.passwd &>/dev/null"), mountpoint));
fs::remove("/tmp/.passwd", err);
#endif
utils::set_root_password(pass);
}
// Create user on the system
@ -420,12 +350,6 @@ void create_new_user() noexcept {
}
}
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
#endif
std::string_view shell{};
{
static constexpr auto DefShell = "\nChoose the default shell.\n";
@ -455,19 +379,8 @@ void create_new_user() noexcept {
screen.ExitLoopClosure()();
};
detail::radiolist_widget(radiobox_list, ok_callback, &selected, &screen, {.text = shells_options_body}, {.text_size = nothing});
#ifdef NDEVENV
if (selected != 1) {
const auto& packages = fmt::format(FMT_COMPILE("cachyos-{}-config"), (selected == 0) ? "zsh" : "fish");
const auto& hostcache = std::get<std::int32_t>(config_data["hostcache"]);
const auto& cmd = (hostcache) ? "pacstrap" : "pacstrap -c";
detail::follow_process_log_widget({"/bin/sh", "-c", fmt::format(FMT_COMPILE("{} {} {} |& tee /tmp/pacstrap.log"), cmd, mountpoint, packages)});
}
#endif
}
spdlog::info("default shell: [{}]", shell);
// Enter password. This step will only be reached where the loop has been skipped or broken.
std::string pass{};
static constexpr auto user_pass_body = "Enter password for";
@ -497,32 +410,7 @@ void create_new_user() noexcept {
detail::infobox_widget("\nCreating User and setting groups...\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
#ifdef NDEVENV
// Create the user, set password, then remove temporary password file
utils::arch_chroot("groupadd sudo", false);
utils::arch_chroot(fmt::format(FMT_COMPILE("groupadd {}"), user), false);
utils::arch_chroot(fmt::format(FMT_COMPILE("useradd {0} -m -g {0} -G sudo,storage,power,network,video,audio,lp,sys,input -s {1}"), user, shell), false);
spdlog::info("add user to groups");
// check if user has been created
const auto& user_check = utils::exec(fmt::format(FMT_COMPILE("arch-chroot {} getent passwd {}"), mountpoint, user));
if (user_check.empty()) {
spdlog::error("User has not been created!");
}
std::error_code err{};
utils::exec(fmt::format(FMT_COMPILE("echo -e \"{}\\n{}\" > /tmp/.passwd"), pass, confirm));
const auto& ret_status = utils::exec(fmt::format(FMT_COMPILE("arch-chroot {} passwd {} < /tmp/.passwd &>/dev/null"), mountpoint, user), true);
spdlog::info("create user pwd: {}", ret_status);
fs::remove("/tmp/.passwd", err);
// Set up basic configuration files and permissions for user
// arch_chroot "cp /etc/skel/.bashrc /home/${USER}"
utils::arch_chroot(fmt::format(FMT_COMPILE("chown -R {0}:{0} /home/{0}"), user), false);
const auto& sudoers_file = fmt::format(FMT_COMPILE("{}/etc/sudoers"), mountpoint);
if (fs::exists(sudoers_file)) {
utils::exec(fmt::format(FMT_COMPILE("sed -i '/NOPASSWD/!s/# %sudo/%sudo/g' {}"), sudoers_file));
}
#endif
utils::create_new_user(user, pass, shell);
}
// Install pkgs from user input
@ -1441,7 +1329,7 @@ void install_bootloader() noexcept {
}
// BIOS and UEFI
void auto_partition() noexcept {
void auto_partition(bool interactive) noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
@ -1488,6 +1376,10 @@ void auto_partition() noexcept {
utils::exec(fmt::format(FMT_COMPILE("parted -s {} mkpart primary ext3 513MiB 100% 2>>/tmp/cachyos-install.log &>/dev/null"), device_info));
#endif
/* clang-format off */
if (!interactive) { return; }
/* clang-format on */
// Show created partitions
const auto& disk_list = utils::exec(fmt::format(FMT_COMPILE("lsblk {} -o NAME,TYPE,FSTYPE,SIZE"), device_info));
detail::msgbox_widget(disk_list, size(HEIGHT, GREATER_THAN, 5));
@ -1533,7 +1425,6 @@ bool select_device() noexcept {
// Set static list of filesystems rather than on-the-fly. Partially as most require additional flags, and
// partially because some don't seem to be viable.
// Set static list of filesystems rather than on-the-fly.
bool select_filesystem() noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
@ -1562,42 +1453,7 @@ bool select_filesystem() noexcept {
const auto& src = menu_entries[static_cast<std::size_t>(selected)];
const auto& lines = utils::make_multiline(src, false, " ");
const auto& file_sys = lines[0];
if (file_sys == "btrfs") {
config_data["FILESYSTEM"] = "mkfs.btrfs -f";
config_data["fs_opts"] = std::vector<std::string>{"autodefrag", "compress=zlib", "compress=lzo", "compress=zstd", "compress=no", "compress-force=zlib", "compress-force=lzo", "compress-force=zstd", "discard", "noacl", "noatime", "nodatasum", "nospace_cache", "recovery", "skip_balance", "space_cache", "nossd", "ssd", "ssd_spread", "commit=120"};
#ifdef NDEVENV
utils::exec("modprobe btrfs");
#endif
} else if (file_sys == "ext2") {
config_data["FILESYSTEM"] = "mkfs.ext2 -q";
} else if (file_sys == "ext3") {
config_data["FILESYSTEM"] = "mkfs.ext3 -q";
} else if (file_sys == "ext4") {
config_data["FILESYSTEM"] = "mkfs.ext4 -q";
config_data["fs_opts"] = std::vector<std::string>{"data=journal", "data=writeback", "dealloc", "discard", "noacl", "noatime", "nobarrier", "nodelalloc"};
} else if (file_sys == "f2fs") {
config_data["FILESYSTEM"] = "mkfs.f2fs -q";
config_data["fs_opts"] = std::vector<std::string>{"data_flush", "disable_roll_forward", "disable_ext_identify", "discard", "fastboot", "flush_merge", "inline_xattr", "inline_data", "inline_dentry", "no_heap", "noacl", "nobarrier", "noextent_cache", "noinline_data", "norecovery"};
#ifdef NDEVENV
utils::exec("modprobe f2fs");
#endif
} else if (file_sys == "jfs") {
config_data["FILESYSTEM"] = "mkfs.jfs -q";
config_data["fs_opts"] = std::vector<std::string>{"discard", "errors=continue", "errors=panic", "nointegrity"};
} else if (file_sys == "nilfs2") {
config_data["FILESYSTEM"] = "mkfs.nilfs2 -fq";
config_data["fs_opts"] = std::vector<std::string>{"discard", "nobarrier", "errors=continue", "errors=panic", "order=relaxed", "order=strict", "norecovery"};
} else if (file_sys == "ntfs") {
config_data["FILESYSTEM"] = "mkfs.ntfs -q";
} else if (file_sys == "reiserfs") {
config_data["FILESYSTEM"] = "mkfs.reiserfs -q";
config_data["fs_opts"] = std::vector<std::string>{"acl", "nolog", "notail", "replayonly", "user_xattr"};
} else if (file_sys == "vfat") {
config_data["FILESYSTEM"] = "mkfs.vfat -F32";
} else if (file_sys == "xfs") {
config_data["FILESYSTEM"] = "mkfs.xfs -f";
config_data["fs_opts"] = std::vector<std::string>{"discard", "filestreams", "ikeep", "largeio", "noalign", "nobarrier", "norecovery", "noquota", "wsync"};
}
utils::select_filesystem(file_sys.c_str());
success = true;
screen.ExitLoopClosure()();
};
@ -1625,7 +1481,7 @@ bool select_filesystem() noexcept {
// This subfunction allows for special mounting options to be applied for relevant fs's.
// Separate subfunction for neatness.
void mount_opts() noexcept {
void mount_opts(bool force) noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
@ -1657,9 +1513,22 @@ void mount_opts() noexcept {
/* clang-format on */
}
auto screen = ScreenInteractive::Fullscreen();
auto& mount_opts_info = std::get<std::string>(config_data["MOUNT_OPTS"]);
auto ok_callback = [&] {
// Now clean up the file
auto cleaup_mount_opts = [](auto& opts_info) {
opts_info = utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | sed 's/ /,/g'"), opts_info));
opts_info = utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | sed '$s/,$//'"), opts_info));
};
if (force) {
mount_opts_info = detail::from_checklist_string(fs_opts, fs_opts_state.get());
cleaup_mount_opts(mount_opts_info);
return;
}
auto screen = ScreenInteractive::Fullscreen();
auto ok_callback = [&] {
mount_opts_info = detail::from_checklist_string(fs_opts, fs_opts_state.get());
screen.ExitLoopClosure()();
};
@ -1670,10 +1539,7 @@ void mount_opts() noexcept {
static constexpr auto mount_options_body = "\nUse [Space] to de/select the desired mount\noptions and review carefully. Please do not\nselect multiple versions of the same option.\n";
detail::checklist_widget(fs_opts, ok_callback, fs_opts_state.get(), &screen, mount_options_body, fs_title, {content_size, nothing});
// Now clean up the file
mount_opts_info = utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | sed 's/ /,/g'"), mount_opts_info));
mount_opts_info = utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" | sed '$s/,$//'"), mount_opts_info));
cleaup_mount_opts(mount_opts_info);
// If mount options selected, confirm choice
if (!mount_opts_info.empty()) {
@ -1688,7 +1554,7 @@ void mount_opts() noexcept {
}
}
bool mount_current_partition() noexcept {
bool mount_current_partition(bool force) noexcept {
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
@ -1706,7 +1572,7 @@ bool mount_current_partition() noexcept {
/* clang-format off */
// Get mounting options for appropriate filesystems
const auto& fs_opts = std::get<std::vector<std::string>>(config_data["fs_opts"]);
if (!fs_opts.empty()) { mount_opts(); }
if (!fs_opts.empty()) { mount_opts(force); }
/* clang-format on */
// TODO: use libmount instead.
@ -1723,7 +1589,10 @@ bool mount_current_partition() noexcept {
spdlog::info("{}", mount_status);
}
#endif
confirm_mount(fmt::format(FMT_COMPILE("{}{}"), mountpoint, mount_dev));
/* clang-format off */
if (!force) { confirm_mount(fmt::format(FMT_COMPILE("{}{}"), mountpoint, mount_dev)); }
/* clang-format on */
// Identify if mounted partition is type "crypt" (LUKS on LVM, or LUKS alone)
if (!utils::exec(fmt::format(FMT_COMPILE("lsblk -lno TYPE {} | grep \"crypt\""), partition)).empty()) {
@ -2828,29 +2697,20 @@ void menu_advanced() noexcept {
detail::menu_widget(menu_entries, ok_callback, &selected, &screen);
}
void menu_simple() noexcept {
// Prepare
utils::umount_partitions();
if (tui::select_device()) {
tui::create_partitions();
}
tui::mount_partitions();
// Install process
if (!utils::check_mount()) {
spdlog::error("Your partitions are not mounted");
}
tui::install_base();
tui::install_desktop();
if (!utils::check_base()) {
spdlog::error("Base is not installed");
}
tui::install_bootloader();
tui::config_base_menu();
}
void init() noexcept {
#if 0
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& menus = std::get<std::int32_t>(config_data["menus"]);
utils::parse_config();
if (menus == 1) {
tui::menu_simple();
return;
} else if (menus == 2) {
tui::menu_advanced();
return;
}
const std::vector<std::string> menu_entries = {
"Simple installation",
"Advanced installation",
@ -2878,8 +2738,6 @@ void init() noexcept {
default:
break;
}
#endif
tui::menu_advanced();
}
} // namespace tui

View File

@ -2,6 +2,15 @@
#define TUI_HPP
namespace tui {
void set_hostname() noexcept;
void set_locale() noexcept;
void set_xkbmap() noexcept;
bool set_timezone() noexcept;
void create_new_user() noexcept;
void set_root_password() noexcept;
void mount_opts(bool force = false) noexcept;
bool mount_current_partition(bool force = false) noexcept;
void auto_partition(bool interactive = true) noexcept;
void create_partitions() noexcept;
bool select_device() noexcept;
void init() noexcept;

View File

@ -39,6 +39,9 @@
#include <range/v3/view/split.hpp>
#include <range/v3/view/transform.hpp>
#include <rapidjson/document.h>
#include <rapidjson/istreamwrapper.h>
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
@ -306,6 +309,160 @@ void secure_wipe() noexcept {
#endif
}
void generate_fstab(const std::string_view& fstab_cmd) noexcept {
spdlog::info("Generating with fstab '{}'", fstab_cmd);
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
utils::exec(fmt::format(FMT_COMPILE("{0} {1} > {1}/etc/fstab"), fstab_cmd, mountpoint));
spdlog::info("Created fstab file:\n");
utils::exec(fmt::format(FMT_COMPILE("cat {}/etc/fstab >> /tmp/cachyos-install.log"), mountpoint));
const auto& swap_file = fmt::format(FMT_COMPILE("{}/swapfile"), mountpoint);
if (fs::exists(swap_file) && fs::is_regular_file(swap_file)) {
spdlog::info("appending swapfile to the fstab..");
utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/\\\\{0}//\" {0}/etc/fstab"), mountpoint));
}
// Edit fstab in case of btrfs subvolumes
utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/subvolid=.*,subvol=\\/.*,//g\" {}/etc/fstab"), mountpoint));
// remove any zfs datasets that are mounted by zfs
const auto& msource_list = utils::make_multiline(utils::exec(fmt::format(FMT_COMPILE("cat {}/etc/fstab | grep \"^[a-z,A-Z]\" | {}"), mountpoint, "awk '{print $1}'")));
for (const auto& msource : msource_list) {
if (utils::exec(fmt::format(FMT_COMPILE("zfs list -H -o mountpoint,name | grep \"^/\" | {} | grep \"^{}$\""), "awk '{print $2}'", msource), true) == "0")
utils::exec(fmt::format(FMT_COMPILE("sed -e \"\\|^{}[[:space:]]| s/^#*/#/\" -i {}/etc/fstab"), msource, mountpoint));
}
#endif
}
// Set system hostname
void set_hostname(const std::string_view& hostname) noexcept {
spdlog::info("Setting hostname {}", hostname);
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
utils::exec(fmt::format(FMT_COMPILE("echo \"{}\" > {}/etc/hostname"), hostname, mountpoint));
const auto& cmd = fmt::format(FMT_COMPILE("echo -e \"#<ip-address>\\t<hostname.domain.org>\\t<hostname>\\n127.0.0.1\\tlocalhost.localdomain\\tlocalhost\\t{0}\\n::1\\tlocalhost.localdomain\\tlocalhost\\t{0}\">{1}/etc/hosts"), hostname, mountpoint);
utils::exec(cmd);
#endif
}
// Set system language
void set_locale(const std::string_view& locale) noexcept {
spdlog::info("Selected locale: {}", locale);
#ifdef NDEVENV
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
const auto& locale_config_path = fmt::format(FMT_COMPILE("{}/etc/locale.conf"), mountpoint);
const auto& locale_gen_path = fmt::format(FMT_COMPILE("{}/etc/locale.gen"), mountpoint);
static constexpr auto locale_config_part = R"(LANG="{0}"
LC_NUMERIC="{0}"
LC_TIME="{0}"
LC_MONETARY="{0}"
LC_PAPER="{0}"
LC_NAME="{0}"
LC_ADDRESS="{0}"
LC_TELEPHONE="{0}"
LC_MEASUREMENT="{0}"
LC_IDENTIFICATION="{0}"
LC_MESSAGES="{0}")";
std::ofstream locale_config_file{locale_config_path};
locale_config_file << fmt::format(locale_config_part, locale);
utils::exec(fmt::format(FMT_COMPILE("sed -i \"s/#{0}/{0}/\" {1}"), locale, locale_gen_path));
// Generate locales
utils::arch_chroot("locale-gen", false);
#endif
}
void set_xkbmap(const std::string_view& xkbmap) noexcept {
spdlog::info("Selected xkbmap: {}", xkbmap);
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
utils::exec(fmt::format(FMT_COMPILE("echo -e \"Section \"\\\"InputClass\"\\\"\\nIdentifier \"\\\"system-keyboard\"\\\"\\nMatchIsKeyboard \"\\\"on\"\\\"\\nOption \"\\\"XkbLayout\"\\\" \"\\\"{0}\"\\\"\\nEndSection\" > {1}/etc/X11/xorg.conf.d/00-keyboard.conf"), xkbmap, mountpoint));
#endif
}
void set_timezone(const std::string_view& timezone) noexcept {
spdlog::info("Timezone is set to {}", timezone);
#ifdef NDEVENV
utils::arch_chroot(fmt::format(FMT_COMPILE("ln -sf /usr/share/zoneinfo/{} /etc/localtime"), timezone), false);
#endif
}
void set_hw_clock(const std::string_view& clock_type) noexcept {
spdlog::info("Clock type is: {}", clock_type);
#ifdef NDEVENV
utils::arch_chroot(fmt::format(FMT_COMPILE("hwclock --systohc --{}"), clock_type), false);
#endif
}
// Create user on the system
void create_new_user(const std::string_view& user, const std::string_view& password, const std::string_view& shell) noexcept {
spdlog::info("default shell: [{}]", shell);
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
if (shell.ends_with("zsh") || shell.ends_with("fish")) {
const auto& packages = fmt::format(FMT_COMPILE("cachyos-{}-config"), (shell.ends_with("zsh")) ? "zsh" : "fish");
const auto& hostcache = std::get<std::int32_t>(config_data["hostcache"]);
const auto& cmd = (hostcache) ? "pacstrap" : "pacstrap -c";
tui::detail::follow_process_log_widget({"/bin/sh", "-c", fmt::format(FMT_COMPILE("{} {} {} |& tee /tmp/pacstrap.log"), cmd, mountpoint, packages)});
}
// Create the user, set password, then remove temporary password file
utils::arch_chroot("groupadd sudo", false);
utils::arch_chroot(fmt::format(FMT_COMPILE("groupadd {}"), user), false);
utils::arch_chroot(fmt::format(FMT_COMPILE("useradd {0} -m -g {0} -G sudo,storage,power,network,video,audio,lp,sys,input -s {1}"), user, shell), false);
spdlog::info("add user to groups");
// check if user has been created
const auto& user_check = utils::exec(fmt::format(FMT_COMPILE("arch-chroot {} getent passwd {}"), mountpoint, user));
if (user_check.empty()) {
spdlog::error("User has not been created!");
}
std::error_code err{};
utils::exec(fmt::format(FMT_COMPILE("echo -e \"{0}\\n{0}\" > /tmp/.passwd"), password));
const auto& ret_status = utils::exec(fmt::format(FMT_COMPILE("arch-chroot {} passwd {} < /tmp/.passwd &>/dev/null"), mountpoint, user), true);
spdlog::info("create user pwd: {}", ret_status);
fs::remove("/tmp/.passwd", err);
// Set up basic configuration files and permissions for user
// arch_chroot "cp /etc/skel/.bashrc /home/${USER}"
utils::arch_chroot(fmt::format(FMT_COMPILE("chown -R {0}:{0} /home/{0}"), user), false);
const auto& sudoers_file = fmt::format(FMT_COMPILE("{}/etc/sudoers"), mountpoint);
if (fs::exists(sudoers_file)) {
utils::exec(fmt::format(FMT_COMPILE("sed -i '/NOPASSWD/!s/# %sudo/%sudo/g' {}"), sudoers_file));
}
#endif
}
// Set password for root user
void set_root_password([[maybe_unused]] const std::string_view& password) noexcept {
#ifdef NDEVENV
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
const auto& mountpoint = std::get<std::string>(config_data["MOUNTPOINT"]);
std::error_code err{};
utils::exec(fmt::format(FMT_COMPILE("echo -e \"{0}\n{0}\" > /tmp/.passwd"), password));
utils::exec(fmt::format(FMT_COMPILE("arch-chroot {} passwd root < /tmp/.passwd &>/dev/null"), mountpoint));
fs::remove("/tmp/.passwd", err);
#endif
}
// Finds all available partitions according to type(s) specified and generates a list
// of them. This also includes partitions on different devices.
void find_partitions() noexcept {
@ -751,6 +908,81 @@ vm.vfs_cache_pressure = 50
}
void parse_config() noexcept {
using namespace rapidjson;
// 1. Open file for reading.
static constexpr auto file_path = "settings.json";
std::ifstream ifs{file_path};
if (!ifs.is_open()) {
fmt::print(stderr, "Config not found running with defaults");
return;
}
IStreamWrapper isw{ifs};
// 2. Parse a JSON.
Document doc;
doc.ParseStream(isw);
// Document is a JSON value represents the root of DOM. Root can be either an object or array.
assert(doc.IsObject());
assert(doc.HasMember("menus"));
assert(doc["menus"].IsInt());
auto* config_instance = Config::instance();
auto& config_data = config_instance->data();
config_data["menus"] = doc["menus"].GetInt();
if (doc.HasMember("device")) {
assert(doc["device"].IsString());
config_data["DEVICE"] = std::string{doc["device"].GetString()};
}
if (doc.HasMember("fs_name")) {
assert(doc["fs_name"].IsString());
config_data["FILESYSTEM_NAME"] = std::string{doc["fs_name"].GetString()};
}
if (doc.HasMember("mount_opts")) {
assert(doc["mount_opts"].IsString());
config_data["MOUNT_OPTS"] = std::string{doc["mount_opts"].GetString()};
}
if (doc.HasMember("hostname")) {
assert(doc["hostname"].IsString());
config_data["HOSTNAME"] = std::string{doc["hostname"].GetString()};
}
if (doc.HasMember("locale")) {
assert(doc["locale"].IsString());
config_data["LOCALE"] = std::string{doc["locale"].GetString()};
}
if (doc.HasMember("xkbmap")) {
assert(doc["xkbmap"].IsString());
config_data["XKBMAP"] = std::string{doc["xkbmap"].GetString()};
}
if (doc.HasMember("timezone")) {
assert(doc["timezone"].IsString());
config_data["TIMEZONE"] = std::string{doc["timezone"].GetString()};
}
if (doc.HasMember("user_name") && doc.HasMember("user_pass") && doc.HasMember("user_shell")) {
assert(doc["user_name"].IsString());
assert(doc["user_pass"].IsString());
assert(doc["user_shell"].IsString());
config_data["USER_NAME"] = std::string{doc["user_name"].GetString()};
config_data["USER_PASS"] = std::string{doc["user_pass"].GetString()};
config_data["USER_SHELL"] = std::string{doc["user_shell"].GetString()};
}
if (doc.HasMember("root_pass")) {
assert(doc["root_pass"].IsString());
config_data["ROOT_PASS"] = std::string{doc["root_pass"].GetString()};
}
/*
using namespace simdjson;

View File

@ -20,6 +20,14 @@ void clear_screen() noexcept;
[[nodiscard]] auto make_multiline(std::vector<std::string>& multiline, bool reverse = false, const std::string_view&& delim = "\n") noexcept -> std::string;
void inst_needed(const std::string_view& pkg) noexcept;
void secure_wipe() noexcept;
void generate_fstab(const std::string_view& fstab_cmd) noexcept;
void set_hostname(const std::string_view& hostname) noexcept;
void set_locale(const std::string_view& locale) noexcept;
void set_xkbmap(const std::string_view& xkbmap) noexcept;
void set_timezone(const std::string_view& timezone) noexcept;
void set_hw_clock(const std::string_view& clock_type) noexcept;
void create_new_user(const std::string_view& user, const std::string_view& password, const std::string_view& shell) noexcept;
void set_root_password(const std::string_view& password) noexcept;
[[nodiscard]] bool check_mount() noexcept;
[[nodiscard]] bool check_base() noexcept;
[[nodiscard]] std::string list_mounted() noexcept;

View File

@ -0,0 +1,4 @@
project('rapidjson', 'cpp', version: '1.1.0', license: 'MIT')
rapidjson_inc = include_directories('include')
rapidjson_dep = declare_dependency(include_directories: rapidjson_inc)

View File

@ -0,0 +1,8 @@
[wrap-git]
url = https://github.com/Tencent/rapidjson.git
revision = 232389d4f1012dddec4ef84861face2d2ba85709
patch_directory = rapidjson
[provide]
rapidjson = rapidjson_dep