176 lines
4.2 KiB
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()
|
|
}
|