aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Willemsen <dwillemsen@google.com>2020-07-07 19:51:46 -0700
committerDan Willemsen <dwillemsen@google.com>2020-07-07 22:36:46 -0700
commitf94f19e6eca76079dba1c3fe272885f97dfcf5b7 (patch)
treec6eb53be55f3c9e87b98508e1d30da15459d65c8
parentadd90eee261445c62c3dabfdaf1bdd425310dd49 (diff)
parent0a2e2cae7038ce519b0524c07d7135c3e520c9cd (diff)
downloadninja-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.cc5
-rw-r--r--src/build.h2
-rw-r--r--src/depfile_parser.cc145
-rw-r--r--src/depfile_parser.h17
-rw-r--r--src/depfile_parser.in.cc72
-rw-r--r--src/depfile_parser_test.cc138
-rw-r--r--src/disk_interface_test.cc2
-rw-r--r--src/graph.cc4
-rw-r--r--src/graph.h14
-rw-r--r--src/graph_test.cc4
-rw-r--r--src/ninja.cc16
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