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
36 changes: 24 additions & 12 deletions src/build.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1000,10 +1000,10 @@ bool Builder::ExtractDeps(CommandRunner::Result* result,
// complexity in IncludesNormalize::Relativize.
deps_nodes->push_back(state_->GetNode(*i, ~0u));
}
} else if (deps_type == "gcc") {
} else if (deps_type == "gcc" || deps_type == "zero") {
string depfile = result->edge->GetUnescapedDepfile();
if (depfile.empty()) {
*err = string("edge with deps=gcc but no depfile makes no sense");
*err = string("edge with deps=") + deps_type + string(" but no depfile makes no sense");
return false;
}

Expand All @@ -1021,17 +1021,29 @@ bool Builder::ExtractDeps(CommandRunner::Result* result,
if (content.empty())
return true;

DepfileParser deps(config_.depfile_parser_options);
if (!deps.Parse(&content, err))
return false;
if (deps_type == "gcc") {
DepfileParser deps(config_.depfile_parser_options);
if (!deps.Parse(&content, err))
return false;

// XXX check depfile matches expected output.
deps_nodes->reserve(deps.ins_.size());
for (vector<StringPiece>::iterator i = deps.ins_.begin();
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));
// XXX check depfile matches expected output.
deps_nodes->reserve(deps.ins_.size());
for (vector<StringPiece>::iterator i = deps.ins_.begin();
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));
}
} else {
const char* ptr = content.c_str();
const char* const end = &content.back() + 1;
while (ptr < end) {
StringPiece piece = ptr;
ptr += piece.size() + 1;
uint64_t slash_bits;
CanonicalizePath(const_cast<char*>(piece.str_), &piece.len_, &slash_bits);
deps_nodes->push_back(state_->GetNode(piece, slash_bits));
}
}

if (!g_keep_depfile) {
Expand Down
75 changes: 75 additions & 0 deletions src/build_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -641,6 +641,8 @@ bool FakeCommandRunner::StartCommand(Edge* edge) {
edge->rule().name() == "cc" ||
edge->rule().name() == "cp_multi_msvc" ||
edge->rule().name() == "cp_multi_gcc" ||
edge->rule().name() == "cp_multi_zero" ||
edge->rule().name() == "cp_multi_zero_del" ||
edge->rule().name() == "touch" ||
edge->rule().name() == "touch-interrupt" ||
edge->rule().name() == "touch-fail-tick2") {
Expand Down Expand Up @@ -2538,6 +2540,79 @@ TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileGCCOnlySecondaryOutput) {
EXPECT_EQ("in2", out2_deps->nodes[1]->path());
}

/// Test nul-separated deps log with multiple outputs.
TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileZero) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule cp_multi_zero\n"
" command = printf 'in1\\0in2\\0' > in.d && for file in $out; do cp in1 $$file; done\n"
" deps = zero\n"
" depfile = in.d\n"
"build out1 out2: cp_multi_zero in1 in2\n"));

std::string err;
EXPECT_TRUE(builder_.AddTarget("out1", &err));
ASSERT_EQ("", err);

std::string cont = "in1";
cont.push_back('\0');
cont.append("in2");
cont.push_back('\0');

fs_.Create("in.d", cont);
EXPECT_EQ(builder_.Build(&err), ExitSuccess);
EXPECT_EQ("", err);
ASSERT_EQ(1u, command_runner_.commands_ran_.size());
EXPECT_EQ("printf 'in1\\0in2\\0' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]);

Node* out1_node = state_.LookupNode("out1");
DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);
EXPECT_EQ(2, out1_deps->node_count);
EXPECT_EQ("in1", out1_deps->nodes[0]->path());
EXPECT_EQ("in2", out1_deps->nodes[1]->path());

Node* out2_node = state_.LookupNode("out2");
DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);
EXPECT_EQ(2, out2_deps->node_count);
EXPECT_EQ("in1", out2_deps->nodes[0]->path());
EXPECT_EQ("in2", out2_deps->nodes[1]->path());
}

/// Test nul-separated deps log with multiple outputs and no trailing nul.
TEST_F(BuildWithQueryDepsLogTest, TwoOutputsDepFileZeroDel) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"rule cp_multi_zero_del\n"
" command = printf 'in1\\0in2' > in.d && for file in $out; do cp in1 $$file; done\n"
" deps = zero\n"
" depfile = in.d\n"
"build out1 out2: cp_multi_zero_del in1 in2\n"));

std::string err;
EXPECT_TRUE(builder_.AddTarget("out1", &err));
ASSERT_EQ("", err);

std::string cont = "in1";
cont.push_back('\0');
cont.append("in2");

fs_.Create("in.d", cont);
EXPECT_EQ(builder_.Build(&err), ExitSuccess);
EXPECT_EQ("", err);
ASSERT_EQ(1u, command_runner_.commands_ran_.size());
EXPECT_EQ("printf 'in1\\0in2' > in.d && for file in out1 out2; do cp in1 $file; done", command_runner_.commands_ran_[0]);

Node* out1_node = state_.LookupNode("out1");
DepsLog::Deps* out1_deps = log_.GetDeps(out1_node);
EXPECT_EQ(2, out1_deps->node_count);
EXPECT_EQ("in1", out1_deps->nodes[0]->path());
EXPECT_EQ("in2", out1_deps->nodes[1]->path());

Node* out2_node = state_.LookupNode("out2");
DepsLog::Deps* out2_deps = log_.GetDeps(out2_node);
EXPECT_EQ(2, out2_deps->node_count);
EXPECT_EQ("in1", out2_deps->nodes[0]->path());
EXPECT_EQ("in2", out2_deps->nodes[1]->path());
}

/// Tests of builds involving deps logs necessarily must span
/// multiple builds. We reuse methods on BuildTest but not the
/// builder_ it sets up, because we want pristine objects for
Expand Down