Update build_broken_logs script

This can now discover which make module types have been deprecated and
suggest what needs to happen with the BUILD_BROKEN_USES_* flags.

Bug: 130720555
Test: go run .../build_broken_logs.go *  (see header comment)
Change-Id: Iffaa32c0b0079e99b926162257a7c15c530c15a1
diff --git a/scripts/build_broken_logs.go b/scripts/build_broken_logs.go
index 8021e55..82ba749 100644
--- a/scripts/build_broken_logs.go
+++ b/scripts/build_broken_logs.go
@@ -54,11 +54,13 @@
 	DefaultDeprecated
 )
 
-var buildBrokenSettings = []struct {
+type Setting struct {
 	name     string
 	behavior BuildBrokenBehavior
 	warnings []string
-}{
+}
+
+var buildBrokenSettings = []Setting{
 	{
 		name:     "BUILD_BROKEN_DUP_RULES",
 		behavior: DefaultFalse,
@@ -68,6 +70,19 @@
 		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 {
@@ -82,35 +97,48 @@
 }
 
 type Log struct {
-	BuildBroken []*bool
-	HasBroken   []bool
+	WarningModuleTypes []string
+	ErrorModuleTypes   []string
+
+	BuildBroken map[string]*bool
+	HasBroken   map[string]int
 }
 
 func Merge(l, l2 Log) Log {
-	if len(l.BuildBroken) == 0 {
-		l.BuildBroken = make([]*bool, len(buildBrokenSettings))
+	if l.BuildBroken == nil {
+		l.BuildBroken = map[string]*bool{}
 	}
-	if len(l.HasBroken) == 0 {
-		l.HasBroken = make([]bool, len(buildBrokenSettings))
+	if l.HasBroken == nil {
+		l.HasBroken = map[string]int{}
 	}
 
-	if len(l.BuildBroken) != len(l2.BuildBroken) || len(l.HasBroken) != len(l2.HasBroken) {
-		panic("mis-matched logs")
-	}
-
-	for i, v := range l.BuildBroken {
+	for n, v := range l.BuildBroken {
 		if v == nil {
-			l.BuildBroken[i] = l2.BuildBroken[i]
+			l.BuildBroken[n] = l2.BuildBroken[n]
 		}
 	}
-	for i := range l.HasBroken {
-		l.HasBroken[i] = l.HasBroken[i] || l2.HasBroken[i]
+	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(products []ProductLog) {
+func PrintResults(branch Branch) {
+	products := branch.Logs
 	devices := map[string]Log{}
 	deviceNames := []string{}
 
@@ -124,39 +152,48 @@
 
 	sort.Strings(deviceNames)
 
-	for i, setting := range buildBrokenSettings {
+	for _, setting := range branch.Settings {
 		printed := false
+		n := setting.name
 
 		for _, device := range deviceNames {
 			log := devices[device]
 
 			if setting.behavior == DefaultTrue {
-				if log.BuildBroken[i] == nil || *log.BuildBroken[i] == false {
-					if log.HasBroken[i] {
+				if log.BuildBroken[n] == nil || *log.BuildBroken[n] == false {
+					if log.HasBroken[n] > 0 {
 						printed = true
-						fmt.Printf("  %s needs to set %s := true\n", device, setting.name)
+						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[i] {
+				} 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[i] == nil {
+				if log.BuildBroken[n] == nil {
 					// Nothing to be done
-				} else if *log.BuildBroken[i] == false {
+				} 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[i] {
+				} 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[i] != nil {
+				if log.BuildBroken[n] != nil {
 					printed = true
-					if log.HasBroken[i] {
-						fmt.Printf("  %s sets %s := %v, which is deprecated, but has failures\n", device, setting.name, *log.BuildBroken[i])
+					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[i])
+						fmt.Printf("  %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[n])
 					}
 				}
 			}
@@ -168,17 +205,45 @@
 	}
 }
 
-func ParseBranch(name string) []ProductLog {
+func ParseBranch(name string) Branch {
 	products, err := filepath.Glob(filepath.Join(name, "*"))
 	if err != nil {
 		log.Fatal(err)
 	}
 
-	ret := []ProductLog{}
+	ret := Branch{Logs: []ProductLog{}}
 	for _, product := range products {
 		product = filepath.Base(product)
 
-		ret = append(ret, ParseProduct(ProductBranch{Branch: name, Name: 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
 }
@@ -192,15 +257,15 @@
 	ret := ProductLog{
 		ProductBranch: p,
 		Log: Log{
-			BuildBroken: make([]*bool, len(buildBrokenSettings)),
-			HasBroken:   make([]bool, len(buildBrokenSettings)),
+			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 {
+		if len(fields) < 5 {
 			continue
 		}
 
@@ -208,30 +273,35 @@
 			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_") {
-			for i, setting := range buildBrokenSettings {
-				if setting.name == fields[3] {
-					ret.BuildBroken[i] = ParseBoolPtr(fields[4])
-				}
-			}
+			ret.BuildBroken[fields[3]] = ParseBoolPtr(fields[4])
 		}
 	}
 
-	stdLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "std_full.log"))
+	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 i, setting := range buildBrokenSettings {
+	for _, setting := range settings {
 		for _, warning := range setting.warnings {
 			if strings.Contains(stdStr, warning) {
-				ret.HasBroken[i] = true
+				l.HasBroken[setting.name] += strings.Count(stdStr, warning)
 			}
 		}
 	}
-
-	return ret
 }
 
 func ParseBoolPtr(str string) *bool {