From 427ea9655e7a1ec38881c17015b27c8fbce582a5 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Sat, 7 Dec 2024 10:45:35 +0200 Subject: [PATCH] Add an xtask crate to generate manpages For information about the cargo xtask workflow pattern see: This commit adds an xtask crate with only one task, "mangen", which generates a ROFF manual page under `target/dist/man` directory for the vhost-device-sound binary. The xtask crate can be configured using cargo features, to help packagers configure what manpages are generated. This generates a manpage in target/dist/man/vhost-device-sound.1 The rendered ROFF output looks like: vhost-device-sound(1) General Commands Manual vhost-device-sound(1) NAME vhost-device-sound - A virtio-sound device using the vhost-user protocol. SYNOPSIS vhost-device-sound <--socket> <--backend> [-h|--help] [-V|--version] DESCRIPTION A virtio-sound device using the vhost-user protocol. OPTIONS --socket=SOCKET vhost-user Unix domain socket path --backend=BACKEND audio backend to be used [possible values: null, pipewire, alsa] -h, --help Print help -V, --version Print version VERSION v0.2.0 REPORTING BUGS Report bugs to the project's issue tracker: https://github.com/rust-vmm/vhost-device vhost-device-sound 0.2.0 vhost-device-sound(1) Fixes #687 ("Add man page for vhost-device-sound") Resolves: https://github.com/rust-vmm/vhost-device/issues/687 Signed-off-by: Manos Pitsidianakis --- .cargo/config.toml | 2 + Cargo.lock | 25 +++++++ Cargo.toml | 1 + coverage_config_x86_64.json | 2 +- xtask/Cargo.toml | 27 ++++++++ xtask/build.rs | 14 ++++ xtask/src/main.rs | 134 ++++++++++++++++++++++++++++++++++++ 7 files changed, 204 insertions(+), 1 deletion(-) create mode 100644 xtask/Cargo.toml create mode 100644 xtask/build.rs create mode 100644 xtask/src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml index ba63e46..7df701a 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,3 +1,5 @@ [target.aarch64-unknown-linux-musl] rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ] +[alias] +xtask = "run --package xtask --" diff --git a/Cargo.lock b/Cargo.lock index fe67bfa..a438c17 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -322,6 +322,16 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "clap_mangen" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "724842fa9b144f9b89b3f3d371a89f3455eea660361d13a554f68f8ae5d6c13a" +dependencies = [ + "clap", + "roff", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -1267,6 +1277,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "roff" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88f8660c1ff60292143c98d08fc6e2f654d722db50410e3f3797d40baaf9d8f3" + [[package]] name = "rstest" version = "0.25.0" @@ -2328,6 +2344,15 @@ dependencies = [ "tap", ] +[[package]] +name = "xtask" +version = "0.1.0" +dependencies = [ + "clap", + "clap_mangen", + "toml 0.8.19", +] + [[package]] name = "yansi-term" version = "0.1.2" diff --git a/Cargo.toml b/Cargo.toml index 54a1c46..3251c06 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,4 +15,5 @@ members = [ "vhost-device-spi", "vhost-device-template", "vhost-device-vsock", + "xtask", ] diff --git a/coverage_config_x86_64.json b/coverage_config_x86_64.json index 79c169f..733dd59 100644 --- a/coverage_config_x86_64.json +++ b/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { "coverage_score": 86.60, - "exclude_path": "", + "exclude_path": "xtask", "crate_features": "" } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml new file mode 100644 index 0000000..e0e5c4e --- /dev/null +++ b/xtask/Cargo.toml @@ -0,0 +1,27 @@ +[package] +name = "xtask" +version = "0.1.0" +authors = ["Manos Pitsidianakis "] +description = "A helper binary crate following the cargo-xtask workflow recommended in " +repository = "https://github.com/rust-vmm/vhost-device" +readme = "README.md" +license = "EUPL-1.2 OR GPL-3.0-or-later" +edition = "2021" +publish = false + +[dependencies] +clap = { version = "4.5", features = ["derive"], optional = true } +clap_mangen = { version = "0.2.24", optional = true } +toml = { version = "0.8.19", optional = true } + +[build-dependencies] + +[features] +default = ["vhost-device-sound"] +vhost-device-sound = ["vhost-device-sound-alsa", "vhost-device-sound-pipewire"] +vhost-device-sound-alsa = ["mangen"] +vhost-device-sound-pipewire = ["mangen"] +mangen = ["dep:clap_mangen", "dep:clap", "dep:toml"] + +[lints.rust] +unexpected_cfgs = { level = "allow", check-cfg = ['cfg(feature, values("alsa-backend", "pw-backend"))'] } diff --git a/xtask/build.rs b/xtask/build.rs new file mode 100644 index 0000000..6583207 --- /dev/null +++ b/xtask/build.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later +// Copyright (c) 2024 Linaro Ltd. + +fn main() { + #[cfg(feature = "vhost-device-sound-pipewire")] + println!("cargo::rustc-cfg=feature=\"pw-backend\""); + #[cfg(feature = "vhost-device-sound-alsa")] + println!("cargo::rustc-cfg=feature=\"alsa-backend\""); + #[cfg(any( + feature = "vhost-device-sound-pipewire", + feature = "vhost-device-sound-alsa" + ))] + println!("cargo::rustc-cfg=target_env=\"gnu\""); +} diff --git a/xtask/src/main.rs b/xtask/src/main.rs new file mode 100644 index 0000000..4b9c8de --- /dev/null +++ b/xtask/src/main.rs @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: EUPL-1.2 OR GPL-3.0-or-later +// Copyright (c) 2024 Linaro Ltd. + +// the `explicit_builtin_cfgs_in_flags` lint must be allowed because we might +// emit `target_env = "gnu"` in build.rs in order to get some cfg checks to +// compile; it does no harm because we're not using anything target_env specific +// here. If we find out that it affects the xtask's crate dependencies (e.g. +// when using this xtask under musl), we should figure out some other solution. +#![allow(unknown_lints, explicit_builtin_cfgs_in_flags)] + +use std::error::Error; + +#[cfg(feature = "mangen")] +use clap::CommandFactory; +#[cfg(feature = "mangen")] +use clap_mangen::Man; +#[cfg(feature = "mangen")] +use toml::value::Table; + +// Use vhost-device-sound's args module as our own using the #[path] attribute + +#[cfg(any( + feature = "vhost-device-sound-pipewire", + feature = "vhost-device-sound-alsa" +))] +#[path = "../../vhost-device-sound/src/args.rs"] +mod vhost_device_sound; + +fn main() { + if let Err(err) = run_app() { + eprintln!("{}", err); + std::process::exit(-1); + } +} + +fn run_app() -> Result<(), Box> { + let task = std::env::args().nth(1); + match task.as_deref() { + #[cfg(feature = "mangen")] + Some("mangen") => mangen()?, + _ => print_help(), + } + Ok(()) +} + +fn print_help() { + eprintln!( + "Tasks: + +{mangen}", + mangen = if cfg!(feature = "mangen") { + "mangen builds man pages using clap_mangen under target/dist/man" + } else { + "" + }, + ) +} + +#[cfg(feature = "mangen")] +fn mangen() -> Result<(), Box> { + let workspace_dir = std::path::Path::new(&env!("CARGO_MANIFEST_DIR")) + .ancestors() + .nth(1) + .unwrap() + .to_path_buf(); + let dist_dir = workspace_dir.join("target/dist/man"); + let _ = std::fs::remove_dir_all(&dist_dir); + std::fs::create_dir_all(&dist_dir)?; + + let mut generated_artifacts = vec![]; + + #[cfg(any( + feature = "vhost-device-sound-pipewire", + feature = "vhost-device-sound-alsa" + ))] + { + use vhost_device_sound::SoundArgs; + + let manifest = + std::fs::read_to_string(workspace_dir.join("vhost-device-sound/Cargo.toml"))?; + let manifest = manifest.as_str().parse::()?; + + let name: &'static str = manifest["package"]["name"] + .as_str() + .unwrap() + .to_string() + .leak(); + let version: &'static str = manifest["package"]["version"] + .as_str() + .unwrap() + .to_string() + .leak(); + let repository: &'static str = manifest["package"]["repository"] + .as_str() + .unwrap() + .to_string() + .leak(); + let description: &'static str = manifest["package"]["description"] + .as_str() + .unwrap() + .to_string() + .leak(); + let cmd = ::command() + .name(name) + .display_name(name) + .bin_name(name) + .version(version) + .about(description); + let man = Man::new(cmd); + let mut buffer: Vec = Default::default(); + man.render(&mut buffer)?; + clap_mangen::roff::Roff::new() + .control("SH", ["REPORTING BUGS"]) + .text(vec![format!( + "Report bugs to the project's issue tracker: {repository}" + ) + .into()]) + .to_writer(&mut buffer)?; + + let man_path = dist_dir.join("vhost-device-sound.1"); + std::fs::write(&man_path, buffer)?; + generated_artifacts.push(man_path); + } + if generated_artifacts.is_empty() { + println!("No manpages were generated! Try using the correct xtask cargo features."); + } else { + println!("Generated the following manual pages:"); + for art in generated_artifacts { + println!("{}", art.display()); + } + } + + Ok(()) +}