aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorColin Cross <ccross@android.com>2020-06-09 18:41:27 -0700
committerColin Cross <ccross@android.com>2020-06-19 23:09:16 -0700
commitadd90eee261445c62c3dabfdaf1bdd425310dd49 (patch)
tree0a72a7b58ac28175a5a151bdc3e06294bed7ae1e
parent0160e910ee3efcb8f2dccc965300eb8172cccdd1 (diff)
downloadninja-add90eee261445c62c3dabfdaf1bdd425310dd49.tar.gz
Add validation nodes to ninja
A common problem in the Android build is inserting rules that perform some sort of error checking that doesn't produce any artifacts needed by the build, for example static analysis tools. There are a few patterns currently used, both of which have downsides. The first is to have a rule that depends on all of the static analysis results. This ensures they run, but requires running static analysis over everything, and not just the active parts of the build graph. The second is to insert the static analysis rule into the build graph between the artifact producing rule and anything that depends on it, often copying the artifact as the output of the static analysis rule. This increases the critical path of the build, often reducing parallelism. In the case of copying the artifact, it also wastes disk space. This patch adds "validation nodes" to edges in Ninja. A build statement can specify validation nodes using "|@" in the edge inputs. The validation nodes are not used as an input to the edge (the edge can run before the validation node is ready), but are added to the initial nodes of the build graph whenever the edge is part of the build graph. The edge that outputs the validation node can depend on the output of the edge that is being validated. Test: ninja_test Change-Id: Ife27086c50c1b257a26509373199664680b2b247
-rw-r--r--doc/manual.asciidoc27
-rw-r--r--src/build.cc16
-rw-r--r--src/build_test.cc181
-rw-r--r--src/depfile_parser.cc5
-rw-r--r--src/disk_interface_test.cc8
-rw-r--r--src/graph.cc82
-rw-r--r--src/graph.h38
-rw-r--r--src/graph_test.cc88
-rw-r--r--src/lexer.cc1093
-rw-r--r--src/lexer.h1
-rw-r--r--src/lexer.in.cc2
-rw-r--r--src/manifest_chunk_parser.cc27
-rw-r--r--src/manifest_parser.cc10
-rw-r--r--src/manifest_parser_test.cc10
-rw-r--r--src/ninja.cc25
15 files changed, 1002 insertions, 611 deletions
diff --git a/doc/manual.asciidoc b/doc/manual.asciidoc
index 7d36701..4f8c74f 100644
--- a/doc/manual.asciidoc
+++ b/doc/manual.asciidoc
@@ -696,6 +696,8 @@ A file is a series of declarations. A declaration can be one of:
Order-only dependencies may be tacked on the end with +||
_dependency1_ _dependency2_+. (See <<ref_dependencies,the reference on
dependency types>>.)
+ Validations may be taked on the end with +|@ _validation1_ _validation2_+.
+ (See <<validations,the reference on validations>>.)
+
Implicit outputs _(available since Ninja 1.7)_ may be added before
the `:` with +| _output1_ _output2_+ and do not appear in `$out`.
@@ -972,6 +974,31 @@ express the implicit dependency.)
File paths are compared as is, which means that an absolute path and a
relative path, pointing to the same file, are considered different by Ninja.
+[[validations]]
+Validations
+~~~~~~~~~~~
+Validations listed on the build line cause the specified files to be
+added to the top level of the build graph (as if they were specified
+on the Ninja command line) whenever the build line is a transitive
+dependency of one of the targets specified on the command line or a
+default target.
+
+Validations are added to the build graph regardless of whether the output
+files of the build statement are dirty are not, and the dirty state of
+the build statement that outputs the file being used as a validation
+has no effect on the dirty state of the build statement that requested it.
+
+A build edge can list another build edge as a validation even if the second
+edge depends on the first.
+
+Validations are designed to handle rules that perform error checking but
+don't produce any artifacts needed by the build, for example static
+analysis tools. Marking the static analysis rule as an implicit input
+of the main build rule of the source files or of the rules that depend
+on the main build rule would slow down the critical path of the build,
+but using a validation would allow the build to proceed in parallel with
+the static analysis rule once the main build rule is complete.
+
Variable expansion
~~~~~~~~~~~~~~~~~~
diff --git a/src/build.cc b/src/build.cc
index 19121ee..045331b 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -411,7 +411,8 @@ Node* Builder::AddTarget(const string& name, string* err) {
}
bool Builder::AddTargets(const std::vector<Node*> &nodes, string* err) {
- if (!scan_.RecomputeNodesDirty(nodes, err))
+ std::vector<Node*> validation_nodes;
+ if (!scan_.RecomputeNodesDirty(nodes, &validation_nodes, err))
return false;
for (Node* node : nodes) {
@@ -427,6 +428,19 @@ bool Builder::AddTargets(const std::vector<Node*> &nodes, string* err) {
}
}
+ for (Node* node : validation_nodes) {
+ std::string plan_err;
+ if (!plan_.AddTarget(node, &plan_err)) {
+ if (!plan_err.empty()) {
+ *err = plan_err;
+ return false;
+ } else {
+ // Added a target that is already up-to-date; not really
+ // an error.
+ }
+ }
+ }
+
return true;
}
diff --git a/src/build_test.cc b/src/build_test.cc
index ccfffa1..f7c8550 100644
--- a/src/build_test.cc
+++ b/src/build_test.cc
@@ -2539,7 +2539,6 @@ TEST_F(BuildWithLogTest, OldOutputFileWarning) {
EXPECT_EQ("", err);
EXPECT_TRUE(builder_.Build(&err));
EXPECT_EQ("", err);
-
EXPECT_EQ("ninja: Missing `restat`? An output file is older than the most recent input:\n output: out\n input: in", status_.last_output_);
}
@@ -2708,4 +2707,182 @@ TEST_F(BuildTest, PreRemoveOutputsWithPhonyOutputs) {
EXPECT_EQ(1u, command_runner_.commands_ran_.size());
EXPECT_EQ(0u, fs_.files_created_.size());
EXPECT_EQ(0u, fs_.files_removed_.size());
-} \ No newline at end of file
+}
+
+TEST_F(BuildTest, Validation) {
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+ "build out: cat in |@ validate\n"
+ "build validate: cat in2\n"));
+
+ fs_.Create("in", "");
+ fs_.Create("in2", "");
+
+ string err;
+ EXPECT_TRUE(builder_.AddTarget("out", &err));
+ EXPECT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+
+ EXPECT_EQ(2u, command_runner_.commands_ran_.size());
+
+ // Test touching "in" only rebuilds "out" ("validate" doesn't depend on
+ // "out").
+ fs_.Tick();
+ fs_.Create("in", "");
+
+ err.clear();
+ command_runner_.commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("out", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+
+ EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+
+ // Test touching "in2" only rebuilds "validate" ("out" doesn't depend on
+ // "validate").
+ fs_.Tick();
+ fs_.Create("in2", "");
+
+ err.clear();
+ command_runner_.commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("out", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+
+ EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+}
+
+TEST_F(BuildTest, ValidationDependsOnOutput) {
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+ "build out: cat in |@ validate\n"
+ "build validate: cat in2 | out\n"));
+
+ fs_.Create("in", "");
+ fs_.Create("in2", "");
+
+ string err;
+ EXPECT_TRUE(builder_.AddTarget("out", &err));
+ EXPECT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+
+ EXPECT_EQ(2u, command_runner_.commands_ran_.size());
+
+ // Test touching "in" rebuilds "out" and "validate".
+ fs_.Tick();
+ fs_.Create("in", "");
+
+ err.clear();
+ command_runner_.commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("out", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+
+ EXPECT_EQ(2u, command_runner_.commands_ran_.size());
+
+ // Test touching "in2" only rebuilds "validate" ("out" doesn't depend on
+ // "validate").
+ fs_.Tick();
+ fs_.Create("in2", "");
+
+ err.clear();
+ command_runner_.commands_ran_.clear();
+ state_.Reset();
+ EXPECT_TRUE(builder_.AddTarget("out", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder_.Build(&err));
+ EXPECT_EQ("", err);
+
+ EXPECT_EQ(1u, command_runner_.commands_ran_.size());
+}
+
+TEST_F(BuildWithDepsLogTest, ValidationThroughDepfile) {
+ const char* manifest =
+ "build out: cat in |@ validate\n"
+ "build validate: cat in2 | out\n"
+ "build out2: cat in3\n"
+ " deps = gcc\n"
+ " depfile = out2.d\n";
+
+ string err;
+
+ {
+ fs_.Create("in", "");
+ fs_.Create("in2", "");
+ fs_.Create("in3", "");
+ fs_.Create("out2.d", "out: out");
+
+ State state;
+ ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));
+
+ DepsLog deps_log;
+ ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", fs_, &err));
+ ASSERT_EQ("", err);
+
+ Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);
+ builder.command_runner_.reset(&command_runner_);
+
+ EXPECT_TRUE(builder.AddTarget("out2", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder.Build(&err));
+ EXPECT_EQ("", err);
+
+ // On the first build, only the out2 command is run.
+ EXPECT_EQ(command_runner_.commands_ran_.size(), 1);
+
+ // The deps file should have been removed.
+ EXPECT_EQ(0, fs_.Stat("out2.d", &err));
+
+ deps_log.Close();
+ builder.command_runner_.release();
+ }
+
+ fs_.Tick();
+ command_runner_.commands_ran_.clear();
+
+ {
+ fs_.Create("in2", "");
+ fs_.Create("in3", "");
+
+ State state;
+ ASSERT_NO_FATAL_FAILURE(AddCatRule(&state));
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state, manifest));
+
+ DepsLog deps_log;
+ ASSERT_TRUE(deps_log.Load("ninja_deps", &state, &err));
+ ASSERT_TRUE(deps_log.OpenForWrite("ninja_deps", fs_, &err));
+ ASSERT_EQ("", err);
+
+ Builder builder(&state, config_, NULL, &deps_log, &fs_, &status_, 0);
+ builder.command_runner_.reset(&command_runner_);
+
+ EXPECT_TRUE(builder.AddTarget("out2", &err));
+ ASSERT_EQ("", err);
+
+ EXPECT_TRUE(builder.Build(&err));
+ EXPECT_EQ("", err);
+
+ // The out and validate actions should have been run as well as out2.
+ ASSERT_EQ(command_runner_.commands_ran_.size(), 3);
+ EXPECT_EQ(command_runner_.commands_ran_[0], "cat in > out");
+ EXPECT_EQ(command_runner_.commands_ran_[1], "cat in2 > validate");
+ EXPECT_EQ(command_runner_.commands_ran_[2], "cat in3 > out2");
+
+ deps_log.Close();
+ builder.command_runner_.release();
+ }
+}
diff --git a/src/depfile_parser.cc b/src/depfile_parser.cc
index 345fa15..8d9345e 100644
--- a/src/depfile_parser.cc
+++ b/src/depfile_parser.cc
@@ -1,4 +1,4 @@
-/* Generated by re2c 0.16 */
+/* Generated by re2c 1.3 */
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -108,8 +108,7 @@ yy5:
break;
}
yy6:
- ++in;
- yych = *in;
+ yych = *++in;
if (yybm[0+yych] & 128) {
goto yy6;
}
diff --git a/src/disk_interface_test.cc b/src/disk_interface_test.cc
index 07bd4db..33462fe 100644
--- a/src/disk_interface_test.cc
+++ b/src/disk_interface_test.cc
@@ -274,7 +274,7 @@ TEST_F(StatTest, Simple) {
EXPECT_TRUE(out->Stat(this, &err));
EXPECT_EQ("", err);
ASSERT_EQ(1u, stats_.size());
- scan_.RecomputeDirty(out, NULL);
+ scan_.RecomputeDirty(out, NULL, NULL);
ASSERT_EQ(2u, stats_.size());
ASSERT_EQ("out", stats_[0]);
ASSERT_EQ("in", stats_[1]);
@@ -290,7 +290,7 @@ TEST_F(StatTest, TwoStep) {
EXPECT_TRUE(out->Stat(this, &err));
EXPECT_EQ("", err);
ASSERT_EQ(1u, stats_.size());
- scan_.RecomputeDirty(out, NULL);
+ scan_.RecomputeDirty(out, NULL, NULL);
ASSERT_EQ(3u, stats_.size());
ASSERT_EQ("out", stats_[0]);
ASSERT_TRUE(GetNode("out")->dirty());
@@ -310,7 +310,7 @@ TEST_F(StatTest, Tree) {
EXPECT_TRUE(out->Stat(this, &err));
EXPECT_EQ("", err);
ASSERT_EQ(1u, stats_.size());
- scan_.RecomputeDirty(out, NULL);
+ scan_.RecomputeDirty(out, NULL, NULL);
ASSERT_EQ(1u + 6u, stats_.size());
ASSERT_EQ("mid1", stats_[1]);
ASSERT_TRUE(GetNode("mid1")->dirty());
@@ -331,7 +331,7 @@ TEST_F(StatTest, Middle) {
EXPECT_TRUE(out->Stat(this, &err));
EXPECT_EQ("", err);
ASSERT_EQ(1u, stats_.size());
- scan_.RecomputeDirty(out, NULL);
+ scan_.RecomputeDirty(out, NULL, NULL);
ASSERT_FALSE(GetNode("in")->dirty());
ASSERT_TRUE(GetNode("mid")->dirty());
ASSERT_TRUE(GetNode("out")->dirty());
diff --git a/src/graph.cc b/src/graph.cc
index e93a2e1..13fa85c 100644
--- a/src/graph.cc
+++ b/src/graph.cc
@@ -14,6 +14,7 @@
#include "graph.h"
+#include <deque>
#include <assert.h>
#include <stdio.h>
@@ -54,6 +55,7 @@ bool Node::LStat(DiskInterface* disk_interface, bool* is_dir, string* err) {
}
bool DependencyScan::RecomputeNodesDirty(const std::vector<Node*>& initial_nodes,
+ std::vector<Node*>* validation_nodes,
std::string* err) {
METRIC_RECORD("dep scan");
std::vector<Node*> all_nodes;
@@ -70,15 +72,32 @@ bool DependencyScan::RecomputeNodesDirty(const std::vector<Node*>& initial_nodes
if (!PrecomputeNodesDirty(all_nodes, all_edges, thread_pool.get(), err))
success = false;
+
+ std::deque<Node*> nodes(initial_nodes.begin(), initial_nodes.end());
+
if (success) {
METRIC_RECORD("dep scan : main pass");
- vector<Node*> stack;
- for (Node* node : initial_nodes) {
+ std::vector<Node*> stack;
+ std::vector<Node*> new_validation_nodes;
+ while (!nodes.empty()) {
+ Node* node = nodes.front();
+ nodes.pop_front();
+
stack.clear();
- if (!RecomputeNodeDirty(node, &stack, err)) {
+ new_validation_nodes.clear();
+ if (!RecomputeNodeDirty(node, &stack, &new_validation_nodes, err)) {
success = false;
break;
}
+ nodes.insert(nodes.end(), new_validation_nodes.begin(),
+ new_validation_nodes.end());
+ if (!new_validation_nodes.empty()) {
+ assert(validation_nodes &&
+ "validations require RecomputeDirty to be called with validation_nodes");
+ validation_nodes->insert(validation_nodes->end(),
+ new_validation_nodes.begin(),
+ new_validation_nodes.end());
+ }
}
}
@@ -106,6 +125,7 @@ void DependencyScan::CollectPrecomputeLists(Node* node,
if (edge && !edge->precomputed_dirtiness_) {
edge->precomputed_dirtiness_ = true;
edges->push_back(edge);
+
for (Node* node : edge->inputs_) {
// Duplicate the dirtiness check here to avoid an unnecessary function
// call. (The precomputed_dirtiness() will be inlined, but the recursive
@@ -113,6 +133,16 @@ void DependencyScan::CollectPrecomputeLists(Node* node,
if (!node->precomputed_dirtiness())
CollectPrecomputeLists(node, nodes, edges);
}
+
+ if (!edge->validations_.empty()) {
+ for (Node* node : edge->validations_) {
+ // Duplicate the dirtiness check here to avoid an unnecessary function
+ // call. (The precomputed_dirtiness() will be inlined, but the recursive
+ // call can't be.)
+ if (!node->precomputed_dirtiness())
+ CollectPrecomputeLists(node, nodes, edges);
+ }
+ }
}
// Collect dependencies from the deps log. This pass could also examine
@@ -172,6 +202,7 @@ bool DependencyScan::PrecomputeNodesDirty(const std::vector<Node*>& nodes,
}
bool DependencyScan::RecomputeNodeDirty(Node* node, std::vector<Node*>* stack,
+ std::vector<Node*>* validation_nodes,
std::string* err) {
Edge* edge = node->in_edge();
if (!edge) {
@@ -230,12 +261,20 @@ bool DependencyScan::RecomputeNodeDirty(Node* node, std::vector<Node*>* stack,
}
}
+ // Store any validation nodes from the edge for adding to the initial
+ // nodes. Don't recurse into them, that would trigger the dependency
+ // cycle detector if the validation node depends on this node.
+ // RecomputeNodesDirty will add the validation nodes to the initial nodes
+ // and recurse into them.
+ validation_nodes->insert(validation_nodes->end(),
+ edge->validations_.begin(), edge->validations_.end());
+
// Visit all inputs; we're dirty if any of the inputs are dirty.
Node* most_recent_input = NULL;
for (vector<Node*>::iterator i = edge->inputs_.begin();
i != edge->inputs_.end(); ++i) {
// Visit this input.
- if (!RecomputeNodeDirty(*i, stack, err))
+ if (!RecomputeNodeDirty(*i, stack, validation_nodes, err))
return false;
// If an input is not ready, neither are our outputs.
@@ -633,6 +672,13 @@ void Edge::Dump(const char* prefix) const {
i != outputs_.end() && *i != NULL; ++i) {
printf("%s ", (*i)->path().c_str());
}
+ if (!validations_.empty()) {
+ printf(" validations ");
+ for (vector<Node*>::const_iterator i = validations_.begin();
+ i != validations_.end() && *i != NULL; ++i) {
+ printf("%s ", (*i)->path().c_str());
+ }
+ }
if (pool_) {
if (!pool_->name().empty()) {
printf("(in pool '%s')", pool_->name().c_str());
@@ -718,6 +764,16 @@ std::vector<Edge*> Node::GetOutEdges() const {
return result;
}
+std::vector<Edge*> Node::GetValidationOutEdges() const {
+ std::vector<Edge*> result;
+ for (EdgeList* node = validation_out_edges_.load(); node != nullptr; node = node->next) {
+ result.push_back(node->edge);
+ }
+ std::sort(result.begin(), result.end(), EdgeCmp());
+
+ return result;
+}
+
void Node::AddOutEdge(Edge* edge) {
EdgeList* new_node = new EdgeList { edge };
while (true) {
@@ -728,6 +784,16 @@ void Node::AddOutEdge(Edge* edge) {
}
}
+void Node::AddValidationOutEdge(Edge* edge) {
+ EdgeList* new_node = new EdgeList { edge };
+ while (true) {
+ EdgeList* cur_head = validation_out_edges_.load();
+ new_node->next = cur_head;
+ if (validation_out_edges_.compare_exchange_weak(cur_head, new_node))
+ break;
+ }
+}
+
void Node::Dump(const char* prefix) const {
printf("%s <%s 0x%p> mtime: %" PRId64 "%s, (:%s), ",
prefix, path().c_str(), this,
@@ -744,6 +810,14 @@ void Node::Dump(const char* prefix) const {
e != out_edges.end() && *e != NULL; ++e) {
(*e)->Dump(" +- ");
}
+ const std::vector<Edge*> validation_out_edges = GetValidationOutEdges();
+ if (!validation_out_edges.empty()) {
+ printf(" validation out edges:\n");
+ for (vector<Edge*>::const_iterator e = validation_out_edges.begin();
+ e != validation_out_edges.end() && *e != NULL; ++e) {
+ (*e)->Dump(" +- ");
+ }
+ }
}
bool ImplicitDepLoader::LoadDeps(Edge* edge, string* err) {
diff --git a/src/graph.h b/src/graph.h
index 5c8ea8a..21c95eb 100644
--- a/src/graph.h
+++ b/src/graph.h
@@ -187,7 +187,9 @@ struct Node {
// Thread-safe properties.
bool has_out_edge() const;
std::vector<Edge*> GetOutEdges() const;
+ std::vector<Edge*> GetValidationOutEdges() const;
void AddOutEdge(Edge* edge);
+ void AddValidationOutEdge(Edge* edge);
/// Add an out-edge from the dependency scan. This function differs from
/// AddOutEdge in several ways:
@@ -245,6 +247,7 @@ private:
/// All Edges that use this Node as an input. The order of this list is
/// non-deterministic. An accessor function sorts it each time it's used.
std::atomic<EdgeList*> out_edges_ { nullptr };
+ std::atomic<EdgeList*> validation_out_edges_ { nullptr };
std::vector<Edge*> dep_scan_out_edges_;
@@ -360,12 +363,18 @@ public:
/// Temporary fields used only during manifest parsing.
struct DeferredPathList {
- DeferredPathList(const char* lexer_pos=nullptr, bool is_output=false,
- int count=0)
- : lexer_pos(lexer_pos), is_output(is_output), count(count) {}
+ enum Type {
+ INPUT = 0,
+ OUTPUT = 1,
+ VALIDATION = 2,
+ };
+
+ DeferredPathList(const char* lexer_pos=nullptr,
+ Type type = INPUT, int count=0)
+ : lexer_pos(lexer_pos), type(type), count(count) {}
const char* lexer_pos = nullptr;
- bool is_output = false;
+ Type type;
int count = 0;
};
struct {
@@ -380,6 +389,7 @@ public:
Pool* pool_ = nullptr;
vector<Node*> inputs_;
vector<Node*> outputs_;
+ vector<Node*> validations_;
std::vector<std::pair<HashedStr, std::string>> unevaled_bindings_;
VisitMark mark_ = VisitNone;
bool precomputed_dirtiness_ = false;
@@ -425,6 +435,8 @@ public:
return index >= outputs_.size() - implicit_outs_;
}
+ int validation_deps_ = 0;
+
bool is_phony() const;
bool use_console() const;
bool maybe_phonycycle_diagnostic() const;
@@ -495,16 +507,22 @@ struct DependencyScan {
missing_phony_is_err_(missing_phony_is_err) {}
/// Used for tests.
- bool RecomputeDirty(Node* node, std::string* err) {
- return RecomputeNodesDirty({ node }, err);
+ bool RecomputeDirty(Node* node, std::vector<Node*>* validation_nodes,
+ std::string* err) {
+ std::vector<Node*> nodes = {node};
+ return RecomputeNodesDirty(nodes, validation_nodes, err);
}
- /// Update the |dirty_| state of the given node by inspecting its input edge.
+ /// Update the |dirty_| state of the given nodes by transitively inspecting
+ /// their input edges.
/// Examine inputs, outputs, and command lines to judge whether an edge
/// needs to be re-run, and update outputs_ready_ and each outputs' |dirty_|
/// state accordingly.
+ /// Appends any validation nodes found to the nodes parameter.
/// Returns false on failure.
- bool RecomputeNodesDirty(const std::vector<Node*>& nodes, std::string* err);
+ bool RecomputeNodesDirty(const std::vector<Node*>& initial_nodes,
+ std::vector<Node*>* validation_nodes,
+ std::string* err);
/// Recompute whether any output of the edge is dirty, if so sets |*dirty|.
/// Returns false on failure.
@@ -534,7 +552,9 @@ struct DependencyScan {
const std::vector<Edge*>& edges,
ThreadPool* thread_pool, std::string* err);
- bool RecomputeNodeDirty(Node* node, vector<Node*>* stack, string* err);
+ bool RecomputeNodeDirty(Node* node, vector<Node*>* stack,
+ vector<Node*>* validation_nodes, string* err);
+
bool VerifyDAG(Node* node, vector<Node*>* stack, string* err);
/// Recompute whether a given single output should be marked dirty.
diff --git a/src/graph_test.cc b/src/graph_test.cc
index da495c8..b50d79f 100644
--- a/src/graph_test.cc
+++ b/src/graph_test.cc
@@ -31,7 +31,7 @@ TEST_F(GraphTest, MissingImplicit) {
fs_.Create("out", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
// A missing implicit dep *should* make the output dirty.
@@ -49,7 +49,7 @@ TEST_F(GraphTest, ModifiedImplicit) {
fs_.Create("implicit", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
// A modified implicit dep should make the output dirty.
@@ -69,7 +69,7 @@ TEST_F(GraphTest, FunkyMakefilePath) {
fs_.Create("implicit.h", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
ASSERT_EQ("", err);
// implicit.h has changed, though our depfile refers to it with a
@@ -92,7 +92,7 @@ TEST_F(GraphTest, ExplicitImplicit) {
fs_.Create("data", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
ASSERT_EQ("", err);
// We have both an implicit and an explicit dep on implicit.h.
@@ -120,7 +120,7 @@ TEST_F(GraphTest, ImplicitOutputMissing) {
fs_.Create("out", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out")->dirty());
@@ -136,7 +136,7 @@ TEST_F(GraphTest, ImplicitOutputOutOfDate) {
fs_.Create("out", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out")->dirty());
@@ -160,7 +160,7 @@ TEST_F(GraphTest, ImplicitOutputOnlyMissing) {
fs_.Create("in", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out.imp")->dirty());
@@ -174,7 +174,7 @@ TEST_F(GraphTest, ImplicitOutputOnlyOutOfDate) {
fs_.Create("in", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.imp"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out.imp")->dirty());
@@ -191,7 +191,7 @@ TEST_F(GraphTest, PathWithCurrentDirectory) {
fs_.Create("out.o", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_FALSE(GetNode("out.o")->dirty());
@@ -239,7 +239,7 @@ TEST_F(GraphTest, DepfileWithCanonicalizablePath) {
fs_.Create("out.o", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_FALSE(GetNode("out.o")->dirty());
@@ -259,13 +259,13 @@ TEST_F(GraphTest, DepfileRemoved) {
fs_.Create("out.o", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_FALSE(GetNode("out.o")->dirty());
state_.Reset();
fs_.RemoveFile("out.o.d");
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out.o"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out.o")->dirty());
}
@@ -312,7 +312,7 @@ TEST_F(GraphTest, NestedPhonyPrintsDone) {
"build n2: phony n1\n"
);
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("n2"), NULL, &err));
ASSERT_EQ("", err);
Plan plan_;
@@ -331,7 +331,7 @@ TEST_F(GraphTest, PhonySelfReferenceError) {
parser_opts);
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
ASSERT_EQ("dependency cycle: a -> a [-w phonycycle=err]", err);
}
@@ -347,7 +347,7 @@ TEST_F(GraphTest, OutputSymlinkSourceUpdate) {
fs_.Create("other", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_FALSE(GetNode("out")->dirty());
@@ -362,7 +362,7 @@ TEST_F(GraphTest, OutputSymlinkDangling) {
fs_.Create("out", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_FALSE(GetNode("out")->dirty());
@@ -378,7 +378,7 @@ TEST_F(GraphTest, InputSymlink) {
fs_.Create("in", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out")->dirty());
@@ -394,7 +394,7 @@ TEST_F(GraphTest, InputSymlinkUpdate) {
fs_.CreateSymlink("sym", "in");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
// This can be incorrect if the destination of the symlink changed to
@@ -410,7 +410,7 @@ TEST_F(GraphTest, InputSymlinkDangling) {
fs_.CreateSymlink("sym", "in");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out")->dirty());
@@ -425,7 +425,7 @@ TEST_F(GraphTest, InputDirectoryUpToDate) {
EXPECT_TRUE(fs_.WriteFile("inputs/foo", ""));
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_FALSE(GetNode("out")->dirty());
@@ -441,7 +441,7 @@ TEST_F(GraphTest, InputDirectoryChanged) {
EXPECT_TRUE(fs_.WriteFile("inputs/foo", ""));
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("out")->dirty());
@@ -468,7 +468,7 @@ TEST_F(GraphTest, PhonyOutputDependsOnPhonyOutput) {
"build bar: phony_out foo\n"));
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("bar"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("bar"), NULL, &err));
ASSERT_EQ("", err);
}
@@ -483,7 +483,7 @@ TEST_F(GraphTest, RealDependsOnPhonyOutput) {
"build bar: touch foo\n"));
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("bar"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("bar"), NULL, &err));
EXPECT_EQ("real file 'bar' depends on phony output 'foo'\n", err);
}
@@ -496,7 +496,7 @@ TEST_F(GraphTest, PhonyDependsOnPhonyOutput) {
"build bar: phony foo\n"));
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("bar"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("bar"), NULL, &err));
EXPECT_EQ("real file 'bar' depends on phony output 'foo'\n", err);
}
@@ -505,13 +505,13 @@ TEST_F(GraphTest, MissingPhonyWithPhonyOutputs) {
"build foo: phony\n"));
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("foo"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("foo"), NULL, &err));
EXPECT_EQ("", err);
EXPECT_TRUE(GetNode("foo")->dirty());
state_.Reset();
DependencyScan scan(&state_, NULL, NULL, &fs_, true);
- EXPECT_FALSE(scan.RecomputeDirty(GetNode("foo"), &err));
+ EXPECT_FALSE(scan.RecomputeDirty(GetNode("foo"), NULL, &err));
EXPECT_EQ("output foo of phony edge doesn't exist. Missing 'phony_output = true'?", err);
}
@@ -523,7 +523,7 @@ TEST_F(GraphTest, DependencyCycle) {
"build pre: cat out\n");
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("out"), NULL, &err));
ASSERT_EQ("dependency cycle: out -> mid -> in -> pre -> out", err);
}
@@ -531,7 +531,7 @@ TEST_F(GraphTest, CycleInEdgesButNotInNodes1) {
string err;
AssertParse(&state_,
"build a b: cat a\n");
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
ASSERT_EQ("dependency cycle: a -> a", err);
}
@@ -539,7 +539,7 @@ TEST_F(GraphTest, CycleInEdgesButNotInNodes2) {
string err;
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build b a: cat a\n"));
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
ASSERT_EQ("dependency cycle: a -> a", err);
}
@@ -548,7 +548,7 @@ TEST_F(GraphTest, CycleInEdgesButNotInNodes3) {
ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
"build a b: cat c\n"
"build c: cat a\n"));
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("b"), NULL, &err));
ASSERT_EQ("dependency cycle: a -> c -> a", err);
}
@@ -560,7 +560,7 @@ TEST_F(GraphTest, CycleInEdgesButNotInNodes4) {
"build b: cat a\n"
"build a e: cat d\n"
"build f: cat e\n"));
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("f"), NULL, &err));
ASSERT_EQ("dependency cycle: a -> d -> c -> b -> a", err);
}
@@ -576,7 +576,7 @@ TEST_F(GraphTest, CycleWithLengthZeroFromDepfile) {
fs_.Create("dep.d", "a: b\n");
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
ASSERT_EQ("dependency cycle: b -> b", err);
// Despite the depfile causing edge to be a cycle (it has outputs a and b,
@@ -601,7 +601,7 @@ TEST_F(GraphTest, CycleWithLengthOneFromDepfile) {
fs_.Create("dep.d", "a: c\n");
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("a"), NULL, &err));
ASSERT_EQ("dependency cycle: b -> c -> b", err);
// Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
@@ -628,7 +628,7 @@ TEST_F(GraphTest, CycleWithLengthOneFromDepfileOneHopAway) {
fs_.Create("dep.d", "a: c\n");
string err;
- EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), &err));
+ EXPECT_FALSE(scan_.RecomputeDirty(GetNode("d"), NULL, &err));
ASSERT_EQ("dependency cycle: b -> c -> b", err);
// Despite the depfile causing edge to be a cycle (|edge| has outputs a and b,
@@ -701,8 +701,26 @@ TEST_F(GraphTest, PhonyOutputAlwaysDirty) {
fs_.Create("foo", "");
string err;
- EXPECT_TRUE(scan_.RecomputeDirty(GetNode("foo"), &err));
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("foo"), NULL, &err));
ASSERT_EQ("", err);
EXPECT_TRUE(GetNode("foo")->dirty());
}
+
+TEST_F(GraphTest, Validation) {
+ ASSERT_NO_FATAL_FAILURE(AssertParse(&state_,
+"build out: cat in |@ validate\n"
+"build validate: cat in\n"));
+
+ fs_.Create("in", "");
+ string err;
+ std::vector<Node*> validation_nodes;
+ EXPECT_TRUE(scan_.RecomputeDirty(GetNode("out"), &validation_nodes, &err));
+ ASSERT_EQ("", err);
+
+ ASSERT_EQ(validation_nodes.size(), 1);
+ EXPECT_EQ(validation_nodes[0]->path(), "validate");
+
+ EXPECT_TRUE(GetNode("out")->dirty());
+ EXPECT_TRUE(GetNode("validate")->dirty());
+}
diff --git a/src/lexer.cc b/src/lexer.cc
index 8639901..a5c3c27 100644
--- a/src/lexer.cc
+++ b/src/lexer.cc
@@ -1,4 +1,4 @@
-/* Generated by re2c 1.0.1 */
+/* Generated by re2c 1.3 */
// Copyright 2011 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
@@ -139,6 +139,7 @@ const char* Lexer::TokenName(Token t) {
case NEWLINE: return "newline";
case PIPE2: return "'||'";
case PIPE: return "'|'";
+ case PIPEAT: return "'|@'";
case POOL: return "'pool'";
case RULE: return "'rule'";
case SUBNINJA: return "'subninja'";
@@ -347,7 +348,8 @@ yy25:
goto yy14;
yy26:
yych = *++p;
- if (yych == '|') goto yy41;
+ if (yych == '@') goto yy41;
+ if (yych == '|') goto yy43;
{ token = PIPE; break; }
yy28:
yych = *++p;
@@ -381,126 +383,129 @@ yy34:
goto yy29;
yy35:
yych = *++p;
- if (yych == 'i') goto yy43;
+ if (yych == 'i') goto yy45;
goto yy14;
yy36:
yych = *++p;
- if (yych == 'f') goto yy44;
+ if (yych == 'f') goto yy46;
goto yy14;
yy37:
yych = *++p;
- if (yych == 'c') goto yy45;
+ if (yych == 'c') goto yy47;
goto yy14;
yy38:
yych = *++p;
- if (yych == 'o') goto yy46;
+ if (yych == 'o') goto yy48;
goto yy14;
yy39:
yych = *++p;
- if (yych == 'l') goto yy47;
+ if (yych == 'l') goto yy49;
goto yy14;
yy40:
yych = *++p;
- if (yych == 'b') goto yy48;
+ if (yych == 'b') goto yy50;
goto yy14;
yy41:
++p;
- { token = PIPE2; break; }
+ { token = PIPEAT; break; }
yy43:
- yych = *++p;
- if (yych == 'l') goto yy49;
- goto yy14;
-yy44:
- yych = *++p;
- if (yych == 'a') goto yy50;
- goto yy14;
+ ++p;
+ { token = PIPE2; break; }
yy45:
yych = *++p;
if (yych == 'l') goto yy51;
goto yy14;
yy46:
yych = *++p;
- if (yych == 'l') goto yy52;
+ if (yych == 'a') goto yy52;
goto yy14;
yy47:
yych = *++p;
- if (yych == 'e') goto yy54;
+ if (yych == 'l') goto yy53;
goto yy14;
yy48:
yych = *++p;
- if (yych == 'n') goto yy56;
+ if (yych == 'l') goto yy54;
goto yy14;
yy49:
yych = *++p;
- if (yych == 'd') goto yy57;
+ if (yych == 'e') goto yy56;
goto yy14;
yy50:
yych = *++p;
- if (yych == 'u') goto yy59;
+ if (yych == 'n') goto yy58;
goto yy14;
yy51:
yych = *++p;
- if (yych == 'u') goto yy60;
+ if (yych == 'd') goto yy59;
goto yy14;
yy52:
yych = *++p;
+ if (yych == 'u') goto yy61;
+ goto yy14;
+yy53:
+ yych = *++p;
+ if (yych == 'u') goto yy62;
+ goto yy14;
+yy54:
+ yych = *++p;
if (yybm[0+yych] & 64) {
goto yy13;
}
{ token = POOL; break; }
-yy54:
+yy56:
yych = *++p;
if (yybm[0+yych] & 64) {
goto yy13;
}
{ token = RULE; break; }
-yy56:
+yy58:
yych = *++p;
- if (yych == 'i') goto yy61;
+ if (yych == 'i') goto yy63;
goto yy14;
-yy57:
+yy59:
yych = *++p;
if (yybm[0+yych] & 64) {
goto yy13;
}
{ token = BUILD; break; }
-yy59:
- yych = *++p;
- if (yych == 'l') goto yy62;
- goto yy14;
-yy60:
- yych = *++p;
- if (yych == 'd') goto yy63;
- goto yy14;
yy61:
yych = *++p;
- if (yych == 'n') goto yy64;
+ if (yych == 'l') goto yy64;
goto yy14;
yy62:
yych = *++p;
- if (yych == 't') goto yy65;
+ if (yych == 'd') goto yy65;
goto yy14;
yy63:
yych = *++p;
- if (yych == 'e') goto yy67;
+ if (yych == 'n') goto yy66;
goto yy14;
yy64:
yych = *++p;
- if (yych == 'j') goto yy69;
+ if (yych == 't') goto yy67;
goto yy14;
yy65:
yych = *++p;
+ if (yych == 'e') goto yy69;
+ goto yy14;
+yy66:
+ yych = *++p;
+ if (yych == 'j') goto yy71;
+ goto yy14;
+yy67:
+ yych = *++p;
if (yybm[0+yych] & 64) {
goto yy13;
}
{ token = DEFAULT; break; }
-yy67:
+yy69:
yych = *++p;
if (yybm[0+yych] & 64) {
goto yy13;
}
{ token = INCLUDE; break; }
-yy69:
+yy71:
yych = *++p;
if (yych != 'a') goto yy14;
yych = *++p;
@@ -565,68 +570,68 @@ bool Lexer::PeekIndent() {
};
yych = *p;
if (yybm[0+yych] & 64) {
- goto yy79;
+ goto yy81;
}
if (yych <= '\f') {
- if (yych == '\n') goto yy76;
+ if (yych == '\n') goto yy78;
} else {
- if (yych <= '\r') goto yy78;
- if (yych == '#') goto yy82;
+ if (yych <= '\r') goto yy80;
+ if (yych == '#') goto yy84;
}
++p;
-yy75:
+yy77:
{ last_token_ = ofs_ = start; return false; }
-yy76:
+yy78:
++p;
{ last_token_ = ofs_ = start; return false; }
-yy78:
+yy80:
yych = *++p;
- if (yych == '\n') goto yy76;
- goto yy75;
-yy79:
+ if (yych == '\n') goto yy78;
+ goto yy77;
+yy81:
yyaccept = 0;
yych = *(q = ++p);
if (yybm[0+yych] & 64) {
- goto yy79;
+ goto yy81;
}
if (yych <= '\f') {
- if (yych == '\n') goto yy76;
+ if (yych == '\n') goto yy78;
} else {
- if (yych <= '\r') goto yy83;
- if (yych == '#') goto yy85;
+ if (yych <= '\r') goto yy85;
+ if (yych == '#') goto yy87;
}
-yy81:
+yy83:
{ last_token_ = start; ofs_ = p; return true; }
-yy82:
+yy84:
yyaccept = 1;
yych = *(q = ++p);
- if (yych <= 0x00) goto yy75;
- goto yy86;
-yy83:
+ if (yych <= 0x00) goto yy77;
+ goto yy88;
+yy85:
yych = *++p;
- if (yych == '\n') goto yy76;
-yy84:
+ if (yych == '\n') goto yy78;
+yy86:
p = q;
if (yyaccept == 0) {
- goto yy81;
+ goto yy83;
} else {
- goto yy75;
+ goto yy77;
}
-yy85:
+yy87:
yych = *++p;
-yy86:
+yy88:
if (yybm[0+yych] & 128) {
- goto yy85;
+ goto yy87;
}
- if (yych <= 0x00) goto yy84;
- if (yych >= '\v') goto yy89;
-yy87:
+ if (yych <= 0x00) goto yy86;
+ if (yych >= '\v') goto yy91;
+yy89:
++p;
{ continue; }
-yy89:
+yy91:
yych = *++p;
- if (yych == '\n') goto yy87;
- goto yy84;
+ if (yych == '\n') goto yy89;
+ goto yy86;
}
}
@@ -684,37 +689,37 @@ void Lexer::EatWhitespace() {
};
yych = *p;
if (yybm[0+yych] & 128) {
- goto yy96;
+ goto yy98;
}
- if (yych <= 0x00) goto yy92;
- if (yych == '$') goto yy99;
- goto yy94;
-yy92:
- ++p;
- { break; }
+ if (yych <= 0x00) goto yy94;
+ if (yych == '$') goto yy101;
+ goto yy96;
yy94:
++p;
-yy95:
{ break; }
yy96:
+ ++p;
+yy97:
+ { break; }
+yy98:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy96;
+ goto yy98;
}
{ continue; }
-yy99:
+yy101:
yych = *(q = ++p);
- if (yych == '\n') goto yy100;
- if (yych == '\r') goto yy102;
- goto yy95;
-yy100:
+ if (yych == '\n') goto yy102;
+ if (yych == '\r') goto yy104;
+ goto yy97;
+yy102:
++p;
{ continue; }
-yy102:
+yy104:
yych = *++p;
- if (yych == '\n') goto yy100;
+ if (yych == '\n') goto yy102;
p = q;
- goto yy95;
+ goto yy97;
}
}
@@ -764,17 +769,17 @@ bool Lexer::ReadIdent(StringPiece* out) {
};
yych = *p;
if (yybm[0+yych] & 128) {
- goto yy108;
+ goto yy110;
}
++p;
{
last_token_ = start;
return false;
}
-yy108:
+yy110:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy108;
+ goto yy110;
}
{
*out = StringPiece(start, p - start);
@@ -835,159 +840,159 @@ bool Lexer::ReadBindingValue(StringPiece* out, string* err) {
};
yych = *p;
if (yybm[0+yych] & 64) {
- goto yy115;
+ goto yy117;
}
- if (yych <= 0x00) goto yy113;
- if (yych <= '\n') goto yy118;
- if (yych <= '\r') goto yy120;
- goto yy122;
-yy113:
+ if (yych <= 0x00) goto yy115;
+ if (yych <= '\n') goto yy120;
+ if (yych <= '\r') goto yy122;
+ goto yy124;
+yy115:
++p;
{
last_token_ = start;
return UnexpectedNulError(start, err);
}
-yy115:
+yy117:
yyaccept = 0;
yych = *(q = ++p);
if (yybm[0+yych] & 64) {
- goto yy115;
+ goto yy117;
}
- if (yych >= 0x0E) goto yy123;
-yy117:
+ if (yych >= 0x0E) goto yy125;
+yy119:
{
continue;
}
-yy118:
+yy120:
++p;
{
break;
}
-yy120:
+yy122:
yych = *++p;
- if (yych == '\n') goto yy118;
+ if (yych == '\n') goto yy120;
{
last_token_ = start;
return Error(DescribeLastError(), err);
}
-yy122:
+yy124:
yych = *++p;
if (yych <= '-') {
if (yych <= 0x1F) {
if (yych <= '\n') {
- if (yych <= '\t') goto yy125;
- goto yy115;
+ if (yych <= '\t') goto yy127;
+ goto yy117;
} else {
- if (yych == '\r') goto yy127;
- goto yy125;
+ if (yych == '\r') goto yy129;
+ goto yy127;
}
} else {
if (yych <= '#') {
- if (yych <= ' ') goto yy115;
- goto yy125;
+ if (yych <= ' ') goto yy117;
+ goto yy127;
} else {
- if (yych <= '$') goto yy115;
- if (yych <= ',') goto yy125;
- goto yy115;
+ if (yych <= '$') goto yy117;
+ if (yych <= ',') goto yy127;
+ goto yy117;
}
}
} else {
if (yych <= '^') {
if (yych <= ':') {
- if (yych <= '/') goto yy125;
- goto yy115;
+ if (yych <= '/') goto yy127;
+ goto yy117;
} else {
- if (yych <= '@') goto yy125;
- if (yych <= 'Z') goto yy115;
- goto yy125;
+ if (yych <= '@') goto yy127;
+ if (yych <= 'Z') goto yy117;
+ goto yy127;
}
} else {
if (yych <= '`') {
- if (yych <= '_') goto yy115;
- goto yy125;
+ if (yych <= '_') goto yy117;
+ goto yy127;
} else {
- if (yych <= 'z') goto yy115;
- if (yych <= '{') goto yy128;
- goto yy125;
+ if (yych <= 'z') goto yy117;
+ if (yych <= '{') goto yy130;
+ goto yy127;
}
}
}
-yy123:
+yy125:
yych = *++p;
if (yych <= '-') {
if (yych <= 0x1F) {
if (yych <= '\n') {
- if (yych >= '\n') goto yy115;
+ if (yych >= '\n') goto yy117;
} else {
- if (yych == '\r') goto yy129;
+ if (yych == '\r') goto yy131;
}
} else {
if (yych <= '#') {
- if (yych <= ' ') goto yy115;
+ if (yych <= ' ') goto yy117;
} else {
- if (yych <= '$') goto yy115;
- if (yych >= '-') goto yy115;
+ if (yych <= '$') goto yy117;
+ if (yych >= '-') goto yy117;
}
}
} else {
if (yych <= '^') {
if (yych <= ':') {
- if (yych >= '0') goto yy115;
+ if (yych >= '0') goto yy117;
} else {
- if (yych <= '@') goto yy124;
- if (yych <= 'Z') goto yy115;
+ if (yych <= '@') goto yy126;
+ if (yych <= 'Z') goto yy117;
}
} else {
if (yych <= '`') {
- if (yych <= '_') goto yy115;
+ if (yych <= '_') goto yy117;
} else {
- if (yych <= 'z') goto yy115;
- if (yych <= '{') goto yy130;
+ if (yych <= 'z') goto yy117;
+ if (yych <= '{') goto yy132;
}
}
}
-yy124:
+yy126:
p = q;
if (yyaccept == 0) {
- goto yy117;
+ goto yy119;
} else {
- goto yy126;
+ goto yy128;
}
-yy125:
+yy127:
++p;
-yy126:
+yy128:
{
last_token_ = start;
return Error("bad $-escape (literal $ must be written as $$)", err);
}
-yy127:
+yy129:
yych = *++p;
- if (yych == '\n') goto yy115;
- goto yy126;
-yy128:
+ if (yych == '\n') goto yy117;
+ goto yy128;
+yy130:
yyaccept = 1;
yych = *(q = ++p);
if (yybm[0+yych] & 128) {
- goto yy131;
+ goto yy133;
}
- goto yy126;
-yy129:
+ goto yy128;
+yy131:
yych = *++p;
- if (yych == '\n') goto yy115;
- goto yy124;
-yy130:
+ if (yych == '\n') goto yy117;
+ goto yy126;
+yy132:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy131;
+ goto yy133;
}
- goto yy124;
-yy131:
+ goto yy126;
+yy133:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy131;
+ goto yy133;
}
- if (yych == '}') goto yy115;
- goto yy124;
+ if (yych == '}') goto yy117;
+ goto yy126;
}
}
@@ -1051,448 +1056,448 @@ StringPiece Lexer::PeekCanonicalPath() {
yych = *p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy135;
- if (yych != '\n') goto yy137;
+ if (yych <= 0x00) goto yy137;
+ if (yych != '\n') goto yy139;
} else {
- if (yych <= '\r') goto yy135;
- if (yych != ' ') goto yy137;
+ if (yych <= '\r') goto yy137;
+ if (yych != ' ') goto yy139;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy135;
- if (yych <= '-') goto yy137;
- if (yych <= '.') goto yy138;
- goto yy139;
+ if (yych <= '$') goto yy137;
+ if (yych <= '-') goto yy139;
+ if (yych <= '.') goto yy140;
+ goto yy141;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy137;
+ if (yych <= '9') goto yy139;
} else {
- if (yych != '|') goto yy137;
+ if (yych != '|') goto yy139;
}
}
}
-yy135:
+yy137:
++p;
-yy136:
+yy138:
{ break; }
-yy137:
+yy139:
yych = *(q = ++p);
- if (yych <= 0x00) goto yy136;
- if (yych == '$') goto yy136;
- goto yy141;
-yy138:
+ if (yych <= 0x00) goto yy138;
+ if (yych == '$') goto yy138;
+ goto yy143;
+yy140:
yych = *(q = ++p);
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy136;
- if (yych == '\n') goto yy136;
- goto yy140;
+ if (yych <= 0x00) goto yy138;
+ if (yych == '\n') goto yy138;
+ goto yy142;
} else {
- if (yych <= '\r') goto yy136;
- if (yych == ' ') goto yy136;
- goto yy140;
+ if (yych <= '\r') goto yy138;
+ if (yych == ' ') goto yy138;
+ goto yy142;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy136;
- if (yych <= '-') goto yy140;
- if (yych <= '.') goto yy147;
- goto yy148;
+ if (yych <= '$') goto yy138;
+ if (yych <= '-') goto yy142;
+ if (yych <= '.') goto yy149;
+ goto yy150;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy140;
- goto yy136;
+ if (yych <= '9') goto yy142;
+ goto yy138;
} else {
- if (yych == '|') goto yy136;
- goto yy140;
+ if (yych == '|') goto yy138;
+ goto yy142;
}
}
}
-yy139:
+yy141:
yych = *(q = ++p);
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy136;
- if (yych == '\n') goto yy136;
+ if (yych <= 0x00) goto yy138;
+ if (yych == '\n') goto yy138;
} else {
- if (yych <= '\r') goto yy136;
- if (yych == ' ') goto yy136;
+ if (yych <= '\r') goto yy138;
+ if (yych == ' ') goto yy138;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy136;
- if (yych <= '-') goto yy140;
- if (yych <= '.') goto yy149;
- goto yy136;
+ if (yych <= '$') goto yy138;
+ if (yych <= '-') goto yy142;
+ if (yych <= '.') goto yy151;
+ goto yy138;
} else {
if (yych <= ':') {
- if (yych >= ':') goto yy136;
+ if (yych >= ':') goto yy138;
} else {
- if (yych == '|') goto yy136;
+ if (yych == '|') goto yy138;
}
}
}
-yy140:
+yy142:
yych = *++p;
-yy141:
+yy143:
if (yybm[0+yych] & 128) {
- goto yy140;
+ goto yy142;
}
if (yych <= '\r') {
- if (yych <= 0x00) goto yy142;
+ if (yych <= 0x00) goto yy144;
if (yych <= '\n') {
r = p;
- goto yy143;
+ goto yy145;
}
r = p;
- goto yy145;
+ goto yy147;
} else {
if (yych <= ' ') {
r = p;
- goto yy143;
+ goto yy145;
}
- if (yych <= '$') goto yy142;
- if (yych <= '/') goto yy146;
+ if (yych <= '$') goto yy144;
+ if (yych <= '/') goto yy148;
r = p;
- goto yy143;
+ goto yy145;
}
-yy142:
+yy144:
p = q;
- goto yy136;
-yy143:
+ goto yy138;
+yy145:
++p;
p = r;
{ return finish(ofs_, p); }
-yy145:
+yy147:
yych = *++p;
- if (yych == '\n') goto yy143;
- goto yy142;
-yy146:
+ if (yych == '\n') goto yy145;
+ goto yy144;
+yy148:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy140;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy142;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy140;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy142;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy140;
- if (yych <= '.') goto yy149;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy142;
+ if (yych <= '.') goto yy151;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy140;
- goto yy142;
+ if (yych <= '9') goto yy142;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy140;
+ if (yych == '|') goto yy144;
+ goto yy142;
}
}
}
-yy147:
+yy149:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy140;
+ goto yy142;
}
- if (yych <= '$') goto yy142;
- if (yych <= '/') goto yy150;
- goto yy142;
-yy148:
+ if (yych <= '$') goto yy144;
+ if (yych <= '/') goto yy152;
+ goto yy144;
+yy150:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy151;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy153;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy151;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy153;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy151;
- if (yych <= '.') goto yy153;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy153;
+ if (yych <= '.') goto yy155;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy151;
- goto yy142;
+ if (yych <= '9') goto yy153;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy151;
+ if (yych == '|') goto yy144;
+ goto yy153;
}
}
}
-yy149:
+yy151:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy140;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy142;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy140;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy142;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy140;
- if (yych <= '.') goto yy154;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy142;
+ if (yych <= '.') goto yy156;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy140;
- goto yy142;
+ if (yych <= '9') goto yy142;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy140;
+ if (yych == '|') goto yy144;
+ goto yy142;
}
}
}
-yy150:
+yy152:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy140;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy142;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy140;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy142;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy140;
- if (yych <= '.') goto yy155;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy142;
+ if (yych <= '.') goto yy157;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy140;
- goto yy142;
+ if (yych <= '9') goto yy142;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy140;
+ if (yych == '|') goto yy144;
+ goto yy142;
}
}
}
-yy151:
+yy153:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
+ if (yych <= 0x00) goto yy144;
if (yych == '\n') {
r = p;
- goto yy156;
+ goto yy158;
}
- goto yy151;
+ goto yy153;
} else {
if (yych <= '\r') {
r = p;
- goto yy158;
+ goto yy160;
}
if (yych == ' ') {
r = p;
- goto yy156;
+ goto yy158;
}
- goto yy151;
+ goto yy153;
}
} else {
if (yych <= '9') {
- if (yych <= '$') goto yy142;
- if (yych == '/') goto yy159;
- goto yy151;
+ if (yych <= '$') goto yy144;
+ if (yych == '/') goto yy161;
+ goto yy153;
} else {
if (yych <= ':') {
r = p;
- goto yy156;
+ goto yy158;
}
if (yych == '|') {
r = p;
- goto yy156;
+ goto yy158;
}
- goto yy151;
+ goto yy153;
}
}
-yy153:
+yy155:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy151;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy153;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy151;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy153;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy151;
- if (yych <= '.') goto yy160;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy153;
+ if (yych <= '.') goto yy162;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy151;
- goto yy142;
+ if (yych <= '9') goto yy153;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy151;
+ if (yych == '|') goto yy144;
+ goto yy153;
}
}
}
-yy154:
+yy156:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy140;
+ goto yy142;
}
- goto yy142;
-yy155:
+ goto yy144;
+yy157:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy140;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy142;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy140;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy142;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy140;
- if (yych <= '.') goto yy147;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy142;
+ if (yych <= '.') goto yy149;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy140;
- goto yy142;
+ if (yych <= '9') goto yy142;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy140;
+ if (yych == '|') goto yy144;
+ goto yy142;
}
}
}
-yy156:
+yy158:
++p;
p = r;
{ return finish(ofs_ + 2, p); }
-yy158:
+yy160:
yych = *++p;
- if (yych == '\n') goto yy156;
- goto yy142;
-yy159:
+ if (yych == '\n') goto yy158;
+ goto yy144;
+yy161:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy151;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy153;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy151;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy153;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy151;
- if (yych <= '.') goto yy161;
- goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy153;
+ if (yych <= '.') goto yy163;
+ goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy151;
- goto yy142;
+ if (yych <= '9') goto yy153;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy151;
+ if (yych == '|') goto yy144;
+ goto yy153;
}
}
}
-yy160:
+yy162:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy151;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy153;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy151;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy153;
}
} else {
if (yych <= '9') {
- if (yych <= '$') goto yy142;
- if (yych == '/') goto yy148;
- goto yy151;
+ if (yych <= '$') goto yy144;
+ if (yych == '/') goto yy150;
+ goto yy153;
} else {
- if (yych <= ':') goto yy142;
- if (yych == '|') goto yy142;
- goto yy151;
+ if (yych <= ':') goto yy144;
+ if (yych == '|') goto yy144;
+ goto yy153;
}
}
-yy161:
+yy163:
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy151;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy153;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy151;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy153;
}
} else {
if (yych <= '/') {
- if (yych <= '$') goto yy142;
- if (yych <= '-') goto yy151;
- if (yych >= '/') goto yy142;
+ if (yych <= '$') goto yy144;
+ if (yych <= '-') goto yy153;
+ if (yych >= '/') goto yy144;
} else {
if (yych <= ':') {
- if (yych <= '9') goto yy151;
- goto yy142;
+ if (yych <= '9') goto yy153;
+ goto yy144;
} else {
- if (yych == '|') goto yy142;
- goto yy151;
+ if (yych == '|') goto yy144;
+ goto yy153;
}
}
}
yych = *++p;
if (yych <= '#') {
if (yych <= '\f') {
- if (yych <= 0x00) goto yy142;
- if (yych == '\n') goto yy142;
- goto yy151;
+ if (yych <= 0x00) goto yy144;
+ if (yych == '\n') goto yy144;
+ goto yy153;
} else {
- if (yych <= '\r') goto yy142;
- if (yych == ' ') goto yy142;
- goto yy151;
+ if (yych <= '\r') goto yy144;
+ if (yych == ' ') goto yy144;
+ goto yy153;
}
} else {
if (yych <= '9') {
- if (yych <= '$') goto yy142;
- if (yych == '/') goto yy142;
- goto yy151;
+ if (yych <= '$') goto yy144;
+ if (yych == '/') goto yy144;
+ goto yy153;
} else {
- if (yych <= ':') goto yy142;
- if (yych == '|') goto yy142;
- goto yy151;
+ if (yych <= ':') goto yy144;
+ if (yych == '|') goto yy144;
+ goto yy153;
}
}
}
@@ -1548,176 +1553,176 @@ bool Lexer::ReadPath(LexedPath* out, std::string* err) {
};
yych = *p;
if (yybm[0+yych] & 32) {
- goto yy167;
+ goto yy169;
}
if (yych <= '\r') {
- if (yych <= 0x00) goto yy165;
- if (yych <= '\n') goto yy170;
- goto yy172;
+ if (yych <= 0x00) goto yy167;
+ if (yych <= '\n') goto yy172;
+ goto yy174;
} else {
- if (yych <= ' ') goto yy170;
- if (yych <= '$') goto yy174;
- goto yy170;
+ if (yych <= ' ') goto yy172;
+ if (yych <= '$') goto yy176;
+ goto yy172;
}
-yy165:
+yy167:
++p;
{
last_token_ = start;
return UnexpectedNulError(start, err);
}
-yy167:
+yy169:
yyaccept = 0;
yych = *(q = ++p);
if (yybm[0+yych] & 32) {
- goto yy167;
+ goto yy169;
}
- if (yych <= ' ') goto yy169;
- if (yych <= '$') goto yy175;
-yy169:
+ if (yych <= ' ') goto yy171;
+ if (yych <= '$') goto yy177;
+yy171:
{
continue;
}
-yy170:
+yy172:
++p;
{
p = start;
break;
}
-yy172:
+yy174:
yych = *++p;
- if (yych == '\n') goto yy170;
+ if (yych == '\n') goto yy172;
{
last_token_ = start;
return Error(DescribeLastError(), err);
}
-yy174:
+yy176:
yych = *++p;
if (yych <= '-') {
if (yych <= 0x1F) {
if (yych <= '\n') {
- if (yych <= '\t') goto yy177;
- goto yy179;
+ if (yych <= '\t') goto yy179;
+ goto yy181;
} else {
- if (yych == '\r') goto yy181;
- goto yy177;
+ if (yych == '\r') goto yy183;
+ goto yy179;
}
} else {
if (yych <= '#') {
- if (yych <= ' ') goto yy167;
- goto yy177;
+ if (yych <= ' ') goto yy169;
+ goto yy179;
} else {
- if (yych <= '$') goto yy167;
- if (yych <= ',') goto yy177;
- goto yy167;
+ if (yych <= '$') goto yy169;
+ if (yych <= ',') goto yy179;
+ goto yy169;
}
}
} else {
if (yych <= '^') {
if (yych <= ':') {
- if (yych <= '/') goto yy177;
- goto yy167;
+ if (yych <= '/') goto yy179;
+ goto yy169;
} else {
- if (yych <= '@') goto yy177;
- if (yych <= 'Z') goto yy167;
- goto yy177;
+ if (yych <= '@') goto yy179;
+ if (yych <= 'Z') goto yy169;
+ goto yy179;
}
} else {
if (yych <= '`') {
- if (yych <= '_') goto yy167;
- goto yy177;
+ if (yych <= '_') goto yy169;
+ goto yy179;
} else {
- if (yych <= 'z') goto yy167;
- if (yych <= '{') goto yy182;
- goto yy177;
+ if (yych <= 'z') goto yy169;
+ if (yych <= '{') goto yy184;
+ goto yy179;
}
}
}
-yy175:
+yy177:
yych = *++p;
if (yych <= '-') {
if (yych <= 0x1F) {
if (yych <= '\n') {
- if (yych >= '\n') goto yy179;
+ if (yych >= '\n') goto yy181;
} else {
- if (yych == '\r') goto yy183;
+ if (yych == '\r') goto yy185;
}
} else {
if (yych <= '#') {
- if (yych <= ' ') goto yy167;
+ if (yych <= ' ') goto yy169;
} else {
- if (yych <= '$') goto yy167;
- if (yych >= '-') goto yy167;
+ if (yych <= '$') goto yy169;
+ if (yych >= '-') goto yy169;
}
}
} else {
if (yych <= '^') {
if (yych <= ':') {
- if (yych >= '0') goto yy167;
+ if (yych >= '0') goto yy169;
} else {
- if (yych <= '@') goto yy176;
- if (yych <= 'Z') goto yy167;
+ if (yych <= '@') goto yy178;
+ if (yych <= 'Z') goto yy169;
}
} else {
if (yych <= '`') {
- if (yych <= '_') goto yy167;
+ if (yych <= '_') goto yy169;
} else {
- if (yych <= 'z') goto yy167;
- if (yych <= '{') goto yy184;
+ if (yych <= 'z') goto yy169;
+ if (yych <= '{') goto yy186;
}
}
}
-yy176:
+yy178:
p = q;
if (yyaccept == 0) {
- goto yy169;
+ goto yy171;
} else {
- goto yy178;
+ goto yy180;
}
-yy177:
+yy179:
++p;
-yy178:
+yy180:
{
last_token_ = start;
return Error("bad $-escape (literal $ must be written as $$)", err);
}
-yy179:
+yy181:
yyaccept = 0;
yych = *(q = ++p);
if (yybm[0+yych] & 32) {
- goto yy167;
+ goto yy169;
}
- if (yych <= '\r') goto yy169;
- if (yych <= ' ') goto yy179;
- if (yych <= '$') goto yy175;
- goto yy169;
-yy181:
+ if (yych <= '\r') goto yy171;
+ if (yych <= ' ') goto yy181;
+ if (yych <= '$') goto yy177;
+ goto yy171;
+yy183:
yych = *++p;
- if (yych == '\n') goto yy179;
- goto yy178;
-yy182:
+ if (yych == '\n') goto yy181;
+ goto yy180;
+yy184:
yyaccept = 1;
yych = *(q = ++p);
if (yybm[0+yych] & 128) {
- goto yy185;
+ goto yy187;
}
- goto yy178;
-yy183:
+ goto yy180;
+yy185:
yych = *++p;
- if (yych == '\n') goto yy179;
- goto yy176;
-yy184:
+ if (yych == '\n') goto yy181;
+ goto yy178;
+yy186:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy185;
+ goto yy187;
}
- goto yy176;
-yy185:
+ goto yy178;
+yy187:
yych = *++p;
if (yybm[0+yych] & 128) {
- goto yy185;
+ goto yy187;
}
- if (yych == '}') goto yy167;
- goto yy176;
+ if (yych == '}') goto yy169;
+ goto yy178;
}
}
@@ -1783,85 +1788,85 @@ static inline void EvaluateBinding(std::string* out_append, StringPiece value,
};
yych = *p;
if (yybm[0+yych] & 16) {
- goto yy191;
+ goto yy193;
}
- if (yych <= 0x00) goto yy189;
- if (yych <= '\n') goto yy194;
- if (yych <= '\r') goto yy196;
- goto yy197;
-yy189:
+ if (yych <= 0x00) goto yy191;
+ if (yych <= '\n') goto yy196;
+ if (yych <= '\r') goto yy198;
+ goto yy199;
+yy191:
++p;
-yy190:
+yy192:
{ assert(false && "bad input in EvaluateBinding"); abort(); }
-yy191:
+yy193:
yych = *++p;
if (yybm[0+yych] & 16) {
- goto yy191;
+ goto yy193;
}
{ out_append->append(start, p - start); continue; }
-yy194:
+yy196:
++p;
{ break; }
-yy196:
+yy198:
yych = *++p;
- if (yych == '\n') goto yy194;
- goto yy190;
-yy197:
+ if (yych == '\n') goto yy196;
+ goto yy192;
+yy199:
yych = *(q = ++p);
if (yybm[0+yych] & 64) {
- goto yy205;
+ goto yy207;
}
if (yych <= ' ') {
if (yych <= '\f') {
- if (yych != '\n') goto yy190;
+ if (yych != '\n') goto yy192;
} else {
- if (yych <= '\r') goto yy201;
- if (yych <= 0x1F) goto yy190;
- goto yy203;
+ if (yych <= '\r') goto yy203;
+ if (yych <= 0x1F) goto yy192;
+ goto yy205;
}
} else {
if (yych <= '/') {
- if (yych == '$') goto yy203;
- goto yy190;
+ if (yych == '$') goto yy205;
+ goto yy192;
} else {
- if (yych <= ':') goto yy203;
- if (yych <= '`') goto yy190;
- if (yych <= '{') goto yy208;
- goto yy190;
+ if (yych <= ':') goto yy205;
+ if (yych <= '`') goto yy192;
+ if (yych <= '{') goto yy210;
+ goto yy192;
}
}
-yy198:
+yy200:
yych = *++p;
if (yybm[0+yych] & 32) {
- goto yy198;
+ goto yy200;
}
{ continue; }
-yy201:
+yy203:
yych = *++p;
- if (yych == '\n') goto yy198;
-yy202:
+ if (yych == '\n') goto yy200;
+yy204:
p = q;
- goto yy190;
-yy203:
+ goto yy192;
+yy205:
++p;
{ out_append->push_back(start[1]); continue; }
-yy205:
+yy207:
yych = *++p;
if (yybm[0+yych] & 64) {
- goto yy205;
+ goto yy207;
}
{ expand(start + 1, p); continue; }
-yy208:
+yy210:
yych = *++p;
- if (yych == '}') goto yy202;
- goto yy210;
-yy209:
+ if (yych == '}') goto yy204;
+ goto yy212;
+yy211:
yych = *++p;
-yy210:
+yy212:
if (yybm[0+yych] & 128) {
- goto yy209;
+ goto yy211;
}
- if (yych != '}') goto yy202;
+ if (yych != '}') goto yy204;
++p;
{ expand(start + 2, p - 1); continue; }
}
@@ -1954,90 +1959,90 @@ static inline void EvaluatePath(std::string* out_append, const LexedPath& path,
};
yych = *p;
if (yybm[0+yych] & 16) {
- goto yy217;
+ goto yy219;
}
if (yych <= '\r') {
- if (yych <= 0x00) goto yy215;
- if (yych <= '\n') goto yy220;
- goto yy222;
+ if (yych <= 0x00) goto yy217;
+ if (yych <= '\n') goto yy222;
+ goto yy224;
} else {
- if (yych <= ' ') goto yy220;
- if (yych <= '$') goto yy223;
- goto yy220;
+ if (yych <= ' ') goto yy222;
+ if (yych <= '$') goto yy225;
+ goto yy222;
}
-yy215:
+yy217:
++p;
-yy216:
+yy218:
{ assert(false && "bad input in EvaluatePath"); abort(); }
-yy217:
+yy219:
yych = *++p;
if (yybm[0+yych] & 16) {
- goto yy217;
+ goto yy219;
}
{ out_append->append(start, p - start); continue; }
-yy220:
+yy222:
++p;
{ p = start; break; }
-yy222:
+yy224:
yych = *++p;
- if (yych == '\n') goto yy220;
- goto yy216;
-yy223:
+ if (yych == '\n') goto yy222;
+ goto yy218;
+yy225:
yych = *(q = ++p);
if (yybm[0+yych] & 64) {
- goto yy231;
+ goto yy233;
}
if (yych <= ' ') {
if (yych <= '\f') {
- if (yych != '\n') goto yy216;
+ if (yych != '\n') goto yy218;
} else {
- if (yych <= '\r') goto yy227;
- if (yych <= 0x1F) goto yy216;
- goto yy229;
+ if (yych <= '\r') goto yy229;
+ if (yych <= 0x1F) goto yy218;
+ goto yy231;
}
} else {
if (yych <= '/') {
- if (yych == '$') goto yy229;
- goto yy216;
+ if (yych == '$') goto yy231;
+ goto yy218;
} else {
- if (yych <= ':') goto yy229;
- if (yych <= '`') goto yy216;
- if (yych <= '{') goto yy234;
- goto yy216;
+ if (yych <= ':') goto yy231;
+ if (yych <= '`') goto yy218;
+ if (yych <= '{') goto yy236;
+ goto yy218;
}
}
-yy224:
+yy226:
yych = *++p;
if (yybm[0+yych] & 32) {
- goto yy224;
+ goto yy226;
}
{ continue; }
-yy227:
+yy229:
yych = *++p;
- if (yych == '\n') goto yy224;
-yy228:
+ if (yych == '\n') goto yy226;
+yy230:
p = q;
- goto yy216;
-yy229:
+ goto yy218;
+yy231:
++p;
{ out_append->push_back(start[1]); continue; }
-yy231:
+yy233:
yych = *++p;
if (yybm[0+yych] & 64) {
- goto yy231;
+ goto yy233;
}
{ expand(start + 1, p); continue; }
-yy234:
+yy236:
yych = *++p;
- if (yych == '}') goto yy228;
- goto yy236;
-yy235:
+ if (yych == '}') goto yy230;
+ goto yy238;
+yy237:
yych = *++p;
-yy236:
+yy238:
if (yybm[0+yych] & 128) {
- goto yy235;
+ goto yy237;
}
- if (yych != '}') goto yy228;
+ if (yych != '}') goto yy230;
++p;
{ expand(start + 2, p - 1); continue; }
}
diff --git a/src/lexer.h b/src/lexer.h
index 08bc782..42581a7 100644
--- a/src/lexer.h
+++ b/src/lexer.h
@@ -68,6 +68,7 @@ struct Lexer {
NEWLINE,
PIPE,
PIPE2,
+ PIPEAT,
POOL,
RULE,
SUBNINJA,
diff --git a/src/lexer.in.cc b/src/lexer.in.cc
index 17a825a..63fa937 100644
--- a/src/lexer.in.cc
+++ b/src/lexer.in.cc
@@ -138,6 +138,7 @@ const char* Lexer::TokenName(Token t) {
case NEWLINE: return "newline";
case PIPE2: return "'||'";
case PIPE: return "'|'";
+ case PIPEAT: return "'|@'";
case POOL: return "'pool'";
case RULE: return "'rule'";
case SUBNINJA: return "'subninja'";
@@ -200,6 +201,7 @@ Lexer::Token Lexer::ReadToken() {
"default" { token = DEFAULT; break; }
"=" { token = EQUALS; break; }
":" { token = COLON; break; }
+ "|@" { token = PIPEAT; break; }
"||" { token = PIPE2; break; }
"|" { token = PIPE; break; }
"include" { token = INCLUDE; break; }
diff --git a/src/manifest_chunk_parser.cc b/src/manifest_chunk_parser.cc
index 0feb499..ff36752 100644
--- a/src/manifest_chunk_parser.cc
+++ b/src/manifest_chunk_parser.cc
@@ -255,7 +255,7 @@ bool ChunkParser::ParseRule() {
bool ChunkParser::ParseEdge() {
Edge* edge = new Edge();
- auto parse_path_list = [this, edge](bool is_output, int& count) -> bool {
+ auto parse_path_list = [this, edge](Edge::DeferredPathList::Type type, int& count) -> bool {
const char* start_pos = lexer_.GetPos();
while (true) {
LexedPath path;
@@ -268,7 +268,7 @@ bool ChunkParser::ParseEdge() {
// (wasting memory), store just enough information to parse the path
// lists again once bindings are ready.
edge->parse_state_.deferred_path_lists.push_back({
- start_pos, is_output, count,
+ start_pos, type, count,
});
return true;
}
@@ -276,12 +276,14 @@ bool ChunkParser::ParseEdge() {
}
};
- if (!parse_path_list(true, edge->explicit_outs_))
+ if (!parse_path_list(Edge::DeferredPathList::OUTPUT,
+ edge->explicit_outs_))
return false;
// Add all implicit outs, counting how many as we go.
if (lexer_.PeekToken(Lexer::PIPE)) {
- if (!parse_path_list(true, edge->implicit_outs_))
+ if (!parse_path_list(Edge::DeferredPathList::OUTPUT,
+ edge->implicit_outs_))
return false;
}
@@ -297,21 +299,32 @@ bool ChunkParser::ParseEdge() {
edge->parse_state_.rule_name = rule_name;
edge->parse_state_.rule_name_diag_pos = lexer_.GetLastTokenOffset();
- if (!parse_path_list(false, edge->explicit_deps_))
+ if (!parse_path_list(Edge::DeferredPathList::INPUT,
+ edge->explicit_deps_))
return false;
// Add all implicit deps, counting how many as we go.
if (lexer_.PeekToken(Lexer::PIPE)) {
- if (!parse_path_list(false, edge->implicit_deps_))
+ if (!parse_path_list(Edge::DeferredPathList::INPUT,
+ edge->implicit_deps_))
return false;
}
// Add all order-only deps, counting how many as we go.
if (lexer_.PeekToken(Lexer::PIPE2)) {
- if (!parse_path_list(false, edge->order_only_deps_))
+ if (!parse_path_list(Edge::DeferredPathList::INPUT,
+ edge->order_only_deps_))
return false;
}
+ // Add all validation deps, counting how many as we go.
+ if (lexer_.PeekToken(Lexer::PIPEAT)) {
+ if (!parse_path_list(Edge::DeferredPathList::VALIDATION,
+ edge->validation_deps_))
+ return false;
+ }
+
+
if (!ExpectToken(Lexer::NEWLINE))
return false;
diff --git a/src/manifest_parser.cc b/src/manifest_parser.cc
index 1f9d980..1e4246c 100644
--- a/src/manifest_parser.cc
+++ b/src/manifest_parser.cc
@@ -352,6 +352,7 @@ bool ManifestLoader::AddEdgeToGraph(Edge* edge, const LoadedFile& file,
edge->outputs_.reserve(edge->explicit_outs_ + edge->implicit_outs_);
edge->inputs_.reserve(edge->explicit_deps_ + edge->implicit_deps_ +
edge->order_only_deps_);
+ edge->validations_.reserve(edge->validation_deps_);
// Add the input and output nodes. We already lexed them in the first pass,
// but we couldn't add them because scope bindings weren't available. To save
@@ -360,8 +361,10 @@ bool ManifestLoader::AddEdgeToGraph(Edge* edge, const LoadedFile& file,
Lexer lexer(file.filename(), file.content(), file.content().data());
for (const Edge::DeferredPathList& path_list :
edge->parse_state_.deferred_path_lists) {
- std::vector<Node*>* vec = path_list.is_output ?
- &edge->outputs_ : &edge->inputs_;
+ std::vector<Node*>* vec =
+ path_list.type == Edge::DeferredPathList::INPUT ? &edge->inputs_ :
+ path_list.type == Edge::DeferredPathList::OUTPUT ? &edge->outputs_ :
+ &edge->validations_;
lexer.ResetPos(path_list.lexer_pos);
for (int i = 0; i < path_list.count; ++i) {
if (!AddPathToEdge(state_, *edge, vec, file, lexer, err))
@@ -516,6 +519,9 @@ bool ManifestLoader::FinishLoading(const std::vector<Clump*>& clumps,
for (Node* input : edge->inputs_) {
input->AddOutEdge(edge);
}
+ for (Node* validation : edge->validations_) {
+ validation->AddValidationOutEdge(edge);
+ }
}
});
}
diff --git a/src/manifest_parser_test.cc b/src/manifest_parser_test.cc
index 00f4aa4..0732129 100644
--- a/src/manifest_parser_test.cc
+++ b/src/manifest_parser_test.cc
@@ -1013,6 +1013,16 @@ TEST_F(ParserTest, OrderOnly) {
ASSERT_TRUE(edge->is_order_only(1));
}
+TEST_F(ParserTest, Validations) {
+ ASSERT_NO_FATAL_FAILURE(AssertParse(
+"rule cat\n command = cat $in > $out\n"
+"build foo: cat bar |@ baz\n"));
+
+ Edge* edge = state.LookupNode("foo")->in_edge();
+ ASSERT_EQ(edge->validations_.size(), 1);
+ EXPECT_EQ(edge->validations_[0]->path(), "baz");
+}
+
TEST_F(ParserTest, ImplicitOutput) {
ASSERT_NO_FATAL_FAILURE(AssertParse(
"rule cat\n"
diff --git a/src/ninja.cc b/src/ninja.cc
index 4f7411b..701eb7a 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -408,6 +408,14 @@ int NinjaMain::ToolPath(const Options* options, int argc, char* argv[]) {
}
}
}
+ for (Edge* edge : node->GetValidationOutEdges()) {
+ for (Node* output : edge->outputs_) {
+ if (node_next.count(output) == 0) {
+ node_next[output] = node;
+ queue.push_back(output);
+ }
+ }
+ }
}
Error("%s does not depend on %s", out->path().c_str(), in->path().c_str());
return 1;
@@ -536,6 +544,12 @@ int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) {
label = "|| ";
printf(" %s%s\n", label, edge->inputs_[in]->path().c_str());
}
+ if (!edge->validations_.empty()) {
+ printf(" validations:\n");
+ for (Node* validation : edge->validations_) {
+ printf(" %s\n", validation->path().c_str());
+ }
+ }
}
printf(" outputs:\n");
const std::vector<Edge*> out_edges = node->GetOutEdges();
@@ -546,6 +560,17 @@ int NinjaMain::ToolQuery(const Options* options, int argc, char* argv[]) {
printf(" %s\n", (*out)->path().c_str());
}
}
+ const std::vector<Edge*> validation_edges = node->GetValidationOutEdges();
+ if (!validation_edges.empty()) {
+ printf(" validation for:\n");
+ for (vector<Edge*>::const_iterator edge = validation_edges.begin();
+ edge != validation_edges.end(); ++edge) {
+ for (vector<Node*>::iterator out = (*edge)->outputs_.begin();
+ out != (*edge)->outputs_.end(); ++out) {
+ printf(" %s\n", (*out)->path().c_str());
+ }
+ }
+ }
}
return 0;
}