all lists on lists.proxmox.com
 help / color / mirror / Atom feed
From: Christoph Heiss <c.heiss@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH installer 2/5] chroot: replace clap with pico-args for command argument parsing
Date: Fri,  9 May 2025 14:09:14 +0200	[thread overview]
Message-ID: <20250509121007.1430080-3-c.heiss@proxmox.com> (raw)
In-Reply-To: <20250509121007.1430080-1-c.heiss@proxmox.com>

Drops clap for pico-args, as the former is pretty heavy-weight and
completely overhauls its API with ~every major release.

As for the resulting binary, there is a ~18% decrease in binary size,
from 2.9M to 2.4MiB (after stripping).

   text    data     bss     dec     hex filename
2743595  274576     424 3018595  2e0f63 proxmox-chroot-clap
2208631  261224     408 2470263  25b177 proxmox-chroot-pico

Further, the dependency tree goes from 40 to 29 dependencies, as well as
the package compile time on my machine (on hot caches), roughly measured
with

`cargo clean && time cargo build --package proxmox-chroot --release`

from ~20s to ~16s, so a nice little change w.r.t. to that aspect too.

No functional changes intended.

Signed-off-by: Christoph Heiss <c.heiss@proxmox.com>
---
 proxmox-chroot/Cargo.toml  |   6 +-
 proxmox-chroot/src/main.rs | 159 +++++++++++++++++++++++++++----------
 2 files changed, 120 insertions(+), 45 deletions(-)

diff --git a/proxmox-chroot/Cargo.toml b/proxmox-chroot/Cargo.toml
index 7a1390a..c043938 100644
--- a/proxmox-chroot/Cargo.toml
+++ b/proxmox-chroot/Cargo.toml
@@ -9,8 +9,6 @@ homepage = "https://www.proxmox.com"
 
 [dependencies]
 anyhow.workspace = true
-proxmox-installer-common.workspace = true
-serde_json.workspace = true
 regex.workspace = true
-
-clap = { version = "4.0", features = ["derive"] }
+proxmox-installer-common = { workspace = true, features = [ "cli" ] }
+serde_json.workspace = true
diff --git a/proxmox-chroot/src/main.rs b/proxmox-chroot/src/main.rs
index ef339ed..037f079 100644
--- a/proxmox-chroot/src/main.rs
+++ b/proxmox-chroot/src/main.rs
@@ -1,13 +1,20 @@
+//! Tool to help prepare a successfully installed target environment for
+//! chroot'ing.
+//!
+//! Can also be used in the rescue environment for mounting an existing system.
+
+#![forbid(unsafe_code)]
+
 use std::{
-    fs, io,
+    env, fs, io,
     path::{self, Path, PathBuf},
-    process::Command,
+    process::{self, Command},
+    str::FromStr,
 };
 
 use anyhow::{Result, bail};
-use clap::{Args, Parser, Subcommand, ValueEnum};
 use proxmox_installer_common::{
-    RUNTIME_DIR,
+    RUNTIME_DIR, cli,
     options::FsType,
     setup::{InstallConfig, SetupInfo},
 };
@@ -18,46 +25,87 @@ static BINDMOUNTS: [&str; 4] = ["dev", "proc", "run", "sys"];
 const TARGET_DIR: &str = "/target";
 const ZPOOL_NAME: &str = "rpool";
 
-/// Helper tool to prepare everything to `chroot` into an installation
-#[derive(Parser, Debug)]
-#[command(author, version, about, long_about = None)]
-struct Cli {
-    #[command(subcommand)]
-    command: Commands,
-}
-
-#[derive(Subcommand, Debug)]
-enum Commands {
-    Prepare(CommandPrepare),
-    Cleanup(CommandCleanup),
-}
-
-/// Mount the root file system and bind mounts in preparation to chroot into the installation
-#[derive(Args, Debug)]
-struct CommandPrepare {
+/// Arguments for the `prepare` command.
+struct CommandPrepareArgs {
     /// Filesystem used for the installation. Will try to automatically detect it after a
     /// successful installation.
-    #[arg(short, long, value_enum)]
     filesystem: Option<Filesystems>,
 
-    /// Numerical ID of `rpool` ZFS pool to import. Needed if multiple pools of name `rpool` are present.
-    #[arg(long)]
+    /// Numerical ID of the `rpool` ZFS pool to import. Needed if multiple pools of name `rpool`
+    /// are present.
     rpool_id: Option<u64>,
 
     /// UUID of the BTRFS file system to mount. Needed if multiple BTRFS file systems are present.
-    #[arg(long)]
     btrfs_uuid: Option<String>,
 }
 
-/// Unmount everything. Use once done with chroot.
-#[derive(Args, Debug)]
-struct CommandCleanup {
+impl cli::Subcommand for CommandPrepareArgs {
+    fn parse(args: &mut cli::Arguments) -> Result<Self> {
+        Ok(Self {
+            filesystem: args.opt_value_from_str(["-f", "--filesystem"])?,
+            rpool_id: args.opt_value_from_fn("--rpool-id", str::parse)?,
+            btrfs_uuid: args.opt_value_from_str("--btrfs-uuid")?,
+        })
+    }
+
+    fn print_usage() {
+        eprintln!(
+            r#"Mount the root file system and bind mounts in preparation to chroot into the installation.
+
+USAGE:
+  {} prepare <OPTIONS>
+
+OPTIONS:
+  -f, --filesystem <FILESYSTEM>  Filesystem used for the installation. Will try to automatically detect it after a successful installation. [possible values: zfs, ext4, xfs, btrfs]
+      --rpool-id <RPOOL_ID>      Numerical ID of `rpool` ZFS pool to import. Needed if multiple pools of name `rpool` are present.
+      --btrfs-uuid <BTRFS_UUID>  UUID of the BTRFS file system to mount. Needed if multiple BTRFS file systems are present.
+  -h, --help                       Print this help
+  -V, --version                    Print version
+"#,
+            env!("CARGO_PKG_NAME")
+        );
+    }
+
+    fn run(&self) -> Result<()> {
+        prepare(self)
+    }
+}
+
+/// Arguments for the `cleanup` command.
+struct CommandCleanupArgs {
     /// Filesystem used for the installation. Will try to automatically detect it by default.
-    #[arg(short, long, value_enum)]
     filesystem: Option<Filesystems>,
 }
 
-#[derive(Copy, Clone, Debug, ValueEnum)]
+impl cli::Subcommand for CommandCleanupArgs {
+    fn parse(args: &mut cli::Arguments) -> Result<Self> {
+        Ok(Self {
+            filesystem: args.opt_value_from_str(["-f", "--filesystem"])?,
+        })
+    }
+
+    fn print_usage() {
+        eprintln!(
+            r#"Unmount everything. Use once done with the chroot.
+
+USAGE:
+  {} cleanup <OPTIONS>
+
+OPTIONS:
+  -f, --filesystem <FILESYSTEM>    Filesystem used for the installation. Will try to automatically detect it after a successful installation. [possible values: zfs, ext4, xfs, btrfs]
+  -h, --help                       Print this help
+  -V, --version                    Print version
+"#,
+            env!("CARGO_PKG_NAME")
+        );
+    }
+
+    fn run(&self) -> Result<()> {
+        cleanup(self)
+    }
+}
+
+#[derive(Copy, Clone, Debug)]
 enum Filesystems {
     Zfs,
     Ext4,
@@ -76,19 +124,48 @@ impl From<FsType> for Filesystems {
     }
 }
 
-fn main() {
-    let args = Cli::parse();
-    let res = match &args.command {
-        Commands::Prepare(args) => prepare(args),
-        Commands::Cleanup(args) => cleanup(args),
-    };
-    if let Err(err) = res {
-        eprintln!("{err:#}");
-        std::process::exit(1);
+impl FromStr for Filesystems {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> Result<Self> {
+        match s {
+            "ext4" => Ok(Filesystems::Ext4),
+            "xfs" => Ok(Filesystems::Xfs),
+            _ if s.starts_with("zfs") => Ok(Filesystems::Zfs),
+            _ if s.starts_with("btrfs") => Ok(Filesystems::Btrfs),
+            _ => bail!("unknown filesystem"),
+        }
     }
 }
 
-fn prepare(args: &CommandPrepare) -> Result<()> {
+fn main() -> process::ExitCode {
+    cli::run(cli::AppInfo {
+        global_help: &format!(
+            r#"Helper tool to set up a `chroot` into an existing installation.
+
+USAGE:
+  {} <COMMAND> <OPTIONS>
+
+COMMANDS:
+  prepare            Mount the root file system and bind mounts in preparation to chroot into the installation.
+  cleanup            Unmount everything. Use once done with the chroot.
+
+GLOBAL OPTIONS:
+  -h, --help         Print help
+  -V, --version      Print version information
+"#,
+            env!("CARGO_PKG_NAME")
+        ),
+        on_command: |s, args| match s {
+            Some("prepare") => cli::handle_command::<CommandPrepareArgs>(args),
+            Some("cleanup") => cli::handle_command::<CommandCleanupArgs>(args),
+            Some(s) => bail!("unknown subcommand '{s}'"),
+            None => bail!("subcommand required"),
+        },
+    })
+}
+
+fn prepare(args: &CommandPrepareArgs) -> Result<()> {
     let fs = get_fs(args.filesystem)?;
 
     fs::create_dir_all(TARGET_DIR)?;
@@ -108,7 +185,7 @@ fn prepare(args: &CommandPrepare) -> Result<()> {
     Ok(())
 }
 
-fn cleanup(args: &CommandCleanup) -> Result<()> {
+fn cleanup(args: &CommandCleanupArgs) -> Result<()> {
     let fs = get_fs(args.filesystem)?;
 
     if let Err(e) = bind_umount() {
-- 
2.49.0



_______________________________________________
pve-devel mailing list
pve-devel@lists.proxmox.com
https://lists.proxmox.com/cgi-bin/mailman/listinfo/pve-devel


  parent reply	other threads:[~2025-05-09 12:11 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-09 12:09 [pve-devel] [PATCH installer 0/5] chroot, assistant: replace clap with pico-args Christoph Heiss
2025-05-09 12:09 ` [pve-devel] [PATCH installer 1/5] common: introduce simple cli subcommand parser based on pico-args Christoph Heiss
2025-05-09 12:09 ` Christoph Heiss [this message]
2025-05-09 12:09 ` [pve-devel] [PATCH installer 3/5] assistant: replace clap with pico-args for command argument parsing Christoph Heiss
2025-05-09 12:09 ` [pve-devel] [PATCH installer 4/5] chroot: replace btrfs parsing regex with lsblk json parsing Christoph Heiss
2025-05-09 12:09 ` [pve-devel] [PATCH installer 5/5] tui: drop unused dependencies from manifest Christoph Heiss

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250509121007.1430080-3-c.heiss@proxmox.com \
    --to=c.heiss@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal