| Dan Willemsen | 01f0a05 | 2019-02-05 13:44:20 -0800 | [diff] [blame] | 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 *` | 
|  | 30 | package main | 
|  | 31 |  | 
|  | 32 | import ( | 
|  | 33 | "fmt" | 
|  | 34 | "io/ioutil" | 
|  | 35 | "log" | 
|  | 36 | "os" | 
|  | 37 | "path/filepath" | 
|  | 38 | "sort" | 
|  | 39 | "strings" | 
|  | 40 | ) | 
|  | 41 |  | 
|  | 42 | func main() { | 
|  | 43 | for _, branch := range os.Args[1:] { | 
|  | 44 | fmt.Printf("\nBranch %s:\n", branch) | 
|  | 45 | PrintResults(ParseBranch(branch)) | 
|  | 46 | } | 
|  | 47 | } | 
|  | 48 |  | 
|  | 49 | type BuildBrokenBehavior int | 
|  | 50 |  | 
|  | 51 | const ( | 
|  | 52 | DefaultFalse BuildBrokenBehavior = iota | 
|  | 53 | DefaultTrue | 
|  | 54 | DefaultDeprecated | 
|  | 55 | ) | 
|  | 56 |  | 
|  | 57 | var buildBrokenSettings = []struct { | 
|  | 58 | name     string | 
|  | 59 | behavior BuildBrokenBehavior | 
|  | 60 | warnings []string | 
|  | 61 | }{ | 
|  | 62 | { | 
| Dan Willemsen | 01f0a05 | 2019-02-05 13:44:20 -0800 | [diff] [blame] | 63 | name:     "BUILD_BROKEN_DUP_RULES", | 
|  | 64 | behavior: DefaultFalse, | 
|  | 65 | warnings: []string{"overriding commands for target"}, | 
|  | 66 | }, | 
|  | 67 | { | 
| Dan Willemsen | 25e6f09 | 2019-04-09 10:22:43 -0700 | [diff] [blame] | 68 | name:     "BUILD_BROKEN_USES_NETWORK", | 
|  | 69 | behavior: DefaultDeprecated, | 
|  | 70 | }, | 
| Dan Willemsen | 01f0a05 | 2019-02-05 13:44:20 -0800 | [diff] [blame] | 71 | } | 
|  | 72 |  | 
|  | 73 | type ProductBranch struct { | 
|  | 74 | Branch string | 
|  | 75 | Name   string | 
|  | 76 | } | 
|  | 77 |  | 
|  | 78 | type ProductLog struct { | 
|  | 79 | ProductBranch | 
|  | 80 | Log | 
|  | 81 | Device string | 
|  | 82 | } | 
|  | 83 |  | 
|  | 84 | type Log struct { | 
|  | 85 | BuildBroken []*bool | 
|  | 86 | HasBroken   []bool | 
|  | 87 | } | 
|  | 88 |  | 
|  | 89 | func 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 |  | 
|  | 113 | func 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 |  | 
|  | 171 | func 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 |  | 
|  | 186 | func 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 |  | 
|  | 237 | func ParseBoolPtr(str string) *bool { | 
|  | 238 | var ret *bool | 
|  | 239 | if str != "" { | 
|  | 240 | b := str == "true" | 
|  | 241 | ret = &b | 
|  | 242 | } | 
|  | 243 | return ret | 
|  | 244 | } |