blob: ae026ba26813dfe14c690ab0bf754caf7afcbfc1 [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 })
183
Dan Willemsen59339a22018-07-22 21:18:45 -0700184 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700185 Context: ctx,
186 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800187 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700188 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700189 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700190 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700191 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700192
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500193 config := c.config(buildCtx, args...)
MarkDacek6614d9c2022-12-07 21:57:38 +0000194 config.SetLogsPrefix(c.logsPrefix)
195 logsDir := config.LogsDir()
196 buildStarted = config.BuildStartedTimeOrDefault(buildStarted)
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500197
MarkDacek6614d9c2022-12-07 21:57:38 +0000198 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
199 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
200 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
201 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
MarkDacek00e31522023-01-06 22:15:24 +0000202 bazelMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bazel_metrics.pb")
Jason Wud1254402023-03-01 20:26:30 -0500203 soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
MarkDacek00e31522023-01-06 22:15:24 +0000204
205 //the profile file generated by Bazel"
206 bazelProfileFile := filepath.Join(logsDir, c.logsPrefix+"analyzed_bazel_profile.txt")
MarkDacek6614d9c2022-12-07 21:57:38 +0000207 metricsFiles := []string{
208 buildErrorFile, // build error strings
209 rbeMetricsFile, // high level metrics related to remote build execution.
210 bp2buildMetricsFile, // high level metrics related to bp2build.
211 soongMetricsFile, // high level metrics related to this build system.
MarkDacek00e31522023-01-06 22:15:24 +0000212 bazelMetricsFile, // high level metrics related to bazel execution
Jason Wud1254402023-03-01 20:26:30 -0500213 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000214 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
215 }
216
217 os.MkdirAll(logsDir, 0777)
218
219 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
220
221 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
222
MarkDacek6614d9c2022-12-07 21:57:38 +0000223 if !config.SkipMetricsUpload() {
MarkDacek00e31522023-01-06 22:15:24 +0000224 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, bazelProfileFile, bazelMetricsFile, metricsFiles...)
MarkDacek6614d9c2022-12-07 21:57:38 +0000225 }
Jason Wu7796b112023-02-07 19:05:55 -0500226 defer met.Dump(soongMetricsFile)
MarkDacek6614d9c2022-12-07 21:57:38 +0000227
Jason Wu51d0ad72023-02-08 18:00:33 -0500228 c.run(buildCtx, config, args)
229
MarkDacek6614d9c2022-12-07 21:57:38 +0000230}
231
232func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
233 log := buildCtx.ContextImpl.Logger
234 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700235 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000236 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700237
Patrice Arruda40564022020-12-10 00:42:58 +0000238 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000239 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
240 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
241 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
242 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
243 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000244
Jason Wucc166a72022-12-19 11:53:12 -0500245 //Delete the stale metrics files
246 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
247 if err := deleteStaleMetrics(staleFileSlice); err != nil {
248 log.Fatalln(err)
249 }
250
Kousik Kumara0a44a82020-10-08 02:33:29 -0400251 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000252
MarkDacek6614d9c2022-12-07 21:57:38 +0000253 stat := buildCtx.Status
254 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
255 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000256 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700257 stat.AddOutput(status.NewCriticalPath(log))
MarkDacek6614d9c2022-12-07 21:57:38 +0000258 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700259
Colin Cross8b8bec32019-11-15 13:18:43 -0800260 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
261 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
262 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
263
Liz Kammer4ae119c2022-02-09 10:54:05 -0500264 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500265
MarkDacek6614d9c2022-12-07 21:57:38 +0000266 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800267
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800268 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700269 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800270 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
271 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700272 if !strings.HasSuffix(start, "N") {
273 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
274 log.Verbosef("Took %dms to start up.",
275 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800276 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700277 }
278 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700279
280 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000281 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700282 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700283 }
284
Dan Willemsen6b783c82019-03-08 11:42:28 -0800285 // Fix up the source tree due to a repo bug where it doesn't remove
286 // linkfiles that have been removed
287 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
288 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
289
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800290 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700291 f := build.NewSourceFinder(buildCtx, config)
292 defer f.Shutdown()
293 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700294}
295
Dan Willemsen6b783c82019-03-08 11:42:28 -0800296func fixBadDanglingLink(ctx build.Context, name string) {
297 _, err := os.Lstat(name)
298 if err != nil {
299 return
300 }
301 _, err = os.Stat(name)
302 if os.IsNotExist(err) {
303 err = os.Remove(name)
304 if err != nil {
305 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
306 }
307 }
308}
309
MarkDacek6614d9c2022-12-07 21:57:38 +0000310func dumpVar(ctx build.Context, config build.Config, args []string) {
311 logAndSymlinkSetup(ctx, config)
Dan Willemsen051133b2017-07-14 11:29:29 -0700312 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400313 flags.SetOutput(ctx.Writer)
314
Dan Willemsen051133b2017-07-14 11:29:29 -0700315 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700316 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
317 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
318 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700319
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700320 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
321 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
322 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700323 flags.PrintDefaults()
324 }
325 abs := flags.Bool("abs", false, "Print the absolute path of the value")
326 flags.Parse(args)
327
328 if flags.NArg() != 1 {
329 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400330 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700331 }
332
333 varName := flags.Arg(0)
334 if varName == "report_config" {
335 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
336 if err != nil {
337 ctx.Fatal(err)
338 }
339
340 fmt.Println(build.Banner(varData))
341 } else {
342 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
343 if err != nil {
344 ctx.Fatal(err)
345 }
346
347 if *abs {
348 var res []string
349 for _, path := range strings.Fields(varData[varName]) {
350 if abs, err := filepath.Abs(path); err == nil {
351 res = append(res, abs)
352 } else {
353 ctx.Fatalln("Failed to get absolute path of", path, err)
354 }
355 }
356 fmt.Println(strings.Join(res, " "))
357 } else {
358 fmt.Println(varData[varName])
359 }
360 }
361}
362
MarkDacek6614d9c2022-12-07 21:57:38 +0000363func dumpVars(ctx build.Context, config build.Config, args []string) {
364 logAndSymlinkSetup(ctx, config)
365
Dan Willemsen051133b2017-07-14 11:29:29 -0700366 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400367 flags.SetOutput(ctx.Writer)
368
Dan Willemsen051133b2017-07-14 11:29:29 -0700369 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700370 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
371 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
372 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
373 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
374 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700375
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700376 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
377 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
378 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700379 flags.PrintDefaults()
380 }
381
382 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
383 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
384
385 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
386 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
387
388 flags.Parse(args)
389
390 if flags.NArg() != 0 {
391 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400392 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700393 }
394
395 vars := strings.Fields(*varsStr)
396 absVars := strings.Fields(*absVarsStr)
397
398 allVars := append([]string{}, vars...)
399 allVars = append(allVars, absVars...)
400
401 if i := indexList("report_config", allVars); i != -1 {
402 allVars = append(allVars[:i], allVars[i+1:]...)
403 allVars = append(allVars, build.BannerVars...)
404 }
405
406 if len(allVars) == 0 {
407 return
408 }
409
410 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
411 if err != nil {
412 ctx.Fatal(err)
413 }
414
415 for _, name := range vars {
416 if name == "report_config" {
417 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
418 } else {
419 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
420 }
421 }
422 for _, name := range absVars {
423 var res []string
424 for _, path := range strings.Fields(varData[name]) {
425 abs, err := filepath.Abs(path)
426 if err != nil {
427 ctx.Fatalln("Failed to get absolute path of", path, err)
428 }
429 res = append(res, abs)
430 }
431 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
432 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700433}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700434
Patrice Arrudab7b22822019-05-21 17:46:23 -0700435func stdio() terminal.StdioInterface {
436 return terminal.StdioImpl{}
437}
438
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800439// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
440// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700441func customStdio() terminal.StdioInterface {
442 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
443}
444
445// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
446func dumpVarConfig(ctx build.Context, args ...string) build.Config {
447 return build.NewConfig(ctx)
448}
449
MarkDacek6614d9c2022-12-07 21:57:38 +0000450// uploadOnlyConfig explicitly requires no arguments.
451func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
452 if len(args) > 0 {
453 fmt.Printf("--upload-only does not require arguments.")
454 }
455 return build.UploadOnlyConfig(ctx)
456}
457
Patrice Arrudab7b22822019-05-21 17:46:23 -0700458func buildActionConfig(ctx build.Context, args ...string) build.Config {
459 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400460 flags.SetOutput(ctx.Writer)
461
Patrice Arrudab7b22822019-05-21 17:46:23 -0700462 flags.Usage = func() {
463 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
464 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
465 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
466 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
467 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
468 fmt.Fprintln(ctx.Writer, "")
469 flags.PrintDefaults()
470 }
471
472 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700473 name string
474 description string
475 action build.BuildAction
476 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700477 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700478 name: "all-modules",
479 description: "Build action: build from the top of the source tree.",
480 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700481 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700482 // This is redirecting to mma build command behaviour. Once it has soaked for a
483 // while, the build command is deleted from here once it has been removed from the
484 // envsetup.sh.
485 name: "modules-in-a-dir-no-deps",
486 description: "Build action: builds all of the modules in the current directory without their dependencies.",
487 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700488 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700489 // This is redirecting to mmma build command behaviour. Once it has soaked for a
490 // while, the build command is deleted from here once it has been removed from the
491 // envsetup.sh.
492 name: "modules-in-dirs-no-deps",
493 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
494 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700495 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700496 name: "modules-in-a-dir",
497 description: "Build action: builds all of the modules in the current directory and their dependencies.",
498 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700499 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700500 name: "modules-in-dirs",
501 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
502 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700503 }}
504 for i, flag := range buildActionFlags {
505 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
506 }
507 dir := flags.String("dir", "", "Directory of the executed build command.")
508
509 // Only interested in the first two args which defines the build action and the directory.
510 // The remaining arguments are passed down to the config.
511 const numBuildActionFlags = 2
512 if len(args) < numBuildActionFlags {
513 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400514 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700515 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400516 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700517
518 // The next block of code is to validate that exactly one build action is set and the dir flag
519 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400520 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700521 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400522 for _, f := range buildActionFlags {
523 if f.set {
524 if buildActionFound {
525 if parseError == nil {
526 //otherwise Parse() already called Usage()
527 flags.Usage()
528 }
529 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
530 }
531 buildActionFound = true
532 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700533 }
534 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400535 if !buildActionFound {
536 if parseError == nil {
537 //otherwise Parse() already called Usage()
538 flags.Usage()
539 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700540 ctx.Fatalln("Build action not defined.")
541 }
542 if *dir == "" {
543 ctx.Fatalln("-dir not specified.")
544 }
545
546 // Remove the build action flags from the args as they are not recognized by the config.
547 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700548 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700549}
550
MarkDacek6614d9c2022-12-07 21:57:38 +0000551func runMake(ctx build.Context, config build.Config, _ []string) {
552 logAndSymlinkSetup(ctx, config)
553 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700554 if config.IsVerbose() {
555 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700556 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
557 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
558 fmt.Fprintln(writer, "!")
559 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
560 fmt.Fprintln(writer, "!")
561 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
562 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400563 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700564 }
565
566 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
567 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700568 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700569 fmt.Fprintln(writer, "!")
570 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
571 fmt.Fprintln(writer, "!")
572 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
573 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400574 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700575 }
576
Anton Hansson5a7861a2021-06-04 10:09:01 +0100577 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700578}
579
580// getCommand finds the appropriate command based on args[1] flag. args[0]
581// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700582func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400583 listFlags := func() []string {
584 flags := make([]string, len(commands))
585 for i, c := range commands {
586 flags[i] = c.flag
587 }
588 return flags
589 }
590
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700591 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400592 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700593 }
594
595 for _, c := range commands {
596 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700597 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700598 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700599 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400600 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 -0700601}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000602
603// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
604func populateExternalDistDir(ctx build.Context, config build.Config) {
605 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
606 var err error
607 var internalDistDirPath string
608 var externalDistDirPath string
609 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
610 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
611 }
612 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
613 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
614 }
615 if externalDistDirPath == internalDistDirPath {
616 return
617 }
618
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000619 // Make sure the internal DIST_DIR actually exists before trying to read from it
620 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
621 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
622 return
623 }
624
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000625 // Make sure the external DIST_DIR actually exists before trying to write to it
626 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
627 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
628 }
629
630 ctx.Println("Populating external DIST_DIR...")
631
632 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
633}
634
635func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
636 files, err := ioutil.ReadDir(internalDistDirPath)
637 if err != nil {
638 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
639 }
640 for _, f := range files {
641 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
642 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
643
644 if f.IsDir() {
645 // Moving a directory - check if there is an existing directory to merge with
646 externalLstat, err := os.Lstat(externalFilePath)
647 if err != nil {
648 if !os.IsNotExist(err) {
649 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
650 }
651 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
652 } else {
653 if externalLstat.IsDir() {
654 // Existing dir - try to merge the directories?
655 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
656 continue
657 } else {
658 // Existing file being replaced with a directory. Delete the existing file...
659 if err := os.RemoveAll(externalFilePath); err != nil {
660 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
661 }
662 }
663 }
664 } else {
665 // Moving a file (not a dir) - delete any existing file or directory
666 if err := os.RemoveAll(externalFilePath); err != nil {
667 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
668 }
669 }
670
671 // The actual move - do a rename instead of a copy in order to save disk space.
672 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
673 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
674 }
675 }
676}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500677
678func setMaxFiles(ctx build.Context) {
679 var limits syscall.Rlimit
680
681 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
682 if err != nil {
683 ctx.Println("Failed to get file limit:", err)
684 return
685 }
686
687 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
688 if limits.Cur == limits.Max {
689 return
690 }
691
692 limits.Cur = limits.Max
693 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
694 if err != nil {
695 ctx.Println("Failed to increase file limit:", err)
696 }
697}
MarkDacek6614d9c2022-12-07 21:57:38 +0000698
699func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
700 soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
701
702 //read file into proto
703 data, err := os.ReadFile(soongMetricsFile)
704 if err != nil {
705 ctx.Fatal(err)
706 }
707 met := ctx.ContextImpl.Metrics
708
709 err = met.UpdateTotalRealTime(data)
710 if err != nil {
711 ctx.Fatal(err)
712 }
713}