315 lines
7.4 KiB
Go
315 lines
7.4 KiB
Go
// Copyright 2019 Google Inc. All rights reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
// This is a script that can be used to analyze the results from
|
|
// build/soong/build_test.bash and recommend what devices need changes to their
|
|
// BUILD_BROKEN_* flags.
|
|
//
|
|
// To use, download the logs.zip from one or more branches, and extract them
|
|
// into subdirectories of the current directory. So for example, I have:
|
|
//
|
|
// ./aosp-master/aosp_arm/std_full.log
|
|
// ./aosp-master/aosp_arm64/std_full.log
|
|
// ./aosp-master/...
|
|
// ./internal-master/aosp_arm/std_full.log
|
|
// ./internal-master/aosp_arm64/std_full.log
|
|
// ./internal-master/...
|
|
//
|
|
// Then I use `go run path/to/build_broken_logs.go *`
|
|
package main
|
|
|
|
import (
|
|
"fmt"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"sort"
|
|
"strings"
|
|
)
|
|
|
|
func main() {
|
|
for _, branch := range os.Args[1:] {
|
|
fmt.Printf("\nBranch %s:\n", branch)
|
|
PrintResults(ParseBranch(branch))
|
|
}
|
|
}
|
|
|
|
type BuildBrokenBehavior int
|
|
|
|
const (
|
|
DefaultFalse BuildBrokenBehavior = iota
|
|
DefaultTrue
|
|
DefaultDeprecated
|
|
)
|
|
|
|
type Setting struct {
|
|
name string
|
|
behavior BuildBrokenBehavior
|
|
warnings []string
|
|
}
|
|
|
|
var buildBrokenSettings = []Setting{
|
|
{
|
|
name: "BUILD_BROKEN_DUP_RULES",
|
|
behavior: DefaultFalse,
|
|
warnings: []string{"overriding commands for target"},
|
|
},
|
|
{
|
|
name: "BUILD_BROKEN_USES_NETWORK",
|
|
behavior: DefaultDeprecated,
|
|
},
|
|
{
|
|
name: "BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
|
|
behavior: DefaultTrue,
|
|
warnings: []string{
|
|
"COPY_HEADERS has been deprecated",
|
|
"COPY_HEADERS is deprecated",
|
|
},
|
|
},
|
|
}
|
|
|
|
type Branch struct {
|
|
Settings []Setting
|
|
Logs []ProductLog
|
|
}
|
|
|
|
type ProductBranch struct {
|
|
Branch string
|
|
Name string
|
|
}
|
|
|
|
type ProductLog struct {
|
|
ProductBranch
|
|
Log
|
|
Device string
|
|
}
|
|
|
|
type Log struct {
|
|
WarningModuleTypes []string
|
|
ErrorModuleTypes []string
|
|
|
|
BuildBroken map[string]*bool
|
|
HasBroken map[string]int
|
|
}
|
|
|
|
func Merge(l, l2 Log) Log {
|
|
if l.BuildBroken == nil {
|
|
l.BuildBroken = map[string]*bool{}
|
|
}
|
|
if l.HasBroken == nil {
|
|
l.HasBroken = map[string]int{}
|
|
}
|
|
|
|
for n, v := range l.BuildBroken {
|
|
if v == nil {
|
|
l.BuildBroken[n] = l2.BuildBroken[n]
|
|
}
|
|
}
|
|
for n, v := range l2.BuildBroken {
|
|
if _, ok := l.BuildBroken[n]; !ok {
|
|
l.BuildBroken[n] = v
|
|
}
|
|
}
|
|
|
|
for n := range l.HasBroken {
|
|
if l.HasBroken[n] < l2.HasBroken[n] {
|
|
l.HasBroken[n] = l2.HasBroken[n]
|
|
}
|
|
}
|
|
for n := range l2.HasBroken {
|
|
if _, ok := l.HasBroken[n]; !ok {
|
|
l.HasBroken[n] = l2.HasBroken[n]
|
|
}
|
|
}
|
|
|
|
return l
|
|
}
|
|
|
|
func PrintResults(branch Branch) {
|
|
products := branch.Logs
|
|
devices := map[string]Log{}
|
|
deviceNames := []string{}
|
|
|
|
for _, product := range products {
|
|
device := product.Device
|
|
if _, ok := devices[device]; !ok {
|
|
deviceNames = append(deviceNames, device)
|
|
}
|
|
devices[device] = Merge(devices[device], product.Log)
|
|
}
|
|
|
|
sort.Strings(deviceNames)
|
|
|
|
for _, setting := range branch.Settings {
|
|
printed := false
|
|
n := setting.name
|
|
|
|
for _, device := range deviceNames {
|
|
log := devices[device]
|
|
|
|
if setting.behavior == DefaultTrue {
|
|
if log.BuildBroken[n] == nil || *log.BuildBroken[n] == false {
|
|
if log.HasBroken[n] > 0 {
|
|
printed = true
|
|
plural := ""
|
|
if log.HasBroken[n] > 1 {
|
|
plural = "s"
|
|
}
|
|
fmt.Printf(" %s needs to set %s := true (%d instance%s)\n", device, setting.name, log.HasBroken[n], plural)
|
|
}
|
|
} else if log.HasBroken[n] == 0 {
|
|
printed = true
|
|
fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name)
|
|
}
|
|
} else if setting.behavior == DefaultFalse {
|
|
if log.BuildBroken[n] == nil {
|
|
// Nothing to be done
|
|
} else if *log.BuildBroken[n] == false {
|
|
printed = true
|
|
fmt.Printf(" %s sets %s := false, which is the default and can be removed\n", device, setting.name)
|
|
} else if log.HasBroken[n] == 0 {
|
|
printed = true
|
|
fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name)
|
|
}
|
|
} else if setting.behavior == DefaultDeprecated {
|
|
if log.BuildBroken[n] != nil {
|
|
printed = true
|
|
if log.HasBroken[n] > 0 {
|
|
plural := ""
|
|
if log.HasBroken[n] > 1 {
|
|
plural = "s"
|
|
}
|
|
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)
|
|
} else {
|
|
fmt.Printf(" %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[n])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if printed {
|
|
fmt.Println()
|
|
}
|
|
}
|
|
}
|
|
|
|
func ParseBranch(name string) Branch {
|
|
products, err := filepath.Glob(filepath.Join(name, "*"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
ret := Branch{Logs: []ProductLog{}}
|
|
for _, product := range products {
|
|
product = filepath.Base(product)
|
|
|
|
ret.Logs = append(ret.Logs, ParseProduct(ProductBranch{Branch: name, Name: product}))
|
|
}
|
|
|
|
ret.Settings = append(ret.Settings, buildBrokenSettings...)
|
|
if len(ret.Logs) > 0 {
|
|
for _, mtype := range ret.Logs[0].WarningModuleTypes {
|
|
if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
|
|
continue
|
|
}
|
|
ret.Settings = append(ret.Settings, Setting{
|
|
name: "BUILD_BROKEN_USES_" + mtype,
|
|
behavior: DefaultTrue,
|
|
warnings: []string{mtype + " has been deprecated"},
|
|
})
|
|
}
|
|
for _, mtype := range ret.Logs[0].ErrorModuleTypes {
|
|
if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
|
|
continue
|
|
}
|
|
ret.Settings = append(ret.Settings, Setting{
|
|
name: "BUILD_BROKEN_USES_" + mtype,
|
|
behavior: DefaultFalse,
|
|
warnings: []string{mtype + " has been deprecated"},
|
|
})
|
|
}
|
|
}
|
|
|
|
for _, productLog := range ret.Logs {
|
|
ScanProduct(ret.Settings, productLog)
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func ParseProduct(p ProductBranch) ProductLog {
|
|
soongLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "soong.log"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
ret := ProductLog{
|
|
ProductBranch: p,
|
|
Log: Log{
|
|
BuildBroken: map[string]*bool{},
|
|
HasBroken: map[string]int{},
|
|
},
|
|
}
|
|
|
|
lines := strings.Split(string(soongLog), "\n")
|
|
for _, line := range lines {
|
|
fields := strings.Split(line, " ")
|
|
if len(fields) < 5 {
|
|
continue
|
|
}
|
|
|
|
if fields[3] == "TARGET_DEVICE" {
|
|
ret.Device = fields[4]
|
|
}
|
|
|
|
if fields[3] == "DEFAULT_WARNING_BUILD_MODULE_TYPES" {
|
|
ret.WarningModuleTypes = fields[4:]
|
|
}
|
|
if fields[3] == "DEFAULT_ERROR_BUILD_MODULE_TYPES" {
|
|
ret.ErrorModuleTypes = fields[4:]
|
|
}
|
|
|
|
if strings.HasPrefix(fields[3], "BUILD_BROKEN_") {
|
|
ret.BuildBroken[fields[3]] = ParseBoolPtr(fields[4])
|
|
}
|
|
}
|
|
|
|
return ret
|
|
}
|
|
|
|
func ScanProduct(settings []Setting, l ProductLog) {
|
|
stdLog, err := ioutil.ReadFile(filepath.Join(l.Branch, l.Name, "std_full.log"))
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
stdStr := string(stdLog)
|
|
|
|
for _, setting := range settings {
|
|
for _, warning := range setting.warnings {
|
|
if strings.Contains(stdStr, warning) {
|
|
l.HasBroken[setting.name] += strings.Count(stdStr, warning)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func ParseBoolPtr(str string) *bool {
|
|
var ret *bool
|
|
if str != "" {
|
|
b := str == "true"
|
|
ret = &b
|
|
}
|
|
return ret
|
|
}
|