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