blob: 8adc86f4aa50881d41ec676b514e02578ea00bd3 [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
61 run func(ctx build.Context, config build.Config, args []string, logsDir string)
62}
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
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700113// Main execution of soong_ui. The command format is as follows:
114//
Usta Shrestha59417a12022-08-05 17:14:49 -0400115// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700116//
117// Command is the type of soong_ui execution. Only one type of
118// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700119func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100120 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
121
Patrice Arruda73c790f2020-07-13 23:01:18 +0000122 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000123
Liz Kammer0e7993e2020-10-15 11:07:13 -0700124 c, args, err := getCommand(os.Args)
125 if err != nil {
126 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700127 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700128 }
129
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800130 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000131 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800132 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
133 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700134
Liz Kammerf2a80c62022-10-21 10:42:35 -0400135 // Create and start a new metric record.
136 met := metrics.New()
137 met.SetBuildDateTime(buildStarted)
138 met.SetBuildCommand(os.Args)
139
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800140 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400141 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700142 defer log.Cleanup()
143
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800144 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700145 ctx, cancel := context.WithCancel(context.Background())
146 defer cancel()
147
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800148 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700149 trace := tracer.New(log)
150 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700151
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800152 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700153 stat := &status.Status{}
154 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800155 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700156 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700157 stat.AddOutput(trace.StatusTracer())
158
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800159 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200160 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700161 trace.Close()
162 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700163 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700164 })
165
Dan Willemsen59339a22018-07-22 21:18:45 -0700166 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700167 Context: ctx,
168 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800169 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700170 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700171 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700172 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700173 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700174
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500175 config := c.config(buildCtx, args...)
176
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700177 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800178
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800179 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000180 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700181
Patrice Arruda40564022020-12-10 00:42:58 +0000182 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000183 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
184 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
185 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Jason Wuc4a03132022-11-28 13:39:48 -0500186 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000187
Kousik Kumara0a44a82020-10-08 02:33:29 -0400188 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000189
Dan Willemsenb82471a2018-05-17 16:37:09 -0700190 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700191 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
192 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
193 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
194 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000195 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700196 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700197 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700198
Colin Cross8b8bec32019-11-15 13:18:43 -0800199 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
200 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
201 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
202
Liz Kammer4ae119c2022-02-09 10:54:05 -0500203 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500204
205 {
Patrice Arruda40564022020-12-10 00:42:58 +0000206 // The order of the function calls is important. The last defer function call
207 // is the first one that is executed to save the rbe metrics to a protobuf
208 // file. The soong metrics file is then next. Bazel profiles are written
209 // before the uploadMetrics is invoked. The written files are then uploaded
210 // if the uploading of the metrics is enabled.
211 files := []string{
212 buildErrorFile, // build error strings
213 rbeMetricsFile, // high level metrics related to remote build execution.
Jason Wuc4a03132022-11-28 13:39:48 -0500214 bp2buildMetricsFile, // high level metrics related to bp2build.
Patrice Arruda40564022020-12-10 00:42:58 +0000215 soongMetricsFile, // high level metrics related to this build system.
216 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
217 }
MarkDacekd0e7cd32022-12-02 22:22:40 +0000218
219 if !config.SkipMetricsUpload() {
220 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
221 }
222
Patrice Arruda40564022020-12-10 00:42:58 +0000223 defer met.Dump(soongMetricsFile)
Kousik Kumar7bc78192022-04-27 14:52:56 -0400224 defer build.CheckProdCreds(buildCtx, config)
Patrice Arruda40564022020-12-10 00:42:58 +0000225 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800226
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800227 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700228 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800229 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
230 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700231 if !strings.HasSuffix(start, "N") {
232 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
233 log.Verbosef("Took %dms to start up.",
234 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800235 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700236 }
237 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700238
239 if executable, err := os.Executable(); err == nil {
240 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
241 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700242 }
243
Dan Willemsen6b783c82019-03-08 11:42:28 -0800244 // Fix up the source tree due to a repo bug where it doesn't remove
245 // linkfiles that have been removed
246 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
247 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
248
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800249 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700250 f := build.NewSourceFinder(buildCtx, config)
251 defer f.Shutdown()
252 build.FindSources(buildCtx, config, f)
253
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700254 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700255}
256
Dan Willemsen6b783c82019-03-08 11:42:28 -0800257func fixBadDanglingLink(ctx build.Context, name string) {
258 _, err := os.Lstat(name)
259 if err != nil {
260 return
261 }
262 _, err = os.Stat(name)
263 if os.IsNotExist(err) {
264 err = os.Remove(name)
265 if err != nil {
266 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
267 }
268 }
269}
270
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700271func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700272 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400273 flags.SetOutput(ctx.Writer)
274
Dan Willemsen051133b2017-07-14 11:29:29 -0700275 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700276 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
277 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
278 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700279
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700280 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
281 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
282 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700283 flags.PrintDefaults()
284 }
285 abs := flags.Bool("abs", false, "Print the absolute path of the value")
286 flags.Parse(args)
287
288 if flags.NArg() != 1 {
289 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400290 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700291 }
292
293 varName := flags.Arg(0)
294 if varName == "report_config" {
295 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
296 if err != nil {
297 ctx.Fatal(err)
298 }
299
300 fmt.Println(build.Banner(varData))
301 } else {
302 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
303 if err != nil {
304 ctx.Fatal(err)
305 }
306
307 if *abs {
308 var res []string
309 for _, path := range strings.Fields(varData[varName]) {
310 if abs, err := filepath.Abs(path); err == nil {
311 res = append(res, abs)
312 } else {
313 ctx.Fatalln("Failed to get absolute path of", path, err)
314 }
315 }
316 fmt.Println(strings.Join(res, " "))
317 } else {
318 fmt.Println(varData[varName])
319 }
320 }
321}
322
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700323func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700324 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400325 flags.SetOutput(ctx.Writer)
326
Dan Willemsen051133b2017-07-14 11:29:29 -0700327 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700328 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
329 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
330 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
331 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
332 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700333
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700334 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
335 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
336 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700337 flags.PrintDefaults()
338 }
339
340 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
341 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
342
343 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
344 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
345
346 flags.Parse(args)
347
348 if flags.NArg() != 0 {
349 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400350 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700351 }
352
353 vars := strings.Fields(*varsStr)
354 absVars := strings.Fields(*absVarsStr)
355
356 allVars := append([]string{}, vars...)
357 allVars = append(allVars, absVars...)
358
359 if i := indexList("report_config", allVars); i != -1 {
360 allVars = append(allVars[:i], allVars[i+1:]...)
361 allVars = append(allVars, build.BannerVars...)
362 }
363
364 if len(allVars) == 0 {
365 return
366 }
367
368 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
369 if err != nil {
370 ctx.Fatal(err)
371 }
372
373 for _, name := range vars {
374 if name == "report_config" {
375 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
376 } else {
377 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
378 }
379 }
380 for _, name := range absVars {
381 var res []string
382 for _, path := range strings.Fields(varData[name]) {
383 abs, err := filepath.Abs(path)
384 if err != nil {
385 ctx.Fatalln("Failed to get absolute path of", path, err)
386 }
387 res = append(res, abs)
388 }
389 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
390 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700391}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700392
Patrice Arrudab7b22822019-05-21 17:46:23 -0700393func stdio() terminal.StdioInterface {
394 return terminal.StdioImpl{}
395}
396
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800397// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
398// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700399func customStdio() terminal.StdioInterface {
400 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
401}
402
403// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
404func dumpVarConfig(ctx build.Context, args ...string) build.Config {
405 return build.NewConfig(ctx)
406}
407
Patrice Arrudab7b22822019-05-21 17:46:23 -0700408func buildActionConfig(ctx build.Context, args ...string) build.Config {
409 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400410 flags.SetOutput(ctx.Writer)
411
Patrice Arrudab7b22822019-05-21 17:46:23 -0700412 flags.Usage = func() {
413 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
414 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
415 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
416 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
417 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
418 fmt.Fprintln(ctx.Writer, "")
419 flags.PrintDefaults()
420 }
421
422 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700423 name string
424 description string
425 action build.BuildAction
426 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700427 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700428 name: "all-modules",
429 description: "Build action: build from the top of the source tree.",
430 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700431 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700432 // This is redirecting to mma build command behaviour. Once it has soaked for a
433 // while, the build command is deleted from here once it has been removed from the
434 // envsetup.sh.
435 name: "modules-in-a-dir-no-deps",
436 description: "Build action: builds all of the modules in the current directory without their dependencies.",
437 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700438 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700439 // This is redirecting to mmma build command behaviour. Once it has soaked for a
440 // while, the build command is deleted from here once it has been removed from the
441 // envsetup.sh.
442 name: "modules-in-dirs-no-deps",
443 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
444 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700445 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700446 name: "modules-in-a-dir",
447 description: "Build action: builds all of the modules in the current directory and their dependencies.",
448 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700449 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700450 name: "modules-in-dirs",
451 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
452 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700453 }}
454 for i, flag := range buildActionFlags {
455 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
456 }
457 dir := flags.String("dir", "", "Directory of the executed build command.")
458
459 // Only interested in the first two args which defines the build action and the directory.
460 // The remaining arguments are passed down to the config.
461 const numBuildActionFlags = 2
462 if len(args) < numBuildActionFlags {
463 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400464 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700465 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400466 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700467
468 // The next block of code is to validate that exactly one build action is set and the dir flag
469 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400470 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700471 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400472 for _, f := range buildActionFlags {
473 if f.set {
474 if buildActionFound {
475 if parseError == nil {
476 //otherwise Parse() already called Usage()
477 flags.Usage()
478 }
479 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
480 }
481 buildActionFound = true
482 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700483 }
484 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400485 if !buildActionFound {
486 if parseError == nil {
487 //otherwise Parse() already called Usage()
488 flags.Usage()
489 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700490 ctx.Fatalln("Build action not defined.")
491 }
492 if *dir == "" {
493 ctx.Fatalln("-dir not specified.")
494 }
495
496 // Remove the build action flags from the args as they are not recognized by the config.
497 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700498 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700499}
500
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100501func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700502 if config.IsVerbose() {
503 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700504 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
505 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
506 fmt.Fprintln(writer, "!")
507 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
508 fmt.Fprintln(writer, "!")
509 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
510 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400511 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700512 }
513
514 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
515 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700516 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700517 fmt.Fprintln(writer, "!")
518 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
519 fmt.Fprintln(writer, "!")
520 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
521 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400522 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700523 }
524
Anton Hansson5a7861a2021-06-04 10:09:01 +0100525 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700526}
527
528// getCommand finds the appropriate command based on args[1] flag. args[0]
529// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700530func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400531 listFlags := func() []string {
532 flags := make([]string, len(commands))
533 for i, c := range commands {
534 flags[i] = c.flag
535 }
536 return flags
537 }
538
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700539 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400540 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700541 }
542
543 for _, c := range commands {
544 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700545 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700546 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700547 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400548 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 -0700549}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000550
551// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
552func populateExternalDistDir(ctx build.Context, config build.Config) {
553 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
554 var err error
555 var internalDistDirPath string
556 var externalDistDirPath string
557 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
558 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
559 }
560 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
561 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
562 }
563 if externalDistDirPath == internalDistDirPath {
564 return
565 }
566
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000567 // Make sure the internal DIST_DIR actually exists before trying to read from it
568 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
569 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
570 return
571 }
572
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000573 // Make sure the external DIST_DIR actually exists before trying to write to it
574 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
575 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
576 }
577
578 ctx.Println("Populating external DIST_DIR...")
579
580 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
581}
582
583func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
584 files, err := ioutil.ReadDir(internalDistDirPath)
585 if err != nil {
586 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
587 }
588 for _, f := range files {
589 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
590 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
591
592 if f.IsDir() {
593 // Moving a directory - check if there is an existing directory to merge with
594 externalLstat, err := os.Lstat(externalFilePath)
595 if err != nil {
596 if !os.IsNotExist(err) {
597 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
598 }
599 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
600 } else {
601 if externalLstat.IsDir() {
602 // Existing dir - try to merge the directories?
603 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
604 continue
605 } else {
606 // Existing file being replaced with a directory. Delete the existing file...
607 if err := os.RemoveAll(externalFilePath); err != nil {
608 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
609 }
610 }
611 }
612 } else {
613 // Moving a file (not a dir) - delete any existing file or directory
614 if err := os.RemoveAll(externalFilePath); err != nil {
615 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
616 }
617 }
618
619 // The actual move - do a rename instead of a copy in order to save disk space.
620 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
621 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
622 }
623 }
624}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500625
626func setMaxFiles(ctx build.Context) {
627 var limits syscall.Rlimit
628
629 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
630 if err != nil {
631 ctx.Println("Failed to get file limit:", err)
632 return
633 }
634
635 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
636 if limits.Cur == limits.Max {
637 return
638 }
639
640 limits.Cur = limits.Max
641 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
642 if err != nil {
643 ctx.Println("Failed to increase file limit:", err)
644 }
645}