1// Copyright 2019 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15// This is a script that can be used to analyze the results from 16// build/soong/build_test.bash and recommend what devices need changes to their 17// BUILD_BROKEN_* flags. 18// 19// To use, download the logs.zip from one or more branches, and extract them 20// into subdirectories of the current directory. So for example, I have: 21// 22// ./aosp-master/aosp_arm/std_full.log 23// ./aosp-master/aosp_arm64/std_full.log 24// ./aosp-master/... 25// ./internal-master/aosp_arm/std_full.log 26// ./internal-master/aosp_arm64/std_full.log 27// ./internal-master/... 28// 29// Then I use `go run path/to/build_broken_logs.go *` 30package main 31 32import ( 33 "fmt" 34 "io/ioutil" 35 "log" 36 "os" 37 "path/filepath" 38 "sort" 39 "strings" 40) 41 42func main() { 43 for _, branch := range os.Args[1:] { 44 fmt.Printf("\nBranch %s:\n", branch) 45 PrintResults(ParseBranch(branch)) 46 } 47} 48 49type BuildBrokenBehavior int 50 51const ( 52 DefaultFalse BuildBrokenBehavior = iota 53 DefaultTrue 54 DefaultDeprecated 55) 56 57type Setting struct { 58 name string 59 behavior BuildBrokenBehavior 60 warnings []string 61} 62 63var buildBrokenSettings = []Setting{ 64 { 65 name: "BUILD_BROKEN_DUP_RULES", 66 behavior: DefaultFalse, 67 warnings: []string{"overriding commands for target"}, 68 }, 69 { 70 name: "BUILD_BROKEN_USES_NETWORK", 71 behavior: DefaultDeprecated, 72 }, 73 { 74 name: "BUILD_BROKEN_USES_BUILD_COPY_HEADERS", 75 behavior: DefaultTrue, 76 warnings: []string{ 77 "COPY_HEADERS has been deprecated", 78 "COPY_HEADERS is deprecated", 79 }, 80 }, 81} 82 83type Branch struct { 84 Settings []Setting 85 Logs []ProductLog 86} 87 88type ProductBranch struct { 89 Branch string 90 Name string 91} 92 93type ProductLog struct { 94 ProductBranch 95 Log 96 Device string 97} 98 99type Log struct { 100 WarningModuleTypes []string 101 ErrorModuleTypes []string 102 103 BuildBroken map[string]*bool 104 HasBroken map[string]int 105} 106 107func Merge(l, l2 Log) Log { 108 if l.BuildBroken == nil { 109 l.BuildBroken = map[string]*bool{} 110 } 111 if l.HasBroken == nil { 112 l.HasBroken = map[string]int{} 113 } 114 115 for n, v := range l.BuildBroken { 116 if v == nil { 117 l.BuildBroken[n] = l2.BuildBroken[n] 118 } 119 } 120 for n, v := range l2.BuildBroken { 121 if _, ok := l.BuildBroken[n]; !ok { 122 l.BuildBroken[n] = v 123 } 124 } 125 126 for n := range l.HasBroken { 127 if l.HasBroken[n] < l2.HasBroken[n] { 128 l.HasBroken[n] = l2.HasBroken[n] 129 } 130 } 131 for n := range l2.HasBroken { 132 if _, ok := l.HasBroken[n]; !ok { 133 l.HasBroken[n] = l2.HasBroken[n] 134 } 135 } 136 137 return l 138} 139 140func PrintResults(branch Branch) { 141 products := branch.Logs 142 devices := map[string]Log{} 143 deviceNames := []string{} 144 145 for _, product := range products { 146 device := product.Device 147 if _, ok := devices[device]; !ok { 148 deviceNames = append(deviceNames, device) 149 } 150 devices[device] = Merge(devices[device], product.Log) 151 } 152 153 sort.Strings(deviceNames) 154 155 for _, setting := range branch.Settings { 156 printed := false 157 n := setting.name 158 159 for _, device := range deviceNames { 160 log := devices[device] 161 162 if setting.behavior == DefaultTrue { 163 if log.BuildBroken[n] == nil || *log.BuildBroken[n] == false { 164 if log.HasBroken[n] > 0 { 165 printed = true 166 plural := "" 167 if log.HasBroken[n] > 1 { 168 plural = "s" 169 } 170 fmt.Printf(" %s needs to set %s := true (%d instance%s)\n", device, setting.name, log.HasBroken[n], plural) 171 } 172 } else if log.HasBroken[n] == 0 { 173 printed = true 174 fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name) 175 } 176 } else if setting.behavior == DefaultFalse { 177 if log.BuildBroken[n] == nil { 178 // Nothing to be done 179 } else if *log.BuildBroken[n] == false { 180 printed = true 181 fmt.Printf(" %s sets %s := false, which is the default and can be removed\n", device, setting.name) 182 } else if log.HasBroken[n] == 0 { 183 printed = true 184 fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name) 185 } 186 } else if setting.behavior == DefaultDeprecated { 187 if log.BuildBroken[n] != nil { 188 printed = true 189 if log.HasBroken[n] > 0 { 190 plural := "" 191 if log.HasBroken[n] > 1 { 192 plural = "s" 193 } 194 fmt.Printf(" %s sets %s := %v, which is deprecated, but has %d failure%s\n", device, setting.name, *log.BuildBroken[n], log.HasBroken[n], plural) 195 } else { 196 fmt.Printf(" %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[n]) 197 } 198 } 199 } 200 } 201 202 if printed { 203 fmt.Println() 204 } 205 } 206} 207 208func ParseBranch(name string) Branch { 209 products, err := filepath.Glob(filepath.Join(name, "*")) 210 if err != nil { 211 log.Fatal(err) 212 } 213 214 ret := Branch{Logs: []ProductLog{}} 215 for _, product := range products { 216 product = filepath.Base(product) 217 218 ret.Logs = append(ret.Logs, ParseProduct(ProductBranch{Branch: name, Name: product})) 219 } 220 221 ret.Settings = append(ret.Settings, buildBrokenSettings...) 222 if len(ret.Logs) > 0 { 223 for _, mtype := range ret.Logs[0].WarningModuleTypes { 224 if mtype == "BUILD_COPY_HEADERS" || mtype == "" { 225 continue 226 } 227 ret.Settings = append(ret.Settings, Setting{ 228 name: "BUILD_BROKEN_USES_" + mtype, 229 behavior: DefaultTrue, 230 warnings: []string{mtype + " has been deprecated"}, 231 }) 232 } 233 for _, mtype := range ret.Logs[0].ErrorModuleTypes { 234 if mtype == "BUILD_COPY_HEADERS" || mtype == "" { 235 continue 236 } 237 ret.Settings = append(ret.Settings, Setting{ 238 name: "BUILD_BROKEN_USES_" + mtype, 239 behavior: DefaultFalse, 240 warnings: []string{mtype + " has been deprecated"}, 241 }) 242 } 243 } 244 245 for _, productLog := range ret.Logs { 246 ScanProduct(ret.Settings, productLog) 247 } 248 return ret 249} 250 251func ParseProduct(p ProductBranch) ProductLog { 252 soongLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "soong.log")) 253 if err != nil { 254 log.Fatal(err) 255 } 256 257 ret := ProductLog{ 258 ProductBranch: p, 259 Log: Log{ 260 BuildBroken: map[string]*bool{}, 261 HasBroken: map[string]int{}, 262 }, 263 } 264 265 lines := strings.Split(string(soongLog), "\n") 266 for _, line := range lines { 267 fields := strings.Split(line, " ") 268 if len(fields) < 5 { 269 continue 270 } 271 272 if fields[3] == "TARGET_DEVICE" { 273 ret.Device = fields[4] 274 } 275 276 if fields[3] == "DEFAULT_WARNING_BUILD_MODULE_TYPES" { 277 ret.WarningModuleTypes = fields[4:] 278 } 279 if fields[3] == "DEFAULT_ERROR_BUILD_MODULE_TYPES" { 280 ret.ErrorModuleTypes = fields[4:] 281 } 282 283 if strings.HasPrefix(fields[3], "BUILD_BROKEN_") { 284 ret.BuildBroken[fields[3]] = ParseBoolPtr(fields[4]) 285 } 286 } 287 288 return ret 289} 290 291func ScanProduct(settings []Setting, l ProductLog) { 292 stdLog, err := ioutil.ReadFile(filepath.Join(l.Branch, l.Name, "std_full.log")) 293 if err != nil { 294 log.Fatal(err) 295 } 296 stdStr := string(stdLog) 297 298 for _, setting := range settings { 299 for _, warning := range setting.warnings { 300 if strings.Contains(stdStr, warning) { 301 l.HasBroken[setting.name] += strings.Count(stdStr, warning) 302 } 303 } 304 } 305} 306 307func ParseBoolPtr(str string) *bool { 308 var ret *bool 309 if str != "" { 310 b := str == "true" 311 ret = &b 312 } 313 return ret 314} 315