blob: d8d5e5d75ebe2747724f6a2e3d1d4864ec0aa261 [file] [log] [blame]
Dan Willemsenc2af0be2017-01-20 14:10:01 -08001// Copyright 2017 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
15package main
16
17import (
Lukacs T. Berkicef87b62021-08-10 15:01:13 +020018 "bufio"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080019 "context"
20 "flag"
21 "fmt"
Dan Willemsen41538382018-08-31 19:51:35 -070022 "io"
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +020023 "io/ioutil"
Lukacs T. Berkicef87b62021-08-10 15:01:13 +020024 "log"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080025 "os"
Lukacs T. Berkicef87b62021-08-10 15:01:13 +020026 "os/exec"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080027 "path/filepath"
Lukacs T. Berki2c405692021-08-11 09:19:27 +020028 "regexp"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080029 "runtime"
30 "strings"
31 "sync"
Dan Willemsen22de2162017-12-11 14:35:23 -080032 "syscall"
Steven Moreland552432e2017-03-29 19:26:09 -070033 "time"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080034
Dan Willemsenc2af0be2017-01-20 14:10:01 -080035 "android/soong/ui/logger"
Lukacs T. Berkif656b842021-08-11 11:10:28 +020036 "android/soong/ui/signal"
Dan Willemsenb82471a2018-05-17 16:37:09 -070037 "android/soong/ui/status"
38 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070039 "android/soong/ui/tracer"
Dan Willemsene3480762017-11-07 11:23:27 -080040 "android/soong/zip"
Dan Willemsenc2af0be2017-01-20 14:10:01 -080041)
42
Dan Willemsen1bdbdec2019-12-27 09:54:11 -080043var numJobs = flag.Int("j", 0, "number of parallel jobs [0=autodetect]")
Dan Willemsenc2af0be2017-01-20 14:10:01 -080044
Dan Willemsene3480762017-11-07 11:23:27 -080045var keepArtifacts = flag.Bool("keep", false, "keep archives of artifacts")
Dan Willemsen41538382018-08-31 19:51:35 -070046var incremental = flag.Bool("incremental", false, "run in incremental mode (saving intermediates)")
Dan Willemsenc2af0be2017-01-20 14:10:01 -080047
48var outDir = flag.String("out", "", "path to store output directories (defaults to tmpdir under $OUT when empty)")
Dan Willemsenf624fb92017-05-19 16:39:04 -070049var alternateResultDir = flag.Bool("dist", false, "write select results to $DIST_DIR (or <out>/dist when empty)")
Dan Willemsenc2af0be2017-01-20 14:10:01 -080050
MarkDacekc9d5bd82022-10-26 18:21:55 +000051var bazelMode = flag.Bool("bazel-mode", false, "use bazel for analysis of certain modules")
52var bazelModeStaging = flag.Bool("bazel-mode-staging", false, "use bazel for analysis of certain near-ready modules")
53var bazelModeDev = flag.Bool("bazel-mode-dev", false, "use bazel for analysis of a large number of modules (less stable)")
54
Dan Willemsenc2af0be2017-01-20 14:10:01 -080055var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
56var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
57
Dan Willemsen5ed900b2017-05-07 11:40:30 -070058var buildVariant = flag.String("variant", "eng", "build variant to use")
59
Dan Willemsen9609ad92019-12-05 15:22:41 -080060var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
61var shard = flag.Int("shard", 1, "1-indexed shard to execute")
62
Colin Crossf2f3d312020-12-17 11:29:31 -080063var skipProducts multipleStringArg
64var includeProducts multipleStringArg
65
66func init() {
67 flag.Var(&skipProducts, "skip-products", "comma-separated list of products to skip (known failures, etc)")
68 flag.Var(&includeProducts, "products", "comma-separated list of products to build")
69}
70
71// multipleStringArg is a flag.Value that takes comma separated lists and converts them to a
72// []string. The argument can be passed multiple times to append more values.
73type multipleStringArg []string
74
75func (m *multipleStringArg) String() string {
76 return strings.Join(*m, `, `)
77}
78
79func (m *multipleStringArg) Set(s string) error {
80 *m = append(*m, strings.Split(s, ",")...)
81 return nil
82}
83
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +020084const errorLeadingLines = 20
85const errorTrailingLines = 20
86
87func errMsgFromLog(filename string) string {
88 if filename == "" {
89 return ""
90 }
91
92 data, err := ioutil.ReadFile(filename)
93 if err != nil {
94 return ""
95 }
96
97 lines := strings.Split(strings.TrimSpace(string(data)), "\n")
98 if len(lines) > errorLeadingLines+errorTrailingLines+1 {
99 lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
100 len(lines)-errorLeadingLines-errorTrailingLines)
101
102 lines = append(lines[:errorLeadingLines+1],
103 lines[len(lines)-errorTrailingLines:]...)
104 }
105 var buf strings.Builder
106 for _, line := range lines {
107 buf.WriteString("> ")
108 buf.WriteString(line)
109 buf.WriteString("\n")
110 }
111 return buf.String()
112}
113
Dan Willemsen22de2162017-12-11 14:35:23 -0800114// TODO(b/70370883): This tool uses a lot of open files -- over the default
115// soft limit of 1024 on some systems. So bump up to the hard limit until I fix
116// the algorithm.
117func setMaxFiles(log logger.Logger) {
118 var limits syscall.Rlimit
119
120 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
121 if err != nil {
122 log.Println("Failed to get file limit:", err)
123 return
124 }
125
126 log.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
127 if limits.Cur == limits.Max {
128 return
129 }
130
131 limits.Cur = limits.Max
132 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
133 if err != nil {
134 log.Println("Failed to increase file limit:", err)
135 }
136}
137
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700138func inList(str string, list []string) bool {
139 for _, other := range list {
140 if str == other {
141 return true
142 }
143 }
144 return false
145}
146
Dan Willemsen41538382018-08-31 19:51:35 -0700147func copyFile(from, to string) error {
148 fromFile, err := os.Open(from)
149 if err != nil {
150 return err
151 }
152 defer fromFile.Close()
153
154 toFile, err := os.Create(to)
155 if err != nil {
156 return err
157 }
158 defer toFile.Close()
159
160 _, err = io.Copy(toFile, fromFile)
161 return err
162}
163
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700164type mpContext struct {
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200165 Logger logger.Logger
166 Status status.ToolStatus
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700167
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200168 SoongUi string
169 MainOutDir string
170 MainLogsDir string
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700171}
172
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200173func findNamedProducts(soongUi string, log logger.Logger) []string {
174 cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
175 output, err := cmd.Output()
176 if err != nil {
177 log.Fatalf("Cannot determine named products: %v", err)
178 }
179
180 rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
181 match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
182 return strings.Fields(match[1])
183}
184
185// ensureEmptyFileExists ensures that the containing directory exists, and the
186// specified file exists. If it doesn't exist, it will write an empty file.
187func ensureEmptyFileExists(file string, log logger.Logger) {
188 if _, err := os.Stat(file); os.IsNotExist(err) {
189 f, err := os.Create(file)
190 if err != nil {
191 log.Fatalf("Error creating %s: %q\n", file, err)
192 }
193 f.Close()
194 } else if err != nil {
195 log.Fatalf("Error checking %s: %q\n", file, err)
196 }
197}
198
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200199func outDirBase() string {
200 outDirBase := os.Getenv("OUT_DIR")
201 if outDirBase == "" {
202 return "out"
203 } else {
204 return outDirBase
205 }
206}
207
208func distDir(outDir string) string {
209 if distDir := os.Getenv("DIST_DIR"); distDir != "" {
210 return filepath.Clean(distDir)
211 } else {
212 return filepath.Join(outDir, "dist")
213 }
214}
215
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800216func forceAnsiOutput() bool {
217 value := os.Getenv("SOONG_UI_ANSI_OUTPUT")
218 return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
219}
220
MarkDacekc9d5bd82022-10-26 18:21:55 +0000221func getBazelArg() string {
222 count := 0
223 str := ""
224 if *bazelMode {
225 count++
226 str = "--bazel-mode"
227 }
228 if *bazelModeStaging {
229 count++
230 str = "--bazel-mode-staging"
231 }
232 if *bazelModeDev {
233 count++
234 str = "--bazel-mode-dev"
235 }
236
237 if count > 1 {
238 // Can't set more than one
239 fmt.Errorf("Only one bazel mode is permitted to be set.")
240 os.Exit(1)
241 }
242
243 return str
244}
245
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800246func main() {
Colin Cross097ed2a2019-06-08 21:48:58 -0700247 stdio := terminal.StdioImpl{}
Dan Willemsenb82471a2018-05-17 16:37:09 -0700248
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800249 output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false,
250 forceAnsiOutput())
Colin Crosse0df1a32019-06-09 19:40:08 -0700251 log := logger.New(output)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800252 defer log.Cleanup()
253
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200254 for _, v := range os.Environ() {
255 log.Println("Environment: " + v)
256 }
257
258 log.Printf("Argv: %v\n", os.Args)
259
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800260 flag.Parse()
261
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200262 _, cancel := context.WithCancel(context.Background())
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800263 defer cancel()
264
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700265 trace := tracer.New(log)
266 defer trace.Close()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800267
Dan Willemsenb82471a2018-05-17 16:37:09 -0700268 stat := &status.Status{}
269 defer stat.Finish()
Colin Crosse0df1a32019-06-09 19:40:08 -0700270 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700271
Dan Willemsen34218ec2018-07-19 22:57:18 -0700272 var failures failureCount
273 stat.AddOutput(&failures)
274
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200275 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700276 trace.Close()
277 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700278 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700279 })
280
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200281 soongUi := "build/soong/soong_ui.bash"
282
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200283 var outputDir string
284 if *outDir != "" {
285 outputDir = *outDir
286 } else {
Dan Willemsen41538382018-08-31 19:51:35 -0700287 name := "multiproduct"
288 if !*incremental {
289 name += "-" + time.Now().Format("20060102150405")
290 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200291 outputDir = filepath.Join(outDirBase(), name)
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200292 }
293
294 log.Println("Output directory:", outputDir)
295
296 // The ninja_build file is used by our buildbots to understand that the output
297 // can be parsed as ninja output.
298 if err := os.MkdirAll(outputDir, 0777); err != nil {
299 log.Fatalf("Failed to create output directory: %v", err)
300 }
301 ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
302
303 logsDir := filepath.Join(outputDir, "logs")
Dan Willemsene3480762017-11-07 11:23:27 -0800304 os.MkdirAll(logsDir, 0777)
305
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200306 var configLogsDir string
307 if *alternateResultDir {
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200308 configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200309 } else {
310 configLogsDir = outputDir
311 }
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000312
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200313 log.Println("Logs dir: " + configLogsDir)
314
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200315 os.MkdirAll(configLogsDir, 0777)
316 log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
317 trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800318
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800319 var jobs = *numJobs
320 if jobs < 1 {
321 jobs = runtime.NumCPU() / 4
322
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200323 ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
Colin Cross5cb73662021-10-18 09:54:11 -0700324 if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800325 jobs = ramJobs
326 }
327
328 if jobs < 1 {
329 jobs = 1
330 }
331 }
332 log.Verbosef("Using %d parallel jobs", jobs)
333
Dan Willemsen22de2162017-12-11 14:35:23 -0800334 setMaxFiles(log)
335
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200336 allProducts := findNamedProducts(soongUi, log)
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700337 var productsList []string
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700338
Colin Crossf2f3d312020-12-17 11:29:31 -0800339 if len(includeProducts) > 0 {
340 var missingProducts []string
341 for _, product := range includeProducts {
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700342 if inList(product, allProducts) {
343 productsList = append(productsList, product)
344 } else {
345 missingProducts = append(missingProducts, product)
346 }
347 }
348 if len(missingProducts) > 0 {
349 log.Fatalf("Products don't exist: %s\n", missingProducts)
350 }
351 } else {
352 productsList = allProducts
353 }
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700354
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700355 finalProductsList := make([]string, 0, len(productsList))
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700356 skipProduct := func(p string) bool {
Colin Crossf2f3d312020-12-17 11:29:31 -0800357 for _, s := range skipProducts {
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700358 if p == s {
359 return true
360 }
361 }
362 return false
363 }
364 for _, product := range productsList {
365 if !skipProduct(product) {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700366 finalProductsList = append(finalProductsList, product)
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700367 } else {
368 log.Verbose("Skipping: ", product)
369 }
370 }
371
Dan Willemsen9609ad92019-12-05 15:22:41 -0800372 if *shard < 1 {
373 log.Fatalf("--shard value must be >= 1, not %d\n", *shard)
374 } else if *shardCount < 1 {
375 log.Fatalf("--shard-count value must be >= 1, not %d\n", *shardCount)
376 } else if *shard > *shardCount {
377 log.Fatalf("--shard (%d) must not be greater than --shard-count (%d)\n", *shard,
378 *shardCount)
379 } else if *shardCount > 1 {
380 finalProductsList = splitList(finalProductsList, *shardCount)[*shard-1]
381 }
382
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700383 log.Verbose("Got product list: ", finalProductsList)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800384
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200385 s := stat.StartTool()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700386 s.SetTotalActions(len(finalProductsList))
Dan Willemsena4e43a72017-05-06 16:58:26 -0700387
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700388 mpCtx := &mpContext{
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200389 Logger: log,
390 Status: s,
391 SoongUi: soongUi,
392 MainOutDir: outputDir,
393 MainLogsDir: logsDir,
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800394 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700395
396 products := make(chan string, len(productsList))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800397 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700398 defer close(products)
399 for _, product := range finalProductsList {
400 products <- product
401 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800402 }()
403
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700404 var wg sync.WaitGroup
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800405 for i := 0; i < jobs; i++ {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700406 wg.Add(1)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800407 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700408 defer wg.Done()
409 for {
410 select {
411 case product := <-products:
412 if product == "" {
413 return
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800414 }
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200415 runSoongUiForProduct(mpCtx, product)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700416 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800417 }
418 }()
419 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700420 wg.Wait()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800421
Dan Willemsene3480762017-11-07 11:23:27 -0800422 if *alternateResultDir {
423 args := zip.ZipArgs{
424 FileArgs: []zip.FileArg{
425 {GlobDir: logsDir, SourcePrefixToStrip: logsDir},
426 },
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200427 OutputFilePath: filepath.Join(distDir(outDirBase()), "logs.zip"),
Dan Willemsene3480762017-11-07 11:23:27 -0800428 NumParallelJobs: runtime.NumCPU(),
429 CompressionLevel: 5,
430 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200431 log.Printf("Logs zip: %v\n", args.OutputFilePath)
Colin Cross05518bc2018-09-27 15:06:19 -0700432 if err := zip.Zip(args); err != nil {
Dan Willemsene3480762017-11-07 11:23:27 -0800433 log.Fatalf("Error zipping logs: %v", err)
434 }
435 }
436
Dan Willemsenb82471a2018-05-17 16:37:09 -0700437 s.Finish()
Dan Willemsen34218ec2018-07-19 22:57:18 -0700438
Liz Kammer124967f2022-02-14 15:42:53 -0500439 if failures.count == 1 {
Dan Willemsen34218ec2018-07-19 22:57:18 -0700440 log.Fatal("1 failure")
Liz Kammer124967f2022-02-14 15:42:53 -0500441 } else if failures.count > 1 {
442 log.Fatalf("%d failures %q", failures.count, failures.fails)
Dan Willemsen34218ec2018-07-19 22:57:18 -0700443 } else {
Colin Crosse0df1a32019-06-09 19:40:08 -0700444 fmt.Fprintln(output, "Success")
Dan Willemsen34218ec2018-07-19 22:57:18 -0700445 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800446}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700447
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200448func cleanupAfterProduct(outDir, productZip string) {
449 if *keepArtifacts {
450 args := zip.ZipArgs{
451 FileArgs: []zip.FileArg{
452 {
453 GlobDir: outDir,
454 SourcePrefixToStrip: outDir,
455 },
456 },
457 OutputFilePath: productZip,
458 NumParallelJobs: runtime.NumCPU(),
459 CompressionLevel: 5,
460 }
461 if err := zip.Zip(args); err != nil {
462 log.Fatalf("Error zipping artifacts: %v", err)
463 }
464 }
465 if !*incremental {
466 os.RemoveAll(outDir)
467 }
468}
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700469
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200470func runSoongUiForProduct(mpctx *mpContext, product string) {
471 outDir := filepath.Join(mpctx.MainOutDir, product)
472 logsDir := filepath.Join(mpctx.MainLogsDir, product)
473 productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200474 consoleLogPath := filepath.Join(logsDir, "std.log")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700475
476 if err := os.MkdirAll(outDir, 0777); err != nil {
477 mpctx.Logger.Fatalf("Error creating out directory: %v", err)
478 }
479 if err := os.MkdirAll(logsDir, 0777); err != nil {
480 mpctx.Logger.Fatalf("Error creating log directory: %v", err)
481 }
482
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200483 consoleLogFile, err := os.Create(consoleLogPath)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700484 if err != nil {
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200485 mpctx.Logger.Fatalf("Error creating console log file: %v", err)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700486 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200487 defer consoleLogFile.Close()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700488
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200489 consoleLogWriter := bufio.NewWriter(consoleLogFile)
490 defer consoleLogWriter.Flush()
491
492 args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
493
494 if !*keepArtifacts {
495 args = append(args, "--empty-ninja-file")
496 }
497
498 if *onlyConfig {
499 args = append(args, "--config-only")
500 } else if *onlySoong {
501 args = append(args, "--soong-only")
502 }
503
MarkDacekc9d5bd82022-10-26 18:21:55 +0000504 bazelStr := getBazelArg()
505 if bazelStr != "" {
506 args = append(args, bazelStr)
507 }
508
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200509 cmd := exec.Command(mpctx.SoongUi, args...)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200510 cmd.Stdout = consoleLogWriter
511 cmd.Stderr = consoleLogWriter
512 cmd.Env = append(os.Environ(),
513 "OUT_DIR="+outDir,
514 "TARGET_PRODUCT="+product,
515 "TARGET_BUILD_VARIANT="+*buildVariant,
516 "TARGET_BUILD_TYPE=release",
517 "TARGET_BUILD_APPS=",
518 "TARGET_BUILD_UNBUNDLED=")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700519
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200520 if *alternateResultDir {
521 cmd.Env = append(cmd.Env,
522 "DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
523 }
524
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700525 action := &status.Action{
526 Description: product,
527 Outputs: []string{product},
528 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200529
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700530 mpctx.Status.StartAction(action)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200531 defer cleanupAfterProduct(outDir, productZip)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700532
533 before := time.Now()
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200534 err = cmd.Run()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700535
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200536 if !*onlyConfig && !*onlySoong {
537 katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
538 if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
539 err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700540 if err != nil {
541 log.Fatalf("Error copying log file: %s", err)
542 }
543 }
544 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200545 var errOutput string
546 if err == nil {
547 errOutput = ""
548 } else {
549 errOutput = errMsgFromLog(consoleLogPath)
550 }
551
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700552 mpctx.Status.FinishAction(status.ActionResult{
553 Action: action,
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200554 Error: err,
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200555 Output: errOutput,
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700556 })
557}
558
Liz Kammer124967f2022-02-14 15:42:53 -0500559type failureCount struct {
560 count int
561 fails []string
562}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700563
564func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
565
566func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
567 if result.Error != nil {
Liz Kammer124967f2022-02-14 15:42:53 -0500568 f.count += 1
569 f.fails = append(f.fails, result.Action.Description)
Dan Willemsen34218ec2018-07-19 22:57:18 -0700570 }
571}
572
573func (f *failureCount) Message(level status.MsgLevel, message string) {
574 if level >= status.ErrorLvl {
Liz Kammer124967f2022-02-14 15:42:53 -0500575 f.count += 1
Dan Willemsen34218ec2018-07-19 22:57:18 -0700576 }
577}
578
579func (f *failureCount) Flush() {}
Colin Crosse0df1a32019-06-09 19:40:08 -0700580
581func (f *failureCount) Write(p []byte) (int, error) {
582 // discard writes
583 return len(p), nil
584}
Dan Willemsen9609ad92019-12-05 15:22:41 -0800585
586func splitList(list []string, shardCount int) (ret [][]string) {
587 each := len(list) / shardCount
588 extra := len(list) % shardCount
589 for i := 0; i < shardCount; i++ {
590 count := each
591 if extra > 0 {
592 count += 1
593 extra -= 1
594 }
595 ret = append(ret, list[:count])
596 list = list[count:]
597 }
598 return
599}