xtask/src/fast.rs
//! **`cargo xtask fast --product <id>`** (A28 H14, lane N3): the fast test of a product, on
//! THIS checkout's monetize-fast (monetize v2.0 P1, A24 §8.2): its estate bought, paid, laid,
//! installed and paired by the real ONE box, its real box binaries in rootless containers,
//! then torn down; one receipt.
//!
//! What is the product's stays the product's: its checkout beside this one builds its box rows,
//! its container media and its guest, and calls `monetize-fast` from `../monetize` (holger's
//! `cargo xtask fast`, gunnar's `cargo xtask fast`). This table names only where that verb is;
//! the estate's names and media are the product's facts, read by monetize-fast from its guest.
//!
//! `cargo xtask fast --product <holger|gunnar> [--out DIR] [--budget-s N] [--twin-late-s N] [--no-build]`
//! (everything after `--product <id>` goes to the product's verb). On the host: it starts containers.
use std::path::{Path, PathBuf};
use std::process::Command;
use std::time::Instant;
/// One product the fast test runs: its id and the verb its own checkout carries.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FastProduct {
/// The product (`--product`), as the shop sells it.
pub id: &'static str,
/// Its checkout, beside this one.
pub checkout: &'static str,
/// The verb there (cargo's arguments).
pub verb: &'static [&'static str],
}
/// **The declared table**: every product with a fast verb of its own.
pub const PRODUCTS: &[FastProduct] = &[
FastProduct {
id: "holger",
checkout: "holger",
verb: &["xtask", "fast"],
},
FastProduct {
id: "gunnar",
checkout: "gunnar",
verb: &["xtask", "fast"],
},
];
const USAGE: &str = "cargo xtask fast --product <holger|gunnar> [--out DIR] [--budget-s N] [--twin-late-s N] [--no-build]";
/// The parsed verb.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Args {
pub product: &'static FastProduct,
/// Handed to the product's verb as they are.
pub rest: Vec<String>,
}
/// Parse `--product <id> [rest…]` (the flag may come anywhere).
pub fn parse(args: &[String]) -> Result<Args, String> {
let at = args
.iter()
.position(|a| a == "--product")
.ok_or_else(|| format!("which product? `{USAGE}`"))?;
let id = args
.get(at + 1)
.ok_or_else(|| format!("--product wants a value; `{USAGE}`"))?;
let product = PRODUCTS.iter().find(|p| p.id == id).ok_or_else(|| {
let known = PRODUCTS.iter().map(|p| p.id).collect::<Vec<_>>().join(", ");
if id == "lake" || id == "echo" {
format!("[fast-product] the Lake lays no boxes of its own yet: its test is `cargo xtask bench lake`; the fast test runs {known}")
} else {
format!("[fast-product] `{id}` has no fast verb; the fast test runs {known}")
}
})?;
let rest = args
.iter()
.enumerate()
.filter(|(i, _)| *i != at && *i != at + 1)
.map(|(_, a)| a.clone())
.collect();
Ok(Args { product, rest })
}
/// Pure: where the product's verb runs and its argv.
pub fn command(root: &Path, a: &Args) -> (PathBuf, Vec<String>) {
let dir = root.parent().unwrap_or(root).join(a.product.checkout);
let mut argv = vec!["cargo".to_string()];
argv.extend(a.product.verb.iter().map(|s| s.to_string()));
argv.extend(a.rest.iter().cloned());
(dir, argv)
}
/// Pure: **the product's own target** under a shared one (`$CARGO_TARGET_DIR/fast-<product>`).
/// Both repos have a path package `xtask` of the same version, and cargo gives such a package
/// the same build hash in every checkout: under ONE target the product's launcher would run
/// monetize's xtask. `None`: no shared target, the product builds in its own default.
pub fn product_target(shared: Option<&Path>, product: &str) -> Option<PathBuf> {
shared.map(|t| t.join(format!("fast-{product}")))
}
/// Run it; answers the wall time in seconds.
pub fn run(root: &Path, a: &Args) -> Result<u64, String> {
let (dir, argv) = command(root, a);
if !dir.join("Cargo.toml").is_file() {
return Err(format!(
"[fast-no-checkout] {}'s checkout is not beside monetize: {} (clone it there)",
a.product.id,
dir.display()
));
}
println!(
"fast {}: {} (in {})",
a.product.id,
argv.join(" "),
dir.display()
);
let t0 = Instant::now();
let mut cmd = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()));
cmd.args(&argv[1..]).current_dir(&dir);
let shared = std::env::var_os("CARGO_TARGET_DIR").map(PathBuf::from);
match product_target(shared.as_deref(), a.product.id) {
Some(t) => {
println!("fast {}: its own target {}", a.product.id, t.display());
cmd.env("CARGO_TARGET_DIR", t);
}
None => {
cmd.env_remove("CARGO_TARGET_DIR");
}
}
let status = cmd
.status()
.map_err(|e| format!("{}: {e}", argv.join(" ")))?;
let secs = t0.elapsed().as_secs();
if status.success() {
Ok(secs)
} else {
Err(format!("[fast-red] the fast test of {} is RED after {secs} s ({status}); its receipt is under {}", a.product.id, dir.join("target/fast").display()))
}
}
#[cfg(test)]
mod tests {
use super::*;
/// ★ **A product's verb never shares our target** (2026-10-25): both repos have a path
/// package `xtask` of the same version, and cargo gives such a package the same build hash
/// in every checkout. Under one CARGO_TARGET_DIR the product's launcher ran OUR xtask
/// ("which product?"). So the verb gets its own sub-target, inside a bounded one.
#[test]
fn the_products_verb_builds_in_its_own_target() {
let t = product_target(Some(Path::new("/work/ci/target")), "holger");
assert_eq!(t.as_deref(), Some(Path::new("/work/ci/target/fast-holger")));
assert_ne!(
product_target(Some(Path::new("/t")), "holger"),
product_target(Some(Path::new("/t")), "gunnar"),
"one per product"
);
assert_eq!(
product_target(None, "holger"),
None,
"no shared target: the product's own default"
);
}
}