blob: 928ae172dc1d642543e1a73d24095980f55dffd1 [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() {
Jason Wuf0ec6312022-11-30 10:55:05 -0500120 //TODO(juu): Add logic to soong_ui to delete a hardcoded list of metrics files
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100121 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
122
Patrice Arruda73c790f2020-07-13 23:01:18 +0000123 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000124
Liz Kammer0e7993e2020-10-15 11:07:13 -0700125 c, args, err := getCommand(os.Args)
126 if err != nil {
127 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700128 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700129 }
130
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800131 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000132 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800133 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
134 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700135
Liz Kammerf2a80c62022-10-21 10:42:35 -0400136 // Create and start a new metric record.
137 met := metrics.New()
138 met.SetBuildDateTime(buildStarted)
139 met.SetBuildCommand(os.Args)
140
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800141 // Attach a new logger instance to the terminal output.
Liz Kammerf2a80c62022-10-21 10:42:35 -0400142 log := logger.NewWithMetrics(output, met)
Dan Willemsen1e704462016-08-21 15:17:17 -0700143 defer log.Cleanup()
144
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800145 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700146 ctx, cancel := context.WithCancel(context.Background())
147 defer cancel()
148
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800149 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700150 trace := tracer.New(log)
151 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700152
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800153 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700154 stat := &status.Status{}
155 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800156 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700157 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700158 stat.AddOutput(trace.StatusTracer())
159
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800160 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200161 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700162 trace.Close()
163 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700164 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700165 })
166
Dan Willemsen59339a22018-07-22 21:18:45 -0700167 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700168 Context: ctx,
169 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800170 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700171 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700172 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700173 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700174 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700175
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500176 config := c.config(buildCtx, args...)
177
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700178 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800179
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800180 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000181 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700182
Patrice Arruda40564022020-12-10 00:42:58 +0000183 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000184 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
185 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
186 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Jason Wuc4a03132022-11-28 13:39:48 -0500187 bp2buildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"bp2build_metrics.pb")
Jason Wuf0ec6312022-11-30 10:55:05 -0500188 soongBuildMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_build_metrics.pb")
Patrice Arruda40564022020-12-10 00:42:58 +0000189
Kousik Kumara0a44a82020-10-08 02:33:29 -0400190 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000191
Dan Willemsenb82471a2018-05-17 16:37:09 -0700192 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700193 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
194 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
195 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
196 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000197 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700198 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700199 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700200
Colin Cross8b8bec32019-11-15 13:18:43 -0800201 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
202 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
203 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
204
Liz Kammer4ae119c2022-02-09 10:54:05 -0500205 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500206
207 {
Patrice Arruda40564022020-12-10 00:42:58 +0000208 // The order of the function calls is important. The last defer function call
209 // is the first one that is executed to save the rbe metrics to a protobuf
210 // file. The soong metrics file is then next. Bazel profiles are written
211 // before the uploadMetrics is invoked. The written files are then uploaded
212 // if the uploading of the metrics is enabled.
213 files := []string{
214 buildErrorFile, // build error strings
215 rbeMetricsFile, // high level metrics related to remote build execution.
Jason Wuf0ec6312022-11-30 10:55:05 -0500216 soongBuildMetricsFile, // high level metrics related to soong build(except bp2build).
Jason Wuc4a03132022-11-28 13:39:48 -0500217 bp2buildMetricsFile, // high level metrics related to bp2build.
Patrice Arruda40564022020-12-10 00:42:58 +0000218 soongMetricsFile, // high level metrics related to this build system.
219 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
220 }
MarkDacekd0e7cd32022-12-02 22:22:40 +0000221
222 if !config.SkipMetricsUpload() {
223 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
224 }
Patrice Arruda40564022020-12-10 00:42:58 +0000225 defer met.Dump(soongMetricsFile)
Kousik Kumar7bc78192022-04-27 14:52:56 -0400226 defer build.CheckProdCreds(buildCtx, config)
Patrice Arruda40564022020-12-10 00:42:58 +0000227 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800228
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800229 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700230 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800231 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
232 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700233 if !strings.HasSuffix(start, "N") {
234 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
235 log.Verbosef("Took %dms to start up.",
236 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800237 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700238 }
239 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700240
241 if executable, err := os.Executable(); err == nil {
242 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
243 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700244 }
245
Dan Willemsen6b783c82019-03-08 11:42:28 -0800246 // Fix up the source tree due to a repo bug where it doesn't remove
247 // linkfiles that have been removed
248 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
249 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
250
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800251 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700252 f := build.NewSourceFinder(buildCtx, config)
253 defer f.Shutdown()
254 build.FindSources(buildCtx, config, f)
255
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700256 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700257}
258
Dan Willemsen6b783c82019-03-08 11:42:28 -0800259func fixBadDanglingLink(ctx build.Context, name string) {
260 _, err := os.Lstat(name)
261 if err != nil {
262 return
263 }
264 _, err = os.Stat(name)
265 if os.IsNotExist(err) {
266 err = os.Remove(name)
267 if err != nil {
268 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
269 }
270 }
271}
272
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700273func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700274 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400275 flags.SetOutput(ctx.Writer)
276
Dan Willemsen051133b2017-07-14 11:29:29 -0700277 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700278 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
279 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
280 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700281
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700282 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
283 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
284 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700285 flags.PrintDefaults()
286 }
287 abs := flags.Bool("abs", false, "Print the absolute path of the value")
288 flags.Parse(args)
289
290 if flags.NArg() != 1 {
291 flags.Usage()
Liz Kammerf2a80c62022-10-21 10:42:35 -0400292 ctx.Fatalf("Invalid usage")
Dan Willemsen051133b2017-07-14 11:29:29 -0700293 }
294
295 varName := flags.Arg(0)
296 if varName == "report_config" {
297 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
298 if err != nil {
299 ctx.Fatal(err)
300 }
301
302 fmt.Println(build.Banner(varData))
303 } else {
304 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
305 if err != nil {
306 ctx.Fatal(err)
307 }
308
309 if *abs {
310 var res []string
311 for _, path := range strings.Fields(varData[varName]) {
312 if abs, err := filepath.Abs(path); err == nil {
313 res = append(res, abs)
314 } else {
315 ctx.Fatalln("Failed to get absolute path of", path, err)
316 }
317 }
318 fmt.Println(strings.Join(res, " "))
319 } else {
320 fmt.Println(varData[varName])
321 }
322 }
323}
324
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700325func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700326 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400327 flags.SetOutput(ctx.Writer)
328
Dan Willemsen051133b2017-07-14 11:29:29 -0700329 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700330 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
331 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
332 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
333 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
334 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700335
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700336 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
337 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
338 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700339 flags.PrintDefaults()
340 }
341
342 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
343 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
344
345 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
346 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
347
348 flags.Parse(args)
349
350 if flags.NArg() != 0 {
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 vars := strings.Fields(*varsStr)
356 absVars := strings.Fields(*absVarsStr)
357
358 allVars := append([]string{}, vars...)
359 allVars = append(allVars, absVars...)
360
361 if i := indexList("report_config", allVars); i != -1 {
362 allVars = append(allVars[:i], allVars[i+1:]...)
363 allVars = append(allVars, build.BannerVars...)
364 }
365
366 if len(allVars) == 0 {
367 return
368 }
369
370 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
371 if err != nil {
372 ctx.Fatal(err)
373 }
374
375 for _, name := range vars {
376 if name == "report_config" {
377 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
378 } else {
379 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
380 }
381 }
382 for _, name := range absVars {
383 var res []string
384 for _, path := range strings.Fields(varData[name]) {
385 abs, err := filepath.Abs(path)
386 if err != nil {
387 ctx.Fatalln("Failed to get absolute path of", path, err)
388 }
389 res = append(res, abs)
390 }
391 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
392 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700393}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700394
Patrice Arrudab7b22822019-05-21 17:46:23 -0700395func stdio() terminal.StdioInterface {
396 return terminal.StdioImpl{}
397}
398
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800399// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
400// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700401func customStdio() terminal.StdioInterface {
402 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
403}
404
405// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
406func dumpVarConfig(ctx build.Context, args ...string) build.Config {
407 return build.NewConfig(ctx)
408}
409
Patrice Arrudab7b22822019-05-21 17:46:23 -0700410func buildActionConfig(ctx build.Context, args ...string) build.Config {
411 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400412 flags.SetOutput(ctx.Writer)
413
Patrice Arrudab7b22822019-05-21 17:46:23 -0700414 flags.Usage = func() {
415 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
416 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
417 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
418 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
419 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
420 fmt.Fprintln(ctx.Writer, "")
421 flags.PrintDefaults()
422 }
423
424 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700425 name string
426 description string
427 action build.BuildAction
428 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700429 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700430 name: "all-modules",
431 description: "Build action: build from the top of the source tree.",
432 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700433 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700434 // This is redirecting to mma build command behaviour. Once it has soaked for a
435 // while, the build command is deleted from here once it has been removed from the
436 // envsetup.sh.
437 name: "modules-in-a-dir-no-deps",
438 description: "Build action: builds all of the modules in the current directory without their dependencies.",
439 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700440 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700441 // This is redirecting to mmma build command behaviour. Once it has soaked for a
442 // while, the build command is deleted from here once it has been removed from the
443 // envsetup.sh.
444 name: "modules-in-dirs-no-deps",
445 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
446 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700447 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700448 name: "modules-in-a-dir",
449 description: "Build action: builds all of the modules in the current directory and their dependencies.",
450 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700451 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700452 name: "modules-in-dirs",
453 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
454 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700455 }}
456 for i, flag := range buildActionFlags {
457 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
458 }
459 dir := flags.String("dir", "", "Directory of the executed build command.")
460
461 // Only interested in the first two args which defines the build action and the directory.
462 // The remaining arguments are passed down to the config.
463 const numBuildActionFlags = 2
464 if len(args) < numBuildActionFlags {
465 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400466 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700467 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400468 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700469
470 // The next block of code is to validate that exactly one build action is set and the dir flag
471 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400472 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700473 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400474 for _, f := range buildActionFlags {
475 if f.set {
476 if buildActionFound {
477 if parseError == nil {
478 //otherwise Parse() already called Usage()
479 flags.Usage()
480 }
481 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
482 }
483 buildActionFound = true
484 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700485 }
486 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400487 if !buildActionFound {
488 if parseError == nil {
489 //otherwise Parse() already called Usage()
490 flags.Usage()
491 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700492 ctx.Fatalln("Build action not defined.")
493 }
494 if *dir == "" {
495 ctx.Fatalln("-dir not specified.")
496 }
497
498 // Remove the build action flags from the args as they are not recognized by the config.
499 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700500 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700501}
502
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100503func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700504 if config.IsVerbose() {
505 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700506 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
507 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
508 fmt.Fprintln(writer, "!")
509 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
510 fmt.Fprintln(writer, "!")
511 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
512 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400513 ctx.Fatal("Invalid argument")
Dan Willemsenc6360832019-07-25 14:07:36 -0700514 }
515
516 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
517 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700518 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700519 fmt.Fprintln(writer, "!")
520 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
521 fmt.Fprintln(writer, "!")
522 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
523 fmt.Fprintln(writer, "")
Liz Kammerf2a80c62022-10-21 10:42:35 -0400524 ctx.Fatal("Invalid environment")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700525 }
526
Anton Hansson5a7861a2021-06-04 10:09:01 +0100527 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700528}
529
530// getCommand finds the appropriate command based on args[1] flag. args[0]
531// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700532func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400533 listFlags := func() []string {
534 flags := make([]string, len(commands))
535 for i, c := range commands {
536 flags[i] = c.flag
537 }
538 return flags
539 }
540
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700541 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400542 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700543 }
544
545 for _, c := range commands {
546 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700547 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700548 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700549 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400550 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 -0700551}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000552
553// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
554func populateExternalDistDir(ctx build.Context, config build.Config) {
555 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
556 var err error
557 var internalDistDirPath string
558 var externalDistDirPath string
559 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
560 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
561 }
562 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
563 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
564 }
565 if externalDistDirPath == internalDistDirPath {
566 return
567 }
568
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000569 // Make sure the internal DIST_DIR actually exists before trying to read from it
570 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
571 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
572 return
573 }
574
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000575 // Make sure the external DIST_DIR actually exists before trying to write to it
576 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
577 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
578 }
579
580 ctx.Println("Populating external DIST_DIR...")
581
582 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
583}
584
585func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
586 files, err := ioutil.ReadDir(internalDistDirPath)
587 if err != nil {
588 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
589 }
590 for _, f := range files {
591 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
592 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
593
594 if f.IsDir() {
595 // Moving a directory - check if there is an existing directory to merge with
596 externalLstat, err := os.Lstat(externalFilePath)
597 if err != nil {
598 if !os.IsNotExist(err) {
599 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
600 }
601 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
602 } else {
603 if externalLstat.IsDir() {
604 // Existing dir - try to merge the directories?
605 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
606 continue
607 } else {
608 // Existing file being replaced with a directory. Delete the existing file...
609 if err := os.RemoveAll(externalFilePath); err != nil {
610 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
611 }
612 }
613 }
614 } else {
615 // Moving a file (not a dir) - delete any existing file or directory
616 if err := os.RemoveAll(externalFilePath); err != nil {
617 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
618 }
619 }
620
621 // The actual move - do a rename instead of a copy in order to save disk space.
622 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
623 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
624 }
625 }
626}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500627
628func setMaxFiles(ctx build.Context) {
629 var limits syscall.Rlimit
630
631 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
632 if err != nil {
633 ctx.Println("Failed to get file limit:", err)
634 return
635 }
636
637 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
638 if limits.Cur == limits.Max {
639 return
640 }
641
642 limits.Cur = limits.Max
643 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
644 if err != nil {
645 ctx.Println("Failed to increase file limit:", err)
646 }
647}