201 lines
5.4 KiB
Go
201 lines
5.4 KiB
Go
package mkcompare
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
// Classify takes two maps with string keys and return the lists of left-only, common, and right-only keys
|
|
func Classify[V interface{}](mLeft map[string]V, mRight map[string]V, varFilter func(_ string) bool) (left []string, common []string, right []string) {
|
|
for k := range mLeft {
|
|
if !varFilter(k) {
|
|
break
|
|
}
|
|
if _, ok := mRight[k]; ok {
|
|
common = append(common, k)
|
|
} else {
|
|
left = append(left, k)
|
|
}
|
|
}
|
|
for k := range mRight {
|
|
if !varFilter(k) {
|
|
break
|
|
}
|
|
if _, ok := mLeft[k]; !ok {
|
|
right = append(right, k)
|
|
}
|
|
}
|
|
|
|
return left, common, right
|
|
}
|
|
|
|
var normalizer = map[string]func(ref, our string) (string, string){
|
|
"LOCAL_SOONG_INSTALL_PAIRS": normalizeInstallPairs,
|
|
"LOCAL_COMPATIBILITY_SUPPORT_FILES": normalizeInstallPairs,
|
|
"LOCAL_PREBUILT_MODULE_FILE": normalizePrebuiltModuleFile,
|
|
"LOCAL_SOONG_CLASSES_JAR": normalizePrebuiltModuleFile,
|
|
"LOCAL_SOONG_HEADER_JAR": normalizePrebuiltModuleFile,
|
|
}
|
|
|
|
func normalizePrebuiltModuleFile(ref string, our string) (string, string) {
|
|
return strings.ReplaceAll(ref, "/bazelCombined/", "/combined/"), strings.ReplaceAll(our, "/bazelCombined/", "/combined/")
|
|
}
|
|
|
|
var rexRemoveInstallSource = regexp.MustCompile("([^ ]+:)")
|
|
|
|
func normalizeInstallPairs(ref string, our string) (string, string) {
|
|
return rexRemoveInstallSource.ReplaceAllString(ref, ""), rexRemoveInstallSource.ReplaceAllString(our, "")
|
|
}
|
|
|
|
type MkVarDiff struct {
|
|
Name string
|
|
MissingItems []string `json:",omitempty"`
|
|
ExtraItems []string `json:",omitempty"`
|
|
}
|
|
|
|
// MkModuleDiff holds module difference between reference and our mkfile.
|
|
type MkModuleDiff struct {
|
|
Ref *MkModule `json:"-"`
|
|
Our *MkModule `json:"-"`
|
|
MissingVars []string `json:",omitempty"`
|
|
ExtraVars []string `json:",omitempty"`
|
|
DiffVars []MkVarDiff `json:",omitempty"`
|
|
TypeDiffers bool `json:",omitempty"`
|
|
ExtrasDiffer bool `json:",omitempty"`
|
|
}
|
|
|
|
// Empty returns true if there is no difference
|
|
func (d *MkModuleDiff) Empty() bool {
|
|
return !d.TypeDiffers && !d.ExtrasDiffer && len(d.MissingVars) == 0 && len(d.ExtraVars) == 0 && len(d.DiffVars) == 0
|
|
}
|
|
|
|
// Print prints the difference
|
|
func (d *MkModuleDiff) Print(sink io.Writer, name string) {
|
|
if d.Empty() {
|
|
return
|
|
}
|
|
fmt.Fprintf(sink, "%s (ref line %d, our line %d):\n", name, d.Ref.Location, d.Our.Location)
|
|
if d.TypeDiffers {
|
|
fmt.Fprintf(sink, " type %s <-> %s\n", d.Ref.Type, d.Our.Type)
|
|
}
|
|
|
|
if !d.ExtrasDiffer {
|
|
fmt.Fprintf(sink, " extras %d <-> %d\n", d.Ref.Extras, d.Our.Extras)
|
|
}
|
|
|
|
if len(d.MissingVars)+len(d.DiffVars) > 0 {
|
|
fmt.Fprintf(sink, " variables:\n")
|
|
if len(d.MissingVars) > 0 {
|
|
fmt.Fprintf(sink, " -%v\n", d.MissingVars)
|
|
}
|
|
if len(d.ExtraVars) > 0 {
|
|
fmt.Fprintf(sink, " +%v\n", d.ExtraVars)
|
|
}
|
|
}
|
|
for _, vdiff := range d.DiffVars {
|
|
fmt.Printf(" %s value:\n", vdiff.Name)
|
|
if len(vdiff.MissingItems) > 0 {
|
|
fmt.Printf(" -%v\n", vdiff.MissingItems)
|
|
}
|
|
if len(vdiff.ExtraItems) > 0 {
|
|
fmt.Printf(" +%v\n", vdiff.ExtraItems)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compare returns the difference for a module. Only the variables filtered by the given
|
|
// function are considered.
|
|
func Compare(refMod *MkModule, ourMod *MkModule, varFilter func(string) bool) MkModuleDiff {
|
|
d := MkModuleDiff{
|
|
Ref: refMod,
|
|
Our: ourMod,
|
|
TypeDiffers: refMod.Type != ourMod.Type,
|
|
ExtrasDiffer: refMod.Extras != ourMod.Extras,
|
|
}
|
|
var common []string
|
|
d.MissingVars, common, d.ExtraVars = Classify(d.Ref.Variables, d.Our.Variables, varFilter)
|
|
|
|
if len(common) > 0 {
|
|
for _, v := range common {
|
|
doSort := true // TODO(asmundak): find if for some variables the value should not be sorted
|
|
refValue := d.Ref.Variables[v]
|
|
ourValue := d.Our.Variables[v]
|
|
if f, ok := normalizer[v]; ok {
|
|
refValue, ourValue = f(refValue, ourValue)
|
|
}
|
|
missingItems, extraItems := compareVariableValues(refValue, ourValue, doSort)
|
|
if len(missingItems)+len(extraItems) > 0 {
|
|
d.DiffVars = append(d.DiffVars, MkVarDiff{
|
|
Name: v,
|
|
MissingItems: missingItems,
|
|
ExtraItems: extraItems,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
return d
|
|
}
|
|
|
|
func compareVariableValues(ref string, our string, sortItems bool) ([]string, []string) {
|
|
refTokens := strings.Split(ref, " ")
|
|
ourTokens := strings.Split(our, " ")
|
|
if sortItems {
|
|
sort.Strings(refTokens)
|
|
sort.Strings(ourTokens)
|
|
}
|
|
var missing []string
|
|
var extra []string
|
|
refStream := &tokenStream{refTokens, 0}
|
|
ourStream := &tokenStream{ourTokens, 0}
|
|
refToken := refStream.next()
|
|
ourToken := ourStream.next()
|
|
compare := 0
|
|
for refToken != tsEOF || ourToken != tsEOF {
|
|
if refToken == tsEOF {
|
|
compare = 1
|
|
} else if ourToken == tsEOF {
|
|
compare = -1
|
|
} else {
|
|
compare = 0
|
|
if refToken <= ourToken {
|
|
compare = -1
|
|
}
|
|
if refToken >= ourToken {
|
|
compare = compare + 1
|
|
}
|
|
}
|
|
switch compare {
|
|
case -1:
|
|
missing = append(missing, refToken)
|
|
refToken = refStream.next()
|
|
case 0:
|
|
refToken = refStream.next()
|
|
ourToken = ourStream.next()
|
|
case 1:
|
|
extra = append(extra, ourToken)
|
|
ourToken = ourStream.next()
|
|
}
|
|
}
|
|
return missing, extra
|
|
}
|
|
|
|
// Auxiliary stuff used to find the difference
|
|
const tsEOF = " "
|
|
|
|
type tokenStream struct {
|
|
tokens []string
|
|
current int
|
|
}
|
|
|
|
func (ts *tokenStream) next() string {
|
|
if ts.current >= len(ts.tokens) {
|
|
return tsEOF
|
|
}
|
|
ret := ts.tokens[ts.current]
|
|
ts.current = ts.current + 1
|
|
return ret
|
|
}
|