blob: 07b1e3fc4c52af6c3d16c18662b165aac4bb2482 [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 detectTotalRAM() uint64 {
170 var info syscall.Sysinfo_t
171 err := syscall.Sysinfo(&info)
172 if err != nil {
173 panic(err)
174 }
175 return info.Totalram * uint64(info.Unit)
176}
177
178func findNamedProducts(soongUi string, log logger.Logger) []string {
179 cmd := exec.Command(soongUi, "--dumpvars-mode", "--vars=all_named_products")
180 output, err := cmd.Output()
181 if err != nil {
182 log.Fatalf("Cannot determine named products: %v", err)
183 }
184
185 rx := regexp.MustCompile(`^all_named_products='(.*)'$`)
186 match := rx.FindStringSubmatch(strings.TrimSpace(string(output)))
187 return strings.Fields(match[1])
188}
189
190// ensureEmptyFileExists ensures that the containing directory exists, and the
191// specified file exists. If it doesn't exist, it will write an empty file.
192func ensureEmptyFileExists(file string, log logger.Logger) {
193 if _, err := os.Stat(file); os.IsNotExist(err) {
194 f, err := os.Create(file)
195 if err != nil {
196 log.Fatalf("Error creating %s: %q\n", file, err)
197 }
198 f.Close()
199 } else if err != nil {
200 log.Fatalf("Error checking %s: %q\n", file, err)
201 }
202}
203
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200204func outDirBase() string {
205 outDirBase := os.Getenv("OUT_DIR")
206 if outDirBase == "" {
207 return "out"
208 } else {
209 return outDirBase
210 }
211}
212
213func distDir(outDir string) string {
214 if distDir := os.Getenv("DIST_DIR"); distDir != "" {
215 return filepath.Clean(distDir)
216 } else {
217 return filepath.Join(outDir, "dist")
218 }
219}
220
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800221func forceAnsiOutput() bool {
222 value := os.Getenv("SOONG_UI_ANSI_OUTPUT")
223 return value == "1" || value == "y" || value == "yes" || value == "on" || value == "true"
224}
225
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800226func main() {
Colin Cross097ed2a2019-06-08 21:48:58 -0700227 stdio := terminal.StdioImpl{}
Dan Willemsenb82471a2018-05-17 16:37:09 -0700228
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800229 output := terminal.NewStatusOutput(stdio.Stdout(), "", false, false,
230 forceAnsiOutput())
Colin Crosse0df1a32019-06-09 19:40:08 -0700231 log := logger.New(output)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800232 defer log.Cleanup()
233
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200234 for _, v := range os.Environ() {
235 log.Println("Environment: " + v)
236 }
237
238 log.Printf("Argv: %v\n", os.Args)
239
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800240 flag.Parse()
241
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200242 _, cancel := context.WithCancel(context.Background())
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800243 defer cancel()
244
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700245 trace := tracer.New(log)
246 defer trace.Close()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800247
Dan Willemsenb82471a2018-05-17 16:37:09 -0700248 stat := &status.Status{}
249 defer stat.Finish()
Colin Crosse0df1a32019-06-09 19:40:08 -0700250 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700251
Dan Willemsen34218ec2018-07-19 22:57:18 -0700252 var failures failureCount
253 stat.AddOutput(&failures)
254
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200255 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700256 trace.Close()
257 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700258 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700259 })
260
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200261 soongUi := "build/soong/soong_ui.bash"
262
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200263 var outputDir string
264 if *outDir != "" {
265 outputDir = *outDir
266 } else {
Dan Willemsen41538382018-08-31 19:51:35 -0700267 name := "multiproduct"
268 if !*incremental {
269 name += "-" + time.Now().Format("20060102150405")
270 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200271 outputDir = filepath.Join(outDirBase(), name)
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200272 }
273
274 log.Println("Output directory:", outputDir)
275
276 // The ninja_build file is used by our buildbots to understand that the output
277 // can be parsed as ninja output.
278 if err := os.MkdirAll(outputDir, 0777); err != nil {
279 log.Fatalf("Failed to create output directory: %v", err)
280 }
281 ensureEmptyFileExists(filepath.Join(outputDir, "ninja_build"), log)
282
283 logsDir := filepath.Join(outputDir, "logs")
Dan Willemsene3480762017-11-07 11:23:27 -0800284 os.MkdirAll(logsDir, 0777)
285
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200286 var configLogsDir string
287 if *alternateResultDir {
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200288 configLogsDir = filepath.Join(distDir(outDirBase()), "logs")
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200289 } else {
290 configLogsDir = outputDir
291 }
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000292
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200293 log.Println("Logs dir: " + configLogsDir)
294
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200295 os.MkdirAll(configLogsDir, 0777)
296 log.SetOutput(filepath.Join(configLogsDir, "soong.log"))
297 trace.SetOutput(filepath.Join(configLogsDir, "build.trace"))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800298
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800299 var jobs = *numJobs
300 if jobs < 1 {
301 jobs = runtime.NumCPU() / 4
302
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200303 ramGb := int(detectTotalRAM() / (1024 * 1024 * 1024))
Colin Cross5cb73662021-10-18 09:54:11 -0700304 if ramJobs := ramGb / 30; ramGb > 0 && jobs > ramJobs {
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800305 jobs = ramJobs
306 }
307
308 if jobs < 1 {
309 jobs = 1
310 }
311 }
312 log.Verbosef("Using %d parallel jobs", jobs)
313
Dan Willemsen22de2162017-12-11 14:35:23 -0800314 setMaxFiles(log)
315
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200316 allProducts := findNamedProducts(soongUi, log)
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700317 var productsList []string
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700318
Colin Crossf2f3d312020-12-17 11:29:31 -0800319 if len(includeProducts) > 0 {
320 var missingProducts []string
321 for _, product := range includeProducts {
Jeff Gastonb61e3f72017-10-25 15:02:45 -0700322 if inList(product, allProducts) {
323 productsList = append(productsList, product)
324 } else {
325 missingProducts = append(missingProducts, product)
326 }
327 }
328 if len(missingProducts) > 0 {
329 log.Fatalf("Products don't exist: %s\n", missingProducts)
330 }
331 } else {
332 productsList = allProducts
333 }
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700334
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700335 finalProductsList := make([]string, 0, len(productsList))
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700336 skipProduct := func(p string) bool {
Colin Crossf2f3d312020-12-17 11:29:31 -0800337 for _, s := range skipProducts {
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700338 if p == s {
339 return true
340 }
341 }
342 return false
343 }
344 for _, product := range productsList {
345 if !skipProduct(product) {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700346 finalProductsList = append(finalProductsList, product)
Dan Willemsen9957b9c2017-10-06 15:05:05 -0700347 } else {
348 log.Verbose("Skipping: ", product)
349 }
350 }
351
Dan Willemsen9609ad92019-12-05 15:22:41 -0800352 if *shard < 1 {
353 log.Fatalf("--shard value must be >= 1, not %d\n", *shard)
354 } else if *shardCount < 1 {
355 log.Fatalf("--shard-count value must be >= 1, not %d\n", *shardCount)
356 } else if *shard > *shardCount {
357 log.Fatalf("--shard (%d) must not be greater than --shard-count (%d)\n", *shard,
358 *shardCount)
359 } else if *shardCount > 1 {
360 finalProductsList = splitList(finalProductsList, *shardCount)[*shard-1]
361 }
362
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700363 log.Verbose("Got product list: ", finalProductsList)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800364
Lukacs T. Berki2c405692021-08-11 09:19:27 +0200365 s := stat.StartTool()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700366 s.SetTotalActions(len(finalProductsList))
Dan Willemsena4e43a72017-05-06 16:58:26 -0700367
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700368 mpCtx := &mpContext{
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200369 Logger: log,
370 Status: s,
371 SoongUi: soongUi,
372 MainOutDir: outputDir,
373 MainLogsDir: logsDir,
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800374 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700375
376 products := make(chan string, len(productsList))
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800377 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700378 defer close(products)
379 for _, product := range finalProductsList {
380 products <- product
381 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800382 }()
383
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700384 var wg sync.WaitGroup
Dan Willemsen1bdbdec2019-12-27 09:54:11 -0800385 for i := 0; i < jobs; i++ {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700386 wg.Add(1)
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800387 go func() {
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700388 defer wg.Done()
389 for {
390 select {
391 case product := <-products:
392 if product == "" {
393 return
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800394 }
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200395 runSoongUiForProduct(mpCtx, product)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700396 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800397 }
398 }()
399 }
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700400 wg.Wait()
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800401
Dan Willemsene3480762017-11-07 11:23:27 -0800402 if *alternateResultDir {
403 args := zip.ZipArgs{
404 FileArgs: []zip.FileArg{
405 {GlobDir: logsDir, SourcePrefixToStrip: logsDir},
406 },
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200407 OutputFilePath: filepath.Join(distDir(outDirBase()), "logs.zip"),
Dan Willemsene3480762017-11-07 11:23:27 -0800408 NumParallelJobs: runtime.NumCPU(),
409 CompressionLevel: 5,
410 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200411 log.Printf("Logs zip: %v\n", args.OutputFilePath)
Colin Cross05518bc2018-09-27 15:06:19 -0700412 if err := zip.Zip(args); err != nil {
Dan Willemsene3480762017-11-07 11:23:27 -0800413 log.Fatalf("Error zipping logs: %v", err)
414 }
415 }
416
Dan Willemsenb82471a2018-05-17 16:37:09 -0700417 s.Finish()
Dan Willemsen34218ec2018-07-19 22:57:18 -0700418
419 if failures == 1 {
420 log.Fatal("1 failure")
421 } else if failures > 1 {
422 log.Fatalf("%d failures", failures)
423 } else {
Colin Crosse0df1a32019-06-09 19:40:08 -0700424 fmt.Fprintln(output, "Success")
Dan Willemsen34218ec2018-07-19 22:57:18 -0700425 }
Dan Willemsenc2af0be2017-01-20 14:10:01 -0800426}
Dan Willemsen34218ec2018-07-19 22:57:18 -0700427
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200428func cleanupAfterProduct(outDir, productZip string) {
429 if *keepArtifacts {
430 args := zip.ZipArgs{
431 FileArgs: []zip.FileArg{
432 {
433 GlobDir: outDir,
434 SourcePrefixToStrip: outDir,
435 },
436 },
437 OutputFilePath: productZip,
438 NumParallelJobs: runtime.NumCPU(),
439 CompressionLevel: 5,
440 }
441 if err := zip.Zip(args); err != nil {
442 log.Fatalf("Error zipping artifacts: %v", err)
443 }
444 }
445 if !*incremental {
446 os.RemoveAll(outDir)
447 }
448}
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700449
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200450func runSoongUiForProduct(mpctx *mpContext, product string) {
451 outDir := filepath.Join(mpctx.MainOutDir, product)
452 logsDir := filepath.Join(mpctx.MainLogsDir, product)
453 productZip := filepath.Join(mpctx.MainOutDir, product+".zip")
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200454 consoleLogPath := filepath.Join(logsDir, "std.log")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700455
456 if err := os.MkdirAll(outDir, 0777); err != nil {
457 mpctx.Logger.Fatalf("Error creating out directory: %v", err)
458 }
459 if err := os.MkdirAll(logsDir, 0777); err != nil {
460 mpctx.Logger.Fatalf("Error creating log directory: %v", err)
461 }
462
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200463 consoleLogFile, err := os.Create(consoleLogPath)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700464 if err != nil {
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200465 mpctx.Logger.Fatalf("Error creating console log file: %v", err)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700466 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200467 defer consoleLogFile.Close()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700468
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200469 consoleLogWriter := bufio.NewWriter(consoleLogFile)
470 defer consoleLogWriter.Flush()
471
472 args := []string{"--make-mode", "--skip-soong-tests", "--skip-ninja"}
473
474 if !*keepArtifacts {
475 args = append(args, "--empty-ninja-file")
476 }
477
478 if *onlyConfig {
479 args = append(args, "--config-only")
480 } else if *onlySoong {
481 args = append(args, "--soong-only")
482 }
483
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200484 cmd := exec.Command(mpctx.SoongUi, args...)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200485 cmd.Stdout = consoleLogWriter
486 cmd.Stderr = consoleLogWriter
487 cmd.Env = append(os.Environ(),
488 "OUT_DIR="+outDir,
489 "TARGET_PRODUCT="+product,
490 "TARGET_BUILD_VARIANT="+*buildVariant,
491 "TARGET_BUILD_TYPE=release",
492 "TARGET_BUILD_APPS=",
493 "TARGET_BUILD_UNBUNDLED=")
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700494
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200495 if *alternateResultDir {
496 cmd.Env = append(cmd.Env,
497 "DIST_DIR="+filepath.Join(distDir(outDirBase()), "products/"+product))
498 }
499
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700500 action := &status.Action{
501 Description: product,
502 Outputs: []string{product},
503 }
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200504
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700505 mpctx.Status.StartAction(action)
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200506 defer cleanupAfterProduct(outDir, productZip)
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700507
508 before := time.Now()
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200509 err = cmd.Run()
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700510
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200511 if !*onlyConfig && !*onlySoong {
512 katiBuildNinjaFile := filepath.Join(outDir, "build-"+product+".ninja")
513 if after, err := os.Stat(katiBuildNinjaFile); err == nil && after.ModTime().After(before) {
514 err := copyFile(consoleLogPath, filepath.Join(filepath.Dir(consoleLogPath), "std_full.log"))
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700515 if err != nil {
516 log.Fatalf("Error copying log file: %s", err)
517 }
518 }
519 }
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200520 var errOutput string
521 if err == nil {
522 errOutput = ""
523 } else {
524 errOutput = errMsgFromLog(consoleLogPath)
525 }
526
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700527 mpctx.Status.FinishAction(status.ActionResult{
528 Action: action,
Lukacs T. Berkicef87b62021-08-10 15:01:13 +0200529 Error: err,
Lukacs T. Berkibf5bdb22021-08-24 10:47:13 +0200530 Output: errOutput,
Dan Willemsenbcc1dbf2018-09-04 18:09:47 -0700531 })
532}
533
Dan Willemsen34218ec2018-07-19 22:57:18 -0700534type failureCount int
535
536func (f *failureCount) StartAction(action *status.Action, counts status.Counts) {}
537
538func (f *failureCount) FinishAction(result status.ActionResult, counts status.Counts) {
539 if result.Error != nil {
540 *f += 1
541 }
542}
543
544func (f *failureCount) Message(level status.MsgLevel, message string) {
545 if level >= status.ErrorLvl {
546 *f += 1
547 }
548}
549
550func (f *failureCount) Flush() {}
Colin Crosse0df1a32019-06-09 19:40:08 -0700551
552func (f *failureCount) Write(p []byte) (int, error) {
553 // discard writes
554 return len(p), nil
555}
Dan Willemsen9609ad92019-12-05 15:22:41 -0800556
557func splitList(list []string, shardCount int) (ret [][]string) {
558 each := len(list) / shardCount
559 extra := len(list) % shardCount
560 for i := 0; i < shardCount; i++ {
561 count := each
562 if extra > 0 {
563 count += 1
564 extra -= 1
565 }
566 ret = append(ret, list[:count])
567 list = list[count:]
568 }
569 return
570}