blob: 3d8458cf3f046e4faf50902e04ad2b2cc5093ab0 [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,
MarkDacek6614d9c2022-12-07 21:57:38 +000094 }, {
95 flag: "--upload-metrics-only",
96 description: "upload metrics without building anything",
97 config: uploadOnlyConfig,
98 stdio: stdio,
99 // Upload-only mode mostly skips to the metrics-uploading phase of soong_ui.
100 // However, this invocation marks the true "end of the build", and thus we
101 // need to update the total runtime of the build to include this upload step.
102 run: updateTotalRealTime,
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700103 },
104}
105
106// indexList returns the index of first found s. -1 is return if s is not
107// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700108func indexList(s string, list []string) int {
109 for i, l := range list {
110 if l == s {
111 return i
112 }
113 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700114 return -1
115}
116
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700117// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700118func inList(s string, list []string) bool {
119 return indexList(s, list) != -1
120}
121
Jason Wucc166a72022-12-19 11:53:12 -0500122func deleteStaleMetrics(metricsFilePathSlice []string) error {
123 for _, metricsFilePath := range metricsFilePathSlice {
124 if err := os.Remove(metricsFilePath); err != nil && !os.IsNotExist(err) {
125 return fmt.Errorf("Failed to remove %s\nError message: %w", metricsFilePath, err)
126 }
127 }
128 return nil
129}
130
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700131// Main execution of soong_ui. The command format is as follows:
132//
Usta Shrestha59417a12022-08-05 17:14:49 -0400133// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700134//
135// Command is the type of soong_ui execution. Only one type of
136// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700137func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100138 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
139
Patrice Arruda73c790f2020-07-13 23:01:18 +0000140 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000141
Liz Kammer0e7993e2020-10-15 11:07:13 -0700142 c, args, err := getCommand(os.Args)
143 if err != nil {
144 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700145 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700146 }
147
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800148 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000149 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800150 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
151 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700152
Liz Kammerf2a80c62022-10-21 10:42:35 -0400153 // Create and start a new metric record.
154 met := metrics.New()
155 met.SetBuildDateTime(buildStarted)
156 met.SetBuildCommand(os.Args)
157
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800158 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400159 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700160 defer log.Cleanup()
161
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800162 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700163 ctx, cancel := context.WithCancel(context.Background())
164 defer cancel()
165
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800166 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700167 trace := tracer.New(log)
168 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700169
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800170 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700171 stat := &status.Status{}
172 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800173 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700174 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700175 stat.AddOutput(trace.StatusTracer())
176
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800177 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200178 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700179 trace.Close()
180 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700181 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700182 })
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900183 criticalPath := status.NewCriticalPath()
Dan Willemsen59339a22018-07-22 21:18:45 -0700184 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900185 Context: ctx,
186 Logger: log,
187 Metrics: met,
188 Tracer: trace,
189 Writer: output,
190 Status: stat,
191 CriticalPath: criticalPath,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700192 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700193
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500194 config := c.config(buildCtx, args...)
MarkDacek6614d9c2022-12-07 21:57:38 +0000195 config.SetLogsPrefix(c.logsPrefix)
196 logsDir := config.LogsDir()
197 buildStarted = config.BuildStartedTimeOrDefault(buildStarted)
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500198
MarkDacek6614d9c2022-12-07 21:57:38 +0000199 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
200 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
201 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
202 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
MarkDacek00e31522023-01-06 22:15:24 +0000203 bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
Jason Wud1254402023-03-01 20:26:30 -0500204 soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
MarkDacek00e31522023-01-06 22:15:24 +0000205
206 //the profile file generated by Bazel"
207 bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
MarkDacek6614d9c2022-12-07 21:57:38 +0000208 metricsFiles := []string{
209 buildErrorFile, // build error strings
210 rbeMetricsFile, // high level metrics related to remote build execution.
211 bp2buildMetricsFile, // high level metrics related to bp2build.
212 soongMetricsFile, // high level metrics related to this build system.
MarkDacek00e31522023-01-06 22:15:24 +0000213 bazelMetricsFile, // high level metrics related to bazel execution
Jason Wud1254402023-03-01 20:26:30 -0500214 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000215 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
216 }
217
218 os.MkdirAll(logsDir, 0777)
219
220 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
221
222 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
223
MarkDacek6614d9c2022-12-07 21:57:38 +0000224 if !config.SkipMetricsUpload() {
MarkDacek00e31522023-01-06 22:15:24 +0000225 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
MarkDacek6614d9c2022-12-07 21:57:38 +0000226 }
Jason Wu7796b112023-02-07 19:05:55 -0500227 defer met.Dump(soongMetricsFile)
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900228 // Should run before Metric.Dump
229 defer criticalPath.WriteToMetrics(met)
MarkDacek6614d9c2022-12-07 21:57:38 +0000230
Jason Wu51d0ad72023-02-08 18:00:33 -0500231 c.run(buildCtx, config, args)
232
MarkDacek6614d9c2022-12-07 21:57:38 +0000233}
234
235func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
236 log := buildCtx.ContextImpl.Logger
237 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700238 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000239 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700240
Patrice Arruda40564022020-12-10 00:42:58 +0000241 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000242 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
243 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
244 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
245 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
246 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000247
Jason Wucc166a72022-12-19 11:53:12 -0500248 //Delete the stale metrics files
249 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
250 if err := deleteStaleMetrics(staleFileSlice); err != nil {
251 log.Fatalln(err)
252 }
253
Kousik Kumara0a44a82020-10-08 02:33:29 -0400254 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000255
MarkDacek6614d9c2022-12-07 21:57:38 +0000256 stat := buildCtx.Status
257 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
258 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000259 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900260 stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
MarkDacek6614d9c2022-12-07 21:57:38 +0000261 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700262
Colin Cross8b8bec32019-11-15 13:18:43 -0800263 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
264 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
265 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
266
Liz Kammer4ae119c2022-02-09 10:54:05 -0500267 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500268
MarkDacek6614d9c2022-12-07 21:57:38 +0000269 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800270
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800271 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700272 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800273 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
274 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700275 if !strings.HasSuffix(start, "N") {
276 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
277 log.Verbosef("Took %dms to start up.",
278 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800279 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700280 }
281 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700282
283 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000284 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700285 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700286 }
287
Dan Willemsen6b783c82019-03-08 11:42:28 -0800288 // Fix up the source tree due to a repo bug where it doesn't remove
289 // linkfiles that have been removed
290 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
291 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
292
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800293 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700294 f := build.NewSourceFinder(buildCtx, config)
295 defer f.Shutdown()
296 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700297}
298
Dan Willemsen6b783c82019-03-08 11:42:28 -0800299func fixBadDanglingLink(ctx build.Context, name string) {
300 _, err := os.Lstat(name)
301 if err != nil {
302 return
303 }
304 _, err = os.Stat(name)
305 if os.IsNotExist(err) {
306 err = os.Remove(name)
307 if err != nil {
308 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
309 }
310 }
311}
312
MarkDacek6614d9c2022-12-07 21:57:38 +0000313func dumpVar(ctx build.Context, config build.Config, args []string) {
314 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) {
367 logAndSymlinkSetup(ctx, config)
368
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
MarkDacek6614d9c2022-12-07 21:57:38 +0000453// uploadOnlyConfig explicitly requires no arguments.
454func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
455 if len(args) > 0 {
456 fmt.Printf("--upload-only does not require arguments.")
457 }
458 return build.UploadOnlyConfig(ctx)
459}
460
Patrice Arrudab7b22822019-05-21 17:46:23 -0700461func buildActionConfig(ctx build.Context, args ...string) build.Config {
462 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400463 flags.SetOutput(ctx.Writer)
464
Patrice Arrudab7b22822019-05-21 17:46:23 -0700465 flags.Usage = func() {
466 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
467 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
468 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
469 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
470 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
471 fmt.Fprintln(ctx.Writer, "")
472 flags.PrintDefaults()
473 }
474
475 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700476 name string
477 description string
478 action build.BuildAction
479 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700480 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700481 name: "all-modules",
482 description: "Build action: build from the top of the source tree.",
483 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700484 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700485 // This is redirecting to mma build command behaviour. Once it has soaked for a
486 // while, the build command is deleted from here once it has been removed from the
487 // envsetup.sh.
488 name: "modules-in-a-dir-no-deps",
489 description: "Build action: builds all of the modules in the current directory without their dependencies.",
490 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700491 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700492 // This is redirecting to mmma build command behaviour. Once it has soaked for a
493 // while, the build command is deleted from here once it has been removed from the
494 // envsetup.sh.
495 name: "modules-in-dirs-no-deps",
496 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
497 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700498 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700499 name: "modules-in-a-dir",
500 description: "Build action: builds all of the modules in the current directory and their dependencies.",
501 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700502 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700503 name: "modules-in-dirs",
504 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
505 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700506 }}
507 for i, flag := range buildActionFlags {
508 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
509 }
510 dir := flags.String("dir", "", "Directory of the executed build command.")
511
512 // Only interested in the first two args which defines the build action and the directory.
513 // The remaining arguments are passed down to the config.
514 const numBuildActionFlags = 2
515 if len(args) < numBuildActionFlags {
516 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400517 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700518 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400519 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700520
521 // The next block of code is to validate that exactly one build action is set and the dir flag
522 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400523 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700524 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400525 for _, f := range buildActionFlags {
526 if f.set {
527 if buildActionFound {
528 if parseError == nil {
529 //otherwise Parse() already called Usage()
530 flags.Usage()
531 }
532 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
533 }
534 buildActionFound = true
535 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700536 }
537 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400538 if !buildActionFound {
539 if parseError == nil {
540 //otherwise Parse() already called Usage()
541 flags.Usage()
542 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700543 ctx.Fatalln("Build action not defined.")
544 }
545 if *dir == "" {
546 ctx.Fatalln("-dir not specified.")
547 }
548
549 // Remove the build action flags from the args as they are not recognized by the config.
550 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700551 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700552}
553
MarkDacek6614d9c2022-12-07 21:57:38 +0000554func runMake(ctx build.Context, config build.Config, _ []string) {
555 logAndSymlinkSetup(ctx, config)
556 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700557 if config.IsVerbose() {
558 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700559 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
560 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
561 fmt.Fprintln(writer, "!")
562 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
563 fmt.Fprintln(writer, "!")
564 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
565 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400566 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700567 }
568
569 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
570 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700571 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700572 fmt.Fprintln(writer, "!")
573 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
574 fmt.Fprintln(writer, "!")
575 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
576 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400577 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700578 }
579
Anton Hansson5a7861a2021-06-04 10:09:01 +0100580 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700581}
582
583// getCommand finds the appropriate command based on args[1] flag. args[0]
584// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700585func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400586 listFlags := func() []string {
587 flags := make([]string, len(commands))
588 for i, c := range commands {
589 flags[i] = c.flag
590 }
591 return flags
592 }
593
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700594 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400595 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700596 }
597
598 for _, c := range commands {
599 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700600 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700601 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700602 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400603 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 -0700604}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000605
606// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
607func populateExternalDistDir(ctx build.Context, config build.Config) {
608 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
609 var err error
610 var internalDistDirPath string
611 var externalDistDirPath string
612 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
613 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
614 }
615 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
616 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
617 }
618 if externalDistDirPath == internalDistDirPath {
619 return
620 }
621
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000622 // Make sure the internal DIST_DIR actually exists before trying to read from it
623 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
624 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
625 return
626 }
627
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000628 // Make sure the external DIST_DIR actually exists before trying to write to it
629 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
630 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
631 }
632
633 ctx.Println("Populating external DIST_DIR...")
634
635 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
636}
637
638func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
639 files, err := ioutil.ReadDir(internalDistDirPath)
640 if err != nil {
641 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
642 }
643 for _, f := range files {
644 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
645 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
646
647 if f.IsDir() {
648 // Moving a directory - check if there is an existing directory to merge with
649 externalLstat, err := os.Lstat(externalFilePath)
650 if err != nil {
651 if !os.IsNotExist(err) {
652 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
653 }
654 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
655 } else {
656 if externalLstat.IsDir() {
657 // Existing dir - try to merge the directories?
658 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
659 continue
660 } else {
661 // Existing file being replaced with a directory. Delete the existing file...
662 if err := os.RemoveAll(externalFilePath); err != nil {
663 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
664 }
665 }
666 }
667 } else {
668 // Moving a file (not a dir) - delete any existing file or directory
669 if err := os.RemoveAll(externalFilePath); err != nil {
670 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
671 }
672 }
673
674 // The actual move - do a rename instead of a copy in order to save disk space.
675 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
676 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
677 }
678 }
679}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500680
681func setMaxFiles(ctx build.Context) {
682 var limits syscall.Rlimit
683
684 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
685 if err != nil {
686 ctx.Println("Failed to get file limit:", err)
687 return
688 }
689
690 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
691 if limits.Cur == limits.Max {
692 return
693 }
694
695 limits.Cur = limits.Max
696 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
697 if err != nil {
698 ctx.Println("Failed to increase file limit:", err)
699 }
700}
MarkDacek6614d9c2022-12-07 21:57:38 +0000701
702func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
703 soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
704
705 //read file into proto
706 data, err := os.ReadFile(soongMetricsFile)
707 if err != nil {
708 ctx.Fatal(err)
709 }
710 met := ctx.ContextImpl.Metrics
711
712 err = met.UpdateTotalRealTime(data)
713 if err != nil {
714 ctx.Fatal(err)
715 }
716}