diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/build.cc | 64 | ||||
-rw-r--r-- | src/build.h | 27 | ||||
-rw-r--r-- | src/build_log.cc | 3 | ||||
-rw-r--r-- | src/disk_interface.cc | 13 | ||||
-rw-r--r-- | src/disk_interface_test.cc | 32 |
5 files changed, 110 insertions, 29 deletions
diff --git a/src/build.cc b/src/build.cc index 61ef0e8..c24d6a9 100644 --- a/src/build.cc +++ b/src/build.cc @@ -16,6 +16,7 @@ #include <assert.h> #include <errno.h> +#include <limits.h> #include <stdio.h> #include <stdlib.h> #include <functional> @@ -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) { @@ -318,18 +331,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<map<Edge*, bool>::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<map<Edge*, Want>::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 +368,32 @@ Edge* Plan::FindWork() { return edge; } -void Plan::ScheduleWork(Edge* edge) { - set<Edge*>::iterator e = ready_.lower_bound(edge); - if (e != ready_.end() && !ready_.key_comp()(edge, *e)) { +void Plan::ScheduleWork(map<Edge*, Want>::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<Edge*, bool>::iterator e = want_.find(edge); + map<Edge*, Want>::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 +420,14 @@ void Plan::NodeFinished(Node* node) { // See if we we want any edges from this node. for (vector<Edge*>::const_iterator oe = node->out_edges().begin(); oe != node->out_edges().end(); ++oe) { - map<Edge*, bool>::iterator want_e = want_.find(*oe); + map<Edge*, Want>::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 +443,8 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { for (vector<Edge*>::const_iterator oe = node->out_edges().begin(); oe != node->out_edges().end(); ++oe) { // Don't process edges that we don't actually want. - map<Edge*, bool>::iterator want_e = want_.find(*oe); - if (want_e == want_.end() || !want_e->second) + map<Edge*, Want>::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. @@ -441,7 +456,12 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { vector<Node*>::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<Node*>::iterator i = begin; i != end; ++i) { @@ -464,7 +484,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 +496,8 @@ bool Plan::CleanNode(DependencyScan* scan, Node* node, string* err) { void Plan::Dump() { printf("pending: %d\n", (int)want_.size()); - for (map<Edge*, bool>::iterator e = want_.begin(); e != want_.end(); ++e) { - if (e->second) + for (map<Edge*, Want>::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..ac7f951 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<Edge*, Want>::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<Edge*, bool> want_; + /// dependents. If it does contain an entry, the enumeration indicates what + /// we want for the edge. + map<Edge*, Want> want_; set<Edge*> ready_; @@ -178,7 +190,11 @@ struct Builder { State* state_; const BuildConfig& config_; Plan plan_; +#if __cplusplus < 201703L auto_ptr<CommandRunner> command_runner_; +#else + unique_ptr<CommandRunner> command_runner_; // auto_ptr was removed in C++17. +#endif BuildStatus* status_; private: @@ -206,6 +222,7 @@ struct BuildStatus { enum EdgeStatus { kEdgeStarted, + kEdgeRunning, kEdgeFinished, }; 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; diff --git a/src/disk_interface.cc b/src/disk_interface.cc index aceb575..f0f6346 100644 --- a/src/disk_interface.cc +++ b/src/disk_interface.cc @@ -105,13 +105,19 @@ bool StatAllFilesInDir(const string& dir, map<string, TimeStamp>* 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; } 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 +170,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..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 @@ -87,6 +108,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 +128,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 +137,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 +151,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 +166,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); |