blob: ba263bec7e348835a9a97ff84b3d7b8fbf596e49 [file] [log] [blame]
//===-- MakefileDepsParser.cpp --------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "llbuild/Core/MakefileDepsParser.h"
#include "llvm/ADT/SmallString.h"
using namespace llvm;
using namespace llbuild;
using namespace llbuild::core;
MakefileDepsParser::ParseActions::~ParseActions() {}
#pragma mark - MakefileDepsParser Implementation
static bool isWordChar(int c) {
switch (c) {
case '\0':
case '\t':
case '\n':
case ' ':
case '$':
case ':':
case ';':
case '=':
case '|':
case '%':
return false;
default:
return true;
}
}
static void skipWhitespaceAndComments(const char*& cur, const char* end) {
for (; cur != end; ++cur) {
int c = *cur;
// Skip comments.
if (c == '#') {
// Skip to the next newline.
while (cur + 1 != end && cur[1] == '\n')
++cur;
continue;
}
if (c == ' ' || c == '\t' || c == '\n')
continue;
break;
}
}
static void skipNonNewlineWhitespace(const char*& cur, const char* end) {
for (; cur != end; ++cur) {
int c = *cur;
// Skip regular whitespace.
if (c == ' ' || c == '\t')
continue;
// If this is an escaped newline, also skip it.
if (c == '\\' && cur + 1 != end && cur[1] == '\n') {
++cur;
continue;
}
// Otherwise, stop scanning.
break;
}
}
static void skipToEndOfLine(const char*& cur, const char* end) {
for (; cur != end; ++cur) {
int c = *cur;
if (c == '\n') {
++cur;
break;
}
}
}
static void lexWord(const char*& cur, const char* end,
SmallVectorImpl<char> &unescapedWord) {
unescapedWord.clear();
for (; cur != end; ++cur) {
int c = *cur;
// Check if this is an escape sequence.
if (c == '\\') {
// If this is a line continuation, it ends the word.
if (cur + 1 != end && cur[1] == '\n')
break;
// Otherwise, skip the escaped character.
++cur;
int c = *cur;
// Honor the escaping rules as generated by Clang and GCC, which are *not
// necessarily* the actual escaping rules of BSD Make or GNU Make. Due to
// incompatibilities in the escape syntax between those two makes, and the
// GNU make behavior of retrying an escaped string with the original
// input, GCC/Clang adopt a strategy around escaping ' ' and '#', but not
// r"\\" itself, or any other characters. However, there are some
// situations where Clang *will* generate an escaped '\\' using the
// r"\\\\" sequence, so we also honor that.
//
// FIXME: Make this more complete, or move to a better dependency format.
if (c == ' ' || c == '#' || c == '\\') {
unescapedWord.push_back(c);
} else {
unescapedWord.push_back('\\');
unescapedWord.push_back(c);
}
continue;
} else if (c == '$' && cur + 1 != end && cur[1] == '$') {
// "$$" is an escaped '$'.
unescapedWord.push_back(c);
++cur;
continue;
}
// Otherwise, if this is not a valid word character then skip it.
if (!isWordChar(c))
break;
unescapedWord.push_back(c);
}
}
void MakefileDepsParser::parse() {
const char* cur = data;
const char* end = data + length;
// Storage for currently begin lexed unescaped word.
SmallString<256> unescapedWord;
// While we have input data...
while (cur != end) {
// Skip leading whitespace and comments.
skipWhitespaceAndComments(cur, end);
// If we have reached the end of the input, we are done.
if (cur == end)
break;
// The next token should be a word.
const char* wordStart = cur;
lexWord(cur, end, unescapedWord);
if (cur == wordStart) {
actions.error("unexpected character in file", cur - data);
skipToEndOfLine(cur, end);
continue;
}
actions.actOnRuleStart(wordStart, cur - wordStart, unescapedWord.str());
// The next token should be a colon.
skipNonNewlineWhitespace(cur, end);
if (cur == end || *cur != ':') {
actions.error("missing ':' following rule", cur - data);
actions.actOnRuleEnd();
skipToEndOfLine(cur, end);
continue;
}
// Skip the colon.
++cur;
// Consume dependency words until we reach the end of a line.
while (cur != end) {
// Skip forward and check for EOL.
skipNonNewlineWhitespace(cur, end);
if (cur == end || *cur == '\n')
break;
// Otherwise, we should have a word.
const char* wordStart = cur;
lexWord(cur, end, unescapedWord);
if (cur == wordStart) {
actions.error("unexpected character in prerequisites", cur - data);
skipToEndOfLine(cur, end);
continue;
}
actions.actOnRuleDependency(wordStart, cur - wordStart,
unescapedWord.str());
}
actions.actOnRuleEnd();
}
}