summaryrefslogtreecommitdiffstats
path: root/src/build.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/build.cc')
-rw-r--r--src/build.cc106
1 files changed, 70 insertions, 36 deletions
diff --git a/src/build.cc b/src/build.cc
index 0fc387c..e1aaad1 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -26,6 +26,10 @@
#include <sys/time.h>
#endif
+#if defined(__SVR4) && defined(__sun)
+#include <sys/termios.h>
+#endif
+
#include "build_log.h"
#include "disk_interface.h"
#include "graph.h"
@@ -37,7 +41,9 @@ 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) {
+ have_blank_line_(true), progress_status_format_(NULL),
+ overall_rate_(), current_rate_(),
+ current_rate_average_count_(config.parallelism) {
#ifndef _WIN32
const char* term = getenv("TERM");
smart_terminal_ = isatty(1) && term && string(term) != "dumb";
@@ -171,6 +177,25 @@ string BuildStatus::FormatProgressStatus(const char* progress_status_format) con
out += buf;
break;
+ // Overall finished edges per second.
+ case 'o':
+ overall_rate_.UpdateRate(finished_edges_, finished_edges_);
+ overall_rate_.snprinfRate(buf, "%.1f");
+ out += buf;
+ break;
+
+ // Current rate, average over the last '-j' jobs.
+ case 'c':
+ // TODO use sliding window?
+ if (finished_edges_ > current_rate_.last_update() &&
+ finished_edges_ - current_rate_.last_update() == current_rate_average_count_) {
+ current_rate_.UpdateRate(current_rate_average_count_, finished_edges_);
+ current_rate_.Restart();
+ }
+ current_rate_.snprinfRate(buf, "%.0f");
+ out += buf;
+ break;
+
default: {
Fatal("unknown placeholder '%%%c' in $NINJA_STATUS", *s);
return "";
@@ -208,50 +233,57 @@ void BuildStatus::PrintStatus(Edge* edge) {
#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) {
- const int kMargin = 3; // Space for "...".
#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) {
- if (to_print.size() + kMargin > size.ws_col) {
- int elide_size = (size.ws_col - kMargin) / 2;
- to_print = to_print.substr(0, elide_size)
- + "..."
- + to_print.substr(to_print.size() - elide_size, elide_size);
- }
+ 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<size_t>(csbi.dwSize.X) - 1;
- if (to_print.size() + kMargin > width) {
- int elide_size = (width - kMargin) / 2;
- to_print = to_print.substr(0, elide_size)
- + "..."
- + to_print.substr(to_print.size() - elide_size, elide_size);
- }
+ to_print = ElideMiddle(to_print, width);
#endif
}
- printf("%s", to_print.c_str());
-
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
- // Clear to end of line.
+ // 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);
- int num_spaces = csbi.dwSize.X - 1 - csbi.dwCursorPosition.X;
- printf("%*s", num_spaces, "");
+ 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("\n");
+ printf("%s\n", to_print.c_str());
}
}
@@ -378,7 +410,7 @@ void Plan::NodeFinished(Node* node) {
}
}
-void Plan::CleanNode(BuildLog* build_log, Node* node) {
+void Plan::CleanNode(DependencyScan* scan, Node* node) {
node->set_dirty(false);
for (vector<Edge*>::const_iterator ei = node->out_edges().begin();
@@ -394,10 +426,11 @@ void Plan::CleanNode(BuildLog* build_log, Node* node) {
end = (*ei)->inputs_.end() - (*ei)->order_only_deps_;
if (find_if(begin, end, mem_fun(&Node::dirty)) == end) {
// Recompute most_recent_input and command.
- TimeStamp most_recent_input = 1;
- for (vector<Node*>::iterator ni = begin; ni != end; ++ni)
- if ((*ni)->mtime() > most_recent_input)
- most_recent_input = (*ni)->mtime();
+ Node* most_recent_input = NULL;
+ for (vector<Node*>::iterator ni = begin; ni != end; ++ni) {
+ if (!most_recent_input || (*ni)->mtime() > most_recent_input->mtime())
+ most_recent_input = *ni;
+ }
string command = (*ei)->EvaluateCommand(true);
// Now, recompute the dirty state of each output.
@@ -407,12 +440,12 @@ void Plan::CleanNode(BuildLog* build_log, Node* node) {
if (!(*ni)->dirty())
continue;
- if ((*ei)->RecomputeOutputDirty(build_log, most_recent_input, NULL, command,
- *ni)) {
+ if (scan->RecomputeOutputDirty(*ei, most_recent_input,
+ command, *ni)) {
(*ni)->MarkDirty();
all_outputs_clean = false;
} else {
- CleanNode(build_log, *ni);
+ CleanNode(scan, *ni);
}
}
@@ -524,11 +557,11 @@ struct DryRunCommandRunner : public CommandRunner {
queue<Edge*> finished_;
};
-Builder::Builder(State* state, const BuildConfig& config)
- : state_(state), config_(config) {
- disk_interface_ = new RealDiskInterface;
+Builder::Builder(State* state, const BuildConfig& config,
+ BuildLog* log, DiskInterface* disk_interface)
+ : state_(state), config_(config), disk_interface_(disk_interface),
+ scan_(state, log, disk_interface) {
status_ = new BuildStatus(config);
- log_ = state->build_log_;
}
Builder::~Builder() {
@@ -576,7 +609,7 @@ Node* Builder::AddTarget(const string& name, string* err) {
bool Builder::AddTarget(Node* node, string* err) {
node->StatIfNecessary(disk_interface_);
if (Edge* in_edge = node->in_edge()) {
- if (!in_edge->RecomputeDirty(state_, disk_interface_, err))
+ if (!scan_.RecomputeDirty(in_edge, err))
return false;
if (in_edge->outputs_ready())
return true; // Nothing to do.
@@ -728,7 +761,7 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
// The rule command did not change the output. Propagate the clean
// state through the build graph.
// Note that this also applies to nonexistent outputs (mtime == 0).
- plan_.CleanNode(log_, *i);
+ plan_.CleanNode(&scan_, *i);
node_cleaned = true;
}
}
@@ -767,6 +800,7 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) {
int start_time, end_time;
status_->BuildEdgeFinished(edge, success, output, &start_time, &end_time);
- if (success && log_)
- log_->RecordCommand(edge, start_time, end_time, restat_mtime);
+ if (success && scan_.build_log())
+ scan_.build_log()->RecordCommand(edge, start_time, end_time, restat_mtime);
}
+