diff --git a/Cargo.lock b/Cargo.lock index 752cd5c6a..2cf433c29 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -98,6 +98,7 @@ name = "hyperfine" version = "0.3.0" dependencies = [ "approx 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "atty 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.29.2 (registry+https://github.com/rust-lang/crates.io-index)", "colored 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "indicatif 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index ef8cd7aab..2a8297265 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ colored = "1.6" indicatif = "0.9" statistical = "0.1" libc = "0.2" +atty = "0.2.2" [dependencies.clap] version = "2" diff --git a/src/hyperfine/benchmark.rs b/src/hyperfine/benchmark.rs index f98198c2d..304473c4c 100644 --- a/src/hyperfine/benchmark.rs +++ b/src/hyperfine/benchmark.rs @@ -5,8 +5,8 @@ use std::time::Instant; use colored::*; use statistical::{mean, standard_deviation}; -use hyperfine::internal::{get_progress_bar, max, min, CmdFailureAction, HyperfineOptions, Second, - MIN_EXECUTION_TIME}; +use hyperfine::internal::{get_progress_bar, max, min, CmdFailureAction, HyperfineOptions, + OutputStyleOption, Second, MIN_EXECUTION_TIME}; use hyperfine::warnings::Warnings; use hyperfine::format::{format_duration, format_duration_unit}; use hyperfine::cputime::{cpu_time_interval, get_cpu_times}; @@ -88,9 +88,9 @@ pub fn time_shell_command( } /// Measure the average shell spawning time -pub fn mean_shell_spawning_time() -> io::Result { +pub fn mean_shell_spawning_time(style: &OutputStyleOption) -> io::Result { const COUNT: u64 = 200; - let progress_bar = get_progress_bar(COUNT, "Measuring shell spawning time"); + let progress_bar = get_progress_bar(COUNT, "Measuring shell spawning time", style); let mut times_real: Vec = vec![]; let mut times_user: Vec = vec![]; @@ -162,7 +162,11 @@ pub fn run_benchmark( // Warmup phase if options.warmup_count > 0 { - let progress_bar = get_progress_bar(options.warmup_count, "Performing warmup runs"); + let progress_bar = get_progress_bar( + options.warmup_count, + "Performing warmup runs", + &options.output_style, + ); for _ in 0..options.warmup_count { let _ = time_shell_command(cmd, options.failure_action, None)?; @@ -172,7 +176,11 @@ pub fn run_benchmark( } // Set up progress bar (and spinner for initial measurement) - let progress_bar = get_progress_bar(options.min_runs, "Initial time measurement"); + let progress_bar = get_progress_bar( + options.min_runs, + "Initial time measurement", + &options.output_style, + ); // Run init / cleanup command run_preparation_command(&options.preparation_command)?; @@ -286,6 +294,7 @@ pub fn run_benchmark( if !warnings.is_empty() { eprintln!(" "); + for warning in &warnings { eprintln!(" {}: {}", "Warning".yellow(), warning); } diff --git a/src/hyperfine/internal.rs b/src/hyperfine/internal.rs index 1b1507cfd..f385a2101 100644 --- a/src/hyperfine/internal.rs +++ b/src/hyperfine/internal.rs @@ -16,6 +16,16 @@ pub enum CmdFailureAction { Ignore, } +/// Output style type option +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum OutputStyleOption { + /// Do not output with colors or any special formatting + Basic, + + /// Output with full color and formatting + Full, +} + /// A set of options for hyperfine pub struct HyperfineOptions { /// Number of warmup runs @@ -32,6 +42,9 @@ pub struct HyperfineOptions { /// Command to run before each timing run pub preparation_command: Option, + + /// What color mode to use for output + pub output_style: OutputStyleOption, } impl Default for HyperfineOptions { @@ -42,17 +55,24 @@ impl Default for HyperfineOptions { min_time_sec: 3.0, failure_action: CmdFailureAction::RaiseError, preparation_command: None, + output_style: OutputStyleOption::Full, } } } /// Return a pre-configured progress bar -pub fn get_progress_bar(length: u64, msg: &str) -> ProgressBar { - let progressbar_style = ProgressStyle::default_spinner() - .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏") - .template(" {spinner} {msg:<30} {wide_bar} ETA {eta_precise}"); - - let progress_bar = ProgressBar::new(length); +pub fn get_progress_bar(length: u64, msg: &str, option: &OutputStyleOption) -> ProgressBar { + let progressbar_style = match option { + &OutputStyleOption::Basic => ProgressStyle::default_bar(), + &OutputStyleOption::Full => ProgressStyle::default_spinner() + .tick_chars("⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏") + .template(" {spinner} {msg:<30} {wide_bar} ETA {eta_precise}"), + }; + + let progress_bar = match option { + &OutputStyleOption::Basic => ProgressBar::hidden(), + &OutputStyleOption::Full => ProgressBar::new(length), + }; progress_bar.set_style(progressbar_style.clone()); progress_bar.enable_steady_tick(80); progress_bar.set_message(msg); diff --git a/src/main.rs b/src/main.rs index 0d9bd3d3c..15b3777a2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,5 @@ +extern crate atty; + #[macro_use] extern crate clap; extern crate colored; @@ -13,12 +15,13 @@ use std::cmp; use std::error::Error; use std::io; +use atty::Stream; use colored::*; use clap::{App, AppSettings, Arg}; mod hyperfine; -use hyperfine::internal::{CmdFailureAction, HyperfineOptions}; +use hyperfine::internal::{CmdFailureAction, HyperfineOptions, OutputStyleOption}; use hyperfine::benchmark::{mean_shell_spawning_time, run_benchmark}; /// Print error message to stderr and terminate @@ -29,7 +32,7 @@ pub fn error(message: &str) -> ! { /// Runs the benchmark for the given commands fn run(commands: &Vec<&str>, options: &HyperfineOptions) -> io::Result<()> { - let shell_spawning_time = mean_shell_spawning_time()?; + let shell_spawning_time = mean_shell_spawning_time(&options.output_style)?; // Run the benchmarks for (num, cmd) in commands.iter().enumerate() { @@ -43,9 +46,15 @@ fn main() { // Process command line options let mut options = HyperfineOptions::default(); + let clap_color_setting = if atty::is(Stream::Stdout) { + AppSettings::ColoredHelp + } else { + AppSettings::ColorNever + }; + let matches = App::new("hyperfine") .version(crate_version!()) - .setting(AppSettings::ColoredHelp) + .setting(clap_color_setting) .setting(AppSettings::DeriveDisplayOrder) .setting(AppSettings::UnifiedHelpMessage) .max_term_width(90) @@ -90,6 +99,19 @@ fn main() { clearing disk caches, for example.", ), ) + .arg( + Arg::with_name("style") + .long("style") + .short("s") + .takes_value(true) + .value_name("TYPE") + .possible_values(&["auto", "basic", "full"]) + .help( + "Set output style type. If set to 'basic', all colors and special \ + formatting will be disabled. If set to 'auto' when output target is not \ + a TTY, 'basic' is used (default: auto).", + ), + ) .arg( Arg::with_name("ignore-failure") .long("ignore-failure") @@ -114,6 +136,19 @@ fn main() { options.preparation_command = matches.value_of("prepare").map(String::from); + options.output_style = match matches.value_of("style") { + Some("full") => OutputStyleOption::Full, + Some("basic") => OutputStyleOption::Basic, + _ => if atty::is(Stream::Stdout) { + OutputStyleOption::Full + } else { + OutputStyleOption::Basic + }, + }; + + if options.output_style == OutputStyleOption::Basic { + colored::control::set_override(false); + } if matches.is_present("ignore-failure") { options.failure_action = CmdFailureAction::Ignore; }