Add soong_build primary builder

Initial build logic for building android with soong.  It can build
a variety of C and C++ files for arm/arm64 and host.

Change-Id: I10eb37c2c2a50be6af1bb5fd568c0962b9476bf0
diff --git a/androidmk/parser/parser.go b/androidmk/parser/parser.go
new file mode 100644
index 0000000..58e612e
--- /dev/null
+++ b/androidmk/parser/parser.go
@@ -0,0 +1,633 @@
+package parser
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"sort"
+	"text/scanner"
+)
+
+var errTooManyErrors = errors.New("too many errors")
+
+const maxErrors = 100
+
+type ParseError struct {
+	Err error
+	Pos scanner.Position
+}
+
+func (e *ParseError) Error() string {
+	return fmt.Sprintf("%s: %s", e.Pos, e.Err)
+}
+
+func (p *parser) Parse() ([]MakeThing, []error) {
+	defer func() {
+		if r := recover(); r != nil {
+			if r == errTooManyErrors {
+				return
+			}
+			panic(r)
+		}
+	}()
+
+	p.parseLines()
+	p.accept(scanner.EOF)
+	p.things = append(p.things, p.comments...)
+	sort.Sort(byPosition(p.things))
+
+	return p.things, p.errors
+}
+
+type parser struct {
+	scanner  scanner.Scanner
+	tok      rune
+	errors   []error
+	comments []MakeThing
+	things   []MakeThing
+}
+
+func NewParser(filename string, r io.Reader) *parser {
+	p := &parser{}
+	p.scanner.Init(r)
+	p.scanner.Error = func(sc *scanner.Scanner, msg string) {
+		p.errorf(msg)
+	}
+	p.scanner.Whitespace = 0
+	p.scanner.IsIdentRune = func(ch rune, i int) bool {
+		return ch > 0 && ch != ':' && ch != '#' && ch != '=' && ch != '+' && ch != '$' &&
+			ch != '\\' && ch != '(' && ch != ')' && ch != '{' && ch != '}' && ch != ';' &&
+			ch != '|' && ch != '?' && ch != '\r' && !isWhitespace(ch)
+	}
+	p.scanner.Mode = scanner.ScanIdents
+	p.scanner.Filename = filename
+	p.next()
+	return p
+}
+
+func (p *parser) errorf(format string, args ...interface{}) {
+	pos := p.scanner.Position
+	if !pos.IsValid() {
+		pos = p.scanner.Pos()
+	}
+	err := &ParseError{
+		Err: fmt.Errorf(format, args...),
+		Pos: pos,
+	}
+	p.errors = append(p.errors, err)
+	if len(p.errors) >= maxErrors {
+		panic(errTooManyErrors)
+	}
+}
+
+func (p *parser) accept(toks ...rune) bool {
+	for _, tok := range toks {
+		if p.tok != tok {
+			p.errorf("expected %s, found %s", scanner.TokenString(tok),
+				scanner.TokenString(p.tok))
+			return false
+		}
+		p.next()
+	}
+	return true
+}
+
+func (p *parser) next() {
+	if p.tok != scanner.EOF {
+		p.tok = p.scanner.Scan()
+		for p.tok == '\r' {
+			p.tok = p.scanner.Scan()
+		}
+	}
+	return
+}
+
+func (p *parser) parseLines() {
+	for {
+		p.ignoreWhitespace()
+
+		if p.parseDirective() {
+			continue
+		}
+
+		ident, _ := p.parseExpression('=', '?', ':', '#', '\n')
+
+		p.ignoreSpaces()
+
+		switch p.tok {
+		case '?':
+			p.accept('?')
+			if p.tok == '=' {
+				p.parseAssignment("?=", nil, ident)
+			} else {
+				p.errorf("expected = after ?")
+			}
+		case '+':
+			p.accept('+')
+			if p.tok == '=' {
+				p.parseAssignment("+=", nil, ident)
+			} else {
+				p.errorf("expected = after +")
+			}
+		case ':':
+			p.accept(':')
+			switch p.tok {
+			case '=':
+				p.parseAssignment(":=", nil, ident)
+			default:
+				p.parseRule(ident)
+			}
+		case '=':
+			p.parseAssignment("=", nil, ident)
+		case '#', '\n', scanner.EOF:
+			ident.TrimRightSpaces()
+			if v, ok := toVariable(ident); ok {
+				p.things = append(p.things, v)
+			} else if !ident.Empty() {
+				p.errorf("expected directive, rule, or assignment after ident " + ident.Dump())
+			}
+			switch p.tok {
+			case scanner.EOF:
+				return
+			case '\n':
+				p.accept('\n')
+			case '#':
+				p.parseComment()
+			}
+		default:
+			p.errorf("expected assignment or rule definition, found %s\n",
+				p.scanner.TokenText())
+			return
+		}
+	}
+}
+
+func (p *parser) parseDirective() bool {
+	if p.tok != scanner.Ident || !isDirective(p.scanner.TokenText()) {
+		return false
+	}
+
+	d := p.scanner.TokenText()
+	pos := p.scanner.Position
+	endPos := pos
+	p.accept(scanner.Ident)
+
+	expression := SimpleMakeString("", pos)
+
+	switch d {
+	case "endif", "endef", "else":
+		// Nothing
+	case "define":
+		expression = p.parseDefine()
+	default:
+		p.ignoreSpaces()
+		expression, endPos = p.parseExpression()
+	}
+
+	p.things = append(p.things, Directive{
+		makeThing: makeThing{
+			pos:    pos,
+			endPos: endPos,
+		},
+		Name: d,
+		Args: expression,
+	})
+	return true
+}
+
+func (p *parser) parseDefine() *MakeString {
+	value := SimpleMakeString("", p.scanner.Position)
+
+loop:
+	for {
+		switch p.tok {
+		case scanner.Ident:
+			if p.scanner.TokenText() == "endef" {
+				p.accept(scanner.Ident)
+				break loop
+			}
+			value.appendString(p.scanner.TokenText())
+			p.accept(scanner.Ident)
+		case '\\':
+			p.parseEscape()
+			switch p.tok {
+			case '\n':
+				value.appendString(" ")
+			case scanner.EOF:
+				p.errorf("expected escaped character, found %s",
+					scanner.TokenString(p.tok))
+				break loop
+			default:
+				value.appendString(`\` + string(p.tok))
+			}
+			p.accept(p.tok)
+		//TODO: handle variables inside defines?  result depends if
+		//define is used in make or rule context
+		//case '$':
+		//	variable := p.parseVariable()
+		//	value.appendVariable(variable)
+		case scanner.EOF:
+			p.errorf("unexpected EOF while looking for endef")
+			break loop
+		default:
+			value.appendString(p.scanner.TokenText())
+			p.accept(p.tok)
+		}
+	}
+
+	return value
+}
+
+func (p *parser) parseEscape() {
+	p.scanner.Mode = 0
+	p.accept('\\')
+	p.scanner.Mode = scanner.ScanIdents
+}
+
+func (p *parser) parseExpression(end ...rune) (*MakeString, scanner.Position) {
+	value := SimpleMakeString("", p.scanner.Position)
+
+	endParen := false
+	for _, r := range end {
+		if r == ')' {
+			endParen = true
+		}
+	}
+	parens := 0
+
+	endPos := p.scanner.Position
+
+loop:
+	for {
+		if endParen && parens > 0 && p.tok == ')' {
+			parens--
+			value.appendString(")")
+			endPos = p.scanner.Position
+			p.accept(')')
+			continue
+		}
+
+		for _, r := range end {
+			if p.tok == r {
+				break loop
+			}
+		}
+
+		switch p.tok {
+		case '\n':
+			break loop
+		case scanner.Ident:
+			value.appendString(p.scanner.TokenText())
+			endPos = p.scanner.Position
+			p.accept(scanner.Ident)
+		case '\\':
+			p.parseEscape()
+			switch p.tok {
+			case '\n':
+				value.appendString(" ")
+			case scanner.EOF:
+				p.errorf("expected escaped character, found %s",
+					scanner.TokenString(p.tok))
+				return value, endPos
+			default:
+				value.appendString(`\` + string(p.tok))
+			}
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		case '#':
+			p.parseComment()
+			break loop
+		case '$':
+			var variable Variable
+			variable, endPos = p.parseVariable()
+			value.appendVariable(variable)
+		case scanner.EOF:
+			break loop
+		case '(':
+			if endParen {
+				parens++
+			}
+			value.appendString("(")
+			endPos = p.scanner.Position
+			p.accept('(')
+		default:
+			value.appendString(p.scanner.TokenText())
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		}
+	}
+
+	if parens > 0 {
+		p.errorf("expected closing paren %s", value.Dump())
+	}
+	return value, endPos
+}
+
+func (p *parser) parseVariable() (Variable, scanner.Position) {
+	pos := p.scanner.Position
+	endPos := pos
+	p.accept('$')
+	var name *MakeString
+	switch p.tok {
+	case '(':
+		return p.parseBracketedVariable('(', ')', pos)
+	case '{':
+		return p.parseBracketedVariable('{', '}', pos)
+	case '$':
+		name = SimpleMakeString("__builtin_dollar", scanner.Position{})
+	case scanner.EOF:
+		p.errorf("expected variable name, found %s",
+			scanner.TokenString(p.tok))
+	default:
+		name, endPos = p.parseExpression(variableNameEndRunes...)
+	}
+
+	return p.nameToVariable(name, pos, endPos), endPos
+}
+
+func (p *parser) parseBracketedVariable(start, end rune, pos scanner.Position) (Variable, scanner.Position) {
+	p.accept(start)
+	name, endPos := p.parseExpression(end)
+	p.accept(end)
+	return p.nameToVariable(name, pos, endPos), endPos
+}
+
+func (p *parser) nameToVariable(name *MakeString, pos, endPos scanner.Position) Variable {
+	return Variable{
+		makeThing: makeThing{
+			pos:    pos,
+			endPos: endPos,
+		},
+		Name: name,
+	}
+}
+
+func (p *parser) parseRule(target *MakeString) {
+	prerequisites, newLine := p.parseRulePrerequisites(target)
+
+	recipe := ""
+	endPos := p.scanner.Position
+loop:
+	for {
+		if newLine {
+			if p.tok == '\t' {
+				endPos = p.scanner.Position
+				p.accept('\t')
+				newLine = false
+				continue loop
+			} else if p.parseDirective() {
+				newLine = false
+				continue
+			} else {
+				break loop
+			}
+		}
+
+		newLine = false
+		switch p.tok {
+		case '\\':
+			p.parseEscape()
+			recipe += string(p.tok)
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		case '\n':
+			newLine = true
+			recipe += "\n"
+			endPos = p.scanner.Position
+			p.accept('\n')
+		case scanner.EOF:
+			break loop
+		default:
+			recipe += p.scanner.TokenText()
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		}
+	}
+
+	if prerequisites != nil {
+		p.things = append(p.things, Rule{
+			makeThing: makeThing{
+				pos:    target.Pos,
+				endPos: endPos,
+			},
+			Target:        target,
+			Prerequisites: prerequisites,
+			Recipe:        recipe,
+		})
+	}
+}
+
+func (p *parser) parseRulePrerequisites(target *MakeString) (*MakeString, bool) {
+	newLine := false
+
+	p.ignoreSpaces()
+
+	prerequisites, _ := p.parseExpression('#', '\n', ';', ':', '=')
+
+	switch p.tok {
+	case '\n':
+		p.accept('\n')
+		newLine = true
+	case '#':
+		p.parseComment()
+		newLine = true
+	case ';':
+		p.accept(';')
+	case ':':
+		p.accept(':')
+		if p.tok == '=' {
+			p.parseAssignment(":=", target, prerequisites)
+			return nil, true
+		} else {
+			more, _ := p.parseExpression('#', '\n', ';')
+			prerequisites.appendMakeString(more)
+		}
+	case '=':
+		p.parseAssignment("=", target, prerequisites)
+		return nil, true
+	default:
+		p.errorf("unexpected token %s after rule prerequisites", scanner.TokenString(p.tok))
+	}
+
+	return prerequisites, newLine
+}
+
+func (p *parser) parseComment() {
+	pos := p.scanner.Position
+	p.accept('#')
+	comment := ""
+	endPos := pos
+loop:
+	for {
+		switch p.tok {
+		case '\\':
+			p.parseEscape()
+			if p.tok == '\n' {
+				comment += "\n"
+			} else {
+				comment += "\\" + p.scanner.TokenText()
+			}
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		case '\n':
+			endPos = p.scanner.Position
+			p.accept('\n')
+			break loop
+		case scanner.EOF:
+			break loop
+		default:
+			comment += p.scanner.TokenText()
+			endPos = p.scanner.Position
+			p.accept(p.tok)
+		}
+	}
+
+	p.comments = append(p.comments, Comment{
+		makeThing: makeThing{
+			pos:    pos,
+			endPos: endPos,
+		},
+		Comment: comment,
+	})
+}
+
+func (p *parser) parseAssignment(t string, target *MakeString, ident *MakeString) {
+	// The value of an assignment is everything including and after the first
+	// non-whitespace character after the = until the end of the logical line,
+	// which may included escaped newlines
+	p.accept('=')
+	value, endPos := p.parseExpression()
+	value.TrimLeftSpaces()
+	if ident.EndsWith('+') && t == "=" {
+		ident.TrimRightOne()
+		t = "+="
+	}
+
+	ident.TrimRightSpaces()
+
+	p.things = append(p.things, Assignment{
+		makeThing: makeThing{
+			pos:    ident.Pos,
+			endPos: endPos,
+		},
+		Name:   ident,
+		Value:  value,
+		Target: target,
+		Type:   t,
+	})
+}
+
+type androidMkModule struct {
+	assignments map[string]string
+}
+
+type androidMkFile struct {
+	assignments map[string]string
+	modules     []androidMkModule
+	includes    []string
+}
+
+var directives = [...]string{
+	"define",
+	"else",
+	"endef",
+	"endif",
+	"ifdef",
+	"ifeq",
+	"ifndef",
+	"ifneq",
+	"include",
+	"-include",
+}
+
+var functions = [...]string{
+	"abspath",
+	"addprefix",
+	"addsuffix",
+	"basename",
+	"dir",
+	"notdir",
+	"subst",
+	"suffix",
+	"filter",
+	"filter-out",
+	"findstring",
+	"firstword",
+	"flavor",
+	"join",
+	"lastword",
+	"patsubst",
+	"realpath",
+	"shell",
+	"sort",
+	"strip",
+	"wildcard",
+	"word",
+	"wordlist",
+	"words",
+	"origin",
+	"foreach",
+	"call",
+	"info",
+	"error",
+	"warning",
+	"if",
+	"or",
+	"and",
+	"value",
+	"eval",
+	"file",
+}
+
+func init() {
+	sort.Strings(directives[:])
+	sort.Strings(functions[:])
+}
+
+func isDirective(s string) bool {
+	for _, d := range directives {
+		if s == d {
+			return true
+		} else if s < d {
+			return false
+		}
+	}
+	return false
+}
+
+func isFunctionName(s string) bool {
+	for _, f := range functions {
+		if s == f {
+			return true
+		} else if s < f {
+			return false
+		}
+	}
+	return false
+}
+
+func isWhitespace(ch rune) bool {
+	return ch == ' ' || ch == '\t' || ch == '\n'
+}
+
+func isValidVariableRune(ch rune) bool {
+	return ch != scanner.Ident && ch != ':' && ch != '=' && ch != '#'
+}
+
+var whitespaceRunes = []rune{' ', '\t', '\n'}
+var variableNameEndRunes = append([]rune{':', '=', '#', ')', '}'}, whitespaceRunes...)
+
+func (p *parser) ignoreSpaces() int {
+	skipped := 0
+	for p.tok == ' ' || p.tok == '\t' {
+		p.accept(p.tok)
+		skipped++
+	}
+	return skipped
+}
+
+func (p *parser) ignoreWhitespace() {
+	for isWhitespace(p.tok) {
+		p.accept(p.tok)
+	}
+}