1*7594170eSAndroid Build Coastguard Workerpackage main 2*7594170eSAndroid Build Coastguard Worker 3*7594170eSAndroid Build Coastguard Workerimport ( 4*7594170eSAndroid Build Coastguard Worker "android/bazel/mkcompare" 5*7594170eSAndroid Build Coastguard Worker "bufio" 6*7594170eSAndroid Build Coastguard Worker "encoding/json" 7*7594170eSAndroid Build Coastguard Worker "flag" 8*7594170eSAndroid Build Coastguard Worker "fmt" 9*7594170eSAndroid Build Coastguard Worker "math" 10*7594170eSAndroid Build Coastguard Worker "os" 11*7594170eSAndroid Build Coastguard Worker "runtime" 12*7594170eSAndroid Build Coastguard Worker "runtime/pprof" 13*7594170eSAndroid Build Coastguard Worker "sort" 14*7594170eSAndroid Build Coastguard Worker "strings" 15*7594170eSAndroid Build Coastguard Worker) 16*7594170eSAndroid Build Coastguard Worker 17*7594170eSAndroid Build Coastguard Workervar cpuprofile = flag.String("cpuprofile", "", "write cpu profile to `file`") 18*7594170eSAndroid Build Coastguard Workervar memprofile = flag.String("memprofile", "", "write memory profile to `file`") 19*7594170eSAndroid Build Coastguard Workervar ignoredVariables = flag.String("ignore_variables", "", "comma-separated list of variables to ignore") 20*7594170eSAndroid Build Coastguard Workervar maxDiff = flag.Int("max", math.MaxInt, "stop after finding N different modules") 21*7594170eSAndroid Build Coastguard Workervar showPerModuleDiffs = flag.Bool("show_module_diffs", false, "show per-module differences") 22*7594170eSAndroid Build Coastguard Workervar showModulesPerType = flag.Bool("show_type_modules", false, "show modules for each differing type") 23*7594170eSAndroid Build Coastguard Workervar jsonOut = flag.Bool("json", false, "generate JSON output") 24*7594170eSAndroid Build Coastguard Workervar showSummary = flag.Bool("show_summary", true, "show summary") 25*7594170eSAndroid Build Coastguard Workervar ignoredVarSet map[string]bool 26*7594170eSAndroid Build Coastguard Worker 27*7594170eSAndroid Build Coastguard Workerfunc maybeQuit(err error) { 28*7594170eSAndroid Build Coastguard Worker if err == nil { 29*7594170eSAndroid Build Coastguard Worker return 30*7594170eSAndroid Build Coastguard Worker } 31*7594170eSAndroid Build Coastguard Worker 32*7594170eSAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, err) 33*7594170eSAndroid Build Coastguard Worker os.Exit(1) 34*7594170eSAndroid Build Coastguard Worker} 35*7594170eSAndroid Build Coastguard Worker 36*7594170eSAndroid Build Coastguard Workerfunc parse(path string) *mkcompare.MkFile { 37*7594170eSAndroid Build Coastguard Worker f, err := os.Open(path) 38*7594170eSAndroid Build Coastguard Worker maybeQuit(err) 39*7594170eSAndroid Build Coastguard Worker mkFile, err := mkcompare.ParseMkFile(bufio.NewReader(f)) 40*7594170eSAndroid Build Coastguard Worker maybeQuit(err) 41*7594170eSAndroid Build Coastguard Worker f.Close() 42*7594170eSAndroid Build Coastguard Worker return mkFile 43*7594170eSAndroid Build Coastguard Worker} 44*7594170eSAndroid Build Coastguard Worker 45*7594170eSAndroid Build Coastguard Workerfunc processArgs() { 46*7594170eSAndroid Build Coastguard Worker flag.Usage = func() { 47*7594170eSAndroid Build Coastguard Worker fmt.Fprintln(os.Stderr, `usage: mkcompare <options> refMkFile mkFile`) 48*7594170eSAndroid Build Coastguard Worker flag.PrintDefaults() 49*7594170eSAndroid Build Coastguard Worker os.Exit(2) 50*7594170eSAndroid Build Coastguard Worker } 51*7594170eSAndroid Build Coastguard Worker flag.Parse() 52*7594170eSAndroid Build Coastguard Worker if len(flag.Args()) != 2 { 53*7594170eSAndroid Build Coastguard Worker flag.Usage() 54*7594170eSAndroid Build Coastguard Worker } 55*7594170eSAndroid Build Coastguard Worker if *jsonOut { 56*7594170eSAndroid Build Coastguard Worker *showPerModuleDiffs = false 57*7594170eSAndroid Build Coastguard Worker *showModulesPerType = false 58*7594170eSAndroid Build Coastguard Worker *showSummary = false 59*7594170eSAndroid Build Coastguard Worker } 60*7594170eSAndroid Build Coastguard Worker} 61*7594170eSAndroid Build Coastguard Worker 62*7594170eSAndroid Build Coastguard Workerfunc goParse(path string) chan *mkcompare.MkFile { 63*7594170eSAndroid Build Coastguard Worker ch := make(chan *mkcompare.MkFile, 1) 64*7594170eSAndroid Build Coastguard Worker go func() { ch <- parse(path) }() 65*7594170eSAndroid Build Coastguard Worker return ch 66*7594170eSAndroid Build Coastguard Worker} 67*7594170eSAndroid Build Coastguard Worker 68*7594170eSAndroid Build Coastguard Workerfunc printVars(title string, modulesByVar map[string][]string, mkFile *mkcompare.MkFile) { 69*7594170eSAndroid Build Coastguard Worker if len(modulesByVar) > 0 { 70*7594170eSAndroid Build Coastguard Worker fmt.Println(title) 71*7594170eSAndroid Build Coastguard Worker for varName, mods := range modulesByVar { 72*7594170eSAndroid Build Coastguard Worker printModulesByType(fmt.Sprintf(" %s, by type:", varName), mods, mkFile) 73*7594170eSAndroid Build Coastguard Worker } 74*7594170eSAndroid Build Coastguard Worker } 75*7594170eSAndroid Build Coastguard Worker} 76*7594170eSAndroid Build Coastguard Worker 77*7594170eSAndroid Build Coastguard Workerfunc printModulesByType(title string, moduleNames []string, mkFile *mkcompare.MkFile) { 78*7594170eSAndroid Build Coastguard Worker // Indent all lines by the title's indent 79*7594170eSAndroid Build Coastguard Worker prefix := title 80*7594170eSAndroid Build Coastguard Worker for i, c := range title { 81*7594170eSAndroid Build Coastguard Worker if string(c) != " " { 82*7594170eSAndroid Build Coastguard Worker prefix = title[0:i] 83*7594170eSAndroid Build Coastguard Worker break 84*7594170eSAndroid Build Coastguard Worker } 85*7594170eSAndroid Build Coastguard Worker } 86*7594170eSAndroid Build Coastguard Worker fmt.Println(title) 87*7594170eSAndroid Build Coastguard Worker sortedTypes, byType := mkFile.ModulesByType(moduleNames) 88*7594170eSAndroid Build Coastguard Worker for _, typ := range sortedTypes { 89*7594170eSAndroid Build Coastguard Worker fmt.Printf("%s %s (%d modules)\n", prefix, typ, len(byType[typ])) 90*7594170eSAndroid Build Coastguard Worker if !*showPerModuleDiffs { 91*7594170eSAndroid Build Coastguard Worker continue 92*7594170eSAndroid Build Coastguard Worker } 93*7594170eSAndroid Build Coastguard Worker for _, m := range byType[typ] { 94*7594170eSAndroid Build Coastguard Worker fmt.Println(prefix, " ", m) 95*7594170eSAndroid Build Coastguard Worker } 96*7594170eSAndroid Build Coastguard Worker } 97*7594170eSAndroid Build Coastguard Worker} 98*7594170eSAndroid Build Coastguard Worker 99*7594170eSAndroid Build Coastguard Workertype diffMod struct { 100*7594170eSAndroid Build Coastguard Worker Name string 101*7594170eSAndroid Build Coastguard Worker mkcompare.MkModuleDiff 102*7594170eSAndroid Build Coastguard Worker RefLocation int 103*7594170eSAndroid Build Coastguard Worker OurLocation int 104*7594170eSAndroid Build Coastguard Worker Type string 105*7594170eSAndroid Build Coastguard Worker ReferenceType string `json:",omitempty"` 106*7594170eSAndroid Build Coastguard Worker} 107*7594170eSAndroid Build Coastguard Worker 108*7594170eSAndroid Build Coastguard Workertype missingOrExtraMod struct { 109*7594170eSAndroid Build Coastguard Worker Name string 110*7594170eSAndroid Build Coastguard Worker Location int 111*7594170eSAndroid Build Coastguard Worker Type string 112*7594170eSAndroid Build Coastguard Worker} 113*7594170eSAndroid Build Coastguard Worker 114*7594170eSAndroid Build Coastguard Workertype Diff struct { 115*7594170eSAndroid Build Coastguard Worker RefPath string 116*7594170eSAndroid Build Coastguard Worker OurPath string 117*7594170eSAndroid Build Coastguard Worker ExtraModules []missingOrExtraMod `json:",omitempty"` 118*7594170eSAndroid Build Coastguard Worker MissingModules []missingOrExtraMod `json:",omitempty"` 119*7594170eSAndroid Build Coastguard Worker DiffModules []diffMod `json:",omitempty"` 120*7594170eSAndroid Build Coastguard Worker} 121*7594170eSAndroid Build Coastguard Worker 122*7594170eSAndroid Build Coastguard Workerfunc process(refMkFile, ourMkFile *mkcompare.MkFile) bool { 123*7594170eSAndroid Build Coastguard Worker diff := Diff{RefPath: refMkFile.Path, OurPath: ourMkFile.Path} 124*7594170eSAndroid Build Coastguard Worker missing, common, extra := 125*7594170eSAndroid Build Coastguard Worker mkcompare.Classify(refMkFile.Modules, ourMkFile.Modules, func(_ string) bool { return true }) 126*7594170eSAndroid Build Coastguard Worker 127*7594170eSAndroid Build Coastguard Worker sort.Strings(missing) 128*7594170eSAndroid Build Coastguard Worker if len(missing) > 0 { 129*7594170eSAndroid Build Coastguard Worker if *showSummary { 130*7594170eSAndroid Build Coastguard Worker printModulesByType(fmt.Sprintf("%d missing modules, by type:", len(missing)), 131*7594170eSAndroid Build Coastguard Worker missing, refMkFile) 132*7594170eSAndroid Build Coastguard Worker } 133*7594170eSAndroid Build Coastguard Worker if *jsonOut { 134*7594170eSAndroid Build Coastguard Worker for _, name := range missing { 135*7594170eSAndroid Build Coastguard Worker mod := refMkFile.Modules[name] 136*7594170eSAndroid Build Coastguard Worker diff.MissingModules = append(diff.MissingModules, 137*7594170eSAndroid Build Coastguard Worker missingOrExtraMod{name, mod.Location, mod.Type}) 138*7594170eSAndroid Build Coastguard Worker } 139*7594170eSAndroid Build Coastguard Worker } 140*7594170eSAndroid Build Coastguard Worker } 141*7594170eSAndroid Build Coastguard Worker 142*7594170eSAndroid Build Coastguard Worker sort.Strings(extra) 143*7594170eSAndroid Build Coastguard Worker if len(extra) > 0 { 144*7594170eSAndroid Build Coastguard Worker if *showSummary { 145*7594170eSAndroid Build Coastguard Worker printModulesByType(fmt.Sprintf("%d extra modules, by type:", len(extra)), extra, ourMkFile) 146*7594170eSAndroid Build Coastguard Worker } 147*7594170eSAndroid Build Coastguard Worker if *jsonOut { 148*7594170eSAndroid Build Coastguard Worker for _, name := range extra { 149*7594170eSAndroid Build Coastguard Worker mod := ourMkFile.Modules[name] 150*7594170eSAndroid Build Coastguard Worker diff.ExtraModules = append(diff.ExtraModules, 151*7594170eSAndroid Build Coastguard Worker missingOrExtraMod{name, mod.Location, mod.Type}) 152*7594170eSAndroid Build Coastguard Worker } 153*7594170eSAndroid Build Coastguard Worker } 154*7594170eSAndroid Build Coastguard Worker } 155*7594170eSAndroid Build Coastguard Worker filesAreEqual := len(diff.MissingModules)+len(diff.ExtraModules) == 0 156*7594170eSAndroid Build Coastguard Worker 157*7594170eSAndroid Build Coastguard Worker nDiff := 0 158*7594170eSAndroid Build Coastguard Worker sort.Strings(common) 159*7594170eSAndroid Build Coastguard Worker filterVars := func(name string) bool { 160*7594170eSAndroid Build Coastguard Worker _, ok := ignoredVarSet[name] 161*7594170eSAndroid Build Coastguard Worker return !ok 162*7594170eSAndroid Build Coastguard Worker } 163*7594170eSAndroid Build Coastguard Worker var missingVariables = make(map[string][]string) 164*7594170eSAndroid Build Coastguard Worker var extraVariables = make(map[string][]string) 165*7594170eSAndroid Build Coastguard Worker var diffVariables = make(map[string][]string) 166*7594170eSAndroid Build Coastguard Worker for _, name := range common { 167*7594170eSAndroid Build Coastguard Worker d := mkcompare.Compare(refMkFile.Modules[name], ourMkFile.Modules[name], filterVars) 168*7594170eSAndroid Build Coastguard Worker if d.Empty() { 169*7594170eSAndroid Build Coastguard Worker continue 170*7594170eSAndroid Build Coastguard Worker } 171*7594170eSAndroid Build Coastguard Worker filesAreEqual = false 172*7594170eSAndroid Build Coastguard Worker var refType string 173*7594170eSAndroid Build Coastguard Worker if d.Ref.Type != d.Our.Type { 174*7594170eSAndroid Build Coastguard Worker refType = d.Ref.Type 175*7594170eSAndroid Build Coastguard Worker } 176*7594170eSAndroid Build Coastguard Worker if *jsonOut { 177*7594170eSAndroid Build Coastguard Worker diff.DiffModules = append(diff.DiffModules, diffMod{ 178*7594170eSAndroid Build Coastguard Worker MkModuleDiff: d, 179*7594170eSAndroid Build Coastguard Worker Name: name, 180*7594170eSAndroid Build Coastguard Worker RefLocation: d.Ref.Location, 181*7594170eSAndroid Build Coastguard Worker OurLocation: d.Our.Location, 182*7594170eSAndroid Build Coastguard Worker Type: d.Our.Type, 183*7594170eSAndroid Build Coastguard Worker ReferenceType: refType, 184*7594170eSAndroid Build Coastguard Worker }) 185*7594170eSAndroid Build Coastguard Worker } 186*7594170eSAndroid Build Coastguard Worker nDiff = nDiff + 1 187*7594170eSAndroid Build Coastguard Worker if nDiff >= *maxDiff { 188*7594170eSAndroid Build Coastguard Worker fmt.Printf("Only the first %d module diffs are processed\n", *maxDiff) 189*7594170eSAndroid Build Coastguard Worker break 190*7594170eSAndroid Build Coastguard Worker } 191*7594170eSAndroid Build Coastguard Worker addToDiffList := func(d map[string][]string, items []string) { 192*7594170eSAndroid Build Coastguard Worker if len(items) == 0 { 193*7594170eSAndroid Build Coastguard Worker return 194*7594170eSAndroid Build Coastguard Worker } 195*7594170eSAndroid Build Coastguard Worker for _, v := range items { 196*7594170eSAndroid Build Coastguard Worker d[v] = append(d[v], name) 197*7594170eSAndroid Build Coastguard Worker } 198*7594170eSAndroid Build Coastguard Worker } 199*7594170eSAndroid Build Coastguard Worker addToDiffList(missingVariables, d.MissingVars) 200*7594170eSAndroid Build Coastguard Worker addToDiffList(extraVariables, d.ExtraVars) 201*7594170eSAndroid Build Coastguard Worker for _, dv := range d.DiffVars { 202*7594170eSAndroid Build Coastguard Worker diffVariables[dv.Name] = append(diffVariables[dv.Name], name) 203*7594170eSAndroid Build Coastguard Worker } 204*7594170eSAndroid Build Coastguard Worker if *showPerModuleDiffs { 205*7594170eSAndroid Build Coastguard Worker fmt.Println() 206*7594170eSAndroid Build Coastguard Worker d.Print(os.Stdout, name) 207*7594170eSAndroid Build Coastguard Worker } 208*7594170eSAndroid Build Coastguard Worker } 209*7594170eSAndroid Build Coastguard Worker if *showSummary { 210*7594170eSAndroid Build Coastguard Worker printVars(fmt.Sprintf("\nMissing variables (%d):", len(missingVariables)), missingVariables, refMkFile) 211*7594170eSAndroid Build Coastguard Worker printVars(fmt.Sprintf("\nExtra variables (%d):", len(extraVariables)), extraVariables, ourMkFile) 212*7594170eSAndroid Build Coastguard Worker printVars(fmt.Sprintf("\nDiff variables: (%d)", len(diffVariables)), diffVariables, refMkFile) 213*7594170eSAndroid Build Coastguard Worker } 214*7594170eSAndroid Build Coastguard Worker if *jsonOut { 215*7594170eSAndroid Build Coastguard Worker enc := json.NewEncoder(os.Stdout) 216*7594170eSAndroid Build Coastguard Worker enc.SetIndent("", " ") 217*7594170eSAndroid Build Coastguard Worker enc.Encode(diff) 218*7594170eSAndroid Build Coastguard Worker } 219*7594170eSAndroid Build Coastguard Worker return filesAreEqual 220*7594170eSAndroid Build Coastguard Worker} 221*7594170eSAndroid Build Coastguard Worker 222*7594170eSAndroid Build Coastguard Workerfunc main() { 223*7594170eSAndroid Build Coastguard Worker processArgs() 224*7594170eSAndroid Build Coastguard Worker if *cpuprofile != "" { 225*7594170eSAndroid Build Coastguard Worker f, err := os.Create(*cpuprofile) 226*7594170eSAndroid Build Coastguard Worker maybeQuit(err) 227*7594170eSAndroid Build Coastguard Worker defer f.Close() // error handling omitted for example 228*7594170eSAndroid Build Coastguard Worker maybeQuit(pprof.StartCPUProfile(f)) 229*7594170eSAndroid Build Coastguard Worker defer pprof.StopCPUProfile() 230*7594170eSAndroid Build Coastguard Worker } 231*7594170eSAndroid Build Coastguard Worker chRef := goParse(flag.Arg(0)) 232*7594170eSAndroid Build Coastguard Worker chNew := goParse(flag.Arg(1)) 233*7594170eSAndroid Build Coastguard Worker if *ignoredVariables != "" { 234*7594170eSAndroid Build Coastguard Worker ignoredVarSet = make(map[string]bool) 235*7594170eSAndroid Build Coastguard Worker for _, v := range strings.Split(*ignoredVariables, ",") { 236*7594170eSAndroid Build Coastguard Worker ignoredVarSet[v] = true 237*7594170eSAndroid Build Coastguard Worker } 238*7594170eSAndroid Build Coastguard Worker } 239*7594170eSAndroid Build Coastguard Worker refMkFile, newMkFile := <-chRef, <-chNew 240*7594170eSAndroid Build Coastguard Worker refMkFile.Path = flag.Arg(0) 241*7594170eSAndroid Build Coastguard Worker newMkFile.Path = flag.Arg(1) 242*7594170eSAndroid Build Coastguard Worker equal := process(refMkFile, newMkFile) 243*7594170eSAndroid Build Coastguard Worker if *memprofile != "" { 244*7594170eSAndroid Build Coastguard Worker f, err := os.Create(*memprofile) 245*7594170eSAndroid Build Coastguard Worker maybeQuit(err) 246*7594170eSAndroid Build Coastguard Worker defer f.Close() // error handling omitted for example 247*7594170eSAndroid Build Coastguard Worker runtime.GC() // get up-to-date statistics 248*7594170eSAndroid Build Coastguard Worker maybeQuit(pprof.WriteHeapProfile(f)) 249*7594170eSAndroid Build Coastguard Worker } 250*7594170eSAndroid Build Coastguard Worker if !equal { 251*7594170eSAndroid Build Coastguard Worker os.Exit(2) 252*7594170eSAndroid Build Coastguard Worker } 253*7594170eSAndroid Build Coastguard Worker} 254