diff options
Diffstat (limited to 'ninja/src/manifest_parser.cc')
-rw-r--r-- | ninja/src/manifest_parser.cc | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/ninja/src/manifest_parser.cc b/ninja/src/manifest_parser.cc new file mode 100644 index 00000000000..d742331bb76 --- /dev/null +++ b/ninja/src/manifest_parser.cc @@ -0,0 +1,379 @@ +// Copyright 2011 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 "manifest_parser.h" + +#include <stdio.h> +#include <stdlib.h> +#include <vector> + +#include "graph.h" +#include "metrics.h" +#include "state.h" +#include "util.h" +#include "version.h" + +ManifestParser::ManifestParser(State* state, FileReader* file_reader) + : state_(state), file_reader_(file_reader) { + env_ = &state->bindings_; +} +bool ManifestParser::Load(const string& filename, string* err) { + string contents; + string read_err; + if (!file_reader_->ReadFile(filename, &contents, &read_err)) { + *err = "loading '" + filename + "': " + read_err; + return false; + } + contents.resize(contents.size() + 10); + return Parse(filename, contents, err); +} + +bool ManifestParser::Parse(const string& filename, const string& input, + string* err) { + METRIC_RECORD(".ninja parse"); + lexer_.Start(filename, input); + + for (;;) { + Lexer::Token token = lexer_.ReadToken(); + switch (token) { + case Lexer::POOL: + if (!ParsePool(err)) + return false; + break; + case Lexer::BUILD: + if (!ParseEdge(err)) + return false; + break; + case Lexer::RULE: + if (!ParseRule(err)) + return false; + break; + case Lexer::DEFAULT: + if (!ParseDefault(err)) + return false; + break; + case Lexer::IDENT: { + lexer_.UnreadToken(); + string name; + EvalString let_value; + if (!ParseLet(&name, &let_value, err)) + return false; + string value = let_value.Evaluate(env_); + // Check ninja_required_version immediately so we can exit + // before encountering any syntactic surprises. + if (name == "ninja_required_version") + CheckNinjaVersion(value); + env_->AddBinding(name, value); + break; + } + case Lexer::INCLUDE: + if (!ParseFileInclude(false, err)) + return false; + break; + case Lexer::SUBNINJA: + if (!ParseFileInclude(true, err)) + return false; + break; + case Lexer::ERROR: { + return lexer_.Error(lexer_.DescribeLastError(), err); + } + case Lexer::TEOF: + return true; + case Lexer::NEWLINE: + break; + default: + return lexer_.Error(string("unexpected ") + Lexer::TokenName(token), + err); + } + } + return false; // not reached +} + + +bool ManifestParser::ParsePool(string* err) { + string name; + if (!lexer_.ReadIdent(&name)) + return lexer_.Error("expected pool name", err); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + if (state_->LookupPool(name) != NULL) + return lexer_.Error("duplicate pool '" + name + "'", err); + + int depth = -1; + + while (lexer_.PeekToken(Lexer::INDENT)) { + string key; + EvalString value; + if (!ParseLet(&key, &value, err)) + return false; + + if (key == "depth") { + string depth_string = value.Evaluate(env_); + depth = atol(depth_string.c_str()); + if (depth < 0) + return lexer_.Error("invalid pool depth", err); + } else { + return lexer_.Error("unexpected variable '" + key + "'", err); + } + } + + if (depth < 0) + return lexer_.Error("expected 'depth =' line", err); + + state_->AddPool(new Pool(name, depth)); + return true; +} + + +bool ManifestParser::ParseRule(string* err) { + string name; + if (!lexer_.ReadIdent(&name)) + return lexer_.Error("expected rule name", err); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + if (state_->LookupRule(name) != NULL) { + *err = "duplicate rule '" + name + "'"; + return false; + } + + Rule* rule = new Rule(name); // XXX scoped_ptr + + while (lexer_.PeekToken(Lexer::INDENT)) { + string key; + EvalString value; + if (!ParseLet(&key, &value, err)) + return false; + + if (Rule::IsReservedBinding(key)) { + rule->AddBinding(key, value); + } else { + // Die on other keyvals for now; revisit if we want to add a + // scope here. + return lexer_.Error("unexpected variable '" + key + "'", err); + } + } + + if (rule->bindings_["rspfile"].empty() != + rule->bindings_["rspfile_content"].empty()) { + return lexer_.Error("rspfile and rspfile_content need to be " + "both specified", err); + } + + if (rule->bindings_["command"].empty()) + return lexer_.Error("expected 'command =' line", err); + + state_->AddRule(rule); + return true; +} + +bool ManifestParser::ParseLet(string* key, EvalString* value, string* err) { + if (!lexer_.ReadIdent(key)) + return false; + if (!ExpectToken(Lexer::EQUALS, err)) + return false; + if (!lexer_.ReadVarValue(value, err)) + return false; + return true; +} + +bool ManifestParser::ParseDefault(string* err) { + EvalString eval; + if (!lexer_.ReadPath(&eval, err)) + return false; + if (eval.empty()) + return lexer_.Error("expected target name", err); + + do { + string path = eval.Evaluate(env_); + string path_err; + if (!CanonicalizePath(&path, &path_err)) + return lexer_.Error(path_err, err); + if (!state_->AddDefault(path, &path_err)) + return lexer_.Error(path_err, err); + + eval.Clear(); + if (!lexer_.ReadPath(&eval, err)) + return false; + } while (!eval.empty()); + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + return true; +} + +bool ManifestParser::ParseEdge(string* err) { + vector<EvalString> ins, outs; + + { + EvalString out; + if (!lexer_.ReadPath(&out, err)) + return false; + if (out.empty()) + return lexer_.Error("expected path", err); + + do { + outs.push_back(out); + + out.Clear(); + if (!lexer_.ReadPath(&out, err)) + return false; + } while (!out.empty()); + } + + if (!ExpectToken(Lexer::COLON, err)) + return false; + + string rule_name; + if (!lexer_.ReadIdent(&rule_name)) + return lexer_.Error("expected build command name", err); + + const Rule* rule = state_->LookupRule(rule_name); + if (!rule) + return lexer_.Error("unknown build rule '" + rule_name + "'", err); + + for (;;) { + // XXX should we require one path here? + EvalString in; + if (!lexer_.ReadPath(&in, err)) + return false; + if (in.empty()) + break; + ins.push_back(in); + } + + // Add all implicit deps, counting how many as we go. + int implicit = 0; + if (lexer_.PeekToken(Lexer::PIPE)) { + for (;;) { + EvalString in; + if (!lexer_.ReadPath(&in, err)) + return err; + if (in.empty()) + break; + ins.push_back(in); + ++implicit; + } + } + + // Add all order-only deps, counting how many as we go. + int order_only = 0; + if (lexer_.PeekToken(Lexer::PIPE2)) { + for (;;) { + EvalString in; + if (!lexer_.ReadPath(&in, err)) + return false; + if (in.empty()) + break; + ins.push_back(in); + ++order_only; + } + } + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + // XXX scoped_ptr to handle error case. + BindingEnv* env = new BindingEnv(env_); + + while (lexer_.PeekToken(Lexer::INDENT)) { + string key; + EvalString val; + if (!ParseLet(&key, &val, err)) + return false; + + env->AddBinding(key, val.Evaluate(env_)); + } + + Edge* edge = state_->AddEdge(rule); + edge->env_ = env; + + string pool_name = edge->GetBinding("pool"); + if (!pool_name.empty()) { + Pool* pool = state_->LookupPool(pool_name); + if (pool == NULL) + return lexer_.Error("unknown pool name", err); + edge->pool_ = pool; + } + + for (vector<EvalString>::iterator i = ins.begin(); i != ins.end(); ++i) { + string path = i->Evaluate(env); + string path_err; + if (!CanonicalizePath(&path, &path_err)) + return lexer_.Error(path_err, err); + state_->AddIn(edge, path); + } + for (vector<EvalString>::iterator i = outs.begin(); i != outs.end(); ++i) { + string path = i->Evaluate(env); + string path_err; + if (!CanonicalizePath(&path, &path_err)) + return lexer_.Error(path_err, err); + state_->AddOut(edge, path); + } + edge->implicit_deps_ = implicit; + edge->order_only_deps_ = order_only; + + // Multiple outputs aren't (yet?) supported with depslog. + string deps_type = edge->GetBinding("deps"); + if (!deps_type.empty() && edge->outputs_.size() > 1) { + return lexer_.Error("multiple outputs aren't (yet?) supported by depslog; " + "bring this up on the mailing list if it affects you", + err); + } + + return true; +} + +bool ManifestParser::ParseFileInclude(bool new_scope, string* err) { + // XXX this should use ReadPath! + EvalString eval; + if (!lexer_.ReadPath(&eval, err)) + return false; + string path = eval.Evaluate(env_); + + string contents; + string read_err; + if (!file_reader_->ReadFile(path, &contents, &read_err)) + return lexer_.Error("loading '" + path + "': " + read_err, err); + + ManifestParser subparser(state_, file_reader_); + if (new_scope) { + subparser.env_ = new BindingEnv(env_); + } else { + subparser.env_ = env_; + } + + if (!subparser.Parse(path, contents, err)) + return false; + + if (!ExpectToken(Lexer::NEWLINE, err)) + return false; + + return true; +} + +bool ManifestParser::ExpectToken(Lexer::Token expected, string* err) { + Lexer::Token token = lexer_.ReadToken(); + if (token != expected) { + string message = string("expected ") + Lexer::TokenName(expected); + message += string(", got ") + Lexer::TokenName(token); + message += Lexer::TokenErrorHint(expected); + return lexer_.Error(message, err); + } + return true; +} |