diff --git a/annotate.py b/annotate.py new file mode 100644 index 0000000..3cf92f9 --- /dev/null +++ b/annotate.py @@ -0,0 +1,46 @@ +import sys +import subprocess as sp +import json +from dataclasses import dataclass, field + +""" Piped commands not working currently +""" + +@dataclass +class Annotation: + json: str + data: dict = field(init=False) + + def __post_init__(self): + with open(self.json, 'r') as f: + self.data = json.load(f) + + def execute_cmd(self, key, cmd): + try: + result = sp.check_output(cmd, shell=True).decode('ascii') + except sp.CalledProcessError as err: + print(f"Failed w/ {err}") + raise + else: + tmp = self.data["results"][0] + if hasattr(tmp,"annotations"): + tmp["annotations"][key] = result + else: + tmp["annotations"] = {key:result} + self.data["results"][0] = tmp + + def export(self): + with open('new_' + self.json, 'w') as f: + json.dump(self.data, f, indent=2) + + +def main(keyword, hyperfine_json, cmd): + annon = Annotation(hyperfine_json) + annon.execute_cmd(keyword, cmd) + annon.export() + +if __name__ == '__main__': + keyword = sys.argv[1] + js = sys.argv[2] + command = sys.argv[3:] + main(keyword = keyword, hyperfine_json = js, cmd = command) diff --git a/examples/duplicate.toml b/examples/duplicate.toml index 137c36d..163a226 100644 --- a/examples/duplicate.toml +++ b/examples/duplicate.toml @@ -41,6 +41,10 @@ setup = "which ls" shell = "none" command = "dd if={ifile} of=/tmp/Cargo.toml.dd" +[run.dd.annotations] +filesize = "ls -al /tmp/Cargo.toml.dd | awk '{{print $5}}'" +modified = "ls -al /tmp/Cargo.toml.dd | awk '{{print $6\" \"$7\" \"$8}}'" + [run.cp] command = "cp {ifile} /tmp/Cargo.toml.cp" diff --git a/src/core.rs b/src/core.rs index 7a4137d..1be12b7 100644 --- a/src/core.rs +++ b/src/core.rs @@ -12,6 +12,30 @@ use std::{collections::HashMap, fmt::Display, fs::File, io::Read, path::PathBuf} /// Transformation to hyperfine parameters pub trait Hyperfined { fn to_hyperfine(&self) -> Vec; + fn to_hyperfine_with_json(&self, json: &str) -> Vec { + let mut params = self.to_hyperfine(); + params.push("--export-json".to_string()); + params.push(json.to_string()); + params + } + fn to_hyperfine_with_json_and_annotations(&self, json: &str, kw: &HashMap) -> Vec { + let mut params = self.to_hyperfine(); + let mut annotations: Vec<_> = kw.iter().map(|(k, v)| util::generate_jq_cmd(&k, &v, json)).collect(); + match ¶ms.iter().position(|x| x == &"--cleanup".to_string()) { + Some(ix) => { + // Cleanup used in command and annotations must be prepand + annotations.push(params.get(ix+1).unwrap().clone()); + params.insert(ix + 1, annotations.join(" && ")); + params.remove(ix + 2); + }, + None => { + // Cleanup not used, annotations can be added as cleanup + params.push("--cleanup".to_string()); + params.push(annotations.join(" && ")); + } + } + params + } } /// Configuration for a complete Benchmark set consisting of several Runs @@ -64,6 +88,7 @@ pub(crate) struct Run { prepare: Option, setup: Option, shell: Option, + pub annotations: Option>, command: String, } diff --git a/src/main.rs b/src/main.rs index 364dad0..3c3990a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,13 +10,13 @@ mod util; /// Command-line Interface (CLI) for the hypcmp library #[derive(Parser, Debug)] #[clap(author, version, about, long_about = None)] -pub struct Cli { +struct Cli { /// Configuration file [*.toml] #[clap(value_parser)] - pub config: PathBuf, + config: PathBuf, #[clap(flatten)] - pub verbose: clap_verbosity_flag::Verbosity, + verbose: clap_verbosity_flag::Verbosity, } fn main() -> std::io::Result<()> { @@ -42,23 +42,28 @@ fn main() -> std::io::Result<()> { for (label, run) in c.run.iter() { debug!("Run: {run:?}"); + // Initiate `hyperfine` command let mut cmd = Command::new("hyperfine"); - cmd.args(c.to_hyperfine()); - let mut name = vec!["--command-name".to_string()]; - name.push(label.clone()); - cmd.args(name); - - let mut json = vec!["--export-json".to_string()]; + // Add json output to arguments let mut filename = label.clone(); filename.push_str(".json"); let output = dir.path().join(filename).display().to_string(); - json.push(output.clone()); - cmd.args(json); + cmd.args(c.to_hyperfine_with_json(&output)); + + // Add command name to arguments + let mut name = vec!["--command-name".to_string()]; + name.push(label.clone()); + cmd.args(name); - cmd.args(run.to_hyperfine()); + // Add run specific arguments + match &run.annotations { + Some(hm) => cmd.args(run.to_hyperfine_with_json_and_annotations(&output, hm)), + None => cmd.args(run.to_hyperfine()), + }; info!("Running: {cmd:?}"); + // Execute command let result = cmd.output()?; if result.status.success() { debug!("Benchmark run successful"); @@ -80,7 +85,7 @@ fn main() -> std::io::Result<()> { } else { let json = util::merge_json_files(&files_to_be_merged)?; util::write_json_to_disk(json)?; - util::cleanup(files_to_be_merged, dir)?; + // util::cleanup(files_to_be_merged, dir)?; util::checkout(current_branch)?; } Ok(()) diff --git a/src/util.rs b/src/util.rs index aceab7c..4f6ee45 100644 --- a/src/util.rs +++ b/src/util.rs @@ -251,3 +251,26 @@ fn move_commit_label_to_cmd_name(mut json: Value) -> std::io::Result String { + format!( + "jq --arg {0} $({1}) '.results[0].{0}=${0}' {2} > {2}", + key, cmd, json + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn jq() { + let key = "filesize"; + let cmd = "ls -al /tmp/Cargo.toml.dd | awk '{{print $5}}'"; + let json = "out.json"; + let expected = "jq --arg filesize $(ls -al /tmp/Cargo.toml.dd | awk '{{print $5}}') '.results[0].filesize=$filesize' out.json"; + + assert_eq!(expected, generate_jq_cmd(key, cmd, json)) + } +}