diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.cc | 64 | ||||
-rw-r--r-- | src/build.h | 37 | ||||
-rw-r--r-- | src/build_log.cc | 5 | ||||
-rw-r--r-- | src/build_log_perftest.cc | 3 | ||||
-rw-r--r-- | src/build_log_test.cc | 6 | ||||
-rw-r--r-- | src/build_test.cc | 54 | ||||
-rw-r--r-- | src/canon_perftest.cc | 1 | ||||
-rw-r--r-- | src/clean.h | 1 | ||||
-rw-r--r-- | src/depfile_parser.cc | 36 | ||||
-rw-r--r-- | src/depfile_parser.in.cc | 2 | ||||
-rw-r--r-- | src/depfile_parser_test.cc | 9 | ||||
-rw-r--r-- | src/getopt.c | 8 | ||||
-rw-r--r-- | src/graph.cc | 7 | ||||
-rw-r--r-- | src/lexer.cc | 110 | ||||
-rw-r--r-- | src/lexer.in.cc | 4 | ||||
-rw-r--r-- | src/manifest_parser.cc (renamed from src/parsers.cc) | 2 | ||||
-rw-r--r-- | src/manifest_parser.h (renamed from src/parsers.h) | 6 | ||||
-rw-r--r-- | src/manifest_parser_test.cc (renamed from src/parsers_test.cc) | 22 | ||||
-rw-r--r-- | src/metrics.cc | 10 | ||||
-rw-r--r-- | src/metrics.h | 25 | ||||
-rw-r--r-- | src/ninja.cc | 7 | ||||
-rw-r--r-- | src/parser_perftest.cc | 1 | ||||
-rw-r--r-- | src/state.h | 1 | ||||
-rw-r--r-- | src/subprocess-win32.cc | 9 | ||||
-rw-r--r-- | src/subprocess.cc | 7 | ||||
-rw-r--r-- | src/subprocess_test.cc | 11 | ||||
-rw-r--r-- | src/test.cc | 2 | ||||
-rw-r--r-- | src/util.cc | 16 | ||||
-rw-r--r-- | src/util.h | 6 | ||||
-rw-r--r-- | src/win32port.h | 3 |
30 files changed, 341 insertions, 134 deletions
diff --git a/src/build.cc b/src/build.cc index 65aa6a9..90a84c2 100644 --- a/src/build.cc +++ b/src/build.cc @@ -37,7 +37,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 +173,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,6 +229,10 @@ 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) { @@ -236,22 +261,36 @@ void BuildStatus::PrintStatus(Edge* edge) { #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, + 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); have_blank_line_ = false; #endif } else { - printf("\n"); + printf("%s\n", to_print.c_str()); } } @@ -465,7 +504,7 @@ void RealCommandRunner::Abort() { bool RealCommandRunner::CanRunMore() { return ((int)subprocs_.running_.size()) < config_.parallelism - && ((subprocs_.running_.size() == 0 || config_.max_load_average <= 0.0f) + && ((subprocs_.running_.empty() || config_.max_load_average <= 0.0f) || GetLoadAverage() < config_.max_load_average); } @@ -739,19 +778,13 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) { for (vector<Node*>::iterator i = edge->inputs_.begin(); i != edge->inputs_.end() - edge->order_only_deps_; ++i) { TimeStamp input_mtime = disk_interface_->Stat((*i)->path()); - if (input_mtime == 0) { - restat_mtime = 0; - break; - } if (input_mtime > restat_mtime) restat_mtime = input_mtime; } if (restat_mtime != 0 && !edge->rule().depfile().empty()) { TimeStamp depfile_mtime = disk_interface_->Stat(edge->EvaluateDepFile()); - if (depfile_mtime == 0) - restat_mtime = 0; - else if (depfile_mtime > restat_mtime) + if (depfile_mtime > restat_mtime) restat_mtime = depfile_mtime; } @@ -776,3 +809,4 @@ void Builder::FinishEdge(Edge* edge, bool success, const string& output) { if (success && log_) log_->RecordCommand(edge, start_time, end_time, restat_mtime); } + diff --git a/src/build.h b/src/build.h index c9ee4ac..5e26bbb 100644 --- a/src/build.h +++ b/src/build.h @@ -21,9 +21,10 @@ #include <queue> #include <vector> #include <memory> -using namespace std; +#include <cstdio> #include "exit_status.h" +#include "metrics.h" #include "util.h" // int64_t struct BuildLog; @@ -191,9 +192,43 @@ struct BuildStatus { /// The custom progress status format to use. const char* progress_status_format_; + struct RateInfo { + RateInfo() : last_update_(0), rate_(-1) {} + + double rate() const { return rate_; } + int last_update() const { return last_update_; } + void Restart() { return stopwatch_.Restart(); } + + double UpdateRate(int edges, int update_hint) { + if (update_hint != last_update_) { + rate_ = edges / stopwatch_.Elapsed() + 0.5; + last_update_ = update_hint; + } + return rate_; + } + + template<class T> + void snprinfRate(T buf, const char* format) { + if (rate_ == -1) + snprintf(buf, sizeof(buf), "?"); + else + snprintf(buf, sizeof(buf), format, rate_); + } + + private: + Stopwatch stopwatch_; + int last_update_; + double rate_; + }; + + mutable RateInfo overall_rate_; + mutable RateInfo current_rate_; + const int current_rate_average_count_; + #ifdef _WIN32 void* console_; #endif }; #endif // NINJA_BUILD_H_ + diff --git a/src/build_log.cc b/src/build_log.cc index 0a44817..c35b0e4 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -164,7 +164,9 @@ void BuildLog::Close() { class LineReader { public: explicit LineReader(FILE* file) - : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) {} + : file_(file), buf_end_(buf_), line_start_(buf_), line_end_(NULL) { + memset(buf_, 0, sizeof(buf_)); + } // Reads a \n-terminated line from the file passed to the constructor. // On return, *line_start points to the beginning of the next line, and @@ -235,6 +237,7 @@ bool BuildLog::Load(const string& path, string* err) { if (log_version < kOldestSupportedVersion) { *err = "unable to extract version from build log, perhaps due to " "being too old; you must clobber your build output and rebuild"; + fclose(file); return false; } } diff --git a/src/build_log_perftest.cc b/src/build_log_perftest.cc index 51bb51b..5755079 100644 --- a/src/build_log_perftest.cc +++ b/src/build_log_perftest.cc @@ -17,9 +17,10 @@ #include "build_log.h" #include "graph.h" -#include "parsers.h" +#include "manifest_parser.h" #include "state.h" #include "util.h" +#include "metrics.h" const char kTestFilename[] = "BuildLogPerfTest-tempfile"; diff --git a/src/build_log_test.cc b/src/build_log_test.cc index 9fb42c9..0225684 100644 --- a/src/build_log_test.cc +++ b/src/build_log_test.cc @@ -131,7 +131,7 @@ TEST_F(BuildLogTest, Truncate) { ASSERT_GT(statbuf.st_size, 0); // For all possible truncations of the input file, assert that we don't - // crash or report an error when parsing. + // crash when parsing. for (off_t size = statbuf.st_size; size > 0; --size) { #ifndef _WIN32 ASSERT_EQ(0, truncate(kTestFilename, size)); @@ -143,8 +143,8 @@ TEST_F(BuildLogTest, Truncate) { #endif BuildLog log2; - EXPECT_TRUE(log2.Load(kTestFilename, &err)); - ASSERT_EQ("", err); + err.clear(); + ASSERT_TRUE(log2.Load(kTestFilename, &err) || !err.empty()); } } diff --git a/src/build_test.cc b/src/build_test.cc index 23f1909..a3f345b 100644 --- a/src/build_test.cc +++ b/src/build_test.cc @@ -802,6 +802,60 @@ TEST_F(BuildWithLogTest, RestatMissingFile) { ASSERT_EQ(1u, commands_ran_.size()); } +// Test scenario, in which an input file is removed, but output isn't changed +// https://github.com/martine/ninja/issues/295 +TEST_F(BuildWithLogTest, RestatMissingInput) { + ASSERT_NO_FATAL_FAILURE(AssertParse(&state_, + "rule true\n" + " command = true\n" + " depfile = $out.d\n" + " restat = 1\n" + "rule cc\n" + " command = cc\n" + "build out1: true in\n" + "build out2: cc out1\n")); + + // Create all necessary files + fs_.Create("in", now_, ""); + + // The implicit dependencies and the depfile itself + // are newer than the output + TimeStamp restat_mtime = ++now_; + fs_.Create("out1.d", now_, "out1: will.be.deleted restat.file\n"); + fs_.Create("will.be.deleted", now_, ""); + fs_.Create("restat.file", now_, ""); + + // Run the build, out1 and out2 get built + string err; + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(2u, commands_ran_.size()); + + // See that an entry in the logfile is created, capturing + // the right mtime + BuildLog::LogEntry * log_entry = build_log_.LookupByOutput("out1"); + ASSERT_TRUE(NULL != log_entry); + ASSERT_EQ(restat_mtime, log_entry->restat_mtime); + + // Now remove a file, referenced from depfile, so that target becomes + // dirty, but the output does not change + fs_.RemoveFile("will.be.deleted"); + + // Trigger the build again - only out1 gets built + commands_ran_.clear(); + state_.Reset(); + EXPECT_TRUE(builder_.AddTarget("out2", &err)); + ASSERT_EQ("", err); + EXPECT_TRUE(builder_.Build(&err)); + ASSERT_EQ(1u, commands_ran_.size()); + + // Check that the logfile entry remains correctly set + log_entry = build_log_.LookupByOutput("out1"); + ASSERT_TRUE(NULL != log_entry); + ASSERT_EQ(restat_mtime, log_entry->restat_mtime); +} + struct BuildDryRun : public BuildWithLogTest { BuildDryRun() { config_.dry_run = true; diff --git a/src/canon_perftest.cc b/src/canon_perftest.cc index 6248937..d0ed397 100644 --- a/src/canon_perftest.cc +++ b/src/canon_perftest.cc @@ -16,6 +16,7 @@ #include <string.h> #include "util.h" +#include "metrics.h" const char kPath[] = "../../third_party/WebKit/Source/WebCore/" diff --git a/src/clean.h b/src/clean.h index 4d9b4e6..5938dff 100644 --- a/src/clean.h +++ b/src/clean.h @@ -14,7 +14,6 @@ #ifndef NINJA_CLEAN_H_ #define NINJA_CLEAN_H_ -#pragma once #include <set> #include <string> diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index b6ea1ce..47b64d1 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -56,8 +56,8 @@ bool DepfileParser::Parse(string* content, string* err) { 0, 0, 0, 0, 0, 0, 0, 0, 128, 128, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 0, 0, 0, 0, 0, - 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 0, 0, 128, 0, 0, + 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 0, 0, 128, @@ -84,27 +84,35 @@ bool DepfileParser::Parse(string* content, string* err) { }; yych = *in; - if (yych <= '[') { + if (yych <= 'Z') { if (yych <= '*') { if (yych <= 0x00) goto yy6; if (yych <= '\'') goto yy8; if (yych <= ')') goto yy4; goto yy8; } else { - if (yych <= ':') goto yy4; - if (yych <= '@') goto yy8; - if (yych <= 'Z') goto yy4; - goto yy8; + if (yych <= '<') { + if (yych <= ':') goto yy4; + goto yy8; + } else { + if (yych <= '=') goto yy4; + if (yych <= '?') goto yy8; + goto yy4; + } } } else { - if (yych <= '`') { - if (yych <= '\\') goto yy2; - if (yych == '_') goto yy4; - goto yy8; + if (yych <= '_') { + if (yych == '\\') goto yy2; + if (yych <= '^') goto yy8; + goto yy4; } else { - if (yych <= 'z') goto yy4; - if (yych == '~') goto yy4; - goto yy8; + if (yych <= 'z') { + if (yych <= '`') goto yy8; + goto yy4; + } else { + if (yych == '~') goto yy4; + goto yy8; + } } } yy2: diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index 8c415b9..68b6a95 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -68,7 +68,7 @@ bool DepfileParser::Parse(string* content, string* err) { *out++ = yych; continue; } - [a-zA-Z0-9+,/_:.~()-]+ { + [a-zA-Z0-9+,/_:.~()@=-]+ { // Got a span of plain text. int len = in - start; // Need to shift it over if we're overwriting backslashes. diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index fd76ae7..2bb9105 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -106,12 +106,17 @@ TEST_F(DepfileParserTest, Escapes) { TEST_F(DepfileParserTest, SpecialChars) { string err; EXPECT_TRUE(Parse( -"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h:", +"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n" +" en@quot.header~ t+t-x=1", &err)); ASSERT_EQ("", err); EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h", parser_.out_.AsString()); - ASSERT_EQ(0u, parser_.ins_.size()); + ASSERT_EQ(2u, parser_.ins_.size()); + EXPECT_EQ("en@quot.header~", + parser_.ins_[0].AsString()); + EXPECT_EQ("t+t-x=1", + parser_.ins_[1].AsString()); } TEST_F(DepfileParserTest, UnifyMultipleOutputs) { diff --git a/src/getopt.c b/src/getopt.c index 1e3c09d..75ef99c 100644 --- a/src/getopt.c +++ b/src/getopt.c @@ -91,10 +91,6 @@ gpietsch@comcast.net #include "getopt.h" #endif -#ifdef _WIN32 -#pragma warning(disable: 4701) -#endif - /* macros */ /* types */ @@ -159,7 +155,7 @@ getopt_internal (int argc, char **argv, char *shortopts, char *possible_arg = NULL; int longopt_match = -1; int has_arg = -1; - char *cp; + char *cp = NULL; int arg_next = 0; /* first, deal with silly parameters and easy stuff */ @@ -255,7 +251,7 @@ getopt_internal (int argc, char **argv, char *shortopts, longopts[optindex].name, match_chars) == 0) { /* do we have an exact match? */ - if (match_chars == (int) (strlen (longopts[optindex].name))) + if (match_chars == strlen (longopts[optindex].name)) { longopt_match = optindex; break; diff --git a/src/graph.cc b/src/graph.cc index 4bf558a..caf2aca 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -21,8 +21,8 @@ #include "depfile_parser.h" #include "disk_interface.h" #include "explain.h" +#include "manifest_parser.h" #include "metrics.h" -#include "parsers.h" #include "state.h" #include "util.h" @@ -140,7 +140,10 @@ bool Edge::RecomputeOutputDirty(BuildLog* build_log, if (rule_->restat() && build_log && (entry = build_log->LookupByOutput(output->path()))) { if (entry->restat_mtime < most_recent_input) { - EXPLAIN("restat of output %s older than inputs", output->path().c_str()); + EXPLAIN("restat of output %s older than most recent input %s (%d vs %d)", + output->path().c_str(), + most_recent_node ? most_recent_node->path().c_str() : "", + entry->restat_mtime, most_recent_input); return true; } } else { diff --git a/src/lexer.cc b/src/lexer.cc index 48a46b0..45bf4ef 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -127,7 +127,7 @@ Lexer::Token Lexer::ReadToken() { unsigned int yyaccept = 0; static const unsigned char yybm[] = { 0, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 0, 64, 64, 64, 64, 64, + 64, 64, 0, 64, 64, 0, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 64, 64, 64, 64, 64, 64, 64, @@ -216,7 +216,8 @@ yy3: yy4: yyaccept = 1; yych = *(q = ++p); - if (yych >= 0x01) goto yy60; + if (yych <= 0x00) goto yy5; + if (yych != '\r') goto yy60; yy5: { token = ERROR; break; } yy6: @@ -354,7 +355,9 @@ yy60: if (yybm[0+yych] & 64) { goto yy59; } - if (yych >= 0x01) goto yy62; + if (yych <= 0x00) goto yy61; + if (yych <= '\f') goto yy62; +yy61: p = q; if (yyaccept <= 0) { goto yy3; @@ -576,7 +579,7 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { unsigned char yych; static const unsigned char yybm[] = { 0, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 0, 128, 128, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 16, 128, 128, 128, 0, 128, 128, 128, @@ -609,24 +612,25 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { 128, 128, 128, 128, 128, 128, 128, 128, }; yych = *p; - if (yych <= '#') { + if (yych <= ' ') { if (yych <= '\n') { if (yych <= 0x00) goto yy96; if (yych >= '\n') goto yy92; } else { - if (yych == ' ') goto yy92; + if (yych == '\r') goto yy98; + if (yych >= ' ') goto yy92; } } else { - if (yych <= ':') { - if (yych <= '$') goto yy94; - if (yych >= ':') goto yy92; + if (yych <= '9') { + if (yych == '$') goto yy94; } else { + if (yych <= ':') goto yy92; if (yych == '|') goto yy92; } } ++p; yych = *p; - goto yy120; + goto yy121; yy91: { eval->AddText(StringPiece(start, p - start)); @@ -649,39 +653,40 @@ yy94: ++p; if ((yych = *p) <= '/') { if (yych <= ' ') { - if (yych == '\n') goto yy109; - if (yych <= 0x1F) goto yy98; - goto yy100; + if (yych == '\n') goto yy110; + if (yych <= 0x1F) goto yy99; + goto yy101; } else { if (yych <= '$') { - if (yych <= '#') goto yy98; - goto yy102; + if (yych <= '#') goto yy99; + goto yy103; } else { - if (yych == '-') goto yy104; - goto yy98; + if (yych == '-') goto yy105; + goto yy99; } } } else { if (yych <= '^') { if (yych <= ':') { - if (yych <= '9') goto yy104; - goto yy106; + if (yych <= '9') goto yy105; + goto yy107; } else { - if (yych <= '@') goto yy98; - if (yych <= 'Z') goto yy104; - goto yy98; + if (yych <= '@') goto yy99; + if (yych <= 'Z') goto yy105; + goto yy99; } } else { if (yych <= '`') { - if (yych <= '_') goto yy104; - goto yy98; + if (yych <= '_') goto yy105; + goto yy99; } else { - if (yych <= 'z') goto yy104; - if (yych <= '{') goto yy108; - goto yy98; + if (yych <= 'z') goto yy105; + if (yych <= '{') goto yy109; + goto yy99; } } } +yy95: { last_token_ = start; return Error("lexing error", err); @@ -693,83 +698,86 @@ yy96: return Error("unexpected EOF", err); } yy98: - ++p; + yych = *++p; + goto yy95; yy99: + ++p; +yy100: { last_token_ = start; return Error("bad $-escape (literal $ must be written as $$)", err); } -yy100: +yy101: ++p; { eval->AddText(StringPiece(" ", 1)); continue; } -yy102: +yy103: ++p; { eval->AddText(StringPiece("$", 1)); continue; } -yy104: +yy105: ++p; yych = *p; - goto yy118; -yy105: + goto yy119; +yy106: { eval->AddSpecial(StringPiece(start + 1, p - start - 1)); continue; } -yy106: +yy107: ++p; { eval->AddText(StringPiece(":", 1)); continue; } -yy108: +yy109: yych = *(q = ++p); if (yybm[0+yych] & 32) { - goto yy112; + goto yy113; } - goto yy99; -yy109: + goto yy100; +yy110: ++p; yych = *p; if (yybm[0+yych] & 16) { - goto yy109; + goto yy110; } { continue; } -yy112: +yy113: ++p; yych = *p; if (yybm[0+yych] & 32) { - goto yy112; + goto yy113; } - if (yych == '}') goto yy115; + if (yych == '}') goto yy116; p = q; - goto yy99; -yy115: + goto yy100; +yy116: ++p; { eval->AddSpecial(StringPiece(start + 2, p - start - 3)); continue; } -yy117: +yy118: ++p; yych = *p; -yy118: +yy119: if (yybm[0+yych] & 64) { - goto yy117; + goto yy118; } - goto yy105; -yy119: + goto yy106; +yy120: ++p; yych = *p; -yy120: +yy121: if (yybm[0+yych] & 128) { - goto yy119; + goto yy120; } goto yy91; } diff --git a/src/lexer.in.cc b/src/lexer.in.cc index e478921..852d6e9 100644 --- a/src/lexer.in.cc +++ b/src/lexer.in.cc @@ -130,7 +130,7 @@ Lexer::Token Lexer::ReadToken() { simple_varname = [a-zA-Z0-9_-]+; varname = [a-zA-Z0-9_.-]+; - [ ]*"#"[^\000\n]*"\n" { continue; } + [ ]*"#"[^\000\r\n]*"\n" { continue; } [ ]*[\n] { token = NEWLINE; break; } [ ]+ { token = INDENT; break; } "build" { token = BUILD; break; } @@ -200,7 +200,7 @@ bool Lexer::ReadEvalString(EvalString* eval, bool path, string* err) { for (;;) { start = p; /*!re2c - [^$ :\n|\000]+ { + [^$ :\r\n|\000]+ { eval->AddText(StringPiece(start, p - start)); continue; } diff --git a/src/parsers.cc b/src/manifest_parser.cc index bc76ba1..057e12c 100644 --- a/src/parsers.cc +++ b/src/manifest_parser.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "parsers.h" +#include "manifest_parser.h" #include <assert.h> #include <errno.h> diff --git a/src/parsers.h b/src/manifest_parser.h index f889156..a2c6c93 100644 --- a/src/parsers.h +++ b/src/manifest_parser.h @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -#ifndef NINJA_PARSERS_H_ -#define NINJA_PARSERS_H_ +#ifndef NINJA_MANIFEST_PARSER_H_ +#define NINJA_MANIFEST_PARSER_H_ #include <string> #include <vector> @@ -68,4 +68,4 @@ private: Lexer lexer_; }; -#endif // NINJA_PARSERS_H_ +#endif // NINJA_MANIFEST_PARSER_H_ diff --git a/src/parsers_test.cc b/src/manifest_parser_test.cc index fc83946..3261d39 100644 --- a/src/parsers_test.cc +++ b/src/manifest_parser_test.cc @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -#include "parsers.h" +#include "manifest_parser.h" #include <gtest/gtest.h> @@ -682,3 +682,23 @@ TEST_F(ParserTest, UTF8) { " command = true\n" " description = compilaci\xC3\xB3\n")); } + +// We might want to eventually allow CRLF to be nice to Windows developers, +// but for now just verify we error out with a nice message. +TEST_F(ParserTest, CRLF) { + State state; + ManifestParser parser(&state, NULL); + string err; + + EXPECT_FALSE(parser.ParseTest("# comment with crlf\r\n", + &err)); + EXPECT_EQ("input:1: lexing error\n", + err); + + EXPECT_FALSE(parser.ParseTest("foo = foo\nbar = bar\r\n", + &err)); + EXPECT_EQ("input:2: lexing error\n" + "bar = bar\r\n" + " ^ near here", + err); +} diff --git a/src/metrics.cc b/src/metrics.cc index fb44868..8407717 100644 --- a/src/metrics.cc +++ b/src/metrics.cc @@ -72,6 +72,7 @@ int64_t TimerToMicros(int64_t dt) { } // anonymous namespace + ScopedMetric::ScopedMetric(Metric* metric) { metric_ = metric; if (!metric_) @@ -113,3 +114,12 @@ void Metrics::Report() { metric->count, avg, total); } } + +uint64_t Stopwatch::Now() const { + return TimerToMicros(HighResTimer()); +} + +int64_t GetTimeMillis() { + return 1000 * TimerToMicros(HighResTimer()); +} + diff --git a/src/metrics.h b/src/metrics.h index af6e9a2..dae6b9f 100644 --- a/src/metrics.h +++ b/src/metrics.h @@ -33,6 +33,7 @@ struct Metric { int64_t sum; }; + /// A scoped object for recording a metric across the body of a function. /// Used by the METRIC_RECORD macro. struct ScopedMetric { @@ -57,6 +58,30 @@ private: vector<Metric*> metrics_; }; +/// Get the current time as relative to some epoch. +/// Epoch varies between platforms; only useful for measuring elapsed +/// time. +int64_t GetTimeMillis(); + + +/// A simple stopwatch which retruns the time +// in seconds since Restart() was called +class Stopwatch +{ +public: + Stopwatch() : started_(0) {} + + /// Seconds since Restart() call + double Elapsed() const { return 1e-6 * (Now() - started_); } + + void Restart() { started_ = Now(); } + +private: + uint64_t started_; + uint64_t Now() const; +}; + + /// The primary interface to metrics. Use METRIC_RECORD("foobar") at the top /// of a function to get timing stats recorded for each call of the function. #define METRIC_RECORD(name) \ diff --git a/src/ninja.cc b/src/ninja.cc index 6090bdb..619c3bf 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -42,8 +42,8 @@ #include "explain.h" #include "graph.h" #include "graphviz.h" +#include "manifest_parser.h" #include "metrics.h" -#include "parsers.h" #include "state.h" #include "util.h" @@ -707,7 +707,10 @@ int main(int argc, char** argv) { // The formatting of this string, complete with funny quotes, is // so Emacs can properly identify that the cwd has changed for // subsequent commands. - printf("ninja: Entering directory `%s'\n", working_dir); + // Don't print this if a tool is being used, so that tool output + // can be piped into a file without this string showing up. + if (tool == "") + printf("ninja: Entering directory `%s'\n", working_dir); #ifdef _WIN32 if (_chdir(working_dir) < 0) { #else diff --git a/src/parser_perftest.cc b/src/parser_perftest.cc index b1d5bf0..b215221 100644 --- a/src/parser_perftest.cc +++ b/src/parser_perftest.cc @@ -17,6 +17,7 @@ #include "depfile_parser.h" #include "util.h" +#include "metrics.h" int main(int argc, char* argv[]) { if (argc < 2) { diff --git a/src/state.h b/src/state.h index 23ca12b..9197ef8 100644 --- a/src/state.h +++ b/src/state.h @@ -14,7 +14,6 @@ #ifndef NINJA_STATE_H_ #define NINJA_STATE_H_ -#pragma once #include <map> #include <string> diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc index 3484e5f..38b6957 100644 --- a/src/subprocess-win32.cc +++ b/src/subprocess-win32.cc @@ -44,7 +44,7 @@ Subprocess::~Subprocess() { HANDLE Subprocess::SetupPipe(HANDLE ioport) { char pipe_name[100]; snprintf(pipe_name, sizeof(pipe_name), - "\\\\.\\pipe\\ninja_pid%u_sp%p", GetCurrentProcessId(), this); + "\\\\.\\pipe\\ninja_pid%lu_sp%p", GetCurrentProcessId(), this); pipe_ = ::CreateNamedPipeA(pipe_name, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, @@ -271,9 +271,12 @@ Subprocess* SubprocessSet::NextFinished() { void SubprocessSet::Clear() { for (vector<Subprocess*>::iterator i = running_.begin(); i != running_.end(); ++i) { - if ((*i)->child_) - if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, GetProcessId((*i)->child_))) + if ((*i)->child_) { + if (!GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, + GetProcessId((*i)->child_))) { Win32Fatal("GenerateConsoleCtrlEvent"); + } + } } for (vector<Subprocess*>::iterator i = running_.begin(); i != running_.end(); ++i) diff --git a/src/subprocess.cc b/src/subprocess.cc index 0d240b1..1c47fd1 100644 --- a/src/subprocess.cc +++ b/src/subprocess.cc @@ -25,6 +25,13 @@ #include <string.h> #include <sys/wait.h> +// Older versions of glibc (like 2.4) won't find this in <poll.h>. glibc +// 2.4 keeps it in <asm-generic/poll.h>, though attempting to include that +// will redefine the pollfd structure. +#ifndef POLLRDHUP +#define POLLRDHUP 0x2000 +#endif + #include "util.h" Subprocess::Subprocess() : fd_(-1), pid_(-1) { diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index c155012..7dbaf97 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -16,6 +16,13 @@ #include "test.h" +#ifndef _WIN32 +// SetWithLots need setrlimit. +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#endif + namespace { #ifdef _WIN32 @@ -142,7 +149,7 @@ TEST_F(SubprocessTest, SetWithMulti) { } } -#ifdef linux +#ifndef _WIN32 TEST_F(SubprocessTest, SetWithLots) { // Arbitrary big number; needs to be over 1024 to confirm we're no longer // hostage to pselect. @@ -169,4 +176,4 @@ TEST_F(SubprocessTest, SetWithLots) { } ASSERT_EQ(kNumProcs, subprocs_.finished_.size()); } -#endif // linux +#endif // _WIN32 diff --git a/src/test.cc b/src/test.cc index afedd10..0138b3a 100644 --- a/src/test.cc +++ b/src/test.cc @@ -19,7 +19,7 @@ #include <errno.h> #include "build_log.h" -#include "parsers.h" +#include "manifest_parser.h" #include "util.h" #ifdef _WIN32 diff --git a/src/util.cc b/src/util.cc index d8d7fb3..ca05292 100644 --- a/src/util.cc +++ b/src/util.cc @@ -43,7 +43,7 @@ void Fatal(const char* msg, ...) { va_list ap; - fprintf(stderr, "ninja: FATAL: "); + fprintf(stderr, "ninja: fatal: "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); @@ -61,7 +61,7 @@ void Fatal(const char* msg, ...) { void Warning(const char* msg, ...) { va_list ap; - fprintf(stderr, "ninja: WARNING: "); + fprintf(stderr, "ninja: warning: "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); @@ -70,7 +70,7 @@ void Warning(const char* msg, ...) { void Error(const char* msg, ...) { va_list ap; - fprintf(stderr, "ninja: ERROR: "); + fprintf(stderr, "ninja: error: "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); @@ -204,16 +204,6 @@ void SetCloseOnExec(int fd) { #endif // ! _WIN32 } -int64_t GetTimeMillis() { -#ifdef _WIN32 - // GetTickCount64 is only available on Vista or later. - return GetTickCount(); -#else - timeval now; - gettimeofday(&now, NULL); - return ((int64_t)now.tv_sec * 1000) + (now.tv_usec / 1000); -#endif -} const char* SpellcheckStringV(const string& text, const vector<const char*>& words) { @@ -14,7 +14,6 @@ #ifndef NINJA_UTIL_H_ #define NINJA_UTIL_H_ -#pragma once #ifdef _WIN32 #include "win32port.h" @@ -53,11 +52,6 @@ int ReadFile(const string& path, string* contents, string* err); /// Mark a file descriptor to not be inherited on exec()s. void SetCloseOnExec(int fd); -/// Get the current time as relative to some epoch. -/// Epoch varies between platforms; only useful for measuring elapsed -/// time. -int64_t GetTimeMillis(); - /// Given a misspelled string and a list of correct spellings, returns /// the closest match or NULL if there is no close enough match. const char* SpellcheckStringV(const string& text, const vector<const char*>& words); diff --git a/src/win32port.h b/src/win32port.h index 8b42b38..ce3c949 100644 --- a/src/win32port.h +++ b/src/win32port.h @@ -14,8 +14,9 @@ #ifndef NINJA_WIN32PORT_H_ #define NINJA_WIN32PORT_H_ -#pragma once +typedef signed short int16_t; +typedef unsigned short uint16_t; /// A 64-bit integer type typedef signed long long int64_t; typedef unsigned long long uint64_t; |