blob: c3b0381780351782d054bb921edb677b21a5b946 [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")
MarkDacekc9d5bd82022-10-26 18:21:55 +000053
Dan Willemsenc2af0be2017-01-20 14:10:01 -080054var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
55var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
56
Dan Willemsen5ed900b2017-05-07 11:40:30 -070057var buildVariant = flag.String("variant", "eng", "build variant to use")
58
Dan Willemsen9609ad92019-12-05 15:22:41 -080059var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
60var shard = flag.Int("shard", 1, "1-indexed shard to execute")
61
Colin Crossf2f3d312020-12-17 11:29:31 -080062var skipProducts multipleStringArg
63var includeProducts multipleStringArg
64
65func init() {
66 flag.Var(&skipProducts, "skip-products", "comma-separated list of products to skip (known failures, etc)")
67 flag.Var(&includeProducts, "products", "comma-separated list of products to build")
68}
69
70// multipleStringArg is a flag.Value that takes comma separated lists and converts them to a
71// []string. The argument can be passed multiple times to append more values.
72type multipleStringArg []string
73
74func (m *multipleStringArg) String() string {
75 return strings.Join(*m, `, `)
76}
77
78func (m *multipleStringArg) Set(s string) error {
79 *m = append(*m, strings.Split(s, ",")...)
80 return nil
81}
82
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +020083const errorLeadingLines = 20
84const errorTrailingLines = 20
85
86func errMsgFromLog(filename string) string {
87 if filename == "" {
88 return ""
89 }
90
91 data, err := ioutil.ReadFile(filename)
92 if err != nil {
93 return ""
94 }
95
96 lines := strings.Split(strings.TrimSpace(string(data)), "\n")
97 if len(lines) > errorLeadingLines+errorTrailingLines+1 {
98 lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
99 len(lines)-errorLeadingLines-errorTrailingLines)
100
101 lines = append(lines[:errorLeadingLines+1],
102 lines[len(lines)-errorTrailingLines:]...)
103 }
104 var buf strings.Builder
105 for _, line := range lines {
106 buf.WriteString("> ")
107 buf.WriteString(line)
108 buf.WriteString("\n")
109 }
110 return buf.String()
111}
112
Dan Willemsen22de2162017-12-11 14:35:23 -0800113// TODO(b/70370883): This tool uses a lot of open files -- over the default
114// soft limit of 1024 on some systems. So bump up to the hard limit until I fix
115// the algorithm.
116func setMaxFiles(log logger.Logger) {
117 var limits syscall.Rlimit
118
119 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
120 if err != nil {
121 log.Println("Failed to get file limit:", err)
122 return
123 }
124
125 log.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
126 if limits.Cur == limits.Max {
127 return
128 }
129
130 limits.Cur = limits.Max
131 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
132 if err != nil {
133 log.Println("Failed to increase file limit:", err)
134 }
135}
136
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700137func inList(str string, list []string) bool {
138 for _, other := range list {
139 if str == other {
140 return true
141 }
142 }
143 return false
144}
145
Dan Willemsen41538382018-08-31 19:51:35 -0700146func copyFile(from, to string) error {
147 fromFile, err := os.Open(from)
148 if err != nil {
149 return err
150 }
151 defer fromFile.Close()
152
153 toFile, err := os.Create(to)
154 if err != nil {
155 return err
156 }
157 defer toFile.Close()
158
159 _, err = io.Copy(toFile, fromFile)
160 return err
161}
162
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700163type mpContext struct {
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200164 Logger logger.Logger
165 Status status.ToolStatus
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700166
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200167 SoongUi string
168 MainOutDir string
169 MainLogsDir string
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700170}
171
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200172func findNamedProducts(soongUi string, log logger.Logger) []string {
173 cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
174 output, err := cmd.Output()
175 if err != nil {
176 log.Fatalf("Cannot determine named products: %v", err)
177 }
178
179 rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
180 match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
181 return strings.Fields(match[1])
182}
183
184// ensureEmptyFileExists ensures that the containing directory exists, and the
185// specified file exists. If it doesn't exist, it will write an empty file.
186func ensureEmptyFileExists(file string, log logger.Logger) {
187 if _, err := os.Stat(file); os.IsNotExist(err) {
188 f, err := os.Create(file)
189 if err != nil {
190 log.Fatalf("Error creating %s: %q\n", file, err)
191 }
192 f.Close()
193 } else if err != nil {
194 log.Fatalf("Error checking %s: %q\n", file, err)
195 }
196}
197
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200198func outDirBase() string {
199 outDirBase := os.Getenv("OUT_DIR")
200 if outDirBase == "" {
201 return "out"
202 } else {
203 return outDirBase
204 }
205}
206
207func distDir(outDir string) string {
208 if distDir := os.Getenv("DIST_DIR"); distDir != "" {
209 return filepath.Clean(distDir)
210 } else {
211 return filepath.Join(outDir, "dist")
212 }
213}
214
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800215func forceAnsiOutput() bool {
216 value := os.Getenv("SOONG_UI_ANSI_OUTPUT")
217 return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
218}
219
MarkDacekc9d5bd82022-10-26 18:21:55 +0000220func getBazelArg() string {
221 count := 0
222 str := ""
223 if *bazelMode {
224 count++
225 str = "--bazel-mode"
226 }
227 if *bazelModeStaging {
228 count++
229 str = "--bazel-mode-staging"
230 }
MarkDacekc9d5bd82022-10-26 18:21:55 +0000231
232 if count > 1 {
233 // Can't set more than one
234 fmt.Errorf("Only one bazel mode is permitted to be set.")
235 os.Exit(1)
236 }
237
238 return str
239}
240
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800241func main() {
Colin Cross097ed2a2019-06-08 21:48:58 -0700242 stdio := terminal.StdioImpl{}
Dan Willemsenb82471a2018-05-17 16:37:09 -0700243
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800244 output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false,
245 forceAnsiOutput())
Colin Crosse0df1a32019-06-09 19:40:08 -0700246 log := logger.New(output)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800247 defer log.Cleanup()
248
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200249 for _, v := range os.Environ() {
250 log.Println("Environment: " + v)
251 }
252
253 log.Printf("Argv: %v\n", os.Args)
254
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800255 flag.Parse()
256
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200257 _, cancel := context.WithCancel(context.Background())
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800258 defer cancel()
259
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700260 trace := tracer.New(log)
261 defer trace.Close()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800262
Dan Willemsenb82471a2018-05-17 16:37:09 -0700263 stat := &status.Status{}
264 defer stat.Finish()
Colin Crosse0df1a32019-06-09 19:40:08 -0700265 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700266
Dan Willemsen34218ec2018-07-19 22:57:18 -0700267 var failures failureCount
268 stat.AddOutput(&failures)
269
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200270 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700271 trace.Close()
272 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700273 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700274 })
275
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200276 soongUi := "build/soong/soong_ui.bash"
277
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200278 var outputDir string
279 if *outDir != "" {
280 outputDir = *outDir
281 } else {
Dan Willemsen41538382018-08-31 19:51:35 -0700282 name := "multiproduct"
283 if !*incremental {
284 name += "-" + time.Now().Format("20060102150405")
285 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200286 outputDir = filepath.Join(outDirBase(), name)
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200287 }
288
289 log.Println("Output directory:", outputDir)
290
291 // The ninja_build file is used by our buildbots to understand that the output
292 // can be parsed as ninja output.
293 if err := os.MkdirAll(outputDir, 0777); err != nil {
294 log.Fatalf("Failed to create output directory: %v", err)
295 }
296 ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
297
298 logsDir := filepath.Join(outputDir, "logs")
Dan Willemsene3480762017-11-07 11:23:27 -0800299 os.MkdirAll(logsDir, 0777)
300
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200301 var configLogsDir string
302 if *alternateResultDir {
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200303 configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200304 } else {
305 configLogsDir = outputDir
306 }
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000307
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200308 log.Println("Logs dir: " + configLogsDir)
309
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200310 os.MkdirAll(configLogsDir, 0777)
311 log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
312 trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800313
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800314 var jobs = *numJobs
315 if jobs < 1 {
316 jobs = runtime.NumCPU() / 4
317
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200318 ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
Cole Faust36b259f2023-01-11 14:19:32 -0800319 if ramJobs := ramGb / 40; ramGb > 0 && jobs > ramJobs {
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800320 jobs = ramJobs
321 }
322
323 if jobs < 1 {
324 jobs = 1
325 }
326 }
327 log.Verbosef("Using %d parallel jobs", jobs)
328
Dan Willemsen22de2162017-12-11 14:35:23 -0800329 setMaxFiles(log)
330
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200331 allProducts := findNamedProducts(soongUi, log)
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700332 var productsList []string
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700333
Colin Crossf2f3d312020-12-17 11:29:31 -0800334 if len(includeProducts) > 0 {
335 var missingProducts []string
336 for _, product := range includeProducts {
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700337 if inList(product, allProducts) {
338 productsList = append(productsList, product)
339 } else {
340 missingProducts = append(missingProducts, product)
341 }
342 }
343 if len(missingProducts) > 0 {
344 log.Fatalf("Products don't exist: %s\n", missingProducts)
345 }
346 } else {
347 productsList = allProducts
348 }
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700349
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700350 finalProductsList := make([]string, 0, len(productsList))
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700351 skipProduct := func(p string) bool {
Colin Crossf2f3d312020-12-17 11:29:31 -0800352 for _, s := range skipProducts {
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700353 if p == s {
354 return true
355 }
356 }
357 return false
358 }
359 for _, product := range productsList {
360 if !skipProduct(product) {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700361 finalProductsList = append(finalProductsList, product)
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700362 } else {
363 log.Verbose("Skipping: ", product)
364 }
365 }
366
Dan Willemsen9609ad92019-12-05 15:22:41 -0800367 if *shard < 1 {
368 log.Fatalf("--shard value must be >= 1, not %d\n", *shard)
369 } else if *shardCount < 1 {
370 log.Fatalf("--shard-count value must be >= 1, not %d\n", *shardCount)
371 } else if *shard > *shardCount {
372 log.Fatalf("--shard (%d) must not be greater than --shard-count (%d)\n", *shard,
373 *shardCount)
374 } else if *shardCount > 1 {
375 finalProductsList = splitList(finalProductsList, *shardCount)[*shard-1]
376 }
377
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700378 log.Verbose("Got product list: ", finalProductsList)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800379
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200380 s := stat.StartTool()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700381 s.SetTotalActions(len(finalProductsList))
Dan Willemsena4e43a72017-05-06 16:58:26 -0700382
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700383 mpCtx := &mpContext{
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200384 Logger: log,
385 Status: s,
386 SoongUi: soongUi,
387 MainOutDir: outputDir,
388 MainLogsDir: logsDir,
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800389 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700390
391 products := make(chan string, len(productsList))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800392 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700393 defer close(products)
394 for _, product := range finalProductsList {
395 products <- product
396 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800397 }()
398
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700399 var wg sync.WaitGroup
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800400 for i := 0; i < jobs; i++ {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700401 wg.Add(1)
LaMont Jones610ebf02023-06-21 21:15:48 +0000402 // To smooth out the spikes in memory usage, skew the
403 // initial starting time of the jobs by a small amount.
404 time.Sleep(15 * time.Second)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800405 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700406 defer wg.Done()
407 for {
408 select {
409 case product := <-products:
410 if product == "" {
411 return
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800412 }
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200413 runSoongUiForProduct(mpCtx, product)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700414 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800415 }
416 }()
417 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700418 wg.Wait()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800419
Dan Willemsene3480762017-11-07 11:23:27 -0800420 if *alternateResultDir {
421 args := zip.ZipArgs{
422 FileArgs: []zip.FileArg{
423 {GlobDir: logsDir, SourcePrefixToStrip: logsDir},
424 },
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200425 OutputFilePath: filepath.Join(distDir(outDirBase()), "logs.zip"),
Dan Willemsene3480762017-11-07 11:23:27 -0800426 NumParallelJobs: runtime.NumCPU(),
427 CompressionLevel: 5,
428 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200429 log.Printf("Logs zip: %v\n", args.OutputFilePath)
Colin Cross05518bc2018-09-27 15:06:19 -0700430 if err := zip.Zip(args); err != nil {
Dan Willemsene3480762017-11-07 11:23:27 -0800431 log.Fatalf("Error zipping logs: %v", err)
432 }
433 }
434
Dan Willemsenb82471a2018-05-17 16:37:09 -0700435 s.Finish()
Dan Willemsen34218ec2018-07-19 22:57:18 -0700436
Liz Kammer124967f2022-02-14 15:42:53 -0500437 if failures.count == 1 {
Dan Willemsen34218ec2018-07-19 22:57:18 -0700438 log.Fatal("1 failure")
Liz Kammer124967f2022-02-14 15:42:53 -0500439 } else if failures.count > 1 {
440 log.Fatalf("%d failures %q", failures.count, failures.fails)
Dan Willemsen34218ec2018-07-19 22:57:18 -0700441 } else {
Colin Crosse0df1a32019-06-09 19:40:08 -0700442 fmt.Fprintln(output, "Success")
Dan Willemsen34218ec2018-07-19 22:57:18 -0700443 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800444}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700445
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200446func cleanupAfterProduct(outDir, productZip string) {
447 if *keepArtifacts {
448 args := zip.ZipArgs{
449 FileArgs: []zip.FileArg{
450 {
451 GlobDir: outDir,
452 SourcePrefixToStrip: outDir,
453 },
454 },
455 OutputFilePath: productZip,
456 NumParallelJobs: runtime.NumCPU(),
457 CompressionLevel: 5,
458 }
459 if err := zip.Zip(args); err != nil {
460 log.Fatalf("Error zipping artifacts: %v", err)
461 }
462 }
463 if !*incremental {
464 os.RemoveAll(outDir)
465 }
466}
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700467
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200468func runSoongUiForProduct(mpctx *mpContext, product string) {
469 outDir := filepath.Join(mpctx.MainOutDir, product)
470 logsDir := filepath.Join(mpctx.MainLogsDir, product)
471 productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200472 consoleLogPath := filepath.Join(logsDir, "std.log")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700473
474 if err := os.MkdirAll(outDir, 0777); err != nil {
475 mpctx.Logger.Fatalf("Error creating out directory: %v", err)
476 }
477 if err := os.MkdirAll(logsDir, 0777); err != nil {
478 mpctx.Logger.Fatalf("Error creating log directory: %v", err)
479 }
480
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200481 consoleLogFile, err := os.Create(consoleLogPath)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700482 if err != nil {
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200483 mpctx.Logger.Fatalf("Error creating console log file: %v", err)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700484 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200485 defer consoleLogFile.Close()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700486
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200487 consoleLogWriter := bufio.NewWriter(consoleLogFile)
488 defer consoleLogWriter.Flush()
489
490 args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
491
492 if !*keepArtifacts {
493 args = append(args, "--empty-ninja-file")
494 }
495
496 if *onlyConfig {
497 args = append(args, "--config-only")
498 } else if *onlySoong {
499 args = append(args, "--soong-only")
500 }
501
MarkDacekc9d5bd82022-10-26 18:21:55 +0000502 bazelStr := getBazelArg()
503 if bazelStr != "" {
504 args = append(args, bazelStr)
505 }
506
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200507 cmd := exec.Command(mpctx.SoongUi, args...)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200508 cmd.Stdout = consoleLogWriter
509 cmd.Stderr = consoleLogWriter
510 cmd.Env = append(os.Environ(),
511 "OUT_DIR="+outDir,
512 "TARGET_PRODUCT="+product,
513 "TARGET_BUILD_VARIANT="+*buildVariant,
514 "TARGET_BUILD_TYPE=release",
515 "TARGET_BUILD_APPS=",
Cole Faust81f170f2023-02-17 11:36:35 -0800516 "TARGET_BUILD_UNBUNDLED=",
517 "USE_RBE=false") // Disabling RBE saves ~10 secs per product
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700518
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200519 if *alternateResultDir {
520 cmd.Env = append(cmd.Env,
521 "DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
522 }
523
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700524 action := &status.Action{
525 Description: product,
526 Outputs: []string{product},
527 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200528
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700529 mpctx.Status.StartAction(action)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200530 defer cleanupAfterProduct(outDir, productZip)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700531
532 before := time.Now()
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200533 err = cmd.Run()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700534
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200535 if !*onlyConfig && !*onlySoong {
536 katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
537 if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
538 err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700539 if err != nil {
540 log.Fatalf("Error copying log file: %s", err)
541 }
542 }
543 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200544 var errOutput string
545 if err == nil {
546 errOutput = ""
547 } else {
548 errOutput = errMsgFromLog(consoleLogPath)
549 }
550
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700551 mpctx.Status.FinishAction(status.ActionResult{
552 Action: action,
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200553 Error: err,
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200554 Output: errOutput,
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700555 })
556}
557
Liz Kammer124967f2022-02-14 15:42:53 -0500558type failureCount struct {
559 count int
560 fails []string
561}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700562
563func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
564
565func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
566 if result.Error != nil {
Liz Kammer124967f2022-02-14 15:42:53 -0500567 f.count += 1
568 f.fails = append(f.fails, result.Action.Description)
Dan Willemsen34218ec2018-07-19 22:57:18 -0700569 }
570}
571
572func (f *failureCount) Message(level status.MsgLevel, message string) {
573 if level >= status.ErrorLvl {
Liz Kammer124967f2022-02-14 15:42:53 -0500574 f.count += 1
Dan Willemsen34218ec2018-07-19 22:57:18 -0700575 }
576}
577
578func (f *failureCount) Flush() {}
Colin Crosse0df1a32019-06-09 19:40:08 -0700579
580func (f *failureCount) Write(p []byte) (int, error) {
581 // discard writes
582 return len(p), nil
583}
Dan Willemsen9609ad92019-12-05 15:22:41 -0800584
585func splitList(list []string, shardCount int) (ret [][]string) {
586 each := len(list) / shardCount
587 extra := len(list) % shardCount
588 for i := 0; i < shardCount; i++ {
589 count := each
590 if extra > 0 {
591 count += 1
592 extra -= 1
593 }
594 ret = append(ret, list[:count])
595 list = list[count:]
596 }
597 return
598}