From 10d1c939ae788bfb410eb37c7e71da2be07797f9 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Mon, 8 Apr 2013 20:54:28 -0700 Subject: move single-line printing to new class --- configure.py | 1 + src/build.cc | 97 +++++----------------------------------------------- src/build.h | 7 ++-- src/line_printer.cc | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/line_printer.h | 37 ++++++++++++++++++++ src/ninja_test.cc | 14 ++++++++ 6 files changed, 162 insertions(+), 92 deletions(-) create mode 100644 src/line_printer.cc create mode 100644 src/line_printer.h diff --git a/configure.py b/configure.py index 3ea91f0..6db181c 100755 --- a/configure.py +++ b/configure.py @@ -276,6 +276,7 @@ for name in ['build', 'graph', 'graphviz', 'lexer', + 'line_printer', 'manifest_parser', 'metrics', 'state', diff --git a/src/build.cc b/src/build.cc index ae47a50..a98dfda 100644 --- a/src/build.cc +++ b/src/build.cc @@ -19,14 +19,6 @@ #include #include -#ifdef _WIN32 -#include -#else -#include -#include -#include -#endif - #if defined(__SVR4) && defined(__sun) #include #endif @@ -80,25 +72,12 @@ BuildStatus::BuildStatus(const BuildConfig& config) : config_(config), start_time_millis_(GetTimeMillis()), started_edges_(0), finished_edges_(0), total_edges_(0), - have_blank_line_(true), progress_status_format_(NULL), + progress_status_format_(NULL), overall_rate_(), current_rate_(config.parallelism) { -#ifndef _WIN32 - const char* term = getenv("TERM"); - smart_terminal_ = isatty(1) && term && string(term) != "dumb"; -#else - // Disable output buffer. It'd be nice to use line buffering but - // MSDN says: "For some systems, [_IOLBF] provides line - // buffering. However, for Win32, the behavior is the same as _IOFBF - // - Full Buffering." - setvbuf(stdout, NULL, _IONBF, 0); - console_ = GetStdHandle(STD_OUTPUT_HANDLE); - CONSOLE_SCREEN_BUFFER_INFO csbi; - smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); -#endif // Don't do anything fancy in verbose mode. if (config_.verbosity != BuildConfig::NORMAL) - smart_terminal_ = false; + printer_.smart_terminal_ = false; progress_status_format_ = getenv("NINJA_STATUS"); if (!progress_status_format_) @@ -133,11 +112,11 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (config_.verbosity == BuildConfig::QUIET) return; - if (smart_terminal_) + if (printer_.smart_terminal_) PrintStatus(edge); if (!success || !output.empty()) { - if (smart_terminal_) + if (printer_.smart_terminal_) printf("\n"); // Print the command that is spewing before printing its output. @@ -157,7 +136,7 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, // thousands of parallel compile commands.) // TODO: There should be a flag to disable escape code stripping. string final_output; - if (!smart_terminal_) + if (!printer_.smart_terminal_) final_output = StripAnsiEscapeCodes(output); else final_output = output; @@ -165,12 +144,12 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (!final_output.empty()) printf("%s", final_output.c_str()); - have_blank_line_ = true; + printer_.have_blank_line_ = true; } } void BuildStatus::BuildFinished() { - if (smart_terminal_ && !have_blank_line_) + if (printer_.smart_terminal_ && !printer_.have_blank_line_) printf("\n"); } @@ -267,72 +246,14 @@ void BuildStatus::PrintStatus(Edge* edge) { if (to_print.empty() || force_full_command) to_print = edge->GetBinding("command"); -#ifdef _WIN32 - CONSOLE_SCREEN_BUFFER_INFO csbi; - GetConsoleScreenBufferInfo(console_, &csbi); -#endif - - if (smart_terminal_) { -#ifndef _WIN32 - printf("\r"); // Print over previous line, if any. -#else - csbi.dwCursorPosition.X = 0; - SetConsoleCursorPosition(console_, csbi.dwCursorPosition); -#endif - } - if (finished_edges_ == 0) { overall_rate_.Restart(); current_rate_.Restart(); } to_print = FormatProgressStatus(progress_status_format_) + to_print; - if (smart_terminal_ && !force_full_command) { -#ifndef _WIN32 - // Limit output to width of the terminal if provided so we don't cause - // line-wrapping. - winsize size; - if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) { - to_print = ElideMiddle(to_print, size.ws_col); - } -#else - // Don't use the full width or console will move to next line. - size_t width = static_cast(csbi.dwSize.X) - 1; - to_print = ElideMiddle(to_print, width); -#endif - } - - if (smart_terminal_ && !force_full_command) { -#ifndef _WIN32 - printf("%s", to_print.c_str()); - printf("\x1B[K"); // Clear to end of line. - fflush(stdout); - have_blank_line_ = false; -#else - // We don't want to have the cursor spamming back and forth, so - // use WriteConsoleOutput instead which updates the contents of - // the buffer, but doesn't move the cursor position. - GetConsoleScreenBufferInfo(console_, &csbi); - COORD buf_size = { csbi.dwSize.X, 1 }; - COORD zero_zero = { 0, 0 }; - SMALL_RECT target = { csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, - (SHORT)(csbi.dwCursorPosition.X + csbi.dwSize.X - 1), - csbi.dwCursorPosition.Y }; - CHAR_INFO* char_data = new CHAR_INFO[csbi.dwSize.X]; - memset(char_data, 0, sizeof(CHAR_INFO) * csbi.dwSize.X); - for (int i = 0; i < csbi.dwSize.X; ++i) { - char_data[i].Char.AsciiChar = ' '; - char_data[i].Attributes = csbi.wAttributes; - } - for (size_t i = 0; i < to_print.size(); ++i) - char_data[i].Char.AsciiChar = to_print[i]; - WriteConsoleOutput(console_, char_data, buf_size, zero_zero, &target); - delete[] char_data; - have_blank_line_ = false; -#endif - } else { - printf("%s\n", to_print.c_str()); - } + printer_.Print(to_print, + force_full_command ? LinePrinter::FULL : LinePrinter::SHORT); } Plan::Plan() : command_edges_(0), wanted_edges_(0) {} diff --git a/src/build.h b/src/build.h index 5747170..52c277a 100644 --- a/src/build.h +++ b/src/build.h @@ -25,6 +25,7 @@ #include "graph.h" // XXX needed for DependencyScan; should rearrange. #include "exit_status.h" +#include "line_printer.h" #include "metrics.h" #include "util.h" // int64_t @@ -198,14 +199,12 @@ struct BuildStatus { 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_; + /// Prints progress output. + LinePrinter printer_; /// The custom progress status format to use. const char* progress_status_format_; diff --git a/src/line_printer.cc b/src/line_printer.cc new file mode 100644 index 0000000..54f0a5b --- /dev/null +++ b/src/line_printer.cc @@ -0,0 +1,98 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "line_printer.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#endif + +#include "util.h" + +LinePrinter::LinePrinter() : have_blank_line_(true) { +#ifndef _WIN32 + const char* term = getenv("TERM"); + smart_terminal_ = isatty(1) && term && string(term) != "dumb"; +#else + // Disable output buffer. It'd be nice to use line buffering but + // MSDN says: "For some systems, [_IOLBF] provides line + // buffering. However, for Win32, the behavior is the same as _IOFBF + // - Full Buffering." + setvbuf(stdout, NULL, _IONBF, 0); + console_ = GetStdHandle(STD_OUTPUT_HANDLE); + CONSOLE_SCREEN_BUFFER_INFO csbi; + smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); +#endif +} + +void LinePrinter::Print(std::string to_print, LineType type) { +#ifdef _WIN32 + CONSOLE_SCREEN_BUFFER_INFO csbi; + GetConsoleScreenBufferInfo(console_, &csbi); +#endif + + if (smart_terminal_) { +#ifndef _WIN32 + printf("\r"); // Print over previous line, if any. +#else + csbi.dwCursorPosition.X = 0; + SetConsoleCursorPosition(console_, csbi.dwCursorPosition); +#endif + } + + if (smart_terminal_ && type == SHORT) { +#ifdef _WIN32 + // Don't use the full width or console will move to next line. + size_t width = static_cast(csbi.dwSize.X) - 1; + to_print = ElideMiddle(to_print, width); + // We don't want to have the cursor spamming back and forth, so + // use WriteConsoleOutput instead which updates the contents of + // the buffer, but doesn't move the cursor position. + GetConsoleScreenBufferInfo(console_, &csbi); + COORD buf_size = { csbi.dwSize.X, 1 }; + COORD zero_zero = { 0, 0 }; + SMALL_RECT target = { csbi.dwCursorPosition.X, csbi.dwCursorPosition.Y, + (SHORT)(csbi.dwCursorPosition.X + csbi.dwSize.X - 1), + csbi.dwCursorPosition.Y }; + CHAR_INFO* char_data = new CHAR_INFO[csbi.dwSize.X]; + memset(char_data, 0, sizeof(CHAR_INFO) * csbi.dwSize.X); + for (int i = 0; i < csbi.dwSize.X; ++i) { + char_data[i].Char.AsciiChar = ' '; + char_data[i].Attributes = csbi.wAttributes; + } + for (size_t i = 0; i < to_print.size(); ++i) + char_data[i].Char.AsciiChar = to_print[i]; + WriteConsoleOutput(console_, char_data, buf_size, zero_zero, &target); + delete[] char_data; +#else + // Limit output to width of the terminal if provided so we don't cause + // line-wrapping. + winsize size; + if ((ioctl(0, TIOCGWINSZ, &size) == 0) && size.ws_col) { + to_print = ElideMiddle(to_print, size.ws_col); + } + printf("%s", to_print.c_str()); + printf("\x1B[K"); // Clear to end of line. + fflush(stdout); +#endif + + have_blank_line_ = false; + } else { + printf("%s\n", to_print.c_str()); + } +} diff --git a/src/line_printer.h b/src/line_printer.h new file mode 100644 index 0000000..6002667 --- /dev/null +++ b/src/line_printer.h @@ -0,0 +1,37 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef NINJA_LINE_PRINTER_H_ +#define NINJA_LINE_PRINTER_H_ + +#include + +class LinePrinter { + public: + LinePrinter(); + + enum LineType { + FULL, + SHORT + }; + void Print(std::string to_print, LineType type); + + //private: + /// Whether we can do fancy terminal control codes. + bool smart_terminal_; + + bool have_blank_line_; +}; + +#endif // NINJA_LINE_PRINTER_H_ diff --git a/src/ninja_test.cc b/src/ninja_test.cc index 0e0f882..f257ee3 100644 --- a/src/ninja_test.cc +++ b/src/ninja_test.cc @@ -1,3 +1,17 @@ +// Copyright 2013 Google Inc. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + #include "gtest/gtest.h" /// A test result printer that's less wordy than gtest's default. -- cgit v0.12