blob: 3b8f4f5a498f72305457a0889aaa02fd3f46afbb [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 }, {
MarkDacek3cd6a252023-05-10 18:39:58 +000095 flag: "--finalize-bazel-metrics",
96 description: "finalize b metrics and upload",
MarkDacekd33c2fd2023-05-04 20:40:04 +000097 config: build.UploadOnlyConfig,
MarkDacek6614d9c2022-12-07 21:57:38 +000098 stdio: stdio,
MarkDacek3cd6a252023-05-10 18:39:58 +000099 // Finalize-bazel-metrics mode updates metrics files and calls the metrics
100 // uploader. This marks the end of a b invocation.
MarkDacek733b77c2023-05-09 18:21:36 +0000101 run: finalizeBazelMetrics,
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700102 },
103}
104
105// indexList returns the index of first found s. -1 is return if s is not
106// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700107func indexList(s string, list []string) int {
108 for i, l := range list {
109 if l == s {
110 return i
111 }
112 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700113 return -1
114}
115
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700116// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700117func inList(s string, list []string) bool {
118 return indexList(s, list) != -1
119}
120
Jason Wucc166a72022-12-19 11:53:12 -0500121func deleteStaleMetrics(metricsFilePathSlice []string) error {
122 for _, metricsFilePath := range metricsFilePathSlice {
123 if err := os.Remove(metricsFilePath); err != nil && !os.IsNotExist(err) {
124 return fmt.Errorf("Failed to remove %s\nError message: %w", metricsFilePath, err)
125 }
126 }
127 return nil
128}
129
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700130// Main execution of soong_ui. The command format is as follows:
131//
Usta Shrestha59417a12022-08-05 17:14:49 -0400132// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700133//
134// Command is the type of soong_ui execution. Only one type of
135// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700136func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100137 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
138
Patrice Arruda73c790f2020-07-13 23:01:18 +0000139 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000140
Liz Kammer0e7993e2020-10-15 11:07:13 -0700141 c, args, err := getCommand(os.Args)
142 if err != nil {
143 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700144 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700145 }
146
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800147 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000148 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800149 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
150 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700151
Liz Kammerf2a80c62022-10-21 10:42:35 -0400152 // Create and start a new metric record.
153 met := metrics.New()
154 met.SetBuildDateTime(buildStarted)
155 met.SetBuildCommand(os.Args)
156
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800157 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400158 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700159 defer log.Cleanup()
160
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800161 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700162 ctx, cancel := context.WithCancel(context.Background())
163 defer cancel()
164
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800165 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700166 trace := tracer.New(log)
167 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700168
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800169 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700170 stat := &status.Status{}
Jeongik Cha036b5a32023-03-22 17:31:48 +0900171
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800172 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700173 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700174 stat.AddOutput(trace.StatusTracer())
175
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800176 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200177 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700178 trace.Close()
179 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700180 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700181 })
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900182 criticalPath := status.NewCriticalPath()
Dan Willemsen59339a22018-07-22 21:18:45 -0700183 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900184 Context: ctx,
185 Logger: log,
186 Metrics: met,
187 Tracer: trace,
188 Writer: output,
189 Status: stat,
190 CriticalPath: criticalPath,
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
MarkDacek6614d9c2022-12-07 21:57:38 +0000205 metricsFiles := []string{
206 buildErrorFile, // build error strings
207 rbeMetricsFile, // high level metrics related to remote build execution.
208 bp2buildMetricsFile, // high level metrics related to bp2build.
209 soongMetricsFile, // high level metrics related to this build system.
MarkDacek00e31522023-01-06 22:15:24 +0000210 bazelMetricsFile, // high level metrics related to bazel execution
Jason Wud1254402023-03-01 20:26:30 -0500211 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000212 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
213 }
214
215 os.MkdirAll(logsDir, 0777)
216
217 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
218
219 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
220
Joe Onorato010c6b62023-07-14 16:32:53 -0700221 log.Verbose("Command Line: ")
222 for i, arg := range os.Args {
223 log.Verbosef(" [%d] %s", i, arg)
224 }
225
Jeongik Cha036b5a32023-03-22 17:31:48 +0900226 defer func() {
227 stat.Finish()
228 criticalPath.WriteToMetrics(met)
229 met.Dump(soongMetricsFile)
230 if !config.SkipMetricsUpload() {
MarkDacek733b77c2023-05-09 18:21:36 +0000231 build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
Jeongik Cha036b5a32023-03-22 17:31:48 +0900232 }
233 }()
Jason Wu51d0ad72023-02-08 18:00:33 -0500234 c.run(buildCtx, config, args)
235
MarkDacek6614d9c2022-12-07 21:57:38 +0000236}
237
238func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
239 log := buildCtx.ContextImpl.Logger
240 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700241 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000242 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700243
Patrice Arruda40564022020-12-10 00:42:58 +0000244 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000245 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
246 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
247 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
248 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
249 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
MarkDacek1716eef2023-06-06 18:20:13 +0000250 bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000251
Jason Wucc166a72022-12-19 11:53:12 -0500252 //Delete the stale metrics files
MarkDacek1716eef2023-06-06 18:20:13 +0000253 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile, bazelMetricsFile}
Jason Wucc166a72022-12-19 11:53:12 -0500254 if err := deleteStaleMetrics(staleFileSlice); err != nil {
255 log.Fatalln(err)
256 }
257
Kousik Kumara0a44a82020-10-08 02:33:29 -0400258 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000259
MarkDacek6614d9c2022-12-07 21:57:38 +0000260 stat := buildCtx.Status
261 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
262 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000263 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900264 stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
MarkDacek6614d9c2022-12-07 21:57:38 +0000265 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700266
Colin Cross8b8bec32019-11-15 13:18:43 -0800267 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
268 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
269 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
270
Liz Kammer4ae119c2022-02-09 10:54:05 -0500271 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500272
MarkDacek6614d9c2022-12-07 21:57:38 +0000273 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800274
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800275 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700276 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800277 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
278 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700279 if !strings.HasSuffix(start, "N") {
280 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
281 log.Verbosef("Took %dms to start up.",
282 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800283 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700284 }
285 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700286
287 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000288 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700289 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700290 }
291
Liz Kammer00543dc2023-09-15 21:03:08 -0400292 removeBadTargetRename(buildCtx, config)
293
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800294 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700295 f := build.NewSourceFinder(buildCtx, config)
296 defer f.Shutdown()
297 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700298}
299
Liz Kammer00543dc2023-09-15 21:03:08 -0400300func removeBadTargetRename(ctx build.Context, config build.Config) {
301 log := ctx.ContextImpl.Logger
302 // find bad paths
303 m, err := filepath.Glob(filepath.Join(config.OutDir(), "bazel", "output", "execroot", "__main__", "bazel-out", "mixed_builds_product-*", "bin", "tools", "metalava", "metalava"))
304 if err != nil {
305 log.Fatalf("Glob for invalid file failed %s", err)
306 }
307 for _, f := range m {
308 info, err := os.Stat(f)
309 if err != nil {
310 log.Fatalf("Stat of invalid file %q failed %s", f, err)
311 }
312 // if it's a directory, leave it, but remove the files
313 if !info.IsDir() {
314 err = os.Remove(f)
315 if err != nil {
316 log.Fatalf("Remove of invalid file %q failed %s", f, err)
317 } else {
318 log.Verbosef("Removed %q", f)
319 }
320 }
321 }
322}
323
MarkDacek6614d9c2022-12-07 21:57:38 +0000324func dumpVar(ctx build.Context, config build.Config, args []string) {
325 logAndSymlinkSetup(ctx, config)
Dan Willemsen051133b2017-07-14 11:29:29 -0700326 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400327 flags.SetOutput(ctx.Writer)
328
Dan Willemsen051133b2017-07-14 11:29:29 -0700329 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700330 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
331 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
332 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700333
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700334 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
335 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
336 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700337 flags.PrintDefaults()
338 }
339 abs := flags.Bool("abs", false, "Print the absolute path of the value")
340 flags.Parse(args)
341
342 if flags.NArg() != 1 {
343 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400344 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700345 }
346
347 varName := flags.Arg(0)
348 if varName == "report_config" {
349 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
350 if err != nil {
351 ctx.Fatal(err)
352 }
353
354 fmt.Println(build.Banner(varData))
355 } else {
356 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
357 if err != nil {
358 ctx.Fatal(err)
359 }
360
361 if *abs {
362 var res []string
363 for _, path := range strings.Fields(varData[varName]) {
364 if abs, err := filepath.Abs(path); err == nil {
365 res = append(res, abs)
366 } else {
367 ctx.Fatalln("Failed to get absolute path of", path, err)
368 }
369 }
370 fmt.Println(strings.Join(res, " "))
371 } else {
372 fmt.Println(varData[varName])
373 }
374 }
375}
376
MarkDacek6614d9c2022-12-07 21:57:38 +0000377func dumpVars(ctx build.Context, config build.Config, args []string) {
378 logAndSymlinkSetup(ctx, config)
379
Dan Willemsen051133b2017-07-14 11:29:29 -0700380 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400381 flags.SetOutput(ctx.Writer)
382
Dan Willemsen051133b2017-07-14 11:29:29 -0700383 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700384 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
385 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
386 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
387 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
388 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700389
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700390 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
391 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
392 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700393 flags.PrintDefaults()
394 }
395
396 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
397 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
398
399 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
400 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
401
402 flags.Parse(args)
403
404 if flags.NArg() != 0 {
405 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400406 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700407 }
408
409 vars := strings.Fields(*varsStr)
410 absVars := strings.Fields(*absVarsStr)
411
412 allVars := append([]string{}, vars...)
413 allVars = append(allVars, absVars...)
414
415 if i := indexList("report_config", allVars); i != -1 {
416 allVars = append(allVars[:i], allVars[i+1:]...)
417 allVars = append(allVars, build.BannerVars...)
418 }
419
420 if len(allVars) == 0 {
421 return
422 }
423
424 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
425 if err != nil {
426 ctx.Fatal(err)
427 }
428
429 for _, name := range vars {
430 if name == "report_config" {
431 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
432 } else {
433 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
434 }
435 }
436 for _, name := range absVars {
437 var res []string
438 for _, path := range strings.Fields(varData[name]) {
439 abs, err := filepath.Abs(path)
440 if err != nil {
441 ctx.Fatalln("Failed to get absolute path of", path, err)
442 }
443 res = append(res, abs)
444 }
445 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
446 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700447}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700448
Patrice Arrudab7b22822019-05-21 17:46:23 -0700449func stdio() terminal.StdioInterface {
450 return terminal.StdioImpl{}
451}
452
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800453// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
454// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700455func customStdio() terminal.StdioInterface {
456 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
457}
458
459// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
460func dumpVarConfig(ctx build.Context, args ...string) build.Config {
461 return build.NewConfig(ctx)
462}
463
Patrice Arrudab7b22822019-05-21 17:46:23 -0700464func buildActionConfig(ctx build.Context, args ...string) build.Config {
465 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400466 flags.SetOutput(ctx.Writer)
467
Patrice Arrudab7b22822019-05-21 17:46:23 -0700468 flags.Usage = func() {
469 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
470 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
471 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
472 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
473 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
474 fmt.Fprintln(ctx.Writer, "")
475 flags.PrintDefaults()
476 }
477
478 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700479 name string
480 description string
481 action build.BuildAction
482 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700483 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700484 name: "all-modules",
485 description: "Build action: build from the top of the source tree.",
486 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700487 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700488 // This is redirecting to mma build command behaviour. Once it has soaked for a
489 // while, the build command is deleted from here once it has been removed from the
490 // envsetup.sh.
491 name: "modules-in-a-dir-no-deps",
492 description: "Build action: builds all of the modules in the current directory without 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 // This is redirecting to mmma build command behaviour. Once it has soaked for a
496 // while, the build command is deleted from here once it has been removed from the
497 // envsetup.sh.
498 name: "modules-in-dirs-no-deps",
499 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
500 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700501 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700502 name: "modules-in-a-dir",
503 description: "Build action: builds all of the modules in the current directory and their dependencies.",
504 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700505 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700506 name: "modules-in-dirs",
507 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
508 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700509 }}
510 for i, flag := range buildActionFlags {
511 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
512 }
513 dir := flags.String("dir", "", "Directory of the executed build command.")
514
515 // Only interested in the first two args which defines the build action and the directory.
516 // The remaining arguments are passed down to the config.
517 const numBuildActionFlags = 2
518 if len(args) < numBuildActionFlags {
519 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400520 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700521 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400522 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700523
524 // The next block of code is to validate that exactly one build action is set and the dir flag
525 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400526 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700527 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400528 for _, f := range buildActionFlags {
529 if f.set {
530 if buildActionFound {
531 if parseError == nil {
532 //otherwise Parse() already called Usage()
533 flags.Usage()
534 }
535 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
536 }
537 buildActionFound = true
538 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700539 }
540 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400541 if !buildActionFound {
542 if parseError == nil {
543 //otherwise Parse() already called Usage()
544 flags.Usage()
545 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700546 ctx.Fatalln("Build action not defined.")
547 }
548 if *dir == "" {
549 ctx.Fatalln("-dir not specified.")
550 }
551
552 // Remove the build action flags from the args as they are not recognized by the config.
553 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700554 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700555}
556
MarkDacek6614d9c2022-12-07 21:57:38 +0000557func runMake(ctx build.Context, config build.Config, _ []string) {
558 logAndSymlinkSetup(ctx, config)
559 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700560 if config.IsVerbose() {
561 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700562 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
563 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
564 fmt.Fprintln(writer, "!")
565 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
566 fmt.Fprintln(writer, "!")
567 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
568 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400569 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700570 }
571
572 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
573 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700574 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700575 fmt.Fprintln(writer, "!")
576 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
577 fmt.Fprintln(writer, "!")
578 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
579 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400580 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700581 }
582
Anton Hansson5a7861a2021-06-04 10:09:01 +0100583 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700584}
585
586// getCommand finds the appropriate command based on args[1] flag. args[0]
587// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700588func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400589 listFlags := func() []string {
590 flags := make([]string, len(commands))
591 for i, c := range commands {
592 flags[i] = c.flag
593 }
594 return flags
595 }
596
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700597 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400598 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700599 }
600
601 for _, c := range commands {
602 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700603 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700604 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700605 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400606 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 -0700607}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000608
609// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
610func populateExternalDistDir(ctx build.Context, config build.Config) {
611 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
612 var err error
613 var internalDistDirPath string
614 var externalDistDirPath string
615 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
616 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
617 }
618 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
619 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
620 }
621 if externalDistDirPath == internalDistDirPath {
622 return
623 }
624
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000625 // Make sure the internal DIST_DIR actually exists before trying to read from it
626 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
627 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
628 return
629 }
630
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000631 // Make sure the external DIST_DIR actually exists before trying to write to it
632 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
633 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
634 }
635
636 ctx.Println("Populating external DIST_DIR...")
637
638 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
639}
640
641func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
642 files, err := ioutil.ReadDir(internalDistDirPath)
643 if err != nil {
644 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
645 }
646 for _, f := range files {
647 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
648 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
649
650 if f.IsDir() {
651 // Moving a directory - check if there is an existing directory to merge with
652 externalLstat, err := os.Lstat(externalFilePath)
653 if err != nil {
654 if !os.IsNotExist(err) {
655 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
656 }
657 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
658 } else {
659 if externalLstat.IsDir() {
660 // Existing dir - try to merge the directories?
661 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
662 continue
663 } else {
664 // Existing file being replaced with a directory. Delete the existing file...
665 if err := os.RemoveAll(externalFilePath); err != nil {
666 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
667 }
668 }
669 }
670 } else {
671 // Moving a file (not a dir) - delete any existing file or directory
672 if err := os.RemoveAll(externalFilePath); err != nil {
673 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
674 }
675 }
676
677 // The actual move - do a rename instead of a copy in order to save disk space.
678 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
679 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
680 }
681 }
682}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500683
684func setMaxFiles(ctx build.Context) {
685 var limits syscall.Rlimit
686
687 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
688 if err != nil {
689 ctx.Println("Failed to get file limit:", err)
690 return
691 }
692
693 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
694 if limits.Cur == limits.Max {
695 return
696 }
697
698 limits.Cur = limits.Max
699 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
700 if err != nil {
701 ctx.Println("Failed to increase file limit:", err)
702 }
703}
MarkDacek6614d9c2022-12-07 21:57:38 +0000704
MarkDacek733b77c2023-05-09 18:21:36 +0000705func finalizeBazelMetrics(ctx build.Context, config build.Config, args []string) {
706 updateTotalRealTime(ctx, config, args)
707
708 logsDir := config.LogsDir()
709 logsPrefix := config.GetLogsPrefix()
710 bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
711 bazelProfileFile := filepath.Join(logsDir, logsPrefix+"analyzed_bazel_profile.txt")
712 build.ProcessBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx, config)
713}
MarkDacek6614d9c2022-12-07 21:57:38 +0000714func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
715 soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
716
717 //read file into proto
718 data, err := os.ReadFile(soongMetricsFile)
719 if err != nil {
720 ctx.Fatal(err)
721 }
722 met := ctx.ContextImpl.Metrics
723
MarkDacekd33c2fd2023-05-04 20:40:04 +0000724 err = met.UpdateTotalRealTimeAndNonZeroExit(data, config.BazelExitCode())
MarkDacek6614d9c2022-12-07 21:57:38 +0000725 if err != nil {
726 ctx.Fatal(err)
727 }
728}