aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChris Parsons <cparsons@google.com>2022-05-17 02:03:00 +0000
committerChris Parsons <cparsons@google.com>2022-05-17 02:03:00 +0000
commitb3dd6ea54b471469d46ad13d9ef0ee1de4bb7b7d (patch)
treea68bee0f478641d9bf339357f595f5c0fab92298
parente2408a6ab1aeb6d46c31410cd2f180c67a6e7cd2 (diff)
downloadninja-b3dd6ea54b471469d46ad13d9ef0ee1de4bb7b7d.tar.gz
Set output mtime of phony edges to latest inputs
This is a partial cherrypick of https://github.com/ninja-build/ninja/pull/1964/ which fixes a build correctness bug for phony edges. Previously, actions with implicit dependencies on phony targets would not rerun if the inputs to these phony targets were changed. Test: Manually verified bug fix with repro case described in b/232030877#comment27 Test: New unit test Change-Id: I1d58abb9c46e9a31f0cb3b44bf45e87f8f301209
-rw-r--r--src/graph.cc12
-rw-r--r--src/graph.h3
-rw-r--r--src/graph_test.cc37
3 files changed, 52 insertions, 0 deletions
diff --git a/src/graph.cc b/src/graph.cc
index b7a3c7c..682182e 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -49,6 +49,12 @@ bool Node::Stat(DiskInterface* disk_interface, string* err) {
}
}
+void Node::UpdatePhonyMtime(TimeStamp mtime) {
+ if (!exists()) {
+ mtime_ = std::max(mtime_, mtime);
+ }
+}
+
bool Node::LStat(
DiskInterface* disk_interface, bool* is_dir, bool* is_symlink, string* err) {
assert(in_edge() != nullptr);
@@ -427,6 +433,12 @@ bool DependencyScan::RecomputeOutputsDirty(Edge* edge, Node* most_recent_input,
return true;
}
}
+
+ // Update the mtime with the newest input. Dependents can thus call mtime()
+ // on the fake node and get the latest mtime of the dependencies
+ if (most_recent_input) {
+ (*o)->UpdatePhonyMtime(most_recent_input->mtime());
+ }
continue;
}
if (RecomputeOutputDirty(edge, most_recent_input, command_hash, *o)) {
diff --git a/src/graph.h b/src/graph.h
index 3472c07..33a42fc 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -119,6 +119,9 @@ struct Node {
/// Only use when lstat() is desired (output files)
bool LStat(DiskInterface* disk_interface, bool* is_dir, bool* is_symlink, string* err);
+ /// If the file doesn't exist, set the mtime_ from its dependencies
+ void UpdatePhonyMtime(TimeStamp mtime);
+
/// Return false on error.
bool StatIfNecessary(DiskInterface* disk_interface, string* err) {
if (status_known())
diff --git a/src/graph_test.cc b/src/graph_test.cc
index de39980..427334f 100644
--- a/src/graph_test.cc
+++ b/src/graph_test.cc
@@ -1189,3 +1189,40 @@ TEST_F(GraphTest, GetAllPathsInCycles) {
std::vector<std::string> expected_path = {"f", "d", "c", "b", "a"};
ASSERT_TRUE(paths_f_to_a[0] == expected_path);
}
+
+// Check that phony's dependencies' mtimes are propagated.
+TEST_F(GraphTest, PhonyDepsMtimes) {
+ string err;
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"rule touch\n"
+" command = touch $out\n"
+"build in_ph: phony in1\n"
+"build out1: touch in_ph\n"
+));
+ fs_.Create("in1", "");
+ fs_.Create("out1", "");
+ Node* out1 = GetNode("out1");
+ Node* in1 = GetNode("in1");
+
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out1"), NULL, &err));
+ EXPECT_TRUE(!out1->dirty());
+
+ // Get the mtime of out1
+ ASSERT_TRUE(in1->Stat(&fs_, &err));
+ ASSERT_TRUE(out1->Stat(&fs_, &err));
+ TimeStamp out1Mtime1 = out1->mtime();
+ TimeStamp in1Mtime1 = in1->mtime();
+
+ // Touch in1. This should cause out1 to be dirty
+ state_.Reset();
+ fs_.Tick();
+ fs_.Create("in1", "");
+
+ ASSERT_TRUE(in1->Stat(&fs_, &err));
+ EXPECT_GT(in1->mtime(), in1Mtime1);
+
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out1"), NULL, &err));
+ EXPECT_GT(in1->mtime(), in1Mtime1);
+ EXPECT_EQ(out1->mtime(), out1Mtime1);
+ EXPECT_TRUE(out1->dirty());
+}