use clap::CommandFactory; use clap_complete::{generate_to, Shell}; use std::env; use std::fs; use std::path::{Path, PathBuf}; use std::process::Command; #[path = "src/cli.rs"] mod cli; fn apply_template(template: &Path) -> String { let outdir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR environment variable not defined")); fs::create_dir_all(&outdir).expect("unable to create out dir"); let profile = env::var_os("PROFILE").expect("PROFILE environment variable not defined"); let mdevctl_bin_path = PathBuf::from("target").join(profile).join("mdevctl"); let version = env::var("CARGO_PKG_VERSION").expect("CARGO_PKG_VERSION not set"); fs::read_to_string(template) .unwrap_or_else(|_| panic!("Failed to read template {:?}", template)) .replace("@@mdevctl@@", mdevctl_bin_path.to_str().unwrap()) .replace("@@outdir@@", outdir.to_str().unwrap()) .replace("@@mdevctl_version@@", version.as_str()) .replace( "@@generated_notice@@", format!( "# DO NOT EDIT\n# This file is automatically generated from {:?}", template ) .as_str(), ) } fn generate_manpage>(outdir: P) { let rst2man = env::var("RST2MAN").unwrap_or_else(|_| "rst2man".to_owned()); let infile = PathBuf::from("mdevctl.rst"); println!("cargo:rerun-if-changed={}", infile.to_str().unwrap()); let outfile = outdir.as_ref().join("mdevctl.8"); Command::new(rst2man) .arg(infile) .arg(outfile) .output() .expect("Unable to generate manpage. Is 'rst2man' installed? You can specify a custom 'rst2man' executable by setting the RST2MAN environment variable."); } fn generate_completion(cmd: &mut clap::Command, outdir: &PathBuf) { let name = cmd.get_name().to_string(); generate_to(Shell::Bash, cmd, name, outdir).expect("Unable to generate shell completion files"); } fn main() { let outdir = PathBuf::from(env::var_os("OUT_DIR").expect("OUT_DIR environment variable not defined")); // generate bash completions for both executables generate_completion(&mut cli::MdevctlCommands::command(), &outdir); generate_completion(&mut cli::LsmdevOptions::command(), &outdir); // generate manpage generate_manpage(outdir); // generate a rpm spec file from the spec.in template let rpm_in = PathBuf::from("mdevctl.spec.in"); let rpm_out = PathBuf::from(rpm_in.file_stem().unwrap()); let contents = apply_template(&rpm_in); std::fs::write(&rpm_out, contents).unwrap_or_else(|_| panic!("Failed to write {:?}", rpm_out)); println!("cargo:rerun-if-changed={}", rpm_in.to_str().unwrap()); // Generate a makefile for installing the auxiliary files based on the Makefile.in template let makefile_in = PathBuf::from("Makefile.in"); let makefile_out = PathBuf::from(makefile_in.file_stem().unwrap()); let contents = apply_template(&makefile_in); // FIXME: build.rs really shouldn't touch anything outside of OUT_DIR, but generating the // Makefile in a directory named ./target/$PROFILE/build/mdevctl-$HASH/out/ is very // undiscoverable and kind of defeats the purpose. Need to find a better solution. std::fs::write(&makefile_out, contents) .unwrap_or_else(|_| panic!("Failed to write {:?}", makefile_out)); println!("cargo:rerun-if-changed={}", makefile_in.to_str().unwrap()); }