summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/build.cc64
-rw-r--r--src/build.h27
-rw-r--r--src/build_log.cc3
-rw-r--r--src/disk_interface.cc13
-rw-r--r--src/disk_interface_test.cc32
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);