blob: 8c99988f2633d79bcb8c16754fe7c6078a72a779 [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")
202 metricsFiles := []string{
203 buildErrorFile, // build error strings
204 rbeMetricsFile, // high level metrics related to remote build execution.
205 bp2buildMetricsFile, // high level metrics related to bp2build.
206 soongMetricsFile, // high level metrics related to this build system.
207 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
208 }
209
210 os.MkdirAll(logsDir, 0777)
211
212 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
213
214 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
215
216 c.run(buildCtx, config, args)
217
218 defer met.Dump(soongMetricsFile)
219 if !config.SkipMetricsUpload() {
220 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
221 }
222
223}
224
225func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
226 log := buildCtx.ContextImpl.Logger
227 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700228 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000229 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700230
Patrice Arruda40564022020-12-10 00:42:58 +0000231 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000232 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
233 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
234 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
235 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
236 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000237
Jason Wucc166a72022-12-19 11:53:12 -0500238 //Delete the stale metrics files
239 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
240 if err := deleteStaleMetrics(staleFileSlice); err != nil {
241 log.Fatalln(err)
242 }
243
Kousik Kumara0a44a82020-10-08 02:33:29 -0400244 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000245
MarkDacek6614d9c2022-12-07 21:57:38 +0000246 stat := buildCtx.Status
247 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
248 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000249 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700250 stat.AddOutput(status.NewCriticalPath(log))
MarkDacek6614d9c2022-12-07 21:57:38 +0000251 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700252
Colin Cross8b8bec32019-11-15 13:18:43 -0800253 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
254 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
255 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
256
Liz Kammer4ae119c2022-02-09 10:54:05 -0500257 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500258
MarkDacek6614d9c2022-12-07 21:57:38 +0000259 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800260
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800261 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700262 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800263 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
264 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700265 if !strings.HasSuffix(start, "N") {
266 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
267 log.Verbosef("Took %dms to start up.",
268 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800269 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700270 }
271 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700272
273 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000274 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700275 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700276 }
277
Dan Willemsen6b783c82019-03-08 11:42:28 -0800278 // Fix up the source tree due to a repo bug where it doesn't remove
279 // linkfiles that have been removed
280 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
281 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
282
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800283 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700284 f := build.NewSourceFinder(buildCtx, config)
285 defer f.Shutdown()
286 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700287}
288
Dan Willemsen6b783c82019-03-08 11:42:28 -0800289func fixBadDanglingLink(ctx build.Context, name string) {
290 _, err := os.Lstat(name)
291 if err != nil {
292 return
293 }
294 _, err = os.Stat(name)
295 if os.IsNotExist(err) {
296 err = os.Remove(name)
297 if err != nil {
298 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
299 }
300 }
301}
302
MarkDacek6614d9c2022-12-07 21:57:38 +0000303func dumpVar(ctx build.Context, config build.Config, args []string) {
304 logAndSymlinkSetup(ctx, config)
Dan Willemsen051133b2017-07-14 11:29:29 -0700305 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400306 flags.SetOutput(ctx.Writer)
307
Dan Willemsen051133b2017-07-14 11:29:29 -0700308 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700309 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
310 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
311 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700312
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700313 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
314 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
315 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700316 flags.PrintDefaults()
317 }
318 abs := flags.Bool("abs", false, "Print the absolute path of the value")
319 flags.Parse(args)
320
321 if flags.NArg() != 1 {
322 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400323 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700324 }
325
326 varName := flags.Arg(0)
327 if varName == "report_config" {
328 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
329 if err != nil {
330 ctx.Fatal(err)
331 }
332
333 fmt.Println(build.Banner(varData))
334 } else {
335 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
336 if err != nil {
337 ctx.Fatal(err)
338 }
339
340 if *abs {
341 var res []string
342 for _, path := range strings.Fields(varData[varName]) {
343 if abs, err := filepath.Abs(path); err == nil {
344 res = append(res, abs)
345 } else {
346 ctx.Fatalln("Failed to get absolute path of", path, err)
347 }
348 }
349 fmt.Println(strings.Join(res, " "))
350 } else {
351 fmt.Println(varData[varName])
352 }
353 }
354}
355
MarkDacek6614d9c2022-12-07 21:57:38 +0000356func dumpVars(ctx build.Context, config build.Config, args []string) {
357 logAndSymlinkSetup(ctx, config)
358
Dan Willemsen051133b2017-07-14 11:29:29 -0700359 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400360 flags.SetOutput(ctx.Writer)
361
Dan Willemsen051133b2017-07-14 11:29:29 -0700362 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700363 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
364 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
365 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
366 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
367 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700368
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700369 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
370 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
371 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700372 flags.PrintDefaults()
373 }
374
375 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
376 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
377
378 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
379 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
380
381 flags.Parse(args)
382
383 if flags.NArg() != 0 {
384 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400385 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700386 }
387
388 vars := strings.Fields(*varsStr)
389 absVars := strings.Fields(*absVarsStr)
390
391 allVars := append([]string{}, vars...)
392 allVars = append(allVars, absVars...)
393
394 if i := indexList("report_config", allVars); i != -1 {
395 allVars = append(allVars[:i], allVars[i+1:]...)
396 allVars = append(allVars, build.BannerVars...)
397 }
398
399 if len(allVars) == 0 {
400 return
401 }
402
403 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
404 if err != nil {
405 ctx.Fatal(err)
406 }
407
408 for _, name := range vars {
409 if name == "report_config" {
410 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
411 } else {
412 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
413 }
414 }
415 for _, name := range absVars {
416 var res []string
417 for _, path := range strings.Fields(varData[name]) {
418 abs, err := filepath.Abs(path)
419 if err != nil {
420 ctx.Fatalln("Failed to get absolute path of", path, err)
421 }
422 res = append(res, abs)
423 }
424 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
425 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700426}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700427
Patrice Arrudab7b22822019-05-21 17:46:23 -0700428func stdio() terminal.StdioInterface {
429 return terminal.StdioImpl{}
430}
431
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800432// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
433// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700434func customStdio() terminal.StdioInterface {
435 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
436}
437
438// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
439func dumpVarConfig(ctx build.Context, args ...string) build.Config {
440 return build.NewConfig(ctx)
441}
442
MarkDacek6614d9c2022-12-07 21:57:38 +0000443// uploadOnlyConfig explicitly requires no arguments.
444func uploadOnlyConfig(ctx build.Context, args ...string) build.Config {
445 if len(args) > 0 {
446 fmt.Printf("--upload-only does not require arguments.")
447 }
448 return build.UploadOnlyConfig(ctx)
449}
450
Patrice Arrudab7b22822019-05-21 17:46:23 -0700451func buildActionConfig(ctx build.Context, args ...string) build.Config {
452 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400453 flags.SetOutput(ctx.Writer)
454
Patrice Arrudab7b22822019-05-21 17:46:23 -0700455 flags.Usage = func() {
456 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
457 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
458 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
459 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
460 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
461 fmt.Fprintln(ctx.Writer, "")
462 flags.PrintDefaults()
463 }
464
465 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700466 name string
467 description string
468 action build.BuildAction
469 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700470 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700471 name: "all-modules",
472 description: "Build action: build from the top of the source tree.",
473 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700474 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700475 // This is redirecting to mma build command behaviour. Once it has soaked for a
476 // while, the build command is deleted from here once it has been removed from the
477 // envsetup.sh.
478 name: "modules-in-a-dir-no-deps",
479 description: "Build action: builds all of the modules in the current directory without their dependencies.",
480 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700481 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700482 // This is redirecting to mmma 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-dirs-no-deps",
486 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
487 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700488 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700489 name: "modules-in-a-dir",
490 description: "Build action: builds all of the modules in the current directory and their dependencies.",
491 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700492 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700493 name: "modules-in-dirs",
494 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
495 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700496 }}
497 for i, flag := range buildActionFlags {
498 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
499 }
500 dir := flags.String("dir", "", "Directory of the executed build command.")
501
502 // Only interested in the first two args which defines the build action and the directory.
503 // The remaining arguments are passed down to the config.
504 const numBuildActionFlags = 2
505 if len(args) < numBuildActionFlags {
506 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400507 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700508 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400509 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700510
511 // The next block of code is to validate that exactly one build action is set and the dir flag
512 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400513 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700514 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400515 for _, f := range buildActionFlags {
516 if f.set {
517 if buildActionFound {
518 if parseError == nil {
519 //otherwise Parse() already called Usage()
520 flags.Usage()
521 }
522 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
523 }
524 buildActionFound = true
525 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700526 }
527 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400528 if !buildActionFound {
529 if parseError == nil {
530 //otherwise Parse() already called Usage()
531 flags.Usage()
532 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700533 ctx.Fatalln("Build action not defined.")
534 }
535 if *dir == "" {
536 ctx.Fatalln("-dir not specified.")
537 }
538
539 // Remove the build action flags from the args as they are not recognized by the config.
540 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700541 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700542}
543
MarkDacek6614d9c2022-12-07 21:57:38 +0000544func runMake(ctx build.Context, config build.Config, _ []string) {
545 logAndSymlinkSetup(ctx, config)
546 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700547 if config.IsVerbose() {
548 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700549 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
550 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
551 fmt.Fprintln(writer, "!")
552 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
553 fmt.Fprintln(writer, "!")
554 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
555 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400556 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700557 }
558
559 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
560 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700561 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700562 fmt.Fprintln(writer, "!")
563 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
564 fmt.Fprintln(writer, "!")
565 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
566 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400567 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700568 }
569
Anton Hansson5a7861a2021-06-04 10:09:01 +0100570 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700571}
572
573// getCommand finds the appropriate command based on args[1] flag. args[0]
574// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700575func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400576 listFlags := func() []string {
577 flags := make([]string, len(commands))
578 for i, c := range commands {
579 flags[i] = c.flag
580 }
581 return flags
582 }
583
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700584 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400585 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700586 }
587
588 for _, c := range commands {
589 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700590 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700591 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700592 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400593 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 -0700594}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000595
596// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
597func populateExternalDistDir(ctx build.Context, config build.Config) {
598 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
599 var err error
600 var internalDistDirPath string
601 var externalDistDirPath string
602 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
603 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
604 }
605 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
606 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
607 }
608 if externalDistDirPath == internalDistDirPath {
609 return
610 }
611
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000612 // Make sure the internal DIST_DIR actually exists before trying to read from it
613 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
614 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
615 return
616 }
617
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000618 // Make sure the external DIST_DIR actually exists before trying to write to it
619 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
620 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
621 }
622
623 ctx.Println("Populating external DIST_DIR...")
624
625 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
626}
627
628func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
629 files, err := ioutil.ReadDir(internalDistDirPath)
630 if err != nil {
631 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
632 }
633 for _, f := range files {
634 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
635 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
636
637 if f.IsDir() {
638 // Moving a directory - check if there is an existing directory to merge with
639 externalLstat, err := os.Lstat(externalFilePath)
640 if err != nil {
641 if !os.IsNotExist(err) {
642 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
643 }
644 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
645 } else {
646 if externalLstat.IsDir() {
647 // Existing dir - try to merge the directories?
648 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
649 continue
650 } else {
651 // Existing file being replaced with a directory. Delete the existing file...
652 if err := os.RemoveAll(externalFilePath); err != nil {
653 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
654 }
655 }
656 }
657 } else {
658 // Moving a file (not a dir) - delete any existing file or directory
659 if err := os.RemoveAll(externalFilePath); err != nil {
660 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
661 }
662 }
663
664 // The actual move - do a rename instead of a copy in order to save disk space.
665 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
666 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
667 }
668 }
669}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500670
671func setMaxFiles(ctx build.Context) {
672 var limits syscall.Rlimit
673
674 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
675 if err != nil {
676 ctx.Println("Failed to get file limit:", err)
677 return
678 }
679
680 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
681 if limits.Cur == limits.Max {
682 return
683 }
684
685 limits.Cur = limits.Max
686 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
687 if err != nil {
688 ctx.Println("Failed to increase file limit:", err)
689 }
690}
MarkDacek6614d9c2022-12-07 21:57:38 +0000691
692func updateTotalRealTime(ctx build.Context, config build.Config, args []string) {
693 soongMetricsFile := filepath.Join(config.LogsDir(), "soong_metrics")
694
695 //read file into proto
696 data, err := os.ReadFile(soongMetricsFile)
697 if err != nil {
698 ctx.Fatal(err)
699 }
700 met := ctx.ContextImpl.Metrics
701
702 err = met.UpdateTotalRealTime(data)
703 if err != nil {
704 ctx.Fatal(err)
705 }
706}