Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions src/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -825,6 +825,17 @@ bool Builder::StartEdge(Edge* edge, string* err) {
if (edge->is_phony())
return true;

// Late validation: verify that dyndep‑only inputs are present.
for (const auto input : edge->inputs_) {
if (input->missing() && input->generated_by_dyndep_loader()) {
string referenced;
referenced = ", needed by dyndep '" + edge->GetUnescapedDyndep() + "',";
*err = "'" + input->path() + "'" + referenced +
" missing and no known rule to make it";
return false;
}
}

int64_t start_time_millis = GetTimeMillis() - start_time_millis_;
running_edges_.insert(make_pair(edge, start_time_millis));

Expand Down Expand Up @@ -943,6 +954,13 @@ bool Builder::FinishCommand(CommandRunner::Result* result, string* err) {
}
}
}

// Refresh output file status to support later dyndep existence checks.
// https://github.com/ninja-build/ninja/issues/2573
for (auto o : edge->outputs_) {
o->MarkExists();
}

if (node_cleaned) {
record_mtime = edge->command_start_time_;
}
Expand Down Expand Up @@ -998,7 +1016,7 @@ bool Builder::ExtractDeps(CommandRunner::Result* result,
// all backslashes (as some of the slashes will certainly be backslashes
// anyway). This could be fixed if necessary with some additional
// complexity in IncludesNormalize::Relativize.
deps_nodes->push_back(state_->GetNode(*i, ~0u));
deps_nodes->push_back(state_->FindOrCreateDepfileNode(*i, ~0u));
}
} else if (deps_type == "gcc") {
string depfile = result->edge->GetUnescapedDepfile();
Expand Down Expand Up @@ -1031,7 +1049,7 @@ bool Builder::ExtractDeps(CommandRunner::Result* result,
i != deps.ins_.end(); ++i) {
uint64_t slash_bits;
CanonicalizePath(const_cast<char*>(i->str_), &i->len_, &slash_bits);
deps_nodes->push_back(state_->GetNode(*i, slash_bits));
deps_nodes->push_back(state_->FindOrCreateDepfileNode(*i, slash_bits));
}

if (!g_keep_depfile) {
Expand Down
65 changes: 61 additions & 4 deletions src/build_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1044,6 +1044,7 @@ TEST_F(BuildTest, DepFileOK) {
Edge* edge = state_.edges_.back();

fs_.Create("foo.c", "");
state_.FindOrCreateDepfileNode("bar.h",0); // create node 'bar.h'
GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing.
fs_.Create("foo.o.d", "foo.o: blah.h bar.h\n");
EXPECT_TRUE(builder_.AddTarget("foo.o", &err));
Expand Down Expand Up @@ -1231,7 +1232,8 @@ TEST_F(BuildTest, DepFileCanonicalize) {
"build gen/stuff\\things/foo.o: cc x\\y/z\\foo.c\n"));

fs_.Create("x/y/z/foo.c", "");
GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing.
state_.FindOrCreateDyndepNode("bar.h",0); // create node 'bar.h'
GetNode("bar.h")->MarkDirty(); // Mark bar.h as missing.
// Note, different slashes from manifest.
fs_.Create("gen/stuff\\things/foo.o.d",
"gen\\stuff\\things\\foo.o: blah.h bar.h\n");
Expand Down Expand Up @@ -1969,7 +1971,7 @@ TEST_F(BuildWithLogTest, RestatInputChangesDueToRule) {
// mtime
EXPECT_TRUE(builder_.AddTarget("out1", &err));
ASSERT_EQ("", err);
EXPECT_TRUE(!state_.GetNode("out1", 0)->dirty());
EXPECT_TRUE(!state_.GetNode("out1")->dirty());
EXPECT_EQ(builder_.Build(&err), ExitSuccess);
ASSERT_EQ("", err);
EXPECT_EQ(size_t(1), command_runner_.commands_ran_.size());
Expand Down Expand Up @@ -3068,7 +3070,7 @@ TEST_F(BuildWithDepsLogTest, DepFileOKDepsLog) {

Edge* edge = state.edges_.back();

state.GetNode("bar.h", 0)->MarkDirty(); // Mark bar.h as missing.
state.FindOrCreateDepfileNode("bar.h", 0)->MarkDirty(); // Mark bar.h as missing.
EXPECT_TRUE(builder.AddTarget("fo o.o", &err));
ASSERT_EQ("", err);

Expand Down Expand Up @@ -3214,7 +3216,7 @@ TEST_F(BuildWithDepsLogTest, DepFileDepsLogCanonicalize) {
builder.command_runner_.reset(&command_runner_);
SafeRelease protect(&builder);

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

Expand Down Expand Up @@ -3365,6 +3367,61 @@ TEST_F(BuildTest, DyndepMissingAndNoRule) {
EXPECT_EQ("loading 'dd': No such file or directory", err);
}

TEST_F(BuildTest, DyndepMissingInput) {
// Check that the build system detects when a dyndep-provided input is missing
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
R"ninja(
rule touch
command = touch $out $out.imp
build out: touch || dd
dyndep = dd
)ninja"));

fs_.Create("dd", R"ninja(
ninja_dyndep_version = 1
build out | out.imp: dyndep | tmp.imp missing.imp
)ninja");

fs_.Create("tmp.imp", "");

string err;
EXPECT_TRUE(builder_.AddTarget("out", &err));
ASSERT_EQ("", err);
EXPECT_EQ(builder_.Build(&err), ExitFailure);
EXPECT_EQ("'missing.imp', needed by dyndep 'dd', missing and no known rule to make it", err);
EXPECT_TRUE(command_runner_.commands_ran_.empty());
}

TEST_F(BuildTest, DyndepMissingInputUsedInDepFile) {
// The missing input cannot be detected because the dyndep‑introduced node is
// also marked as coming from a depfile.
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
R"ninja(
rule touch
command = touch $out $out.imp
build out2: touch out1 || dd
dyndep = dd
build out1: touch
depfile = out2.d
)ninja"));

fs_.Create("dd", R"ninja(
ninja_dyndep_version = 1
build out2 | out2.imp: dyndep | tmp.imp missing.imp
)ninja");
fs_.Create("out2.d", "out1: missing.imp\n");
fs_.Create("tmp.imp", "");

string err;
EXPECT_TRUE(builder_.AddTarget("out2", &err));
ASSERT_EQ("", err);
EXPECT_EQ(builder_.Build(&err), ExitSuccess);
EXPECT_EQ("", err);
ASSERT_EQ(2u, command_runner_.commands_ran_.size());
EXPECT_EQ("touch out1 out1.imp", command_runner_.commands_ran_[0]);
EXPECT_EQ("touch out2 out2.imp", command_runner_.commands_ran_[1]);
}

TEST_F(BuildTest, DyndepReadyImplicitConnection) {
// Verify that a dyndep file can be loaded immediately to discover
// that one edge has an implicit output that is also an implicit
Expand Down
2 changes: 1 addition & 1 deletion src/deps_log.cc
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ LoadStatus DepsLog::Load(const string& path, State* state, string* err) {
// have a correct slash_bits that GetNode will look up), or it is an
// implicit dependency from a .d which does not affect the build command
// (and so need not have its slashes maintained).
Node* node = state->GetNode(subpath, 0);
Node* node = state->FindOrCreateDepfileNode(subpath, 0);

// Check that the expected index matches the actual index. This can only
// happen if two ninja processes write to the same deps log concurrently.
Expand Down
Loading