1*7594170eSAndroid Build Coastguard Workerpackage mkcompare 2*7594170eSAndroid Build Coastguard Worker 3*7594170eSAndroid Build Coastguard Workerimport ( 4*7594170eSAndroid Build Coastguard Worker "bufio" 5*7594170eSAndroid Build Coastguard Worker "fmt" 6*7594170eSAndroid Build Coastguard Worker "github.com/google/go-cmp/cmp" 7*7594170eSAndroid Build Coastguard Worker "io" 8*7594170eSAndroid Build Coastguard Worker "regexp" 9*7594170eSAndroid Build Coastguard Worker "sort" 10*7594170eSAndroid Build Coastguard Worker "strings" 11*7594170eSAndroid Build Coastguard Worker) 12*7594170eSAndroid Build Coastguard Worker 13*7594170eSAndroid Build Coastguard Workertype MkVariable struct { 14*7594170eSAndroid Build Coastguard Worker Name string 15*7594170eSAndroid Build Coastguard Worker Value string 16*7594170eSAndroid Build Coastguard Worker} 17*7594170eSAndroid Build Coastguard Worker 18*7594170eSAndroid Build Coastguard Workertype MkModule struct { 19*7594170eSAndroid Build Coastguard Worker Type string 20*7594170eSAndroid Build Coastguard Worker Location int 21*7594170eSAndroid Build Coastguard Worker Extras int 22*7594170eSAndroid Build Coastguard Worker Variables map[string]string 23*7594170eSAndroid Build Coastguard Worker} 24*7594170eSAndroid Build Coastguard Worker 25*7594170eSAndroid Build Coastguard Workertype MkFile struct { 26*7594170eSAndroid Build Coastguard Worker Path string 27*7594170eSAndroid Build Coastguard Worker Modules map[string]*MkModule 28*7594170eSAndroid Build Coastguard Worker} 29*7594170eSAndroid Build Coastguard Worker 30*7594170eSAndroid Build Coastguard Workertype myScanner struct { 31*7594170eSAndroid Build Coastguard Worker *bufio.Scanner 32*7594170eSAndroid Build Coastguard Worker lineNo int 33*7594170eSAndroid Build Coastguard Worker} 34*7594170eSAndroid Build Coastguard Worker 35*7594170eSAndroid Build Coastguard Workerfunc (s *myScanner) Scan() bool { 36*7594170eSAndroid Build Coastguard Worker if s.Scanner.Scan() { 37*7594170eSAndroid Build Coastguard Worker s.lineNo = s.lineNo + 1 38*7594170eSAndroid Build Coastguard Worker return true 39*7594170eSAndroid Build Coastguard Worker } 40*7594170eSAndroid Build Coastguard Worker return false 41*7594170eSAndroid Build Coastguard Worker} 42*7594170eSAndroid Build Coastguard Worker 43*7594170eSAndroid Build Coastguard Workervar ( 44*7594170eSAndroid Build Coastguard Worker rexEmpty = regexp.MustCompile("^ *$") 45*7594170eSAndroid Build Coastguard Worker rexHeader = regexp.MustCompile("^include +\\Q$(CLEAR_VARS)\\E *(# *(.*))?") 46*7594170eSAndroid Build Coastguard Worker rexAssign = regexp.MustCompile("^ *(.*) ([:+])= *(.*)$") 47*7594170eSAndroid Build Coastguard Worker rexFooter = regexp.MustCompile("^-?include *(.*)$") 48*7594170eSAndroid Build Coastguard Worker rexIgnore1 = regexp.MustCompile("\\$\\(call dist-for-goals") 49*7594170eSAndroid Build Coastguard Worker rexIgnore2 = regexp.MustCompile("\\$\\(LOCAL_INSTALLED_MODULE\\)") 50*7594170eSAndroid Build Coastguard Worker) 51*7594170eSAndroid Build Coastguard Worker 52*7594170eSAndroid Build Coastguard Workerconst ( 53*7594170eSAndroid Build Coastguard Worker rexPairsHeader = 6 54*7594170eSAndroid Build Coastguard Worker rexPairsAssign = 8 55*7594170eSAndroid Build Coastguard Worker rexPairsFooter = 4 56*7594170eSAndroid Build Coastguard Worker) 57*7594170eSAndroid Build Coastguard Worker 58*7594170eSAndroid Build Coastguard Workerfunc (mk *MkFile) handleModule(scanner *myScanner, moduleType string) (*MkModule, error) { 59*7594170eSAndroid Build Coastguard Worker mod := MkModule{Location: scanner.lineNo, Type: moduleType, Variables: make(map[string]string)} 60*7594170eSAndroid Build Coastguard Worker includePath := "" 61*7594170eSAndroid Build Coastguard Worker for scanner.Scan() { 62*7594170eSAndroid Build Coastguard Worker line := scanner.Text() 63*7594170eSAndroid Build Coastguard Worker if rexEmpty.MatchString(line) { 64*7594170eSAndroid Build Coastguard Worker break 65*7594170eSAndroid Build Coastguard Worker } 66*7594170eSAndroid Build Coastguard Worker if m := rexAssign.FindStringSubmatchIndex(line); len(m) == rexPairsAssign { 67*7594170eSAndroid Build Coastguard Worker v := line[m[2]:m[3]] 68*7594170eSAndroid Build Coastguard Worker if line[m[4]:m[5]] == "+" { 69*7594170eSAndroid Build Coastguard Worker mod.Variables[v] = mod.Variables[v] + line[m[6]:m[7]] 70*7594170eSAndroid Build Coastguard Worker } else { 71*7594170eSAndroid Build Coastguard Worker mod.Variables[v] = line[m[6]:m[7]] 72*7594170eSAndroid Build Coastguard Worker } 73*7594170eSAndroid Build Coastguard Worker } else if m := rexFooter.FindStringSubmatchIndex(line); len(m) == rexPairsFooter { 74*7594170eSAndroid Build Coastguard Worker if includePath != "" { 75*7594170eSAndroid Build Coastguard Worker return nil, fmt.Errorf("%d: second include for module", scanner.lineNo) 76*7594170eSAndroid Build Coastguard Worker } 77*7594170eSAndroid Build Coastguard Worker includePath = strings.TrimSpace(line[m[2]:m[3]]) 78*7594170eSAndroid Build Coastguard Worker if mod.Type == "" { 79*7594170eSAndroid Build Coastguard Worker mod.Type = includePath 80*7594170eSAndroid Build Coastguard Worker } 81*7594170eSAndroid Build Coastguard Worker } else if mod.Type != "" { 82*7594170eSAndroid Build Coastguard Worker mod.Extras = mod.Extras + 1 83*7594170eSAndroid Build Coastguard Worker continue 84*7594170eSAndroid Build Coastguard Worker } else if rexIgnore1.MatchString(line) { 85*7594170eSAndroid Build Coastguard Worker continue 86*7594170eSAndroid Build Coastguard Worker } else if rexIgnore2.MatchString(line) { 87*7594170eSAndroid Build Coastguard Worker continue 88*7594170eSAndroid Build Coastguard Worker } else { 89*7594170eSAndroid Build Coastguard Worker return nil, fmt.Errorf("%d: unexpected line:\n%s", scanner.lineNo, line) 90*7594170eSAndroid Build Coastguard Worker } 91*7594170eSAndroid Build Coastguard Worker } 92*7594170eSAndroid Build Coastguard Worker return &mod, scanner.Err() 93*7594170eSAndroid Build Coastguard Worker} 94*7594170eSAndroid Build Coastguard Worker 95*7594170eSAndroid Build Coastguard Workerfunc (mk *MkFile) ModulesByType(names []string) (sortedKeys []string, byType map[string][]string) { 96*7594170eSAndroid Build Coastguard Worker byType = make(map[string][]string) 97*7594170eSAndroid Build Coastguard Worker for _, name := range names { 98*7594170eSAndroid Build Coastguard Worker mod, ok := mk.Modules[name] 99*7594170eSAndroid Build Coastguard Worker if !ok { 100*7594170eSAndroid Build Coastguard Worker break 101*7594170eSAndroid Build Coastguard Worker } 102*7594170eSAndroid Build Coastguard Worker mt := mod.Type 103*7594170eSAndroid Build Coastguard Worker v, ok := byType[mt] 104*7594170eSAndroid Build Coastguard Worker if !ok { 105*7594170eSAndroid Build Coastguard Worker sortedKeys = append(sortedKeys, mt) 106*7594170eSAndroid Build Coastguard Worker } 107*7594170eSAndroid Build Coastguard Worker byType[mt] = append(v, name) 108*7594170eSAndroid Build Coastguard Worker } 109*7594170eSAndroid Build Coastguard Worker sort.Strings(sortedKeys) 110*7594170eSAndroid Build Coastguard Worker return 111*7594170eSAndroid Build Coastguard Worker} 112*7594170eSAndroid Build Coastguard Worker 113*7594170eSAndroid Build Coastguard Workerfunc (mk *MkFile) moduleKey(mod *MkModule) (string, error) { 114*7594170eSAndroid Build Coastguard Worker // Synthesize unique module name. 115*7594170eSAndroid Build Coastguard Worker name := mod.Variables["LOCAL_MODULE"] 116*7594170eSAndroid Build Coastguard Worker if name == "" { 117*7594170eSAndroid Build Coastguard Worker return "", fmt.Errorf("%d: the module above lacks LOCAL_MODULE assignment", mod.Location) 118*7594170eSAndroid Build Coastguard Worker } 119*7594170eSAndroid Build Coastguard Worker var buf strings.Builder 120*7594170eSAndroid Build Coastguard Worker writebuf := func(chunks ...string) { 121*7594170eSAndroid Build Coastguard Worker for _, s := range chunks { 122*7594170eSAndroid Build Coastguard Worker buf.WriteString(s) 123*7594170eSAndroid Build Coastguard Worker } 124*7594170eSAndroid Build Coastguard Worker } 125*7594170eSAndroid Build Coastguard Worker 126*7594170eSAndroid Build Coastguard Worker writebuf(name, "|class:", mod.Variables["LOCAL_MODULE_CLASS"]) 127*7594170eSAndroid Build Coastguard Worker if mod.Variables["LOCAL_IS_HOST_MODULE"] == "true" { 128*7594170eSAndroid Build Coastguard Worker if v, ok := mod.Variables["LOCAL_MODULE_HOST_ARCH"]; ok { 129*7594170eSAndroid Build Coastguard Worker writebuf("|host_arch:", v) 130*7594170eSAndroid Build Coastguard Worker } 131*7594170eSAndroid Build Coastguard Worker if v, ok := mod.Variables["LOCAL_MODULE_HOST_CROSS_ARCH"]; ok { 132*7594170eSAndroid Build Coastguard Worker writebuf("|cross_arch:", v) 133*7594170eSAndroid Build Coastguard Worker } 134*7594170eSAndroid Build Coastguard Worker } else { 135*7594170eSAndroid Build Coastguard Worker if v, ok := mod.Variables["LOCAL_MODULE_TARGET_ARCH"]; ok { 136*7594170eSAndroid Build Coastguard Worker writebuf("|target_arch:", v) 137*7594170eSAndroid Build Coastguard Worker } else { 138*7594170eSAndroid Build Coastguard Worker writebuf("|target_arch:*") 139*7594170eSAndroid Build Coastguard Worker } 140*7594170eSAndroid Build Coastguard Worker } 141*7594170eSAndroid Build Coastguard Worker return buf.String(), nil 142*7594170eSAndroid Build Coastguard Worker} 143*7594170eSAndroid Build Coastguard Worker 144*7594170eSAndroid Build Coastguard Worker// ParseMkFile parses Android-TARGET.mk file generated by Android build 145*7594170eSAndroid Build Coastguard Workerfunc ParseMkFile(source io.Reader) (*MkFile, error) { 146*7594170eSAndroid Build Coastguard Worker scanner := &myScanner{bufio.NewScanner(source), 0} 147*7594170eSAndroid Build Coastguard Worker buffer := make([]byte, 1000000000) 148*7594170eSAndroid Build Coastguard Worker scanner.Scanner.Buffer(buffer, len(buffer)) 149*7594170eSAndroid Build Coastguard Worker mkFile := &MkFile{Modules: make(map[string]*MkModule)} 150*7594170eSAndroid Build Coastguard Worker 151*7594170eSAndroid Build Coastguard Worker for scanner.Scan() { 152*7594170eSAndroid Build Coastguard Worker line := scanner.Text() 153*7594170eSAndroid Build Coastguard Worker m := rexHeader.FindStringSubmatchIndex(line) 154*7594170eSAndroid Build Coastguard Worker if len(m) != rexPairsHeader { 155*7594170eSAndroid Build Coastguard Worker continue 156*7594170eSAndroid Build Coastguard Worker } 157*7594170eSAndroid Build Coastguard Worker moduleType := "" 158*7594170eSAndroid Build Coastguard Worker if m[4] >= 0 { 159*7594170eSAndroid Build Coastguard Worker moduleType = line[m[4]:m[5]] 160*7594170eSAndroid Build Coastguard Worker } 161*7594170eSAndroid Build Coastguard Worker mod, err := mkFile.handleModule(scanner, moduleType) 162*7594170eSAndroid Build Coastguard Worker if err != nil { 163*7594170eSAndroid Build Coastguard Worker return mkFile, err 164*7594170eSAndroid Build Coastguard Worker } 165*7594170eSAndroid Build Coastguard Worker name, err := mkFile.moduleKey(mod) 166*7594170eSAndroid Build Coastguard Worker if err != nil { 167*7594170eSAndroid Build Coastguard Worker return mkFile, err 168*7594170eSAndroid Build Coastguard Worker } 169*7594170eSAndroid Build Coastguard Worker if old, found := mkFile.Modules[name]; found { 170*7594170eSAndroid Build Coastguard Worker return mkFile, fmt.Errorf(":%d: module %s already found, diff: %s", old.Location, name, cmp.Diff(old, mod)) 171*7594170eSAndroid Build Coastguard Worker } 172*7594170eSAndroid Build Coastguard Worker mkFile.Modules[name] = mod 173*7594170eSAndroid Build Coastguard Worker } 174*7594170eSAndroid Build Coastguard Worker return mkFile, scanner.Err() 175*7594170eSAndroid Build Coastguard Worker} 176