xref: /aosp_15_r20/build/bazel/mkcompare/mkdiff.go (revision 7594170e27e0732bc44b93d1440d87a54b6ffe7c)
1*7594170eSAndroid Build Coastguard Workerpackage mkcompare
2*7594170eSAndroid Build Coastguard Worker
3*7594170eSAndroid Build Coastguard Workerimport (
4*7594170eSAndroid Build Coastguard Worker	"fmt"
5*7594170eSAndroid Build Coastguard Worker	"io"
6*7594170eSAndroid Build Coastguard Worker	"regexp"
7*7594170eSAndroid Build Coastguard Worker	"sort"
8*7594170eSAndroid Build Coastguard Worker	"strings"
9*7594170eSAndroid Build Coastguard Worker)
10*7594170eSAndroid Build Coastguard Worker
11*7594170eSAndroid Build Coastguard Worker// Classify takes two maps with string keys and return the lists of left-only, common, and right-only keys
12*7594170eSAndroid Build Coastguard Workerfunc Classify[V interface{}](mLeft map[string]V, mRight map[string]V, varFilter func(_ string) bool) (left []string, common []string, right []string) {
13*7594170eSAndroid Build Coastguard Worker	for k := range mLeft {
14*7594170eSAndroid Build Coastguard Worker		if !varFilter(k) {
15*7594170eSAndroid Build Coastguard Worker			break
16*7594170eSAndroid Build Coastguard Worker		}
17*7594170eSAndroid Build Coastguard Worker		if _, ok := mRight[k]; ok {
18*7594170eSAndroid Build Coastguard Worker			common = append(common, k)
19*7594170eSAndroid Build Coastguard Worker		} else {
20*7594170eSAndroid Build Coastguard Worker			left = append(left, k)
21*7594170eSAndroid Build Coastguard Worker		}
22*7594170eSAndroid Build Coastguard Worker	}
23*7594170eSAndroid Build Coastguard Worker	for k := range mRight {
24*7594170eSAndroid Build Coastguard Worker		if !varFilter(k) {
25*7594170eSAndroid Build Coastguard Worker			break
26*7594170eSAndroid Build Coastguard Worker		}
27*7594170eSAndroid Build Coastguard Worker		if _, ok := mLeft[k]; !ok {
28*7594170eSAndroid Build Coastguard Worker			right = append(right, k)
29*7594170eSAndroid Build Coastguard Worker		}
30*7594170eSAndroid Build Coastguard Worker	}
31*7594170eSAndroid Build Coastguard Worker
32*7594170eSAndroid Build Coastguard Worker	return left, common, right
33*7594170eSAndroid Build Coastguard Worker}
34*7594170eSAndroid Build Coastguard Worker
35*7594170eSAndroid Build Coastguard Workervar normalizer = map[string]func(ref, our string) (string, string){
36*7594170eSAndroid Build Coastguard Worker	"LOCAL_SOONG_INSTALL_PAIRS":         normalizeInstallPairs,
37*7594170eSAndroid Build Coastguard Worker	"LOCAL_COMPATIBILITY_SUPPORT_FILES": normalizeInstallPairs,
38*7594170eSAndroid Build Coastguard Worker	"LOCAL_PREBUILT_MODULE_FILE":        normalizePrebuiltModuleFile,
39*7594170eSAndroid Build Coastguard Worker	"LOCAL_SOONG_CLASSES_JAR":           normalizePrebuiltModuleFile,
40*7594170eSAndroid Build Coastguard Worker	"LOCAL_SOONG_HEADER_JAR":            normalizePrebuiltModuleFile,
41*7594170eSAndroid Build Coastguard Worker}
42*7594170eSAndroid Build Coastguard Worker
43*7594170eSAndroid Build Coastguard Workerfunc normalizePrebuiltModuleFile(ref string, our string) (string, string) {
44*7594170eSAndroid Build Coastguard Worker	return strings.ReplaceAll(ref, "/bazelCombined/", "/combined/"), strings.ReplaceAll(our, "/bazelCombined/", "/combined/")
45*7594170eSAndroid Build Coastguard Worker}
46*7594170eSAndroid Build Coastguard Worker
47*7594170eSAndroid Build Coastguard Workervar rexRemoveInstallSource = regexp.MustCompile("([^ ]+:)")
48*7594170eSAndroid Build Coastguard Worker
49*7594170eSAndroid Build Coastguard Workerfunc normalizeInstallPairs(ref string, our string) (string, string) {
50*7594170eSAndroid Build Coastguard Worker	return rexRemoveInstallSource.ReplaceAllString(ref, ""), rexRemoveInstallSource.ReplaceAllString(our, "")
51*7594170eSAndroid Build Coastguard Worker}
52*7594170eSAndroid Build Coastguard Worker
53*7594170eSAndroid Build Coastguard Workertype MkVarDiff struct {
54*7594170eSAndroid Build Coastguard Worker	Name         string
55*7594170eSAndroid Build Coastguard Worker	MissingItems []string `json:",omitempty"`
56*7594170eSAndroid Build Coastguard Worker	ExtraItems   []string `json:",omitempty"`
57*7594170eSAndroid Build Coastguard Worker}
58*7594170eSAndroid Build Coastguard Worker
59*7594170eSAndroid Build Coastguard Worker// MkModuleDiff holds module difference between reference and our mkfile.
60*7594170eSAndroid Build Coastguard Workertype MkModuleDiff struct {
61*7594170eSAndroid Build Coastguard Worker	Ref          *MkModule   `json:"-"`
62*7594170eSAndroid Build Coastguard Worker	Our          *MkModule   `json:"-"`
63*7594170eSAndroid Build Coastguard Worker	MissingVars  []string    `json:",omitempty"`
64*7594170eSAndroid Build Coastguard Worker	ExtraVars    []string    `json:",omitempty"`
65*7594170eSAndroid Build Coastguard Worker	DiffVars     []MkVarDiff `json:",omitempty"`
66*7594170eSAndroid Build Coastguard Worker	TypeDiffers  bool        `json:",omitempty"`
67*7594170eSAndroid Build Coastguard Worker	ExtrasDiffer bool        `json:",omitempty"`
68*7594170eSAndroid Build Coastguard Worker}
69*7594170eSAndroid Build Coastguard Worker
70*7594170eSAndroid Build Coastguard Worker// Empty returns true if there is no difference
71*7594170eSAndroid Build Coastguard Workerfunc (d *MkModuleDiff) Empty() bool {
72*7594170eSAndroid Build Coastguard Worker	return !d.TypeDiffers && !d.ExtrasDiffer && len(d.MissingVars) == 0 && len(d.ExtraVars) == 0 && len(d.DiffVars) == 0
73*7594170eSAndroid Build Coastguard Worker}
74*7594170eSAndroid Build Coastguard Worker
75*7594170eSAndroid Build Coastguard Worker// Print prints the difference
76*7594170eSAndroid Build Coastguard Workerfunc (d *MkModuleDiff) Print(sink io.Writer, name string) {
77*7594170eSAndroid Build Coastguard Worker	if d.Empty() {
78*7594170eSAndroid Build Coastguard Worker		return
79*7594170eSAndroid Build Coastguard Worker	}
80*7594170eSAndroid Build Coastguard Worker	fmt.Fprintf(sink, "%s (ref line %d, our line %d):\n", name, d.Ref.Location, d.Our.Location)
81*7594170eSAndroid Build Coastguard Worker	if d.TypeDiffers {
82*7594170eSAndroid Build Coastguard Worker		fmt.Fprintf(sink, "  type %s <-> %s\n", d.Ref.Type, d.Our.Type)
83*7594170eSAndroid Build Coastguard Worker	}
84*7594170eSAndroid Build Coastguard Worker
85*7594170eSAndroid Build Coastguard Worker	if !d.ExtrasDiffer {
86*7594170eSAndroid Build Coastguard Worker		fmt.Fprintf(sink, "  extras %d <-> %d\n", d.Ref.Extras, d.Our.Extras)
87*7594170eSAndroid Build Coastguard Worker	}
88*7594170eSAndroid Build Coastguard Worker
89*7594170eSAndroid Build Coastguard Worker	if len(d.MissingVars)+len(d.DiffVars) > 0 {
90*7594170eSAndroid Build Coastguard Worker		fmt.Fprintf(sink, "  variables:\n")
91*7594170eSAndroid Build Coastguard Worker		if len(d.MissingVars) > 0 {
92*7594170eSAndroid Build Coastguard Worker			fmt.Fprintf(sink, "    -%v\n", d.MissingVars)
93*7594170eSAndroid Build Coastguard Worker		}
94*7594170eSAndroid Build Coastguard Worker		if len(d.ExtraVars) > 0 {
95*7594170eSAndroid Build Coastguard Worker			fmt.Fprintf(sink, "    +%v\n", d.ExtraVars)
96*7594170eSAndroid Build Coastguard Worker		}
97*7594170eSAndroid Build Coastguard Worker	}
98*7594170eSAndroid Build Coastguard Worker	for _, vdiff := range d.DiffVars {
99*7594170eSAndroid Build Coastguard Worker		fmt.Printf("   %s value:\n", vdiff.Name)
100*7594170eSAndroid Build Coastguard Worker		if len(vdiff.MissingItems) > 0 {
101*7594170eSAndroid Build Coastguard Worker			fmt.Printf("    -%v\n", vdiff.MissingItems)
102*7594170eSAndroid Build Coastguard Worker		}
103*7594170eSAndroid Build Coastguard Worker		if len(vdiff.ExtraItems) > 0 {
104*7594170eSAndroid Build Coastguard Worker			fmt.Printf("    +%v\n", vdiff.ExtraItems)
105*7594170eSAndroid Build Coastguard Worker		}
106*7594170eSAndroid Build Coastguard Worker	}
107*7594170eSAndroid Build Coastguard Worker}
108*7594170eSAndroid Build Coastguard Worker
109*7594170eSAndroid Build Coastguard Worker// Compare returns the difference for a module. Only the variables filtered by the given
110*7594170eSAndroid Build Coastguard Worker// function are considered.
111*7594170eSAndroid Build Coastguard Workerfunc Compare(refMod *MkModule, ourMod *MkModule, varFilter func(string) bool) MkModuleDiff {
112*7594170eSAndroid Build Coastguard Worker	d := MkModuleDiff{
113*7594170eSAndroid Build Coastguard Worker		Ref:          refMod,
114*7594170eSAndroid Build Coastguard Worker		Our:          ourMod,
115*7594170eSAndroid Build Coastguard Worker		TypeDiffers:  refMod.Type != ourMod.Type,
116*7594170eSAndroid Build Coastguard Worker		ExtrasDiffer: refMod.Extras != ourMod.Extras,
117*7594170eSAndroid Build Coastguard Worker	}
118*7594170eSAndroid Build Coastguard Worker	var common []string
119*7594170eSAndroid Build Coastguard Worker	d.MissingVars, common, d.ExtraVars = Classify(d.Ref.Variables, d.Our.Variables, varFilter)
120*7594170eSAndroid Build Coastguard Worker
121*7594170eSAndroid Build Coastguard Worker	if len(common) > 0 {
122*7594170eSAndroid Build Coastguard Worker		for _, v := range common {
123*7594170eSAndroid Build Coastguard Worker			doSort := true // TODO(asmundak): find if for some variables the value should not be sorted
124*7594170eSAndroid Build Coastguard Worker			refValue := d.Ref.Variables[v]
125*7594170eSAndroid Build Coastguard Worker			ourValue := d.Our.Variables[v]
126*7594170eSAndroid Build Coastguard Worker			if f, ok := normalizer[v]; ok {
127*7594170eSAndroid Build Coastguard Worker				refValue, ourValue = f(refValue, ourValue)
128*7594170eSAndroid Build Coastguard Worker			}
129*7594170eSAndroid Build Coastguard Worker			missingItems, extraItems := compareVariableValues(refValue, ourValue, doSort)
130*7594170eSAndroid Build Coastguard Worker			if len(missingItems)+len(extraItems) > 0 {
131*7594170eSAndroid Build Coastguard Worker				d.DiffVars = append(d.DiffVars, MkVarDiff{
132*7594170eSAndroid Build Coastguard Worker					Name:         v,
133*7594170eSAndroid Build Coastguard Worker					MissingItems: missingItems,
134*7594170eSAndroid Build Coastguard Worker					ExtraItems:   extraItems,
135*7594170eSAndroid Build Coastguard Worker				})
136*7594170eSAndroid Build Coastguard Worker			}
137*7594170eSAndroid Build Coastguard Worker		}
138*7594170eSAndroid Build Coastguard Worker	}
139*7594170eSAndroid Build Coastguard Worker	return d
140*7594170eSAndroid Build Coastguard Worker}
141*7594170eSAndroid Build Coastguard Worker
142*7594170eSAndroid Build Coastguard Workerfunc compareVariableValues(ref string, our string, sortItems bool) ([]string, []string) {
143*7594170eSAndroid Build Coastguard Worker	refTokens := strings.Split(ref, " ")
144*7594170eSAndroid Build Coastguard Worker	ourTokens := strings.Split(our, " ")
145*7594170eSAndroid Build Coastguard Worker	if sortItems {
146*7594170eSAndroid Build Coastguard Worker		sort.Strings(refTokens)
147*7594170eSAndroid Build Coastguard Worker		sort.Strings(ourTokens)
148*7594170eSAndroid Build Coastguard Worker	}
149*7594170eSAndroid Build Coastguard Worker	var missing []string
150*7594170eSAndroid Build Coastguard Worker	var extra []string
151*7594170eSAndroid Build Coastguard Worker	refStream := &tokenStream{refTokens, 0}
152*7594170eSAndroid Build Coastguard Worker	ourStream := &tokenStream{ourTokens, 0}
153*7594170eSAndroid Build Coastguard Worker	refToken := refStream.next()
154*7594170eSAndroid Build Coastguard Worker	ourToken := ourStream.next()
155*7594170eSAndroid Build Coastguard Worker	compare := 0
156*7594170eSAndroid Build Coastguard Worker	for refToken != tsEOF || ourToken != tsEOF {
157*7594170eSAndroid Build Coastguard Worker		if refToken == tsEOF {
158*7594170eSAndroid Build Coastguard Worker			compare = 1
159*7594170eSAndroid Build Coastguard Worker		} else if ourToken == tsEOF {
160*7594170eSAndroid Build Coastguard Worker			compare = -1
161*7594170eSAndroid Build Coastguard Worker		} else {
162*7594170eSAndroid Build Coastguard Worker			compare = 0
163*7594170eSAndroid Build Coastguard Worker			if refToken <= ourToken {
164*7594170eSAndroid Build Coastguard Worker				compare = -1
165*7594170eSAndroid Build Coastguard Worker			}
166*7594170eSAndroid Build Coastguard Worker			if refToken >= ourToken {
167*7594170eSAndroid Build Coastguard Worker				compare = compare + 1
168*7594170eSAndroid Build Coastguard Worker			}
169*7594170eSAndroid Build Coastguard Worker		}
170*7594170eSAndroid Build Coastguard Worker		switch compare {
171*7594170eSAndroid Build Coastguard Worker		case -1:
172*7594170eSAndroid Build Coastguard Worker			missing = append(missing, refToken)
173*7594170eSAndroid Build Coastguard Worker			refToken = refStream.next()
174*7594170eSAndroid Build Coastguard Worker		case 0:
175*7594170eSAndroid Build Coastguard Worker			refToken = refStream.next()
176*7594170eSAndroid Build Coastguard Worker			ourToken = ourStream.next()
177*7594170eSAndroid Build Coastguard Worker		case 1:
178*7594170eSAndroid Build Coastguard Worker			extra = append(extra, ourToken)
179*7594170eSAndroid Build Coastguard Worker			ourToken = ourStream.next()
180*7594170eSAndroid Build Coastguard Worker		}
181*7594170eSAndroid Build Coastguard Worker	}
182*7594170eSAndroid Build Coastguard Worker	return missing, extra
183*7594170eSAndroid Build Coastguard Worker}
184*7594170eSAndroid Build Coastguard Worker
185*7594170eSAndroid Build Coastguard Worker// Auxiliary stuff used to find the difference
186*7594170eSAndroid Build Coastguard Workerconst tsEOF = " "
187*7594170eSAndroid Build Coastguard Worker
188*7594170eSAndroid Build Coastguard Workertype tokenStream struct {
189*7594170eSAndroid Build Coastguard Worker	tokens  []string
190*7594170eSAndroid Build Coastguard Worker	current int
191*7594170eSAndroid Build Coastguard Worker}
192*7594170eSAndroid Build Coastguard Worker
193*7594170eSAndroid Build Coastguard Workerfunc (ts *tokenStream) next() string {
194*7594170eSAndroid Build Coastguard Worker	if ts.current >= len(ts.tokens) {
195*7594170eSAndroid Build Coastguard Worker		return tsEOF
196*7594170eSAndroid Build Coastguard Worker	}
197*7594170eSAndroid Build Coastguard Worker	ret := ts.tokens[ts.current]
198*7594170eSAndroid Build Coastguard Worker	ts.current = ts.current + 1
199*7594170eSAndroid Build Coastguard Worker	return ret
200*7594170eSAndroid Build Coastguard Worker}
201