blob: 1c6aaad35d677e19523f7bf8dbf897e4c542184f [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",
MarkDacekd33c2fd2023-05-04 20:40:04 +000097 config: build.UploadOnlyConfig,
MarkDacek6614d9c2022-12-07 21:57:38 +000098 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.
MarkDacek733b77c2023-05-09 18:21:36 +0000102 run: finalizeBazelMetrics,
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{}
Jeongik Cha036b5a32023-03-22 17:31:48 +0900172
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
MarkDacek6614d9c2022-12-07 21:57:38 +0000206 metricsFiles := []string{
207 buildErrorFile, // build error strings
208 rbeMetricsFile, // high level metrics related to remote build execution.
209 bp2buildMetricsFile, // high level metrics related to bp2build.
210 soongMetricsFile, // high level metrics related to this build system.
MarkDacek00e31522023-01-06 22:15:24 +0000211 bazelMetricsFile, // high level metrics related to bazel execution
Jason Wud1254402023-03-01 20:26:30 -0500212 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000213 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
214 }
215
216 os.MkdirAll(logsDir, 0777)
217
218 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
219
220 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
221
Jeongik Cha036b5a32023-03-22 17:31:48 +0900222 defer func() {
223 stat.Finish()
224 criticalPath.WriteToMetrics(met)
225 met.Dump(soongMetricsFile)
226 if !config.SkipMetricsUpload() {
MarkDacek733b77c2023-05-09 18:21:36 +0000227 build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
Jeongik Cha036b5a32023-03-22 17:31:48 +0900228 }
229 }()
Jason Wu51d0ad72023-02-08 18:00:33 -0500230 c.run(buildCtx, config, args)
231
MarkDacek6614d9c2022-12-07 21:57:38 +0000232}
233
234func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
235 log := buildCtx.ContextImpl.Logger
236 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700237 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000238 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700239
Patrice Arruda40564022020-12-10 00:42:58 +0000240 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000241 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
242 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
243 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
244 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
245 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000246
Jason Wucc166a72022-12-19 11:53:12 -0500247 //Delete the stale metrics files
248 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
249 if err := deleteStaleMetrics(staleFileSlice); err != nil {
250 log.Fatalln(err)
251 }
252
Kousik Kumara0a44a82020-10-08 02:33:29 -0400253 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000254
MarkDacek6614d9c2022-12-07 21:57:38 +0000255 stat := buildCtx.Status
256 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
257 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000258 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900259 stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
MarkDacek6614d9c2022-12-07 21:57:38 +0000260 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700261
Colin Cross8b8bec32019-11-15 13:18:43 -0800262 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
263 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
264 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
265
Liz Kammer4ae119c2022-02-09 10:54:05 -0500266 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500267
MarkDacek6614d9c2022-12-07 21:57:38 +0000268 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800269
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800270 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700271 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800272 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
273 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700274 if !strings.HasSuffix(start, "N") {
275 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
276 log.Verbosef("Took %dms to start up.",
277 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800278 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700279 }
280 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700281
282 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000283 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700284 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700285 }
286
Dan Willemsen6b783c82019-03-08 11:42:28 -0800287 // Fix up the source tree due to a repo bug where it doesn't remove
288 // linkfiles that have been removed
289 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
290 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
291
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800292 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700293 f := build.NewSourceFinder(buildCtx, config)
294 defer f.Shutdown()
295 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700296}
297
Dan Willemsen6b783c82019-03-08 11:42:28 -0800298func fixBadDanglingLink(ctx build.Context, name string) {
299 _, err := os.Lstat(name)
300 if err != nil {
301 return
302 }
303 _, err = os.Stat(name)
304 if os.IsNotExist(err) {
305 err = os.Remove(name)
306 if err != nil {
307 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
308 }
309 }
310}
311
MarkDacek6614d9c2022-12-07 21:57:38 +0000312func dumpVar(ctx build.Context, config build.Config, args []string) {
313 logAndSymlinkSetup(ctx, config)
Dan Willemsen051133b2017-07-14 11:29:29 -0700314 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400315 flags.SetOutput(ctx.Writer)
316
Dan Willemsen051133b2017-07-14 11:29:29 -0700317 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700318 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
319 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
320 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700321
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700322 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
323 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
324 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700325 flags.PrintDefaults()
326 }
327 abs := flags.Bool("abs", false, "Print the absolute path of the value")
328 flags.Parse(args)
329
330 if flags.NArg() != 1 {
331 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400332 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700333 }
334
335 varName := flags.Arg(0)
336 if varName == "report_config" {
337 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
338 if err != nil {
339 ctx.Fatal(err)
340 }
341
342 fmt.Println(build.Banner(varData))
343 } else {
344 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
345 if err != nil {
346 ctx.Fatal(err)
347 }
348
349 if *abs {
350 var res []string
351 for _, path := range strings.Fields(varData[varName]) {
352 if abs, err := filepath.Abs(path); err == nil {
353 res = append(res, abs)
354 } else {
355 ctx.Fatalln("Failed to get absolute path of", path, err)
356 }
357 }
358 fmt.Println(strings.Join(res, " "))
359 } else {
360 fmt.Println(varData[varName])
361 }
362 }
363}
364
MarkDacek6614d9c2022-12-07 21:57:38 +0000365func dumpVars(ctx build.Context, config build.Config, args []string) {
366 logAndSymlinkSetup(ctx, config)
367
Dan Willemsen051133b2017-07-14 11:29:29 -0700368 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400369 flags.SetOutput(ctx.Writer)
370
Dan Willemsen051133b2017-07-14 11:29:29 -0700371 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700372 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
373 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
374 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
375 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
376 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700377
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700378 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
379 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
380 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700381 flags.PrintDefaults()
382 }
383
384 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
385 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
386
387 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
388 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
389
390 flags.Parse(args)
391
392 if flags.NArg() != 0 {
393 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400394 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700395 }
396
397 vars := strings.Fields(*varsStr)
398 absVars := strings.Fields(*absVarsStr)
399
400 allVars := append([]string{}, vars...)
401 allVars = append(allVars, absVars...)
402
403 if i := indexList("report_config", allVars); i != -1 {
404 allVars = append(allVars[:i], allVars[i+1:]...)
405 allVars = append(allVars, build.BannerVars...)
406 }
407
408 if len(allVars) == 0 {
409 return
410 }
411
412 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
413 if err != nil {
414 ctx.Fatal(err)
415 }
416
417 for _, name := range vars {
418 if name == "report_config" {
419 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
420 } else {
421 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
422 }
423 }
424 for _, name := range absVars {
425 var res []string
426 for _, path := range strings.Fields(varData[name]) {
427 abs, err := filepath.Abs(path)
428 if err != nil {
429 ctx.Fatalln("Failed to get absolute path of", path, err)
430 }
431 res = append(res, abs)
432 }
433 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
434 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700435}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700436
Patrice Arrudab7b22822019-05-21 17:46:23 -0700437func stdio() terminal.StdioInterface {
438 return terminal.StdioImpl{}
439}
440
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800441// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
442// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700443func customStdio() terminal.StdioInterface {
444 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
445}
446
447// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
448func dumpVarConfig(ctx build.Context, args ...string) build.Config {
449 return build.NewConfig(ctx)
450}
451
Patrice Arrudab7b22822019-05-21 17:46:23 -0700452func buildActionConfig(ctx build.Context, args ...string) build.Config {
453 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400454 flags.SetOutput(ctx.Writer)
455
Patrice Arrudab7b22822019-05-21 17:46:23 -0700456 flags.Usage = func() {
457 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
458 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
459 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
460 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
461 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
462 fmt.Fprintln(ctx.Writer, "")
463 flags.PrintDefaults()
464 }
465
466 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700467 name string
468 description string
469 action build.BuildAction
470 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700471 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700472 name: "all-modules",
473 description: "Build action: build from the top of the source tree.",
474 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700475 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700476 // This is redirecting to mma build command behaviour. Once it has soaked for a
477 // while, the build command is deleted from here once it has been removed from the
478 // envsetup.sh.
479 name: "modules-in-a-dir-no-deps",
480 description: "Build action: builds all of the modules in the current directory without their dependencies.",
481 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700482 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700483 // This is redirecting to mmma build command behaviour. Once it has soaked for a
484 // while, the build command is deleted from here once it has been removed from the
485 // envsetup.sh.
486 name: "modules-in-dirs-no-deps",
487 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
488 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700489 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700490 name: "modules-in-a-dir",
491 description: "Build action: builds all of the modules in the current directory and their dependencies.",
492 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700493 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700494 name: "modules-in-dirs",
495 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
496 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700497 }}
498 for i, flag := range buildActionFlags {
499 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
500 }
501 dir := flags.String("dir", "", "Directory of the executed build command.")
502
503 // Only interested in the first two args which defines the build action and the directory.
504 // The remaining arguments are passed down to the config.
505 const numBuildActionFlags = 2
506 if len(args) < numBuildActionFlags {
507 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400508 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700509 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400510 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700511
512 // The next block of code is to validate that exactly one build action is set and the dir flag
513 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400514 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700515 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400516 for _, f := range buildActionFlags {
517 if f.set {
518 if buildActionFound {
519 if parseError == nil {
520 //otherwise Parse() already called Usage()
521 flags.Usage()
522 }
523 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
524 }
525 buildActionFound = true
526 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700527 }
528 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400529 if !buildActionFound {
530 if parseError == nil {
531 //otherwise Parse() already called Usage()
532 flags.Usage()
533 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700534 ctx.Fatalln("Build action not defined.")
535 }
536 if *dir == "" {
537 ctx.Fatalln("-dir not specified.")
538 }
539
540 // Remove the build action flags from the args as they are not recognized by the config.
541 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700542 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700543}
544
MarkDacek6614d9c2022-12-07 21:57:38 +0000545func runMake(ctx build.Context, config build.Config, _ []string) {
546 logAndSymlinkSetup(ctx, config)
547 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700548 if config.IsVerbose() {
549 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700550 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
551 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
552 fmt.Fprintln(writer, "!")
553 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
554 fmt.Fprintln(writer, "!")
555 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
556 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400557 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700558 }
559
560 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
561 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700562 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700563 fmt.Fprintln(writer, "!")
564 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
565 fmt.Fprintln(writer, "!")
566 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
567 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400568 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700569 }
570
Anton Hansson5a7861a2021-06-04 10:09:01 +0100571 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700572}
573
574// getCommand finds the appropriate command based on args[1] flag. args[0]
575// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700576func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400577 listFlags := func() []string {
578 flags := make([]string, len(commands))
579 for i, c := range commands {
580 flags[i] = c.flag
581 }
582 return flags
583 }
584
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700585 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400586 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700587 }
588
589 for _, c := range commands {
590 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700591 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700592 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700593 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400594 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 -0700595}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000596
597// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
598func populateExternalDistDir(ctx build.Context, config build.Config) {
599 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
600 var err error
601 var internalDistDirPath string
602 var externalDistDirPath string
603 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
604 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
605 }
606 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
607 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
608 }
609 if externalDistDirPath == internalDistDirPath {
610 return
611 }
612
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000613 // Make sure the internal DIST_DIR actually exists before trying to read from it
614 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
615 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
616 return
617 }
618
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000619 // Make sure the external DIST_DIR actually exists before trying to write to it
620 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
621 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
622 }
623
624 ctx.Println("Populating external DIST_DIR...")
625
626 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
627}
628
629func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
630 files, err := ioutil.ReadDir(internalDistDirPath)
631 if err != nil {
632 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
633 }
634 for _, f := range files {
635 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
636 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
637
638 if f.IsDir() {
639 // Moving a directory - check if there is an existing directory to merge with
640 externalLstat, err := os.Lstat(externalFilePath)
641 if err != nil {
642 if !os.IsNotExist(err) {
643 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
644 }
645 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
646 } else {
647 if externalLstat.IsDir() {
648 // Existing dir - try to merge the directories?
649 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
650 continue
651 } else {
652 // Existing file being replaced with a directory. Delete the existing file...
653 if err := os.RemoveAll(externalFilePath); err != nil {
654 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
655 }
656 }
657 }
658 } else {
659 // Moving a file (not a dir) - delete any existing file or directory
660 if err := os.RemoveAll(externalFilePath); err != nil {
661 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
662 }
663 }
664
665 // The actual move - do a rename instead of a copy in order to save disk space.
666 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
667 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
668 }
669 }
670}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500671
672func setMaxFiles(ctx build.Context) {
673 var limits syscall.Rlimit
674
675 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
676 if err != nil {
677 ctx.Println("Failed to get file limit:", err)
678 return
679 }
680
681 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
682 if limits.Cur == limits.Max {
683 return
684 }
685
686 limits.Cur = limits.Max
687 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
688 if err != nil {
689 ctx.Println("Failed to increase file limit:", err)
690 }
691}
MarkDacek6614d9c2022-12-07 21:57:38 +0000692
MarkDacek733b77c2023-05-09 18:21:36 +0000693func finalizeBazelMetrics(ctx build.Context, config build.Config, args []string) {
694 updateTotalRealTime(ctx, config, args)
695
696 logsDir := config.LogsDir()
697 logsPrefix := config.GetLogsPrefix()
698 bazelMetricsFile := filepath.Join(logsDir, logsPrefix+"bazel_metrics.pb")
699 bazelProfileFile := filepath.Join(logsDir, logsPrefix+"analyzed_bazel_profile.txt")
700 build.ProcessBazelMetrics(bazelProfileFile, bazelMetricsFile, ctx, config)
701}
MarkDacek6614d9c2022-12-07 21:57:38 +0000702func 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
MarkDacekd33c2fd2023-05-04 20:40:04 +0000712 err = met.UpdateTotalRealTimeAndNonZeroExit(data, config.BazelExitCode())
MarkDacek6614d9c2022-12-07 21:57:38 +0000713 if err != nil {
714 ctx.Fatal(err)
715 }
716}