1use anyhow::Context;
12use clap::Parser;
13use clap::Subcommand;
14use std::path::Path;
15use std::path::PathBuf;
16
17mod completions;
18pub mod fs_helpers;
19pub mod tasks;
20
21pub const XTASK_PATH_FILE: &str = "./target/xtask-path";
27
28#[derive(Clone)]
30pub struct XtaskCtx {
31 pub root: PathBuf,
33 pub in_git_hook: bool,
35 pub in_run_on_save: bool,
37}
38
39pub trait Xtask: Parser {
41 fn run(self, ctx: XtaskCtx) -> anyhow::Result<()>;
48}
49
50#[derive(Parser)]
51#[clap(name = "xtask", about = "HvLite repo automation")]
52struct Cli {
53 #[clap(subcommand)]
54 command: Commands,
55
56 #[clap(long)]
61 custom_root: Option<PathBuf>,
62
63 #[clap(long)]
74 run_on_save: bool,
75}
76
77#[expect(clippy::large_enum_variant)]
78#[derive(Subcommand)]
79enum Commands {
80 #[clap(hide = true)]
81 Hook(tasks::RunGitHook),
82 #[clap(hide = true)]
83 Complete(clap_dyn_complete::Complete),
84 Completions(completions::Completions),
85
86 #[clap(hide = true)]
88 BuildIgvm(tasks::BuildIgvm),
89
90 Fmt(tasks::Fmt),
91 Fuzz(tasks::Fuzz),
92 GuestTest(tasks::GuestTest),
93 InstallGitHooks(tasks::InstallGitHooks),
94 VerifySize(tasks::VerifySize),
95}
96
97fn main() {
98 ci_logger::init("XTASK_LOG").unwrap();
99
100 if let Err(e) = try_main() {
101 log::error!("Error: {:#}", e);
102 std::process::exit(-1);
103 }
104}
105
106fn try_main() -> anyhow::Result<()> {
107 let cli = Cli::parse();
108
109 let orig_root = Path::new(&env!("CARGO_MANIFEST_DIR"))
110 .ancestors()
111 .nth(1)
112 .unwrap()
113 .to_path_buf();
114
115 let root = cli
116 .custom_root
117 .map(std::path::absolute)
118 .transpose()?
119 .unwrap_or(orig_root.clone());
120
121 std::env::set_current_dir(&root)?;
123
124 if let Ok(path) = std::env::current_exe() {
128 if let Err(e) = fs_err::write(orig_root.join(XTASK_PATH_FILE), path.display().to_string()) {
129 log::debug!("Unable to create XTASK_PATH_FILE: {:#}", e)
130 }
131 }
132
133 if !matches!(cli.command, Commands::Complete(..)) {
134 tasks::update_hooks(&root).context("failed to update git hooks")?;
135 }
136
137 let ctx = XtaskCtx {
138 root,
139 in_git_hook: matches!(cli.command, Commands::Hook(..)),
140 in_run_on_save: cli.run_on_save,
141 };
142
143 match cli.command {
144 Commands::Hook(task) => task.run(ctx),
145 Commands::Completions(task) => task.run(),
146 Commands::Complete(task) => {
147 futures::executor::block_on(task.println_to_stub_script::<Cli>(
148 Some("cargo"),
149 completions::XtaskCompleteFactory { ctx },
150 ));
151 Ok(())
152 }
153
154 Commands::BuildIgvm(task) => task.run(ctx),
155 Commands::Fmt(task) => task.run(ctx),
156 Commands::Fuzz(task) => task.run(ctx),
157 Commands::GuestTest(task) => task.run(ctx),
158 Commands::InstallGitHooks(task) => task.run(ctx),
159 Commands::VerifySize(task) => task.run(ctx),
160 }
161}