• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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