From eeb572aa84564768311b70e14c5b5354a31bfd29 Mon Sep 17 00:00:00 2001 From: Nicolas Despres Date: Tue, 24 Apr 2012 11:09:28 +0200 Subject: Refactor and test progress status formatting. --- src/build.cc | 114 ++++++++++++++++++++++-------------------------------- src/build.h | 58 +++++++++++++++++++++++++++ src/build_test.cc | 37 +++++++++++++++++- 3 files changed, 140 insertions(+), 69 deletions(-) diff --git a/src/build.cc b/src/build.cc index 303f134..aa44d8b 100644 --- a/src/build.cc +++ b/src/build.cc @@ -16,6 +16,7 @@ #include #include +#include #ifdef _WIN32 #include @@ -33,52 +34,6 @@ #include "subprocess.h" #include "util.h" -/// Tracks the status of a build: completion fraction, printing updates. -struct BuildStatus { - BuildStatus(const BuildConfig& config); - void PlanHasTotalEdges(int total); - void BuildEdgeStarted(Edge* edge); - void BuildEdgeFinished(Edge* edge, bool success, const string& output, - int* start_time, int* end_time); - void BuildFinished(); - - private: - void PrintStatus(Edge* edge); - /// Print the progress status. - /// - /// Get the status from the NINJA_STATUS environment variable if defined. - /// See the user manual for more information about the available - /// placeholders. - /// - /// @return The number of printed characters. - int PrintProgressStatus() const; - - const BuildConfig& config_; - - /// Time the build started. - int64_t start_time_millis_; - /// Time we last printed an update. - int64_t last_update_millis_; - - int started_edges_, finished_edges_, total_edges_; - - bool have_blank_line_; - - /// Map of running edge to time the edge started running. - typedef map RunningEdgeMap; - RunningEdgeMap running_edges_; - - /// Whether we can do fancy terminal control codes. - bool smart_terminal_; - - /// The custom progress status format to use. - const char* progress_status_format_; - -#ifdef _WIN32 - HANDLE console_; -#endif -}; - BuildStatus::BuildStatus(const BuildConfig& config) : config_(config), start_time_millis_(GetTimeMillis()), @@ -186,68 +141,91 @@ void BuildStatus::BuildFinished() { printf("\n"); } -int BuildStatus::PrintProgressStatus() const { - // Print the custom status. - const int kBUFF_SIZE = 1024; - char buff[kBUFF_SIZE] = { '\0' }; +int BuildStatus::FormatProgressStatus(const char* progress_status_format, + char* buffer, + const int buffer_size, + string* err) const { int i = 0; - for (const char* s = progress_status_format_; - *s != '\0' && i < kBUFF_SIZE; + for (const char* s = progress_status_format; + *s != '\0' && i < buffer_size; ++s) { if (*s == '%') { ++s; switch(*s) { case '%': - buff[i] = '%'; + buffer[i] = '%'; ++i; break; // Started edges. case 's': - i += snprintf(&buff[i], kBUFF_SIZE - i, "%d", started_edges_); + i += snprintf(&buffer[i], buffer_size - i, "%d", started_edges_); break; // Total edges. case 't': - i += snprintf(&buff[i], kBUFF_SIZE - i, "%d", total_edges_); + i += snprintf(&buffer[i], buffer_size - i, "%d", total_edges_); break; // Running edges. case 'r': - i += snprintf(&buff[i], kBUFF_SIZE - i, "%d", + i += snprintf(&buffer[i], buffer_size - i, "%d", started_edges_ - finished_edges_); break; // Unstarted edges. case 'u': - i += snprintf(&buff[i], kBUFF_SIZE - i, "%d", + i += snprintf(&buffer[i], buffer_size - i, "%d", total_edges_ - started_edges_); break; // Finished edges. case 'f': - i += snprintf(&buff[i], kBUFF_SIZE - i, "%d", finished_edges_); + i += snprintf(&buffer[i], buffer_size - i, "%d", finished_edges_); break; default: - return printf("!! unknown placeholders '%c' in NINJA_STATUS !! ", - *s); - break; + if (err != NULL) { + ostringstream oss; + oss << "unknown placeholders '%" << *s << "' in NINJA_STATUS"; + *err = oss.str(); + } + return -1; } } else { - buff[i] = *s; + buffer[i] = *s; ++i; } } - if (i >= kBUFF_SIZE) - return printf("!! custom NINJA_STATUS exceed buffer size %d !! ", - kBUFF_SIZE); - else { - buff[i] = '\0'; - return printf("%s", buff); + if (i >= buffer_size) { + if (err != NULL) { + ostringstream oss; + oss << "custom NINJA_STATUS exceed buffer size " << buffer_size; + *err = oss.str(); + } + return -1; + } else { + buffer[i] = '\0'; + if (err != NULL) + *err = ""; + return i; } } +int BuildStatus::PrintProgressStatus() const { + const int kBUFF_SIZE = 1024; + char buff[kBUFF_SIZE] = { '\0' }; + string err; + int progress_chars = FormatProgressStatus(progress_status_format_, + buff, + kBUFF_SIZE, + &err); + if (progress_chars < 0) + return printf("!! %s !! ", err.c_str()); + else + return printf("%s", buff); +} + void BuildStatus::PrintStatus(Edge* edge) { if (config_.verbosity == BuildConfig::QUIET) return; diff --git a/src/build.h b/src/build.h index 179fca6..8078910 100644 --- a/src/build.h +++ b/src/build.h @@ -149,4 +149,62 @@ private: void operator=(const Builder &other); // DO NOT IMPLEMENT }; +/// Tracks the status of a build: completion fraction, printing updates. +struct BuildStatus { + BuildStatus(const BuildConfig& config); + void PlanHasTotalEdges(int total); + void BuildEdgeStarted(Edge* edge); + void BuildEdgeFinished(Edge* edge, bool success, const string& output, + int* start_time, int* end_time); + void BuildFinished(); + + /// Format the progress status string by replacing the placeholders. + /// See the user manual for more information about the available + /// placeholders. + /// @param progress_status_format_ The format of the progress status. + /// @param buffer The buffer where is stored the formatted progress status. + /// @param buffer_size The size of the given @a buffer. + /// @param err The error message if -1 is returned. + /// @return The number of characters inserted in @a buffer and -1 on error. + int FormatProgressStatus(const char* progress_status_format, + char* buffer, + const int buffer_size, + string* err) const; + + private: + void PrintStatus(Edge* edge); + + /// Print the progress status. + /// + /// Get the status from the NINJA_STATUS environment variable if defined. + /// + /// @return The number of printed characters. + int PrintProgressStatus() const; + + const BuildConfig& config_; + + /// Time the build started. + int64_t start_time_millis_; + /// Time we last printed an update. + int64_t last_update_millis_; + + int started_edges_, finished_edges_, total_edges_; + + bool have_blank_line_; + + /// Map of running edge to time the edge started running. + typedef map RunningEdgeMap; + RunningEdgeMap running_edges_; + + /// Whether we can do fancy terminal control codes. + bool smart_terminal_; + + /// The custom progress status format to use. + const char* progress_status_format_; + +#ifdef _WIN32 + HANDLE console_; +#endif +}; + #endif // NINJA_BUILD_H_ diff --git a/src/build_test.cc b/src/build_test.cc index 467a908..f3f5472 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -179,7 +179,7 @@ TEST_F(PlanTest, DependencyCycle) { struct BuildTest : public StateTestWithBuiltinRules, public CommandRunner { BuildTest() : config_(MakeConfig()), builder_(&state_, config_), now_(1), - last_command_(NULL) { + last_command_(NULL), status_(config_) { builder_.disk_interface_ = &fs_; builder_.command_runner_.reset(this); AssertParse(&state_, @@ -219,6 +219,7 @@ struct BuildTest : public StateTestWithBuiltinRules, vector commands_ran_; Edge* last_command_; + BuildStatus status_; }; void BuildTest::Dirty(const string& path) { @@ -1022,3 +1023,37 @@ TEST_F(BuildTest, PhonyWithNoInputs) { EXPECT_EQ("", err); ASSERT_EQ(1u, commands_ran_.size()); } + +TEST_F(BuildTest, StatusFormatBufferTooSmall) { + const int kBUFF_SIZE = 5; + char buff[kBUFF_SIZE] = { '\0' }; + string err; + + EXPECT_EQ(-1, status_.FormatProgressStatus("0123456789", + buff, kBUFF_SIZE, + &err)); + EXPECT_EQ("custom NINJA_STATUS exceed buffer size 5", err); +} + +TEST_F(BuildTest, StatusFormatWrongPlaceholder) { + const int kBUFF_SIZE = 1024; + char buff[kBUFF_SIZE] = { '\0' }; + string err; + + EXPECT_EQ(-1, status_.FormatProgressStatus("[%r/%X]", + buff, kBUFF_SIZE, + &err)); + EXPECT_EQ("unknown placeholders '%X' in NINJA_STATUS", err); +} + +TEST_F(BuildTest, StatusFormatReplacePlaceholder) { + const int kBUFF_SIZE = 1024; + char buff[kBUFF_SIZE] = { '\0' }; + string err; + + EXPECT_EQ(18, status_.FormatProgressStatus("[%%/s%s/t%t/r%r/u%u/f%f]", + buff, kBUFF_SIZE, + &err)); + EXPECT_EQ("", err); + EXPECT_STREQ("[%/s0/t0/r0/u0/f0]", buff); +} -- cgit v0.12