blob: 8021e55d0537604ad9b159ab1a9e873622f4232d [file] [log] [blame]
Dan Willemsen01f0a052019-02-05 13:44:20 -08001// 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 {
Dan Willemsen01f0a052019-02-05 13:44:20 -080063 name: "BUILD_BROKEN_DUP_RULES",
64 behavior: DefaultFalse,
65 warnings: []string{"overriding commands for target"},
66 },
67 {
Dan Willemsen25e6f092019-04-09 10:22:43 -070068 name: "BUILD_BROKEN_USES_NETWORK",
69 behavior: DefaultDeprecated,
70 },
Dan Willemsen01f0a052019-02-05 13:44:20 -080071}
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}