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