From c9b5eaa55231612aeff85385033a792949162228 Mon Sep 17 00:00:00 2001 From: Dan Willemsen Date: Wed, 19 Oct 2016 16:16:21 -0700 Subject: Optimize ReadFile allocations Instead of continuously reallocating the output string, call reserve() up front to enlarge the string's buffer. Also use feof() instead of an empty read to detect the end of the file. This saves a syscall on <64kb files, which is about 5% (200ns) of the time to read a file on my machine. For our larger build.ninja files (~550MB), this saves about 500ms, which is more than half the time spent in this function. (Measured by adding METRICS_RECORD to this function during manifest_parser_perftest) For the standard manifest_parser_perftest, this only saves ~20ms out of ~600ms for a full run. --- src/util.cc | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/util.cc b/src/util.cc index e31fd1f..de280cd 100644 --- a/src/util.cc +++ b/src/util.cc @@ -380,9 +380,19 @@ int ReadFile(const string& path, string* contents, string* err) { return -errno; } + struct stat st; + if (fstat(fileno(f), &st) < 0) { + err->assign(strerror(errno)); + fclose(f); + return -errno; + } + + // +1 is for the resize in ManifestParser::Load + contents->reserve(st.st_size + 1); + char buf[64 << 10]; size_t len; - while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { + while (!feof(f) && (len = fread(buf, 1, sizeof(buf), f)) > 0) { contents->append(buf, len); } if (ferror(f)) { -- cgit v0.12 From 87efe5f206fe138a1e88238153212c5cbac401cd Mon Sep 17 00:00:00 2001 From: Pietro Cerutti Date: Tue, 11 Apr 2017 10:53:40 +0000 Subject: DirName's separators and their length are known at compile time --- src/disk_interface.cc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 1b4135f..40796ed 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -34,14 +34,15 @@ namespace { string DirName(const string& path) { #ifdef _WIN32 - const char kPathSeparators[] = "\\/"; + static const char kPathSeparators[] = "\\/"; #else - const char kPathSeparators[] = "/"; + static const char kPathSeparators[] = "/"; #endif + static const char* const kEnd = kPathSeparators + sizeof(kPathSeparators) - 1; + string::size_type slash_pos = path.find_last_of(kPathSeparators); if (slash_pos == string::npos) return string(); // Nothing to do. - const char* const kEnd = kPathSeparators + strlen(kPathSeparators); while (slash_pos > 0 && std::find(kPathSeparators, kEnd, path[slash_pos - 1]) != kEnd) --slash_pos; -- cgit v0.12 From e237f1b5a19fcd3f34f580b106e09c8026b434a6 Mon Sep 17 00:00:00 2001 From: Peter Collingbourne Date: Wed, 5 Jul 2017 16:53:45 -0700 Subject: Flush stdout after printing "Cleaning..." message --- src/clean.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/clean.cc b/src/clean.cc index 1d6ba9e..44b8a80 100644 --- a/src/clean.cc +++ b/src/clean.cc @@ -101,6 +101,7 @@ void Cleaner::PrintHeader() { printf("\n"); else printf(" "); + fflush(stdout); } void Cleaner::PrintFooter() { -- cgit v0.12 From d933127a69d9e7e54f9ba7e177feb6b4c832ec46 Mon Sep 17 00:00:00 2001 From: Thomas Klausner Date: Sun, 10 Sep 2017 23:25:42 +0200 Subject: Include missing header for pselect(). --- src/subprocess-posix.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index 1de22c3..001fdf1 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -14,6 +14,7 @@ #include "subprocess.h" +#include #include #include #include -- cgit v0.12 From 2950bb1af9f00e30330de2d665be7317cf2b2ca3 Mon Sep 17 00:00:00 2001 From: Thomas Klausner Date: Sun, 10 Sep 2017 23:26:27 +0200 Subject: Add NetBSD in another clause. --- configure.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.py b/configure.py index a443748..6eda66a 100755 --- a/configure.py +++ b/configure.py @@ -98,7 +98,7 @@ class Platform(object): return self._platform == 'aix' def uses_usr_local(self): - return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly') + return self._platform in ('freebsd', 'openbsd', 'bitrig', 'dragonfly', 'netbsd') def supports_ppoll(self): return self._platform in ('freebsd', 'linux', 'openbsd', 'bitrig', -- cgit v0.12 From f87e865e5b4ef91ac642f063ba55708359b8294f Mon Sep 17 00:00:00 2001 From: Brad King Date: Tue, 20 Jun 2017 15:17:39 -0400 Subject: Track in Plan whether wanted edges have been scheduled Refactor the `want_` map to track for wanted edges whether they have been scheduled or not. This gives `ScheduleWork` a direct place to keep this information, making the logic more robust and easier to follow. It also future-proofs `ScheduleWork` to avoid repeat scheduling if it is called after an edge has been removed from `ready_` by `FindWork`. --- src/build.cc | 44 +++++++++++++++++++++++--------------------- src/build.h | 22 +++++++++++++++++----- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/src/build.cc b/src/build.cc index 61ef0e8..b888288 100644 --- a/src/build.cc +++ b/src/build.cc @@ -318,18 +318,18 @@ bool Plan::AddSubTarget(Node* node, Node* dependent, string* err) { return false; // Don't need to do anything. // If an entry in want_ does not already exist for edge, create an entry which - // maps to false, indicating that we do not want to build this entry itself. - pair::iterator, bool> want_ins = - want_.insert(make_pair(edge, false)); - bool& want = want_ins.first->second; + // maps to kWantNothing, indicating that we do not want to build this entry itself. + pair::iterator, bool> want_ins = + want_.insert(make_pair(edge, kWantNothing)); + Want& want = want_ins.first->second; // If we do need to build edge and we haven't already marked it as wanted, // mark it now. - if (node->dirty() && !want) { - want = true; + if (node->dirty() && want == kWantNothing) { + want = kWantToStart; ++wanted_edges_; if (edge->AllInputsReady()) - ScheduleWork(edge); + ScheduleWork(want_ins.first); if (!edge->is_phony()) ++command_edges_; } @@ -355,30 +355,32 @@ Edge* Plan::FindWork() { return edge; } -void Plan::ScheduleWork(Edge* edge) { - set::iterator e = ready_.lower_bound(edge); - if (e != ready_.end() && !ready_.key_comp()(edge, *e)) { +void Plan::ScheduleWork(map::iterator want_e) { + if (want_e->second == kWantToFinish) { // This edge has already been scheduled. We can get here again if an edge // and one of its dependencies share an order-only input, or if a node // duplicates an out edge (see https://github.com/ninja-build/ninja/pull/519). // Avoid scheduling the work again. return; } + assert(want_e->second == kWantToStart); + want_e->second = kWantToFinish; + Edge* edge = want_e->first; Pool* pool = edge->pool(); if (pool->ShouldDelayEdge()) { pool->DelayEdge(edge); pool->RetrieveReadyEdges(&ready_); } else { pool->EdgeScheduled(*edge); - ready_.insert(e, edge); + ready_.insert(edge); } } void Plan::EdgeFinished(Edge* edge, EdgeResult result) { - map::iterator e = want_.find(edge); + map::iterator e = want_.find(edge); assert(e != want_.end()); - bool directly_wanted = e->second; + bool directly_wanted = e->second != kWantNothing; // See if this job frees up any delayed jobs. if (directly_wanted) @@ -405,14 +407,14 @@ void Plan::NodeFinished(Node* node) { // See if we we want any edges from this node. for (vector::const_iterator oe = node->out_edges().begin(); oe != node->out_edges().end(); ++oe) { - map::iterator want_e = want_.find(*oe); + map::iterator want_e = want_.find(*oe); if (want_e == want_.end()) continue; // See if the edge is now ready. if ((*oe)->AllInputsReady()) { - if (want_e->second) { - ScheduleWork(*oe); + if (want_e->second != kWantNothing) { + ScheduleWork(want_e); } else { // We do not need to build this edge, but we might need to build one of // its dependents. @@ -428,8 +430,8 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { for (vector::const_iterator oe = node->out_edges().begin(); oe != node->out_edges().end(); ++oe) { // Don't process edges that we don't actually want. - map::iterator want_e = want_.find(*oe); - if (want_e == want_.end() || !want_e->second) + map::iterator want_e = want_.find(*oe); + if (want_e == want_.end() || want_e->second == kWantNothing) continue; // Don't attempt to clean an edge if it failed to load deps. @@ -464,7 +466,7 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { return false; } - want_e->second = false; + want_e->second = kWantNothing; --wanted_edges_; if (!(*oe)->is_phony()) --command_edges_; @@ -476,8 +478,8 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { void Plan::Dump() { printf("pending: %d\n", (int)want_.size()); - for (map::iterator e = want_.begin(); e != want_.end(); ++e) { - if (e->second) + for (map::iterator e = want_.begin(); e != want_.end(); ++e) { + if (e->second != kWantNothing) printf("want "); e->first->Dump(); } diff --git a/src/build.h b/src/build.h index 43786f1..d7a9a79 100644 --- a/src/build.h +++ b/src/build.h @@ -78,17 +78,29 @@ private: bool AddSubTarget(Node* node, Node* dependent, string* err); void NodeFinished(Node* node); + /// Enumerate possible steps we want for an edge. + enum Want + { + /// We do not want to build the edge, but we might want to build one of + /// its dependents. + kWantNothing, + /// We want to build the edge, but have not yet scheduled it. + kWantToStart, + /// We want to build the edge, have scheduled it, and are waiting + /// for it to complete. + kWantToFinish + }; + /// Submits a ready edge as a candidate for execution. /// The edge may be delayed from running, for example if it's a member of a /// currently-full pool. - void ScheduleWork(Edge* edge); + void ScheduleWork(map::iterator want_e); /// Keep track of which edges we want to build in this plan. If this map does /// not contain an entry for an edge, we do not want to build the entry or its - /// dependents. If an entry maps to false, we do not want to build it, but we - /// might want to build one of its dependents. If the entry maps to true, we - /// want to build it. - map want_; + /// dependents. If it does contain an entry, the enumeration indicates what + /// we want for the edge. + map want_; set ready_; -- cgit v0.12 From 5fcdcf95cb62ab3d593c36ef90df27cef63874a1 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 31 Dec 2016 03:12:51 -0500 Subject: Make TimeStamp 64-bit. This prepares it for higher-resolution timestamps. --- src/build_log.cc | 2 +- src/deps_log.cc | 13 ++++++------- src/deps_log.h | 7 ++++--- src/graph.cc | 8 ++++---- src/ninja.cc | 2 +- src/timestamp.h | 6 +++--- src/util.h | 1 + src/win32port.h | 1 + 8 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/build_log.cc b/src/build_log.cc index 333915a..a591050 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -353,7 +353,7 @@ BuildLog::LogEntry* BuildLog::LookupByOutput(const string& path) { } bool BuildLog::WriteEntry(FILE* f, const LogEntry& entry) { - return fprintf(f, "%d\t%d\t%d\t%s\t%" PRIx64 "\n", + return fprintf(f, "%d\t%d\t%" PRId64 "\t%s\t%" PRIx64 "\n", entry.start_time, entry.end_time, entry.mtime, entry.output.c_str(), entry.command_hash) > 0; } diff --git a/src/deps_log.cc b/src/deps_log.cc index 89c6023..d7f0b26 100644 --- a/src/deps_log.cc +++ b/src/deps_log.cc @@ -30,7 +30,7 @@ // The version is stored as 4 bytes after the signature and also serves as a // byte order mark. Signature and version combined are 16 bytes long. const char kFileSignature[] = "# ninjadeps\n"; -const int kCurrentVersion = 3; +const int kCurrentVersion = 4; // Record size is currently limited to less than the full 32 bit, due to // internal buffers having to have this size. @@ -124,7 +124,7 @@ bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, return true; // Update on-disk representation. - unsigned size = 4 * (1 + 1 + node_count); + unsigned size = 4 * (1 + 2 + node_count); if (size > kMaxRecordSize) { errno = ERANGE; return false; @@ -135,8 +135,7 @@ bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, int id = node->id(); if (fwrite(&id, 4, 1, file_) < 1) return false; - int timestamp = mtime; - if (fwrite(×tamp, 4, 1, file_) < 1) + if (fwrite(&mtime, 8, 1, file_) < 1) return false; for (int i = 0; i < node_count; ++i) { id = nodes[i]->id(); @@ -218,9 +217,9 @@ bool DepsLog::Load(const string& path, State* state, string* err) { assert(size % 4 == 0); int* deps_data = reinterpret_cast(buf); int out_id = deps_data[0]; - int mtime = deps_data[1]; - deps_data += 2; - int deps_count = (size / 4) - 2; + TimeStamp mtime = reinterpret_cast(&deps_data[1])[0]; + deps_data += 3; + int deps_count = (size / 4) - 3; Deps* deps = new Deps(mtime, deps_count); for (int i = 0; i < deps_count; ++i) { diff --git a/src/deps_log.h b/src/deps_log.h index cec0257..793af07 100644 --- a/src/deps_log.h +++ b/src/deps_log.h @@ -57,7 +57,8 @@ struct State; /// one's complement of the expected index of the record (to detect /// concurrent writes of multiple ninja processes to the log). /// dependency records are an array of 4-byte integers -/// [output path id, output path mtime, input path id, input path id...] +/// [output path id, output path mtime (8-byte int), input path id, +/// input path id...] /// (The mtime is compared against the on-disk output path mtime /// to verify the stored data is up-to-date.) /// If two records reference the same output the latter one in the file @@ -75,10 +76,10 @@ struct DepsLog { // Reading (startup-time) interface. struct Deps { - Deps(int mtime, int node_count) + Deps(int64_t mtime, int node_count) : mtime(mtime), node_count(node_count), nodes(new Node*[node_count]) {} ~Deps() { delete [] nodes; } - int mtime; + TimeStamp mtime; int node_count; Node** nodes; }; diff --git a/src/graph.cc b/src/graph.cc index ce4ea77..b41c247 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -233,7 +233,7 @@ bool DependencyScan::RecomputeOutputDirty(Edge* edge, if (output_mtime < most_recent_input->mtime()) { EXPLAIN("%soutput %s older than most recent input %s " - "(%d vs %d)", + "(%" PRId64 " vs %" PRId64 ")", used_restat ? "restat of " : "", output->path().c_str(), most_recent_input->path().c_str(), output_mtime, most_recent_input->mtime()); @@ -257,7 +257,7 @@ bool DependencyScan::RecomputeOutputDirty(Edge* edge, // mtime of the most recent input. This can occur even when the mtime // on disk is newer if a previous run wrote to the output file but // exited with an error or was interrupted. - EXPLAIN("recorded mtime of %s older than most recent input %s (%d vs %d)", + EXPLAIN("recorded mtime of %s older than most recent input %s (%" PRId64 " vs %" PRId64 ")", output->path().c_str(), most_recent_input->path().c_str(), entry->mtime, most_recent_input->mtime()); return true; @@ -441,7 +441,7 @@ string Node::PathDecanonicalized(const string& path, uint64_t slash_bits) { } void Node::Dump(const char* prefix) const { - printf("%s <%s 0x%p> mtime: %d%s, (:%s), ", + printf("%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ", prefix, path().c_str(), this, mtime(), mtime() ? "" : " (:missing)", dirty() ? " dirty" : " clean"); @@ -547,7 +547,7 @@ bool ImplicitDepLoader::LoadDepsFromLog(Edge* edge, string* err) { // Deps are invalid if the output is newer than the deps. if (output->mtime() > deps->mtime) { - EXPLAIN("stored deps info out of date for '%s' (%d vs %d)", + EXPLAIN("stored deps info out of date for '%s' (%" PRId64 " vs %" PRId64 ")", output->path().c_str(), deps->mtime, output->mtime()); return false; } diff --git a/src/ninja.cc b/src/ninja.cc index ed004ac..a9030aa 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -494,7 +494,7 @@ int NinjaMain::ToolDeps(const Options* options, int argc, char** argv) { TimeStamp mtime = disk_interface.Stat((*it)->path(), &err); if (mtime == -1) Error("%s", err.c_str()); // Log and ignore Stat() errors; - printf("%s: #deps %d, deps mtime %d (%s)\n", + printf("%s: #deps %d, deps mtime %" PRId64 " (%s)\n", (*it)->path().c_str(), deps->node_count, deps->mtime, (!mtime || mtime > deps->mtime ? "STALE":"VALID")); for (int i = 0; i < deps->node_count; ++i) diff --git a/src/timestamp.h b/src/timestamp.h index cee7ba8..58ae148 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -17,8 +17,8 @@ // When considering file modification times we only care to compare // them against one another -- we never convert them to an absolute -// real time. On POSIX we use time_t (seconds since epoch) and on -// Windows we use a different value. Both fit in an int. -typedef int TimeStamp; +// real time. On POSIX we use timespec (seconds&nanoseconds since epoch) +// and on Windows we use a different value. Both fit in an int64. +typedef int64_t TimeStamp; #endif // NINJA_TIMESTAMP_H_ diff --git a/src/util.h b/src/util.h index 4ee41a5..a8493d6 100644 --- a/src/util.h +++ b/src/util.h @@ -18,6 +18,7 @@ #ifdef _WIN32 #include "win32port.h" #else +#include #include #endif diff --git a/src/win32port.h b/src/win32port.h index ce3c949..5464797 100644 --- a/src/win32port.h +++ b/src/win32port.h @@ -23,6 +23,7 @@ typedef unsigned long long uint64_t; // printf format specifier for uint64_t, from C99. #ifndef PRIu64 +#define PRId64 "I64d" #define PRIu64 "I64u" #define PRIx64 "I64x" #endif -- cgit v0.12 From f50a5250e49347df802d7844358f5ea567ddfac2 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 31 Dec 2016 03:33:01 -0500 Subject: Read file timestamps in higher resolution. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This uses nanoseconds on POSIX (±~292 years) and 100-ns increments on Windows (±~29247 years). The fallbacks to different structure fields is the only thing grabbed from #337, with a slight modification in implementation. --- src/disk_interface.cc | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 28530b1..ffa58db 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -61,12 +61,11 @@ int MakeDir(const string& path) { TimeStamp TimeStampFromFileTime(const FILETIME& filetime) { // FILETIME is in 100-nanosecond increments since the Windows epoch. // We don't much care about epoch correctness but we do want the - // resulting value to fit in an integer. + // resulting value to fit in a 64-bit integer. uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | ((uint64_t)filetime.dwLowDateTime); - mtime /= 1000000000LL / 100; // 100ns -> s. - mtime -= 12622770400LL; // 1600 epoch -> 2000 epoch (subtract 400 years). - return (TimeStamp)mtime; + // 1600 epoch -> 2000 epoch (subtract 400 years). + return (TimeStamp)mtime - 12622770400LL * 1000000000LL / 100; } TimeStamp StatSingleFile(const string& path, string* err) { @@ -192,7 +191,17 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { // that it doesn't exist. if (st.st_mtime == 0) return 1; - return st.st_mtime; +#if defined(__APPLE__) && !defined(_POSIX_C_SOURCE) + return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL + + st.st_mtimespec.tv_nsec); +#elif defined(_LARGEFILE64_SOURCE) + return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec; +#elif defined(__CYGWIN__) + return (int64_t)st.st_mtime * 1000000000LL; +#else + // see http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html + return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec; +#endif #endif } -- cgit v0.12 From 9fa250d794cabc68fe53087de1b5b9797e515e18 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sun, 1 Jan 2017 03:51:09 -0500 Subject: Move #include for type definition to correct place. Not sure why the old way works in newer compilers; maybe they just pre-define these types by defaulting to a newer standard. --- src/timestamp.h | 6 ++++++ src/util.h | 1 - 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/timestamp.h b/src/timestamp.h index 58ae148..5565154 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -15,6 +15,12 @@ #ifndef NINJA_TIMESTAMP_H_ #define NINJA_TIMESTAMP_H_ +#ifdef _WIN32 +#include "win32port.h" +#else +#include +#endif + // When considering file modification times we only care to compare // them against one another -- we never convert them to an absolute // real time. On POSIX we use timespec (seconds&nanoseconds since epoch) diff --git a/src/util.h b/src/util.h index a8493d6..4ee41a5 100644 --- a/src/util.h +++ b/src/util.h @@ -18,7 +18,6 @@ #ifdef _WIN32 #include "win32port.h" #else -#include #include #endif -- cgit v0.12 From eb6cea27f23c0674cea47871f281aaffb8a8efc3 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sun, 1 Jan 2017 04:06:56 -0500 Subject: Add #define to get printf-format specifiers. This is needed on older compilers/stdlibs such as on Ubuntu Precise which is used on Travis. --- src/timestamp.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/timestamp.h b/src/timestamp.h index 5565154..6a7ccd0 100644 --- a/src/timestamp.h +++ b/src/timestamp.h @@ -18,6 +18,9 @@ #ifdef _WIN32 #include "win32port.h" #else +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif #include #endif -- cgit v0.12 From bf4fb03e4b266e7f75f88664b6a941d20650046b Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Thu, 20 Apr 2017 23:10:56 -0400 Subject: Use 64-bit-alignment-safe timestamp reading. Read and write the timestamp as two separate 32-bit integers in a fixed order to prevent any issues with alignment or byte order. --- src/deps_log.cc | 10 ++++++++-- src/deps_log.h | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/deps_log.cc b/src/deps_log.cc index d7f0b26..4239426 100644 --- a/src/deps_log.cc +++ b/src/deps_log.cc @@ -135,7 +135,11 @@ bool DepsLog::RecordDeps(Node* node, TimeStamp mtime, int id = node->id(); if (fwrite(&id, 4, 1, file_) < 1) return false; - if (fwrite(&mtime, 8, 1, file_) < 1) + uint32_t mtime_part = static_cast(mtime & 0xffffffff); + if (fwrite(&mtime_part, 4, 1, file_) < 1) + return false; + mtime_part = static_cast((mtime >> 32) & 0xffffffff); + if (fwrite(&mtime_part, 4, 1, file_) < 1) return false; for (int i = 0; i < node_count; ++i) { id = nodes[i]->id(); @@ -217,7 +221,9 @@ bool DepsLog::Load(const string& path, State* state, string* err) { assert(size % 4 == 0); int* deps_data = reinterpret_cast(buf); int out_id = deps_data[0]; - TimeStamp mtime = reinterpret_cast(&deps_data[1])[0]; + TimeStamp mtime; + mtime = (TimeStamp)(((uint64_t)(unsigned int)deps_data[2] << 32) | + (uint64_t)(unsigned int)deps_data[1]); deps_data += 3; int deps_count = (size / 4) - 3; diff --git a/src/deps_log.h b/src/deps_log.h index 793af07..b1aa361 100644 --- a/src/deps_log.h +++ b/src/deps_log.h @@ -57,8 +57,9 @@ struct State; /// one's complement of the expected index of the record (to detect /// concurrent writes of multiple ninja processes to the log). /// dependency records are an array of 4-byte integers -/// [output path id, output path mtime (8-byte int), input path id, -/// input path id...] +/// [output path id, +/// output path mtime (lower 4 bytes), output path mtime (upper 8 bytes), +/// input path id, input path id...] /// (The mtime is compared against the on-disk output path mtime /// to verify the stored data is up-to-date.) /// If two records reference the same output the latter one in the file -- cgit v0.12 From d2bf82fef0535bc351a69392c0e0c5edc3aaf69f Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Fri, 30 Jun 2017 23:55:09 -0400 Subject: Update checks for new stat fields. This uses the macros as defined by the man page, which, as noted in the comments, are defined correctly on as many libc's that I could check. --- src/disk_interface.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index ffa58db..f83151c 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -194,12 +194,14 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { #if defined(__APPLE__) && !defined(_POSIX_C_SOURCE) return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL + st.st_mtimespec.tv_nsec); -#elif defined(_LARGEFILE64_SOURCE) +#elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \ + defined(__BIONIC__)) + // For glibc, see "Timestamp files" in the Notes of http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html + // newlib, uClibc and musl follow the kernel (or Cygwin) headers and define the right macro values above. + // For bsd, see https://github.com/freebsd/freebsd/blob/master/sys/sys/stat.h and similar + // For bionic, C and POSIX API is always enabled. return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec; -#elif defined(__CYGWIN__) - return (int64_t)st.st_mtime * 1000000000LL; #else - // see http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec; #endif #endif -- cgit v0.12 From f117cc16f6ecf991d16737472f75f9328aa423f0 Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 1 Jul 2017 03:49:59 -0400 Subject: Fix some Windows troubles. Add parentheses so that constant does not overflow; include inttypes.h when using MinGW to get the proper macros. --- src/disk_interface.cc | 2 +- src/win32port.h | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index f83151c..4b4c4c7 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -65,7 +65,7 @@ TimeStamp TimeStampFromFileTime(const FILETIME& filetime) { uint64_t mtime = ((uint64_t)filetime.dwHighDateTime << 32) | ((uint64_t)filetime.dwLowDateTime); // 1600 epoch -> 2000 epoch (subtract 400 years). - return (TimeStamp)mtime - 12622770400LL * 1000000000LL / 100; + return (TimeStamp)mtime - 12622770400LL * (1000000000LL / 100); } TimeStamp StatSingleFile(const string& path, string* err) { diff --git a/src/win32port.h b/src/win32port.h index 5464797..e542536 100644 --- a/src/win32port.h +++ b/src/win32port.h @@ -15,6 +15,13 @@ #ifndef NINJA_WIN32PORT_H_ #define NINJA_WIN32PORT_H_ +#if defined(__MINGW32__) || defined(__MINGW64__) +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif +#include +#endif + typedef signed short int16_t; typedef unsigned short uint16_t; /// A 64-bit integer type -- cgit v0.12 From df19c5a597991942bb771df7f6450b6eb15f882a Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 16 Sep 2017 05:02:57 -0400 Subject: Use strtoll when reading mtime from build log. This prevents overflow on Windows where 'long' is not 64-bit. --- src/build_log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/build_log.cc b/src/build_log.cc index a591050..648617c 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -290,7 +290,7 @@ bool BuildLog::Load(const string& path, string* err) { if (!end) continue; *end = 0; - restat_mtime = atol(start); + restat_mtime = strtoll(start, NULL, 10); start = end + 1; end = (char*)memchr(start, kFieldSeparator, line_end - start); -- cgit v0.12 From 0c42653da8ccaeb14b7c28ba9670eeac0460037e Mon Sep 17 00:00:00 2001 From: Elliott Sales de Andrade Date: Sat, 16 Sep 2017 05:34:29 -0400 Subject: Fix minor typo. --- src/deps_log.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deps_log.h b/src/deps_log.h index b1aa361..3812a28 100644 --- a/src/deps_log.h +++ b/src/deps_log.h @@ -58,7 +58,7 @@ struct State; /// concurrent writes of multiple ninja processes to the log). /// dependency records are an array of 4-byte integers /// [output path id, -/// output path mtime (lower 4 bytes), output path mtime (upper 8 bytes), +/// output path mtime (lower 4 bytes), output path mtime (upper 4 bytes), /// input path id, input path id...] /// (The mtime is compared against the on-disk output path mtime /// to verify the stored data is up-to-date.) -- cgit v0.12 From 4244022f16c79505a3af583ac80c8953dbaf9977 Mon Sep 17 00:00:00 2001 From: Takuto Ikuta Date: Wed, 13 Sep 2017 17:53:46 +0900 Subject: update RELEASING --- RELEASING | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/RELEASING b/RELEASING index 5f51b73..da4dbdd 100644 --- a/RELEASING +++ b/RELEASING @@ -1,19 +1,20 @@ Notes to myself on all the steps to make for a Ninja release. Push new release branch: -1. Consider sending a heads-up to the ninja-build mailing list first -2. Make sure branches 'master' and 'release' are synced up locally -3. update src/version.cc with new version (with ".git"), then +1. Run afl-fuzz for a day or so (see HACKING.md) and run ninja_test +2. Consider sending a heads-up to the ninja-build mailing list first +3. Make sure branches 'master' and 'release' are synced up locally +4. Update src/version.cc with new version (with ".git"), then git commit -am 'mark this 1.5.0.git' -4. git checkout release; git merge master -5. fix version number in src/version.cc (it will likely conflict in the above) -6. fix version in doc/manual.asciidoc (exists only on release branch) -7. commit, tag, push (don't forget to push --tags) +5. git checkout release; git merge master +6. Fix version number in src/version.cc (it will likely conflict in the above) +7. Fix version in doc/manual.asciidoc (exists only on release branch) +8. commit, tag, push (don't forget to push --tags) git commit -am v1.5.0; git push origin release git tag v1.5.0; git push --tags # Push the 1.5.0.git change on master too: git checkout master; git push origin master -8. construct release notes from prior notes +9. Construct release notes from prior notes credits: git shortlog -s --no-merges REV.. Release on github: -- cgit v0.12 From aa44a55f67f79cfb52cb590e956d5067f343276c Mon Sep 17 00:00:00 2001 From: Kulak Date: Thu, 28 Sep 2017 15:02:14 -0700 Subject: correction of location of binary original said "source root", but actual location is project root. Perhaps it is mean to be "src" parent, but that's a bit confusing. --- HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.md b/HACKING.md index e7c91ef..9198bdf 100644 --- a/HACKING.md +++ b/HACKING.md @@ -13,7 +13,7 @@ run `ninja_test` when developing. Ninja is built using itself. To bootstrap the first binary, run the configure script as `./configure.py --bootstrap`. This first compiles all non-test source files together, then re-builds Ninja using itself. -You should end up with a `ninja` binary (or `ninja.exe`) in the source root. +You should end up with a `ninja` binary (or `ninja.exe`) in the project root. #### Windows -- cgit v0.12 From 7c80007b55fd42cdd56b06cb4330fb8ceb468e09 Mon Sep 17 00:00:00 2001 From: Logan Chien Date: Mon, 16 Oct 2017 14:04:34 +0800 Subject: Fix potential buffer overrun This commit rearranges record size comparison and fread() to make sure fread() only reads the data that can fit into the buffer. --- src/deps_log.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deps_log.cc b/src/deps_log.cc index 89c6023..8734dd7 100644 --- a/src/deps_log.cc +++ b/src/deps_log.cc @@ -209,7 +209,7 @@ bool DepsLog::Load(const string& path, State* state, string* err) { bool is_deps = (size >> 31) != 0; size = size & 0x7FFFFFFF; - if (fread(buf, size, 1, f) < 1 || size > kMaxRecordSize) { + if (size > kMaxRecordSize || fread(buf, size, 1, f) < 1) { read_failed = true; break; } -- cgit v0.12 From 251e476309723b82864db043ca1c3e405a25c358 Mon Sep 17 00:00:00 2001 From: Refael Ackermann Date: Thu, 19 Oct 2017 16:49:39 -0400 Subject: escape usage examples --- src/ninja.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index ed004ac..8d9e89c 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -212,10 +212,10 @@ void Usage(const BuildConfig& config) { " -n dry run (don't run commands but act like they succeeded)\n" " -v show all command lines while building\n" "\n" -" -d MODE enable debugging (use -d list to list modes)\n" -" -t TOOL run a subtool (use -t list to list subtools)\n" +" -d MODE enable debugging (use '-d list' to list modes)\n" +" -t TOOL run a subtool (use '-t list' to list subtools)\n" " terminates toplevel options; further flags are passed to the tool\n" -" -w FLAG adjust warnings (use -w list to list warnings)\n", +" -w FLAG adjust warnings (use '-w list' to list warnings)\n", kNinjaVersion, config.parallelism); } -- cgit v0.12 From 59849864592b421e0a8f993011e7e5c2ab27e77b Mon Sep 17 00:00:00 2001 From: Gergely Nagy Date: Tue, 31 Oct 2017 14:10:07 +0100 Subject: Fix building on Windows in UNICODE mode --- src/disk_interface.cc | 2 +- src/includes_normalize-win32.cc | 6 +++--- src/minidump-win32.cc | 6 +++--- src/msvc_helper-win32.cc | 12 ++++++------ src/msvc_helper_main-win32.cc | 2 +- src/subprocess-win32.cc | 11 ++++++----- src/util.cc | 9 ++------- 7 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 4b4c4c7..44b9491 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -70,7 +70,7 @@ TimeStamp TimeStampFromFileTime(const FILETIME& filetime) { TimeStamp StatSingleFile(const string& path, string* err) { WIN32_FILE_ATTRIBUTE_DATA attrs; - if (!GetFileAttributesEx(path.c_str(), GetFileExInfoStandard, &attrs)) { + if (!GetFileAttributesExA(path.c_str(), GetFileExInfoStandard, &attrs)) { DWORD win_err = GetLastError(); if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) return 0; diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc index 459329b..795542b 100644 --- a/src/includes_normalize-win32.cc +++ b/src/includes_normalize-win32.cc @@ -61,8 +61,8 @@ bool SameDrive(StringPiece a, StringPiece b) { char a_absolute[_MAX_PATH]; char b_absolute[_MAX_PATH]; - GetFullPathName(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL); - GetFullPathName(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL); + GetFullPathNameA(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL); + GetFullPathNameA(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL); char a_drive[_MAX_DIR]; char b_drive[_MAX_DIR]; _splitpath(a_absolute, a_drive, NULL, NULL, NULL); @@ -122,7 +122,7 @@ string IncludesNormalize::AbsPath(StringPiece s) { } char result[_MAX_PATH]; - GetFullPathName(s.AsString().c_str(), sizeof(result), result, NULL); + GetFullPathNameA(s.AsString().c_str(), sizeof(result), result, NULL); for (char* c = result; *c; ++c) if (*c == '\\') *c = '/'; diff --git a/src/minidump-win32.cc b/src/minidump-win32.cc index 1efb085..ca93638 100644 --- a/src/minidump-win32.cc +++ b/src/minidump-win32.cc @@ -32,17 +32,17 @@ typedef BOOL (WINAPI *MiniDumpWriteDumpFunc) ( /// Creates a windows minidump in temp folder. void CreateWin32MiniDump(_EXCEPTION_POINTERS* pep) { char temp_path[MAX_PATH]; - GetTempPath(sizeof(temp_path), temp_path); + GetTempPathA(sizeof(temp_path), temp_path); char temp_file[MAX_PATH]; sprintf(temp_file, "%s\\ninja_crash_dump_%lu.dmp", temp_path, GetCurrentProcessId()); // Delete any previous minidump of the same name. - DeleteFile(temp_file); + DeleteFileA(temp_file); // Load DbgHelp.dll dynamically, as library is not present on all // Windows versions. - HMODULE dbghelp = LoadLibrary("dbghelp.dll"); + HMODULE dbghelp = LoadLibraryA("dbghelp.dll"); if (dbghelp == NULL) { Error("failed to create minidump: LoadLibrary('dbghelp.dll'): %s", GetLastErrorString().c_str()); diff --git a/src/msvc_helper-win32.cc b/src/msvc_helper-win32.cc index e37a26e..de6147a 100644 --- a/src/msvc_helper-win32.cc +++ b/src/msvc_helper-win32.cc @@ -43,10 +43,10 @@ int CLWrapper::Run(const string& command, string* output) { security_attributes.bInheritHandle = TRUE; // Must be inheritable so subprocesses can dup to children. - HANDLE nul = CreateFile("NUL", GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | - FILE_SHARE_DELETE, - &security_attributes, OPEN_EXISTING, 0, NULL); + HANDLE nul = + CreateFileA("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); if (nul == INVALID_HANDLE_VALUE) Fatal("couldn't open nul"); @@ -58,8 +58,8 @@ int CLWrapper::Run(const string& command, string* output) { Win32Fatal("SetHandleInformation"); PROCESS_INFORMATION process_info = {}; - STARTUPINFO startup_info = {}; - startup_info.cb = sizeof(STARTUPINFO); + STARTUPINFOA startup_info = {}; + startup_info.cb = sizeof(STARTUPINFOA); startup_info.hStdInput = nul; startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); startup_info.hStdOutput = stdout_write; diff --git a/src/msvc_helper_main-win32.cc b/src/msvc_helper_main-win32.cc index e419cd7..644b2a2 100644 --- a/src/msvc_helper_main-win32.cc +++ b/src/msvc_helper_main-win32.cc @@ -113,7 +113,7 @@ int MSVCHelperMain(int argc, char** argv) { PushPathIntoEnvironment(env); } - char* command = GetCommandLine(); + char* command = GetCommandLineA(); command = strstr(command, " -- "); if (!command) { Fatal("expected command line to end with \" -- command args\""); diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc index 4bab719..5982b06 100644 --- a/src/subprocess-win32.cc +++ b/src/subprocess-win32.cc @@ -59,8 +59,8 @@ HANDLE Subprocess::SetupPipe(HANDLE ioport) { } // Get the write end of the pipe as a handle inheritable across processes. - HANDLE output_write_handle = CreateFile(pipe_name, GENERIC_WRITE, 0, - NULL, OPEN_EXISTING, 0, NULL); + HANDLE output_write_handle = + CreateFileA(pipe_name, GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); HANDLE output_write_child; if (!DuplicateHandle(GetCurrentProcess(), output_write_handle, GetCurrentProcess(), &output_write_child, @@ -80,9 +80,10 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); security_attributes.bInheritHandle = TRUE; // Must be inheritable so subprocesses can dup to children. - HANDLE nul = CreateFile("NUL", GENERIC_READ, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, - &security_attributes, OPEN_EXISTING, 0, NULL); + HANDLE nul = + CreateFileA("NUL", GENERIC_READ, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + &security_attributes, OPEN_EXISTING, 0, NULL); if (nul == INVALID_HANDLE_VALUE) Fatal("couldn't open nul"); diff --git a/src/util.cc b/src/util.cc index ae94d34..61a038b 100644 --- a/src/util.cc +++ b/src/util.cc @@ -318,13 +318,8 @@ int ReadFile(const string& path, string* contents, string* err) { // This makes a ninja run on a set of 1500 manifest files about 4% faster // than using the generic fopen code below. err->clear(); - HANDLE f = ::CreateFile(path.c_str(), - GENERIC_READ, - FILE_SHARE_READ, - NULL, - OPEN_EXISTING, - FILE_FLAG_SEQUENTIAL_SCAN, - NULL); + HANDLE f = ::CreateFileA(path.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, + OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL); if (f == INVALID_HANDLE_VALUE) { err->assign(GetLastErrorString()); return -ENOENT; -- cgit v0.12 From 5cc2d465fe5f408bd1718595d3a697d8bc75d973 Mon Sep 17 00:00:00 2001 From: Jerome Duval Date: Tue, 31 Oct 2017 21:37:20 +0100 Subject: posix_spawn_* calls don't set errno, use the return value. --- src/subprocess-posix.cc | 66 +++++++++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 27 deletions(-) diff --git a/src/subprocess-posix.cc b/src/subprocess-posix.cc index 1de22c3..53f5a3a 100644 --- a/src/subprocess-posix.cc +++ b/src/subprocess-posix.cc @@ -54,21 +54,25 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { SetCloseOnExec(fd_); posix_spawn_file_actions_t action; - if (posix_spawn_file_actions_init(&action) != 0) - Fatal("posix_spawn_file_actions_init: %s", strerror(errno)); + int err = posix_spawn_file_actions_init(&action); + if (err != 0) + Fatal("posix_spawn_file_actions_init: %s", strerror(err)); - if (posix_spawn_file_actions_addclose(&action, output_pipe[0]) != 0) - Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno)); + err = posix_spawn_file_actions_addclose(&action, output_pipe[0]); + if (err != 0) + Fatal("posix_spawn_file_actions_addclose: %s", strerror(err)); posix_spawnattr_t attr; - if (posix_spawnattr_init(&attr) != 0) - Fatal("posix_spawnattr_init: %s", strerror(errno)); + err = posix_spawnattr_init(&attr); + if (err != 0) + Fatal("posix_spawnattr_init: %s", strerror(err)); short flags = 0; flags |= POSIX_SPAWN_SETSIGMASK; - if (posix_spawnattr_setsigmask(&attr, &set->old_mask_) != 0) - Fatal("posix_spawnattr_setsigmask: %s", strerror(errno)); + err = posix_spawnattr_setsigmask(&attr, &set->old_mask_); + if (err != 0) + Fatal("posix_spawnattr_setsigmask: %s", strerror(err)); // Signals which are set to be caught in the calling process image are set to // default action in the new process image, so no explicit // POSIX_SPAWN_SETSIGDEF parameter is needed. @@ -79,17 +83,21 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { // No need to posix_spawnattr_setpgroup(&attr, 0), it's the default. // Open /dev/null over stdin. - if (posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY, - 0) != 0) { - Fatal("posix_spawn_file_actions_addopen: %s", strerror(errno)); + err = posix_spawn_file_actions_addopen(&action, 0, "/dev/null", O_RDONLY, + 0); + if (err != 0) { + Fatal("posix_spawn_file_actions_addopen: %s", strerror(err)); } - if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1) != 0) - Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); - if (posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2) != 0) - Fatal("posix_spawn_file_actions_adddup2: %s", strerror(errno)); - if (posix_spawn_file_actions_addclose(&action, output_pipe[1]) != 0) - Fatal("posix_spawn_file_actions_addclose: %s", strerror(errno)); + err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 1); + if (err != 0) + Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err)); + err = posix_spawn_file_actions_adddup2(&action, output_pipe[1], 2); + if (err != 0) + Fatal("posix_spawn_file_actions_adddup2: %s", strerror(err)); + err = posix_spawn_file_actions_addclose(&action, output_pipe[1]); + if (err != 0) + Fatal("posix_spawn_file_actions_addclose: %s", strerror(err)); // In the console case, output_pipe is still inherited by the child and // closed when the subprocess finishes, which then notifies ninja. } @@ -97,18 +105,22 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { flags |= POSIX_SPAWN_USEVFORK; #endif - if (posix_spawnattr_setflags(&attr, flags) != 0) - Fatal("posix_spawnattr_setflags: %s", strerror(errno)); + err = posix_spawnattr_setflags(&attr, flags); + if (err != 0) + Fatal("posix_spawnattr_setflags: %s", strerror(err)); const char* spawned_args[] = { "/bin/sh", "-c", command.c_str(), NULL }; - if (posix_spawn(&pid_, "/bin/sh", &action, &attr, - const_cast(spawned_args), environ) != 0) - Fatal("posix_spawn: %s", strerror(errno)); - - if (posix_spawnattr_destroy(&attr) != 0) - Fatal("posix_spawnattr_destroy: %s", strerror(errno)); - if (posix_spawn_file_actions_destroy(&action) != 0) - Fatal("posix_spawn_file_actions_destroy: %s", strerror(errno)); + err = posix_spawn(&pid_, "/bin/sh", &action, &attr, + const_cast(spawned_args), environ); + if (err != 0) + Fatal("posix_spawn: %s", strerror(err)); + + err = posix_spawnattr_destroy(&attr); + if (err != 0) + Fatal("posix_spawnattr_destroy: %s", strerror(err)); + err = posix_spawn_file_actions_destroy(&action); + if (err != 0) + Fatal("posix_spawn_file_actions_destroy: %s", strerror(err)); close(output_pipe[1]); return true; -- cgit v0.12 From bff884d550cdd4479b3a9aea29ebb0e9ff106e80 Mon Sep 17 00:00:00 2001 From: Mathias Stearn Date: Thu, 9 Nov 2017 12:00:04 -0500 Subject: Improve vim syntax definition Only highlights comments where the ninja lexer would treat them as such. Also correctly scopes the rule- and pool-specific variables highlighting. --- misc/ninja.vim | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/misc/ninja.vim b/misc/ninja.vim index 190d9ce..6912d0d 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -21,7 +21,10 @@ set cpo&vim syn case match -syn match ninjaComment /#.*/ contains=@Spell +" Comments are only matched when the # is at the beginning of the line (with +" optional whitespace), as long as the prior line didn't end with a $ +" continuation. +syn match ninjaComment /\(\$\n\)\@" " limited set of magic variables, 'build' allows general " let assignments. " manifest_parser.cc, ParseRule() -syn region ninjaRule start="^rule" end="^\ze\S" contains=ALL transparent -syn keyword ninjaRuleCommand contained command deps depfile description generator +syn region ninjaRule start="^rule" end="^\ze\S" contains=TOP transparent +syn keyword ninjaRuleCommand contained containedin=ninjaRule command + \ deps depfile description generator \ pool restat rspfile rspfile_content -syn region ninjaPool start="^pool" end="^\ze\S" contains=ALL transparent -syn keyword ninjaPoolCommand contained depth +syn region ninjaPool start="^pool" end="^\ze\S" contains=TOP transparent +syn keyword ninjaPoolCommand contained containedin=ninjaPool depth " Strings are parsed as follows: " lexer.in.cc, ReadEvalString() -- cgit v0.12 From c1c8bbaa5fc95ee90ce50d2aa5e16f4c86431114 Mon Sep 17 00:00:00 2001 From: Pawel Pluciennik Date: Fri, 24 Nov 2017 11:26:23 +0100 Subject: Flush changes into .ninja_log right away. --- src/build_log.cc | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/build_log.cc b/src/build_log.cc index 648617c..c75be95 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -167,6 +167,9 @@ bool BuildLog::RecordCommand(Edge* edge, int start_time, int end_time, if (log_file_) { if (!WriteEntry(log_file_, *log_entry)) return false; + if (fflush(log_file_) != 0) { + return false; + } } } return true; -- cgit v0.12 From ed11516a03583602fbaae2748f3d428542096136 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Fri, 15 Dec 2017 21:37:37 +0100 Subject: Fix disk_interface_test.cc on Windows for 64-bit timestamp subdir/subsubdir/.. seems to get the time of subdir/subsubdir on NTFS (Windows 7), not the time of subdir. --- src/disk_interface.cc | 10 ++++++++++ src/disk_interface_test.cc | 11 ++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 4b4c4c7..aba6ab2 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -112,6 +112,11 @@ bool StatAllFilesInDir(const string& dir, map* stamps, } do { string lowername = ffd.cFileName; + if (lowername == "..") { + // Seems to just copy the timestamp for ".." from ".", which is wrong. + // This is the case at least on NTFS under Windows 7. + continue; + } transform(lowername.begin(), lowername.end(), lowername.begin(), ::tolower); stamps->insert(make_pair(lowername, TimeStampFromFileTime(ffd.ftLastWriteTime))); @@ -164,6 +169,11 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { string dir = DirName(path); string base(path.substr(dir.size() ? dir.size() + 1 : 0)); + if (base == "..") { + // StatAllFilesInDir does not report any information for base = "..". + base = "."; + dir = path; + } transform(dir.begin(), dir.end(), dir.begin(), ::tolower); transform(base.begin(), base.end(), base.begin(), ::tolower); diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index d7fb8f8..81aa63a 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -87,6 +87,8 @@ TEST_F(DiskInterfaceTest, StatExistingDir) { string err; ASSERT_TRUE(disk_.MakeDir("subdir")); ASSERT_TRUE(disk_.MakeDir("subdir/subsubdir")); + EXPECT_GT(disk_.Stat("..", &err), 1); + EXPECT_EQ("", err); EXPECT_GT(disk_.Stat(".", &err), 1); EXPECT_EQ("", err); EXPECT_GT(disk_.Stat("subdir", &err), 1); @@ -105,7 +107,6 @@ TEST_F(DiskInterfaceTest, StatExistingDir) { #ifdef _WIN32 TEST_F(DiskInterfaceTest, StatCache) { string err; - disk_.AllowStatCache(true); ASSERT_TRUE(Touch("file1")); ASSERT_TRUE(Touch("fiLE2")); @@ -115,6 +116,10 @@ TEST_F(DiskInterfaceTest, StatCache) { ASSERT_TRUE(Touch("subdir\\SUBFILE2")); ASSERT_TRUE(Touch("subdir\\SUBFILE3")); + disk_.AllowStatCache(false); + TimeStamp parent_stat_uncached = disk_.Stat("..", &err); + disk_.AllowStatCache(true); + EXPECT_GT(disk_.Stat("FIle1", &err), 1); EXPECT_EQ("", err); EXPECT_GT(disk_.Stat("file1", &err), 1); @@ -125,6 +130,8 @@ TEST_F(DiskInterfaceTest, StatCache) { EXPECT_GT(disk_.Stat("sUbdir\\suBFile1", &err), 1); EXPECT_EQ("", err); + EXPECT_GT(disk_.Stat("..", &err), 1); + EXPECT_EQ("", err); EXPECT_GT(disk_.Stat(".", &err), 1); EXPECT_EQ("", err); EXPECT_GT(disk_.Stat("subdir", &err), 1); @@ -138,6 +145,8 @@ TEST_F(DiskInterfaceTest, StatCache) { EXPECT_EQ(disk_.Stat("subdir", &err), disk_.Stat("subdir/subsubdir/..", &err)); EXPECT_EQ("", err); + EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached); + EXPECT_EQ("", err); EXPECT_EQ(disk_.Stat("subdir/subsubdir", &err), disk_.Stat("subdir/subsubdir/.", &err)); EXPECT_EQ("", err); -- cgit v0.12 From 6c864097ef11da366fb4070e6ab9f34d6a293766 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Tue, 19 Dec 2017 14:49:07 +0100 Subject: Fix stat when subdirectory is a file Make sure that stat on Windows, both with and without cache, returns "missing file" when running stat on notadir/foo where notadir is a file. --- src/disk_interface.cc | 3 ++- src/disk_interface_test.cc | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index aba6ab2..0a5991d 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -105,7 +105,8 @@ bool StatAllFilesInDir(const string& dir, map* stamps, if (find_handle == INVALID_HANDLE_VALUE) { DWORD win_err = GetLastError(); - if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) + if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND || + win_err == ERROR_DIRECTORY) // File and not a directory return true; *err = "FindFirstFileExA(" + dir + "): " + GetLastErrorString(); return false; diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 81aa63a..5f7e468 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -63,6 +63,27 @@ TEST_F(DiskInterfaceTest, StatMissingFile) { EXPECT_EQ("", err); } +#ifdef _WIN32 +TEST_F(DiskInterfaceTest, StatMissingFileWithCache) { + string err; + disk_.AllowStatCache(true); + + EXPECT_EQ(0, disk_.Stat("nosuchfile", &err)); + EXPECT_EQ("", err); + + // On Windows, the errno for a file in a nonexistent directory + // is different. + EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err)); + EXPECT_EQ("", err); + + // On POSIX systems, the errno is different if a component of the + // path prefix is not a directory. + ASSERT_TRUE(Touch("notadir")); + EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err)); + EXPECT_EQ("", err); +} +#endif + TEST_F(DiskInterfaceTest, StatBadPath) { string err; #ifdef _WIN32 -- cgit v0.12 From 776b03c256ca140b17e7e3f3c76c979ad764079a Mon Sep 17 00:00:00 2001 From: Billy Donahue Date: Fri, 12 Jan 2018 13:31:06 -0500 Subject: Update Usage to show "-k 0" behavior For "-k N", N==0 is interpreted as infinite. It's useful but not documented in the help, unfortunately. --- src/ninja.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ninja.cc b/src/ninja.cc index 30f89c2..475d039 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -207,7 +207,7 @@ void Usage(const BuildConfig& config) { " -f FILE specify input build file [default=build.ninja]\n" "\n" " -j N run N jobs in parallel [default=%d, derived from CPUs available]\n" -" -k N keep going until N jobs fail [default=1]\n" +" -k N keep going until N jobs fail (0 means infinity) [default=1]\n" " -l N do not start new jobs if the load average is greater than N\n" " -n dry run (don't run commands but act like they succeeded)\n" " -v show all command lines while building\n" -- cgit v0.12 From 9a7ca218b8422fd2d870aa32753312d3b0521bc1 Mon Sep 17 00:00:00 2001 From: Scott Graham Date: Tue, 6 Feb 2018 21:59:16 -0800 Subject: Add 'output' field to compdb output --- src/ninja.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ninja.cc b/src/ninja.cc index 30f89c2..4a31d52 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -691,6 +691,8 @@ int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* a EncodeJSONString((*e)->EvaluateCommand().c_str()); printf("\",\n \"file\": \""); EncodeJSONString((*e)->inputs_[0]->path().c_str()); + printf("\",\n \"output\": \""); + EncodeJSONString((*e)->outputs_[0]->path().c_str()); printf("\"\n }"); first = false; -- cgit v0.12 From b4b283e7dc43f8b29f80b37a24f1f21ddf8640c6 Mon Sep 17 00:00:00 2001 From: Josh Gao Date: Mon, 19 Feb 2018 02:22:46 -0800 Subject: Don't clean up after ourselves when exiting. Destruction of NinjaMain can be an expensive operation when dealing with stupidly large ninjafiles. exit directly instead of returning out of real_main to avoid doing so. --- src/ninja.cc | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index 30f89c2..fee9374 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -1118,7 +1118,9 @@ int ReadFlags(int* argc, char*** argv, return -1; } -int real_main(int argc, char** argv) { +NORETURN void real_main(int argc, char** argv) { + // Use exit() instead of return in this function to avoid potentially + // expensive cleanup when destructing NinjaMain. BuildConfig config; Options options = {}; options.input_file = "build.ninja"; @@ -1128,7 +1130,7 @@ int real_main(int argc, char** argv) { int exit_code = ReadFlags(&argc, &argv, &options, &config); if (exit_code >= 0) - return exit_code; + exit(exit_code); if (options.working_dir) { // The formatting of this string, complete with funny quotes, is @@ -1147,7 +1149,7 @@ int real_main(int argc, char** argv) { // None of the RUN_AFTER_FLAGS actually use a NinjaMain, but it's needed // by other tools. NinjaMain ninja(ninja_command, config); - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); } // Limit number of rebuilds, to prevent infinite loops. @@ -1166,43 +1168,43 @@ int real_main(int argc, char** argv) { string err; if (!parser.Load(options.input_file, &err)) { Error("%s", err.c_str()); - return 1; + exit(1); } if (options.tool && options.tool->when == Tool::RUN_AFTER_LOAD) - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); if (!ninja.EnsureBuildDirExists()) - return 1; + exit(1); if (!ninja.OpenBuildLog() || !ninja.OpenDepsLog()) - return 1; + exit(1); if (options.tool && options.tool->when == Tool::RUN_AFTER_LOGS) - return (ninja.*options.tool->func)(&options, argc, argv); + exit((ninja.*options.tool->func)(&options, argc, argv)); // Attempt to rebuild the manifest before building anything else if (ninja.RebuildManifest(options.input_file, &err)) { // In dry_run mode the regeneration will succeed without changing the // manifest forever. Better to return immediately. if (config.dry_run) - return 0; + exit(0); // Start the build over with the new manifest. continue; } else if (!err.empty()) { Error("rebuilding '%s': %s", options.input_file, err.c_str()); - return 1; + exit(1); } int result = ninja.RunBuild(argc, argv); if (g_metrics) ninja.DumpMetrics(); - return result; + exit(result); } Error("manifest '%s' still dirty after %d tries\n", options.input_file, kCycleLimit); - return 1; + exit(1); } } // anonymous namespace @@ -1215,7 +1217,7 @@ int main(int argc, char** argv) { __try { // Running inside __try ... __except suppresses any Windows error // dialogs for errors such as bad_alloc. - return real_main(argc, argv); + real_main(argc, argv); } __except(ExceptionFilter(GetExceptionCode(), GetExceptionInformation())) { // Common error situations return exitCode=1. 2 was chosen to @@ -1223,6 +1225,6 @@ int main(int argc, char** argv) { return 2; } #else - return real_main(argc, argv); + real_main(argc, argv); #endif } -- cgit v0.12 From 85fab32362e2afe3c58dde00ca36676c9e29a9a6 Mon Sep 17 00:00:00 2001 From: Kareem Khazem Date: Tue, 20 Feb 2018 18:51:01 +0000 Subject: Support pool for Writer.build() in ninja_syntax.py ninja_syntax.py now supports a pool being specified for individual builds, as well as rules. --- misc/ninja_syntax.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 5c52ea2..051bac1 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -60,7 +60,7 @@ class Writer(object): self.variable('deps', deps, indent=1) def build(self, outputs, rule, inputs=None, implicit=None, order_only=None, - variables=None, implicit_outputs=None): + variables=None, implicit_outputs=None, pool=None): outputs = as_list(outputs) out_outputs = [escape_path(x) for x in outputs] all_inputs = [escape_path(x) for x in as_list(inputs)] @@ -81,6 +81,8 @@ class Writer(object): self._line('build %s: %s' % (' '.join(out_outputs), ' '.join([rule] + all_inputs))) + if pool is not None: + self._line(' pool = %s' % pool) if variables: if isinstance(variables, dict): -- cgit v0.12 From 1952afa56177d006077447477a237ad4e98270d5 Mon Sep 17 00:00:00 2001 From: alekseyshl Date: Mon, 5 Mar 2018 03:13:42 -0800 Subject: Fix build on Solaris. Solaris also does not define struct stat's st_mtimensec field. --- src/disk_interface.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 4b4c4c7..aceb575 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -195,11 +195,12 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL + st.st_mtimespec.tv_nsec); #elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \ - defined(__BIONIC__)) + defined(__BIONIC__) || (defined (__SVR4) && defined (__sun))) // For glibc, see "Timestamp files" in the Notes of http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html // newlib, uClibc and musl follow the kernel (or Cygwin) headers and define the right macro values above. // For bsd, see https://github.com/freebsd/freebsd/blob/master/sys/sys/stat.h and similar // For bionic, C and POSIX API is always enabled. + // For solaris, see https://docs.oracle.com/cd/E88353_01/html/E37841/stat-2.html. return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec; #else return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec; -- cgit v0.12 From b9d3ba2f169e172f2d73330f6a1d1f0436baa859 Mon Sep 17 00:00:00 2001 From: Frank Henigman Date: Sun, 17 Dec 2017 23:30:03 -0500 Subject: Canonicalize targets of clean command. This corrects an inconsistency where build targets were canonicalized but clean targets were not. For example you could build ./foo but not clean ./foo. --- src/clean.cc | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/src/clean.cc b/src/clean.cc index 1d6ba9e..4f31a03 100644 --- a/src/clean.cc +++ b/src/clean.cc @@ -180,15 +180,22 @@ int Cleaner::CleanTargets(int target_count, char* targets[]) { Reset(); PrintHeader(); for (int i = 0; i < target_count; ++i) { - const char* target_name = targets[i]; - Node* target = state_->LookupNode(target_name); - if (target) { - if (IsVerbose()) - printf("Target %s\n", target_name); - DoCleanTarget(target); - } else { - Error("unknown target '%s'", target_name); + string target_name = targets[i]; + uint64_t slash_bits; + string err; + if (!CanonicalizePath(&target_name, &slash_bits, &err)) { + Error("failed to canonicalize '%s': %s", target_name.c_str(), err.c_str()); status_ = 1; + } else { + Node* target = state_->LookupNode(target_name); + if (target) { + if (IsVerbose()) + printf("Target %s\n", target_name.c_str()); + DoCleanTarget(target); + } else { + Error("unknown target '%s'", target_name.c_str()); + status_ = 1; + } } } PrintFooter(); -- cgit v0.12 From b6296fbde37c677654bfeb7c64515870d54457fb Mon Sep 17 00:00:00 2001 From: Asanka Herath Date: Mon, 9 Jan 2017 14:52:54 -0500 Subject: [compdb] Expand response files inline based on a switch. References to response files in a clang compile_commands.json file can be tricky to deal with when tooling expects all the command flags to be present in the 'command' field. This change introduces a '-x' option to '-t compdb' that will expand @rspfile style response file invocations inline. E.g. ```sh $ ninja -t compdb cc [ { "directory": "/src/foo", "command": "cc -foo -bar @foo.obj.rsp", "file": "foo.cc" } ] $ ninja -t compdb -x cc [ { "directory": "/src/foo", "command": "cc -foo -bar foo.cc", "file": "foo.cc" } ] ``` --- src/ninja.cc | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 60 insertions(+), 2 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index 245cac1..bc86456 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -662,10 +662,68 @@ void EncodeJSONString(const char *str) { } } -int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* argv[]) { +enum EvaluateCommandMode { + ECM_NORMAL, + ECM_EXPAND_RSPFILE +}; +string EvaluateCommandWithRspfile(Edge* edge, EvaluateCommandMode mode) { + string command = edge->EvaluateCommand(); + if (mode == ECM_NORMAL) + return command; + + string rspfile = edge->GetUnescapedRspfile(); + if (rspfile.empty()) + return command; + + size_t index = command.find(rspfile); + if (index == 0 || index == string::npos || command[index - 1] != '@') + return command; + + string rspfile_content = edge->GetBinding("rspfile_content"); + size_t newline_index = 0; + while ((newline_index = rspfile_content.find('\n', newline_index)) != + string::npos) { + rspfile_content.replace(newline_index, 1, 1, ' '); + ++newline_index; + } + command.replace(index - 1, rspfile.length() + 1, rspfile_content); + return command; +} + +int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, + char* argv[]) { + // The compdb tool uses getopt, and expects argv[0] to contain the name of + // the tool, i.e. "compdb". + argc++; + argv--; + bool first = true; vector cwd; + EvaluateCommandMode eval_mode = ECM_NORMAL; + + optind = 1; + int opt; + while ((opt = getopt(argc, argv, const_cast("hx"))) != -1) { + switch(opt) { + case 'x': + eval_mode = ECM_EXPAND_RSPFILE; + break; + + case 'h': + default: + printf( + "usage: ninja -t compdb [options] [rules]\n" + "\n" + "options:\n" + " -x expand @rspfile style response file invocations\n" + ); + return 1; + } + } + argv += optind; + argc -= optind; + do { cwd.resize(cwd.size() + 1024); errno = 0; @@ -688,7 +746,7 @@ int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, char* a printf("\n {\n \"directory\": \""); EncodeJSONString(&cwd[0]); printf("\",\n \"command\": \""); - EncodeJSONString((*e)->EvaluateCommand().c_str()); + EncodeJSONString(EvaluateCommandWithRspfile(*e, eval_mode).c_str()); printf("\",\n \"file\": \""); EncodeJSONString((*e)->inputs_[0]->path().c_str()); printf("\",\n \"output\": \""); -- cgit v0.12 From e71bcceefb942f8355aab83ab447d702354ba272 Mon Sep 17 00:00:00 2001 From: Asanka Herath Date: Thu, 5 Apr 2018 11:18:09 -0400 Subject: [compdb] Move declarations closer to their use. --- src/ninja.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index bc86456..f4246d6 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -697,9 +697,6 @@ int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, argc++; argv--; - bool first = true; - vector cwd; - EvaluateCommandMode eval_mode = ECM_NORMAL; optind = 1; @@ -724,6 +721,9 @@ int NinjaMain::ToolCompilationDatabase(const Options* options, int argc, argv += optind; argc -= optind; + bool first = true; + vector cwd; + do { cwd.resize(cwd.size() + 1024); errno = 0; -- cgit v0.12 From dfed28c3073947cf6b77a53a6bab06285ff04a11 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 5 Apr 2018 12:25:02 -0400 Subject: make ninja build with -std=c++17 Ninja is supposed to be able to build as C++98 so it can run on old systems, but it should also be possible to optionally build it with newer dialects. --- src/build.cc | 7 ++++++- src/build.h | 4 ++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/build.cc b/src/build.cc index 61ef0e8..5239637 100644 --- a/src/build.cc +++ b/src/build.cc @@ -441,7 +441,12 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { vector::iterator begin = (*oe)->inputs_.begin(), end = (*oe)->inputs_.end() - (*oe)->order_only_deps_; - if (find_if(begin, end, mem_fun(&Node::dirty)) == end) { +#if __cplusplus < 201703L +#define MEM_FN mem_fun +#else +#define MEM_FN mem_fn // mem_fun was removed in C++17. +#endif + if (find_if(begin, end, MEM_FN(&Node::dirty)) == end) { // Recompute most_recent_input. Node* most_recent_input = NULL; for (vector::iterator i = begin; i != end; ++i) { diff --git a/src/build.h b/src/build.h index 43786f1..e38719c 100644 --- a/src/build.h +++ b/src/build.h @@ -178,7 +178,11 @@ struct Builder { State* state_; const BuildConfig& config_; Plan plan_; +#if __cplusplus < 201703L auto_ptr command_runner_; +#else + unique_ptr command_runner_; // auto_ptr was removed in C++17. +#endif BuildStatus* status_; private: -- cgit v0.12 From 52c1d0c8f8545231581c4d51cb0a85f50564c415 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 5 Apr 2018 13:23:28 -0400 Subject: Fix confusing smart console output from concurrent builds Developers tend to blame the last printed line when a build takes too long. Unfortunately, when building concurrently, the last printed line may have actually finished a long time ago. Under the current system, ninja does not update the status line to reflect what jobs are still running. This change makes ninja always print the oldest still running job instead. In other words, the likely build bottlenecks. Patch from David Zarzycki, originally uploaded at #1320. --- src/build.cc | 13 +++++++++++++ src/build.h | 1 + 2 files changed, 14 insertions(+) diff --git a/src/build.cc b/src/build.cc index 0eda16b..c24d6a9 100644 --- a/src/build.cc +++ b/src/build.cc @@ -16,6 +16,7 @@ #include #include +#include #include #include #include @@ -130,6 +131,18 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (!edge->use_console()) PrintStatus(edge, kEdgeFinished); + if (printer_.is_smart_terminal()) { + int oldest_start = INT_MAX; + Edge* oldest = NULL; + for (i = running_edges_.begin(); i != running_edges_.end(); i++) { + if (i->second < oldest_start) { + oldest_start = i->second; + oldest = i->first; + } + } + if (oldest) + PrintStatus(oldest, kEdgeRunning); + } // Print the command that is spewing before printing its output. if (!success) { diff --git a/src/build.h b/src/build.h index 9b90e8a..ac7f951 100644 --- a/src/build.h +++ b/src/build.h @@ -222,6 +222,7 @@ struct BuildStatus { enum EdgeStatus { kEdgeStarted, + kEdgeRunning, kEdgeFinished, }; -- cgit v0.12 From 42eefc854b32b3c1efc27548baaacdebad199853 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 5 Apr 2018 14:48:35 -0400 Subject: make `-w dupbuild` default to `err` You can still opt out of this by passing `-w dupbuild=warn`. But if you're getting this diagnostic, your build files are incorrect and you should ideally just fix them. This is step 3 on https://github.com/ninja-build/ninja/issues/931 I sent an RfC to ninja-build a few months ago; nobody objected. --- src/ninja.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/src/ninja.cc b/src/ninja.cc index f4246d6..f9efc77 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -1184,6 +1184,7 @@ NORETURN void real_main(int argc, char** argv) { BuildConfig config; Options options = {}; options.input_file = "build.ninja"; + options.dupe_edges_should_err = true; setvbuf(stdout, NULL, _IOLBF, BUFSIZ); const char* ninja_command = argv[0]; -- cgit v0.12 From 78759d09774d19904ede13eeab8340757e3ca9b0 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Thu, 5 Apr 2018 15:23:56 -0400 Subject: update ninja.vim version after bff884d55 --- misc/ninja.vim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/misc/ninja.vim b/misc/ninja.vim index 6912d0d..c1ffd50 100644 --- a/misc/ninja.vim +++ b/misc/ninja.vim @@ -1,8 +1,8 @@ " ninja build file syntax. " Language: ninja build file as described at " http://ninja-build.org/manual.html -" Version: 1.4 -" Last Change: 2014/05/13 +" Version: 1.5 +" Last Change: 2018/04/05 " Maintainer: Nicolas Weber " Version 1.4 of this script is in the upstream vim repository and will be " included in the next vim release. If you change this, please send your change -- cgit v0.12 From 834c674368748bff2e42c254a5191c97b12945d0 Mon Sep 17 00:00:00 2001 From: Takuto Ikuta Date: Fri, 6 Apr 2018 17:02:01 +0900 Subject: Add appveyor.yml --- appveyor.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..c8e1a9d --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,14 @@ +version: 1.0.{build} +image: Visual Studio 2017 +build_script: +- cmd: >- + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" + + python configure.py --bootstrap + + ninja.bootstrap.exe all + + ninja_test + + python misc/ninja_syntax_test.py +test: off -- cgit v0.12 From ccec031950b2c0cf1fa4895dc164807185823ccb Mon Sep 17 00:00:00 2001 From: Ryan Schmidt Date: Fri, 6 Apr 2018 03:15:01 -0500 Subject: Honor CXXFLAGS in configure.py --- configure.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/configure.py b/configure.py index a443748..9e23a5a 100755 --- a/configure.py +++ b/configure.py @@ -256,7 +256,7 @@ configure_args = sys.argv[1:] if '--bootstrap' in configure_args: configure_args.remove('--bootstrap') n.variable('configure_args', ' '.join(configure_args)) -env_keys = set(['CXX', 'AR', 'CFLAGS', 'LDFLAGS']) +env_keys = set(['CXX', 'AR', 'CFLAGS', 'CXXFLAGS', 'LDFLAGS']) configure_env = dict((k, os.environ[k]) for k in os.environ if k in env_keys) if configure_env: config_str = ' '.join([k + '=' + pipes.quote(configure_env[k]) @@ -397,6 +397,10 @@ def shell_escape(str): if 'CFLAGS' in configure_env: cflags.append(configure_env['CFLAGS']) + ldflags.append(configure_env['CFLAGS']) +if 'CXXFLAGS' in configure_env: + cflags.append(configure_env['CXXFLAGS']) + ldflags.append(configure_env['CXXFLAGS']) n.variable('cflags', ' '.join(shell_escape(flag) for flag in cflags)) if 'LDFLAGS' in configure_env: ldflags.append(configure_env['LDFLAGS']) -- cgit v0.12 From 001b1e3cf01aa4befe011b7f17bcd22c12d4c8c4 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 6 Apr 2018 12:43:49 -0400 Subject: Improve location of error messages around identifiers. Lexer::ReadIdent() now sets last_token_ before returning, like Lexer::ReadEvalString() does. So all "expected identifiers" and things that call ReadIdent (pool parser, rule parser, let parser, code parsing the rule name after a : in a build line) now point the "^ near here" at what was there instead of the previous last_token According to manifest_parser_perftest, this is perf-neutral. --- src/lexer.cc | 9 +++++++-- src/lexer.in.cc | 9 +++++++-- src/manifest_parser_test.cc | 18 +++++++++++++----- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/lexer.cc b/src/lexer.cc index 37b8678..49c69aa 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -537,8 +537,9 @@ yy92: bool Lexer::ReadIdent(string* out) { const char* p = ofs_; + const char* start; for (;;) { - const char* start = p; + start = p; { unsigned char yych; @@ -604,7 +605,10 @@ yy96: } yy97: ++p; - { return false; } + { + last_token_ = start; + return false; + } yy99: ++p; yych = *p; @@ -616,6 +620,7 @@ yy100: } } + last_token_ = start; ofs_ = p; EatWhitespace(); return true; diff --git a/src/lexer.in.cc b/src/lexer.in.cc index f861239..20ed442 100644 --- a/src/lexer.in.cc +++ b/src/lexer.in.cc @@ -182,16 +182,21 @@ void Lexer::EatWhitespace() { bool Lexer::ReadIdent(string* out) { const char* p = ofs_; + const char* start; for (;;) { - const char* start = p; + start = p; /*!re2c varname { out->assign(start, p - start); break; } - [^] { return false; } + [^] { + last_token_ = start; + return false; + } */ } + last_token_ = start; ofs_ = p; EatWhitespace(); return true; diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc index 39ed810..c91d8d1 100644 --- a/src/manifest_parser_test.cc +++ b/src/manifest_parser_test.cc @@ -523,7 +523,7 @@ TEST_F(ParserTest, Errors) { EXPECT_FALSE(parser.ParseTest("build x: y z\n", &err)); EXPECT_EQ("input:1: unknown build rule 'y'\n" "build x: y z\n" - " ^ near here" + " ^ near here" , err); } @@ -534,7 +534,7 @@ TEST_F(ParserTest, Errors) { EXPECT_FALSE(parser.ParseTest("build x:: y z\n", &err)); EXPECT_EQ("input:1: expected build command name\n" "build x:: y z\n" - " ^ near here" + " ^ near here" , err); } @@ -636,7 +636,10 @@ TEST_F(ParserTest, Errors) { string err; EXPECT_FALSE(parser.ParseTest("rule %foo\n", &err)); - EXPECT_EQ("input:1: expected rule name\n", err); + EXPECT_EQ("input:1: expected rule name\n" + "rule %foo\n" + " ^ near here", + err); } { @@ -672,7 +675,10 @@ TEST_F(ParserTest, Errors) { string err; EXPECT_FALSE(parser.ParseTest("rule cc\n command = foo\n && bar", &err)); - EXPECT_EQ("input:3: expected variable name\n", err); + EXPECT_EQ("input:3: expected variable name\n" + " && bar\n" + " ^ near here", + err); } { @@ -767,7 +773,9 @@ TEST_F(ParserTest, Errors) { ManifestParser parser(&local_state, NULL); string err; EXPECT_FALSE(parser.ParseTest("pool\n", &err)); - EXPECT_EQ("input:1: expected pool name\n", err); + EXPECT_EQ("input:1: expected pool name\n" + "pool\n" + " ^ near here", err); } { -- cgit v0.12 From 265a6eaf399778c746c7d2c02b8742f3c0ff07e9 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 6 Apr 2018 12:58:01 -0400 Subject: rename a variable --- src/lexer.cc | 10 +++++----- src/lexer.in.cc | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lexer.cc b/src/lexer.cc index 49c69aa..3c6e70e 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -23,14 +23,14 @@ bool Lexer::Error(const string& message, string* err) { // Compute line/column. int line = 1; - const char* context = input_.str_; + const char* line_start = input_.str_; for (const char* p = input_.str_; p < last_token_; ++p) { if (*p == '\n') { ++line; - context = p + 1; + line_start = p + 1; } } - int col = last_token_ ? (int)(last_token_ - context) : 0; + int col = last_token_ ? (int)(last_token_ - line_start) : 0; char buf[1024]; snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); @@ -43,12 +43,12 @@ bool Lexer::Error(const string& message, string* err) { int len; bool truncated = true; for (len = 0; len < kTruncateColumn; ++len) { - if (context[len] == 0 || context[len] == '\n') { + if (line_start[len] == 0 || line_start[len] == '\n') { truncated = false; break; } } - *err += string(context, len); + *err += string(line_start, len); if (truncated) *err += "..."; *err += "\n"; diff --git a/src/lexer.in.cc b/src/lexer.in.cc index 20ed442..c1fb822 100644 --- a/src/lexer.in.cc +++ b/src/lexer.in.cc @@ -22,14 +22,14 @@ bool Lexer::Error(const string& message, string* err) { // Compute line/column. int line = 1; - const char* context = input_.str_; + const char* line_start = input_.str_; for (const char* p = input_.str_; p < last_token_; ++p) { if (*p == '\n') { ++line; - context = p + 1; + line_start = p + 1; } } - int col = last_token_ ? (int)(last_token_ - context) : 0; + int col = last_token_ ? (int)(last_token_ - line_start) : 0; char buf[1024]; snprintf(buf, sizeof(buf), "%s:%d: ", filename_.AsString().c_str(), line); @@ -42,12 +42,12 @@ bool Lexer::Error(const string& message, string* err) { int len; bool truncated = true; for (len = 0; len < kTruncateColumn; ++len) { - if (context[len] == 0 || context[len] == '\n') { + if (line_start[len] == 0 || line_start[len] == '\n') { truncated = false; break; } } - *err += string(context, len); + *err += string(line_start, len); if (truncated) *err += "..."; *err += "\n"; -- cgit v0.12 From 49626c3610826a49f5fa7aee139415fe03d650fd Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Mon, 9 Apr 2018 21:27:31 +0200 Subject: Revert "Fix stat when subdirectory is a file" This reverts commit 6c864097ef11da366fb4070e6ab9f34d6a293766 and fixes the broken Appveyor builds on GitHub. --- src/disk_interface.cc | 3 +-- src/disk_interface_test.cc | 21 --------------------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 8334d69..504c679 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -105,8 +105,7 @@ bool StatAllFilesInDir(const string& dir, map* stamps, if (find_handle == INVALID_HANDLE_VALUE) { DWORD win_err = GetLastError(); - if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND || - win_err == ERROR_DIRECTORY) // File and not a directory + if (win_err == ERROR_FILE_NOT_FOUND || win_err == ERROR_PATH_NOT_FOUND) return true; *err = "FindFirstFileExA(" + dir + "): " + GetLastErrorString(); return false; diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 5f7e468..81aa63a 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -63,27 +63,6 @@ TEST_F(DiskInterfaceTest, StatMissingFile) { EXPECT_EQ("", err); } -#ifdef _WIN32 -TEST_F(DiskInterfaceTest, StatMissingFileWithCache) { - string err; - disk_.AllowStatCache(true); - - EXPECT_EQ(0, disk_.Stat("nosuchfile", &err)); - EXPECT_EQ("", err); - - // On Windows, the errno for a file in a nonexistent directory - // is different. - EXPECT_EQ(0, disk_.Stat("nosuchdir/nosuchfile", &err)); - EXPECT_EQ("", err); - - // On POSIX systems, the errno is different if a component of the - // path prefix is not a directory. - ASSERT_TRUE(Touch("notadir")); - EXPECT_EQ(0, disk_.Stat("notadir/nosuchfile", &err)); - EXPECT_EQ("", err); -} -#endif - TEST_F(DiskInterfaceTest, StatBadPath) { string err; #ifdef _WIN32 -- cgit v0.12 From 08ef815c7b55f28417b1a965eeab3640558d5f5a Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Sat, 7 Apr 2018 16:42:35 +0300 Subject: Add NINJA_FALLTHROUGH macro Borrow macro implementation from OpenSSL code. Add the macro after each fallthrough switch case to indicate our intention to the compiler. This silences GCC -Wimplicit-fallthrough warnings, which is implied by GCC 7.x -Wextra. --- src/build_log.cc | 6 ++++++ src/hash_map.h | 3 +++ src/util.cc | 2 +- src/util.h | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/build_log.cc b/src/build_log.cc index c75be95..2a65f9d 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -76,11 +76,17 @@ uint64_t MurmurHash64A(const void* key, size_t len) { switch (len & 7) { case 7: h ^= uint64_t(data[6]) << 48; + NINJA_FALLTHROUGH; case 6: h ^= uint64_t(data[5]) << 40; + NINJA_FALLTHROUGH; case 5: h ^= uint64_t(data[4]) << 32; + NINJA_FALLTHROUGH; case 4: h ^= uint64_t(data[3]) << 24; + NINJA_FALLTHROUGH; case 3: h ^= uint64_t(data[2]) << 16; + NINJA_FALLTHROUGH; case 2: h ^= uint64_t(data[1]) << 8; + NINJA_FALLTHROUGH; case 1: h ^= uint64_t(data[0]); h *= m; }; diff --git a/src/hash_map.h b/src/hash_map.h index a91aeb9..55d2c9d 100644 --- a/src/hash_map.h +++ b/src/hash_map.h @@ -18,6 +18,7 @@ #include #include #include "string_piece.h" +#include "util.h" // MurmurHash2, by Austin Appleby static inline @@ -40,7 +41,9 @@ unsigned int MurmurHash2(const void* key, size_t len) { } switch (len) { case 3: h ^= data[2] << 16; + NINJA_FALLTHROUGH; case 2: h ^= data[1] << 8; + NINJA_FALLTHROUGH; case 1: h ^= data[0]; h *= m; }; diff --git a/src/util.cc b/src/util.cc index 61a038b..760bc23 100644 --- a/src/util.cc +++ b/src/util.cc @@ -197,7 +197,7 @@ bool CanonicalizePath(char* path, size_t* len, uint64_t* slash_bits, case '\\': bits |= bits_mask; *c = '/'; - // Intentional fallthrough. + NINJA_FALLTHROUGH; case '/': bits_mask <<= 1; } diff --git a/src/util.h b/src/util.h index 4ee41a5..1b4227c 100644 --- a/src/util.h +++ b/src/util.h @@ -34,6 +34,20 @@ using namespace std; /// Log a fatal message and exit. NORETURN void Fatal(const char* msg, ...); +// Have a generic fall-through for different versions of C/C++. +#if defined(__cplusplus) && __cplusplus >= 201703L +#define NINJA_FALLTHROUGH [[fallthrough]] +#elif defined(__cplusplus) && __cplusplus >= 201103L && defined(__clang__) +#define NINJA_FALLTHROUGH [[clang::fallthrough]] +#elif defined(__cplusplus) && __cplusplus >= 201103L && defined(__GNUC__) && \ + __GNUC__ >= 7 +#define NINJA_FALLTHROUGH [[gnu::fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 // gcc 7 +#define NINJA_FALLTHROUGH __attribute__ ((fallthrough)) +#else // C++11 on gcc 6, and all other cases +#define NINJA_FALLTHROUGH +#endif + /// Log a warning message. void Warning(const char* msg, ...); -- cgit v0.12 From eb52bed89bb86c30111c6d6ee254f41b7c50f1c0 Mon Sep 17 00:00:00 2001 From: Christopher Waldon Date: Tue, 24 Apr 2018 23:52:58 -0400 Subject: Fix simple typo in HACKING.md --- HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.md b/HACKING.md index 9198bdf..5e88958 100644 --- a/HACKING.md +++ b/HACKING.md @@ -95,7 +95,7 @@ and run that directly on some representative input files. Generally it's the [Google C++ coding style][], but in brief: * Function name are camelcase. -* Member methods are camelcase, expect for trivial getters which are +* Member methods are camelcase, except for trivial getters which are underscore separated. * Local variables are underscore separated. * Member variables are underscore separated and suffixed by an extra -- cgit v0.12 From 056633514fcb735f2a11a1354d71896262e1b92c Mon Sep 17 00:00:00 2001 From: Vasili Skurydzin Date: Wed, 1 Aug 2018 12:31:42 -0400 Subject: Port to AIX platform. Taking care of printf format specifiers and large files using compiler macros in configure.py --- configure.py | 5 +++++ src/disk_interface.cc | 2 ++ 2 files changed, 7 insertions(+) diff --git a/configure.py b/configure.py index 9e23a5a..5841f8d 100755 --- a/configure.py +++ b/configure.py @@ -356,6 +356,11 @@ else: if platform.uses_usr_local(): cflags.append('-I/usr/local/include') ldflags.append('-L/usr/local/lib') + if platform.is_aix(): + # printf formats for int64_t, uint64_t; large file support + cflags.append('-D__STDC_FORMAT_MACROS') + cflags.append('-D_LARGE_FILES') + libs = [] diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 504c679..3fe35fe 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -212,6 +212,8 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { // For bionic, C and POSIX API is always enabled. // For solaris, see https://docs.oracle.com/cd/E88353_01/html/E37841/stat-2.html. return (int64_t)st.st_mtim.tv_sec * 1000000000LL + st.st_mtim.tv_nsec; +#elif defined(_AIX) + return (int64_t)st.st_mtime * 1000000000LL + st.st_mtime_n; #else return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec; #endif -- cgit v0.12 From 33448c830bc0c72a1bee767934121c08f45d45a7 Mon Sep 17 00:00:00 2001 From: Mo Zhou Date: Thu, 6 Sep 2018 04:09:48 +0000 Subject: Misc typo fixes by https://github.com/codespell-project/codespell/ --- misc/ninja-mode.el | 2 +- misc/ninja_syntax_test.py | 4 ++-- src/deps_log_test.cc | 4 ++-- src/ninja.cc | 2 +- src/state.h | 2 +- src/test.h | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/misc/ninja-mode.el b/misc/ninja-mode.el index 639e537..8b975d5 100644 --- a/misc/ninja-mode.el +++ b/misc/ninja-mode.el @@ -56,7 +56,7 @@ (save-excursion (goto-char (line-end-position 0)) (or - ;; If we're continuting the previous line, it's not a + ;; If we're continuing the previous line, it's not a ;; comment. (not (eq ?$ (char-before))) ;; Except if the previous line is a comment as well, as the diff --git a/misc/ninja_syntax_test.py b/misc/ninja_syntax_test.py index 07e3ed3..90ff9c6 100755 --- a/misc/ninja_syntax_test.py +++ b/misc/ninja_syntax_test.py @@ -46,13 +46,13 @@ class TestLineWordWrap(unittest.TestCase): self.out.getvalue()) def test_comment_wrap(self): - # Filenames shoud not be wrapped + # Filenames should not be wrapped self.n.comment('Hello /usr/local/build-tools/bin') self.assertEqual('# Hello\n# /usr/local/build-tools/bin\n', self.out.getvalue()) def test_short_words_indented(self): - # Test that indent is taking into acount when breaking subsequent lines. + # Test that indent is taking into account when breaking subsequent lines. # The second line should not be ' to tree', as that's longer than the # test layout width of 8. self.n._line('line_one to tree') diff --git a/src/deps_log_test.cc b/src/deps_log_test.cc index 89f7be1..0cdeb45 100644 --- a/src/deps_log_test.cc +++ b/src/deps_log_test.cc @@ -143,7 +143,7 @@ TEST_F(DepsLogTest, DoubleEntry) { ASSERT_GT(file_size, 0); } - // Now reload the file, and readd the same deps. + // Now reload the file, and read the same deps. { State state; DepsLog log; @@ -203,7 +203,7 @@ TEST_F(DepsLogTest, Recompact) { ASSERT_GT(file_size, 0); } - // Now reload the file, and add slighly different deps. + // Now reload the file, and add slightly different deps. int file_size_2; { State state; diff --git a/src/ninja.cc b/src/ninja.cc index f9efc77..8108f21 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -154,7 +154,7 @@ struct NinjaMain : public BuildLogUser { // Just checking n isn't enough: If an old output is both in the build log // and in the deps log, it will have a Node object in state_. (It will also // have an in edge if one of its inputs is another output that's in the deps - // log, but having a deps edge product an output thats input to another deps + // log, but having a deps edge product an output that's input to another deps // edge is rare, and the first recompaction will delete all old outputs from // the deps log, and then a second recompaction will clear the build log, // which seems good enough for this corner case.) diff --git a/src/state.h b/src/state.h index 54e9dc5..6fe886c 100644 --- a/src/state.h +++ b/src/state.h @@ -33,7 +33,7 @@ struct Rule; /// Pools are scoped to a State. Edges within a State will share Pools. A Pool /// will keep a count of the total 'weight' of the currently scheduled edges. If /// a Plan attempts to schedule an Edge which would cause the total weight to -/// exceed the depth of the Pool, the Pool will enque the Edge instead of +/// exceed the depth of the Pool, the Pool will enqueue the Edge instead of /// allowing the Plan to schedule it. The Pool will relinquish queued Edges when /// the total scheduled weight diminishes enough (i.e. when a scheduled edge /// completes). diff --git a/src/test.h b/src/test.h index 3bce8f7..6af17b3 100644 --- a/src/test.h +++ b/src/test.h @@ -104,7 +104,7 @@ extern testing::Test* g_current_test; } \ } -// Support utilites for tests. +// Support utilities for tests. struct Node; -- cgit v0.12 From 957d1990b6eb83e27127e725864dac8c4c9b652b Mon Sep 17 00:00:00 2001 From: Konstantin Kharlamov Date: Fri, 28 Sep 2018 15:22:46 +0300 Subject: Fix wrong description of script installation The described way of installation makes zsh fail with `_arguments:comparguments:325: can only be called from completion function`. Per [zsh documentation](https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org#telling-zsh-which-function-to-use-for-completing-a-command) the correct way is to use `$fpath`. --- misc/zsh-completion | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/zsh-completion b/misc/zsh-completion index bf23fac..4cee3b8 100644 --- a/misc/zsh-completion +++ b/misc/zsh-completion @@ -14,7 +14,7 @@ # limitations under the License. # Add the following to your .zshrc to tab-complete ninja targets -# . path/to/ninja/misc/zsh-completion +# fpath=(path/to/ninja/misc/zsh-completion $fpath) __get_targets() { dir="." -- cgit v0.12 From f56a83284495a2998e68797390d277ca94089aa2 Mon Sep 17 00:00:00 2001 From: David Emett Date: Sun, 20 Sep 2015 14:22:26 +0100 Subject: Query terminal width from STDOUT_FILENO Rather than 0 (stdin). So it will work even if ninja's stdin isn't connected to the terminal for whatever reason. --- src/line_printer.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/line_printer.cc b/src/line_printer.cc index 2cd3e17..6bf9e1b 100644 --- a/src/line_printer.cc +++ b/src/line_printer.cc @@ -82,7 +82,7 @@ void LinePrinter::Print(string to_print, LineType type) { // 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 ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &size) == 0) && size.ws_col) { to_print = ElideMiddle(to_print, size.ws_col); } printf("%s", to_print.c_str()); -- cgit v0.12 From 74f0355d8a0be644451ab2de460537bcae1de4c7 Mon Sep 17 00:00:00 2001 From: Ritesh Raj Sarraf Date: Fri, 12 Oct 2018 20:56:43 +0530 Subject: Replace `whoami` with a more generic command In our docker environment, the normal user does not have a name. This results in the `whoami` command to fail which expects a name to print Replace `whoami` with `id -u`, which print print the numeric id I have no name!@7427761b8f4c:/tmp/d$ whoami whoami: cannot find name for user ID 1000 I have no name!@7427761b8f4c:/tmp/d$ id uid=1000 gid=0(root) groups=0(root) Signed-off-by: Ritesh Raj Sarraf --- src/subprocess_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/subprocess_test.cc b/src/subprocess_test.cc index 0a8c206..6e487db 100644 --- a/src/subprocess_test.cc +++ b/src/subprocess_test.cc @@ -182,7 +182,7 @@ TEST_F(SubprocessTest, SetWithMulti) { "cmd /c echo hi", "cmd /c time /t", #else - "whoami", + "id -u", "pwd", #endif }; -- cgit v0.12 From b8834a2b3d81fdb2d89267795803b42f0aaf2fd2 Mon Sep 17 00:00:00 2001 From: Shoaib Meenai Date: Fri, 19 Oct 2018 13:11:49 -0700 Subject: Change gyp to gn Chrome has switched from gyp to gn, and Fuschia also uses it now. Note that the "Chromium Ninja documentation" link is dead, but I'm not sure what to replace it with. The closest I've found is https://chromium.googlesource.com/experimental/chromium/src/+/refs/wip/bajones/webvr/docs/ninja_build.md, but I'm not sure that's the original intended target. --- doc/manual.asciidoc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 9e55c02..25101b3 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -154,10 +154,10 @@ design is quite clever. Ninja's benefit comes from using it in conjunction with a smarter meta-build system. -http://code.google.com/p/gyp/[gyp]:: The meta-build system used to +https://gn.googlesource.com/gn/[gn]:: The meta-build system used to generate build files for Google Chrome and related projects (v8, -node.js). gyp can generate Ninja files for all platforms supported by -Chrome. See the +node.js), as well as Google Fuschia. gn can generate Ninja files for +all platforms supported by Chrome. See the https://chromium.googlesource.com/chromium/src/+/master/docs/ninja_build.md[Chromium Ninja documentation for more details]. https://cmake.org/[CMake]:: A widely used meta-build system that -- cgit v0.12 From 6e30a04bbe384269fa6a2d51a0a491a0a10b6e69 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Fri, 18 May 2018 14:21:59 +0200 Subject: browse.py: fix delay with multiple connections When Firefox opens two TCP connections and sends a request over the second connection, it is not processed until the first one is closed. Use a threaded server to solve this issue. Tested with Python 2.7.15 and 3.6.5. (Also tested with 2.6, but that failed due to lack of argparse.) Link: https://bugs.python.org/issue31639 --- src/browse.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/browse.py b/src/browse.py index 64a16f2..1c9c39b 100755 --- a/src/browse.py +++ b/src/browse.py @@ -24,8 +24,10 @@ from __future__ import print_function try: import http.server as httpserver + import socketserver except ImportError: import BaseHTTPServer as httpserver + import SocketServer as socketserver import argparse import cgi import os @@ -205,10 +207,14 @@ parser.add_argument('-f', default='build.ninja', parser.add_argument('initial_target', default='all', nargs='?', help='Initial target to show (default %(default)s)') +class HTTPServer(socketserver.ThreadingMixIn, httpserver.HTTPServer): + # terminate server immediately when Python exits. + daemon_threads = True + args = parser.parse_args() port = args.port hostname = args.hostname -httpd = httpserver.HTTPServer((hostname,port), RequestHandler) +httpd = HTTPServer((hostname,port), RequestHandler) try: if hostname == "": hostname = socket.gethostname() -- cgit v0.12 From febd3b37610c850ab0c35aa13a20c147419c13fd Mon Sep 17 00:00:00 2001 From: Simon Arlott Date: Thu, 25 Oct 2018 20:50:21 +0100 Subject: Fix compilation on FreeBSD 11.2 (use st_mtim.tv_nsec) ./src/disk_interface.cc: In member function 'virtual TimeStamp RealDiskInterface::Stat(const string&, std::__cxx11::string*) const': ./src/disk_interface.cc:216:51: error: 'struct stat' has no member named 'st_mtimensec'; did you mean 'st_mtim'? return (int64_t)st.st_mtime * 1000000000LL + st.st_mtimensec; ^~~~~~~~~~~~ st_mtim --- src/disk_interface.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/disk_interface.cc b/src/disk_interface.cc index 504c679..7eb44b2 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -205,7 +205,7 @@ TimeStamp RealDiskInterface::Stat(const string& path, string* err) const { return ((int64_t)st.st_mtimespec.tv_sec * 1000000000LL + st.st_mtimespec.tv_nsec); #elif (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700 || defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || \ - defined(__BIONIC__) || (defined (__SVR4) && defined (__sun))) + defined(__BIONIC__) || (defined (__SVR4) && defined (__sun)) || defined(__FreeBSD__)) // For glibc, see "Timestamp files" in the Notes of http://www.kernel.org/doc/man-pages/online/pages/man2/stat.2.html // newlib, uClibc and musl follow the kernel (or Cygwin) headers and define the right macro values above. // For bsd, see https://github.com/freebsd/freebsd/blob/master/sys/sys/stat.h and similar -- cgit v0.12 From cadcfc19ba6cfe38b09bccd44affd5c7e6143472 Mon Sep 17 00:00:00 2001 From: Andrew Maclean Date: Tue, 30 Oct 2018 21:12:13 +1100 Subject: Add more instructions for building ninja in Windows (#1169) This should clarify that the instructions are for Visual Studio. It also opens the possibility for others to write similar sections for using gcc/clang in Windows. --- HACKING.md | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/HACKING.md b/HACKING.md index 5e88958..5c2469b 100644 --- a/HACKING.md +++ b/HACKING.md @@ -19,8 +19,44 @@ You should end up with a `ninja` binary (or `ninja.exe`) in the project root. On Windows, you'll need to install Python to run `configure.py`, and run everything under a Visual Studio Tools Command Prompt (or after -running `vcvarsall` in a normal command prompt). See below if you -want to use mingw or some other compiler instead of Visual Studio. +running `vcvarsall` in a normal command prompt). + +For other combinations such as gcc/clang you will need the compiler +(gcc/cl) in your PATH and you will have to set the appropriate +platform configuration script. + +See below if you want to use mingw or some other compiler instead of +Visual Studio. + +##### Using Visual Studio +Assuming that you now have python installed, then the steps for building under + Windows using Visual Studio are: + +Clone and checkout the latest release (or whatever branch you want). You +can do this in either a command prompt or by opening a git bash prompt: + +``` + $ git clone git://github.com/ninja-build/ninja.git && cd ninja + $ git checkout release +``` + +Then: + +1. Open a Windows command prompt in the folder where you checked out ninja. +2. Select the Microsoft build environment by running +`vcvarsall.bat` with the appropriate environment. +3. Build ninja and test it. + +The steps for a Visual Studio 2015 64-bit build are outlined here: + +``` + > "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x64 + > python configure.py --bootstrap + > ninja --help +``` +Copy the ninja executable to another location, if desired, e.g. C:\local\Ninja. + +Finally add the path where ninja.exe is to the PATH variable. ### Adjusting build flags -- cgit v0.12 From d849e8fc856adf1e11cb5100cdf38afb72c09a02 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Fri, 2 Nov 2018 09:42:50 +0100 Subject: AppVeyor: Also build with MinGW --- appveyor.yml | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index c8e1a9d..4c64f29 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,14 +1,40 @@ version: 1.0.{build} image: Visual Studio 2017 -build_script: -- cmd: >- - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - python configure.py --bootstrap +environment: + CLICOLOR_FORCE: 1 + CHERE_INVOKING: 1 # Tell Bash to inherit the current working directory + matrix: + - MSYSTEM: MINGW64 + - MSYSTEM: MSVC - ninja.bootstrap.exe all +for: + - + matrix: + only: + - MSYSTEM: MINGW64 + build_script: + ps: "C:\\msys64\\usr\\bin\\bash -lc @\"\n + pacman -S --quiet --noconfirm --needed re2c 2>&1\n + sed -i 's|cmd /c $ar cqs $out.tmp $in && move /Y $out.tmp $out|$ar crs $out $in|g' configure.py\n + ./configure.py --bootstrap --platform mingw 2>&1\n + ./ninja all\n + ./ninja_test 2>&1\n + ./misc/ninja_syntax_test.py 2>&1\n\"@" + - + matrix: + only: + - MSYSTEM: MSVC + build_script: + - cmd: >- + call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" - ninja_test + python configure.py --bootstrap + + ninja.bootstrap.exe all + + ninja_test + + python misc/ninja_syntax_test.py - python misc/ninja_syntax_test.py test: off -- cgit v0.12 From 0eb7199902ce1566a39caa8f782c417a59095b6a Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Fri, 2 Nov 2018 11:01:34 +0100 Subject: Ignore Visual Studio Code project files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index a86205b..11150c9 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,6 @@ TAGS # Ninja output .ninja_deps .ninja_log + +# Visual Studio Code project files +/.vscode/ -- cgit v0.12 From 10e66a153daa7b8ce664948031136ba27e8dfeba Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Mon, 5 Nov 2018 16:48:31 +0100 Subject: Do not always strip colored output in verbose mode, fix #1214 --- src/build.cc | 2 +- src/line_printer.cc | 1 + src/line_printer.h | 5 +++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/build.cc b/src/build.cc index c24d6a9..9852a50 100644 --- a/src/build.cc +++ b/src/build.cc @@ -169,7 +169,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 (!printer_.is_smart_terminal()) + if (!printer_.supports_color()) final_output = StripAnsiEscapeCodes(output); else final_output = output; diff --git a/src/line_printer.cc b/src/line_printer.cc index 2cd3e17..cfc1f19 100644 --- a/src/line_printer.cc +++ b/src/line_printer.cc @@ -41,6 +41,7 @@ LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) { CONSOLE_SCREEN_BUFFER_INFO csbi; smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); #endif + supports_color_ = smart_terminal_; } void LinePrinter::Print(string to_print, LineType type) { diff --git a/src/line_printer.h b/src/line_printer.h index 55225e5..92d4dc4 100644 --- a/src/line_printer.h +++ b/src/line_printer.h @@ -27,6 +27,8 @@ struct LinePrinter { bool is_smart_terminal() const { return smart_terminal_; } void set_smart_terminal(bool smart) { smart_terminal_ = smart; } + bool supports_color() const { return supports_color_; } + enum LineType { FULL, ELIDE @@ -46,6 +48,9 @@ struct LinePrinter { /// Whether we can do fancy terminal control codes. bool smart_terminal_; + /// Whether we can use ISO 6429 (ANSI) color sequences. + bool supports_color_; + /// Whether the caret is at the beginning of a blank line. bool have_blank_line_; -- cgit v0.12 From 775c8e89ef6cae7b95a0cb8ccf48b990f4d9bc45 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Mon, 5 Nov 2018 18:18:51 +0100 Subject: Add script to test Ninja's output First test checks for #1214. --- .travis.yml | 7 +++++- misc/output_test.py | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100755 misc/output_test.py diff --git a/.travis.yml b/.travis.yml index 093139b..70b1fd0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,4 +3,9 @@ language: cpp compiler: - gcc - clang -script: ./configure.py --bootstrap && ./ninja all && ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots && ./misc/ninja_syntax_test.py +script: + - ./configure.py --bootstrap + - ./ninja all + - ./ninja_test --gtest_filter=-SubprocessTest.SetWithLots + - ./misc/ninja_syntax_test.py + - ./misc/output_test.py diff --git a/misc/output_test.py b/misc/output_test.py new file mode 100755 index 0000000..0651698 --- /dev/null +++ b/misc/output_test.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python3 + +"""Runs ./ninja and checks if the output is correct. + +In order to simulate a smart terminal it uses the 'script' command. +""" + +import subprocess +import sys +import tempfile +import unittest + +def run(build_ninja, flags='', pipe=False): + with tempfile.NamedTemporaryFile('w') as f: + f.write(build_ninja) + f.flush() + ninja_cmd = './ninja {} -f {}'.format(flags, f.name) + try: + if pipe: + output = subprocess.check_output([ninja_cmd], shell=True) + else: + output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null']) + except subprocess.CalledProcessError as err: + sys.stdout.buffer.write(err.output) + raise err + final_output = '' + for line in output.decode('utf-8').splitlines(True): + if len(line) > 0 and line[-1] == '\r': + continue + final_output += line.replace('\r', '') + return final_output + +class Output(unittest.TestCase): + def test_issue_1214(self): + print_red = '''rule echo + command = printf '\x1b[31mred\x1b[0m' + description = echo $out + +build a: echo +''' + # Only strip color when ninja's output is piped. + self.assertEqual(run(print_red), +'''[1/1] echo a\x1b[K +\x1b[31mred\x1b[0m +''') + self.assertEqual(run(print_red, pipe=True), +'''[1/1] echo a +red +''') + # Even in verbose mode, colors should still only be stripped when piped. + self.assertEqual(run(print_red, flags='-v'), +'''[1/1] printf '\x1b[31mred\x1b[0m' +\x1b[31mred\x1b[0m +''') + self.assertEqual(run(print_red, flags='-v', pipe=True), +'''[1/1] printf '\x1b[31mred\x1b[0m' +red +''') + +if __name__ == '__main__': + unittest.main() -- cgit v0.12 From 4baea2b826aa5fd356a1f90fef7a7e4a40b4d0b1 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Mon, 5 Nov 2018 18:51:08 +0100 Subject: Revert "Fix confusing smart console output from concurrent builds" This reverts commit 52c1d0c8f8545231581c4d51cb0a85f50564c415. Fixes #1418. --- src/build.cc | 13 ------------- src/build.h | 1 - 2 files changed, 14 deletions(-) diff --git a/src/build.cc b/src/build.cc index 9852a50..6b33024 100644 --- a/src/build.cc +++ b/src/build.cc @@ -16,7 +16,6 @@ #include #include -#include #include #include #include @@ -131,18 +130,6 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, if (!edge->use_console()) PrintStatus(edge, kEdgeFinished); - if (printer_.is_smart_terminal()) { - int oldest_start = INT_MAX; - Edge* oldest = NULL; - for (i = running_edges_.begin(); i != running_edges_.end(); i++) { - if (i->second < oldest_start) { - oldest_start = i->second; - oldest = i->first; - } - } - if (oldest) - PrintStatus(oldest, kEdgeRunning); - } // Print the command that is spewing before printing its output. if (!success) { diff --git a/src/build.h b/src/build.h index ac7f951..9b90e8a 100644 --- a/src/build.h +++ b/src/build.h @@ -222,7 +222,6 @@ struct BuildStatus { enum EdgeStatus { kEdgeStarted, - kEdgeRunning, kEdgeFinished, }; -- cgit v0.12 From 3841023a44621e8d8133e9eb6e79e26481dc0bc3 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Mon, 5 Nov 2018 18:52:51 +0100 Subject: Add test for #1418 (edge output should match status) --- misc/output_test.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/misc/output_test.py b/misc/output_test.py index 0651698..d19cebc 100755 --- a/misc/output_test.py +++ b/misc/output_test.py @@ -31,6 +31,27 @@ def run(build_ninja, flags='', pipe=False): return final_output class Output(unittest.TestCase): + def test_issue_1418(self): + self.assertEqual(run( +'''rule echo + command = sleep 0.$delay && echo $out + description = echo $out + +build a: echo + delay = 3 +build b: echo + delay = 2 +build c: echo + delay = 1 +'''), +'''[1/3] echo c\x1b[K +c +[2/3] echo b\x1b[K +b +[3/3] echo a\x1b[K +a +''') + def test_issue_1214(self): print_red = '''rule echo command = printf '\x1b[31mred\x1b[0m' -- cgit v0.12 From 19c294671c21b11b55f594c16aed372ee85dfd28 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Mon, 5 Nov 2018 21:46:34 +0100 Subject: Exclude broken DiskInterfaceTests from MSVC, see #1423 --- src/disk_interface_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 81aa63a..2de4f28 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -139,11 +139,13 @@ TEST_F(DiskInterfaceTest, StatCache) { EXPECT_GT(disk_.Stat("subdir/subsubdir", &err), 1); EXPECT_EQ("", err); +#ifndef _MSC_VER // TODO: Investigate why. Also see https://github.com/ninja-build/ninja/pull/1423 EXPECT_EQ(disk_.Stat("subdir", &err), disk_.Stat("subdir/.", &err)); EXPECT_EQ("", err); EXPECT_EQ(disk_.Stat("subdir", &err), disk_.Stat("subdir/subsubdir/..", &err)); +#endif EXPECT_EQ("", err); EXPECT_EQ(disk_.Stat("..", &err), parent_stat_uncached); EXPECT_EQ("", err); -- cgit v0.12 From edb848dd6c0a2c0a12eb9e8676c3012fc94e80ca Mon Sep 17 00:00:00 2001 From: Maciej Pawlowski Date: Wed, 7 Nov 2018 10:37:32 +0100 Subject: Regenerate depfile_parser.cc and lexer.cc with newer re2c --- src/depfile_parser.cc | 130 +++----- src/lexer.cc | 798 ++++++++++++++++++++++++-------------------------- 2 files changed, 423 insertions(+), 505 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 7cee892..6c0379e 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.16 */ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -82,88 +82,37 @@ bool DepfileParser::Parse(string* content, string* err) { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, }; - yych = *in; - if (yych <= '=') { - if (yych <= '$') { - if (yych <= ' ') { - if (yych <= 0x00) goto yy7; - goto yy9; - } else { - if (yych <= '!') goto yy5; - if (yych <= '#') goto yy9; - goto yy4; - } - } else { - if (yych <= '*') { - if (yych <= '\'') goto yy9; - if (yych <= ')') goto yy5; - goto yy9; - } else { - if (yych <= ':') goto yy5; - if (yych <= '<') goto yy9; - goto yy5; - } - } + if (yybm[0+yych] & 128) { + goto yy6; + } + if (yych <= '$') { + if (yych <= 0x00) goto yy2; + if (yych <= '#') goto yy4; + goto yy9; } else { - if (yych <= '_') { - if (yych <= '[') { - if (yych <= '?') goto yy9; - if (yych <= 'Z') goto yy5; - goto yy9; - } else { - if (yych <= '\\') goto yy2; - if (yych <= '^') goto yy9; - goto yy5; - } - } else { - if (yych <= '|') { - if (yych <= '`') goto yy9; - if (yych <= '{') goto yy5; - goto yy9; - } else { - if (yych == 0x7F) goto yy9; - goto yy5; - } - } + if (yych == '\\') goto yy10; + goto yy4; } yy2: ++in; - if ((yych = *in) <= '"') { - if (yych <= '\f') { - if (yych <= 0x00) goto yy3; - if (yych != '\n') goto yy14; - } else { - if (yych <= '\r') goto yy3; - if (yych == ' ') goto yy16; - goto yy14; - } - } else { - if (yych <= 'Z') { - if (yych <= '#') goto yy16; - if (yych == '*') goto yy16; - goto yy14; - } else { - if (yych <= '\\') goto yy16; - if (yych == '|') goto yy16; - goto yy14; - } + { + break; } -yy3: +yy4: + ++in; +yy5: { // For any other character (e.g. whitespace), swallow it here, // allowing the outer logic to loop around again. break; } -yy4: - yych = *++in; - if (yych == '$') goto yy12; - goto yy3; -yy5: +yy6: ++in; yych = *in; - goto yy11; -yy6: + if (yybm[0+yych] & 128) { + goto yy6; + } { // Got a span of plain text. int len = (int)(in - start); @@ -173,30 +122,41 @@ yy6: out += len; continue; } -yy7: - ++in; - { - break; - } yy9: yych = *++in; - goto yy3; + if (yych == '$') goto yy11; + goto yy5; yy10: - ++in; - yych = *in; -yy11: - if (yybm[0+yych] & 128) { - goto yy10; + yych = *++in; + if (yych <= '"') { + if (yych <= '\f') { + if (yych <= 0x00) goto yy5; + if (yych == '\n') goto yy5; + goto yy13; + } else { + if (yych <= '\r') goto yy5; + if (yych == ' ') goto yy15; + goto yy13; + } + } else { + if (yych <= 'Z') { + if (yych <= '#') goto yy15; + if (yych == '*') goto yy15; + goto yy13; + } else { + if (yych <= '\\') goto yy15; + if (yych == '|') goto yy15; + goto yy13; + } } - goto yy6; -yy12: +yy11: ++in; { // De-escape dollar character. *out++ = '$'; continue; } -yy14: +yy13: ++in; { // Let backslash before other characters through verbatim. @@ -204,7 +164,7 @@ yy14: *out++ = yych; continue; } -yy16: +yy15: ++in; { // De-escape backslashed character. diff --git a/src/lexer.cc b/src/lexer.cc index 3c6e70e..35ae97b 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -1,4 +1,4 @@ -/* Generated by re2c 0.13.5 */ +/* Generated by re2c 0.16 */ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -126,305 +126,325 @@ Lexer::Token Lexer::ReadToken() { unsigned char yych; 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, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 192, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 96, 96, 64, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 64, 64, 64, 64, 64, 64, - 64, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 96, - 64, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 96, 96, 96, 96, 96, - 96, 96, 96, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, - 64, 64, 64, 64, 64, 64, 64, 64, + 0, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 0, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 128, 128, 128, + 160, 128, 128, 128, 128, 128, 128, 128, + 128, 128, 128, 128, 128, 192, 192, 128, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 128, 128, 128, 128, 128, 128, + 128, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 128, 128, 128, 128, 192, + 128, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 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, 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, 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, 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, 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, }; - yych = *p; - if (yych <= 'Z') { - if (yych <= '#') { + if (yybm[0+yych] & 32) { + goto yy9; + } + if (yych <= '^') { + if (yych <= ',') { if (yych <= '\f') { - if (yych <= 0x00) goto yy23; - if (yych == '\n') goto yy7; - goto yy25; + if (yych <= 0x00) goto yy2; + if (yych == '\n') goto yy6; + goto yy4; } else { - if (yych <= 0x1F) { - if (yych <= '\r') goto yy6; - goto yy25; - } else { - if (yych <= ' ') goto yy2; - if (yych <= '"') goto yy25; - goto yy4; - } + if (yych <= '\r') goto yy8; + if (yych == '#') goto yy12; + goto yy4; } } else { - if (yych <= '9') { - if (yych <= ',') goto yy25; - if (yych == '/') goto yy25; - goto yy22; + if (yych <= ':') { + if (yych == '/') goto yy4; + if (yych <= '9') goto yy13; + goto yy16; } else { - if (yych <= '<') { - if (yych <= ':') goto yy16; - goto yy25; + if (yych <= '=') { + if (yych <= '<') goto yy4; + goto yy18; } else { - if (yych <= '=') goto yy14; - if (yych <= '@') goto yy25; - goto yy22; + if (yych <= '@') goto yy4; + if (yych <= 'Z') goto yy13; + goto yy4; } } } } else { if (yych <= 'i') { - if (yych <= 'a') { - if (yych == '_') goto yy22; - if (yych <= '`') goto yy25; - goto yy22; + if (yych <= 'b') { + if (yych == '`') goto yy4; + if (yych <= 'a') goto yy13; + goto yy20; } else { - if (yych <= 'c') { - if (yych <= 'b') goto yy9; - goto yy22; - } else { - if (yych <= 'd') goto yy13; - if (yych <= 'h') goto yy22; - goto yy20; - } + if (yych == 'd') goto yy21; + if (yych <= 'h') goto yy13; + goto yy22; } } else { if (yych <= 'r') { - if (yych == 'p') goto yy11; - if (yych <= 'q') goto yy22; - goto yy12; + if (yych == 'p') goto yy23; + if (yych <= 'q') goto yy13; + goto yy24; } else { if (yych <= 'z') { - if (yych <= 's') goto yy21; - goto yy22; + if (yych <= 's') goto yy25; + goto yy13; } else { - if (yych == '|') goto yy18; - goto yy25; + if (yych == '|') goto yy26; + goto yy4; } } } } yy2: - yyaccept = 0; - yych = *(q = ++p); - goto yy73; -yy3: - { token = INDENT; break; } + ++p; + { token = TEOF; break; } yy4: - yyaccept = 1; - yych = *(q = ++p); - if (yych >= 0x01) goto yy68; + ++p; yy5: { token = ERROR; break; } yy6: - yych = *++p; - if (yych == '\n') goto yy65; - goto yy5; -yy7: ++p; -yy8: { token = NEWLINE; break; } +yy8: + yych = *++p; + if (yych == '\n') goto yy28; + goto yy5; yy9: - ++p; - if ((yych = *p) == 'u') goto yy60; - goto yy27; -yy10: - { token = IDENT; break; } + yyaccept = 0; + q = ++p; + yych = *p; + if (yybm[0+yych] & 32) { + goto yy9; + } + if (yych <= '\f') { + if (yych == '\n') goto yy6; + } else { + if (yych <= '\r') goto yy30; + if (yych == '#') goto yy32; + } yy11: - yych = *++p; - if (yych == 'o') goto yy56; - goto yy27; + { token = INDENT; break; } yy12: - yych = *++p; - if (yych == 'u') goto yy52; - goto yy27; + yyaccept = 1; + yych = *(q = ++p); + if (yych <= 0x00) goto yy5; + goto yy33; yy13: - yych = *++p; - if (yych == 'e') goto yy45; - goto yy27; -yy14: ++p; - { token = EQUALS; break; } + yych = *p; +yy14: + if (yybm[0+yych] & 64) { + goto yy13; + } + { token = IDENT; break; } yy16: ++p; { token = COLON; break; } yy18: ++p; - if ((yych = *p) == '|') goto yy43; - { token = PIPE; break; } + { token = EQUALS; break; } yy20: yych = *++p; - if (yych == 'n') goto yy36; - goto yy27; + if (yych == 'u') goto yy36; + goto yy14; yy21: yych = *++p; - if (yych == 'u') goto yy28; - goto yy27; + if (yych == 'e') goto yy37; + goto yy14; yy22: yych = *++p; - goto yy27; + if (yych == 'n') goto yy38; + goto yy14; yy23: - ++p; - { token = TEOF; break; } + yych = *++p; + if (yych == 'o') goto yy39; + goto yy14; +yy24: + yych = *++p; + if (yych == 'u') goto yy40; + goto yy14; yy25: yych = *++p; - goto yy5; + if (yych == 'u') goto yy41; + goto yy14; yy26: ++p; - yych = *p; -yy27: - if (yybm[0+yych] & 32) { - goto yy26; - } - goto yy10; + if ((yych = *p) == '|') goto yy42; + { token = PIPE; break; } yy28: + ++p; + { token = NEWLINE; break; } +yy30: yych = *++p; - if (yych != 'b') goto yy27; - yych = *++p; - if (yych != 'n') goto yy27; - yych = *++p; - if (yych != 'i') goto yy27; - yych = *++p; - if (yych != 'n') goto yy27; - yych = *++p; - if (yych != 'j') goto yy27; - yych = *++p; - if (yych != 'a') goto yy27; + if (yych == '\n') goto yy28; +yy31: + p = q; + if (yyaccept == 0) { + goto yy11; + } else { + goto yy5; + } +yy32: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + yych = *p; +yy33: + if (yybm[0+yych] & 128) { + goto yy32; } - { token = SUBNINJA; break; } + if (yych <= 0x00) goto yy31; + ++p; + { continue; } yy36: yych = *++p; - if (yych != 'c') goto yy27; + if (yych == 'i') goto yy44; + goto yy14; +yy37: yych = *++p; - if (yych != 'l') goto yy27; + if (yych == 'f') goto yy45; + goto yy14; +yy38: yych = *++p; - if (yych != 'u') goto yy27; + if (yych == 'c') goto yy46; + goto yy14; +yy39: yych = *++p; - if (yych != 'd') goto yy27; + if (yych == 'o') goto yy47; + goto yy14; +yy40: yych = *++p; - if (yych != 'e') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = INCLUDE; break; } -yy43: + if (yych == 'l') goto yy48; + goto yy14; +yy41: + yych = *++p; + if (yych == 'b') goto yy49; + goto yy14; +yy42: ++p; { token = PIPE2; break; } +yy44: + yych = *++p; + if (yych == 'l') goto yy50; + goto yy14; yy45: yych = *++p; - if (yych != 'f') goto yy27; + if (yych == 'a') goto yy51; + goto yy14; +yy46: yych = *++p; - if (yych != 'a') goto yy27; + if (yych == 'l') goto yy52; + goto yy14; +yy47: yych = *++p; - if (yych != 'u') goto yy27; + if (yych == 'l') goto yy53; + goto yy14; +yy48: yych = *++p; - if (yych != 'l') goto yy27; + if (yych == 'e') goto yy55; + goto yy14; +yy49: yych = *++p; - if (yych != 't') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = DEFAULT; break; } -yy52: + if (yych == 'n') goto yy57; + goto yy14; +yy50: yych = *++p; - if (yych != 'l') goto yy27; + if (yych == 'd') goto yy58; + goto yy14; +yy51: + yych = *++p; + if (yych == 'u') goto yy60; + goto yy14; +yy52: yych = *++p; - if (yych != 'e') goto yy27; + if (yych == 'u') goto yy61; + goto yy14; +yy53: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; + } + { token = POOL; break; } +yy55: + ++p; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } { token = RULE; break; } -yy56: - yych = *++p; - if (yych != 'o') goto yy27; +yy57: yych = *++p; - if (yych != 'l') goto yy27; + if (yych == 'i') goto yy62; + goto yy14; +yy58: ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } - { token = POOL; break; } + { token = BUILD; break; } yy60: yych = *++p; - if (yych != 'i') goto yy27; + if (yych == 'l') goto yy63; + goto yy14; +yy61: yych = *++p; - if (yych != 'l') goto yy27; + if (yych == 'd') goto yy64; + goto yy14; +yy62: yych = *++p; - if (yych != 'd') goto yy27; - ++p; - if (yybm[0+(yych = *p)] & 32) { - goto yy26; - } - { token = BUILD; break; } + if (yych == 'n') goto yy65; + goto yy14; +yy63: + yych = *++p; + if (yych == 't') goto yy66; + goto yy14; +yy64: + yych = *++p; + if (yych == 'e') goto yy68; + goto yy14; yy65: + yych = *++p; + if (yych == 'j') goto yy70; + goto yy14; +yy66: ++p; - { token = NEWLINE; break; } -yy67: - ++p; - yych = *p; -yy68: - if (yybm[0+yych] & 64) { - goto yy67; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } - if (yych >= 0x01) goto yy70; -yy69: - p = q; - if (yyaccept <= 0) { - goto yy3; - } else { - goto yy5; - } -yy70: + { token = DEFAULT; break; } +yy68: ++p; - { continue; } -yy72: - yyaccept = 0; - q = ++p; - yych = *p; -yy73: - if (yybm[0+yych] & 128) { - goto yy72; - } - if (yych <= '\f') { - if (yych != '\n') goto yy3; - } else { - if (yych <= '\r') goto yy75; - if (yych == '#') goto yy67; - goto yy3; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; } + { token = INCLUDE; break; } +yy70: yych = *++p; - goto yy8; -yy75: + if (yych != 'a') goto yy14; ++p; - if ((yych = *p) == '\n') goto yy65; - goto yy69; + if (yybm[0+(yych = *p)] & 64) { + goto yy13; + } + { token = SUBNINJA; break; } } } @@ -487,49 +507,42 @@ void Lexer::EatWhitespace() { 0, 0, 0, 0, 0, 0, 0, 0, }; yych = *p; - if (yych <= ' ') { - if (yych <= 0x00) goto yy82; - if (yych <= 0x1F) goto yy84; - } else { - if (yych == '$') goto yy80; - goto yy84; + if (yybm[0+yych] & 128) { + goto yy79; } + if (yych <= 0x00) goto yy75; + if (yych == '$') goto yy82; + goto yy77; +yy75: ++p; - yych = *p; - goto yy92; -yy79: - { continue; } -yy80: - yych = *(q = ++p); - if (yych == '\n') goto yy85; - if (yych == '\r') goto yy87; -yy81: { break; } -yy82: +yy77: ++p; +yy78: { break; } -yy84: - yych = *++p; - goto yy81; -yy85: +yy79: ++p; + yych = *p; + if (yybm[0+yych] & 128) { + goto yy79; + } { continue; } -yy87: +yy82: + yych = *(q = ++p); + if (yych == '\n') goto yy83; + if (yych == '\r') goto yy85; + goto yy78; +yy83: + ++p; + { continue; } +yy85: yych = *++p; - if (yych == '\n') goto yy89; + if (yych == '\n') goto yy87; p = q; - goto yy81; -yy89: + goto yy78; +yy87: ++p; { continue; } -yy91: - ++p; - yych = *p; -yy92: - if (yybm[0+yych] & 128) { - goto yy91; - } - goto yy79; } } @@ -578,45 +591,24 @@ bool Lexer::ReadIdent(string* out) { 0, 0, 0, 0, 0, 0, 0, 0, }; yych = *p; - if (yych <= '@') { - if (yych <= '.') { - if (yych <= ',') goto yy97; - } else { - if (yych <= '/') goto yy97; - if (yych >= ':') goto yy97; - } - } else { - if (yych <= '_') { - if (yych <= 'Z') goto yy95; - if (yych <= '^') goto yy97; - } else { - if (yych <= '`') goto yy97; - if (yych >= '{') goto yy97; - } + if (yybm[0+yych] & 128) { + goto yy93; } -yy95: - ++p; - yych = *p; - goto yy100; -yy96: - { - out->assign(start, p - start); - break; - } -yy97: ++p; { last_token_ = start; return false; } -yy99: +yy93: ++p; yych = *p; -yy100: if (yybm[0+yych] & 128) { - goto yy99; + goto yy93; } - goto yy96; + { + out->assign(start, p - start); + break; + } } } @@ -636,72 +628,69 @@ 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, 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, - 128, 128, 128, 128, 128, 224, 160, 128, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 0, 128, 128, 128, 128, 128, - 128, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 128, 128, 128, 128, 224, - 128, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 224, 224, 224, 224, 224, - 224, 224, 224, 128, 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, - 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, 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, 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, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, + 0, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 0, 16, 16, 0, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 32, 16, 16, 16, 0, 16, 16, 16, + 16, 16, 16, 16, 16, 208, 144, 16, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 0, 16, 16, 16, 16, 16, + 16, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 16, 16, 16, 16, 208, + 16, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 208, 208, 208, 208, 208, + 208, 208, 208, 16, 0, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 16, }; yych = *p; - if (yych <= ' ') { - if (yych <= '\n') { - if (yych <= 0x00) goto yy110; - if (yych >= '\n') goto yy107; - } else { - if (yych == '\r') goto yy105; - if (yych >= ' ') goto yy107; - } + if (yybm[0+yych] & 16) { + goto yy100; + } + if (yych <= '\r') { + if (yych <= 0x00) goto yy98; + if (yych <= '\n') goto yy103; + goto yy105; } else { - if (yych <= '9') { - if (yych == '$') goto yy109; - } else { - if (yych <= ':') goto yy107; - if (yych == '|') goto yy107; - } + if (yych <= ' ') goto yy103; + if (yych <= '$') goto yy107; + goto yy103; } +yy98: ++p; - yych = *p; - goto yy140; -yy104: { - eval->AddText(StringPiece(start, p - start)); - continue; + last_token_ = start; + return Error("unexpected EOF", err); } -yy105: +yy100: ++p; - if ((yych = *p) == '\n') goto yy137; + yych = *p; + if (yybm[0+yych] & 16) { + goto yy100; + } { - last_token_ = start; - return Error(DescribeLastError(), err); + eval->AddText(StringPiece(start, p - start)); + continue; } -yy107: +yy103: ++p; { if (path) { @@ -714,152 +703,121 @@ yy107: continue; } } -yy109: +yy105: + ++p; + if ((yych = *p) == '\n') goto yy108; + { + last_token_ = start; + return Error(DescribeLastError(), err); + } +yy107: yych = *++p; - if (yych <= '-') { - if (yych <= 0x1F) { - if (yych <= '\n') { - if (yych <= '\t') goto yy112; - goto yy124; - } else { - if (yych == '\r') goto yy114; - goto yy112; - } + if (yybm[0+yych] & 64) { + goto yy120; + } + if (yych <= ' ') { + if (yych <= '\f') { + if (yych == '\n') goto yy112; + goto yy110; } else { - if (yych <= '#') { - if (yych <= ' ') goto yy115; - goto yy112; - } else { - if (yych <= '$') goto yy117; - if (yych <= ',') goto yy112; - goto yy119; - } + if (yych <= '\r') goto yy115; + if (yych <= 0x1F) goto yy110; + goto yy116; } } else { - if (yych <= 'Z') { - if (yych <= '9') { - if (yych <= '/') goto yy112; - goto yy119; - } else { - if (yych <= ':') goto yy121; - if (yych <= '@') goto yy112; - goto yy119; - } + if (yych <= '/') { + if (yych == '$') goto yy118; + goto yy110; } else { - if (yych <= '`') { - if (yych == '_') goto yy119; - goto yy112; - } else { - if (yych <= 'z') goto yy119; - if (yych <= '{') goto yy123; - goto yy112; - } + if (yych <= ':') goto yy123; + if (yych <= '`') goto yy110; + if (yych <= '{') goto yy125; + goto yy110; } } +yy108: + ++p; + { + if (path) + p = start; + break; + } yy110: ++p; +yy111: { last_token_ = start; - return Error("unexpected EOF", err); + return Error("bad $-escape (literal $ must be written as $$)", err); } yy112: ++p; -yy113: + yych = *p; + if (yybm[0+yych] & 32) { + goto yy112; + } { - last_token_ = start; - return Error("bad $-escape (literal $ must be written as $$)", err); + continue; } -yy114: - yych = *++p; - if (yych == '\n') goto yy134; - goto yy113; yy115: + yych = *++p; + if (yych == '\n') goto yy126; + goto yy111; +yy116: ++p; { eval->AddText(StringPiece(" ", 1)); continue; } -yy117: +yy118: ++p; { eval->AddText(StringPiece("$", 1)); continue; } -yy119: +yy120: ++p; yych = *p; - goto yy133; -yy120: + if (yybm[0+yych] & 64) { + goto yy120; + } { eval->AddSpecial(StringPiece(start + 1, p - start - 1)); continue; } -yy121: +yy123: ++p; { eval->AddText(StringPiece(":", 1)); continue; } -yy123: +yy125: yych = *(q = ++p); - if (yybm[0+yych] & 32) { - goto yy127; + if (yybm[0+yych] & 128) { + goto yy129; } - goto yy113; -yy124: + goto yy111; +yy126: ++p; yych = *p; - if (yybm[0+yych] & 16) { - goto yy124; - } + if (yych == ' ') goto yy126; { continue; } -yy127: +yy129: ++p; yych = *p; - if (yybm[0+yych] & 32) { - goto yy127; + if (yybm[0+yych] & 128) { + goto yy129; } - if (yych == '}') goto yy130; + if (yych == '}') goto yy132; p = q; - goto yy113; -yy130: - ++p; - { - eval->AddSpecial(StringPiece(start + 2, p - start - 3)); - continue; - } + goto yy111; yy132: ++p; - yych = *p; -yy133: - if (yybm[0+yych] & 64) { - goto yy132; - } - goto yy120; -yy134: - ++p; - yych = *p; - if (yych == ' ') goto yy134; { + eval->AddSpecial(StringPiece(start + 2, p - start - 3)); continue; } -yy137: - ++p; - { - if (path) - p = start; - break; - } -yy139: - ++p; - yych = *p; -yy140: - if (yybm[0+yych] & 128) { - goto yy139; - } - goto yy104; } } -- cgit v0.12 From 3edc4d4110c327cf81f2d87214b707ced5b22cd2 Mon Sep 17 00:00:00 2001 From: Maciej Pawlowski Date: Wed, 7 Nov 2018 10:42:07 +0100 Subject: Fix parsing some special chars in depfiles This allows paths with "[", "]" and "%" to appear in depfiles. Previously, only "[" would be handled properly. Fixes #1227. --- src/depfile_parser.cc | 4 ++-- src/depfile_parser.in.cc | 4 ++-- src/depfile_parser_test.cc | 7 +++++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 6c0379e..345fa15 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -53,7 +53,7 @@ bool DepfileParser::Parse(string* content, string* err) { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 0, 0, 0, 0, 0, 0, + 0, 128, 0, 0, 0, 128, 0, 0, 128, 128, 0, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 0, 0, 128, 0, 0, @@ -144,7 +144,7 @@ yy10: if (yych == '*') goto yy15; goto yy13; } else { - if (yych <= '\\') goto yy15; + if (yych <= ']') goto yy15; if (yych == '|') goto yy15; goto yy13; } diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index 98c1621..464efda 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -55,7 +55,7 @@ bool DepfileParser::Parse(string* content, string* err) { re2c:indent:string = " "; nul = "\000"; - escape = [ \\#*[|]; + escape = [ \\#*[|\]]; '\\' escape { // De-escape backslashed character. @@ -73,7 +73,7 @@ bool DepfileParser::Parse(string* content, string* err) { *out++ = yych; continue; } - [a-zA-Z0-9+,/_:.~()}{@=!\x80-\xFF-]+ { + [a-zA-Z0-9+,/_:.~()}{%@=!\x80-\xFF-]+ { // Got a span of plain text. int len = (int)(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 ee798f8..824073f 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -122,12 +122,13 @@ TEST_F(DepfileParserTest, SpecialChars) { "C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n" " en@quot.header~ t+t-x!=1 \n" " openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\n" -" Fu\303\244ball", +" Fu\303\244ball\n" +" a\\[1\\]b@2%c", &err)); ASSERT_EQ("", err); EXPECT_EQ("C:/Program Files (x86)/Microsoft crtdefs.h", parser_.out_.AsString()); - ASSERT_EQ(4u, parser_.ins_.size()); + ASSERT_EQ(5u, parser_.ins_.size()); EXPECT_EQ("en@quot.header~", parser_.ins_[0].AsString()); EXPECT_EQ("t+t-x!=1", @@ -136,6 +137,8 @@ TEST_F(DepfileParserTest, SpecialChars) { parser_.ins_[2].AsString()); EXPECT_EQ("Fu\303\244ball", parser_.ins_[3].AsString()); + EXPECT_EQ("a[1]b@2%c", + parser_.ins_[4].AsString()); } TEST_F(DepfileParserTest, UnifyMultipleOutputs) { -- cgit v0.12 From 0db30f237ccc66a4fdf19d88666fc1f7e415a562 Mon Sep 17 00:00:00 2001 From: ikifof <38361739+ikifof@users.noreply.github.com> Date: Wed, 7 Nov 2018 02:32:29 -0800 Subject: Fix older VS compatibility issues and PDB files generation issue. (#1435) Fixes #1411. --- configure.py | 23 +++++++++++++++-------- src/build_log.cc | 3 +++ src/deps_log.cc | 3 +++ 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/configure.py b/configure.py index cfe27f2..78cd1de 100755 --- a/configure.py +++ b/configure.py @@ -414,7 +414,7 @@ n.newline() if platform.is_msvc(): n.rule('cxx', - command='$cxx $cflags -c $in /Fo$out', + command='$cxx $cflags -c $in /Fo$out /Fd' + built('$pdb'), description='CXX $out', deps='msvc' # /showIncludes is included in $cflags. ) @@ -485,6 +485,9 @@ else: n.newline() n.comment('Core source files all build into ninja library.') +cxxvariables = [] +if platform.is_msvc(): + cxxvariables = [('pdb', 'ninja.pdb')] for name in ['build', 'build_log', 'clean', @@ -505,15 +508,15 @@ for name in ['build', 'string_piece_util', 'util', 'version']: - objs += cxx(name) + objs += cxx(name, variables=cxxvariables) if platform.is_windows(): for name in ['subprocess-win32', 'includes_normalize-win32', 'msvc_helper-win32', 'msvc_helper_main-win32']: - objs += cxx(name) + objs += cxx(name, variables=cxxvariables) if platform.is_msvc(): - objs += cxx('minidump-win32') + objs += cxx('minidump-win32', variables=cxxvariables) objs += cc('getopt') else: objs += cxx('subprocess-posix') @@ -536,7 +539,7 @@ if platform.is_aix(): all_targets = [] n.comment('Main executable is library plus main() function.') -objs = cxx('ninja') +objs = cxx('ninja', variables=cxxvariables) ninja = n.build(binary('ninja'), 'link', objs, implicit=ninja_lib, variables=[('libs', libs)]) n.newline() @@ -551,6 +554,8 @@ if options.bootstrap: n.comment('Tests all build into ninja_test executable.') objs = [] +if platform.is_msvc(): + cxxvariables = [('pdb', 'ninja_test.pdb')] for name in ['build_log_test', 'build_test', @@ -569,10 +574,10 @@ for name in ['build_log_test', 'subprocess_test', 'test', 'util_test']: - objs += cxx(name) + objs += cxx(name, variables=cxxvariables) if platform.is_windows(): for name in ['includes_normalize_test', 'msvc_helper_test']: - objs += cxx(name) + objs += cxx(name, variables=cxxvariables) ninja_test = n.build(binary('ninja_test'), 'link', objs, implicit=ninja_lib, variables=[('libs', libs)]) @@ -588,7 +593,9 @@ for name in ['build_log_perftest', 'hash_collision_bench', 'manifest_parser_perftest', 'clparser_perftest']: - objs = cxx(name) + if platform.is_msvc(): + cxxvariables = [('pdb', name + '.pdb')] + objs = cxx(name, variables=cxxvariables) all_targets += n.build(binary(name), 'link', objs, implicit=ninja_lib, variables=[('libs', libs)]) diff --git a/src/build_log.cc b/src/build_log.cc index 2a65f9d..c4a08a0 100644 --- a/src/build_log.cc +++ b/src/build_log.cc @@ -35,6 +35,9 @@ #include "graph.h" #include "metrics.h" #include "util.h" +#if defined(_MSC_VER) && (_MSC_VER < 1800) +#define strtoll _strtoi64 +#endif // Implementation details: // Each run's log appends to the log file. diff --git a/src/deps_log.cc b/src/deps_log.cc index eb81a37..0bb96f3 100644 --- a/src/deps_log.cc +++ b/src/deps_log.cc @@ -20,6 +20,9 @@ #include #ifndef _WIN32 #include +#elif defined(_MSC_VER) && (_MSC_VER < 1900) +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; #endif #include "graph.h" -- cgit v0.12 From 44ed3743af5ac879a53b16d58f05d97fe66a3ea4 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 7 Nov 2018 18:18:57 +0100 Subject: Make output_test.py independent of the environment --- misc/output_test.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/misc/output_test.py b/misc/output_test.py index d19cebc..6a5b635 100755 --- a/misc/output_test.py +++ b/misc/output_test.py @@ -5,21 +5,27 @@ In order to simulate a smart terminal it uses the 'script' command. """ +import os import subprocess import sys import tempfile import unittest def run(build_ninja, flags='', pipe=False): + env = dict(os.environ) + if 'NINJA_STATUS' in env: + del env['NINJA_STATUS'] + env['TERM'] = '' with tempfile.NamedTemporaryFile('w') as f: f.write(build_ninja) f.flush() ninja_cmd = './ninja {} -f {}'.format(flags, f.name) try: if pipe: - output = subprocess.check_output([ninja_cmd], shell=True) + output = subprocess.check_output([ninja_cmd], shell=True, env=env) else: - output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null']) + output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'], + env=env) except subprocess.CalledProcessError as err: sys.stdout.buffer.write(err.output) raise err -- cgit v0.12 From ef465615618d0cd129fd3102d904d7151dbaa1f5 Mon Sep 17 00:00:00 2001 From: Fredrik Medley Date: Wed, 6 Sep 2017 13:23:53 +0200 Subject: Verify GetFullPathName return value GetFullPathName previously failed silently on long path names resulting in uninitialized path result. Signed-off-by: Fredrik Medley --- src/includes_normalize-win32.cc | 55 ++++++++++++++++++++++++++++++++--------- src/includes_normalize.h | 4 +-- src/includes_normalize_test.cc | 29 +++++++++++++++++++++- 3 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/includes_normalize-win32.cc b/src/includes_normalize-win32.cc index 795542b..79bf5b4 100644 --- a/src/includes_normalize-win32.cc +++ b/src/includes_normalize-win32.cc @@ -26,6 +26,21 @@ namespace { +bool InternalGetFullPathName(const StringPiece& file_name, char* buffer, + size_t buffer_length, string *err) { + DWORD result_size = GetFullPathNameA(file_name.AsString().c_str(), + buffer_length, buffer, NULL); + if (result_size == 0) { + *err = "GetFullPathNameA(" + file_name.AsString() + "): " + + GetLastErrorString(); + return false; + } else if (result_size > buffer_length) { + *err = "path too long"; + return false; + } + return true; +} + bool IsPathSeparator(char c) { return c == '/' || c == '\\'; } @@ -54,15 +69,19 @@ bool SameDriveFast(StringPiece a, StringPiece b) { } // Return true if paths a and b are on the same Windows drive. -bool SameDrive(StringPiece a, StringPiece b) { +bool SameDrive(StringPiece a, StringPiece b, string* err) { if (SameDriveFast(a, b)) { return true; } char a_absolute[_MAX_PATH]; char b_absolute[_MAX_PATH]; - GetFullPathNameA(a.AsString().c_str(), sizeof(a_absolute), a_absolute, NULL); - GetFullPathNameA(b.AsString().c_str(), sizeof(b_absolute), b_absolute, NULL); + if (!InternalGetFullPathName(a, a_absolute, sizeof(a_absolute), err)) { + return false; + } + if (!InternalGetFullPathName(b, b_absolute, sizeof(b_absolute), err)) { + return false; + } char a_drive[_MAX_DIR]; char b_drive[_MAX_DIR]; _splitpath(a_absolute, a_drive, NULL, NULL, NULL); @@ -106,11 +125,15 @@ bool IsFullPathName(StringPiece s) { } // anonymous namespace IncludesNormalize::IncludesNormalize(const string& relative_to) { - relative_to_ = AbsPath(relative_to); + string err; + relative_to_ = AbsPath(relative_to, &err); + if (!err.empty()) { + Fatal("Initializing IncludesNormalize(): %s", err.c_str()); + } split_relative_to_ = SplitStringPiece(relative_to_, '/'); } -string IncludesNormalize::AbsPath(StringPiece s) { +string IncludesNormalize::AbsPath(StringPiece s, string* err) { if (IsFullPathName(s)) { string result = s.AsString(); for (size_t i = 0; i < result.size(); ++i) { @@ -122,7 +145,9 @@ string IncludesNormalize::AbsPath(StringPiece s) { } char result[_MAX_PATH]; - GetFullPathNameA(s.AsString().c_str(), sizeof(result), result, NULL); + if (!InternalGetFullPathName(s, result, sizeof(result), err)) { + return ""; + } for (char* c = result; *c; ++c) if (*c == '\\') *c = '/'; @@ -130,8 +155,10 @@ string IncludesNormalize::AbsPath(StringPiece s) { } string IncludesNormalize::Relativize( - StringPiece path, const vector& start_list) { - string abs_path = AbsPath(path); + StringPiece path, const vector& start_list, string* err) { + string abs_path = AbsPath(path, err); + if (!err->empty()) + return ""; vector path_list = SplitStringPiece(abs_path, '/'); int i; for (i = 0; i < static_cast(min(start_list.size(), path_list.size())); @@ -165,12 +192,18 @@ bool IncludesNormalize::Normalize(const string& input, if (!CanonicalizePath(copy, &len, &slash_bits, err)) return false; StringPiece partially_fixed(copy, len); - string abs_input = AbsPath(partially_fixed); + string abs_input = AbsPath(partially_fixed, err); + if (!err->empty()) + return false; - if (!SameDrive(abs_input, relative_to_)) { + if (!SameDrive(abs_input, relative_to_, err)) { + if (!err->empty()) + return false; *result = partially_fixed.AsString(); return true; } - *result = Relativize(abs_input, split_relative_to_); + *result = Relativize(abs_input, split_relative_to_, err); + if (!err->empty()) + return false; return true; } diff --git a/src/includes_normalize.h b/src/includes_normalize.h index 3811e53..0339581 100644 --- a/src/includes_normalize.h +++ b/src/includes_normalize.h @@ -25,9 +25,9 @@ struct IncludesNormalize { IncludesNormalize(const string& relative_to); // Internal utilities made available for testing, maybe useful otherwise. - static string AbsPath(StringPiece s); + static string AbsPath(StringPiece s, string* err); static string Relativize(StringPiece path, - const vector& start_list); + const vector& start_list, string* err); /// Normalize by fixing slashes style, fixing redundant .. and . and makes the /// path |input| relative to |this->relative_to_| and store to |result|. diff --git a/src/includes_normalize_test.cc b/src/includes_normalize_test.cc index eac36fd..dbcdbe0 100644 --- a/src/includes_normalize_test.cc +++ b/src/includes_normalize_test.cc @@ -58,9 +58,12 @@ TEST(IncludesNormalize, Simple) { } TEST(IncludesNormalize, WithRelative) { + string err; string currentdir = GetCurDir(); EXPECT_EQ("c", NormalizeRelativeAndCheckNoError("a/b/c", "a/b")); - EXPECT_EQ("a", NormalizeAndCheckNoError(IncludesNormalize::AbsPath("a"))); + EXPECT_EQ("a", + NormalizeAndCheckNoError(IncludesNormalize::AbsPath("a", &err))); + EXPECT_EQ("", err); EXPECT_EQ(string("../") + currentdir + string("/a"), NormalizeRelativeAndCheckNoError("a", "../b")); EXPECT_EQ(string("../") + currentdir + string("/a/b"), @@ -138,3 +141,27 @@ TEST(IncludesNormalize, LongInvalidPath) { EXPECT_EQ(forward_slashes.substr(cwd_len + 1), NormalizeAndCheckNoError(kExactlyMaxPath)); } + +TEST(IncludesNormalize, ShortRelativeButTooLongAbsolutePath) { + string result, err; + IncludesNormalize normalizer("."); + // A short path should work + EXPECT_TRUE(normalizer.Normalize("a", &result, &err)); + EXPECT_EQ("", err); + + // Construct max size path having cwd prefix. + // kExactlyMaxPath = "aaaa\\aaaa...aaaa\0"; + char kExactlyMaxPath[_MAX_PATH + 1]; + for (int i = 0; i < _MAX_PATH; ++i) { + if (i < _MAX_PATH - 1 && i % 10 == 4) + kExactlyMaxPath[i] = '\\'; + else + kExactlyMaxPath[i] = 'a'; + } + kExactlyMaxPath[_MAX_PATH] = '\0'; + EXPECT_EQ(strlen(kExactlyMaxPath), _MAX_PATH); + + // Make sure a path that's exactly _MAX_PATH long fails with a proper error. + EXPECT_FALSE(normalizer.Normalize(kExactlyMaxPath, &result, &err)); + EXPECT_TRUE(err.find("GetFullPathName") != string::npos); +} -- cgit v0.12 From d81cb42a372941c4e937498c33189575f1115e2c Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Mon, 2 Feb 2015 18:08:00 -0500 Subject: util: don't add ellipses width when deciding if they're necessary If the string fits, just use it. If we need the ellipses, *then* we need to compute the width based on that. --- src/util.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/util.cc b/src/util.cc index 760bc23..7bfe033 100644 --- a/src/util.cc +++ b/src/util.cc @@ -573,7 +573,7 @@ double GetLoadAverage() { string ElideMiddle(const string& str, size_t width) { const int kMargin = 3; // Space for "...". string result = str; - if (result.size() + kMargin > width) { + if (result.size() > width) { size_t elide_size = (width - kMargin) / 2; result = result.substr(0, elide_size) + "..." -- cgit v0.12 From 6a9411e784c58492e62a5dcb7d5d1aadfb2e1830 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 3 Oct 2018 09:31:01 -0400 Subject: manual: mention the "invalid parameter" case This happens often enough and the error message is quite unhelpful. Mention this error explicitly in the documentation. --- doc/manual.asciidoc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 9e55c02..db164e7 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -880,7 +880,8 @@ quoting rules are deterimined by the called program, which on Windows are usually provided by the C library. If you need shell interpretation of the command (such as the use of `&&` to chain multiple commands), make the command execute the Windows shell by -prefixing the command with `cmd /c`. +prefixing the command with `cmd /c`. Ninja may error with "invalid parameter" +which usually indicates that the command line length has been exceeded. [[ref_outputs]] Build outputs -- cgit v0.12 From e2aa04fdef84652492f7cd11ce9f4d971dd7a3c3 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 3 Oct 2018 09:31:02 -0400 Subject: Win32Fatal: support a "hint" for the error The callsite might have extra context which is helpful for interpreting the error message. --- src/util.cc | 8 ++++++-- src/util.h | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/util.cc b/src/util.cc index 760bc23..8e67fd9 100644 --- a/src/util.cc +++ b/src/util.cc @@ -432,8 +432,12 @@ string GetLastErrorString() { return msg; } -void Win32Fatal(const char* function) { - Fatal("%s: %s", function, GetLastErrorString().c_str()); +void Win32Fatal(const char* function, const char* hint) { + if (hint) { + Fatal("%s: %s (%s)", function, GetLastErrorString().c_str(), hint); + } else { + Fatal("%s: %s", function, GetLastErrorString().c_str()); + } } #endif diff --git a/src/util.h b/src/util.h index 1b4227c..6a4a7a9 100644 --- a/src/util.h +++ b/src/util.h @@ -119,7 +119,7 @@ bool Truncate(const string& path, size_t size, string* err); string GetLastErrorString(); /// Calls Fatal() with a function name and GetLastErrorString. -NORETURN void Win32Fatal(const char* function); +NORETURN void Win32Fatal(const char* function, const char* hint = NULL); #endif #endif // NINJA_UTIL_H_ -- cgit v0.12 From 1463fdc0f73136aca4869be9ad577d08528b19c4 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 3 Oct 2018 09:31:02 -0400 Subject: subprocess-win32: add hint on ERROR_INVALID_PARAMETER This is generally associated with the command line being too long. Give a hint to this case in the error message. --- src/subprocess-win32.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/subprocess-win32.cc b/src/subprocess-win32.cc index 5982b06..a4a7669 100644 --- a/src/subprocess-win32.cc +++ b/src/subprocess-win32.cc @@ -124,6 +124,10 @@ bool Subprocess::Start(SubprocessSet* set, const string& command) { buf_ = "CreateProcess failed: The system cannot find the file " "specified.\n"; return true; + } else if (error == ERROR_INVALID_PARAMETER) { + // This generally means that the command line was too long. Give extra + // context for this case. + Win32Fatal("CreateProcess", "is the command line too long?"); } else { Win32Fatal("CreateProcess"); // pass all other errors to Win32Fatal } -- cgit v0.12 From bf7107bb864d0383028202e3f4a4228c02302961 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Tue, 13 Nov 2018 15:15:43 +0100 Subject: Allow disabling of escape code stripping, fix #1475 Don't strip colors when CLICOLOR_FORCE is set to a non-zero value. This environment variable is also used by CMake's Make back-end. --- misc/output_test.py | 21 ++++++++++++++++----- src/build.cc | 1 - src/line_printer.cc | 4 ++++ 3 files changed, 20 insertions(+), 6 deletions(-) diff --git a/misc/output_test.py b/misc/output_test.py index 6a5b635..878de19 100755 --- a/misc/output_test.py +++ b/misc/output_test.py @@ -11,11 +11,14 @@ import sys import tempfile import unittest -def run(build_ninja, flags='', pipe=False): - env = dict(os.environ) - if 'NINJA_STATUS' in env: - del env['NINJA_STATUS'] - env['TERM'] = '' +default_env = dict(os.environ) +if 'NINJA_STATUS' in default_env: + del default_env['NINJA_STATUS'] +if 'CLICOLOR_FORCE' in default_env: + del default_env['CLICOLOR_FORCE'] +default_env['TERM'] = '' + +def run(build_ninja, flags='', pipe=False, env=default_env): with tempfile.NamedTemporaryFile('w') as f: f.write(build_ninja) f.flush() @@ -84,5 +87,13 @@ red red ''') + # CLICOLOR_FORCE=1 can be used to disable escape code stripping. + env = default_env.copy() + env['CLICOLOR_FORCE'] = '1' + self.assertEqual(run(print_red, pipe=True, env=env), +'''[1/1] echo a +\x1b[31mred\x1b[0m +''') + if __name__ == '__main__': unittest.main() diff --git a/src/build.cc b/src/build.cc index 6b33024..ed219fd 100644 --- a/src/build.cc +++ b/src/build.cc @@ -154,7 +154,6 @@ void BuildStatus::BuildEdgeFinished(Edge* edge, // (Launching subprocesses in pseudo ttys doesn't work because there are // only a few hundred available on some systems, and ninja can launch // thousands of parallel compile commands.) - // TODO: There should be a flag to disable escape code stripping. string final_output; if (!printer_.supports_color()) final_output = StripAnsiEscapeCodes(output); diff --git a/src/line_printer.cc b/src/line_printer.cc index a3a551e..6effca6 100644 --- a/src/line_printer.cc +++ b/src/line_printer.cc @@ -42,6 +42,10 @@ LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) { smart_terminal_ = GetConsoleScreenBufferInfo(console_, &csbi); #endif supports_color_ = smart_terminal_; + if (!supports_color_) { + const char* clicolor_force = getenv("CLICOLOR_FORCE"); + supports_color_ = clicolor_force && string(clicolor_force) != "0"; + } } void LinePrinter::Print(string to_print, LineType type) { -- cgit v0.12 From d3ea98a8c3ea1782eaead13259c83afcf02d8090 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Tue, 13 Nov 2018 15:17:43 +0100 Subject: Add unit test for #1491 --- src/util_test.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/util_test.cc b/src/util_test.cc index b4b7516..d97b48c 100644 --- a/src/util_test.cc +++ b/src/util_test.cc @@ -419,10 +419,12 @@ TEST(StripAnsiEscapeCodes, StripColors) { TEST(ElideMiddle, NothingToElide) { string input = "Nothing to elide in this short string."; EXPECT_EQ(input, ElideMiddle(input, 80)); + EXPECT_EQ(input, ElideMiddle(input, 38)); } TEST(ElideMiddle, ElideInTheMiddle) { string input = "01234567890123456789"; string elided = ElideMiddle(input, 10); EXPECT_EQ("012...789", elided); + EXPECT_EQ("01234567...23456789", ElideMiddle(input, 19)); } -- cgit v0.12 From bf7517505ad1def03e13bec2b4131399331bc5c4 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Tue, 13 Nov 2018 16:56:22 +0100 Subject: Add --verbose as an alternative spelling for -v, fix #1310 --- src/ninja.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/ninja.cc b/src/ninja.cc index 8108f21..b919950 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -210,7 +210,7 @@ void Usage(const BuildConfig& config) { " -k N keep going until N jobs fail (0 means infinity) [default=1]\n" " -l N do not start new jobs if the load average is greater than N\n" " -n dry run (don't run commands but act like they succeeded)\n" -" -v show all command lines while building\n" +" -v, --verbose show all command lines while building\n" "\n" " -d MODE enable debugging (use '-d list' to list modes)\n" " -t TOOL run a subtool (use '-t list' to list subtools)\n" @@ -1102,6 +1102,7 @@ int ReadFlags(int* argc, char*** argv, const option kLongOptions[] = { { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, OPT_VERSION }, + { "verbose", no_argument, NULL, 'v' }, { NULL, 0, NULL, 0 } }; -- cgit v0.12 From 1b8ab152eab6f2c910010a6d7cd847caf9ac1ff6 Mon Sep 17 00:00:00 2001 From: GoaLitiuM Date: Mon, 27 Aug 2018 04:43:33 +0300 Subject: Enable ANSI escape sequences on Windows 10 terminals --- src/line_printer.cc | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/line_printer.cc b/src/line_printer.cc index 6effca6..953982a 100644 --- a/src/line_printer.cc +++ b/src/line_printer.cc @@ -18,6 +18,9 @@ #include #ifdef _WIN32 #include +#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING +#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x4 +#endif #else #include #include @@ -46,6 +49,15 @@ LinePrinter::LinePrinter() : have_blank_line_(true), console_locked_(false) { const char* clicolor_force = getenv("CLICOLOR_FORCE"); supports_color_ = clicolor_force && string(clicolor_force) != "0"; } +#ifdef _WIN32 + // Try enabling ANSI escape sequence support on Windows 10 terminals. + if (supports_color_) { + DWORD mode; + if (GetConsoleMode(console_, &mode)) { + SetConsoleMode(console_, mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING); + } + } +#endif } void LinePrinter::Print(string to_print, LineType type) { -- cgit v0.12 From 378486ba6e5f15ebd86c76c330231319c2a6df59 Mon Sep 17 00:00:00 2001 From: Martell Malone Date: Wed, 14 Nov 2018 04:46:53 -0800 Subject: Make -j 0 run unlimited parallel builds, fix #1309 --- src/ninja.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index b919950..a6790e1 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -206,7 +206,7 @@ void Usage(const BuildConfig& config) { " -C DIR change to DIR before doing anything else\n" " -f FILE specify input build file [default=build.ninja]\n" "\n" -" -j N run N jobs in parallel [default=%d, derived from CPUs available]\n" +" -j N run N jobs in parallel (0 means infinity) [default=%d, derived from CPUs available]\n" " -k N keep going until N jobs fail (0 means infinity) [default=1]\n" " -l N do not start new jobs if the load average is greater than N\n" " -n dry run (don't run commands but act like they succeeded)\n" @@ -1121,9 +1121,12 @@ int ReadFlags(int* argc, char*** argv, case 'j': { char* end; int value = strtol(optarg, &end, 10); - if (*end != 0 || value <= 0) + if (*end != 0 || value < 0) Fatal("invalid -j parameter"); - config->parallelism = value; + + // We want to run N jobs in parallel. For N = 0, INT_MAX + // is close enough to infinite for most sane builds. + config->parallelism = value > 0 ? value : INT_MAX; break; } case 'k': { -- cgit v0.12 From 65f5d9633b984c4960d99a16728b32e025c95d38 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 14 Nov 2018 15:09:19 +0100 Subject: Fix rendering of code block, fix #955 --- doc/manual.asciidoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index 9e55c02..86d9aaa 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -593,7 +593,7 @@ Ninja supports this processing in two forms. to its stdout. Ninja then filters these lines from the displayed output. No `depfile` attribute is necessary, but the localized string in front of the the header file path. For instance - `msvc_deps_prefix = Note: including file: ` + `msvc_deps_prefix = Note: including file:` for a English Visual Studio (the default). Should be globally defined. + ---- -- cgit v0.12 From 4fd00f2681b95dd0532f754f95f9766cad5196a4 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 14 Nov 2018 09:52:49 -0500 Subject: HACKING: fix some whitespace nits --- HACKING.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/HACKING.md b/HACKING.md index 5c2469b..8ba960f 100644 --- a/HACKING.md +++ b/HACKING.md @@ -30,14 +30,14 @@ Visual Studio. ##### Using Visual Studio Assuming that you now have python installed, then the steps for building under - Windows using Visual Studio are: - +Windows using Visual Studio are: + Clone and checkout the latest release (or whatever branch you want). You can do this in either a command prompt or by opening a git bash prompt: ``` - $ git clone git://github.com/ninja-build/ninja.git && cd ninja - $ git checkout release + $ git clone git://github.com/ninja-build/ninja.git && cd ninja + $ git checkout release ``` Then: -- cgit v0.12 From 70017a9def99e537edf53ddbfea1110c7f191796 Mon Sep 17 00:00:00 2001 From: Ben Boeckel Date: Wed, 14 Nov 2018 09:53:08 -0500 Subject: HACKING: use `Python` for the project --- HACKING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HACKING.md b/HACKING.md index 8ba960f..086940b 100644 --- a/HACKING.md +++ b/HACKING.md @@ -29,7 +29,7 @@ See below if you want to use mingw or some other compiler instead of Visual Studio. ##### Using Visual Studio -Assuming that you now have python installed, then the steps for building under +Assuming that you now have Python installed, then the steps for building under Windows using Visual Studio are: Clone and checkout the latest release (or whatever branch you want). You -- cgit v0.12 From 2108bb31030e9109eb7911115d7ce0b1f6c238ea Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 14 Nov 2018 16:39:14 +0100 Subject: Explain why there's no browse tool, fix #1478 --- src/ninja.cc | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index a6790e1..1267b03 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -387,7 +387,12 @@ int NinjaMain::ToolBrowse(const Options* options, int argc, char* argv[]) { // If we get here, the browse failed. return 1; } -#endif // _WIN32 +#else +int NinjaMain::ToolBrowse(const Options*, int, char**) { + Fatal("browse tool not supported on this platform"); + return 1; +} +#endif #if defined(_MSC_VER) int NinjaMain::ToolMSVC(const Options* options, int argc, char* argv[]) { @@ -803,10 +808,8 @@ int NinjaMain::ToolUrtle(const Options* options, int argc, char** argv) { /// Returns a Tool, or NULL if Ninja should exit. const Tool* ChooseTool(const string& tool_name) { static const Tool kTools[] = { -#if defined(NINJA_HAVE_BROWSE) { "browse", "browse dependency graph in a web browser", Tool::RUN_AFTER_LOAD, &NinjaMain::ToolBrowse }, -#endif #if defined(_MSC_VER) { "msvc", "build helper for MSVC cl.exe (EXPERIMENTAL)", Tool::RUN_AFTER_FLAGS, &NinjaMain::ToolMSVC }, -- cgit v0.12 From a63fb13322aac3ca56e008757eb0514d96549ab5 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 14 Nov 2018 16:57:12 +0100 Subject: Improve error message when Python wasn't found for the browse tool See #1306. --- src/browse.cc | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/browse.cc b/src/browse.cc index 14900f8..c08c9f4 100644 --- a/src/browse.cc +++ b/src/browse.cc @@ -14,6 +14,7 @@ #include "browse.h" +#include #include #include #include @@ -57,7 +58,11 @@ void RunBrowsePython(State* state, const char* ninja_command, } command.push_back(NULL); execvp(command[0], (char**)&command[0]); - perror("ninja: execvp"); + if (errno == ENOENT) { + printf("ninja: %s is required for the browse tool\n", NINJA_PYTHON); + } else { + perror("ninja: execvp"); + } } while (false); _exit(1); } else { // Child. -- cgit v0.12 From cfd74e54fa30f507364bc2ecc4b4314ccbd60a1c Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Fri, 16 Nov 2018 14:07:45 +0100 Subject: Fit --help output into 80 columns and move verbose up, fix #1500 --- src/ninja.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ninja.cc b/src/ninja.cc index 1267b03..a3d73ad 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -201,16 +201,16 @@ void Usage(const BuildConfig& config) { "if targets are unspecified, builds the 'default' target (see manual).\n" "\n" "options:\n" -" --version print ninja version (\"%s\")\n" +" --version print ninja version (\"%s\")\n" +" -v, --verbose show all command lines while building\n" "\n" " -C DIR change to DIR before doing anything else\n" " -f FILE specify input build file [default=build.ninja]\n" "\n" -" -j N run N jobs in parallel (0 means infinity) [default=%d, derived from CPUs available]\n" +" -j N run N jobs in parallel (0 means infinity) [default=%d on this system]\n" " -k N keep going until N jobs fail (0 means infinity) [default=1]\n" " -l N do not start new jobs if the load average is greater than N\n" " -n dry run (don't run commands but act like they succeeded)\n" -" -v, --verbose show all command lines while building\n" "\n" " -d MODE enable debugging (use '-d list' to list modes)\n" " -t TOOL run a subtool (use '-t list' to list subtools)\n" -- cgit v0.12 From 5a78423193c48cae4800eb1ec44b62d14ae03920 Mon Sep 17 00:00:00 2001 From: Takuto Ikuta Date: Sun, 18 Nov 2018 02:23:28 +0900 Subject: Add OSX build on travis (#1502) Also make test script works for OSX script command. --- .travis.yml | 10 +++++++--- misc/output_test.py | 6 +++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 70b1fd0..19a9b28 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,12 @@ +matrix: + include: + - os: linux + compiler: gcc + - os: linux + compiler: clang + - os: osx sudo: false language: cpp -compiler: - - gcc - - clang script: - ./configure.py --bootstrap - ./ninja all diff --git a/misc/output_test.py b/misc/output_test.py index 878de19..1dcde10 100755 --- a/misc/output_test.py +++ b/misc/output_test.py @@ -6,6 +6,7 @@ In order to simulate a smart terminal it uses the 'script' command. """ import os +import platform import subprocess import sys import tempfile @@ -26,6 +27,9 @@ def run(build_ninja, flags='', pipe=False, env=default_env): try: if pipe: output = subprocess.check_output([ninja_cmd], shell=True, env=env) + elif platform.system() == 'Darwin': + output = subprocess.check_output(['script', '-q', '/dev/null', 'bash', '-c', ninja_cmd], + env=env) else: output = subprocess.check_output(['script', '-qfec', ninja_cmd, '/dev/null'], env=env) @@ -43,7 +47,7 @@ class Output(unittest.TestCase): def test_issue_1418(self): self.assertEqual(run( '''rule echo - command = sleep 0.$delay && echo $out + command = sleep $delay && echo $out description = echo $out build a: echo -- cgit v0.12 From a02132dac456875f395614bbc4cc2931a9ac409f Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 8 Oct 2018 06:52:52 -0400 Subject: Re-generate depfile parser with re2cc 1.0.1 --- src/depfile_parser.cc | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 345fa15..24e374f 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -1,4 +1,4 @@ -/* Generated by re2c 0.16 */ +/* Generated by re2c 1.1.1 */ // Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -108,8 +108,7 @@ yy5: break; } yy6: - ++in; - yych = *in; + yych = *++in; if (yybm[0+yych] & 128) { goto yy6; } -- cgit v0.12 From 94f4153da9dd2ad0c166568f273e9ba2f4928554 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 8 Oct 2018 09:01:59 -0400 Subject: Re-arrange depfile parser token processing logic Re-arrange the existing logic to support later addition of post-token code even for empty tokens. --- src/depfile_parser.cc | 21 ++++++++++----------- src/depfile_parser.in.cc | 21 ++++++++++----------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 24e374f..2ad2a00 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -175,22 +175,21 @@ yy15: } int len = (int)(out - filename); - const bool is_target = parsing_targets; + const bool is_dependency = !parsing_targets; if (len > 0 && filename[len - 1] == ':') { len--; // Strip off trailing colon, if any. parsing_targets = false; } - if (len == 0) - continue; - - if (!is_target) { - ins_.push_back(StringPiece(filename, len)); - } else if (!out_.str_) { - out_ = StringPiece(filename, len); - } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; + if (len > 0) { + if (is_dependency) { + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + *err = "depfile has multiple output paths"; + return false; + } } } if (parsing_targets) { diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index 464efda..4df8ce2 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -94,22 +94,21 @@ bool DepfileParser::Parse(string* content, string* err) { } int len = (int)(out - filename); - const bool is_target = parsing_targets; + const bool is_dependency = !parsing_targets; if (len > 0 && filename[len - 1] == ':') { len--; // Strip off trailing colon, if any. parsing_targets = false; } - if (len == 0) - continue; - - if (!is_target) { - ins_.push_back(StringPiece(filename, len)); - } else if (!out_.str_) { - out_ = StringPiece(filename, len); - } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; + if (len > 0) { + if (is_dependency) { + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + *err = "depfile has multiple output paths"; + return false; + } } } if (parsing_targets) { -- cgit v0.12 From 6d6dfd17e83bbde57bee61a0956a73b12088a9d1 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 19 Oct 2015 14:16:29 -0400 Subject: Fix depfile parser test case line continuation Escape newlines for line continuation in the SpecialChars test case. Otherwise the test does not cover valid Makefile syntax. The missing line continuation was tolerated by our parser but is never (not) produced by a real compiler. --- src/depfile_parser_test.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index 824073f..e3eec07 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -119,10 +119,10 @@ TEST_F(DepfileParserTest, SpecialChars) { // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/ string err; EXPECT_TRUE(Parse( -"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n" -" en@quot.header~ t+t-x!=1 \n" -" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\n" -" Fu\303\244ball\n" +"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \\\n" +" en@quot.header~ t+t-x!=1 \\\n" +" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\\\n" +" Fu\303\244ball\\\n" " a\\[1\\]b@2%c", &err)); ASSERT_EQ("", err); -- cgit v0.12 From 4a4f9d40e178a9a9e88f4cd502d2be49bf7938d8 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 19 Oct 2015 14:25:03 -0400 Subject: Fix depfile parser handling of multiple rules Currently we handle Makefile rules of the form: out: in1 in2 in3 and the form: out: in1 \ in2 \ in3 Teach the depfile parser to handle the additional form: out: in1 out: in2 out: in3 This is also valid Makefile syntax and is the depfile format generated by the Intel Compiler for Windows. Note that the `gcc -MP` option adds empty phony rules to the generated Makefile fragment: out: in1 in2 in3 in1: in2: in3: Previously we tolerated these because they were treated as inputs, which was accidentally correct. Instead we must now tolerate these by ignoring targets for which no dependencies are specified. --- src/depfile_parser.cc | 104 +++++++++++++++++++++++++++----------- src/depfile_parser.in.cc | 31 ++++++++++-- src/depfile_parser_test.cc | 123 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+), 33 deletions(-) diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 2ad2a00..7724eed 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -35,8 +35,11 @@ bool DepfileParser::Parse(string* content, string* err) { // parsing_targets: whether we are parsing targets or dependencies. char* in = &(*content)[0]; char* end = in + content->size(); + bool have_target = false; + bool have_secondary_target_on_this_rule = false; bool parsing_targets = true; while (in < end) { + bool have_newline = false; // out: current output point (typically same as in, but can fall behind // as we de-escape backslashes). char* out = in; @@ -45,6 +48,7 @@ bool DepfileParser::Parse(string* content, string* err) { for (;;) { // start: beginning of the current parsed span. const char* start = in; + char* yymarker = NULL; { unsigned char yych; @@ -84,17 +88,25 @@ bool DepfileParser::Parse(string* content, string* err) { }; yych = *in; if (yybm[0+yych] & 128) { - goto yy6; - } - if (yych <= '$') { - if (yych <= 0x00) goto yy2; - if (yych <= '#') goto yy4; goto yy9; + } + if (yych <= '\r') { + if (yych <= '\t') { + if (yych >= 0x01) goto yy4; + } else { + if (yych <= '\n') goto yy6; + if (yych <= '\f') goto yy4; + goto yy8; + } } else { - if (yych == '\\') goto yy10; - goto yy4; + if (yych <= '$') { + if (yych <= '#') goto yy4; + goto yy12; + } else { + if (yych == '\\') goto yy13; + goto yy4; + } } -yy2: ++in; { break; @@ -108,9 +120,20 @@ yy5: break; } yy6: + ++in; + { + // A newline ends the current file name and the current rule. + have_newline = true; + break; + } +yy8: + yych = *++in; + if (yych == '\n') goto yy6; + goto yy5; +yy9: yych = *++in; if (yybm[0+yych] & 128) { - goto yy6; + goto yy9; } { // Got a span of plain text. @@ -121,41 +144,41 @@ yy6: out += len; continue; } -yy9: +yy12: yych = *++in; - if (yych == '$') goto yy11; + if (yych == '$') goto yy14; goto yy5; -yy10: - yych = *++in; +yy13: + yych = *(yymarker = ++in); if (yych <= '"') { if (yych <= '\f') { if (yych <= 0x00) goto yy5; - if (yych == '\n') goto yy5; - goto yy13; + if (yych == '\n') goto yy18; + goto yy16; } else { - if (yych <= '\r') goto yy5; - if (yych == ' ') goto yy15; - goto yy13; + if (yych <= '\r') goto yy20; + if (yych == ' ') goto yy22; + goto yy16; } } else { if (yych <= 'Z') { - if (yych <= '#') goto yy15; - if (yych == '*') goto yy15; - goto yy13; + if (yych <= '#') goto yy22; + if (yych == '*') goto yy22; + goto yy16; } else { - if (yych <= ']') goto yy15; - if (yych == '|') goto yy15; - goto yy13; + if (yych <= ']') goto yy22; + if (yych == '|') goto yy22; + goto yy16; } } -yy11: +yy14: ++in; { // De-escape dollar character. *out++ = '$'; continue; } -yy13: +yy16: ++in; { // Let backslash before other characters through verbatim. @@ -163,7 +186,18 @@ yy13: *out++ = yych; continue; } -yy15: +yy18: + ++in; + { + // A line continuation ends the current file name. + break; + } +yy20: + yych = *++in; + if (yych == '\n') goto yy18; + in = yymarker; + goto yy5; +yy22: ++in; { // De-escape backslashed character. @@ -179,20 +213,30 @@ yy15: if (len > 0 && filename[len - 1] == ':') { len--; // Strip off trailing colon, if any. parsing_targets = false; + have_target = true; } if (len > 0) { if (is_dependency) { + if (have_secondary_target_on_this_rule) { + *err = "depfile has multiple output paths"; + return false; + } ins_.push_back(StringPiece(filename, len)); } else if (!out_.str_) { out_ = StringPiece(filename, len); } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; + have_secondary_target_on_this_rule = true; } } + + if (have_newline) { + // A newline ends a rule so the next filename will be a new target. + parsing_targets = true; + have_secondary_target_on_this_rule = false; + } } - if (parsing_targets) { + if (!have_target) { *err = "expected ':' in depfile"; return false; } diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index 4df8ce2..d299ee2 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -34,8 +34,11 @@ bool DepfileParser::Parse(string* content, string* err) { // parsing_targets: whether we are parsing targets or dependencies. char* in = &(*content)[0]; char* end = in + content->size(); + bool have_target = false; + bool have_secondary_target_on_this_rule = false; bool parsing_targets = true; while (in < end) { + bool have_newline = false; // out: current output point (typically same as in, but can fall behind // as we de-escape backslashes). char* out = in; @@ -44,10 +47,12 @@ bool DepfileParser::Parse(string* content, string* err) { for (;;) { // start: beginning of the current parsed span. const char* start = in; + char* yymarker = NULL; /*!re2c re2c:define:YYCTYPE = "unsigned char"; re2c:define:YYCURSOR = in; re2c:define:YYLIMIT = end; + re2c:define:YYMARKER = yymarker; re2c:yyfill:enable = 0; @@ -56,6 +61,7 @@ bool DepfileParser::Parse(string* content, string* err) { nul = "\000"; escape = [ \\#*[|\]]; + newline = '\r'?'\n'; '\\' escape { // De-escape backslashed character. @@ -85,6 +91,15 @@ bool DepfileParser::Parse(string* content, string* err) { nul { break; } + '\\' newline { + // A line continuation ends the current file name. + break; + } + newline { + // A newline ends the current file name and the current rule. + have_newline = true; + break; + } [^] { // For any other character (e.g. whitespace), swallow it here, // allowing the outer logic to loop around again. @@ -98,20 +113,30 @@ bool DepfileParser::Parse(string* content, string* err) { if (len > 0 && filename[len - 1] == ':') { len--; // Strip off trailing colon, if any. parsing_targets = false; + have_target = true; } if (len > 0) { if (is_dependency) { + if (have_secondary_target_on_this_rule) { + *err = "depfile has multiple output paths"; + return false; + } ins_.push_back(StringPiece(filename, len)); } else if (!out_.str_) { out_ = StringPiece(filename, len); } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; + have_secondary_target_on_this_rule = true; } } + + if (have_newline) { + // A newline ends a rule so the next filename will be a new target. + parsing_targets = true; + have_secondary_target_on_this_rule = false; + } } - if (parsing_targets) { + if (!have_target) { *err = "expected ':' in depfile"; return false; } diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index e3eec07..70e4029 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -158,3 +158,126 @@ TEST_F(DepfileParserTest, RejectMultipleDifferentOutputs) { EXPECT_FALSE(Parse("foo bar: x y z", &err)); ASSERT_EQ("depfile has multiple output paths", err); } + +TEST_F(DepfileParserTest, MultipleEmptyRules) { + string err; + EXPECT_TRUE(Parse("foo: x\n" + "foo: \n" + "foo:\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(1u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMultipleRulesLF) { + string err; + EXPECT_TRUE(Parse("foo: x\n" + "foo: y\n" + "foo \\\n" + "foo: z\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMultipleRulesCRLF) { + string err; + EXPECT_TRUE(Parse("foo: x\r\n" + "foo: y\r\n" + "foo \\\r\n" + "foo: z\r\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMixedRulesLF) { + string err; + EXPECT_TRUE(Parse("foo: x\\\n" + " y\n" + "foo \\\n" + "foo: z\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMixedRulesCRLF) { + string err; + EXPECT_TRUE(Parse("foo: x\\\r\n" + " y\r\n" + "foo \\\r\n" + "foo: z\r\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, IndentedRulesLF) { + string err; + EXPECT_TRUE(Parse(" foo: x\n" + " foo: y\n" + " foo: z\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, IndentedRulesCRLF) { + string err; + EXPECT_TRUE(Parse(" foo: x\r\n" + " foo: y\r\n" + " foo: z\r\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, TolerateMP) { + string err; + EXPECT_TRUE(Parse("foo: x y z\n" + "x:\n" + "y:\n" + "z:\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, MultipleRulesTolerateMP) { + string err; + EXPECT_TRUE(Parse("foo: x\n" + "x:\n" + "foo: y\n" + "y:\n" + "foo: z\n" + "z:\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, MultipleRulesRejectDifferentOutputs) { + // check that multiple different outputs are rejected by the parser + // when spread across multiple rules + string err; + EXPECT_FALSE(Parse("foo: x y\n" + "bar: y z\n", &err)); + ASSERT_EQ("depfile has multiple output paths", err); +} -- cgit v0.12 From 8a9edb110354d5468ab42685cfece6a073146f27 Mon Sep 17 00:00:00 2001 From: Brad King Date: Mon, 8 Oct 2018 09:14:58 -0400 Subject: Restore depfile toleration of multiple output paths on distinct lines Prior to introduction of depfile parser handling of multiple rules, ninja silently accepted a depfile of the form: out: in1 in2 in3 other: otherIn1 otherIn2 otherIn3 and incorrectly treated `other` and `otherIn*` as additional inputs to `out`. Now we prefer to reject this just as we already do for a depfile specifying multiple outputs on one line. However, this can break existing cases where such a depfile was silently tolerated. Add a `-w depfilemulti={err,warn}` option to control this behavior, and make it just a warning by default. --- src/build.cc | 5 +++-- src/build.h | 2 ++ src/depfile_parser.cc | 30 ++++++++++++++++++++++++++++-- src/depfile_parser.h | 17 +++++++++++++++++ src/depfile_parser.in.cc | 30 ++++++++++++++++++++++++++++-- src/depfile_parser_test.cc | 13 ++++++++++--- src/disk_interface_test.cc | 2 +- src/graph.cc | 4 +++- src/graph.h | 13 +++++++++---- src/graph_test.cc | 2 +- src/ninja.cc | 19 ++++++++++++++++++- 11 files changed, 120 insertions(+), 17 deletions(-) diff --git a/src/build.cc b/src/build.cc index ed219fd..b392803 100644 --- a/src/build.cc +++ b/src/build.cc @@ -557,7 +557,8 @@ Builder::Builder(State* state, const BuildConfig& config, BuildLog* build_log, DepsLog* deps_log, DiskInterface* disk_interface) : state_(state), config_(config), disk_interface_(disk_interface), - scan_(state, build_log, deps_log, disk_interface) { + scan_(state, build_log, deps_log, disk_interface, + &config_.depfile_parser_options) { status_ = new BuildStatus(config); } @@ -906,7 +907,7 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, if (content.empty()) return true; - DepfileParser deps; + DepfileParser deps(config_.depfile_parser_options); if (!deps.Parse(&content, err)) return false; diff --git a/src/build.h b/src/build.h index 9b90e8a..a42b8d4 100644 --- a/src/build.h +++ b/src/build.h @@ -23,6 +23,7 @@ #include #include +#include "depfile_parser.h" #include "graph.h" // XXX needed for DependencyScan; should rearrange. #include "exit_status.h" #include "line_printer.h" @@ -151,6 +152,7 @@ struct BuildConfig { /// The maximum load average we must not exceed. A negative value /// means that we do not have any limit. double max_load_average; + DepfileParserOptions depfile_parser_options; }; /// Builder wraps the build process: starting commands, updating status. diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 7724eed..405289f 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -14,6 +14,12 @@ // limitations under the License. #include "depfile_parser.h" +#include "util.h" + +DepfileParser::DepfileParser(DepfileParserOptions options) + : options_(options) +{ +} // A note on backslashes in Makefiles, from reading the docs: // Backslash-newline is the line continuation character. @@ -37,6 +43,8 @@ bool DepfileParser::Parse(string* content, string* err) { char* end = in + content->size(); bool have_target = false; bool have_secondary_target_on_this_rule = false; + bool have_newline_since_primary_target = false; + bool warned_distinct_target_lines = false; bool parsing_targets = true; while (in < end) { bool have_newline = false; @@ -219,8 +227,23 @@ yy22: if (len > 0) { if (is_dependency) { if (have_secondary_target_on_this_rule) { - *err = "depfile has multiple output paths"; - return false; + if (!have_newline_since_primary_target) { + *err = "depfile has multiple output paths"; + return false; + } else if (options_.depfile_distinct_target_lines_action_ == + kDepfileDistinctTargetLinesActionError) { + *err = + "depfile has multiple output paths (on separate lines)" + " [-w depfilemulti=err]"; + return false; + } else { + if (!warned_distinct_target_lines) { + warned_distinct_target_lines = true; + Warning("depfile has multiple output paths (on separate lines); " + "continuing anyway [-w depfilemulti=warn]"); + } + continue; + } } ins_.push_back(StringPiece(filename, len)); } else if (!out_.str_) { @@ -234,6 +257,9 @@ yy22: // A newline ends a rule so the next filename will be a new target. parsing_targets = true; have_secondary_target_on_this_rule = false; + if (have_target) { + have_newline_since_primary_target = true; + } } } if (!have_target) { diff --git a/src/depfile_parser.h b/src/depfile_parser.h index 1e6ebb5..be20374 100644 --- a/src/depfile_parser.h +++ b/src/depfile_parser.h @@ -21,8 +21,24 @@ using namespace std; #include "string_piece.h" +enum DepfileDistinctTargetLinesAction { + kDepfileDistinctTargetLinesActionWarn, + kDepfileDistinctTargetLinesActionError, +}; + +struct DepfileParserOptions { + DepfileParserOptions() + : depfile_distinct_target_lines_action_( + kDepfileDistinctTargetLinesActionWarn) {} + DepfileDistinctTargetLinesAction + depfile_distinct_target_lines_action_; +}; + /// Parser for the dependency information emitted by gcc's -M flags. struct DepfileParser { + explicit DepfileParser(DepfileParserOptions options = + DepfileParserOptions()); + /// Parse an input file. Input must be NUL-terminated. /// Warning: may mutate the content in-place and parsed StringPieces are /// pointers within it. @@ -30,6 +46,7 @@ struct DepfileParser { StringPiece out_; vector ins_; + DepfileParserOptions options_; }; #endif // NINJA_DEPFILE_PARSER_H_ diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index d299ee2..f8c94b3 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -13,6 +13,12 @@ // limitations under the License. #include "depfile_parser.h" +#include "util.h" + +DepfileParser::DepfileParser(DepfileParserOptions options) + : options_(options) +{ +} // A note on backslashes in Makefiles, from reading the docs: // Backslash-newline is the line continuation character. @@ -36,6 +42,8 @@ bool DepfileParser::Parse(string* content, string* err) { char* end = in + content->size(); bool have_target = false; bool have_secondary_target_on_this_rule = false; + bool have_newline_since_primary_target = false; + bool warned_distinct_target_lines = false; bool parsing_targets = true; while (in < end) { bool have_newline = false; @@ -119,8 +127,23 @@ bool DepfileParser::Parse(string* content, string* err) { if (len > 0) { if (is_dependency) { if (have_secondary_target_on_this_rule) { - *err = "depfile has multiple output paths"; - return false; + if (!have_newline_since_primary_target) { + *err = "depfile has multiple output paths"; + return false; + } else if (options_.depfile_distinct_target_lines_action_ == + kDepfileDistinctTargetLinesActionError) { + *err = + "depfile has multiple output paths (on separate lines)" + " [-w depfilemulti=err]"; + return false; + } else { + if (!warned_distinct_target_lines) { + warned_distinct_target_lines = true; + Warning("depfile has multiple output paths (on separate lines); " + "continuing anyway [-w depfilemulti=warn]"); + } + continue; + } } ins_.push_back(StringPiece(filename, len)); } else if (!out_.str_) { @@ -134,6 +157,9 @@ bool DepfileParser::Parse(string* content, string* err) { // A newline ends a rule so the next filename will be a new target. parsing_targets = true; have_secondary_target_on_this_rule = false; + if (have_target) { + have_newline_since_primary_target = true; + } } } if (!have_target) { diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index 70e4029..52fe7cd 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -276,8 +276,15 @@ TEST_F(DepfileParserTest, MultipleRulesTolerateMP) { TEST_F(DepfileParserTest, MultipleRulesRejectDifferentOutputs) { // check that multiple different outputs are rejected by the parser // when spread across multiple rules + DepfileParserOptions parser_opts; + parser_opts.depfile_distinct_target_lines_action_ = + kDepfileDistinctTargetLinesActionError; + DepfileParser parser(parser_opts); string err; - EXPECT_FALSE(Parse("foo: x y\n" - "bar: y z\n", &err)); - ASSERT_EQ("depfile has multiple output paths", err); + string input = + "foo: x y\n" + "bar: y z\n"; + EXPECT_FALSE(parser.Parse(&input, &err)); + ASSERT_EQ("depfile has multiple output paths (on separate lines)" + " [-w depfilemulti=err]", err); } diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 2de4f28..bac515d 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -213,7 +213,7 @@ TEST_F(DiskInterfaceTest, RemoveFile) { struct StatTest : public StateTestWithBuiltinRules, public DiskInterface { - StatTest() : scan_(&state_, NULL, NULL, this) {} + StatTest() : scan_(&state_, NULL, NULL, this, NULL) {} // DiskInterface implementation. virtual TimeStamp Stat(const string& path, string* err) const; diff --git a/src/graph.cc b/src/graph.cc index b41c247..9c2f784 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -491,7 +491,9 @@ bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path, return false; } - DepfileParser depfile; + DepfileParser depfile(depfile_parser_options_ + ? *depfile_parser_options_ + : DepfileParserOptions()); string depfile_err; if (!depfile.Parse(&content, &depfile_err)) { *err = path + ": " + depfile_err; diff --git a/src/graph.h b/src/graph.h index a8f0641..d58fecd 100644 --- a/src/graph.h +++ b/src/graph.h @@ -24,6 +24,7 @@ using namespace std; #include "util.h" struct BuildLog; +struct DepfileParserOptions; struct DiskInterface; struct DepsLog; struct Edge; @@ -209,8 +210,10 @@ struct Edge { /// "depfile" attribute in build files. struct ImplicitDepLoader { ImplicitDepLoader(State* state, DepsLog* deps_log, - DiskInterface* disk_interface) - : state_(state), disk_interface_(disk_interface), deps_log_(deps_log) {} + DiskInterface* disk_interface, + DepfileParserOptions const* depfile_parser_options) + : state_(state), disk_interface_(disk_interface), deps_log_(deps_log), + depfile_parser_options_(depfile_parser_options) {} /// Load implicit dependencies for \a edge. /// @return false on error (without filling \a err if info is just missing @@ -242,6 +245,7 @@ struct ImplicitDepLoader { State* state_; DiskInterface* disk_interface_; DepsLog* deps_log_; + DepfileParserOptions const* depfile_parser_options_; }; @@ -249,10 +253,11 @@ struct ImplicitDepLoader { /// and updating the dirty/outputs_ready state of all the nodes and edges. struct DependencyScan { DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log, - DiskInterface* disk_interface) + DiskInterface* disk_interface, + DepfileParserOptions const* depfile_parser_options) : build_log_(build_log), disk_interface_(disk_interface), - dep_loader_(state, deps_log, disk_interface) {} + dep_loader_(state, deps_log, disk_interface, depfile_parser_options) {} /// Update the |dirty_| state of the given node by inspecting its input edge. /// Examine inputs, outputs, and command lines to judge whether an edge diff --git a/src/graph_test.cc b/src/graph_test.cc index 422bc9a..4a66831 100644 --- a/src/graph_test.cc +++ b/src/graph_test.cc @@ -18,7 +18,7 @@ #include "test.h" struct GraphTest : public StateTestWithBuiltinRules { - GraphTest() : scan_(&state_, NULL, NULL, &fs_) {} + GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL) {} VirtualFileSystem fs_; DependencyScan scan_; diff --git a/src/ninja.cc b/src/ninja.cc index a3d73ad..b608426 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -73,6 +73,10 @@ struct Options { /// Whether phony cycles should warn or print an error. bool phony_cycle_should_err; + + /// Whether a depfile with multiple targets on separate lines should + /// warn or print an error. + bool depfile_distinct_target_lines_should_err; }; /// The Ninja main() loads up a series of data structures; various tools need @@ -912,7 +916,9 @@ bool WarningEnable(const string& name, Options* options) { if (name == "list") { printf("warning flags:\n" " dupbuild={err,warn} multiple build lines for one target\n" -" phonycycle={err,warn} phony build statement references itself\n"); +" phonycycle={err,warn} phony build statement references itself\n" +" depfilemulti={err,warn} depfile has multiple output paths on separate lines\n" + ); return false; } else if (name == "dupbuild=err") { options->dupe_edges_should_err = true; @@ -926,6 +932,12 @@ bool WarningEnable(const string& name, Options* options) { } else if (name == "phonycycle=warn") { options->phony_cycle_should_err = false; return true; + } else if (name == "depfilemulti=err") { + options->depfile_distinct_target_lines_should_err = true; + return true; + } else if (name == "depfilemulti=warn") { + options->depfile_distinct_target_lines_should_err = false; + return true; } else { const char* suggestion = SpellcheckString(name.c_str(), "dupbuild=err", "dupbuild=warn", @@ -1200,6 +1212,11 @@ NORETURN void real_main(int argc, char** argv) { if (exit_code >= 0) exit(exit_code); + if (options.depfile_distinct_target_lines_should_err) { + config.depfile_parser_options.depfile_distinct_target_lines_action_ = + kDepfileDistinctTargetLinesActionError; + } + if (options.working_dir) { // The formatting of this string, complete with funny quotes, is // so Emacs can properly identify that the cwd has changed for -- cgit v0.12 From b735f796cd00c7f49531e248961fd9c4bde04404 Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Sun, 25 Nov 2018 13:58:17 +0100 Subject: Remove dead Chromium link, fix #1263 --- doc/manual.asciidoc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc index d625fa9..3440740 100644 --- a/doc/manual.asciidoc +++ b/doc/manual.asciidoc @@ -157,8 +157,7 @@ meta-build system. https://gn.googlesource.com/gn/[gn]:: The meta-build system used to generate build files for Google Chrome and related projects (v8, node.js), as well as Google Fuschia. gn can generate Ninja files for -all platforms supported by Chrome. See the -https://chromium.googlesource.com/chromium/src/+/master/docs/ninja_build.md[Chromium Ninja documentation for more details]. +all platforms supported by Chrome. https://cmake.org/[CMake]:: A widely used meta-build system that can generate Ninja files on Linux as of CMake version 2.8.8. Newer versions -- cgit v0.12 From 6e02ebc4b5947095fa64b5bf814f14dd6f6f6a9a Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Thu, 20 Dec 2018 12:58:09 +0100 Subject: Remove outdated part about Chrome download, fix #1338 --- HACKING.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/HACKING.md b/HACKING.md index 086940b..bd6fec7 100644 --- a/HACKING.md +++ b/HACKING.md @@ -109,17 +109,9 @@ build "all" before committing to verify the other source still works! ## Testing performance impact of changes -If you have a Chrome build handy, it's a good test case. Otherwise, -[the github downoads page](https://github.com/ninja-build/ninja/releases) -has a copy of the Chrome build files (and depfiles). You can untar -that, then run - - path/to/my/ninja chrome - -and compare that against a baseline Ninja. - -There's a script at `misc/measure.py` that repeatedly runs a command like -the above (to address variance) and summarizes its runtime. E.g. +If you have a Chrome build handy, it's a good test case. There's a +script at `misc/measure.py` that repeatedly runs a command (to address +variance) and summarizes its runtime. E.g. path/to/misc/measure.py path/to/my/ninja chrome -- cgit v0.12 From c6b67ac4024f29130cf3d7b45a3a25ec697737f6 Mon Sep 17 00:00:00 2001 From: Alex Vallee Date: Wed, 30 Jan 2019 22:12:52 +0800 Subject: ninja_syntax.py: remove unused has_path argument The usage of the parameter was removed a long time ago and was never cleaned up. The argument is not provided in the test. --- misc/ninja_syntax.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/misc/ninja_syntax.py b/misc/ninja_syntax.py index 051bac1..ebe6490 100644 --- a/misc/ninja_syntax.py +++ b/misc/ninja_syntax.py @@ -21,7 +21,7 @@ class Writer(object): def newline(self): self.output.write('\n') - def comment(self, text, has_path=False): + def comment(self, text): for line in textwrap.wrap(text, self.width - 2, break_long_words=False, break_on_hyphens=False): self.output.write('# ' + line + '\n') -- cgit v0.12 From 0c158431f30a14d771e5c82c1e69eff7c69a08ce Mon Sep 17 00:00:00 2001 From: Jan Niklas Hasse Date: Wed, 30 Jan 2019 19:57:14 +0100 Subject: mark this 1.9.0.git --- src/version.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.cc b/src/version.cc index 1b6cfac..1c906ae 100644 --- a/src/version.cc +++ b/src/version.cc @@ -18,7 +18,7 @@ #include "util.h" -const char* kNinjaVersion = "1.8.2.git"; +const char* kNinjaVersion = "1.9.0.git"; void ParseVersion(const string& version, int* major, int* minor) { size_t end = version.find('.'); -- cgit v0.12