diff options
author | Dan Willemsen <dwillemsen@google.com> | 2020-07-07 19:51:46 -0700 |
---|---|---|
committer | Dan Willemsen <dwillemsen@google.com> | 2020-07-07 22:36:46 -0700 |
commit | f94f19e6eca76079dba1c3fe272885f97dfcf5b7 (patch) | |
tree | c6eb53be55f3c9e87b98508e1d30da15459d65c8 | |
parent | add90eee261445c62c3dabfdaf1bdd425310dd49 (diff) | |
parent | 0a2e2cae7038ce519b0524c07d7135c3e520c9cd (diff) | |
download | ninja-f94f19e6eca76079dba1c3fe272885f97dfcf5b7.tar.gz |
Merge upstream commit '0a2e2cae' into master
* commit '0a2e2cae7038ce519b0524c07d7135c3e520c9cd':
Restore depfile toleration of multiple output paths on distinct lines
Fix depfile parser handling of multiple rules
Fix depfile parser test case line continuation
Re-arrange depfile parser token processing logic
Re-generate depfile parser with re2cc 1.0.1
depfile_parser.cc conflicts were handled by regenerating with re2c
The other conflicts were all fairly simple argument/option ordering.
Test: compare .ninja_deps of a full android build before and after this
change, they're equivalent
Change-Id: I74b7fc56e035c9dbab4c987b6af17329c596a898
-rw-r--r-- | src/build.cc | 5 | ||||
-rw-r--r-- | src/build.h | 2 | ||||
-rw-r--r-- | src/depfile_parser.cc | 145 | ||||
-rw-r--r-- | src/depfile_parser.h | 17 | ||||
-rw-r--r-- | src/depfile_parser.in.cc | 72 | ||||
-rw-r--r-- | src/depfile_parser_test.cc | 138 | ||||
-rw-r--r-- | src/disk_interface_test.cc | 2 | ||||
-rw-r--r-- | src/graph.cc | 4 | ||||
-rw-r--r-- | src/graph.h | 14 | ||||
-rw-r--r-- | src/graph_test.cc | 4 | ||||
-rw-r--r-- | src/ninja.cc | 16 |
11 files changed, 356 insertions, 63 deletions
diff --git a/src/build.cc b/src/build.cc index 045331b..2f8b860 100644 --- a/src/build.cc +++ b/src/build.cc @@ -359,7 +359,8 @@ Builder::Builder(State* state, const BuildConfig& config, int64_t start_time_millis) : state_(state), config_(config), status_(status), start_time_millis_(start_time_millis), disk_interface_(disk_interface), - scan_(state, build_log, deps_log, disk_interface, config.uses_phony_outputs) { + scan_(state, build_log, deps_log, disk_interface, + &config_.depfile_parser_options, config.uses_phony_outputs) { } Builder::~Builder() { @@ -802,7 +803,7 @@ bool Builder::ExtractDeps(CommandRunner::Result* result, if (content.empty()) return true; - DepfileParser deps; + DepfileParser deps(config_.depfile_parser_options); if (!deps.Parse(&content, err)) return false; diff --git a/src/build.h b/src/build.h index d5d7bc9..5c06f8b 100644 --- a/src/build.h +++ b/src/build.h @@ -26,6 +26,7 @@ #include <sys/resource.h> #endif +#include "depfile_parser.h" #include "graph.h" // XXX needed for DependencyScan; should rearrange. #include "exit_status.h" #include "util.h" // int64_t @@ -162,6 +163,7 @@ struct BuildConfig { /// The maximum load average we must not exceed. A negative value /// means that we do not have any limit. double max_load_average; + DepfileParserOptions depfile_parser_options; /// Command to execute to handle build output const char* frontend; diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc index 8d9345e..16403f1 100644 --- a/src/depfile_parser.cc +++ b/src/depfile_parser.cc @@ -14,6 +14,12 @@ // limitations under the License. #include "depfile_parser.h" +#include "util.h" + +DepfileParser::DepfileParser(DepfileParserOptions options) + : options_(options) +{ +} // A note on backslashes in Makefiles, from reading the docs: // Backslash-newline is the line continuation character. @@ -35,8 +41,13 @@ bool DepfileParser::Parse(string* content, string* err) { // parsing_targets: whether we are parsing targets or dependencies. char* in = &(*content)[0]; char* end = in + content->size(); + bool have_target = false; + bool have_secondary_target_on_this_rule = false; + bool have_newline_since_primary_target = false; + bool warned_distinct_target_lines = false; bool parsing_targets = true; while (in < end) { + bool have_newline = false; // out: current output point (typically same as in, but can fall behind // as we de-escape backslashes). char* out = in; @@ -45,6 +56,7 @@ bool DepfileParser::Parse(string* content, string* err) { for (;;) { // start: beginning of the current parsed span. const char* start = in; + char* yymarker = NULL; { unsigned char yych; @@ -84,17 +96,25 @@ bool DepfileParser::Parse(string* content, string* err) { }; yych = *in; if (yybm[0+yych] & 128) { - goto yy6; - } - if (yych <= '$') { - if (yych <= 0x00) goto yy2; - if (yych <= '#') goto yy4; goto yy9; + } + if (yych <= '\r') { + if (yych <= '\t') { + if (yych >= 0x01) goto yy4; + } else { + if (yych <= '\n') goto yy6; + if (yych <= '\f') goto yy4; + goto yy8; + } } else { - if (yych == '\\') goto yy10; - goto yy4; + if (yych <= '$') { + if (yych <= '#') goto yy4; + goto yy12; + } else { + if (yych == '\\') goto yy13; + goto yy4; + } } -yy2: ++in; { break; @@ -108,9 +128,20 @@ yy5: break; } yy6: + ++in; + { + // A newline ends the current file name and the current rule. + have_newline = true; + break; + } +yy8: + yych = *++in; + if (yych == '\n') goto yy6; + goto yy5; +yy9: yych = *++in; if (yybm[0+yych] & 128) { - goto yy6; + goto yy9; } { // Got a span of plain text. @@ -121,41 +152,41 @@ yy6: out += len; continue; } -yy9: +yy12: yych = *++in; - if (yych == '$') goto yy11; + if (yych == '$') goto yy14; goto yy5; -yy10: - yych = *++in; +yy13: + yych = *(yymarker = ++in); if (yych <= '"') { if (yych <= '\f') { if (yych <= 0x00) goto yy5; - if (yych == '\n') goto yy5; - goto yy13; + if (yych == '\n') goto yy18; + goto yy16; } else { - if (yych <= '\r') goto yy5; - if (yych == ' ') goto yy15; - goto yy13; + if (yych <= '\r') goto yy20; + if (yych == ' ') goto yy22; + goto yy16; } } else { if (yych <= 'Z') { - if (yych <= '#') goto yy15; - if (yych == '*') goto yy15; - goto yy13; + if (yych <= '#') goto yy22; + if (yych == '*') goto yy22; + goto yy16; } else { - if (yych <= ']') goto yy15; - if (yych == '|') goto yy15; - goto yy13; + if (yych <= ']') goto yy22; + if (yych == '|') goto yy22; + goto yy16; } } -yy11: +yy14: ++in; { // De-escape dollar character. *out++ = '$'; continue; } -yy13: +yy16: ++in; { // Let backslash before other characters through verbatim. @@ -163,7 +194,18 @@ yy13: *out++ = yych; continue; } -yy15: +yy18: + ++in; + { + // A line continuation ends the current file name. + break; + } +yy20: + yych = *++in; + if (yych == '\n') goto yy18; + in = yymarker; + goto yy5; +yy22: ++in; { // De-escape backslashed character. @@ -175,25 +217,52 @@ yy15: } int len = (int)(out - filename); - const bool is_target = parsing_targets; + const bool is_dependency = !parsing_targets; if (len > 0 && filename[len - 1] == ':') { len--; // Strip off trailing colon, if any. parsing_targets = false; + have_target = true; } - if (len == 0) - continue; + if (len > 0) { + if (is_dependency) { + if (have_secondary_target_on_this_rule) { + if (!have_newline_since_primary_target) { + *err = "depfile has multiple output paths"; + return false; + } else if (options_.depfile_distinct_target_lines_action_ == + kDepfileDistinctTargetLinesActionError) { + *err = + "depfile has multiple output paths (on separate lines)" + " [-w depfilemulti=err]"; + return false; + } else { + if (!warned_distinct_target_lines) { + warned_distinct_target_lines = true; + Warning("depfile has multiple output paths (on separate lines); " + "continuing anyway [-w depfilemulti=warn]"); + } + continue; + } + } + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + have_secondary_target_on_this_rule = true; + } + } - if (!is_target) { - ins_.push_back(StringPiece(filename, len)); - } else if (!out_.str_) { - out_ = StringPiece(filename, len); - } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; + if (have_newline) { + // A newline ends a rule so the next filename will be a new target. + parsing_targets = true; + have_secondary_target_on_this_rule = false; + if (have_target) { + have_newline_since_primary_target = true; + } } } - if (parsing_targets) { + if (!have_target) { *err = "expected ':' in depfile"; return false; } diff --git a/src/depfile_parser.h b/src/depfile_parser.h index 1e6ebb5..be20374 100644 --- a/src/depfile_parser.h +++ b/src/depfile_parser.h @@ -21,8 +21,24 @@ using namespace std; #include "string_piece.h" +enum DepfileDistinctTargetLinesAction { + kDepfileDistinctTargetLinesActionWarn, + kDepfileDistinctTargetLinesActionError, +}; + +struct DepfileParserOptions { + DepfileParserOptions() + : depfile_distinct_target_lines_action_( + kDepfileDistinctTargetLinesActionWarn) {} + DepfileDistinctTargetLinesAction + depfile_distinct_target_lines_action_; +}; + /// Parser for the dependency information emitted by gcc's -M flags. struct DepfileParser { + explicit DepfileParser(DepfileParserOptions options = + DepfileParserOptions()); + /// Parse an input file. Input must be NUL-terminated. /// Warning: may mutate the content in-place and parsed StringPieces are /// pointers within it. @@ -30,6 +46,7 @@ struct DepfileParser { StringPiece out_; vector<StringPiece> ins_; + DepfileParserOptions options_; }; #endif // NINJA_DEPFILE_PARSER_H_ diff --git a/src/depfile_parser.in.cc b/src/depfile_parser.in.cc index 464efda..f8c94b3 100644 --- a/src/depfile_parser.in.cc +++ b/src/depfile_parser.in.cc @@ -13,6 +13,12 @@ // limitations under the License. #include "depfile_parser.h" +#include "util.h" + +DepfileParser::DepfileParser(DepfileParserOptions options) + : options_(options) +{ +} // A note on backslashes in Makefiles, from reading the docs: // Backslash-newline is the line continuation character. @@ -34,8 +40,13 @@ bool DepfileParser::Parse(string* content, string* err) { // parsing_targets: whether we are parsing targets or dependencies. char* in = &(*content)[0]; char* end = in + content->size(); + bool have_target = false; + bool have_secondary_target_on_this_rule = false; + bool have_newline_since_primary_target = false; + bool warned_distinct_target_lines = false; bool parsing_targets = true; while (in < end) { + bool have_newline = false; // out: current output point (typically same as in, but can fall behind // as we de-escape backslashes). char* out = in; @@ -44,10 +55,12 @@ bool DepfileParser::Parse(string* content, string* err) { for (;;) { // start: beginning of the current parsed span. const char* start = in; + char* yymarker = NULL; /*!re2c re2c:define:YYCTYPE = "unsigned char"; re2c:define:YYCURSOR = in; re2c:define:YYLIMIT = end; + re2c:define:YYMARKER = yymarker; re2c:yyfill:enable = 0; @@ -56,6 +69,7 @@ bool DepfileParser::Parse(string* content, string* err) { nul = "\000"; escape = [ \\#*[|\]]; + newline = '\r'?'\n'; '\\' escape { // De-escape backslashed character. @@ -85,6 +99,15 @@ bool DepfileParser::Parse(string* content, string* err) { nul { break; } + '\\' newline { + // A line continuation ends the current file name. + break; + } + newline { + // A newline ends the current file name and the current rule. + have_newline = true; + break; + } [^] { // For any other character (e.g. whitespace), swallow it here, // allowing the outer logic to loop around again. @@ -94,25 +117,52 @@ bool DepfileParser::Parse(string* content, string* err) { } int len = (int)(out - filename); - const bool is_target = parsing_targets; + const bool is_dependency = !parsing_targets; if (len > 0 && filename[len - 1] == ':') { len--; // Strip off trailing colon, if any. parsing_targets = false; + have_target = true; } - if (len == 0) - continue; + if (len > 0) { + if (is_dependency) { + if (have_secondary_target_on_this_rule) { + if (!have_newline_since_primary_target) { + *err = "depfile has multiple output paths"; + return false; + } else if (options_.depfile_distinct_target_lines_action_ == + kDepfileDistinctTargetLinesActionError) { + *err = + "depfile has multiple output paths (on separate lines)" + " [-w depfilemulti=err]"; + return false; + } else { + if (!warned_distinct_target_lines) { + warned_distinct_target_lines = true; + Warning("depfile has multiple output paths (on separate lines); " + "continuing anyway [-w depfilemulti=warn]"); + } + continue; + } + } + ins_.push_back(StringPiece(filename, len)); + } else if (!out_.str_) { + out_ = StringPiece(filename, len); + } else if (out_ != StringPiece(filename, len)) { + have_secondary_target_on_this_rule = true; + } + } - if (!is_target) { - ins_.push_back(StringPiece(filename, len)); - } else if (!out_.str_) { - out_ = StringPiece(filename, len); - } else if (out_ != StringPiece(filename, len)) { - *err = "depfile has multiple output paths"; - return false; + if (have_newline) { + // A newline ends a rule so the next filename will be a new target. + parsing_targets = true; + have_secondary_target_on_this_rule = false; + if (have_target) { + have_newline_since_primary_target = true; + } } } - if (parsing_targets) { + if (!have_target) { *err = "expected ':' in depfile"; return false; } diff --git a/src/depfile_parser_test.cc b/src/depfile_parser_test.cc index 824073f..52fe7cd 100644 --- a/src/depfile_parser_test.cc +++ b/src/depfile_parser_test.cc @@ -119,10 +119,10 @@ TEST_F(DepfileParserTest, SpecialChars) { // https://github.com/google/libcxx/tree/master/test/iterators/stream.iterators/istreambuf.iterator/ string err; EXPECT_TRUE(Parse( -"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \n" -" en@quot.header~ t+t-x!=1 \n" -" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\n" -" Fu\303\244ball\n" +"C:/Program\\ Files\\ (x86)/Microsoft\\ crtdefs.h: \\\n" +" en@quot.header~ t+t-x!=1 \\\n" +" openldap/slapd.d/cn=config/cn=schema/cn={0}core.ldif\\\n" +" Fu\303\244ball\\\n" " a\\[1\\]b@2%c", &err)); ASSERT_EQ("", err); @@ -158,3 +158,133 @@ TEST_F(DepfileParserTest, RejectMultipleDifferentOutputs) { EXPECT_FALSE(Parse("foo bar: x y z", &err)); ASSERT_EQ("depfile has multiple output paths", err); } + +TEST_F(DepfileParserTest, MultipleEmptyRules) { + string err; + EXPECT_TRUE(Parse("foo: x\n" + "foo: \n" + "foo:\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(1u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMultipleRulesLF) { + string err; + EXPECT_TRUE(Parse("foo: x\n" + "foo: y\n" + "foo \\\n" + "foo: z\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMultipleRulesCRLF) { + string err; + EXPECT_TRUE(Parse("foo: x\r\n" + "foo: y\r\n" + "foo \\\r\n" + "foo: z\r\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMixedRulesLF) { + string err; + EXPECT_TRUE(Parse("foo: x\\\n" + " y\n" + "foo \\\n" + "foo: z\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, UnifyMixedRulesCRLF) { + string err; + EXPECT_TRUE(Parse("foo: x\\\r\n" + " y\r\n" + "foo \\\r\n" + "foo: z\r\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, IndentedRulesLF) { + string err; + EXPECT_TRUE(Parse(" foo: x\n" + " foo: y\n" + " foo: z\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, IndentedRulesCRLF) { + string err; + EXPECT_TRUE(Parse(" foo: x\r\n" + " foo: y\r\n" + " foo: z\r\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, TolerateMP) { + string err; + EXPECT_TRUE(Parse("foo: x y z\n" + "x:\n" + "y:\n" + "z:\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, MultipleRulesTolerateMP) { + string err; + EXPECT_TRUE(Parse("foo: x\n" + "x:\n" + "foo: y\n" + "y:\n" + "foo: z\n" + "z:\n", &err)); + ASSERT_EQ("foo", parser_.out_.AsString()); + ASSERT_EQ(3u, parser_.ins_.size()); + EXPECT_EQ("x", parser_.ins_[0].AsString()); + EXPECT_EQ("y", parser_.ins_[1].AsString()); + EXPECT_EQ("z", parser_.ins_[2].AsString()); +} + +TEST_F(DepfileParserTest, MultipleRulesRejectDifferentOutputs) { + // check that multiple different outputs are rejected by the parser + // when spread across multiple rules + DepfileParserOptions parser_opts; + parser_opts.depfile_distinct_target_lines_action_ = + kDepfileDistinctTargetLinesActionError; + DepfileParser parser(parser_opts); + string err; + string input = + "foo: x y\n" + "bar: y z\n"; + EXPECT_FALSE(parser.Parse(&input, &err)); + ASSERT_EQ("depfile has multiple output paths (on separate lines)" + " [-w depfilemulti=err]", err); +} diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc index 33462fe..090adc4 100644 --- a/src/disk_interface_test.cc +++ b/src/disk_interface_test.cc @@ -213,7 +213,7 @@ TEST_F(DiskInterfaceTest, RemoveFile) { struct StatTest : public StateTestWithBuiltinRules, public DiskInterface { - StatTest() : scan_(&state_, NULL, NULL, this, false) {} + StatTest() : scan_(&state_, NULL, NULL, this, NULL, false) {} // DiskInterface implementation. virtual TimeStamp Stat(const string& path, string* err) const; diff --git a/src/graph.cc b/src/graph.cc index 13fa85c..6c773aa 100644 --- a/src/graph.cc +++ b/src/graph.cc @@ -856,7 +856,9 @@ bool ImplicitDepLoader::LoadDepFile(Edge* edge, const string& path, return false; } - DepfileParser depfile; + DepfileParser depfile(depfile_parser_options_ + ? *depfile_parser_options_ + : DepfileParserOptions()); string depfile_err; if (!depfile.Parse(&content, &depfile_err)) { *err = path + ": " + depfile_err; diff --git a/src/graph.h b/src/graph.h index 21c95eb..b648947 100644 --- a/src/graph.h +++ b/src/graph.h @@ -26,6 +26,7 @@ using namespace std; #include "util.h" struct BuildLog; +struct DepfileParserOptions; struct DiskInterface; struct DepsLog; struct Edge; @@ -460,8 +461,10 @@ typedef set<Edge*, EdgeCmp> EdgeSet; /// "depfile" attribute in build files. struct ImplicitDepLoader { ImplicitDepLoader(State* state, DepsLog* deps_log, - DiskInterface* disk_interface) - : state_(state), disk_interface_(disk_interface), deps_log_(deps_log) {} + DiskInterface* disk_interface, + DepfileParserOptions const* depfile_parser_options) + : state_(state), disk_interface_(disk_interface), deps_log_(deps_log), + depfile_parser_options_(depfile_parser_options) {} /// Load implicit dependencies for \a edge. /// @return false on error (without filling \a err if info is just missing @@ -493,6 +496,7 @@ struct ImplicitDepLoader { State* state_; DiskInterface* disk_interface_; DepsLog* deps_log_; + DepfileParserOptions const* depfile_parser_options_; }; @@ -500,10 +504,12 @@ struct ImplicitDepLoader { /// and updating the dirty/outputs_ready state of all the nodes and edges. struct DependencyScan { DependencyScan(State* state, BuildLog* build_log, DepsLog* deps_log, - DiskInterface* disk_interface, bool missing_phony_is_err) + DiskInterface* disk_interface, + DepfileParserOptions const* depfile_parser_options, + bool missing_phony_is_err) : build_log_(build_log), disk_interface_(disk_interface), - dep_loader_(state, deps_log, disk_interface), + dep_loader_(state, deps_log, disk_interface, depfile_parser_options), missing_phony_is_err_(missing_phony_is_err) {} /// Used for tests. diff --git a/src/graph_test.cc b/src/graph_test.cc index b50d79f..a957513 100644 --- a/src/graph_test.cc +++ b/src/graph_test.cc @@ -18,7 +18,7 @@ #include "test.h" struct GraphTest : public StateTestWithBuiltinRules { - GraphTest() : scan_(&state_, NULL, NULL, &fs_, false) {} + GraphTest() : scan_(&state_, NULL, NULL, &fs_, NULL, false) {} VirtualFileSystem fs_; DependencyScan scan_; @@ -510,7 +510,7 @@ TEST_F(GraphTest, MissingPhonyWithPhonyOutputs) { EXPECT_TRUE(GetNode("foo")->dirty()); state_.Reset(); - DependencyScan scan(&state_, NULL, NULL, &fs_, true); + DependencyScan scan(&state_, NULL, NULL, &fs_, NULL, true); EXPECT_FALSE(scan.RecomputeDirty(GetNode("foo"), NULL, &err)); EXPECT_EQ("output foo of phony edge doesn't exist. Missing 'phony_output = true'?", err); } diff --git a/src/ninja.cc b/src/ninja.cc index 701eb7a..ac5cb7e 100644 --- a/src/ninja.cc +++ b/src/ninja.cc @@ -84,6 +84,10 @@ struct Options { /// Whether phony cycles should warn or print an error. bool phony_cycle_should_err; + /// Whether a depfile with multiple targets on separate lines should + /// warn or print an error. + bool depfile_distinct_target_lines_should_err; + /// Whether to remain persistent. bool persistent; }; @@ -1115,6 +1119,7 @@ bool WarningEnable(const string& name, Options* options, BuildConfig* config) { printf("warning flags:\n" " dupbuild={err,warn} multiple build lines for one target\n" " phonycycle={err,warn} phony build statement references itself\n" +" depfilemulti={err,warn} depfile has multiple output paths on separate lines\n" " missingdepfile={err,warn} how to treat missing depfiles\n" "\n" " requires -o usesphonyoutputs=yes\n" @@ -1134,6 +1139,12 @@ bool WarningEnable(const string& name, Options* options, BuildConfig* config) { } else if (name == "phonycycle=warn") { options->phony_cycle_should_err = false; return true; + } else if (name == "depfilemulti=err") { + options->depfile_distinct_target_lines_should_err = true; + return true; + } else if (name == "depfilemulti=warn") { + options->depfile_distinct_target_lines_should_err = false; + return true; } else if (name == "missingdepfile=err") { config->missing_depfile_should_err = true; return true; @@ -1514,6 +1525,11 @@ NORETURN void real_main(int argc, char** argv) { if (exit_code >= 0) exit(exit_code); + if (options.depfile_distinct_target_lines_should_err) { + config.depfile_parser_options.depfile_distinct_target_lines_action_ = + kDepfileDistinctTargetLinesActionError; + } + if (options.working_dir) { // The formatting of this string, complete with funny quotes, is // so Emacs can properly identify that the cwd has changed for |