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 57var buildBrokenSettings = []struct { 58 name string 59 behavior BuildBrokenBehavior 60 warnings []string 61}{ 62 { 63 name: "BUILD_BROKEN_DUP_RULES", 64 behavior: DefaultFalse, 65 warnings: []string{"overriding commands for target"}, 66 }, 67 { 68 name: "BUILD_BROKEN_USES_NETWORK", 69 behavior: DefaultDeprecated, 70 }, 71} 72 73type ProductBranch struct { 74 Branch string 75 Name string 76} 77 78type ProductLog struct { 79 ProductBranch 80 Log 81 Device string 82} 83 84type Log struct { 85 BuildBroken []*bool 86 HasBroken []bool 87} 88 89func Merge(l, l2 Log) Log { 90 if len(l.BuildBroken) == 0 { 91 l.BuildBroken = make([]*bool, len(buildBrokenSettings)) 92 } 93 if len(l.HasBroken) == 0 { 94 l.HasBroken = make([]bool, len(buildBrokenSettings)) 95 } 96 97 if len(l.BuildBroken) != len(l2.BuildBroken) || len(l.HasBroken) != len(l2.HasBroken) { 98 panic("mis-matched logs") 99 } 100 101 for i, v := range l.BuildBroken { 102 if v == nil { 103 l.BuildBroken[i] = l2.BuildBroken[i] 104 } 105 } 106 for i := range l.HasBroken { 107 l.HasBroken[i] = l.HasBroken[i] || l2.HasBroken[i] 108 } 109 110 return l 111} 112 113func PrintResults(products []ProductLog) { 114 devices := map[string]Log{} 115 deviceNames := []string{} 116 117 for _, product := range products { 118 device := product.Device 119 if _, ok := devices[device]; !ok { 120 deviceNames = append(deviceNames, device) 121 } 122 devices[device] = Merge(devices[device], product.Log) 123 } 124 125 sort.Strings(deviceNames) 126 127 for i, setting := range buildBrokenSettings { 128 printed := false 129 130 for _, device := range deviceNames { 131 log := devices[device] 132 133 if setting.behavior == DefaultTrue { 134 if log.BuildBroken[i] == nil || *log.BuildBroken[i] == false { 135 if log.HasBroken[i] { 136 printed = true 137 fmt.Printf(" %s needs to set %s := true\n", device, setting.name) 138 } 139 } else if !log.HasBroken[i] { 140 printed = true 141 fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name) 142 } 143 } else if setting.behavior == DefaultFalse { 144 if log.BuildBroken[i] == nil { 145 // Nothing to be done 146 } else if *log.BuildBroken[i] == false { 147 printed = true 148 fmt.Printf(" %s sets %s := false, which is the default and can be removed\n", device, setting.name) 149 } else if !log.HasBroken[i] { 150 printed = true 151 fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name) 152 } 153 } else if setting.behavior == DefaultDeprecated { 154 if log.BuildBroken[i] != nil { 155 printed = true 156 if log.HasBroken[i] { 157 fmt.Printf(" %s sets %s := %v, which is deprecated, but has failures\n", device, setting.name, *log.BuildBroken[i]) 158 } else { 159 fmt.Printf(" %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[i]) 160 } 161 } 162 } 163 } 164 165 if printed { 166 fmt.Println() 167 } 168 } 169} 170 171func ParseBranch(name string) []ProductLog { 172 products, err := filepath.Glob(filepath.Join(name, "*")) 173 if err != nil { 174 log.Fatal(err) 175 } 176 177 ret := []ProductLog{} 178 for _, product := range products { 179 product = filepath.Base(product) 180 181 ret = append(ret, ParseProduct(ProductBranch{Branch: name, Name: product})) 182 } 183 return ret 184} 185 186func ParseProduct(p ProductBranch) ProductLog { 187 soongLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "soong.log")) 188 if err != nil { 189 log.Fatal(err) 190 } 191 192 ret := ProductLog{ 193 ProductBranch: p, 194 Log: Log{ 195 BuildBroken: make([]*bool, len(buildBrokenSettings)), 196 HasBroken: make([]bool, len(buildBrokenSettings)), 197 }, 198 } 199 200 lines := strings.Split(string(soongLog), "\n") 201 for _, line := range lines { 202 fields := strings.Split(line, " ") 203 if len(fields) != 5 { 204 continue 205 } 206 207 if fields[3] == "TARGET_DEVICE" { 208 ret.Device = fields[4] 209 } 210 211 if strings.HasPrefix(fields[3], "BUILD_BROKEN_") { 212 for i, setting := range buildBrokenSettings { 213 if setting.name == fields[3] { 214 ret.BuildBroken[i] = ParseBoolPtr(fields[4]) 215 } 216 } 217 } 218 } 219 220 stdLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "std_full.log")) 221 if err != nil { 222 log.Fatal(err) 223 } 224 stdStr := string(stdLog) 225 226 for i, setting := range buildBrokenSettings { 227 for _, warning := range setting.warnings { 228 if strings.Contains(stdStr, warning) { 229 ret.HasBroken[i] = true 230 } 231 } 232 } 233 234 return ret 235} 236 237func ParseBoolPtr(str string) *bool { 238 var ret *bool 239 if str != "" { 240 b := str == "true" 241 ret = &b 242 } 243 return ret 244} 245