build_broken_logs.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  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. // This is a script that can be used to analyze the results from
  15. // build/soong/build_test.bash and recommend what devices need changes to their
  16. // BUILD_BROKEN_* flags.
  17. //
  18. // To use, download the logs.zip from one or more branches, and extract them
  19. // into subdirectories of the current directory. So for example, I have:
  20. //
  21. // ./aosp-master/aosp_arm/std_full.log
  22. // ./aosp-master/aosp_arm64/std_full.log
  23. // ./aosp-master/...
  24. // ./internal-master/aosp_arm/std_full.log
  25. // ./internal-master/aosp_arm64/std_full.log
  26. // ./internal-master/...
  27. //
  28. // Then I use `go run path/to/build_broken_logs.go *`
  29. package main
  30. import (
  31. "fmt"
  32. "io/ioutil"
  33. "log"
  34. "os"
  35. "path/filepath"
  36. "sort"
  37. "strings"
  38. )
  39. func main() {
  40. for _, branch := range os.Args[1:] {
  41. fmt.Printf("\nBranch %s:\n", branch)
  42. PrintResults(ParseBranch(branch))
  43. }
  44. }
  45. type BuildBrokenBehavior int
  46. const (
  47. DefaultFalse BuildBrokenBehavior = iota
  48. DefaultTrue
  49. DefaultDeprecated
  50. )
  51. type Setting struct {
  52. name string
  53. behavior BuildBrokenBehavior
  54. warnings []string
  55. }
  56. var buildBrokenSettings = []Setting{
  57. {
  58. name: "BUILD_BROKEN_DUP_RULES",
  59. behavior: DefaultFalse,
  60. warnings: []string{"overriding commands for target"},
  61. },
  62. {
  63. name: "BUILD_BROKEN_USES_NETWORK",
  64. behavior: DefaultDeprecated,
  65. },
  66. {
  67. name: "BUILD_BROKEN_USES_BUILD_COPY_HEADERS",
  68. behavior: DefaultTrue,
  69. warnings: []string{
  70. "COPY_HEADERS has been deprecated",
  71. "COPY_HEADERS is deprecated",
  72. },
  73. },
  74. }
  75. type Branch struct {
  76. Settings []Setting
  77. Logs []ProductLog
  78. }
  79. type ProductBranch struct {
  80. Branch string
  81. Name string
  82. }
  83. type ProductLog struct {
  84. ProductBranch
  85. Log
  86. Device string
  87. }
  88. type Log struct {
  89. WarningModuleTypes []string
  90. ErrorModuleTypes []string
  91. BuildBroken map[string]*bool
  92. HasBroken map[string]int
  93. }
  94. func Merge(l, l2 Log) Log {
  95. if l.BuildBroken == nil {
  96. l.BuildBroken = map[string]*bool{}
  97. }
  98. if l.HasBroken == nil {
  99. l.HasBroken = map[string]int{}
  100. }
  101. for n, v := range l.BuildBroken {
  102. if v == nil {
  103. l.BuildBroken[n] = l2.BuildBroken[n]
  104. }
  105. }
  106. for n, v := range l2.BuildBroken {
  107. if _, ok := l.BuildBroken[n]; !ok {
  108. l.BuildBroken[n] = v
  109. }
  110. }
  111. for n := range l.HasBroken {
  112. if l.HasBroken[n] < l2.HasBroken[n] {
  113. l.HasBroken[n] = l2.HasBroken[n]
  114. }
  115. }
  116. for n := range l2.HasBroken {
  117. if _, ok := l.HasBroken[n]; !ok {
  118. l.HasBroken[n] = l2.HasBroken[n]
  119. }
  120. }
  121. return l
  122. }
  123. func PrintResults(branch Branch) {
  124. products := branch.Logs
  125. devices := map[string]Log{}
  126. deviceNames := []string{}
  127. for _, product := range products {
  128. device := product.Device
  129. if _, ok := devices[device]; !ok {
  130. deviceNames = append(deviceNames, device)
  131. }
  132. devices[device] = Merge(devices[device], product.Log)
  133. }
  134. sort.Strings(deviceNames)
  135. for _, setting := range branch.Settings {
  136. printed := false
  137. n := setting.name
  138. for _, device := range deviceNames {
  139. log := devices[device]
  140. if setting.behavior == DefaultTrue {
  141. if log.BuildBroken[n] == nil || *log.BuildBroken[n] == false {
  142. if log.HasBroken[n] > 0 {
  143. printed = true
  144. plural := ""
  145. if log.HasBroken[n] > 1 {
  146. plural = "s"
  147. }
  148. fmt.Printf(" %s needs to set %s := true (%d instance%s)\n", device, setting.name, log.HasBroken[n], plural)
  149. }
  150. } else if log.HasBroken[n] == 0 {
  151. printed = true
  152. fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name)
  153. }
  154. } else if setting.behavior == DefaultFalse {
  155. if log.BuildBroken[n] == nil {
  156. // Nothing to be done
  157. } else if *log.BuildBroken[n] == false {
  158. printed = true
  159. fmt.Printf(" %s sets %s := false, which is the default and can be removed\n", device, setting.name)
  160. } else if log.HasBroken[n] == 0 {
  161. printed = true
  162. fmt.Printf(" %s sets %s := true, but does not need it\n", device, setting.name)
  163. }
  164. } else if setting.behavior == DefaultDeprecated {
  165. if log.BuildBroken[n] != nil {
  166. printed = true
  167. if log.HasBroken[n] > 0 {
  168. plural := ""
  169. if log.HasBroken[n] > 1 {
  170. plural = "s"
  171. }
  172. 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)
  173. } else {
  174. fmt.Printf(" %s sets %s := %v, which is deprecated and can be removed\n", device, setting.name, *log.BuildBroken[n])
  175. }
  176. }
  177. }
  178. }
  179. if printed {
  180. fmt.Println()
  181. }
  182. }
  183. }
  184. func ParseBranch(name string) Branch {
  185. products, err := filepath.Glob(filepath.Join(name, "*"))
  186. if err != nil {
  187. log.Fatal(err)
  188. }
  189. ret := Branch{Logs: []ProductLog{}}
  190. for _, product := range products {
  191. product = filepath.Base(product)
  192. ret.Logs = append(ret.Logs, ParseProduct(ProductBranch{Branch: name, Name: product}))
  193. }
  194. ret.Settings = append(ret.Settings, buildBrokenSettings...)
  195. if len(ret.Logs) > 0 {
  196. for _, mtype := range ret.Logs[0].WarningModuleTypes {
  197. if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
  198. continue
  199. }
  200. ret.Settings = append(ret.Settings, Setting{
  201. name: "BUILD_BROKEN_USES_" + mtype,
  202. behavior: DefaultTrue,
  203. warnings: []string{mtype + " has been deprecated"},
  204. })
  205. }
  206. for _, mtype := range ret.Logs[0].ErrorModuleTypes {
  207. if mtype == "BUILD_COPY_HEADERS" || mtype == "" {
  208. continue
  209. }
  210. ret.Settings = append(ret.Settings, Setting{
  211. name: "BUILD_BROKEN_USES_" + mtype,
  212. behavior: DefaultFalse,
  213. warnings: []string{mtype + " has been deprecated"},
  214. })
  215. }
  216. }
  217. for _, productLog := range ret.Logs {
  218. ScanProduct(ret.Settings, productLog)
  219. }
  220. return ret
  221. }
  222. func ParseProduct(p ProductBranch) ProductLog {
  223. soongLog, err := ioutil.ReadFile(filepath.Join(p.Branch, p.Name, "soong.log"))
  224. if err != nil {
  225. log.Fatal(err)
  226. }
  227. ret := ProductLog{
  228. ProductBranch: p,
  229. Log: Log{
  230. BuildBroken: map[string]*bool{},
  231. HasBroken: map[string]int{},
  232. },
  233. }
  234. lines := strings.Split(string(soongLog), "\n")
  235. for _, line := range lines {
  236. fields := strings.Split(line, " ")
  237. if len(fields) < 5 {
  238. continue
  239. }
  240. if fields[3] == "TARGET_DEVICE" {
  241. ret.Device = fields[4]
  242. }
  243. if fields[3] == "DEFAULT_WARNING_BUILD_MODULE_TYPES" {
  244. ret.WarningModuleTypes = fields[4:]
  245. }
  246. if fields[3] == "DEFAULT_ERROR_BUILD_MODULE_TYPES" {
  247. ret.ErrorModuleTypes = fields[4:]
  248. }
  249. if strings.HasPrefix(fields[3], "BUILD_BROKEN_") {
  250. ret.BuildBroken[fields[3]] = ParseBoolPtr(fields[4])
  251. }
  252. }
  253. return ret
  254. }
  255. func ScanProduct(settings []Setting, l ProductLog) {
  256. stdLog, err := ioutil.ReadFile(filepath.Join(l.Branch, l.Name, "std_full.log"))
  257. if err != nil {
  258. log.Fatal(err)
  259. }
  260. stdStr := string(stdLog)
  261. for _, setting := range settings {
  262. for _, warning := range setting.warnings {
  263. if strings.Contains(stdStr, warning) {
  264. l.HasBroken[setting.name] += strings.Count(stdStr, warning)
  265. }
  266. }
  267. }
  268. }
  269. func ParseBoolPtr(str string) *bool {
  270. var ret *bool
  271. if str != "" {
  272. b := str == "true"
  273. ret = &b
  274. }
  275. return ret
  276. }