Skip to content

Commit 1a549bf

Browse files
committed
Dyndep‑listed implicit dependencies are no longer silently ignored when missing.
Ninja now reports an error if a dyndep input is missing and has no rule to build it, except when the node is also marked as depfile‑loaded, in which case the check is suppressed.
1 parent 64cdfe1 commit 1a549bf

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
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 validation: verify that dyndep‑only inputs are present.
829+
for (const auto input : edge->inputs_) {
830+
if (input->missing() && input->generated_by_dyndep_loader()) {
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+
// Refresh output file status to support later dyndep existence checks.
959+
// https://github.com/ninja-build/ninja/issues/2573
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: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3367,6 +3367,61 @@ TEST_F(BuildTest, DyndepMissingAndNoRule) {
33673367
EXPECT_EQ("loading 'dd': No such file or directory", err);
33683368
}
33693369

3370+
TEST_F(BuildTest, DyndepMissingInput) {
3371+
// Check that the build system detects when a dyndep-provided input is missing
3372+
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
3373+
R"ninja(
3374+
rule touch
3375+
command = touch $out $out.imp
3376+
build out: touch || dd
3377+
dyndep = dd
3378+
)ninja"));
3379+
3380+
fs_.Create("dd", R"ninja(
3381+
ninja_dyndep_version = 1
3382+
build out | out.imp: dyndep | tmp.imp missing.imp
3383+
)ninja");
3384+
3385+
fs_.Create("tmp.imp", "");
3386+
3387+
string err;
3388+
EXPECT_TRUE(builder_.AddTarget("out", &err));
3389+
ASSERT_EQ("", err);
3390+
EXPECT_EQ(builder_.Build(&err), ExitFailure);
3391+
EXPECT_EQ("'missing.imp', needed by dyndep 'dd', missing and no known rule to make it", err);
3392+
EXPECT_TRUE(command_runner_.commands_ran_.empty());
3393+
}
3394+
3395+
TEST_F(BuildTest, DyndepMissingInputUsedInDepFile) {
3396+
// The missing input cannot be detected because the dyndep‑introduced node is
3397+
// also marked as coming from a depfile.
3398+
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
3399+
R"ninja(
3400+
rule touch
3401+
command = touch $out $out.imp
3402+
build out2: touch out1 || dd
3403+
dyndep = dd
3404+
build out1: touch
3405+
depfile = out2.d
3406+
)ninja"));
3407+
3408+
fs_.Create("dd", R"ninja(
3409+
ninja_dyndep_version = 1
3410+
build out2 | out2.imp: dyndep | tmp.imp missing.imp
3411+
)ninja");
3412+
fs_.Create("out2.d", "out1: missing.imp\n");
3413+
fs_.Create("tmp.imp", "");
3414+
3415+
string err;
3416+
EXPECT_TRUE(builder_.AddTarget("out2", &err));
3417+
ASSERT_EQ("", err);
3418+
EXPECT_EQ(builder_.Build(&err), ExitSuccess);
3419+
EXPECT_EQ("", err);
3420+
ASSERT_EQ(2u, command_runner_.commands_ran_.size());
3421+
EXPECT_EQ("touch out1 out1.imp", command_runner_.commands_ran_[0]);
3422+
EXPECT_EQ("touch out2 out2.imp", command_runner_.commands_ran_[1]);
3423+
}
3424+
33703425
TEST_F(BuildTest, DyndepReadyImplicitConnection) {
33713426
// Verify that a dyndep file can be loaded immediately to discover
33723427
// that one edge has an implicit output that is also an implicit

src/graph.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ struct Node {
7777
return exists_ == ExistenceStatusExists;
7878
}
7979

80+
void MarkExists() {
81+
exists_ = ExistenceStatusExists;
82+
}
83+
84+
bool missing() const {
85+
return exists_ == ExistenceStatusMissing;
86+
}
87+
8088
bool status_known() const {
8189
return exists_ != ExistenceStatusUnknown;
8290
}

0 commit comments

Comments
 (0)