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