diff --git a/AUTHORS b/AUTHORS index 0e409220..5a545fa5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -24,6 +24,7 @@ Jussi Knuuttila Kaito Udagawa Lei Xu Matt Clarkson +Nick Hutchinson Oleksandr Sochka Paul Redmond Radoslav Yovchev diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 4bff126f..33cd941f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -40,6 +40,7 @@ Kaito Udagawa Kai Wolf Lei Xu Matt Clarkson +Nick Hutchinson Oleksandr Sochka Pascal Leroy Paul Redmond diff --git a/src/benchmark.cc b/src/benchmark.cc index a5073f5a..da195f9d 100644 --- a/src/benchmark.cc +++ b/src/benchmark.cc @@ -34,6 +34,7 @@ #include #include "check.h" +#include "colorprint.h" #include "commandlineflags.h" #include "complexity.h" #include "log.h" @@ -82,7 +83,12 @@ DEFINE_string(benchmark_out_format, "json", DEFINE_string(benchmark_out, "", "The file to write additonal output to"); -DEFINE_bool(color_print, true, "Enables colorized logging."); +DEFINE_string(benchmark_color, "auto", + "Whether to use colors in the output. Valid values: " + "'true'/'yes'/1, 'false'/'no'/0, and 'auto'. 'auto' means to use " + "colors if the output is being sent to a terminal and the TERM " + "environment variable is set to a terminal type that supports " + "colors."); DEFINE_int32(v, 0, "The level of verbose logging to output"); @@ -546,8 +552,14 @@ size_t RunSpecifiedBenchmarks(BenchmarkReporter* console_reporter, std::unique_ptr default_console_reporter; std::unique_ptr default_file_reporter; if (!console_reporter) { - auto output_opts = FLAGS_color_print ? ConsoleReporter::OO_Color - : ConsoleReporter::OO_None; + auto output_opts = ConsoleReporter::OO_None; + if (FLAGS_benchmark_color == "auto") + output_opts = IsColorTerminal() ? ConsoleReporter::OO_Color + : ConsoleReporter::OO_None; + else + output_opts = IsTruthyFlagValue(FLAGS_benchmark_color) + ? ConsoleReporter::OO_Color + : ConsoleReporter::OO_None; default_console_reporter = internal::CreateReporter( FLAGS_benchmark_format, output_opts); console_reporter = default_console_reporter.get(); @@ -602,7 +614,7 @@ void PrintUsageAndExit() { " [--benchmark_format=]\n" " [--benchmark_out=]\n" " [--benchmark_out_format=]\n" - " [--color_print={true|false}]\n" + " [--benchmark_color={auto|true|false}]\n" " [--v=]\n"); exit(0); } @@ -627,8 +639,12 @@ void ParseCommandLineFlags(int* argc, char** argv) { &FLAGS_benchmark_out) || ParseStringFlag(argv[i], "benchmark_out_format", &FLAGS_benchmark_out_format) || - ParseBoolFlag(argv[i], "color_print", - &FLAGS_color_print) || + ParseStringFlag(argv[i], "benchmark_color", + &FLAGS_benchmark_color) || + // "color_print" is the deprecated name for "benchmark_color". + // TODO: Remove this. + ParseStringFlag(argv[i], "color_print", + &FLAGS_benchmark_color) || ParseInt32Flag(argv[i], "v", &FLAGS_v)) { for (int j = i; j != *argc; ++j) argv[j] = argv[j + 1]; @@ -643,6 +659,9 @@ void ParseCommandLineFlags(int* argc, char** argv) { if (*flag != "console" && *flag != "json" && *flag != "csv") { PrintUsageAndExit(); } + if (FLAGS_benchmark_color.empty()) { + PrintUsageAndExit(); + } } int InitializeStreams() { diff --git a/src/colorprint.cc b/src/colorprint.cc index b7d316fe..f24e6f87 100644 --- a/src/colorprint.cc +++ b/src/colorprint.cc @@ -16,16 +16,20 @@ #include #include -#include -#include +#include +#include #include +#include #include "check.h" #include "internal_macros.h" #ifdef BENCHMARK_OS_WINDOWS +#include #include -#endif +#else +#include +#endif // BENCHMARK_OS_WINDOWS namespace benchmark { namespace { @@ -151,4 +155,34 @@ void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, va_list arg } +bool IsColorTerminal() { +#if BENCHMARK_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return 0 != _isatty(_fileno(stdout)); +#else + // On non-Windows platforms, we rely on the TERM variable. This list of + // supported TERM values is copied from Google Test: + // . + const char* const SUPPORTED_TERM_VALUES[] = { + "xterm", "xterm-color", "xterm-256color", + "screen", "screen-256color", "tmux", + "tmux-256color", "rxvt-unicode", "rxvt-unicode-256color", + "linux", "cygwin", + }; + + const char* const term = getenv("TERM"); + + bool term_supports_color = false; + for (const char* candidate : SUPPORTED_TERM_VALUES) { + if (term && 0 == strcmp(term, candidate)) { + term_supports_color = true; + break; + } + } + + return 0 != isatty(fileno(stdout)) && term_supports_color; +#endif // BENCHMARK_OS_WINDOWS +} + } // end namespace benchmark diff --git a/src/colorprint.h b/src/colorprint.h index d9247954..30d4300c 100644 --- a/src/colorprint.h +++ b/src/colorprint.h @@ -23,6 +23,10 @@ std::string FormatString(const char* msg, ...); void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, va_list args); void ColorPrintf(std::ostream& out, LogColor color, const char* fmt, ...); +// Returns true if stdout appears to be a terminal that supports colored +// output, false otherwise. +bool IsColorTerminal(); + } // end namespace benchmark #endif // BENCHMARK_COLORPRINT_H_ diff --git a/src/commandlineflags.cc b/src/commandlineflags.cc index 3e9a37a7..c6fba55e 100644 --- a/src/commandlineflags.cc +++ b/src/commandlineflags.cc @@ -14,6 +14,7 @@ #include "commandlineflags.h" +#include #include #include #include @@ -74,17 +75,6 @@ bool ParseDouble(const std::string& src_text, const char* str, double* value) { return true; } -inline const char* GetEnv(const char* name) { -#if defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) - // Environment variables which we programmatically clear will be set to the - // empty string rather than unset (nullptr). Handle that case. - const char* const env = getenv(name); - return (env != nullptr && env[0] != '\0') ? env : nullptr; -#else - return getenv(name); -#endif -} - // Returns the name of the environment variable corresponding to the // given flag. For example, FlagToEnvVar("foo") will return // "BENCHMARK_FOO" in the open-source version. @@ -104,7 +94,7 @@ static std::string FlagToEnvVar(const char* flag) { // The value is considered true iff it's not "0". bool BoolFromEnv(const char* flag, bool default_value) { const std::string env_var = FlagToEnvVar(flag); - const char* const string_value = GetEnv(env_var.c_str()); + const char* const string_value = getenv(env_var.c_str()); return string_value == nullptr ? default_value : strcmp(string_value, "0") != 0; } @@ -113,7 +103,7 @@ bool BoolFromEnv(const char* flag, bool default_value) { // doesn't represent a valid 32-bit integer, returns default_value. int32_t Int32FromEnv(const char* flag, int32_t default_value) { const std::string env_var = FlagToEnvVar(flag); - const char* const string_value = GetEnv(env_var.c_str()); + const char* const string_value = getenv(env_var.c_str()); if (string_value == nullptr) { // The environment variable is not set. return default_value; @@ -133,7 +123,7 @@ int32_t Int32FromEnv(const char* flag, int32_t default_value) { // the given flag; if it's not set, returns default_value. const char* StringFromEnv(const char* flag, const char* default_value) { const std::string env_var = FlagToEnvVar(flag); - const char* const value = GetEnv(env_var.c_str()); + const char* const value = getenv(env_var.c_str()); return value == nullptr ? default_value : value; } @@ -175,7 +165,7 @@ bool ParseBoolFlag(const char* str, const char* flag, bool* value) { if (value_str == nullptr) return false; // Converts the string value to a bool. - *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + *value = IsTruthyFlagValue(value_str); return true; } @@ -217,4 +207,12 @@ bool ParseStringFlag(const char* str, const char* flag, std::string* value) { bool IsFlag(const char* str, const char* flag) { return (ParseFlagValue(str, flag, true) != nullptr); } + +bool IsTruthyFlagValue(const std::string& str) { + if (str.empty()) + return true; + char ch = str[0]; + return isalnum(ch) && + !(ch == '0' || ch == 'f' || ch == 'F' || ch == 'n' || ch == 'N'); +} } // end namespace benchmark diff --git a/src/commandlineflags.h b/src/commandlineflags.h index 34b9c6f3..945c9a9f 100644 --- a/src/commandlineflags.h +++ b/src/commandlineflags.h @@ -38,8 +38,7 @@ const char* StringFromEnv(const char* flag, const char* default_val); // Parses a string for a bool flag, in the form of either // "--flag=value" or "--flag". // -// In the former case, the value is taken as true as long as it does -// not start with '0', 'f', or 'F'. +// In the former case, the value is taken as true if it passes IsTruthyValue(). // // In the latter case, the value is taken as true. // @@ -71,6 +70,10 @@ bool ParseStringFlag(const char* str, const char* flag, std::string* value); // Returns true if the string matches the flag. bool IsFlag(const char* str, const char* flag); +// Returns true unless value starts with one of: '0', 'f', 'F', 'n' or 'N', or +// some non-alphanumeric character. As a special case, also returns true if +// value is the empty string. +bool IsTruthyFlagValue(const std::string& value); } // end namespace benchmark #endif // BENCHMARK_COMMANDLINEFLAGS_H_