254 lines
6.5 KiB
Go
254 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"android/bazel/mkcompare"
|
|
"bufio"
|
|
"encoding/json"
|
|
"flag"
|
|
"fmt"
|
|
"math"
|
|
"os"
|
|
"runtime"
|
|
"runtime/pprof"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`")
|
|
var memprofile = flag.String("memprofile", "", "write memory profile to `file`")
|
|
var ignoredVariables = flag.String("ignore_variables", "", "comma-separated list of variables to ignore")
|
|
var maxDiff = flag.Int("max", math.MaxInt, "stop after finding N different modules")
|
|
var showPerModuleDiffs = flag.Bool("show_module_diffs", false, "show per-module differences")
|
|
var showModulesPerType = flag.Bool("show_type_modules", false, "show modules for each differing type")
|
|
var jsonOut = flag.Bool("json", false, "generate JSON output")
|
|
var showSummary = flag.Bool("show_summary", true, "show summary")
|
|
var ignoredVarSet map[string]bool
|
|
|
|
func maybeQuit(err error) {
|
|
if err == nil {
|
|
return
|
|
}
|
|
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
func parse(path string) *mkcompare.MkFile {
|
|
f, err := os.Open(path)
|
|
maybeQuit(err)
|
|
mkFile, err := mkcompare.ParseMkFile(bufio.NewReader(f))
|
|
maybeQuit(err)
|
|
f.Close()
|
|
return mkFile
|
|
}
|
|
|
|
func processArgs() {
|
|
flag.Usage = func() {
|
|
fmt.Fprintln(os.Stderr, `usage: mkcompare <options> refMkFile mkFile`)
|
|
flag.PrintDefaults()
|
|
os.Exit(2)
|
|
}
|
|
flag.Parse()
|
|
if len(flag.Args()) != 2 {
|
|
flag.Usage()
|
|
}
|
|
if *jsonOut {
|
|
*showPerModuleDiffs = false
|
|
*showModulesPerType = false
|
|
*showSummary = false
|
|
}
|
|
}
|
|
|
|
func goParse(path string) chan *mkcompare.MkFile {
|
|
ch := make(chan *mkcompare.MkFile, 1)
|
|
go func() { ch <- parse(path) }()
|
|
return ch
|
|
}
|
|
|
|
func printVars(title string, modulesByVar map[string][]string, mkFile *mkcompare.MkFile) {
|
|
if len(modulesByVar) > 0 {
|
|
fmt.Println(title)
|
|
for varName, mods := range modulesByVar {
|
|
printModulesByType(fmt.Sprintf(" %s, by type:", varName), mods, mkFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
func printModulesByType(title string, moduleNames []string, mkFile *mkcompare.MkFile) {
|
|
// Indent all lines by the title's indent
|
|
prefix := title
|
|
for i, c := range title {
|
|
if string(c) != " " {
|
|
prefix = title[0:i]
|
|
break
|
|
}
|
|
}
|
|
fmt.Println(title)
|
|
sortedTypes, byType := mkFile.ModulesByType(moduleNames)
|
|
for _, typ := range sortedTypes {
|
|
fmt.Printf("%s %s (%d modules)\n", prefix, typ, len(byType[typ]))
|
|
if !*showPerModuleDiffs {
|
|
continue
|
|
}
|
|
for _, m := range byType[typ] {
|
|
fmt.Println(prefix, " ", m)
|
|
}
|
|
}
|
|
}
|
|
|
|
type diffMod struct {
|
|
Name string
|
|
mkcompare.MkModuleDiff
|
|
RefLocation int
|
|
OurLocation int
|
|
Type string
|
|
ReferenceType string `json:",omitempty"`
|
|
}
|
|
|
|
type missingOrExtraMod struct {
|
|
Name string
|
|
Location int
|
|
Type string
|
|
}
|
|
|
|
type Diff struct {
|
|
RefPath string
|
|
OurPath string
|
|
ExtraModules []missingOrExtraMod `json:",omitempty"`
|
|
MissingModules []missingOrExtraMod `json:",omitempty"`
|
|
DiffModules []diffMod `json:",omitempty"`
|
|
}
|
|
|
|
func process(refMkFile, ourMkFile *mkcompare.MkFile) bool {
|
|
diff := Diff{RefPath: refMkFile.Path, OurPath: ourMkFile.Path}
|
|
missing, common, extra :=
|
|
mkcompare.Classify(refMkFile.Modules, ourMkFile.Modules, func(_ string) bool { return true })
|
|
|
|
sort.Strings(missing)
|
|
if len(missing) > 0 {
|
|
if *showSummary {
|
|
printModulesByType(fmt.Sprintf("%d missing modules, by type:", len(missing)),
|
|
missing, refMkFile)
|
|
}
|
|
if *jsonOut {
|
|
for _, name := range missing {
|
|
mod := refMkFile.Modules[name]
|
|
diff.MissingModules = append(diff.MissingModules,
|
|
missingOrExtraMod{name, mod.Location, mod.Type})
|
|
}
|
|
}
|
|
}
|
|
|
|
sort.Strings(extra)
|
|
if len(extra) > 0 {
|
|
if *showSummary {
|
|
printModulesByType(fmt.Sprintf("%d extra modules, by type:", len(extra)), extra, ourMkFile)
|
|
}
|
|
if *jsonOut {
|
|
for _, name := range extra {
|
|
mod := ourMkFile.Modules[name]
|
|
diff.ExtraModules = append(diff.ExtraModules,
|
|
missingOrExtraMod{name, mod.Location, mod.Type})
|
|
}
|
|
}
|
|
}
|
|
filesAreEqual := len(diff.MissingModules)+len(diff.ExtraModules) == 0
|
|
|
|
nDiff := 0
|
|
sort.Strings(common)
|
|
filterVars := func(name string) bool {
|
|
_, ok := ignoredVarSet[name]
|
|
return !ok
|
|
}
|
|
var missingVariables = make(map[string][]string)
|
|
var extraVariables = make(map[string][]string)
|
|
var diffVariables = make(map[string][]string)
|
|
for _, name := range common {
|
|
d := mkcompare.Compare(refMkFile.Modules[name], ourMkFile.Modules[name], filterVars)
|
|
if d.Empty() {
|
|
continue
|
|
}
|
|
filesAreEqual = false
|
|
var refType string
|
|
if d.Ref.Type != d.Our.Type {
|
|
refType = d.Ref.Type
|
|
}
|
|
if *jsonOut {
|
|
diff.DiffModules = append(diff.DiffModules, diffMod{
|
|
MkModuleDiff: d,
|
|
Name: name,
|
|
RefLocation: d.Ref.Location,
|
|
OurLocation: d.Our.Location,
|
|
Type: d.Our.Type,
|
|
ReferenceType: refType,
|
|
})
|
|
}
|
|
nDiff = nDiff + 1
|
|
if nDiff >= *maxDiff {
|
|
fmt.Printf("Only the first %d module diffs are processed\n", *maxDiff)
|
|
break
|
|
}
|
|
addToDiffList := func(d map[string][]string, items []string) {
|
|
if len(items) == 0 {
|
|
return
|
|
}
|
|
for _, v := range items {
|
|
d[v] = append(d[v], name)
|
|
}
|
|
}
|
|
addToDiffList(missingVariables, d.MissingVars)
|
|
addToDiffList(extraVariables, d.ExtraVars)
|
|
for _, dv := range d.DiffVars {
|
|
diffVariables[dv.Name] = append(diffVariables[dv.Name], name)
|
|
}
|
|
if *showPerModuleDiffs {
|
|
fmt.Println()
|
|
d.Print(os.Stdout, name)
|
|
}
|
|
}
|
|
if *showSummary {
|
|
printVars(fmt.Sprintf("\nMissing variables (%d):", len(missingVariables)), missingVariables, refMkFile)
|
|
printVars(fmt.Sprintf("\nExtra variables (%d):", len(extraVariables)), extraVariables, ourMkFile)
|
|
printVars(fmt.Sprintf("\nDiff variables: (%d)", len(diffVariables)), diffVariables, refMkFile)
|
|
}
|
|
if *jsonOut {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
enc.Encode(diff)
|
|
}
|
|
return filesAreEqual
|
|
}
|
|
|
|
func main() {
|
|
processArgs()
|
|
if *cpuprofile != "" {
|
|
f, err := os.Create(*cpuprofile)
|
|
maybeQuit(err)
|
|
defer f.Close() // error handling omitted for example
|
|
maybeQuit(pprof.StartCPUProfile(f))
|
|
defer pprof.StopCPUProfile()
|
|
}
|
|
chRef := goParse(flag.Arg(0))
|
|
chNew := goParse(flag.Arg(1))
|
|
if *ignoredVariables != "" {
|
|
ignoredVarSet = make(map[string]bool)
|
|
for _, v := range strings.Split(*ignoredVariables, ",") {
|
|
ignoredVarSet[v] = true
|
|
}
|
|
}
|
|
refMkFile, newMkFile := <-chRef, <-chNew
|
|
refMkFile.Path = flag.Arg(0)
|
|
newMkFile.Path = flag.Arg(1)
|
|
equal := process(refMkFile, newMkFile)
|
|
if *memprofile != "" {
|
|
f, err := os.Create(*memprofile)
|
|
maybeQuit(err)
|
|
defer f.Close() // error handling omitted for example
|
|
runtime.GC() // get up-to-date statistics
|
|
maybeQuit(pprof.WriteHeapProfile(f))
|
|
}
|
|
if !equal {
|
|
os.Exit(2)
|
|
}
|
|
}
|