summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNico Weber <nicolasweber@gmx.de>2015-03-25 03:53:06 (GMT)
committerNico Weber <nicolasweber@gmx.de>2015-03-25 03:53:06 (GMT)
commit41f3b3e2e81688b6e35fe81a1ce7b4032b7b9b6a (patch)
tree4c27f44479075415dbc6fcf656cae8ce4727f20a
parent30dbcb62168a494c1c172ddbe8dbad2ae3a561d6 (diff)
parent92b74c340ab6397cb728391995f86dfdb39bb186 (diff)
downloadNinja-41f3b3e2e81688b6e35fe81a1ce7b4032b7b9b6a.zip
Ninja-41f3b3e2e81688b6e35fe81a1ce7b4032b7b9b6a.tar.gz
Ninja-41f3b3e2e81688b6e35fe81a1ce7b4032b7b9b6a.tar.bz2
Merge pull request #944 from nico/flags
Add an opt-in flag to make duplicate edges an error (`-w dupbuild=err`).
-rw-r--r--src/manifest_parser.cc14
-rw-r--r--src/manifest_parser.h6
-rw-r--r--src/manifest_parser_test.cc13
-rw-r--r--src/ninja.cc43
4 files changed, 66 insertions, 10 deletions
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index 638d751..e8c0436 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -24,8 +24,10 @@
#include "util.h"
#include "version.h"
-ManifestParser::ManifestParser(State* state, FileReader* file_reader)
- : state_(state), file_reader_(file_reader), quiet_(false) {
+ManifestParser::ManifestParser(State* state, FileReader* file_reader,
+ bool dupe_edge_should_err)
+ : state_(state), file_reader_(file_reader),
+ dupe_edge_should_err_(dupe_edge_should_err), quiet_(false) {
env_ = &state->bindings_;
}
@@ -329,10 +331,14 @@ bool ManifestParser::ParseEdge(string* err) {
if (!CanonicalizePath(&path, &slash_bits, &path_err))
return lexer_.Error(path_err, err);
if (!state_->AddOut(edge, path, slash_bits)) {
- if (!quiet_) {
+ if (dupe_edge_should_err_) {
+ lexer_.Error("multiple rules generate " + path + " [-w dupbuild=err]",
+ err);
+ return false;
+ } else if (!quiet_) {
Warning("multiple rules generate %s. "
"builds involving this target will not be correct; "
- "continuing anyway",
+ "continuing anyway [-w dupbuild=warn]",
path.c_str());
}
}
diff --git a/src/manifest_parser.h b/src/manifest_parser.h
index 00a711d..f72cd6f 100644
--- a/src/manifest_parser.h
+++ b/src/manifest_parser.h
@@ -32,10 +32,11 @@ struct ManifestParser {
virtual bool ReadFile(const string& path, string* content, string* err) = 0;
};
- ManifestParser(State* state, FileReader* file_reader);
+ ManifestParser(State* state, FileReader* file_reader,
+ bool dupe_edge_should_err = false);
/// Load and parse a file.
- bool Load(const string& filename, string* err, Lexer* parent=NULL);
+ bool Load(const string& filename, string* err, Lexer* parent = NULL);
/// Parse a text string of input. Used by tests.
bool ParseTest(const string& input, string* err) {
@@ -65,6 +66,7 @@ private:
BindingEnv* env_;
FileReader* file_reader_;
Lexer lexer_;
+ bool dupe_edge_should_err_;
bool quiet_;
};
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index 7e72b34..8f7b575 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -364,6 +364,19 @@ TEST_F(ParserTest, NoDeadPointerFromDuplicateEdge) {
// 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";
+ ManifestParser parser(&state, this, /*dupe_edges_should_err=*/true);
+ string err;
+ EXPECT_FALSE(parser.ParseTest(kInput, &err));
+ EXPECT_EQ("input:5: multiple rules generate out1 [-w dupbuild=err]\n", err);
+}
+
TEST_F(ParserTest, ReservedWords) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
"rule build\n"
diff --git a/src/ninja.cc b/src/ninja.cc
index 48c7239..1e43137 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -64,6 +64,9 @@ 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;
};
/// The Ninja main() loads up a series of data structures; various tools need
@@ -192,14 +195,15 @@ void Usage(const BuildConfig& config) {
" -f FILE specify input build file [default=build.ninja]\n"
"\n"
" -j N run N jobs in parallel [default=%d, derived from CPUs available]\n"
-" -l N do not start new jobs if the load average is greater than N\n"
" -k N keep going until N jobs fail [default=1]\n"
+" -l N do not start new jobs if the load average is greater than N\n"
" -n dry run (don't run commands but act like they succeeded)\n"
" -v show all command lines while building\n"
"\n"
" -d MODE enable debugging (use -d list to list modes)\n"
" -t TOOL run a subtool (use -t list to list subtools)\n"
-" terminates toplevel options; further flags are passed to the tool\n",
+" terminates toplevel options; further flags are passed to the tool\n"
+" -w FLAG adjust warnings (use -w list to list warnings)\n",
kNinjaVersion, config.parallelism);
}
@@ -792,6 +796,32 @@ bool DebugEnable(const string& name) {
}
}
+/// Set a warning flag. Returns false if Ninja should exit instead of
+/// continuing.
+bool WarningEnable(const string& name, Options* options) {
+ if (name == "list") {
+ printf("warning flags:\n"
+" dupbuild={err,warn} multiple build lines for one target\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 {
+ const char* suggestion =
+ SpellcheckString(name.c_str(), "dupbuild=err", "dupbuild=warn", NULL);
+ if (suggestion) {
+ Error("unknown warning flag '%s', did you mean '%s'?",
+ name.c_str(), suggestion);
+ } else {
+ Error("unknown warning flag '%s'", name.c_str());
+ }
+ return false;
+ }
+}
+
bool NinjaMain::OpenBuildLog(bool recompact_only) {
string log_path = ".ninja_log";
if (!build_dir_.empty())
@@ -962,7 +992,7 @@ int ReadFlags(int* argc, char*** argv,
int opt;
while (!options->tool &&
- (opt = getopt_long(*argc, *argv, "d:f:j:k:l:nt:vC:h", kLongOptions,
+ (opt = getopt_long(*argc, *argv, "d:f:j:k:l:nt:vw:C:h", kLongOptions,
NULL)) != -1) {
switch (opt) {
case 'd':
@@ -1011,6 +1041,10 @@ int ReadFlags(int* argc, char*** argv,
case 'v':
config->verbosity = BuildConfig::VERBOSE;
break;
+ case 'w':
+ if (!WarningEnable(optarg, options))
+ return 1;
+ break;
case 'C':
options->working_dir = optarg;
break;
@@ -1067,7 +1101,8 @@ int real_main(int argc, char** argv) {
NinjaMain ninja(ninja_command, config);
RealFileReader file_reader;
- ManifestParser parser(&ninja.state_, &file_reader);
+ ManifestParser parser(&ninja.state_, &file_reader,
+ options.dupe_edges_should_err);
string err;
if (!parser.Load(options.input_file, &err)) {
Error("%s", err.c_str());