unplugged-system/build/bazel/mkcompare/mkfile.go

176 lines
4.2 KiB
Go

package mkcompare
import (
"bufio"
"fmt"
"github.com/google/go-cmp/cmp"
"io"
"regexp"
"sort"
"strings"
)
type MkVariable struct {
Name string
Value string
}
type MkModule struct {
Type string
Location int
Extras int
Variables map[string]string
}
type MkFile struct {
Path string
Modules map[string]*MkModule
}
type myScanner struct {
*bufio.Scanner
lineNo int
}
func (s *myScanner) Scan() bool {
if s.Scanner.Scan() {
s.lineNo = s.lineNo + 1
return true
}
return false
}
var (
rexEmpty = regexp.MustCompile("^ *$")
rexHeader = regexp.MustCompile("^include +\\Q$(CLEAR_VARS)\\E *(# *(.*))?")
rexAssign = regexp.MustCompile("^ *(.*) ([:+])= *(.*)$")
rexFooter = regexp.MustCompile("^-?include *(.*)$")
rexIgnore1 = regexp.MustCompile("\\$\\(call dist-for-goals")
rexIgnore2 = regexp.MustCompile("\\$\\(LOCAL_INSTALLED_MODULE\\)")
)
const (
rexPairsHeader = 6
rexPairsAssign = 8
rexPairsFooter = 4
)
func (mk *MkFile) handleModule(scanner *myScanner, moduleType string) (*MkModule, error) {
mod := MkModule{Location: scanner.lineNo, Type: moduleType, Variables: make(map[string]string)}
includePath := ""
for scanner.Scan() {
line := scanner.Text()
if rexEmpty.MatchString(line) {
break
}
if m := rexAssign.FindStringSubmatchIndex(line); len(m) == rexPairsAssign {
v := line[m[2]:m[3]]
if line[m[4]:m[5]] == "+" {
mod.Variables[v] = mod.Variables[v] + line[m[6]:m[7]]
} else {
mod.Variables[v] = line[m[6]:m[7]]
}
} else if m := rexFooter.FindStringSubmatchIndex(line); len(m) == rexPairsFooter {
if includePath != "" {
return nil, fmt.Errorf("%d: second include for module", scanner.lineNo)
}
includePath = strings.TrimSpace(line[m[2]:m[3]])
if mod.Type == "" {
mod.Type = includePath
}
} else if mod.Type != "" {
mod.Extras = mod.Extras + 1
continue
} else if rexIgnore1.MatchString(line) {
continue
} else if rexIgnore2.MatchString(line) {
continue
} else {
return nil, fmt.Errorf("%d: unexpected line:\n%s", scanner.lineNo, line)
}
}
return &mod, scanner.Err()
}
func (mk *MkFile) ModulesByType(names []string) (sortedKeys []string, byType map[string][]string) {
byType = make(map[string][]string)
for _, name := range names {
mod, ok := mk.Modules[name]
if !ok {
break
}
mt := mod.Type
v, ok := byType[mt]
if !ok {
sortedKeys = append(sortedKeys, mt)
}
byType[mt] = append(v, name)
}
sort.Strings(sortedKeys)
return
}
func (mk *MkFile) moduleKey(mod *MkModule) (string, error) {
// Synthesize unique module name.
name := mod.Variables["LOCAL_MODULE"]
if name == "" {
return "", fmt.Errorf("%d: the module above lacks LOCAL_MODULE assignment", mod.Location)
}
var buf strings.Builder
writebuf := func(chunks ...string) {
for _, s := range chunks {
buf.WriteString(s)
}
}
writebuf(name, "|class:", mod.Variables["LOCAL_MODULE_CLASS"])
if mod.Variables["LOCAL_IS_HOST_MODULE"] == "true" {
if v, ok := mod.Variables["LOCAL_MODULE_HOST_ARCH"]; ok {
writebuf("|host_arch:", v)
}
if v, ok := mod.Variables["LOCAL_MODULE_HOST_CROSS_ARCH"]; ok {
writebuf("|cross_arch:", v)
}
} else {
if v, ok := mod.Variables["LOCAL_MODULE_TARGET_ARCH"]; ok {
writebuf("|target_arch:", v)
} else {
writebuf("|target_arch:*")
}
}
return buf.String(), nil
}
// ParseMkFile parses Android-TARGET.mk file generated by Android build
func ParseMkFile(source io.Reader) (*MkFile, error) {
scanner := &myScanner{bufio.NewScanner(source), 0}
buffer := make([]byte, 1000000000)
scanner.Scanner.Buffer(buffer, len(buffer))
mkFile := &MkFile{Modules: make(map[string]*MkModule)}
for scanner.Scan() {
line := scanner.Text()
m := rexHeader.FindStringSubmatchIndex(line)
if len(m) != rexPairsHeader {
continue
}
moduleType := ""
if m[4] >= 0 {
moduleType = line[m[4]:m[5]]
}
mod, err := mkFile.handleModule(scanner, moduleType)
if err != nil {
return mkFile, err
}
name, err := mkFile.moduleKey(mod)
if err != nil {
return mkFile, err
}
if old, found := mkFile.Modules[name]; found {
return mkFile, fmt.Errorf(":%d: module %s already found, diff: %s", old.Location, name, cmp.Diff(old, mod))
}
mkFile.Modules[name] = mod
}
return mkFile, scanner.Err()
}