123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314 |
- // 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
- }
|