sequoia-sq/build.rs
Justus Winter f81d6f09af
Use our custom manual page generator during build time.
- Previously, there were two ways of generating manual pages.
    We used to use the upstream clap_mangen crate, but decided to
    develop our own custom formatter.  However, we didn't quite switch
    to it, keeping the old mechanism in place and adding our new
    solution to sq, activated by a hidden environment variable.

  - This patch drops the upstream formatter, and uses the custom
    formatter during build time.  First, our custom code can be
    tweaked easily to suit our needs, and switching back to the
    upstream solution is easy enough should it better suit our needs.
    Second, generating manual pages at build time should help
    cross-building environments.
2024-01-05 13:56:32 +01:00

128 lines
3.4 KiB
Rust

use std::env;
use std::fs;
use std::io::Write;
use std::path::PathBuf;
use clap::ValueEnum;
use clap_complete::Shell;
use anyhow::{Context, Result};
pub mod cli {
#![allow(unused_macros)]
include!("src/macros.rs");
include!("src/cli/mod.rs");
}
pub mod man {
include!("src/man.rs");
}
fn main() {
println!("cargo:rerun-if-changed=build.rs");
// Generate subplot tests.
#[cfg(feature = "subplot")]
subplot_build::codegen("sq.subplot")
.expect("failed to generate code with Subplot");
let mut sq = cli::build();
generate_sq_usage_md(&sq).unwrap();
generate_shell_completions(&mut sq).unwrap();
generate_man_pages(&sq).unwrap();
}
/// Generates shell completions.
fn generate_shell_completions(sq: &mut clap::Command) -> Result<()> {
let outdir: PathBuf =
env::var_os("OUT_DIR").expect("OUT_DIR not set").into();
assert!(outdir.is_dir());
let path = outdir.join("shell-completions");
fs::create_dir_all(&path)?;
for shell in Shell::value_variants() {
clap_complete::generate_to(*shell, sq, "sq", &path)?;
};
println!("cargo:warning=shell completions written to {}", path.display());
Ok(())
}
/// Dump help output of all commands and subcommands, for inclusion in
/// the top-level documentation.
fn generate_sq_usage_md(cmd: &clap::Command) -> Result<()> {
let mut cmd = cmd.clone().term_width(80);
cmd.build();
let outdir: PathBuf =
env::var_os("OUT_DIR").expect("OUT_DIR not set").into();
assert!(outdir.is_dir());
let path = outdir.join("sq-usage.md");
let mut sink = fs::File::create(&path)
.with_context(|| format!("trying to create {}", path.display()))?;
dump_help_inner(&mut sink, &mut cmd, "##")
}
fn dump_help_inner(
sink: &mut dyn Write,
cmd: &mut clap::Command,
heading: &str,
) -> Result<()> {
writeln!(sink)?;
let mut buffer = Vec::new();
let _ = cmd.write_long_help(&mut buffer);
let help = std::str::from_utf8(buffer.as_slice())?;
let mut verbatim = false;
for line in help.trim_end().split('\n').skip(1) {
if ! verbatim && line.starts_with("Usage:") {
writeln!(sink, "```text")?;
verbatim = true;
}
if line.is_empty() {
writeln!(sink)?;
} else {
writeln!(sink, "{}", line.trim_end())?;
}
}
if verbatim {
writeln!(sink, "```")?;
}
// Recurse.
for subcommand in cmd
.get_subcommands_mut()
.filter(|sc| sc.get_name() != "help")
{
writeln!(sink)?;
let heading_name = subcommand
// cmd.build() in dump_help makes sure every subcommand has a display_name
.get_display_name()
.unwrap()
.replace('-', " ");
writeln!(sink, "{} Subcommand {}", heading, heading_name)?;
dump_help_inner(sink, subcommand, &format!("{}#", heading))?;
}
Ok(())
}
/// Generates man pages.
fn generate_man_pages(sq: &clap::Command) -> Result<()> {
let outdir: PathBuf =
env::var_os("OUT_DIR").expect("OUT_DIR not set").into();
assert!(outdir.is_dir());
let path = outdir.join("man-pages");
fs::create_dir_all(&path)?;
for man in man::manpages(sq) {
std::fs::write(path.join(man.filename()), man.troff_source())?;
}
println!("cargo:warning=man pages written to {}", path.display());
Ok(())
}