blob: 4484a483ccb262bed249467abec0acb8c8fb72a4 [file] [log] [blame]
Dan Willemsen1e704462016-08-21 15:17:17 -07001// 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 (
18 "context"
Dan Willemsen051133b2017-07-14 11:29:29 -070019 "flag"
20 "fmt"
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +000021 "io/ioutil"
Dan Willemsen1e704462016-08-21 15:17:17 -070022 "os"
23 "path/filepath"
24 "strconv"
25 "strings"
Liz Kammera7541782022-02-07 13:38:52 -050026 "syscall"
Dan Willemsen1e704462016-08-21 15:17:17 -070027 "time"
28
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +010029 "android/soong/shared"
Dan Willemsen1e704462016-08-21 15:17:17 -070030 "android/soong/ui/build"
31 "android/soong/ui/logger"
Nan Zhang17f27672018-12-12 16:01:49 -080032 "android/soong/ui/metrics"
Lukacs T. Berkif656b842021-08-11 11:10:28 +020033 "android/soong/ui/signal"
Dan Willemsenb82471a2018-05-17 16:37:09 -070034 "android/soong/ui/status"
35 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070036 "android/soong/ui/tracer"
Dan Willemsen1e704462016-08-21 15:17:17 -070037)
38
Patrice Arrudaa5c25422019-04-09 18:49:49 -070039// A command represents an operation to be executed in the soong build
40// system.
41type command struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000042 // The flag name (must have double dashes).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070043 flag string
44
Patrice Arrudaf445ba12020-07-28 17:49:01 +000045 // Description for the flag (to display when running help).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070046 description string
47
Patrice Arrudaf445ba12020-07-28 17:49:01 +000048 // Stream the build status output into the simple terminal mode.
49 simpleOutput bool
Colin Crossc0b9f6b2019-09-23 12:44:54 -070050
51 // Sets a prefix string to use for filenames of log files.
52 logsPrefix string
53
Patrice Arrudaa5c25422019-04-09 18:49:49 -070054 // Creates the build configuration based on the args and build context.
55 config func(ctx build.Context, args ...string) build.Config
56
57 // Returns what type of IO redirection this Command requires.
58 stdio func() terminal.StdioInterface
59
60 // run the command
MarkDacek6614d9c2022-12-07 21:57:38 +000061 run func(ctx build.Context, config build.Config, args []string)
Patrice Arrudaa5c25422019-04-09 18:49:49 -070062}
63
Patrice Arrudaa5c25422019-04-09 18:49:49 -070064// list of supported commands (flags) supported by soong ui
Usta6feae382021-12-13 12:31:50 -050065var commands = []command{
Patrice Arrudaa5c25422019-04-09 18:49:49 -070066 {
Anton Hansson5a7861a2021-06-04 10:09:01 +010067 flag: "--make-mode",
Patrice Arrudaa5c25422019-04-09 18:49:49 -070068 description: "build the modules by the target name (i.e. soong_docs)",
Usta Shrestha59417a12022-08-05 17:14:49 -040069 config: build.NewConfig,
70 stdio: stdio,
71 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070072 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000073 flag: "--dumpvar-mode",
74 description: "print the value of the legacy make variable VAR to stdout",
75 simpleOutput: true,
76 logsPrefix: "dumpvars-",
77 config: dumpVarConfig,
78 stdio: customStdio,
79 run: dumpVar,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070080 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000081 flag: "--dumpvars-mode",
82 description: "dump the values of one or more legacy make variables, in shell syntax",
83 simpleOutput: true,
84 logsPrefix: "dumpvars-",
85 config: dumpVarConfig,
86 stdio: customStdio,
87 run: dumpVars,
Patrice Arrudab7b22822019-05-21 17:46:23 -070088 }, {
89 flag: "--build-mode",
90 description: "build modules based on the specified build action",
91 config: buildActionConfig,
92 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010093 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070094 },
95}
96
97// indexList returns the index of first found s. -1 is return if s is not
98// found.
Dan Willemsen1e704462016-08-21 15:17:17 -070099func indexList(s string, list []string) int {
100 for i, l := range list {
101 if l == s {
102 return i
103 }
104 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700105 return -1
106}
107
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700108// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700109func inList(s string, list []string) bool {
110 return indexList(s, list) != -1
111}
112
Jason Wucc166a72022-12-19 11:53:12 -0500113func deleteStaleMetrics(metricsFilePathSlice []string) error {
114 for _, metricsFilePath := range metricsFilePathSlice {
115 if err := os.Remove(metricsFilePath); err != nil && !os.IsNotExist(err) {
116 return fmt.Errorf("Failed to remove %s\nError message: %w", metricsFilePath, err)
117 }
118 }
119 return nil
120}
121
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700122// Main execution of soong_ui. The command format is as follows:
123//
Usta Shrestha59417a12022-08-05 17:14:49 -0400124// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700125//
126// Command is the type of soong_ui execution. Only one type of
127// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700128func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100129 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
130
Patrice Arruda73c790f2020-07-13 23:01:18 +0000131 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000132
Liz Kammer0e7993e2020-10-15 11:07:13 -0700133 c, args, err := getCommand(os.Args)
134 if err != nil {
135 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700136 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700137 }
138
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800139 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000140 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800141 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
142 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700143
Liz Kammerf2a80c62022-10-21 10:42:35 -0400144 // Create and start a new metric record.
145 met := metrics.New()
146 met.SetBuildDateTime(buildStarted)
147 met.SetBuildCommand(os.Args)
148
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800149 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400150 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700151 defer log.Cleanup()
152
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800153 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700154 ctx, cancel := context.WithCancel(context.Background())
155 defer cancel()
156
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800157 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700158 trace := tracer.New(log)
159 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700160
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800161 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700162 stat := &status.Status{}
Jeongik Cha036b5a32023-03-22 17:31:48 +0900163
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800164 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700165 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700166 stat.AddOutput(trace.StatusTracer())
167
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800168 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200169 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700170 trace.Close()
171 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700172 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700173 })
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900174 criticalPath := status.NewCriticalPath()
Dan Willemsen59339a22018-07-22 21:18:45 -0700175 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900176 Context: ctx,
177 Logger: log,
178 Metrics: met,
179 Tracer: trace,
180 Writer: output,
181 Status: stat,
182 CriticalPath: criticalPath,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700183 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700184
Colin Cross6a4fe682023-11-06 21:32:03 +0000185 config := c.config(buildCtx, args...)
186 config.SetLogsPrefix(c.logsPrefix)
MarkDacek6614d9c2022-12-07 21:57:38 +0000187 logsDir := config.LogsDir()
188 buildStarted = config.BuildStartedTimeOrDefault(buildStarted)
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500189
MarkDacek6614d9c2022-12-07 21:57:38 +0000190 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
191 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
192 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
193 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
Jason Wud1254402023-03-01 20:26:30 -0500194 soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
MarkDacek00e31522023-01-06 22:15:24 +0000195
MarkDacek6614d9c2022-12-07 21:57:38 +0000196 metricsFiles := []string{
197 buildErrorFile, // build error strings
198 rbeMetricsFile, // high level metrics related to remote build execution.
199 bp2buildMetricsFile, // high level metrics related to bp2build.
200 soongMetricsFile, // high level metrics related to this build system.
Jason Wud1254402023-03-01 20:26:30 -0500201 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000202 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
203 }
204
205 os.MkdirAll(logsDir, 0777)
206
207 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
208
209 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
210
Joe Onorato010c6b62023-07-14 16:32:53 -0700211 log.Verbose("Command Line: ")
212 for i, arg := range os.Args {
213 log.Verbosef(" [%d] %s", i, arg)
214 }
215
Jeongik Cha036b5a32023-03-22 17:31:48 +0900216 defer func() {
217 stat.Finish()
218 criticalPath.WriteToMetrics(met)
219 met.Dump(soongMetricsFile)
220 if !config.SkipMetricsUpload() {
MarkDacek733b77c2023-05-09 18:21:36 +0000221 build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
Jeongik Cha036b5a32023-03-22 17:31:48 +0900222 }
223 }()
Jason Wu51d0ad72023-02-08 18:00:33 -0500224 c.run(buildCtx, config, args)
225
MarkDacek6614d9c2022-12-07 21:57:38 +0000226}
227
228func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
229 log := buildCtx.ContextImpl.Logger
230 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700231 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000232 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700233
Patrice Arruda40564022020-12-10 00:42:58 +0000234 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000235 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
236 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
237 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
238 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
239 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000240
Jason Wucc166a72022-12-19 11:53:12 -0500241 //Delete the stale metrics files
MarkDacek39825ea2023-10-26 19:59:27 +0000242 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
Jason Wucc166a72022-12-19 11:53:12 -0500243 if err := deleteStaleMetrics(staleFileSlice); err != nil {
244 log.Fatalln(err)
245 }
246
Kousik Kumara0a44a82020-10-08 02:33:29 -0400247 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000248
MarkDacek6614d9c2022-12-07 21:57:38 +0000249 stat := buildCtx.Status
250 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
251 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000252 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900253 stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
MarkDacek6614d9c2022-12-07 21:57:38 +0000254 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700255
Colin Cross8b8bec32019-11-15 13:18:43 -0800256 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
257 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
258 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
259
Liz Kammer4ae119c2022-02-09 10:54:05 -0500260 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500261
MarkDacek6614d9c2022-12-07 21:57:38 +0000262 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800263
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800264 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700265 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800266 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
267 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700268 if !strings.HasSuffix(start, "N") {
269 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
270 log.Verbosef("Took %dms to start up.",
271 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800272 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700273 }
274 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700275
276 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000277 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700278 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700279 }
280
Liz Kammer00543dc2023-09-15 21:03:08 -0400281 removeBadTargetRename(buildCtx, config)
282
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800283 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700284 f := build.NewSourceFinder(buildCtx, config)
285 defer f.Shutdown()
286 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700287}
288
Liz Kammer00543dc2023-09-15 21:03:08 -0400289func removeBadTargetRename(ctx build.Context, config build.Config) {
290 log := ctx.ContextImpl.Logger
291 // find bad paths
292 m, err := filepath.Glob(filepath.Join(config.OutDir(), "bazel", "output", "execroot", "__main__", "bazel-out", "mixed_builds_product-*", "bin", "tools", "metalava", "metalava"))
293 if err != nil {
294 log.Fatalf("Glob for invalid file failed %s", err)
295 }
296 for _, f := range m {
297 info, err := os.Stat(f)
298 if err != nil {
299 log.Fatalf("Stat of invalid file %q failed %s", f, err)
300 }
301 // if it's a directory, leave it, but remove the files
302 if !info.IsDir() {
303 err = os.Remove(f)
304 if err != nil {
305 log.Fatalf("Remove of invalid file %q failed %s", f, err)
306 } else {
307 log.Verbosef("Removed %q", f)
308 }
309 }
310 }
311}
312
MarkDacek6614d9c2022-12-07 21:57:38 +0000313func dumpVar(ctx build.Context, config build.Config, args []string) {
Colin Cross6a4fe682023-11-06 21:32:03 +0000314 logAndSymlinkSetup(ctx, config)
Dan Willemsen051133b2017-07-14 11:29:29 -0700315 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400316 flags.SetOutput(ctx.Writer)
317
Dan Willemsen051133b2017-07-14 11:29:29 -0700318 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700319 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
320 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
321 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700322
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700323 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
324 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
325 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700326 flags.PrintDefaults()
327 }
328 abs := flags.Bool("abs", false, "Print the absolute path of the value")
329 flags.Parse(args)
330
331 if flags.NArg() != 1 {
332 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400333 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700334 }
335
336 varName := flags.Arg(0)
337 if varName == "report_config" {
338 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
339 if err != nil {
340 ctx.Fatal(err)
341 }
342
343 fmt.Println(build.Banner(varData))
344 } else {
345 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
346 if err != nil {
347 ctx.Fatal(err)
348 }
349
350 if *abs {
351 var res []string
352 for _, path := range strings.Fields(varData[varName]) {
353 if abs, err := filepath.Abs(path); err == nil {
354 res = append(res, abs)
355 } else {
356 ctx.Fatalln("Failed to get absolute path of", path, err)
357 }
358 }
359 fmt.Println(strings.Join(res, " "))
360 } else {
361 fmt.Println(varData[varName])
362 }
363 }
364}
365
MarkDacek6614d9c2022-12-07 21:57:38 +0000366func dumpVars(ctx build.Context, config build.Config, args []string) {
Colin Cross6a4fe682023-11-06 21:32:03 +0000367 logAndSymlinkSetup(ctx, config)
MarkDacek6614d9c2022-12-07 21:57:38 +0000368
Dan Willemsen051133b2017-07-14 11:29:29 -0700369 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400370 flags.SetOutput(ctx.Writer)
371
Dan Willemsen051133b2017-07-14 11:29:29 -0700372 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700373 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
374 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
375 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
376 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
377 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700378
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700379 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
380 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
381 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700382 flags.PrintDefaults()
383 }
384
385 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
386 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
387
388 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
389 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
390
391 flags.Parse(args)
392
393 if flags.NArg() != 0 {
394 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400395 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700396 }
397
398 vars := strings.Fields(*varsStr)
399 absVars := strings.Fields(*absVarsStr)
400
401 allVars := append([]string{}, vars...)
402 allVars = append(allVars, absVars...)
403
404 if i := indexList("report_config", allVars); i != -1 {
405 allVars = append(allVars[:i], allVars[i+1:]...)
406 allVars = append(allVars, build.BannerVars...)
407 }
408
409 if len(allVars) == 0 {
410 return
411 }
412
413 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
414 if err != nil {
415 ctx.Fatal(err)
416 }
417
418 for _, name := range vars {
419 if name == "report_config" {
420 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
421 } else {
422 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
423 }
424 }
425 for _, name := range absVars {
426 var res []string
427 for _, path := range strings.Fields(varData[name]) {
428 abs, err := filepath.Abs(path)
429 if err != nil {
430 ctx.Fatalln("Failed to get absolute path of", path, err)
431 }
432 res = append(res, abs)
433 }
434 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
435 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700436}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700437
Patrice Arrudab7b22822019-05-21 17:46:23 -0700438func stdio() terminal.StdioInterface {
439 return terminal.StdioImpl{}
440}
441
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800442// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
443// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700444func customStdio() terminal.StdioInterface {
445 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
446}
447
448// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
449func dumpVarConfig(ctx build.Context, args ...string) build.Config {
450 return build.NewConfig(ctx)
451}
452
Patrice Arrudab7b22822019-05-21 17:46:23 -0700453func buildActionConfig(ctx build.Context, args ...string) build.Config {
454 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400455 flags.SetOutput(ctx.Writer)
456
Patrice Arrudab7b22822019-05-21 17:46:23 -0700457 flags.Usage = func() {
458 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
459 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
460 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
461 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
462 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
463 fmt.Fprintln(ctx.Writer, "")
464 flags.PrintDefaults()
465 }
466
467 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700468 name string
469 description string
470 action build.BuildAction
471 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700472 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700473 name: "all-modules",
474 description: "Build action: build from the top of the source tree.",
475 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700476 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700477 // This is redirecting to mma build command behaviour. Once it has soaked for a
478 // while, the build command is deleted from here once it has been removed from the
479 // envsetup.sh.
480 name: "modules-in-a-dir-no-deps",
481 description: "Build action: builds all of the modules in the current directory without their dependencies.",
482 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700483 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700484 // This is redirecting to mmma build command behaviour. Once it has soaked for a
485 // while, the build command is deleted from here once it has been removed from the
486 // envsetup.sh.
487 name: "modules-in-dirs-no-deps",
488 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
489 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700490 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700491 name: "modules-in-a-dir",
492 description: "Build action: builds all of the modules in the current directory and their dependencies.",
493 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700494 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700495 name: "modules-in-dirs",
496 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
497 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700498 }}
499 for i, flag := range buildActionFlags {
500 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
501 }
502 dir := flags.String("dir", "", "Directory of the executed build command.")
503
504 // Only interested in the first two args which defines the build action and the directory.
505 // The remaining arguments are passed down to the config.
506 const numBuildActionFlags = 2
507 if len(args) < numBuildActionFlags {
508 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400509 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700510 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400511 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700512
513 // The next block of code is to validate that exactly one build action is set and the dir flag
514 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400515 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700516 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400517 for _, f := range buildActionFlags {
518 if f.set {
519 if buildActionFound {
520 if parseError == nil {
521 //otherwise Parse() already called Usage()
522 flags.Usage()
523 }
524 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
525 }
526 buildActionFound = true
527 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700528 }
529 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400530 if !buildActionFound {
531 if parseError == nil {
532 //otherwise Parse() already called Usage()
533 flags.Usage()
534 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700535 ctx.Fatalln("Build action not defined.")
536 }
537 if *dir == "" {
538 ctx.Fatalln("-dir not specified.")
539 }
540
541 // Remove the build action flags from the args as they are not recognized by the config.
542 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700543 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700544}
545
MarkDacek6614d9c2022-12-07 21:57:38 +0000546func runMake(ctx build.Context, config build.Config, _ []string) {
Colin Cross6a4fe682023-11-06 21:32:03 +0000547 logAndSymlinkSetup(ctx, config)
MarkDacek6614d9c2022-12-07 21:57:38 +0000548 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700549 if config.IsVerbose() {
550 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700551 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
552 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
553 fmt.Fprintln(writer, "!")
554 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
555 fmt.Fprintln(writer, "!")
556 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
557 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400558 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700559 }
560
561 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
562 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700563 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700564 fmt.Fprintln(writer, "!")
565 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
566 fmt.Fprintln(writer, "!")
567 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
568 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400569 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700570 }
571
Anton Hansson5a7861a2021-06-04 10:09:01 +0100572 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700573}
574
575// getCommand finds the appropriate command based on args[1] flag. args[0]
576// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700577func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400578 listFlags := func() []string {
579 flags := make([]string, len(commands))
580 for i, c := range commands {
581 flags[i] = c.flag
582 }
583 return flags
584 }
585
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700586 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400587 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700588 }
589
590 for _, c := range commands {
591 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700592 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700593 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700594 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400595 return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args[1], listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700596}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000597
598// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
599func populateExternalDistDir(ctx build.Context, config build.Config) {
600 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
601 var err error
602 var internalDistDirPath string
603 var externalDistDirPath string
604 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
605 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
606 }
607 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
608 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
609 }
610 if externalDistDirPath == internalDistDirPath {
611 return
612 }
613
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000614 // Make sure the internal DIST_DIR actually exists before trying to read from it
615 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
616 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
617 return
618 }
619
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000620 // Make sure the external DIST_DIR actually exists before trying to write to it
621 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
622 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
623 }
624
625 ctx.Println("Populating external DIST_DIR...")
626
627 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
628}
629
630func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
631 files, err := ioutil.ReadDir(internalDistDirPath)
632 if err != nil {
633 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
634 }
635 for _, f := range files {
636 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
637 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
638
639 if f.IsDir() {
640 // Moving a directory - check if there is an existing directory to merge with
641 externalLstat, err := os.Lstat(externalFilePath)
642 if err != nil {
643 if !os.IsNotExist(err) {
644 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
645 }
646 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
647 } else {
648 if externalLstat.IsDir() {
649 // Existing dir - try to merge the directories?
650 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
651 continue
652 } else {
653 // Existing file being replaced with a directory. Delete the existing file...
654 if err := os.RemoveAll(externalFilePath); err != nil {
655 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
656 }
657 }
658 }
659 } else {
660 // Moving a file (not a dir) - delete any existing file or directory
661 if err := os.RemoveAll(externalFilePath); err != nil {
662 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
663 }
664 }
665
666 // The actual move - do a rename instead of a copy in order to save disk space.
667 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
668 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
669 }
670 }
671}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500672
673func setMaxFiles(ctx build.Context) {
674 var limits syscall.Rlimit
675
676 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
677 if err != nil {
678 ctx.Println("Failed to get file limit:", err)
679 return
680 }
681
682 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
Colin Cross611a2fb2023-11-08 22:09:57 -0800683
684 // Go 1.21 modifies the file limit but restores the original when
685 // execing subprocesses if it hasn't be overridden. Call Setrlimit
686 // here even if it doesn't appear to be necessary so that the
687 // syscall package considers it set.
Liz Kammer4ae119c2022-02-09 10:54:05 -0500688
689 limits.Cur = limits.Max
690 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
691 if err != nil {
692 ctx.Println("Failed to increase file limit:", err)
693 }
694}