Add an xtask crate to generate manpages

For information about the cargo xtask workflow pattern see: <https://github.com/matklad/cargo-xtask>

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 <manos.pitsidianakis@linaro.org>
This commit is contained in:
Manos Pitsidianakis 2024-12-07 10:45:35 +02:00 committed by Viresh Kumar
parent de8ed1baea
commit 427ea9655e
7 changed files with 204 additions and 1 deletions

View File

@ -1,3 +1,5 @@
[target.aarch64-unknown-linux-musl]
rustflags = [ "-C", "target-feature=+crt-static", "-C", "link-arg=-lgcc" ]
[alias]
xtask = "run --package xtask --"

25
Cargo.lock generated
View File

@ -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"

View File

@ -15,4 +15,5 @@ members = [
"vhost-device-spi",
"vhost-device-template",
"vhost-device-vsock",
"xtask",
]

View File

@ -1,5 +1,5 @@
{
"coverage_score": 86.60,
"exclude_path": "",
"exclude_path": "xtask",
"crate_features": ""
}

27
xtask/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "xtask"
version = "0.1.0"
authors = ["Manos Pitsidianakis <manos.pitsidianakis@linaro.org>"]
description = "A helper binary crate following the cargo-xtask workflow recommended in <https://github.com/matklad/cargo-xtask>"
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"))'] }

14
xtask/build.rs Normal file
View File

@ -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\"");
}

134
xtask/src/main.rs Normal file
View File

@ -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<dyn Error>> {
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<dyn Error>> {
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::<Table>()?;
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 = <SoundArgs as CommandFactory>::command()
.name(name)
.display_name(name)
.bin_name(name)
.version(version)
.about(description);
let man = Man::new(cmd);
let mut buffer: Vec<u8> = 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(())
}