blob: c94ff07217083dbfdb149e0da0410725f7343166 [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,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070094 },
95}
96
97// indexList returns the index of first found s. -1 is return if s is not
98// found.
Dan Willemsen1e704462016-08-21 15:17:17 -070099func indexList(s string, list []string) int {
100 for i, l := range list {
101 if l == s {
102 return i
103 }
104 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700105 return -1
106}
107
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700108// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700109func inList(s string, list []string) bool {
110 return indexList(s, list) != -1
111}
112
Jason Wucc166a72022-12-19 11:53:12 -0500113func deleteStaleMetrics(metricsFilePathSlice []string) error {
114 for _, metricsFilePath := range metricsFilePathSlice {
115 if err := os.Remove(metricsFilePath); err != nil && !os.IsNotExist(err) {
116 return fmt.Errorf("Failed to remove %s\nError message: %w", metricsFilePath, err)
117 }
118 }
119 return nil
120}
121
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700122// Main execution of soong_ui. The command format is as follows:
123//
Usta Shrestha59417a12022-08-05 17:14:49 -0400124// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700125//
126// Command is the type of soong_ui execution. Only one type of
127// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700128func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100129 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
130
Patrice Arruda73c790f2020-07-13 23:01:18 +0000131 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000132
Liz Kammer0e7993e2020-10-15 11:07:13 -0700133 c, args, err := getCommand(os.Args)
134 if err != nil {
135 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700136 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700137 }
138
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800139 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000140 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800141 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
142 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700143
Liz Kammerf2a80c62022-10-21 10:42:35 -0400144 // Create and start a new metric record.
145 met := metrics.New()
146 met.SetBuildDateTime(buildStarted)
147 met.SetBuildCommand(os.Args)
148
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800149 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400150 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700151 defer log.Cleanup()
152
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800153 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700154 ctx, cancel := context.WithCancel(context.Background())
155 defer cancel()
156
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800157 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700158 trace := tracer.New(log)
159 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700160
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800161 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700162 stat := &status.Status{}
Jeongik Cha036b5a32023-03-22 17:31:48 +0900163
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800164 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700165 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700166 stat.AddOutput(trace.StatusTracer())
167
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800168 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200169 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700170 trace.Close()
171 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700172 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700173 })
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900174 criticalPath := status.NewCriticalPath()
Dan Willemsen59339a22018-07-22 21:18:45 -0700175 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900176 Context: ctx,
177 Logger: log,
178 Metrics: met,
179 Tracer: trace,
180 Writer: output,
181 Status: stat,
182 CriticalPath: criticalPath,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700183 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700184
LaMont Jones9a912862023-11-06 22:11:08 +0000185 freshConfig := func() build.Config {
186 config := c.config(buildCtx, args...)
187 config.SetLogsPrefix(c.logsPrefix)
188 return config
189 }
190 config := freshConfig()
MarkDacek6614d9c2022-12-07 21:57:38 +0000191 logsDir := config.LogsDir()
192 buildStarted = config.BuildStartedTimeOrDefault(buildStarted)
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500193
MarkDacek6614d9c2022-12-07 21:57:38 +0000194 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
195 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
196 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
197 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
Jason Wud1254402023-03-01 20:26:30 -0500198 soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
MarkDacek00e31522023-01-06 22:15:24 +0000199
MarkDacek6614d9c2022-12-07 21:57:38 +0000200 metricsFiles := []string{
201 buildErrorFile, // build error strings
202 rbeMetricsFile, // high level metrics related to remote build execution.
203 bp2buildMetricsFile, // high level metrics related to bp2build.
204 soongMetricsFile, // high level metrics related to this build system.
Jason Wud1254402023-03-01 20:26:30 -0500205 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000206 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
207 }
208
209 os.MkdirAll(logsDir, 0777)
210
211 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
212
213 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
214
Joe Onorato010c6b62023-07-14 16:32:53 -0700215 log.Verbose("Command Line: ")
216 for i, arg := range os.Args {
217 log.Verbosef(" [%d] %s", i, arg)
218 }
219
LaMont Jones9a912862023-11-06 22:11:08 +0000220 // We need to call preProductConfigSetup before we can do product config, which is how we get
221 // PRODUCT_CONFIG_RELEASE_MAPS set for the final product config for the build.
222 // When product config uses a declarative language, we won't need to rerun product config.
223 preProductConfigSetup(buildCtx, config)
224 if build.SetProductReleaseConfigMaps(buildCtx, config) {
225 log.Verbose("Product release config maps found\n")
226 config = freshConfig()
227 }
228
Jeongik Cha036b5a32023-03-22 17:31:48 +0900229 defer func() {
230 stat.Finish()
231 criticalPath.WriteToMetrics(met)
232 met.Dump(soongMetricsFile)
233 if !config.SkipMetricsUpload() {
MarkDacek733b77c2023-05-09 18:21:36 +0000234 build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
Jeongik Cha036b5a32023-03-22 17:31:48 +0900235 }
236 }()
Jason Wu51d0ad72023-02-08 18:00:33 -0500237 c.run(buildCtx, config, args)
238
MarkDacek6614d9c2022-12-07 21:57:38 +0000239}
240
LaMont Jones9a912862023-11-06 22:11:08 +0000241// This function must not modify config, since product config may cause us to recreate the config,
242// and we won't call this function a second time.
243func preProductConfigSetup(buildCtx build.Context, config build.Config) {
MarkDacek6614d9c2022-12-07 21:57:38 +0000244 log := buildCtx.ContextImpl.Logger
245 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700246 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000247 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700248
Patrice Arruda40564022020-12-10 00:42:58 +0000249 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000250 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
251 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
252 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
253 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
254 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000255
Jason Wucc166a72022-12-19 11:53:12 -0500256 //Delete the stale metrics files
MarkDacek39825ea2023-10-26 19:59:27 +0000257 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
Jason Wucc166a72022-12-19 11:53:12 -0500258 if err := deleteStaleMetrics(staleFileSlice); err != nil {
259 log.Fatalln(err)
260 }
261
Kousik Kumara0a44a82020-10-08 02:33:29 -0400262 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000263
MarkDacek6614d9c2022-12-07 21:57:38 +0000264 stat := buildCtx.Status
265 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
266 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000267 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900268 stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
MarkDacek6614d9c2022-12-07 21:57:38 +0000269 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700270
Colin Cross8b8bec32019-11-15 13:18:43 -0800271 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
272 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
273 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
274
Liz Kammer4ae119c2022-02-09 10:54:05 -0500275 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500276
MarkDacek6614d9c2022-12-07 21:57:38 +0000277 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800278
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800279 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700280 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800281 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
282 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700283 if !strings.HasSuffix(start, "N") {
284 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
285 log.Verbosef("Took %dms to start up.",
286 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800287 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700288 }
289 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700290
291 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000292 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700293 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700294 }
295
Liz Kammer00543dc2023-09-15 21:03:08 -0400296 removeBadTargetRename(buildCtx, config)
297
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800298 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700299 f := build.NewSourceFinder(buildCtx, config)
300 defer f.Shutdown()
301 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700302}
303
Liz Kammer00543dc2023-09-15 21:03:08 -0400304func removeBadTargetRename(ctx build.Context, config build.Config) {
305 log := ctx.ContextImpl.Logger
306 // find bad paths
307 m, err := filepath.Glob(filepath.Join(config.OutDir(), "bazel", "output", "execroot", "__main__", "bazel-out", "mixed_builds_product-*", "bin", "tools", "metalava", "metalava"))
308 if err != nil {
309 log.Fatalf("Glob for invalid file failed %s", err)
310 }
311 for _, f := range m {
312 info, err := os.Stat(f)
313 if err != nil {
314 log.Fatalf("Stat of invalid file %q failed %s", f, err)
315 }
316 // if it's a directory, leave it, but remove the files
317 if !info.IsDir() {
318 err = os.Remove(f)
319 if err != nil {
320 log.Fatalf("Remove of invalid file %q failed %s", f, err)
321 } else {
322 log.Verbosef("Removed %q", f)
323 }
324 }
325 }
326}
327
MarkDacek6614d9c2022-12-07 21:57:38 +0000328func dumpVar(ctx build.Context, config build.Config, args []string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700329 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400330 flags.SetOutput(ctx.Writer)
331
Dan Willemsen051133b2017-07-14 11:29:29 -0700332 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700333 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
334 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
335 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700336
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700337 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
338 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
339 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700340 flags.PrintDefaults()
341 }
342 abs := flags.Bool("abs", false, "Print the absolute path of the value")
343 flags.Parse(args)
344
345 if flags.NArg() != 1 {
346 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400347 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700348 }
349
350 varName := flags.Arg(0)
351 if varName == "report_config" {
352 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
353 if err != nil {
354 ctx.Fatal(err)
355 }
356
357 fmt.Println(build.Banner(varData))
358 } else {
359 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
360 if err != nil {
361 ctx.Fatal(err)
362 }
363
364 if *abs {
365 var res []string
366 for _, path := range strings.Fields(varData[varName]) {
367 if abs, err := filepath.Abs(path); err == nil {
368 res = append(res, abs)
369 } else {
370 ctx.Fatalln("Failed to get absolute path of", path, err)
371 }
372 }
373 fmt.Println(strings.Join(res, " "))
374 } else {
375 fmt.Println(varData[varName])
376 }
377 }
378}
379
MarkDacek6614d9c2022-12-07 21:57:38 +0000380func dumpVars(ctx build.Context, config build.Config, args []string) {
MarkDacek6614d9c2022-12-07 21:57:38 +0000381
Dan Willemsen051133b2017-07-14 11:29:29 -0700382 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400383 flags.SetOutput(ctx.Writer)
384
Dan Willemsen051133b2017-07-14 11:29:29 -0700385 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700386 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
387 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
388 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
389 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
390 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700391
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700392 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
393 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
394 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700395 flags.PrintDefaults()
396 }
397
398 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
399 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
400
401 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
402 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
403
404 flags.Parse(args)
405
406 if flags.NArg() != 0 {
407 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400408 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700409 }
410
411 vars := strings.Fields(*varsStr)
412 absVars := strings.Fields(*absVarsStr)
413
414 allVars := append([]string{}, vars...)
415 allVars = append(allVars, absVars...)
416
417 if i := indexList("report_config", allVars); i != -1 {
418 allVars = append(allVars[:i], allVars[i+1:]...)
419 allVars = append(allVars, build.BannerVars...)
420 }
421
422 if len(allVars) == 0 {
423 return
424 }
425
426 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
427 if err != nil {
428 ctx.Fatal(err)
429 }
430
431 for _, name := range vars {
432 if name == "report_config" {
433 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
434 } else {
435 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
436 }
437 }
438 for _, name := range absVars {
439 var res []string
440 for _, path := range strings.Fields(varData[name]) {
441 abs, err := filepath.Abs(path)
442 if err != nil {
443 ctx.Fatalln("Failed to get absolute path of", path, err)
444 }
445 res = append(res, abs)
446 }
447 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
448 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700449}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700450
Patrice Arrudab7b22822019-05-21 17:46:23 -0700451func stdio() terminal.StdioInterface {
452 return terminal.StdioImpl{}
453}
454
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800455// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
456// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700457func customStdio() terminal.StdioInterface {
458 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
459}
460
461// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
462func dumpVarConfig(ctx build.Context, args ...string) build.Config {
463 return build.NewConfig(ctx)
464}
465
Patrice Arrudab7b22822019-05-21 17:46:23 -0700466func buildActionConfig(ctx build.Context, args ...string) build.Config {
467 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400468 flags.SetOutput(ctx.Writer)
469
Patrice Arrudab7b22822019-05-21 17:46:23 -0700470 flags.Usage = func() {
471 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
472 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
473 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
474 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
475 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
476 fmt.Fprintln(ctx.Writer, "")
477 flags.PrintDefaults()
478 }
479
480 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700481 name string
482 description string
483 action build.BuildAction
484 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700485 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700486 name: "all-modules",
487 description: "Build action: build from the top of the source tree.",
488 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700489 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700490 // This is redirecting to mma build command behaviour. Once it has soaked for a
491 // while, the build command is deleted from here once it has been removed from the
492 // envsetup.sh.
493 name: "modules-in-a-dir-no-deps",
494 description: "Build action: builds all of the modules in the current directory without their dependencies.",
495 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700496 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700497 // This is redirecting to mmma build command behaviour. Once it has soaked for a
498 // while, the build command is deleted from here once it has been removed from the
499 // envsetup.sh.
500 name: "modules-in-dirs-no-deps",
501 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
502 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700503 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700504 name: "modules-in-a-dir",
505 description: "Build action: builds all of the modules in the current directory and their dependencies.",
506 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700507 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700508 name: "modules-in-dirs",
509 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
510 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700511 }}
512 for i, flag := range buildActionFlags {
513 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
514 }
515 dir := flags.String("dir", "", "Directory of the executed build command.")
516
517 // Only interested in the first two args which defines the build action and the directory.
518 // The remaining arguments are passed down to the config.
519 const numBuildActionFlags = 2
520 if len(args) < numBuildActionFlags {
521 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400522 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700523 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400524 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700525
526 // The next block of code is to validate that exactly one build action is set and the dir flag
527 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400528 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700529 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400530 for _, f := range buildActionFlags {
531 if f.set {
532 if buildActionFound {
533 if parseError == nil {
534 //otherwise Parse() already called Usage()
535 flags.Usage()
536 }
537 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
538 }
539 buildActionFound = true
540 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700541 }
542 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400543 if !buildActionFound {
544 if parseError == nil {
545 //otherwise Parse() already called Usage()
546 flags.Usage()
547 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700548 ctx.Fatalln("Build action not defined.")
549 }
550 if *dir == "" {
551 ctx.Fatalln("-dir not specified.")
552 }
553
554 // Remove the build action flags from the args as they are not recognized by the config.
555 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700556 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700557}
558
MarkDacek6614d9c2022-12-07 21:57:38 +0000559func runMake(ctx build.Context, config build.Config, _ []string) {
MarkDacek6614d9c2022-12-07 21:57:38 +0000560 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700561 if config.IsVerbose() {
562 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700563 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
564 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
565 fmt.Fprintln(writer, "!")
566 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
567 fmt.Fprintln(writer, "!")
568 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
569 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400570 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700571 }
572
573 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
574 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700575 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700576 fmt.Fprintln(writer, "!")
577 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
578 fmt.Fprintln(writer, "!")
579 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
580 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400581 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700582 }
583
Anton Hansson5a7861a2021-06-04 10:09:01 +0100584 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700585}
586
587// getCommand finds the appropriate command based on args[1] flag. args[0]
588// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700589func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400590 listFlags := func() []string {
591 flags := make([]string, len(commands))
592 for i, c := range commands {
593 flags[i] = c.flag
594 }
595 return flags
596 }
597
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700598 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400599 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700600 }
601
602 for _, c := range commands {
603 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700604 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700605 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700606 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400607 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 -0700608}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000609
610// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
611func populateExternalDistDir(ctx build.Context, config build.Config) {
612 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
613 var err error
614 var internalDistDirPath string
615 var externalDistDirPath string
616 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
617 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
618 }
619 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
620 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
621 }
622 if externalDistDirPath == internalDistDirPath {
623 return
624 }
625
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000626 // Make sure the internal DIST_DIR actually exists before trying to read from it
627 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
628 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
629 return
630 }
631
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000632 // Make sure the external DIST_DIR actually exists before trying to write to it
633 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
634 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
635 }
636
637 ctx.Println("Populating external DIST_DIR...")
638
639 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
640}
641
642func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
643 files, err := ioutil.ReadDir(internalDistDirPath)
644 if err != nil {
645 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
646 }
647 for _, f := range files {
648 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
649 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
650
651 if f.IsDir() {
652 // Moving a directory - check if there is an existing directory to merge with
653 externalLstat, err := os.Lstat(externalFilePath)
654 if err != nil {
655 if !os.IsNotExist(err) {
656 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
657 }
658 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
659 } else {
660 if externalLstat.IsDir() {
661 // Existing dir - try to merge the directories?
662 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
663 continue
664 } else {
665 // Existing file being replaced with a directory. Delete the existing file...
666 if err := os.RemoveAll(externalFilePath); err != nil {
667 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
668 }
669 }
670 }
671 } else {
672 // Moving a file (not a dir) - delete any existing file or directory
673 if err := os.RemoveAll(externalFilePath); err != nil {
674 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
675 }
676 }
677
678 // The actual move - do a rename instead of a copy in order to save disk space.
679 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
680 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
681 }
682 }
683}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500684
685func setMaxFiles(ctx build.Context) {
686 var limits syscall.Rlimit
687
688 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
689 if err != nil {
690 ctx.Println("Failed to get file limit:", err)
691 return
692 }
693
694 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
Colin Cross611a2fb2023-11-08 22:09:57 -0800695
696 // Go 1.21 modifies the file limit but restores the original when
697 // execing subprocesses if it hasn't be overridden. Call Setrlimit
698 // here even if it doesn't appear to be necessary so that the
699 // syscall package considers it set.
Liz Kammer4ae119c2022-02-09 10:54:05 -0500700
701 limits.Cur = limits.Max
702 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
703 if err != nil {
704 ctx.Println("Failed to increase file limit:", err)
705 }
706}