blob: 7cb8ab720d5d7b99bd04c487f1b99f2cd3ebb42b [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
51var onlyConfig = flag.Bool("only-config", false, "Only run product config (not Soong or Kati)")
52var onlySoong = flag.Bool("only-soong", false, "Only run product config and Soong (not Kati)")
53
Dan Willemsen5ed900b2017-05-07 11:40:30 -070054var buildVariant = flag.String("variant", "eng", "build variant to use")
55
Dan Willemsen9609ad92019-12-05 15:22:41 -080056var shardCount = flag.Int("shard-count", 1, "split the products into multiple shards (to spread the build onto multiple machines, etc)")
57var shard = flag.Int("shard", 1, "1-indexed shard to execute")
58
Colin Crossf2f3d312020-12-17 11:29:31 -080059var skipProducts multipleStringArg
60var includeProducts multipleStringArg
61
62func init() {
63 flag.Var(&skipProducts, "skip-products", "comma-separated list of products to skip (known failures, etc)")
64 flag.Var(&includeProducts, "products", "comma-separated list of products to build")
65}
66
67// multipleStringArg is a flag.Value that takes comma separated lists and converts them to a
68// []string. The argument can be passed multiple times to append more values.
69type multipleStringArg []string
70
71func (m *multipleStringArg) String() string {
72 return strings.Join(*m, `, `)
73}
74
75func (m *multipleStringArg) Set(s string) error {
76 *m = append(*m, strings.Split(s, ",")...)
77 return nil
78}
79
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +020080const errorLeadingLines = 20
81const errorTrailingLines = 20
82
83func errMsgFromLog(filename string) string {
84 if filename == "" {
85 return ""
86 }
87
88 data, err := ioutil.ReadFile(filename)
89 if err != nil {
90 return ""
91 }
92
93 lines := strings.Split(strings.TrimSpace(string(data)), "\n")
94 if len(lines) > errorLeadingLines+errorTrailingLines+1 {
95 lines[errorLeadingLines] = fmt.Sprintf("... skipping %d lines ...",
96 len(lines)-errorLeadingLines-errorTrailingLines)
97
98 lines = append(lines[:errorLeadingLines+1],
99 lines[len(lines)-errorTrailingLines:]...)
100 }
101 var buf strings.Builder
102 for _, line := range lines {
103 buf.WriteString("> ")
104 buf.WriteString(line)
105 buf.WriteString("\n")
106 }
107 return buf.String()
108}
109
Dan Willemsen22de2162017-12-11 14:35:23 -0800110// TODO(b/70370883): This tool uses a lot of open files -- over the default
111// soft limit of 1024 on some systems. So bump up to the hard limit until I fix
112// the algorithm.
113func setMaxFiles(log logger.Logger) {
114 var limits syscall.Rlimit
115
116 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
117 if err != nil {
118 log.Println("Failed to get file limit:", err)
119 return
120 }
121
122 log.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
123 if limits.Cur == limits.Max {
124 return
125 }
126
127 limits.Cur = limits.Max
128 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
129 if err != nil {
130 log.Println("Failed to increase file limit:", err)
131 }
132}
133
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700134func inList(str string, list []string) bool {
135 for _, other := range list {
136 if str == other {
137 return true
138 }
139 }
140 return false
141}
142
Dan Willemsen41538382018-08-31 19:51:35 -0700143func copyFile(from, to string) error {
144 fromFile, err := os.Open(from)
145 if err != nil {
146 return err
147 }
148 defer fromFile.Close()
149
150 toFile, err := os.Create(to)
151 if err != nil {
152 return err
153 }
154 defer toFile.Close()
155
156 _, err = io.Copy(toFile, fromFile)
157 return err
158}
159
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700160type mpContext struct {
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200161 Logger logger.Logger
162 Status status.ToolStatus
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700163
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200164 SoongUi string
165 MainOutDir string
166 MainLogsDir string
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700167}
168
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200169func findNamedProducts(soongUi string, log logger.Logger) []string {
170 cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
171 output, err := cmd.Output()
172 if err != nil {
173 log.Fatalf("Cannot determine named products: %v", err)
174 }
175
176 rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
177 match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
178 return strings.Fields(match[1])
179}
180
181// ensureEmptyFileExists ensures that the containing directory exists, and the
182// specified file exists. If it doesn't exist, it will write an empty file.
183func ensureEmptyFileExists(file string, log logger.Logger) {
184 if _, err := os.Stat(file); os.IsNotExist(err) {
185 f, err := os.Create(file)
186 if err != nil {
187 log.Fatalf("Error creating %s: %q\n", file, err)
188 }
189 f.Close()
190 } else if err != nil {
191 log.Fatalf("Error checking %s: %q\n", file, err)
192 }
193}
194
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200195func outDirBase() string {
196 outDirBase := os.Getenv("OUT_DIR")
197 if outDirBase == "" {
198 return "out"
199 } else {
200 return outDirBase
201 }
202}
203
204func distDir(outDir string) string {
205 if distDir := os.Getenv("DIST_DIR"); distDir != "" {
206 return filepath.Clean(distDir)
207 } else {
208 return filepath.Join(outDir, "dist")
209 }
210}
211
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800212func forceAnsiOutput() bool {
213 value := os.Getenv("SOONG_UI_ANSI_OUTPUT")
214 return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
215}
216
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800217func main() {
Colin Cross097ed2a2019-06-08 21:48:58 -0700218 stdio := terminal.StdioImpl{}
Dan Willemsenb82471a2018-05-17 16:37:09 -0700219
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800220 output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false,
221 forceAnsiOutput())
Colin Crosse0df1a32019-06-09 19:40:08 -0700222 log := logger.New(output)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800223 defer log.Cleanup()
224
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200225 for _, v := range os.Environ() {
226 log.Println("Environment: " + v)
227 }
228
229 log.Printf("Argv: %v\n", os.Args)
230
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800231 flag.Parse()
232
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200233 _, cancel := context.WithCancel(context.Background())
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800234 defer cancel()
235
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700236 trace := tracer.New(log)
237 defer trace.Close()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800238
Dan Willemsenb82471a2018-05-17 16:37:09 -0700239 stat := &status.Status{}
240 defer stat.Finish()
Colin Crosse0df1a32019-06-09 19:40:08 -0700241 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700242
Dan Willemsen34218ec2018-07-19 22:57:18 -0700243 var failures failureCount
244 stat.AddOutput(&failures)
245
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200246 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700247 trace.Close()
248 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700249 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700250 })
251
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200252 soongUi := "build/soong/soong_ui.bash"
253
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200254 var outputDir string
255 if *outDir != "" {
256 outputDir = *outDir
257 } else {
Dan Willemsen41538382018-08-31 19:51:35 -0700258 name := "multiproduct"
259 if !*incremental {
260 name += "-" + time.Now().Format("20060102150405")
261 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200262 outputDir = filepath.Join(outDirBase(), name)
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200263 }
264
265 log.Println("Output directory:", outputDir)
266
267 // The ninja_build file is used by our buildbots to understand that the output
268 // can be parsed as ninja output.
269 if err := os.MkdirAll(outputDir, 0777); err != nil {
270 log.Fatalf("Failed to create output directory: %v", err)
271 }
272 ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
273
274 logsDir := filepath.Join(outputDir, "logs")
Dan Willemsene3480762017-11-07 11:23:27 -0800275 os.MkdirAll(logsDir, 0777)
276
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200277 var configLogsDir string
278 if *alternateResultDir {
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200279 configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200280 } else {
281 configLogsDir = outputDir
282 }
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000283
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200284 log.Println("Logs dir: " + configLogsDir)
285
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200286 os.MkdirAll(configLogsDir, 0777)
287 log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
288 trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800289
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800290 var jobs = *numJobs
291 if jobs < 1 {
292 jobs = runtime.NumCPU() / 4
293
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200294 ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
Colin Cross5cb73662021-10-18 09:54:11 -0700295 if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800296 jobs = ramJobs
297 }
298
299 if jobs < 1 {
300 jobs = 1
301 }
302 }
303 log.Verbosef("Using %d parallel jobs", jobs)
304
Dan Willemsen22de2162017-12-11 14:35:23 -0800305 setMaxFiles(log)
306
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200307 allProducts := findNamedProducts(soongUi, log)
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700308 var productsList []string
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700309
Colin Crossf2f3d312020-12-17 11:29:31 -0800310 if len(includeProducts) > 0 {
311 var missingProducts []string
312 for _, product := range includeProducts {
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700313 if inList(product, allProducts) {
314 productsList = append(productsList, product)
315 } else {
316 missingProducts = append(missingProducts, product)
317 }
318 }
319 if len(missingProducts) > 0 {
320 log.Fatalf("Products don't exist: %s\n", missingProducts)
321 }
322 } else {
323 productsList = allProducts
324 }
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700325
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700326 finalProductsList := make([]string, 0, len(productsList))
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700327 skipProduct := func(p string) bool {
Colin Crossf2f3d312020-12-17 11:29:31 -0800328 for _, s := range skipProducts {
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700329 if p == s {
330 return true
331 }
332 }
333 return false
334 }
335 for _, product := range productsList {
336 if !skipProduct(product) {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700337 finalProductsList = append(finalProductsList, product)
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700338 } else {
339 log.Verbose("Skipping: ", product)
340 }
341 }
342
Dan Willemsen9609ad92019-12-05 15:22:41 -0800343 if *shard < 1 {
344 log.Fatalf("--shard value must be >= 1, not %d\n", *shard)
345 } else if *shardCount < 1 {
346 log.Fatalf("--shard-count value must be >= 1, not %d\n", *shardCount)
347 } else if *shard > *shardCount {
348 log.Fatalf("--shard (%d) must not be greater than --shard-count (%d)\n", *shard,
349 *shardCount)
350 } else if *shardCount > 1 {
351 finalProductsList = splitList(finalProductsList, *shardCount)[*shard-1]
352 }
353
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700354 log.Verbose("Got product list: ", finalProductsList)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800355
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200356 s := stat.StartTool()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700357 s.SetTotalActions(len(finalProductsList))
Dan Willemsena4e43a72017-05-06 16:58:26 -0700358
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700359 mpCtx := &mpContext{
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200360 Logger: log,
361 Status: s,
362 SoongUi: soongUi,
363 MainOutDir: outputDir,
364 MainLogsDir: logsDir,
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800365 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700366
367 products := make(chan string, len(productsList))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800368 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700369 defer close(products)
370 for _, product := range finalProductsList {
371 products <- product
372 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800373 }()
374
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700375 var wg sync.WaitGroup
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800376 for i := 0; i < jobs; i++ {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700377 wg.Add(1)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800378 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700379 defer wg.Done()
380 for {
381 select {
382 case product := <-products:
383 if product == "" {
384 return
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800385 }
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200386 runSoongUiForProduct(mpCtx, product)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700387 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800388 }
389 }()
390 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700391 wg.Wait()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800392
Dan Willemsene3480762017-11-07 11:23:27 -0800393 if *alternateResultDir {
394 args := zip.ZipArgs{
395 FileArgs: []zip.FileArg{
396 {GlobDir: logsDir, SourcePrefixToStrip: logsDir},
397 },
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200398 OutputFilePath: filepath.Join(distDir(outDirBase()), "logs.zip"),
Dan Willemsene3480762017-11-07 11:23:27 -0800399 NumParallelJobs: runtime.NumCPU(),
400 CompressionLevel: 5,
401 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200402 log.Printf("Logs zip: %v\n", args.OutputFilePath)
Colin Cross05518bc2018-09-27 15:06:19 -0700403 if err := zip.Zip(args); err != nil {
Dan Willemsene3480762017-11-07 11:23:27 -0800404 log.Fatalf("Error zipping logs: %v", err)
405 }
406 }
407
Dan Willemsenb82471a2018-05-17 16:37:09 -0700408 s.Finish()
Dan Willemsen34218ec2018-07-19 22:57:18 -0700409
Liz Kammer124967f2022-02-14 15:42:53 -0500410 if failures.count == 1 {
Dan Willemsen34218ec2018-07-19 22:57:18 -0700411 log.Fatal("1 failure")
Liz Kammer124967f2022-02-14 15:42:53 -0500412 } else if failures.count > 1 {
413 log.Fatalf("%d failures %q", failures.count, failures.fails)
Dan Willemsen34218ec2018-07-19 22:57:18 -0700414 } else {
Colin Crosse0df1a32019-06-09 19:40:08 -0700415 fmt.Fprintln(output, "Success")
Dan Willemsen34218ec2018-07-19 22:57:18 -0700416 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800417}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700418
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200419func cleanupAfterProduct(outDir, productZip string) {
420 if *keepArtifacts {
421 args := zip.ZipArgs{
422 FileArgs: []zip.FileArg{
423 {
424 GlobDir: outDir,
425 SourcePrefixToStrip: outDir,
426 },
427 },
428 OutputFilePath: productZip,
429 NumParallelJobs: runtime.NumCPU(),
430 CompressionLevel: 5,
431 }
432 if err := zip.Zip(args); err != nil {
433 log.Fatalf("Error zipping artifacts: %v", err)
434 }
435 }
436 if !*incremental {
437 os.RemoveAll(outDir)
438 }
439}
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700440
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200441func runSoongUiForProduct(mpctx *mpContext, product string) {
442 outDir := filepath.Join(mpctx.MainOutDir, product)
443 logsDir := filepath.Join(mpctx.MainLogsDir, product)
444 productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200445 consoleLogPath := filepath.Join(logsDir, "std.log")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700446
447 if err := os.MkdirAll(outDir, 0777); err != nil {
448 mpctx.Logger.Fatalf("Error creating out directory: %v", err)
449 }
450 if err := os.MkdirAll(logsDir, 0777); err != nil {
451 mpctx.Logger.Fatalf("Error creating log directory: %v", err)
452 }
453
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200454 consoleLogFile, err := os.Create(consoleLogPath)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700455 if err != nil {
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200456 mpctx.Logger.Fatalf("Error creating console log file: %v", err)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700457 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200458 defer consoleLogFile.Close()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700459
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200460 consoleLogWriter := bufio.NewWriter(consoleLogFile)
461 defer consoleLogWriter.Flush()
462
463 args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
464
465 if !*keepArtifacts {
466 args = append(args, "--empty-ninja-file")
467 }
468
469 if *onlyConfig {
470 args = append(args, "--config-only")
471 } else if *onlySoong {
472 args = append(args, "--soong-only")
473 }
474
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200475 cmd := exec.Command(mpctx.SoongUi, args...)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200476 cmd.Stdout = consoleLogWriter
477 cmd.Stderr = consoleLogWriter
478 cmd.Env = append(os.Environ(),
479 "OUT_DIR="+outDir,
480 "TARGET_PRODUCT="+product,
481 "TARGET_BUILD_VARIANT="+*buildVariant,
482 "TARGET_BUILD_TYPE=release",
483 "TARGET_BUILD_APPS=",
484 "TARGET_BUILD_UNBUNDLED=")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700485
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200486 if *alternateResultDir {
487 cmd.Env = append(cmd.Env,
488 "DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
489 }
490
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700491 action := &status.Action{
492 Description: product,
493 Outputs: []string{product},
494 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200495
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700496 mpctx.Status.StartAction(action)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200497 defer cleanupAfterProduct(outDir, productZip)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700498
499 before := time.Now()
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200500 err = cmd.Run()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700501
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200502 if !*onlyConfig && !*onlySoong {
503 katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
504 if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
505 err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700506 if err != nil {
507 log.Fatalf("Error copying log file: %s", err)
508 }
509 }
510 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200511 var errOutput string
512 if err == nil {
513 errOutput = ""
514 } else {
515 errOutput = errMsgFromLog(consoleLogPath)
516 }
517
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700518 mpctx.Status.FinishAction(status.ActionResult{
519 Action: action,
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200520 Error: err,
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200521 Output: errOutput,
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700522 })
523}
524
Liz Kammer124967f2022-02-14 15:42:53 -0500525type failureCount struct {
526 count int
527 fails []string
528}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700529
530func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
531
532func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
533 if result.Error != nil {
Liz Kammer124967f2022-02-14 15:42:53 -0500534 f.count += 1
535 f.fails = append(f.fails, result.Action.Description)
Dan Willemsen34218ec2018-07-19 22:57:18 -0700536 }
537}
538
539func (f *failureCount) Message(level status.MsgLevel, message string) {
540 if level >= status.ErrorLvl {
Liz Kammer124967f2022-02-14 15:42:53 -0500541 f.count += 1
Dan Willemsen34218ec2018-07-19 22:57:18 -0700542 }
543}
544
545func (f *failureCount) Flush() {}
Colin Crosse0df1a32019-06-09 19:40:08 -0700546
547func (f *failureCount) Write(p []byte) (int, error) {
548 // discard writes
549 return len(p), nil
550}
Dan Willemsen9609ad92019-12-05 15:22:41 -0800551
552func splitList(list []string, shardCount int) (ret [][]string) {
553 each := len(list) / shardCount
554 extra := len(list) % shardCount
555 for i := 0; i < shardCount; i++ {
556 count := each
557 if extra > 0 {
558 count += 1
559 extra -= 1
560 }
561 ret = append(ret, list[:count])
562 list = list[count:]
563 }
564 return
565}