mirror of
https://github.com/sharkdp/hyperfine.git
synced 2024-10-26 22:21:58 +03:00
Unified error handling
This commit is contained in:
parent
34dabcbed7
commit
2040810ce3
7
Cargo.lock
generated
7
Cargo.lock
generated
@ -11,6 +11,12 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anyhow"
|
||||||
|
version = "1.0.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "approx"
|
name = "approx"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
@ -246,6 +252,7 @@ dependencies = [
|
|||||||
name = "hyperfine"
|
name = "hyperfine"
|
||||||
version = "1.12.0"
|
version = "1.12.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
"approx",
|
"approx",
|
||||||
"assert_cmd",
|
"assert_cmd",
|
||||||
"atty",
|
"atty",
|
||||||
|
@ -23,6 +23,7 @@ rust_decimal = "1.19"
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
shell-words = "1.0"
|
shell-words = "1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
|
anyhow = "1.0"
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
libc = "0.2"
|
libc = "0.2"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::io;
|
|
||||||
use std::process::{ExitStatus, Stdio};
|
use std::process::{ExitStatus, Stdio};
|
||||||
|
|
||||||
use colored::*;
|
use colored::*;
|
||||||
@ -18,6 +17,8 @@ use crate::timer::{TimerStart, TimerStop};
|
|||||||
use crate::units::Second;
|
use crate::units::Second;
|
||||||
use crate::warnings::Warnings;
|
use crate::warnings::Warnings;
|
||||||
|
|
||||||
|
use anyhow::{bail, Result};
|
||||||
|
|
||||||
/// Threshold for warning about fast execution time
|
/// Threshold for warning about fast execution time
|
||||||
pub const MIN_EXECUTION_TIME: Second = 5e-3;
|
pub const MIN_EXECUTION_TIME: Second = 5e-3;
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ pub fn time_shell_command(
|
|||||||
show_output: bool,
|
show_output: bool,
|
||||||
failure_action: CmdFailureAction,
|
failure_action: CmdFailureAction,
|
||||||
shell_spawning_time: Option<TimingResult>,
|
shell_spawning_time: Option<TimingResult>,
|
||||||
) -> io::Result<(TimingResult, ExitStatus)> {
|
) -> Result<(TimingResult, ExitStatus)> {
|
||||||
let (stdout, stderr) = if show_output {
|
let (stdout, stderr) = if show_output {
|
||||||
(Stdio::inherit(), Stdio::inherit())
|
(Stdio::inherit(), Stdio::inherit())
|
||||||
} else {
|
} else {
|
||||||
@ -65,18 +66,14 @@ pub fn time_shell_command(
|
|||||||
let mut time_system = result.system_time;
|
let mut time_system = result.system_time;
|
||||||
|
|
||||||
if failure_action == CmdFailureAction::RaiseError && !result.status.success() {
|
if failure_action == CmdFailureAction::RaiseError && !result.status.success() {
|
||||||
return Err(io::Error::new(
|
bail!(
|
||||||
io::ErrorKind::Other,
|
"{}. Use the '-i'/'--ignore-failure' option if you want to ignore this. \
|
||||||
format!(
|
|
||||||
"{}. \
|
|
||||||
Use the '-i'/'--ignore-failure' option if you want to ignore this. \
|
|
||||||
Alternatively, use the '--show-output' option to debug what went wrong.",
|
Alternatively, use the '--show-output' option to debug what went wrong.",
|
||||||
result.status.code().map_or(
|
result.status.code().map_or(
|
||||||
"The process has been terminated by a signal".into(),
|
"The process has been terminated by a signal".into(),
|
||||||
|c| format!("Command terminated with non-zero exit code: {}", c)
|
|c| format!("Command terminated with non-zero exit code: {}", c)
|
||||||
)
|
)
|
||||||
),
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Correct for shell spawning time
|
// Correct for shell spawning time
|
||||||
@ -101,7 +98,7 @@ pub fn mean_shell_spawning_time(
|
|||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
style: OutputStyleOption,
|
style: OutputStyleOption,
|
||||||
show_output: bool,
|
show_output: bool,
|
||||||
) -> io::Result<TimingResult> {
|
) -> Result<TimingResult> {
|
||||||
const COUNT: u64 = 50;
|
const COUNT: u64 = 50;
|
||||||
let progress_bar = if style != OutputStyleOption::Disabled {
|
let progress_bar = if style != OutputStyleOption::Disabled {
|
||||||
Some(get_progress_bar(
|
Some(get_progress_bar(
|
||||||
@ -135,14 +132,10 @@ pub fn mean_shell_spawning_time(
|
|||||||
format!("{} -c \"\"", shell)
|
format!("{} -c \"\"", shell)
|
||||||
};
|
};
|
||||||
|
|
||||||
return Err(io::Error::new(
|
bail!(
|
||||||
io::ErrorKind::Other,
|
"Could not measure shell execution time. Make sure you can run '{}'.",
|
||||||
format!(
|
|
||||||
"Could not measure shell execution time. \
|
|
||||||
Make sure you can run '{}'.",
|
|
||||||
shell_cmd
|
shell_cmd
|
||||||
),
|
);
|
||||||
));
|
|
||||||
}
|
}
|
||||||
Ok((r, _)) => {
|
Ok((r, _)) => {
|
||||||
times_real.push(r.time_real);
|
times_real.push(r.time_real);
|
||||||
@ -172,11 +165,11 @@ fn run_intermediate_command(
|
|||||||
command: &Option<Command<'_>>,
|
command: &Option<Command<'_>>,
|
||||||
show_output: bool,
|
show_output: bool,
|
||||||
error_output: &'static str,
|
error_output: &'static str,
|
||||||
) -> io::Result<TimingResult> {
|
) -> Result<TimingResult> {
|
||||||
if let Some(ref cmd) = command {
|
if let Some(ref cmd) = command {
|
||||||
let res = time_shell_command(shell, cmd, show_output, CmdFailureAction::RaiseError, None);
|
let res = time_shell_command(shell, cmd, show_output, CmdFailureAction::RaiseError, None);
|
||||||
if res.is_err() {
|
if res.is_err() {
|
||||||
return Err(io::Error::new(io::ErrorKind::Other, error_output));
|
bail!(error_output);
|
||||||
}
|
}
|
||||||
return res.map(|r| r.0);
|
return res.map(|r| r.0);
|
||||||
}
|
}
|
||||||
@ -190,7 +183,7 @@ fn run_setup_command(
|
|||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
command: &Option<Command<'_>>,
|
command: &Option<Command<'_>>,
|
||||||
show_output: bool,
|
show_output: bool,
|
||||||
) -> io::Result<TimingResult> {
|
) -> Result<TimingResult> {
|
||||||
let error_output = "The setup command terminated with a non-zero exit code. \
|
let error_output = "The setup command terminated with a non-zero exit code. \
|
||||||
Append ' || true' to the command if you are sure that this can be ignored.";
|
Append ' || true' to the command if you are sure that this can be ignored.";
|
||||||
|
|
||||||
@ -202,7 +195,7 @@ fn run_preparation_command(
|
|||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
command: &Option<Command<'_>>,
|
command: &Option<Command<'_>>,
|
||||||
show_output: bool,
|
show_output: bool,
|
||||||
) -> io::Result<TimingResult> {
|
) -> Result<TimingResult> {
|
||||||
let error_output = "The preparation command terminated with a non-zero exit code. \
|
let error_output = "The preparation command terminated with a non-zero exit code. \
|
||||||
Append ' || true' to the command if you are sure that this can be ignored.";
|
Append ' || true' to the command if you are sure that this can be ignored.";
|
||||||
|
|
||||||
@ -214,7 +207,7 @@ fn run_cleanup_command(
|
|||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
command: &Option<Command<'_>>,
|
command: &Option<Command<'_>>,
|
||||||
show_output: bool,
|
show_output: bool,
|
||||||
) -> io::Result<TimingResult> {
|
) -> Result<TimingResult> {
|
||||||
let error_output = "The cleanup command terminated with a non-zero exit code. \
|
let error_output = "The cleanup command terminated with a non-zero exit code. \
|
||||||
Append ' || true' to the command if you are sure that this can be ignored.";
|
Append ' || true' to the command if you are sure that this can be ignored.";
|
||||||
|
|
||||||
@ -248,7 +241,7 @@ pub fn run_benchmark(
|
|||||||
cmd: &Command<'_>,
|
cmd: &Command<'_>,
|
||||||
shell_spawning_time: TimingResult,
|
shell_spawning_time: TimingResult,
|
||||||
options: &HyperfineOptions,
|
options: &HyperfineOptions,
|
||||||
) -> io::Result<BenchmarkResult> {
|
) -> Result<BenchmarkResult> {
|
||||||
let command_name = cmd.get_name();
|
let command_name = cmd.get_name();
|
||||||
if options.output_style != OutputStyleOption::Disabled {
|
if options.output_style != OutputStyleOption::Disabled {
|
||||||
println!(
|
println!(
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
|
|
||||||
use crate::benchmark_result::BenchmarkResult;
|
use crate::benchmark_result::BenchmarkResult;
|
||||||
use crate::format::format_duration_value;
|
use crate::format::format_duration_value;
|
||||||
use crate::relative_speed::{self, BenchmarkResultWithRelativeSpeed};
|
use crate::relative_speed::{self, BenchmarkResultWithRelativeSpeed};
|
||||||
@ -7,6 +5,8 @@ use crate::units::Unit;
|
|||||||
|
|
||||||
use super::Exporter;
|
use super::Exporter;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct AsciidocExporter {}
|
pub struct AsciidocExporter {}
|
||||||
|
|
||||||
@ -36,9 +36,8 @@ impl Exporter for AsciidocExporter {
|
|||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(
|
Err(anyhow!(
|
||||||
ErrorKind::Other,
|
"Relative speed comparison is not available for Asciidoctor export."
|
||||||
"Relative speed comparison is not available for Asciidoctor export.",
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
|
|
||||||
use csv::WriterBuilder;
|
use csv::WriterBuilder;
|
||||||
|
|
||||||
@ -7,6 +6,8 @@ use super::Exporter;
|
|||||||
use crate::benchmark_result::BenchmarkResult;
|
use crate::benchmark_result::BenchmarkResult;
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct CsvExporter {}
|
pub struct CsvExporter {}
|
||||||
|
|
||||||
@ -49,9 +50,7 @@ impl Exporter for CsvExporter {
|
|||||||
writer.write_record(fields)?;
|
writer.write_record(fields)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer
|
Ok(writer.into_inner()?)
|
||||||
.into_inner()
|
|
||||||
.map_err(|e| Error::new(ErrorKind::Other, e))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
|
|
||||||
use serde::*;
|
use serde::*;
|
||||||
use serde_json::to_vec_pretty;
|
use serde_json::to_vec_pretty;
|
||||||
|
|
||||||
@ -7,6 +5,8 @@ use super::Exporter;
|
|||||||
use crate::benchmark_result::BenchmarkResult;
|
use crate::benchmark_result::BenchmarkResult;
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
struct HyperfineSummary<'a> {
|
struct HyperfineSummary<'a> {
|
||||||
results: &'a [BenchmarkResult],
|
results: &'a [BenchmarkResult],
|
||||||
@ -22,6 +22,6 @@ impl Exporter for JsonExporter {
|
|||||||
content.push(b'\n');
|
content.push(b'\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
output.map_err(|e| Error::new(ErrorKind::Other, e))
|
Ok(output?)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use std::io::{Error, ErrorKind, Result};
|
|
||||||
|
|
||||||
use super::Exporter;
|
use super::Exporter;
|
||||||
use crate::benchmark_result::BenchmarkResult;
|
use crate::benchmark_result::BenchmarkResult;
|
||||||
use crate::format::format_duration_value;
|
use crate::format::format_duration_value;
|
||||||
use crate::relative_speed::{self, BenchmarkResultWithRelativeSpeed};
|
use crate::relative_speed::{self, BenchmarkResultWithRelativeSpeed};
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Result};
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct MarkdownExporter {}
|
pub struct MarkdownExporter {}
|
||||||
|
|
||||||
@ -31,9 +31,8 @@ impl Exporter for MarkdownExporter {
|
|||||||
|
|
||||||
Ok(destination)
|
Ok(destination)
|
||||||
} else {
|
} else {
|
||||||
Err(Error::new(
|
Err(anyhow!(
|
||||||
ErrorKind::Other,
|
"Relative speed comparison is not available for Markdown export."
|
||||||
"Relative speed comparison is not available for Markdown export.",
|
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::fs::{File, OpenOptions};
|
use std::fs::{File, OpenOptions};
|
||||||
use std::io::{Result, Write};
|
use std::io::Write;
|
||||||
|
|
||||||
mod asciidoc;
|
mod asciidoc;
|
||||||
mod csv;
|
mod csv;
|
||||||
@ -14,6 +14,8 @@ use self::markdown::MarkdownExporter;
|
|||||||
use crate::benchmark_result::BenchmarkResult;
|
use crate::benchmark_result::BenchmarkResult;
|
||||||
use crate::units::Unit;
|
use crate::units::Unit;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
/// The desired form of exporter to use for a given file.
|
/// The desired form of exporter to use for a given file.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub enum ExportType {
|
pub enum ExportType {
|
||||||
@ -50,7 +52,8 @@ pub struct ExportManager {
|
|||||||
impl ExportManager {
|
impl ExportManager {
|
||||||
/// Add an additional exporter to the ExportManager
|
/// Add an additional exporter to the ExportManager
|
||||||
pub fn add_exporter(&mut self, export_type: ExportType, filename: &str) -> Result<()> {
|
pub fn add_exporter(&mut self, export_type: ExportType, filename: &str) -> Result<()> {
|
||||||
let _ = File::create(filename)?;
|
let _ = File::create(filename)
|
||||||
|
.with_context(|| format!("Could not create export file '{filename}'"))?;
|
||||||
|
|
||||||
let exporter: Box<dyn Exporter> = match export_type {
|
let exporter: Box<dyn Exporter> = match export_type {
|
||||||
ExportType::Asciidoc => Box::new(AsciidocExporter::default()),
|
ExportType::Asciidoc => Box::new(AsciidocExporter::default()),
|
||||||
@ -80,4 +83,5 @@ impl ExportManager {
|
|||||||
fn write_to_file(filename: &str, content: &[u8]) -> Result<()> {
|
fn write_to_file(filename: &str, content: &[u8]) -> Result<()> {
|
||||||
let mut file = OpenOptions::new().write(true).open(filename)?;
|
let mut file = OpenOptions::new().write(true).open(filename)?;
|
||||||
file.write_all(content)
|
file.write_all(content)
|
||||||
|
.with_context(|| format!("Failed to export results to '{}'", filename))
|
||||||
}
|
}
|
||||||
|
88
src/main.rs
88
src/main.rs
@ -1,7 +1,6 @@
|
|||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::io;
|
|
||||||
|
|
||||||
use atty::Stream;
|
use atty::Stream;
|
||||||
use clap::ArgMatches;
|
use clap::ArgMatches;
|
||||||
@ -39,11 +38,7 @@ use tokenize::tokenize;
|
|||||||
use types::ParameterValue;
|
use types::ParameterValue;
|
||||||
use units::Unit;
|
use units::Unit;
|
||||||
|
|
||||||
/// Print error message to stderr and terminate
|
use anyhow::{bail, Result};
|
||||||
pub fn error(message: &str) -> ! {
|
|
||||||
eprintln!("{} {}", "Error:".red(), message);
|
|
||||||
std::process::exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_benchmark_comparison(results: &[BenchmarkResult]) {
|
pub fn write_benchmark_comparison(results: &[BenchmarkResult]) {
|
||||||
if results.len() < 2 {
|
if results.len() < 2 {
|
||||||
@ -83,12 +78,11 @@ pub fn write_benchmark_comparison(results: &[BenchmarkResult]) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the benchmark for the given commands
|
fn run_benchmarks_and_print_comparison(
|
||||||
fn run(
|
|
||||||
commands: &[Command<'_>],
|
commands: &[Command<'_>],
|
||||||
options: &HyperfineOptions,
|
options: &HyperfineOptions,
|
||||||
export_manager: &ExportManager,
|
export_manager: &ExportManager,
|
||||||
) -> io::Result<()> {
|
) -> Result<()> {
|
||||||
let shell_spawning_time =
|
let shell_spawning_time =
|
||||||
mean_shell_spawning_time(&options.shell, options.output_style, options.show_output)?;
|
mean_shell_spawning_time(&options.shell, options.output_style, options.show_output)?;
|
||||||
|
|
||||||
@ -96,9 +90,9 @@ fn run(
|
|||||||
|
|
||||||
if let Some(preparation_command) = &options.preparation_command {
|
if let Some(preparation_command) = &options.preparation_command {
|
||||||
if preparation_command.len() > 1 && commands.len() != preparation_command.len() {
|
if preparation_command.len() > 1 && commands.len() != preparation_command.len() {
|
||||||
error(
|
bail!(
|
||||||
"The '--prepare' option has to be provided just once or N times, where N is the \
|
"The '--prepare' option has to be provided just once or N times, where N is the \
|
||||||
number of benchmark commands.",
|
number of benchmark commands."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -108,13 +102,7 @@ fn run(
|
|||||||
timing_results.push(run_benchmark(num, cmd, shell_spawning_time, options)?);
|
timing_results.push(run_benchmark(num, cmd, shell_spawning_time, options)?);
|
||||||
|
|
||||||
// Export (intermediate) results
|
// Export (intermediate) results
|
||||||
let ans = export_manager.write_results(&timing_results, options.time_unit);
|
export_manager.write_results(&timing_results, options.time_unit)?;
|
||||||
if let Err(e) = ans {
|
|
||||||
error(&format!(
|
|
||||||
"The following error occurred while exporting: {}",
|
|
||||||
e
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Print relative speed comparison
|
// Print relative speed comparison
|
||||||
@ -125,23 +113,22 @@ fn run(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn run() -> Result<()> {
|
||||||
let matches = get_arg_matches(env::args_os());
|
let matches = get_arg_matches(env::args_os());
|
||||||
let options = build_hyperfine_options(&matches);
|
let options = build_hyperfine_options(&matches)?;
|
||||||
let commands = build_commands(&matches);
|
let commands = build_commands(&matches)?;
|
||||||
let export_manager = match build_export_manager(&matches) {
|
let export_manager = build_export_manager(&matches)?;
|
||||||
Ok(export_manager) => export_manager,
|
|
||||||
Err(ref e) => error(&e.to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let res = match options {
|
run_benchmarks_and_print_comparison(&commands, &options, &export_manager)
|
||||||
Ok(ref opts) => run(&commands, opts, &export_manager),
|
}
|
||||||
Err(ref e) => error(&e.to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
match res {
|
fn main() {
|
||||||
|
match run() {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => error(&e.to_string()),
|
Err(e) => {
|
||||||
|
eprintln!("{} {:#}", "Error:".red(), e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,10 +230,10 @@ fn build_hyperfine_options<'a>(matches: &ArgMatches) -> Result<HyperfineOptions,
|
|||||||
|
|
||||||
/// Build the ExportManager that will export the results specified
|
/// Build the ExportManager that will export the results specified
|
||||||
/// in the given ArgMatches
|
/// in the given ArgMatches
|
||||||
fn build_export_manager(matches: &ArgMatches) -> io::Result<ExportManager> {
|
fn build_export_manager(matches: &ArgMatches) -> Result<ExportManager> {
|
||||||
let mut export_manager = ExportManager::default();
|
let mut export_manager = ExportManager::default();
|
||||||
{
|
{
|
||||||
let mut add_exporter = |flag, exporttype| -> io::Result<()> {
|
let mut add_exporter = |flag, exporttype| -> Result<()> {
|
||||||
if let Some(filename) = matches.value_of(flag) {
|
if let Some(filename) = matches.value_of(flag) {
|
||||||
export_manager.add_exporter(exporttype, filename)?;
|
export_manager.add_exporter(exporttype, filename)?;
|
||||||
}
|
}
|
||||||
@ -267,10 +254,12 @@ fn build_commands(matches: &ArgMatches) -> Result<Vec<Command>> {
|
|||||||
|
|
||||||
if let Some(args) = matches.values_of("parameter-scan") {
|
if let Some(args) = matches.values_of("parameter-scan") {
|
||||||
let step_size = matches.value_of("parameter-step-size");
|
let step_size = matches.value_of("parameter-step-size");
|
||||||
match get_parameterized_commands(command_names, command_strings, args, step_size) {
|
Ok(get_parameterized_commands(
|
||||||
Ok(commands) => commands,
|
command_names,
|
||||||
Err(e) => error(&e.to_string()),
|
command_strings,
|
||||||
}
|
args,
|
||||||
|
step_size,
|
||||||
|
)?)
|
||||||
} else if let Some(args) = matches.values_of("parameter-list") {
|
} else if let Some(args) = matches.values_of("parameter-list") {
|
||||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||||
|
|
||||||
@ -286,7 +275,7 @@ fn build_commands(matches: &ArgMatches) -> Result<Vec<Command>> {
|
|||||||
{
|
{
|
||||||
let dupes = find_dupes(param_names_and_values.iter().map(|(name, _)| *name));
|
let dupes = find_dupes(param_names_and_values.iter().map(|(name, _)| *name));
|
||||||
if !dupes.is_empty() {
|
if !dupes.is_empty() {
|
||||||
error(&format!("duplicate parameter names: {}", &dupes.join(", ")))
|
bail!("Duplicate parameter names: {}", &dupes.join(", "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let command_list = command_strings.collect::<Vec<&str>>();
|
let command_list = command_strings.collect::<Vec<&str>>();
|
||||||
@ -300,16 +289,18 @@ fn build_commands(matches: &ArgMatches) -> Result<Vec<Command>> {
|
|||||||
.collect();
|
.collect();
|
||||||
let param_space_size = dimensions.iter().product();
|
let param_space_size = dimensions.iter().product();
|
||||||
if param_space_size == 0 {
|
if param_space_size == 0 {
|
||||||
return Vec::new();
|
return Ok(Vec::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
// `--command-name` should appear exactly once or exactly B times,
|
// `--command-name` should appear exactly once or exactly B times,
|
||||||
// where B is the total number of benchmarks.
|
// where B is the total number of benchmarks.
|
||||||
let command_name_count = command_names.len();
|
let command_name_count = command_names.len();
|
||||||
if command_name_count > 1 && command_name_count != param_space_size {
|
if command_name_count > 1 && command_name_count != param_space_size {
|
||||||
let err =
|
return Err(OptionsError::UnexpectedCommandNameCount(
|
||||||
OptionsError::UnexpectedCommandNameCount(command_name_count, param_space_size);
|
command_name_count,
|
||||||
error(&err.to_string());
|
param_space_size,
|
||||||
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut i = 0;
|
let mut i = 0;
|
||||||
@ -346,12 +337,11 @@ fn build_commands(matches: &ArgMatches) -> Result<Vec<Command>> {
|
|||||||
break 'outer;
|
break 'outer;
|
||||||
}
|
}
|
||||||
|
|
||||||
commands
|
Ok(commands)
|
||||||
} else {
|
} else {
|
||||||
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
let command_names = command_names.map_or(vec![], |names| names.collect::<Vec<&str>>());
|
||||||
if command_names.len() > command_strings.len() {
|
if command_names.len() > command_strings.len() {
|
||||||
let err = OptionsError::TooManyCommandNames(command_strings.len());
|
return Err(OptionsError::TooManyCommandNames(command_strings.len()).into());
|
||||||
error(&err.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let command_list = command_strings.collect::<Vec<&str>>();
|
let command_list = command_strings.collect::<Vec<&str>>();
|
||||||
@ -359,7 +349,7 @@ fn build_commands(matches: &ArgMatches) -> Result<Vec<Command>> {
|
|||||||
for (i, s) in command_list.iter().enumerate() {
|
for (i, s) in command_list.iter().enumerate() {
|
||||||
commands.push(Command::new(command_names.get(i).copied(), s));
|
commands.push(Command::new(command_names.get(i).copied(), s));
|
||||||
}
|
}
|
||||||
commands
|
Ok(commands)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,7 +379,7 @@ fn test_build_commands_cross_product() {
|
|||||||
"echo {par1} {par2}",
|
"echo {par1} {par2}",
|
||||||
"printf '%s\n' {par1} {par2}",
|
"printf '%s\n' {par1} {par2}",
|
||||||
]);
|
]);
|
||||||
let result = build_commands(&matches);
|
let result = build_commands(&matches).unwrap();
|
||||||
|
|
||||||
// Iteration order: command list first, then parameters in listed order (here, "par1" before
|
// Iteration order: command list first, then parameters in listed order (here, "par1" before
|
||||||
// "par2", which is distinct from their sorted order), with parameter values in listed order.
|
// "par2", which is distinct from their sorted order), with parameter values in listed order.
|
||||||
@ -423,7 +413,7 @@ fn test_build_parameter_list_commands() {
|
|||||||
"--command-name",
|
"--command-name",
|
||||||
"name-{foo}",
|
"name-{foo}",
|
||||||
]);
|
]);
|
||||||
let commands = build_commands(&matches);
|
let commands = build_commands(&matches).unwrap();
|
||||||
assert_eq!(commands.len(), 2);
|
assert_eq!(commands.len(), 2);
|
||||||
assert_eq!(commands[0].get_name(), "name-1");
|
assert_eq!(commands[0].get_name(), "name-1");
|
||||||
assert_eq!(commands[1].get_name(), "name-2");
|
assert_eq!(commands[1].get_name(), "name-2");
|
||||||
@ -445,7 +435,7 @@ fn test_build_parameter_range_commands() {
|
|||||||
"--command-name",
|
"--command-name",
|
||||||
"name-{val}",
|
"name-{val}",
|
||||||
]);
|
]);
|
||||||
let commands = build_commands(&matches);
|
let commands = build_commands(&matches).unwrap();
|
||||||
assert_eq!(commands.len(), 2);
|
assert_eq!(commands.len(), 2);
|
||||||
assert_eq!(commands[0].get_name(), "name-1");
|
assert_eq!(commands[0].get_name(), "name-1");
|
||||||
assert_eq!(commands[1].get_name(), "name-2");
|
assert_eq!(commands[1].get_name(), "name-2");
|
||||||
|
12
src/shell.rs
12
src/shell.rs
@ -1,9 +1,10 @@
|
|||||||
use std::io;
|
|
||||||
use std::process::{ExitStatus, Stdio};
|
use std::process::{ExitStatus, Stdio};
|
||||||
|
|
||||||
use crate::options::Shell;
|
use crate::options::Shell;
|
||||||
use crate::timer::get_cpu_timer;
|
use crate::timer::get_cpu_timer;
|
||||||
|
|
||||||
|
use anyhow::{Context, Result};
|
||||||
|
|
||||||
/// Used to indicate the result of running a command
|
/// Used to indicate the result of running a command
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ExecuteResult {
|
pub struct ExecuteResult {
|
||||||
@ -24,7 +25,7 @@ pub fn execute_and_time(
|
|||||||
stderr: Stdio,
|
stderr: Stdio,
|
||||||
command: &str,
|
command: &str,
|
||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
) -> io::Result<ExecuteResult> {
|
) -> Result<ExecuteResult> {
|
||||||
let mut child = run_shell_command(stdout, stderr, command, shell)?;
|
let mut child = run_shell_command(stdout, stderr, command, shell)?;
|
||||||
let cpu_timer = get_cpu_timer(&child);
|
let cpu_timer = get_cpu_timer(&child);
|
||||||
let status = child.wait()?;
|
let status = child.wait()?;
|
||||||
@ -44,7 +45,7 @@ pub fn execute_and_time(
|
|||||||
stderr: Stdio,
|
stderr: Stdio,
|
||||||
command: &str,
|
command: &str,
|
||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
) -> io::Result<ExecuteResult> {
|
) -> Result<ExecuteResult> {
|
||||||
let cpu_timer = get_cpu_timer();
|
let cpu_timer = get_cpu_timer();
|
||||||
|
|
||||||
let status = run_shell_command(stdout, stderr, command, shell)?;
|
let status = run_shell_command(stdout, stderr, command, shell)?;
|
||||||
@ -65,7 +66,7 @@ fn run_shell_command(
|
|||||||
stderr: Stdio,
|
stderr: Stdio,
|
||||||
command: &str,
|
command: &str,
|
||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
) -> io::Result<std::process::ExitStatus> {
|
) -> Result<std::process::ExitStatus> {
|
||||||
shell
|
shell
|
||||||
.command()
|
.command()
|
||||||
.arg("-c")
|
.arg("-c")
|
||||||
@ -78,6 +79,7 @@ fn run_shell_command(
|
|||||||
.stdout(stdout)
|
.stdout(stdout)
|
||||||
.stderr(stderr)
|
.stderr(stderr)
|
||||||
.status()
|
.status()
|
||||||
|
.with_context(|| format!("Failed to run command '{}'", command))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run a Windows shell command using `cmd.exe /C`
|
/// Run a Windows shell command using `cmd.exe /C`
|
||||||
@ -87,7 +89,7 @@ fn run_shell_command(
|
|||||||
stderr: Stdio,
|
stderr: Stdio,
|
||||||
command: &str,
|
command: &str,
|
||||||
shell: &Shell,
|
shell: &Shell,
|
||||||
) -> io::Result<std::process::Child> {
|
) -> Result<std::process::Child> {
|
||||||
shell
|
shell
|
||||||
.command()
|
.command()
|
||||||
.arg("/C")
|
.arg("/C")
|
||||||
|
Loading…
Reference in New Issue
Block a user