Skip to content

Commit

Permalink
Merge pull request #2356 from jhasse/remove-w-dupbuild
Browse files Browse the repository at this point in the history
Remove `-w dupbuild` completely, always error on duplicate edges
  • Loading branch information
jhasse authored Dec 7, 2023
2 parents 09b6b4e + 4d98903 commit 9772a29
Show file tree
Hide file tree
Showing 8 changed files with 36 additions and 87 deletions.
17 changes: 3 additions & 14 deletions src/manifest_parser.cc
Original file line number Diff line number Diff line change
Expand Up @@ -336,20 +336,9 @@ bool ManifestParser::ParseEdge(string* err) {
return lexer_.Error("empty path", err);
uint64_t slash_bits;
CanonicalizePath(&path, &slash_bits);
if (!state_->AddOut(edge, path, slash_bits)) {
if (options_.dupe_edge_action_ == kDupeEdgeActionError) {
lexer_.Error("multiple rules generate " + path, err);
return false;
} else {
if (!quiet_) {
Warning(
"multiple rules generate %s. builds involving this target will "
"not be correct; continuing anyway",
path.c_str());
}
if (e - i <= static_cast<size_t>(implicit_outs))
--implicit_outs;
}
if (!state_->AddOut(edge, path, slash_bits, err)) {
lexer_.Error(std::string(*err), err);
return false;
}
}

Expand Down
6 changes: 1 addition & 5 deletions src/manifest_parser.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,7 @@ enum PhonyCycleAction {
};

struct ManifestParserOptions {
ManifestParserOptions()
: dupe_edge_action_(kDupeEdgeActionWarn),
phony_cycle_action_(kPhonyCycleActionWarn) {}
DupeEdgeAction dupe_edge_action_;
PhonyCycleAction phony_cycle_action_;
PhonyCycleAction phony_cycle_action_ = kPhonyCycleActionWarn;
};

/// Parses .ninja files.
Expand Down
61 changes: 16 additions & 45 deletions src/manifest_parser_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -330,39 +330,14 @@ TEST_F(ParserTest, CanonicalizePathsBackslashes) {
}
#endif

TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputs) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
"rule cat\n"
" command = cat $in > $out\n"
"build out1 out2: cat in1\n"
"build out1: cat in2\n"
"build final: cat out1\n"
));
// AssertParse() checks that the generated build graph is self-consistent.
// That's all the checking that this test needs.
}

TEST_F(ParserTest, NoDeadPointerFromDuplicateEdge) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
"rule cat\n"
" command = cat $in > $out\n"
"build out: cat in\n"
"build out: cat in\n"
));
// AssertParse() checks that the generated build graph is self-consistent.
// That's all the checking that this test needs.
}

TEST_F(ParserTest, DuplicateEdgeWithMultipleOutputsError) {
const char kInput[] =
"rule cat\n"
" command = cat $in > $out\n"
"build out1 out2: cat in1\n"
"build out1: cat in2\n"
"build final: cat out1\n";
ManifestParserOptions parser_opts;
parser_opts.dupe_edge_action_ = kDupeEdgeActionError;
ManifestParser parser(&state, &fs_, parser_opts);
ManifestParser parser(&state, &fs_);
string err;
EXPECT_FALSE(parser.ParseTest(kInput, &err));
EXPECT_EQ("input:5: multiple rules generate out1\n", err);
Expand All @@ -377,9 +352,7 @@ TEST_F(ParserTest, DuplicateEdgeInIncludedFile) {
"build final: cat out1\n");
const char kInput[] =
"subninja sub.ninja\n";
ManifestParserOptions parser_opts;
parser_opts.dupe_edge_action_ = kDupeEdgeActionError;
ManifestParser parser(&state, &fs_, parser_opts);
ManifestParser parser(&state, &fs_);
string err;
EXPECT_FALSE(parser.ParseTest(kInput, &err));
EXPECT_EQ("sub.ninja:5: multiple rules generate out1\n", err);
Expand Down Expand Up @@ -997,28 +970,26 @@ TEST_F(ParserTest, ImplicitOutputEmpty) {
EXPECT_FALSE(edge->is_implicit_out(0));
}

TEST_F(ParserTest, ImplicitOutputDupe) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
TEST_F(ParserTest, ImplicitOutputDupeError) {
const char kInput[] =
"rule cat\n"
" command = cat $in > $out\n"
"build foo baz | foo baq foo: cat bar\n"));

Edge* edge = state.LookupNode("foo")->in_edge();
ASSERT_EQ(edge->outputs_.size(), 3);
EXPECT_FALSE(edge->is_implicit_out(0));
EXPECT_FALSE(edge->is_implicit_out(1));
EXPECT_TRUE(edge->is_implicit_out(2));
"build foo baz | foo baq foo: cat bar\n";
ManifestParser parser(&state, &fs_);
string err;
EXPECT_FALSE(parser.ParseTest(kInput, &err));
EXPECT_EQ("input:4: foo is defined as an output multiple times\n", err);
}

TEST_F(ParserTest, ImplicitOutputDupes) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
TEST_F(ParserTest, ImplicitOutputDupesError) {
const char kInput[] =
"rule cat\n"
" command = cat $in > $out\n"
"build foo foo foo | foo foo foo foo: cat bar\n"));

Edge* edge = state.LookupNode("foo")->in_edge();
ASSERT_EQ(edge->outputs_.size(), 1);
EXPECT_FALSE(edge->is_implicit_out(0));
"build foo foo foo | foo foo foo foo: cat bar\n";
ManifestParser parser(&state, &fs_);
string err;
EXPECT_FALSE(parser.ParseTest(kInput, &err));
EXPECT_EQ("input:4: foo is defined as an output multiple times\n", err);
}

TEST_F(ParserTest, NoExplicitOutput) {
Expand Down
6 changes: 3 additions & 3 deletions src/missing_deps_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ struct MissingDependencyScannerTest : public testing::Test {
compile_rule_.AddBinding("deps", deps_type);
generator_rule_.AddBinding("deps", deps_type);
Edge* header_edge = state_.AddEdge(&generator_rule_);
state_.AddOut(header_edge, "generated_header", 0);
state_.AddOut(header_edge, "generated_header", 0, nullptr);
Edge* compile_edge = state_.AddEdge(&compile_rule_);
state_.AddOut(compile_edge, "compiled_object", 0);
state_.AddOut(compile_edge, "compiled_object", 0, nullptr);
}

void CreateGraphDependencyBetween(const char* from, const char* to) {
Expand Down Expand Up @@ -130,7 +130,7 @@ TEST_F(MissingDependencyScannerTest, MissingDepFixedIndirect) {
CreateInitialState();
// Adding an indirect dependency also fixes the issue
Edge* intermediate_edge = state_.AddEdge(&generator_rule_);
state_.AddOut(intermediate_edge, "intermediate", 0);
state_.AddOut(intermediate_edge, "intermediate", 0, nullptr);
CreateGraphDependencyBetween("compiled_object", "intermediate");
CreateGraphDependencyBetween("intermediate", "generated_header");
RecordDepsLogDep("compiled_object", "generated_header");
Expand Down
18 changes: 2 additions & 16 deletions src/ninja.cc
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,6 @@ struct Options {
/// Tool to run rather than building.
const Tool* tool;

/// Whether duplicate rules for one target should warn or print an error.
bool dupe_edges_should_err;

/// Whether phony cycles should warn or print an error.
bool phony_cycle_should_err;
};
Expand Down Expand Up @@ -1210,12 +1207,6 @@ bool WarningEnable(const string& name, Options* options) {
" phonycycle={err,warn} phony build statement references itself\n"
);
return false;
} else if (name == "dupbuild=err") {
options->dupe_edges_should_err = true;
return true;
} else if (name == "dupbuild=warn") {
options->dupe_edges_should_err = false;
return true;
} else if (name == "phonycycle=err") {
options->phony_cycle_should_err = true;
return true;
Expand All @@ -1227,9 +1218,8 @@ bool WarningEnable(const string& name, Options* options) {
Warning("deprecated warning 'depfilemulti'");
return true;
} else {
const char* suggestion =
SpellcheckString(name.c_str(), "dupbuild=err", "dupbuild=warn",
"phonycycle=err", "phonycycle=warn", NULL);
const char* suggestion = SpellcheckString(name.c_str(), "phonycycle=err",
"phonycycle=warn", nullptr);
if (suggestion) {
Error("unknown warning flag '%s', did you mean '%s'?",
name.c_str(), suggestion);
Expand Down Expand Up @@ -1525,7 +1515,6 @@ NORETURN void real_main(int argc, char** argv) {
BuildConfig config;
Options options = {};
options.input_file = "build.ninja";
options.dupe_edges_should_err = true;

setvbuf(stdout, NULL, _IOLBF, BUFSIZ);
const char* ninja_command = argv[0];
Expand Down Expand Up @@ -1562,9 +1551,6 @@ NORETURN void real_main(int argc, char** argv) {
NinjaMain ninja(ninja_command, config);

ManifestParserOptions parser_opts;
if (options.dupe_edges_should_err) {
parser_opts.dupe_edge_action_ = kDupeEdgeActionError;
}
if (options.phony_cycle_should_err) {
parser_opts.phony_cycle_action_ = kPhonyCycleActionError;
}
Expand Down
11 changes: 9 additions & 2 deletions src/state.cc
Original file line number Diff line number Diff line change
Expand Up @@ -133,10 +133,17 @@ void State::AddIn(Edge* edge, StringPiece path, uint64_t slash_bits) {
node->AddOutEdge(edge);
}

bool State::AddOut(Edge* edge, StringPiece path, uint64_t slash_bits) {
bool State::AddOut(Edge* edge, StringPiece path, uint64_t slash_bits,
std::string* err) {
Node* node = GetNode(path, slash_bits);
if (node->in_edge())
if (Edge* other = node->in_edge()) {
if (other == edge) {
*err = path.AsString() + " is defined as an output multiple times";
} else {
*err = "multiple rules generate " + path.AsString();
}
return false;
}
edge->outputs_.push_back(node);
node->set_in_edge(edge);
node->set_generated_by_dep_loader(false);
Expand Down
2 changes: 1 addition & 1 deletion src/state.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ struct State {
/// ensures that the generated_by_dep_loader() flag for all these nodes
/// is set to false, to indicate that they come from the input manifest.
void AddIn(Edge* edge, StringPiece path, uint64_t slash_bits);
bool AddOut(Edge* edge, StringPiece path, uint64_t slash_bits);
bool AddOut(Edge* edge, StringPiece path, uint64_t slash_bits, std::string* err);
void AddValidation(Edge* edge, StringPiece path, uint64_t slash_bits);
bool AddDefault(StringPiece path, std::string* error);

Expand Down
2 changes: 1 addition & 1 deletion src/state_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ TEST(State, Basic) {
Edge* edge = state.AddEdge(rule);
state.AddIn(edge, "in1", 0);
state.AddIn(edge, "in2", 0);
state.AddOut(edge, "out", 0);
state.AddOut(edge, "out", 0, nullptr);

EXPECT_EQ("cat in1 in2 > out", edge->EvaluateCommand());

Expand Down

0 comments on commit 9772a29

Please sign in to comment.