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
This commit is contained in:
170
androidmk/parser/make_strings.go
Normal file
170
androidmk/parser/make_strings.go
Normal file
@@ -0,0 +1,170 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"text/scanner"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// A MakeString is a string that may contain variable substitutions in it.
|
||||
// It can be considered as an alternating list of raw Strings and variable
|
||||
// substitutions, where the first and last entries in the list must be raw
|
||||
// Strings (possibly empty). A MakeString that starts with a variable
|
||||
// will have an empty first raw string, and a MakeString that ends with a
|
||||
// variable will have an empty last raw string. Two sequential Variables
|
||||
// will have an empty raw string between them.
|
||||
//
|
||||
// The MakeString is stored as two lists, a list of raw Strings and a list
|
||||
// of Variables. The raw string list is always one longer than the variable
|
||||
// list.
|
||||
type MakeString struct {
|
||||
Pos scanner.Position
|
||||
Strings []string
|
||||
Variables []Variable
|
||||
}
|
||||
|
||||
func SimpleMakeString(s string, pos scanner.Position) *MakeString {
|
||||
return &MakeString{
|
||||
Pos: pos,
|
||||
Strings: []string{s},
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MakeString) appendString(s string) {
|
||||
if len(ms.Strings) == 0 {
|
||||
ms.Strings = []string{s}
|
||||
return
|
||||
} else {
|
||||
ms.Strings[len(ms.Strings)-1] += s
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MakeString) appendVariable(v Variable) {
|
||||
if len(ms.Strings) == 0 {
|
||||
ms.Strings = []string{"", ""}
|
||||
ms.Variables = []Variable{v}
|
||||
} else {
|
||||
ms.Strings = append(ms.Strings, "")
|
||||
ms.Variables = append(ms.Variables, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MakeString) appendMakeString(other *MakeString) {
|
||||
last := len(ms.Strings) - 1
|
||||
ms.Strings[last] += other.Strings[0]
|
||||
ms.Strings = append(ms.Strings, other.Strings[1:]...)
|
||||
ms.Variables = append(ms.Variables, other.Variables...)
|
||||
}
|
||||
|
||||
func (ms *MakeString) Value(scope Scope) string {
|
||||
if len(ms.Strings) == 0 {
|
||||
return ""
|
||||
} else {
|
||||
ret := ms.Strings[0]
|
||||
for i := range ms.Strings[1:] {
|
||||
ret += ms.Variables[i].Value(scope)
|
||||
ret += ms.Strings[i+1]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MakeString) Dump() string {
|
||||
if len(ms.Strings) == 0 {
|
||||
return ""
|
||||
} else {
|
||||
ret := ms.Strings[0]
|
||||
for i := range ms.Strings[1:] {
|
||||
ret += ms.Variables[i].Dump()
|
||||
ret += ms.Strings[i+1]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MakeString) Const() bool {
|
||||
return len(ms.Strings) <= 1
|
||||
}
|
||||
|
||||
func (ms *MakeString) Empty() bool {
|
||||
return len(ms.Strings) == 0 || (len(ms.Strings) == 1 && ms.Strings[0] == "")
|
||||
}
|
||||
|
||||
func (ms *MakeString) Split(sep string) []*MakeString {
|
||||
return ms.SplitN(sep, -1)
|
||||
}
|
||||
|
||||
func (ms *MakeString) SplitN(sep string, n int) []*MakeString {
|
||||
ret := []*MakeString{}
|
||||
|
||||
curMs := SimpleMakeString("", ms.Pos)
|
||||
|
||||
var i int
|
||||
var s string
|
||||
for i, s = range ms.Strings {
|
||||
if n != 0 {
|
||||
split := splitAnyN(s, sep, n)
|
||||
if n != -1 {
|
||||
if len(split) > n {
|
||||
panic("oops!")
|
||||
} else {
|
||||
n -= len(split)
|
||||
}
|
||||
}
|
||||
curMs.appendString(split[0])
|
||||
|
||||
for _, r := range split[1:] {
|
||||
ret = append(ret, curMs)
|
||||
curMs = SimpleMakeString(r, ms.Pos)
|
||||
}
|
||||
} else {
|
||||
curMs.appendString(s)
|
||||
}
|
||||
|
||||
if i < len(ms.Strings)-1 {
|
||||
curMs.appendVariable(ms.Variables[i])
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, curMs)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (ms *MakeString) TrimLeftSpaces() {
|
||||
ms.Strings[0] = strings.TrimLeftFunc(ms.Strings[0], unicode.IsSpace)
|
||||
}
|
||||
|
||||
func (ms *MakeString) TrimRightSpaces() {
|
||||
last := len(ms.Strings) - 1
|
||||
ms.Strings[last] = strings.TrimRightFunc(ms.Strings[last], unicode.IsSpace)
|
||||
}
|
||||
|
||||
func (ms *MakeString) TrimRightOne() {
|
||||
last := len(ms.Strings) - 1
|
||||
if len(ms.Strings[last]) > 1 {
|
||||
ms.Strings[last] = ms.Strings[last][0 : len(ms.Strings[last])-1]
|
||||
}
|
||||
}
|
||||
|
||||
func (ms *MakeString) EndsWith(ch rune) bool {
|
||||
s := ms.Strings[len(ms.Strings)-1]
|
||||
return s[len(s)-1] == uint8(ch)
|
||||
}
|
||||
|
||||
func splitAnyN(s, sep string, n int) []string {
|
||||
ret := []string{}
|
||||
for n == -1 || n > 1 {
|
||||
index := strings.IndexAny(s, sep)
|
||||
if index >= 0 {
|
||||
ret = append(ret, s[0:index])
|
||||
s = s[index+1:]
|
||||
if n > 0 {
|
||||
n--
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
ret = append(ret, s)
|
||||
return ret
|
||||
}
|
96
androidmk/parser/make_strings_test.go
Normal file
96
androidmk/parser/make_strings_test.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var splitNTestCases = []struct {
|
||||
in *MakeString
|
||||
expected []*MakeString
|
||||
sep string
|
||||
n int
|
||||
}{
|
||||
{
|
||||
in: &MakeString{
|
||||
strings: []string{
|
||||
"a b c",
|
||||
"d e f",
|
||||
" h i j",
|
||||
},
|
||||
variables: []Variable{
|
||||
variable{name: SimpleMakeString("var1")},
|
||||
variable{name: SimpleMakeString("var2")},
|
||||
},
|
||||
},
|
||||
sep: " ",
|
||||
n: -1,
|
||||
expected: []*MakeString{
|
||||
SimpleMakeString("a"),
|
||||
SimpleMakeString("b"),
|
||||
&MakeString{
|
||||
strings: []string{"c", "d"},
|
||||
variables: []Variable{
|
||||
variable{name: SimpleMakeString("var1")},
|
||||
},
|
||||
},
|
||||
SimpleMakeString("e"),
|
||||
&MakeString{
|
||||
strings: []string{"f", ""},
|
||||
variables: []Variable{
|
||||
variable{name: SimpleMakeString("var2")},
|
||||
},
|
||||
},
|
||||
SimpleMakeString("h"),
|
||||
SimpleMakeString("i"),
|
||||
SimpleMakeString("j"),
|
||||
},
|
||||
},
|
||||
{
|
||||
in: &MakeString{
|
||||
strings: []string{
|
||||
"a b c",
|
||||
"d e f",
|
||||
" h i j",
|
||||
},
|
||||
variables: []Variable{
|
||||
variable{name: SimpleMakeString("var1")},
|
||||
variable{name: SimpleMakeString("var2")},
|
||||
},
|
||||
},
|
||||
sep: " ",
|
||||
n: 3,
|
||||
expected: []*MakeString{
|
||||
SimpleMakeString("a"),
|
||||
SimpleMakeString("b"),
|
||||
&MakeString{
|
||||
strings: []string{"c", "d e f", " h i j"},
|
||||
variables: []Variable{
|
||||
variable{name: SimpleMakeString("var1")},
|
||||
variable{name: SimpleMakeString("var2")},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestMakeStringSplitN(t *testing.T) {
|
||||
for _, test := range splitNTestCases {
|
||||
got := test.in.SplitN(test.sep, test.n)
|
||||
gotString := dumpArray(got)
|
||||
expectedString := dumpArray(test.expected)
|
||||
if gotString != expectedString {
|
||||
t.Errorf("expected:\n%s\ngot:\n%s", expectedString, gotString)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dumpArray(a []*MakeString) string {
|
||||
ret := make([]string, len(a))
|
||||
|
||||
for i, s := range a {
|
||||
ret[i] = s.Dump()
|
||||
}
|
||||
|
||||
return strings.Join(ret, "|||")
|
||||
}
|
142
androidmk/parser/makething.go
Normal file
142
androidmk/parser/makething.go
Normal file
@@ -0,0 +1,142 @@
|
||||
package parser
|
||||
|
||||
import (
|
||||
"text/scanner"
|
||||
)
|
||||
|
||||
type MakeThing interface {
|
||||
AsAssignment() (Assignment, bool)
|
||||
AsComment() (Comment, bool)
|
||||
AsDirective() (Directive, bool)
|
||||
AsRule() (Rule, bool)
|
||||
AsVariable() (Variable, bool)
|
||||
Dump() string
|
||||
Pos() scanner.Position
|
||||
EndPos() scanner.Position
|
||||
}
|
||||
|
||||
type Assignment struct {
|
||||
makeThing
|
||||
Name *MakeString
|
||||
Value *MakeString
|
||||
Target *MakeString
|
||||
Type string
|
||||
}
|
||||
|
||||
type Comment struct {
|
||||
makeThing
|
||||
Comment string
|
||||
}
|
||||
|
||||
type Directive struct {
|
||||
makeThing
|
||||
Name string
|
||||
Args *MakeString
|
||||
}
|
||||
|
||||
type Rule struct {
|
||||
makeThing
|
||||
Target *MakeString
|
||||
Prerequisites *MakeString
|
||||
Recipe string
|
||||
}
|
||||
|
||||
type Variable struct {
|
||||
makeThing
|
||||
Name *MakeString
|
||||
}
|
||||
|
||||
type makeThing struct {
|
||||
pos scanner.Position
|
||||
endPos scanner.Position
|
||||
}
|
||||
|
||||
func (m makeThing) Pos() scanner.Position {
|
||||
return m.pos
|
||||
}
|
||||
|
||||
func (m makeThing) EndPos() scanner.Position {
|
||||
return m.endPos
|
||||
}
|
||||
|
||||
func (makeThing) AsAssignment() (a Assignment, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (a Assignment) AsAssignment() (Assignment, bool) {
|
||||
return a, true
|
||||
}
|
||||
|
||||
func (a Assignment) Dump() string {
|
||||
target := ""
|
||||
if a.Target != nil {
|
||||
target = a.Target.Dump() + ": "
|
||||
}
|
||||
return target + a.Name.Dump() + a.Type + a.Value.Dump()
|
||||
}
|
||||
|
||||
func (makeThing) AsComment() (c Comment, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (c Comment) AsComment() (Comment, bool) {
|
||||
return c, true
|
||||
}
|
||||
|
||||
func (c Comment) Dump() string {
|
||||
return "#" + c.Comment
|
||||
}
|
||||
|
||||
func (makeThing) AsDirective() (d Directive, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (d Directive) AsDirective() (Directive, bool) {
|
||||
return d, true
|
||||
}
|
||||
|
||||
func (d Directive) Dump() string {
|
||||
return d.Name + " " + d.Args.Dump()
|
||||
}
|
||||
|
||||
func (makeThing) AsRule() (r Rule, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (r Rule) AsRule() (Rule, bool) {
|
||||
return r, true
|
||||
}
|
||||
|
||||
func (r Rule) Dump() string {
|
||||
recipe := ""
|
||||
if r.Recipe != "" {
|
||||
recipe = "\n" + r.Recipe
|
||||
}
|
||||
return "rule: " + r.Target.Dump() + ": " + r.Prerequisites.Dump() + recipe
|
||||
}
|
||||
|
||||
func (makeThing) AsVariable() (v Variable, ok bool) {
|
||||
return
|
||||
}
|
||||
|
||||
func (v Variable) AsVariable() (Variable, bool) {
|
||||
return v, true
|
||||
}
|
||||
|
||||
func (v Variable) Dump() string {
|
||||
return "$(" + v.Name.Dump() + ")"
|
||||
}
|
||||
|
||||
type byPosition []MakeThing
|
||||
|
||||
func (s byPosition) Len() int {
|
||||
return len(s)
|
||||
}
|
||||
|
||||
func (s byPosition) Swap(i, j int) {
|
||||
s[i], s[j] = s[j], s[i]
|
||||
}
|
||||
|
||||
func (s byPosition) Less(i, j int) bool {
|
||||
return s[i].Pos().Offset < s[j].Pos().Offset
|
||||
}
|
633
androidmk/parser/parser.go
Normal file
633
androidmk/parser/parser.go
Normal file
@@ -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)
|
||||
}
|
||||
}
|
88
androidmk/parser/scope.go
Normal file
88
androidmk/parser/scope.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package parser
|
||||
|
||||
import "strings"
|
||||
|
||||
type Scope interface {
|
||||
Get(name string) string
|
||||
Set(name, value string)
|
||||
Call(name string, args []string) string
|
||||
SetFunc(name string, f func([]string) string)
|
||||
}
|
||||
|
||||
type scope struct {
|
||||
variables map[string]string
|
||||
functions map[string]func([]string) string
|
||||
parent Scope
|
||||
}
|
||||
|
||||
func (s *scope) Get(name string) string {
|
||||
if val, ok := s.variables[name]; ok {
|
||||
return val
|
||||
} else if s.parent != nil {
|
||||
return s.parent.Get(name)
|
||||
} else if val, ok := builtinScope[name]; ok {
|
||||
return val
|
||||
} else {
|
||||
return "<'" + name + "' unset>"
|
||||
}
|
||||
}
|
||||
|
||||
func (s *scope) Set(name, value string) {
|
||||
s.variables[name] = value
|
||||
}
|
||||
|
||||
func (s *scope) Call(name string, args []string) string {
|
||||
if f, ok := s.functions[name]; ok {
|
||||
return f(args)
|
||||
}
|
||||
|
||||
return "<func:'" + name + "' unset>"
|
||||
}
|
||||
|
||||
func (s *scope) SetFunc(name string, f func([]string) string) {
|
||||
s.functions[name] = f
|
||||
}
|
||||
|
||||
func NewScope(parent Scope) Scope {
|
||||
return &scope{
|
||||
variables: make(map[string]string),
|
||||
functions: make(map[string]func([]string) string),
|
||||
parent: parent,
|
||||
}
|
||||
}
|
||||
|
||||
var builtinScope map[string]string
|
||||
|
||||
func init() {
|
||||
builtinScope := make(map[string]string)
|
||||
builtinScope["__builtin_dollar"] = "$"
|
||||
}
|
||||
|
||||
func (v Variable) Value(scope Scope) string {
|
||||
f := v.Name.SplitN(" \t", 2)
|
||||
if len(f) > 1 && f[0].Const() {
|
||||
fname := f[0].Value(nil)
|
||||
if isFunctionName(fname) {
|
||||
args := f[1].Split(",")
|
||||
argVals := make([]string, len(args))
|
||||
for i, a := range args {
|
||||
argVals[i] = a.Value(scope)
|
||||
}
|
||||
|
||||
if fname == "call" {
|
||||
return scope.Call(argVals[0], argVals[1:])
|
||||
} else {
|
||||
return "__builtin_func:" + fname + " " + strings.Join(argVals, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return scope.Get(v.Name.Value(scope))
|
||||
}
|
||||
|
||||
func toVariable(ms *MakeString) (Variable, bool) {
|
||||
if len(ms.Variables) == 1 && ms.Strings[0] == "" && ms.Strings[1] == "" {
|
||||
return ms.Variables[0], true
|
||||
}
|
||||
return Variable{}, false
|
||||
}
|
Reference in New Issue
Block a user