blob: df8101c22953ce09d5abed4ef9bb7e4110da67fe [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)
LaMont Jones127127b2023-10-11 17:58:14 +000062
63 // whether to do common setup before calling run.
64 doSetup bool
Patrice Arrudaa5c25422019-04-09 18:49:49 -070065}
66
Patrice Arrudaa5c25422019-04-09 18:49:49 -070067// list of supported commands (flags) supported by soong ui
Usta6feae382021-12-13 12:31:50 -050068var commands = []command{
Patrice Arrudaa5c25422019-04-09 18:49:49 -070069 {
Anton Hansson5a7861a2021-06-04 10:09:01 +010070 flag: "--make-mode",
Patrice Arrudaa5c25422019-04-09 18:49:49 -070071 description: "build the modules by the target name (i.e. soong_docs)",
Usta Shrestha59417a12022-08-05 17:14:49 -040072 config: build.NewConfig,
73 stdio: stdio,
74 run: runMake,
LaMont Jones127127b2023-10-11 17:58:14 +000075 doSetup: true,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070076 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000077 flag: "--dumpvar-mode",
78 description: "print the value of the legacy make variable VAR to stdout",
79 simpleOutput: true,
80 logsPrefix: "dumpvars-",
81 config: dumpVarConfig,
82 stdio: customStdio,
83 run: dumpVar,
LaMont Jones127127b2023-10-11 17:58:14 +000084 doSetup: true,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070085 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000086 flag: "--dumpvars-mode",
87 description: "dump the values of one or more legacy make variables, in shell syntax",
88 simpleOutput: true,
89 logsPrefix: "dumpvars-",
90 config: dumpVarConfig,
91 stdio: customStdio,
92 run: dumpVars,
LaMont Jones127127b2023-10-11 17:58:14 +000093 doSetup: true,
Patrice Arrudab7b22822019-05-21 17:46:23 -070094 }, {
95 flag: "--build-mode",
96 description: "build modules based on the specified build action",
97 config: buildActionConfig,
98 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010099 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700100 },
101}
102
103// indexList returns the index of first found s. -1 is return if s is not
104// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700105func indexList(s string, list []string) int {
106 for i, l := range list {
107 if l == s {
108 return i
109 }
110 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700111 return -1
112}
113
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700114// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700115func inList(s string, list []string) bool {
116 return indexList(s, list) != -1
117}
118
Jason Wucc166a72022-12-19 11:53:12 -0500119func deleteStaleMetrics(metricsFilePathSlice []string) error {
120 for _, metricsFilePath := range metricsFilePathSlice {
121 if err := os.Remove(metricsFilePath); err != nil && !os.IsNotExist(err) {
122 return fmt.Errorf("Failed to remove %s\nError message: %w", metricsFilePath, err)
123 }
124 }
125 return nil
126}
127
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700128// Main execution of soong_ui. The command format is as follows:
129//
Usta Shrestha59417a12022-08-05 17:14:49 -0400130// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700131//
132// Command is the type of soong_ui execution. Only one type of
133// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700134func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100135 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
136
Patrice Arruda73c790f2020-07-13 23:01:18 +0000137 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000138
Liz Kammer0e7993e2020-10-15 11:07:13 -0700139 c, args, err := getCommand(os.Args)
140 if err != nil {
141 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700142 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700143 }
144
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800145 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000146 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800147 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
148 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700149
Liz Kammerf2a80c62022-10-21 10:42:35 -0400150 // Create and start a new metric record.
151 met := metrics.New()
152 met.SetBuildDateTime(buildStarted)
153 met.SetBuildCommand(os.Args)
154
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800155 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400156 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700157 defer log.Cleanup()
158
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800159 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700160 ctx, cancel := context.WithCancel(context.Background())
161 defer cancel()
162
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800163 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700164 trace := tracer.New(log)
165 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700166
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800167 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700168 stat := &status.Status{}
Jeongik Cha036b5a32023-03-22 17:31:48 +0900169
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800170 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700171 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700172 stat.AddOutput(trace.StatusTracer())
173
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800174 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200175 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700176 trace.Close()
177 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700178 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700179 })
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900180 criticalPath := status.NewCriticalPath()
Dan Willemsen59339a22018-07-22 21:18:45 -0700181 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900182 Context: ctx,
183 Logger: log,
184 Metrics: met,
185 Tracer: trace,
186 Writer: output,
187 Status: stat,
188 CriticalPath: criticalPath,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700189 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700190
LaMont Jones127127b2023-10-11 17:58:14 +0000191 freshConfig := func() build.Config {
192 config := c.config(buildCtx, args...)
193 config.SetLogsPrefix(c.logsPrefix)
194 return config
195 }
196 config := freshConfig()
MarkDacek6614d9c2022-12-07 21:57:38 +0000197 logsDir := config.LogsDir()
198 buildStarted = config.BuildStartedTimeOrDefault(buildStarted)
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500199
MarkDacek6614d9c2022-12-07 21:57:38 +0000200 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
201 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
202 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
203 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_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.
Jason Wud1254402023-03-01 20:26:30 -0500211 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build)
MarkDacek6614d9c2022-12-07 21:57:38 +0000212 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
213 }
214
215 os.MkdirAll(logsDir, 0777)
216
217 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
218
219 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
220
Joe Onorato010c6b62023-07-14 16:32:53 -0700221 log.Verbose("Command Line: ")
222 for i, arg := range os.Args {
223 log.Verbosef(" [%d] %s", i, arg)
224 }
225
LaMont Jones127127b2023-10-11 17:58:14 +0000226 if c.doSetup {
227 // We need to call logAndSymlinkSetup before we can do product
228 // config, which is how we get PRODUCT_CONFIG_RELEASE_MAPS set
229 // for the final product config for the build.
230 logAndSymlinkSetup(buildCtx, config)
231 if build.SetProductReleaseConfigMaps(buildCtx, config) {
232 config = freshConfig()
233 }
234
235 }
Jeongik Cha036b5a32023-03-22 17:31:48 +0900236 defer func() {
237 stat.Finish()
238 criticalPath.WriteToMetrics(met)
239 met.Dump(soongMetricsFile)
240 if !config.SkipMetricsUpload() {
MarkDacek733b77c2023-05-09 18:21:36 +0000241 build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, metricsFiles...)
Jeongik Cha036b5a32023-03-22 17:31:48 +0900242 }
243 }()
Jason Wu51d0ad72023-02-08 18:00:33 -0500244 c.run(buildCtx, config, args)
245
MarkDacek6614d9c2022-12-07 21:57:38 +0000246}
247
248func logAndSymlinkSetup(buildCtx build.Context, config build.Config) {
249 log := buildCtx.ContextImpl.Logger
250 logsPrefix := config.GetLogsPrefix()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700251 build.SetupOutDir(buildCtx, config)
Patrice Arruda83842d72020-12-08 19:42:08 +0000252 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700253
Patrice Arruda40564022020-12-10 00:42:58 +0000254 // Common list of metric file definition.
MarkDacek6614d9c2022-12-07 21:57:38 +0000255 buildErrorFile := filepath.Join(logsDir, logsPrefix+"build_error")
256 rbeMetricsFile := filepath.Join(logsDir, logsPrefix+"rbe_metrics.pb")
257 soongMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_metrics")
258 bp2buildMetricsFile := filepath.Join(logsDir, logsPrefix+"bp2build_metrics.pb")
259 soongBuildMetricsFile := filepath.Join(logsDir, logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000260
Jason Wucc166a72022-12-19 11:53:12 -0500261 //Delete the stale metrics files
MarkDacek39825ea2023-10-26 19:59:27 +0000262 staleFileSlice := []string{buildErrorFile, rbeMetricsFile, soongMetricsFile, bp2buildMetricsFile, soongBuildMetricsFile}
Jason Wucc166a72022-12-19 11:53:12 -0500263 if err := deleteStaleMetrics(staleFileSlice); err != nil {
264 log.Fatalln(err)
265 }
266
Kousik Kumara0a44a82020-10-08 02:33:29 -0400267 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000268
MarkDacek6614d9c2022-12-07 21:57:38 +0000269 stat := buildCtx.Status
270 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, logsPrefix+"verbose.log")))
271 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000272 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Jeongik Cha28c1fe52023-03-07 15:19:44 +0900273 stat.AddOutput(status.NewCriticalPathLogger(log, buildCtx.CriticalPath))
MarkDacek6614d9c2022-12-07 21:57:38 +0000274 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700275
Colin Cross8b8bec32019-11-15 13:18:43 -0800276 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
277 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
278 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
279
Liz Kammer4ae119c2022-02-09 10:54:05 -0500280 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500281
MarkDacek6614d9c2022-12-07 21:57:38 +0000282 defer build.CheckProdCreds(buildCtx, config)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800283
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800284 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700285 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800286 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
287 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700288 if !strings.HasSuffix(start, "N") {
289 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
290 log.Verbosef("Took %dms to start up.",
291 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800292 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700293 }
294 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700295
296 if executable, err := os.Executable(); err == nil {
MarkDacek6614d9c2022-12-07 21:57:38 +0000297 buildCtx.ContextImpl.Tracer.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
Dan Willemsencae59bc2017-07-13 14:27:31 -0700298 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700299 }
300
Liz Kammer00543dc2023-09-15 21:03:08 -0400301 removeBadTargetRename(buildCtx, config)
302
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800303 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700304 f := build.NewSourceFinder(buildCtx, config)
305 defer f.Shutdown()
306 build.FindSources(buildCtx, config, f)
Dan Willemsen051133b2017-07-14 11:29:29 -0700307}
308
Liz Kammer00543dc2023-09-15 21:03:08 -0400309func removeBadTargetRename(ctx build.Context, config build.Config) {
310 log := ctx.ContextImpl.Logger
311 // find bad paths
312 m, err := filepath.Glob(filepath.Join(config.OutDir(), "bazel", "output", "execroot", "__main__", "bazel-out", "mixed_builds_product-*", "bin", "tools", "metalava", "metalava"))
313 if err != nil {
314 log.Fatalf("Glob for invalid file failed %s", err)
315 }
316 for _, f := range m {
317 info, err := os.Stat(f)
318 if err != nil {
319 log.Fatalf("Stat of invalid file %q failed %s", f, err)
320 }
321 // if it's a directory, leave it, but remove the files
322 if !info.IsDir() {
323 err = os.Remove(f)
324 if err != nil {
325 log.Fatalf("Remove of invalid file %q failed %s", f, err)
326 } else {
327 log.Verbosef("Removed %q", f)
328 }
329 }
330 }
331}
332
MarkDacek6614d9c2022-12-07 21:57:38 +0000333func dumpVar(ctx build.Context, config build.Config, args []string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700334 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400335 flags.SetOutput(ctx.Writer)
336
Dan Willemsen051133b2017-07-14 11:29:29 -0700337 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700338 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
339 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
340 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700341
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700342 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
343 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
344 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700345 flags.PrintDefaults()
346 }
347 abs := flags.Bool("abs", false, "Print the absolute path of the value")
348 flags.Parse(args)
349
350 if flags.NArg() != 1 {
351 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400352 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700353 }
354
355 varName := flags.Arg(0)
356 if varName == "report_config" {
357 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
358 if err != nil {
359 ctx.Fatal(err)
360 }
361
362 fmt.Println(build.Banner(varData))
363 } else {
364 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
365 if err != nil {
366 ctx.Fatal(err)
367 }
368
369 if *abs {
370 var res []string
371 for _, path := range strings.Fields(varData[varName]) {
372 if abs, err := filepath.Abs(path); err == nil {
373 res = append(res, abs)
374 } else {
375 ctx.Fatalln("Failed to get absolute path of", path, err)
376 }
377 }
378 fmt.Println(strings.Join(res, " "))
379 } else {
380 fmt.Println(varData[varName])
381 }
382 }
383}
384
MarkDacek6614d9c2022-12-07 21:57:38 +0000385func dumpVars(ctx build.Context, config build.Config, args []string) {
MarkDacek6614d9c2022-12-07 21:57:38 +0000386
Dan Willemsen051133b2017-07-14 11:29:29 -0700387 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400388 flags.SetOutput(ctx.Writer)
389
Dan Willemsen051133b2017-07-14 11:29:29 -0700390 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700391 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
392 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
393 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
394 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
395 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700396
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700397 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
398 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
399 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700400 flags.PrintDefaults()
401 }
402
403 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
404 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
405
406 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
407 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
408
409 flags.Parse(args)
410
411 if flags.NArg() != 0 {
412 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400413 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700414 }
415
416 vars := strings.Fields(*varsStr)
417 absVars := strings.Fields(*absVarsStr)
418
419 allVars := append([]string{}, vars...)
420 allVars = append(allVars, absVars...)
421
422 if i := indexList("report_config", allVars); i != -1 {
423 allVars = append(allVars[:i], allVars[i+1:]...)
424 allVars = append(allVars, build.BannerVars...)
425 }
426
427 if len(allVars) == 0 {
428 return
429 }
430
431 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
432 if err != nil {
433 ctx.Fatal(err)
434 }
435
436 for _, name := range vars {
437 if name == "report_config" {
438 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
439 } else {
440 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
441 }
442 }
443 for _, name := range absVars {
444 var res []string
445 for _, path := range strings.Fields(varData[name]) {
446 abs, err := filepath.Abs(path)
447 if err != nil {
448 ctx.Fatalln("Failed to get absolute path of", path, err)
449 }
450 res = append(res, abs)
451 }
452 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
453 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700454}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700455
Patrice Arrudab7b22822019-05-21 17:46:23 -0700456func stdio() terminal.StdioInterface {
457 return terminal.StdioImpl{}
458}
459
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800460// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
461// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700462func customStdio() terminal.StdioInterface {
463 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
464}
465
466// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
467func dumpVarConfig(ctx build.Context, args ...string) build.Config {
468 return build.NewConfig(ctx)
469}
470
Patrice Arrudab7b22822019-05-21 17:46:23 -0700471func buildActionConfig(ctx build.Context, args ...string) build.Config {
472 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400473 flags.SetOutput(ctx.Writer)
474
Patrice Arrudab7b22822019-05-21 17:46:23 -0700475 flags.Usage = func() {
476 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
477 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
478 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
479 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
480 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
481 fmt.Fprintln(ctx.Writer, "")
482 flags.PrintDefaults()
483 }
484
485 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700486 name string
487 description string
488 action build.BuildAction
489 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700490 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700491 name: "all-modules",
492 description: "Build action: build from the top of the source tree.",
493 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700494 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700495 // This is redirecting to mma build command behaviour. Once it has soaked for a
496 // while, the build command is deleted from here once it has been removed from the
497 // envsetup.sh.
498 name: "modules-in-a-dir-no-deps",
499 description: "Build action: builds all of the modules in the current directory without their dependencies.",
500 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700501 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700502 // This is redirecting to mmma build command behaviour. Once it has soaked for a
503 // while, the build command is deleted from here once it has been removed from the
504 // envsetup.sh.
505 name: "modules-in-dirs-no-deps",
506 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
507 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700508 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700509 name: "modules-in-a-dir",
510 description: "Build action: builds all of the modules in the current directory and their dependencies.",
511 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700512 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700513 name: "modules-in-dirs",
514 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
515 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700516 }}
517 for i, flag := range buildActionFlags {
518 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
519 }
520 dir := flags.String("dir", "", "Directory of the executed build command.")
521
522 // Only interested in the first two args which defines the build action and the directory.
523 // The remaining arguments are passed down to the config.
524 const numBuildActionFlags = 2
525 if len(args) < numBuildActionFlags {
526 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400527 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700528 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400529 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700530
531 // The next block of code is to validate that exactly one build action is set and the dir flag
532 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400533 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700534 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400535 for _, f := range buildActionFlags {
536 if f.set {
537 if buildActionFound {
538 if parseError == nil {
539 //otherwise Parse() already called Usage()
540 flags.Usage()
541 }
542 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
543 }
544 buildActionFound = true
545 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700546 }
547 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400548 if !buildActionFound {
549 if parseError == nil {
550 //otherwise Parse() already called Usage()
551 flags.Usage()
552 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700553 ctx.Fatalln("Build action not defined.")
554 }
555 if *dir == "" {
556 ctx.Fatalln("-dir not specified.")
557 }
558
559 // Remove the build action flags from the args as they are not recognized by the config.
560 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700561 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700562}
563
MarkDacek6614d9c2022-12-07 21:57:38 +0000564func runMake(ctx build.Context, config build.Config, _ []string) {
MarkDacek6614d9c2022-12-07 21:57:38 +0000565 logsDir := config.LogsDir()
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700566 if config.IsVerbose() {
567 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700568 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
569 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
570 fmt.Fprintln(writer, "!")
571 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
572 fmt.Fprintln(writer, "!")
573 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
574 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400575 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700576 }
577
578 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
579 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700580 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700581 fmt.Fprintln(writer, "!")
582 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
583 fmt.Fprintln(writer, "!")
584 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
585 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400586 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700587 }
588
Anton Hansson5a7861a2021-06-04 10:09:01 +0100589 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700590}
591
592// getCommand finds the appropriate command based on args[1] flag. args[0]
593// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700594func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400595 listFlags := func() []string {
596 flags := make([]string, len(commands))
597 for i, c := range commands {
598 flags[i] = c.flag
599 }
600 return flags
601 }
602
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700603 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400604 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700605 }
606
607 for _, c := range commands {
608 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700609 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700610 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700611 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400612 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 -0700613}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000614
615// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
616func populateExternalDistDir(ctx build.Context, config build.Config) {
617 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
618 var err error
619 var internalDistDirPath string
620 var externalDistDirPath string
621 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
622 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
623 }
624 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
625 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
626 }
627 if externalDistDirPath == internalDistDirPath {
628 return
629 }
630
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000631 // Make sure the internal DIST_DIR actually exists before trying to read from it
632 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
633 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
634 return
635 }
636
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000637 // Make sure the external DIST_DIR actually exists before trying to write to it
638 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
639 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
640 }
641
642 ctx.Println("Populating external DIST_DIR...")
643
644 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
645}
646
647func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
648 files, err := ioutil.ReadDir(internalDistDirPath)
649 if err != nil {
650 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
651 }
652 for _, f := range files {
653 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
654 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
655
656 if f.IsDir() {
657 // Moving a directory - check if there is an existing directory to merge with
658 externalLstat, err := os.Lstat(externalFilePath)
659 if err != nil {
660 if !os.IsNotExist(err) {
661 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
662 }
663 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
664 } else {
665 if externalLstat.IsDir() {
666 // Existing dir - try to merge the directories?
667 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
668 continue
669 } else {
670 // Existing file being replaced with a directory. Delete the existing file...
671 if err := os.RemoveAll(externalFilePath); err != nil {
672 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
673 }
674 }
675 }
676 } else {
677 // Moving a file (not a dir) - delete any existing file or directory
678 if err := os.RemoveAll(externalFilePath); err != nil {
679 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
680 }
681 }
682
683 // The actual move - do a rename instead of a copy in order to save disk space.
684 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
685 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
686 }
687 }
688}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500689
690func setMaxFiles(ctx build.Context) {
691 var limits syscall.Rlimit
692
693 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
694 if err != nil {
695 ctx.Println("Failed to get file limit:", err)
696 return
697 }
698
699 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
700 if limits.Cur == limits.Max {
701 return
702 }
703
704 limits.Cur = limits.Max
705 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
706 if err != nil {
707 ctx.Println("Failed to increase file limit:", err)
708 }
709}