| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "dyndep_parser.h" |
| |
| #include <vector> |
| |
| #include "dyndep.h" |
| #include "graph.h" |
| #include "state.h" |
| #include "util.h" |
| #include "version.h" |
| |
| DyndepParser::DyndepParser(State* state, FileReader* file_reader, |
| DyndepFile* dyndep_file) |
| : Parser(state, file_reader) |
| , dyndep_file_(dyndep_file) { |
| } |
| |
| bool DyndepParser::Parse(const string& filename, const string& input, |
| string* err) { |
| Lexer lexer(filename, input, input.data()); |
| |
| // Require a supported ninja_dyndep_version value immediately so |
| // we can exit before encountering any syntactic surprises. |
| bool haveDyndepVersion = false; |
| |
| for (;;) { |
| Lexer::Token token = lexer.ReadToken(); |
| switch (token) { |
| case Lexer::BUILD: { |
| if (!haveDyndepVersion) |
| return lexer.Error("expected 'ninja_dyndep_version = ...'", err); |
| if (!ParseEdge(lexer, err)) |
| return false; |
| break; |
| } |
| case Lexer::IDENT: { |
| lexer.UnreadToken(); |
| if (haveDyndepVersion) |
| return lexer.Error(string("unexpected ") + Lexer::TokenName(token), |
| err); |
| if (!ParseDyndepVersion(lexer, err)) |
| return false; |
| haveDyndepVersion = true; |
| break; |
| } |
| case Lexer::ERROR: |
| return lexer.Error(lexer.DescribeLastError(), err); |
| case Lexer::TEOF: |
| if (!haveDyndepVersion) |
| return lexer.Error("expected 'ninja_dyndep_version = ...'", err); |
| return true; |
| case Lexer::NEWLINE: |
| break; |
| default: |
| return lexer.Error(string("unexpected ") + Lexer::TokenName(token), |
| err); |
| } |
| } |
| return false; // not reached |
| } |
| |
| static const HashedStrView kNinjaDyndepVersion { "ninja_dyndep_version" }; |
| |
| bool DyndepParser::ParseDyndepVersion(Lexer& lexer, string* err) { |
| StringPiece name; |
| StringPiece let_value; |
| if (!ParseLet(lexer, &name, &let_value, err)) |
| return false; |
| if (name != kNinjaDyndepVersion) { |
| return lexer.Error("expected 'ninja_dyndep_version = ...'", err); |
| } |
| string version; |
| EvaluateBindingInScope(&version, let_value, scope_); |
| int major, minor; |
| ParseVersion(version, &major, &minor); |
| if (major != 1 || minor != 0) { |
| return lexer.Error( |
| string("unsupported 'ninja_dyndep_version = ") + version + "'", err); |
| return false; |
| } |
| return true; |
| } |
| |
| bool DyndepParser::ParseLet(Lexer& lexer, StringPiece* key, StringPiece* value, string* err) { |
| if (!lexer.ReadIdent(key)) |
| return lexer.Error("expected variable name", err); |
| if (!ExpectToken(lexer, Lexer::EQUALS, err)) |
| return false; |
| if (!lexer.ReadBindingValue(value, err)) |
| return false; |
| return true; |
| } |
| |
| static const HashedStrView kDyndep { "dyndep" }; |
| static const HashedStrView kRestat { "restat" }; |
| |
| bool DyndepParser::ParseEdge(Lexer& lexer, string* err) { |
| // Parse one explicit output. We expect it to already have an edge. |
| // We will record its dynamically-discovered dependency information. |
| Dyndeps* dyndeps = NULL; |
| { |
| LexedPath out0; |
| if (!lexer.ReadPath(&out0, err)) |
| return false; |
| if (out0.str_.empty()) |
| return lexer.Error("expected path", err); |
| |
| string path; |
| EvaluatePathInScope(&path, out0, scope_); |
| string path_err; |
| uint64_t slash_bits; |
| if (!CanonicalizePath(&path, &slash_bits, &path_err)) |
| return lexer.Error(path_err, err); |
| Node* node = state_->LookupNode(path); |
| if (!node || !node->in_edge()) |
| return lexer.Error("no build statement exists for '" + path + "'", err); |
| Edge* edge = node->in_edge(); |
| std::pair<DyndepFile::iterator, bool> res = |
| dyndep_file_->insert(DyndepFile::value_type(edge, Dyndeps())); |
| if (!res.second) |
| return lexer.Error("multiple statements for '" + path + "'", err); |
| dyndeps = &res.first->second; |
| } |
| |
| // Disallow explicit outputs. |
| { |
| LexedPath out; |
| if (!lexer.ReadPath(&out, err)) |
| return false; |
| if (!out.str_.empty()) |
| return lexer.Error("explicit outputs not supported", err); |
| } |
| |
| // Parse implicit outputs, if any. |
| vector<LexedPath> outs; |
| if (lexer.PeekToken(Lexer::PIPE)) { |
| for (;;) { |
| LexedPath out; |
| if (!lexer.ReadPath(&out, err)) |
| return err; |
| if (out.str_.empty()) |
| break; |
| outs.push_back(out); |
| } |
| } |
| |
| if (!ExpectToken(lexer, Lexer::COLON, err)) |
| return false; |
| |
| StringPiece rule_name; |
| if (!lexer.ReadIdent(&rule_name) || rule_name != kDyndep) |
| return lexer.Error("expected build command name 'dyndep'", err); |
| |
| // Disallow explicit inputs. |
| { |
| LexedPath in; |
| if (!lexer.ReadPath(&in, err)) |
| return false; |
| if (!in.str_.empty()) |
| return lexer.Error("explicit inputs not supported", err); |
| } |
| |
| // Parse implicit inputs, if any. |
| vector<LexedPath> ins; |
| if (lexer.PeekToken(Lexer::PIPE)) { |
| for (;;) { |
| LexedPath in; |
| if (!lexer.ReadPath(&in, err)) |
| return err; |
| if (in.str_.empty()) |
| break; |
| ins.push_back(in); |
| } |
| } |
| |
| // Disallow order-only inputs. |
| if (lexer.PeekToken(Lexer::PIPE2)) |
| return lexer.Error("order-only inputs not supported", err); |
| |
| if (!ExpectToken(lexer, Lexer::NEWLINE, err)) |
| return false; |
| |
| if (lexer.PeekToken(Lexer::INDENT)) { |
| StringPiece key; |
| StringPiece val; |
| if (!ParseLet(lexer, &key, &val, err)) |
| return false; |
| if (key != kRestat) |
| return lexer.Error("binding is not 'restat'", err); |
| string value; |
| EvaluateBindingInScope(&value, val, scope_); |
| dyndeps->restat_ = !value.empty(); |
| } |
| |
| dyndeps->implicit_inputs_.reserve(ins.size()); |
| for (vector<LexedPath>::iterator i = ins.begin(); i != ins.end(); ++i) { |
| string path; |
| EvaluatePathInScope(&path, *i, scope_); |
| string path_err; |
| uint64_t slash_bits; |
| if (!CanonicalizePath(&path, &slash_bits, &path_err)) |
| return lexer.Error(path_err, err); |
| Node* n = state_->GetNode(path, slash_bits); |
| dyndeps->implicit_inputs_.push_back(n); |
| } |
| |
| dyndeps->implicit_outputs_.reserve(outs.size()); |
| for (vector<LexedPath>::iterator i = outs.begin(); i != outs.end(); ++i) { |
| string path; |
| EvaluatePathInScope(&path, *i, scope_); |
| string path_err; |
| uint64_t slash_bits; |
| if (!CanonicalizePath(&path, &slash_bits, &path_err)) |
| return lexer.Error(path_err, err); |
| Node* n = state_->GetNode(path, slash_bits); |
| dyndeps->implicit_outputs_.push_back(n); |
| } |
| |
| return true; |
| } |