use std::collections::VecDeque;
use std::time::Instant;
use runite::process::{Command, Output};
use runite::task::JoinSet;
struct Step {
name: &'static str,
script: &'static str,
}
const COMPILE_STEPS: &[Step] = &[
Step {
name: "lexer.c",
script: "sleep 0.12; echo 'lexer: 412 lines ok'",
},
Step {
name: "parser.c",
script: "sleep 0.08; echo 'parser.c:88: unbalanced brace' >&2; exit 1",
},
Step {
name: "eval.c",
script: "sleep 0.15; echo 'eval: 890 lines ok'",
},
Step {
name: "main.c",
script: "sleep 0.05; echo 'main: 120 lines ok'",
},
];
const CONCURRENCY: usize = 2;
async fn run_step(step: &'static Step) -> (&'static Step, std::io::Result<Output>) {
let output = Command::new("sh").arg("-c").arg(step.script).output().await;
(step, output)
}
fn report(step: &Step, took: std::time::Duration, output: &Output) -> bool {
if output.status.success() {
let stdout = String::from_utf8_lossy(&output.stdout);
println!(" ok {:<10} {took:>10.1?} {}", step.name, stdout.trim());
true
} else {
let stderr = String::from_utf8_lossy(&output.stderr);
println!(
" FAIL {:<10} {took:>10.1?} (exit {:?}) {}",
step.name,
output.status.code(),
stderr.trim(),
);
false
}
}
#[runite::main]
async fn main() -> std::io::Result<()> {
let started = Instant::now();
println!(
"compiling {} units, {CONCURRENCY} at a time:",
COMPILE_STEPS.len()
);
let mut queue: VecDeque<&'static Step> = COMPILE_STEPS.iter().collect();
let mut in_flight = JoinSet::new();
let mut failures = 0u32;
loop {
while in_flight.len() < CONCURRENCY {
match queue.pop_front() {
Some(step) => {
let launched = Instant::now();
in_flight.spawn(async move {
let (step, output) = run_step(step).await;
(step, launched.elapsed(), output)
});
}
None => break,
}
}
match in_flight.join_next().await {
Some(finished) => {
let (step, took, output) = finished.expect("step task should not be aborted");
if !report(step, took, &output?) {
failures += 1;
}
}
None => break, }
}
if failures == 0 {
let output = Command::new("sh")
.arg("-c")
.arg("echo 'linked app (4 objects)'")
.output()
.await?;
println!("link: {}", String::from_utf8_lossy(&output.stdout).trim());
} else {
println!("link: skipped ({failures} unit(s) failed)");
}
println!(
"pipeline finished in {:.1?} — {} succeeded, {failures} failed",
started.elapsed(),
COMPILE_STEPS.len() as u32 - failures,
);
assert_eq!(failures, 1, "exactly the scripted step should fail");
Ok(())
}