xref: /aosp_15_r20/build/bazel/mkcompare/cmd/mkcompare.go (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
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