Skip to content

Commit 7f2201a

Browse files
committed
Dyndep‑listed implicit dependencies were ignored when missing, mirroring depfile behavior.
Ninja now errors out when a dyndep input is missing and has no rule.
1 parent 38152c8 commit 7f2201a

File tree

4 files changed

+54
-4
lines changed

4 files changed

+54
-4
lines changed

src/build.cc

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,17 @@ bool Builder::StartEdge(Edge* edge, string* err) {
825825
if (edge->is_phony())
826826
return true;
827827

828+
// late check if dyndep loaded inputs do exist
829+
for (const auto input : edge->inputs_) {
830+
if (input->missing() && input->get_dep_loader() == Node::Loader::Dyndep) {
831+
string referenced;
832+
referenced = ", needed by dyndep '" + edge->GetUnescapedDyndep() + "',";
833+
*err = "'" + input->path() + "'" + referenced +
834+
" missing and no known rule to make it";
835+
return false;
836+
}
837+
}
838+
828839
int64_t start_time_millis = GetTimeMillis() - start_time_millis_;
829840
running_edges_.insert(make_pair(edge, start_time_millis));
830841

@@ -943,6 +954,13 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
943954
}
944955
}
945956
}
957+
958+
// update file status
959+
// required to check if dyndep loaded input exists
960+
for (auto o : edge->outputs_) {
961+
o->MarkExists();
962+
}
963+
946964
if (node_cleaned) {
947965
record_mtime = edge->command_start_time_;
948966
}

src/build_test.cc

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1231,7 +1231,7 @@ TEST_F(BuildTest, DepFileCanonicalize) {
12311231
"build gen/stuff\\things/foo.o: cc x\\y/z\\foo.c\n"));
12321232

12331233
fs_.Create("x/y/z/foo.c", "");
1234-
GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing.
1234+
state_.GetNodeDeps("bar.h", 0)->MarkDirty(); // Mark bar.h as missing.
12351235
// Note, different slashes from manifest.
12361236
fs_.Create("gen/stuff\\things/foo.o.d",
12371237
"gen\\stuff\\things\\foo.o: blah.h bar.h\n");
@@ -3214,7 +3214,7 @@ TEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) {
32143214
builder.command_runner_.reset(&command_runner_);
32153215
SafeRelease protect(&builder);
32163216

3217-
state.GetNode("bar.h", 0)->MarkDirty(); // Mark bar.h as missing.
3217+
// bar.h as missing.
32183218
EXPECT_TRUE(builder.AddTarget("a/b/c/d/e/fo o.o", &err));
32193219
ASSERT_EQ("", err);
32203220

@@ -3365,6 +3365,28 @@ TEST_F(BuildTest, DyndepMissingAndNoRule) {
33653365
EXPECT_EQ("loading 'dd': No such file or directory", err);
33663366
}
33673367

3368+
TEST_F(BuildTest, DyndepMissingInput) {
3369+
// Check that the build system detects when a dyndep-provided input is missing
3370+
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
3371+
R"ninja(
3372+
rule touch
3373+
command = touch $out $out.imp
3374+
build out: touch || dd
3375+
dyndep = dd
3376+
)ninja"));
3377+
fs_.Create("dd", R"ninja(
3378+
ninja_dyndep_version = 1
3379+
build out | out.imp: dyndep | tmp.imp missing.imp
3380+
)ninja");
3381+
3382+
string err;
3383+
EXPECT_TRUE(builder_.AddTarget("out", &err));
3384+
ASSERT_EQ("", err);
3385+
EXPECT_EQ(builder_.Build(&err), ExitFailure);
3386+
EXPECT_EQ("'tmp.imp', needed by dyndep 'dd', missing and no known rule to make it", err);
3387+
EXPECT_TRUE(command_runner_.commands_ran_.empty());
3388+
}
3389+
33683390
TEST_F(BuildTest, DyndepReadyImplicitConnection) {
33693391
// Verify that a dyndep file can be loaded immediately to discover
33703392
// that one edge has an implicit output that is also an implicit

src/graph.h

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ struct State;
4141
/// it's dirty, mtime, etc.
4242
struct Node {
4343
enum class Loader : char { Depfile, Dyndep, Manifest };
44-
44+
4545
Node(const std::string& path, uint64_t slash_bits, Loader loader)
4646
: path_(path), slash_bits_(slash_bits), generated_by_dep_loader_(loader) {}
4747

@@ -77,6 +77,9 @@ struct Node {
7777
return exists_ == ExistenceStatusExists;
7878
}
7979

80+
void MarkExists() { exists_ = ExistenceStatusExists; }
81+
bool missing() const { return exists_ == ExistenceStatusMissing; }
82+
8083
bool status_known() const {
8184
return exists_ != ExistenceStatusUnknown;
8285
}
@@ -108,6 +111,11 @@ struct Node {
108111
return Loader::Manifest != generated_by_dep_loader_;
109112
}
110113

114+
// regular input or output from the Ninja manifest.
115+
void set_generated_by_manifest() {
116+
generated_by_dep_loader_ = Loader::Manifest;
117+
}
118+
111119
Loader get_dep_loader() const { return generated_by_dep_loader_; }
112120

113121
int id() const { return id_; }

src/state.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ Node* State::GetNode(StringPiece path, uint64_t slash_bits,
110110
}
111111

112112
Node* State::GetNodeManifest(StringPiece path, uint64_t slash_bits) {
113-
return GetNode(path, slash_bits, Node::Loader::Manifest);
113+
auto node = GetNode(path, slash_bits, Node::Loader::Manifest);
114+
node->set_generated_by_manifest(); // Manifest wins
115+
return node;
114116
}
115117

116118
Node* State::GetNodeDeps(StringPiece path, uint64_t slash_bits) {

0 commit comments

Comments
 (0)