blob: ce3b03ecd29c975c04f493b5aa7e3288fd990966 [file] [log] [blame]
// 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;
}