From c67fa967002ff3762398c7dc1b3020cd7e3a234a Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 3 Dec 2022 20:31:19 -0800 Subject: [PATCH] Run bench for each commit. --- src/main.rs | 183 ++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 136 insertions(+), 47 deletions(-) diff --git a/src/main.rs b/src/main.rs index ffca413..572f300 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,12 @@ #[macro_use] extern crate rocket; use std::{ - fs, io, + fs, + fs::File, + io, path::{Path, PathBuf}, - process::Command, + process::{Command, Output}, + time::{Duration, Instant}, }; use glog::Flags; @@ -29,63 +32,149 @@ fn post_reload(config: &State, repo: &str) -> Result reload(config, repo) } -fn logging_run(cmd: &mut Command) -> &mut Command { - info!("Running {cmd:?}"); - cmd +fn indent_output(bytes: &[u8]) -> String { + let s = String::from_utf8_lossy(bytes); + let s = if s.is_empty() { "" } else { &s }; + s.split('\n') + .map(|l| format!("\t{l}")) + .collect::>() + .join("\n") +} + +fn format_task_status(ts: &TaskStatus) -> String { + format!( + "Status {} ({}) {}s:\nStdout:\n{}\nStderr:\n{}", + ts.command, + ts.output.status, + ts.duration.as_secs_f32(), + indent_output(&ts.output.stdout), + indent_output(&ts.output.stderr) + ) +} + +struct TaskStatus { + command: String, + output: Output, + duration: Duration, +} + +fn logging_run(cmd: &mut Command) -> Result { + let start = Instant::now(); + let o = cmd.output()?; + let ts = TaskStatus { + output: o, + command: format!("{:?}", cmd), + duration: start.elapsed(), + }; + info!("{}", format_task_status(&ts)); + Ok(ts) +} + +fn git_checkout(build_path: &Path, rev: &str) -> Result { + logging_run( + Command::new("git") + .current_dir(&build_path) + .arg("checkout") + .arg("-q") + .arg(rev), + ) +} + +fn bench_at_commit( + commit: &str, + build_path: &Path, + target_path: &Path, +) -> Result, SyncError> { + let mut output = Vec::new(); + output.push(git_checkout(build_path, commit)?); + // Run `cargo aoc bench` + output.push(logging_run( + Command::new("cargo") + .env("CARGO_TARGET_DIR", target_path) + .current_dir(&build_path) + .arg("aoc") + .arg("bench"), + )?); + output.push(git_checkout(build_path, "HEAD")?); + + Ok(output) } fn reload(config: &State, repo: &str) -> Result { info!("Need to reload '{}': {:?}", repo, config); let source_path = config.source_root.join(repo); - let build_path = config.build_root.join(repo); - dbg!(&build_path); - dbg!(&source_path); + let build_path = config.build_root.join("git").join(repo); + let target_path = config.build_root.join("target").join(repo); + let commits_root = config.build_root.join("commits"); + let www_root = config.www_root.join(repo); + + dbg!( + &source_path, + &build_path, + &target_path, + &commits_root, + &www_root + ); + + if !commits_root.exists() { + info!("Creating {}", commits_root.display()); + fs::create_dir_all(&commits_root)?; + } + if !www_root.exists() { + info!("Creating {}", www_root.display()); + fs::create_dir_all(&www_root)?; + } let mut output = Vec::new(); let needs_clone = !build_path.exists(); if needs_clone { - output.push( - logging_run( - Command::new("/run/current-system/sw/bin/git") - .current_dir(&config.build_root) - .arg("clone") - .arg(&source_path) - .arg(&build_path), - ) - .output()?, - ); + output.push(logging_run( + Command::new("git") + .current_dir(&config.build_root) + .arg("clone") + .arg(&source_path) + .arg(&build_path), + )?); } + output.push(logging_run( + Command::new("git") + .current_dir(&config.build_root) + .arg("checkout") + .arg("-f") + .arg("origin"), + )?); // Make sure buildable clone is up to date - output.push( - logging_run( - Command::new("/run/current-system/sw/bin/git") - .current_dir(&build_path) - .arg("pull"), - ) - .output()?, - ); - // Run `cargo aoc bench` - output.push( - logging_run( - Command::new("cargo") - .current_dir(&build_path) - .arg("aoc") - .arg("bench"), - ) - .output()?, - ); + output.push(logging_run( + Command::new("git").current_dir(&build_path).arg("pull"), + )?); + let commits = logging_run(Command::new("git").current_dir(&build_path).args([ + "log", + "--format=%H", + "origin", + ]))?; + let binding = String::from_utf8_lossy(&commits.output.stdout).into_owned(); + let mut unknown_commits: Vec<_> = binding + .lines() + .filter(|commit| !commits_root.join(commit).exists()) + .collect(); + unknown_commits.reverse(); + output.push(commits); + info!("Need to bench commits: {:?}", unknown_commits); + for commit in unknown_commits { + match bench_at_commit(commit, &build_path, &target_path) { + Ok(outputs) => { + output.extend(outputs); + File::create(commits_root.join(commit))?; + } + Err(err) => error!("Failed to bench {}@{}: {}", repo, commit, err), + } + } // Copy files from `target/` to serving directory - let bench_path = build_path.join("target/aoc/aoc-autobench/target/criterion"); - copy_dir_all(bench_path, config.www_root.join(repo))?; + let bench_path = target_path.join("criterion"); + info!("Copying {} -> {}", bench_path.display(), www_root.display()); + copy_dir_all(bench_path, www_root)?; let response = output .iter() - .map(|o| { - format!( - "Status {}:\nStdout: {}\nStderr: {}", - o.status, - String::from_utf8_lossy(&o.stdout), - String::from_utf8_lossy(&o.stderr) - ) - }) + .map(|ts| format!("{}", format_task_status(ts))) .collect::>() .join("\n"); info!("{}", response); @@ -138,6 +227,6 @@ fn rocket() -> _ { .unwrap(); rocket::build() .mount("/", routes![index, get_reload, post_reload]) - .register("/", catchers![http500]) + //.register("/", catchers![http500]) .attach(AdHoc::config::()) }