blob: cb856348849c61f8145738bce2604dc82ab9939c [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
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800135 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700136 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700137 defer log.Cleanup()
138
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800139 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700140 ctx, cancel := context.WithCancel(context.Background())
141 defer cancel()
142
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800143 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700144 trace := tracer.New(log)
145 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700146
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800147 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800148 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000149 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700150 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800151
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
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000179 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000180 defer populateExternalDistDir(buildCtx, config)
181 }
182
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800183 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000184 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700185
Patrice Arruda40564022020-12-10 00:42:58 +0000186 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000187 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
188 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
189 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000190
Kousik Kumara0a44a82020-10-08 02:33:29 -0400191 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000192
Dan Willemsenb82471a2018-05-17 16:37:09 -0700193 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700194 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
195 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
196 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
197 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000198 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700199 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700200 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700201
Colin Cross8b8bec32019-11-15 13:18:43 -0800202 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
203 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
204 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
205
Liz Kammer4ae119c2022-02-09 10:54:05 -0500206 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500207
208 {
Patrice Arruda40564022020-12-10 00:42:58 +0000209 // The order of the function calls is important. The last defer function call
210 // is the first one that is executed to save the rbe metrics to a protobuf
211 // file. The soong metrics file is then next. Bazel profiles are written
212 // before the uploadMetrics is invoked. The written files are then uploaded
213 // if the uploading of the metrics is enabled.
214 files := []string{
215 buildErrorFile, // build error strings
216 rbeMetricsFile, // high level metrics related to remote build execution.
217 soongMetricsFile, // high level metrics related to this build system.
218 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
219 }
220 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
221 defer met.Dump(soongMetricsFile)
Kousik Kumar7bc78192022-04-27 14:52:56 -0400222 defer build.CheckProdCreds(buildCtx, config)
Patrice Arruda40564022020-12-10 00:42:58 +0000223 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800224
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800225 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700226 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800227 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
228 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700229 if !strings.HasSuffix(start, "N") {
230 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
231 log.Verbosef("Took %dms to start up.",
232 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800233 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700234 }
235 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700236
237 if executable, err := os.Executable(); err == nil {
238 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
239 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700240 }
241
Dan Willemsen6b783c82019-03-08 11:42:28 -0800242 // Fix up the source tree due to a repo bug where it doesn't remove
243 // linkfiles that have been removed
244 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
245 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
246
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800247 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700248 f := build.NewSourceFinder(buildCtx, config)
249 defer f.Shutdown()
250 build.FindSources(buildCtx, config, f)
251
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700252 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700253}
254
Dan Willemsen6b783c82019-03-08 11:42:28 -0800255func fixBadDanglingLink(ctx build.Context, name string) {
256 _, err := os.Lstat(name)
257 if err != nil {
258 return
259 }
260 _, err = os.Stat(name)
261 if os.IsNotExist(err) {
262 err = os.Remove(name)
263 if err != nil {
264 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
265 }
266 }
267}
268
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700269func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700270 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400271 flags.SetOutput(ctx.Writer)
272
Dan Willemsen051133b2017-07-14 11:29:29 -0700273 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700274 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
275 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
276 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700277
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700278 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
279 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
280 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700281 flags.PrintDefaults()
282 }
283 abs := flags.Bool("abs", false, "Print the absolute path of the value")
284 flags.Parse(args)
285
286 if flags.NArg() != 1 {
287 flags.Usage()
288 os.Exit(1)
289 }
290
291 varName := flags.Arg(0)
292 if varName == "report_config" {
293 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
294 if err != nil {
295 ctx.Fatal(err)
296 }
297
298 fmt.Println(build.Banner(varData))
299 } else {
300 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
301 if err != nil {
302 ctx.Fatal(err)
303 }
304
305 if *abs {
306 var res []string
307 for _, path := range strings.Fields(varData[varName]) {
308 if abs, err := filepath.Abs(path); err == nil {
309 res = append(res, abs)
310 } else {
311 ctx.Fatalln("Failed to get absolute path of", path, err)
312 }
313 }
314 fmt.Println(strings.Join(res, " "))
315 } else {
316 fmt.Println(varData[varName])
317 }
318 }
319}
320
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700321func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700322 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400323 flags.SetOutput(ctx.Writer)
324
Dan Willemsen051133b2017-07-14 11:29:29 -0700325 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700326 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
327 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
328 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
329 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
330 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700331
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700332 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
333 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
334 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700335 flags.PrintDefaults()
336 }
337
338 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
339 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
340
341 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
342 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
343
344 flags.Parse(args)
345
346 if flags.NArg() != 0 {
347 flags.Usage()
348 os.Exit(1)
349 }
350
351 vars := strings.Fields(*varsStr)
352 absVars := strings.Fields(*absVarsStr)
353
354 allVars := append([]string{}, vars...)
355 allVars = append(allVars, absVars...)
356
357 if i := indexList("report_config", allVars); i != -1 {
358 allVars = append(allVars[:i], allVars[i+1:]...)
359 allVars = append(allVars, build.BannerVars...)
360 }
361
362 if len(allVars) == 0 {
363 return
364 }
365
366 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
367 if err != nil {
368 ctx.Fatal(err)
369 }
370
371 for _, name := range vars {
372 if name == "report_config" {
373 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
374 } else {
375 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
376 }
377 }
378 for _, name := range absVars {
379 var res []string
380 for _, path := range strings.Fields(varData[name]) {
381 abs, err := filepath.Abs(path)
382 if err != nil {
383 ctx.Fatalln("Failed to get absolute path of", path, err)
384 }
385 res = append(res, abs)
386 }
387 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
388 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700389}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700390
Patrice Arrudab7b22822019-05-21 17:46:23 -0700391func stdio() terminal.StdioInterface {
392 return terminal.StdioImpl{}
393}
394
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800395// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
396// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700397func customStdio() terminal.StdioInterface {
398 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
399}
400
401// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
402func dumpVarConfig(ctx build.Context, args ...string) build.Config {
403 return build.NewConfig(ctx)
404}
405
Patrice Arrudab7b22822019-05-21 17:46:23 -0700406func buildActionConfig(ctx build.Context, args ...string) build.Config {
407 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
Usta Shrestha675564d2022-08-09 18:03:23 -0400408 flags.SetOutput(ctx.Writer)
409
Patrice Arrudab7b22822019-05-21 17:46:23 -0700410 flags.Usage = func() {
411 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
412 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
413 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
414 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
415 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
416 fmt.Fprintln(ctx.Writer, "")
417 flags.PrintDefaults()
418 }
419
420 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700421 name string
422 description string
423 action build.BuildAction
424 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700425 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700426 name: "all-modules",
427 description: "Build action: build from the top of the source tree.",
428 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700429 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700430 // This is redirecting to mma build command behaviour. Once it has soaked for a
431 // while, the build command is deleted from here once it has been removed from the
432 // envsetup.sh.
433 name: "modules-in-a-dir-no-deps",
434 description: "Build action: builds all of the modules in the current directory without their dependencies.",
435 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700436 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700437 // This is redirecting to mmma build command behaviour. Once it has soaked for a
438 // while, the build command is deleted from here once it has been removed from the
439 // envsetup.sh.
440 name: "modules-in-dirs-no-deps",
441 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
442 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700443 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700444 name: "modules-in-a-dir",
445 description: "Build action: builds all of the modules in the current directory and their dependencies.",
446 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700447 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700448 name: "modules-in-dirs",
449 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
450 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700451 }}
452 for i, flag := range buildActionFlags {
453 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
454 }
455 dir := flags.String("dir", "", "Directory of the executed build command.")
456
457 // Only interested in the first two args which defines the build action and the directory.
458 // The remaining arguments are passed down to the config.
459 const numBuildActionFlags = 2
460 if len(args) < numBuildActionFlags {
461 flags.Usage()
Usta Shrestha675564d2022-08-09 18:03:23 -0400462 ctx.Fatalln("Improper build action arguments: too few arguments")
Patrice Arrudab7b22822019-05-21 17:46:23 -0700463 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400464 parseError := flags.Parse(args[0:numBuildActionFlags])
Patrice Arrudab7b22822019-05-21 17:46:23 -0700465
466 // The next block of code is to validate that exactly one build action is set and the dir flag
467 // is specified.
Usta Shrestha675564d2022-08-09 18:03:23 -0400468 buildActionFound := false
Patrice Arrudab7b22822019-05-21 17:46:23 -0700469 var buildAction build.BuildAction
Usta Shrestha675564d2022-08-09 18:03:23 -0400470 for _, f := range buildActionFlags {
471 if f.set {
472 if buildActionFound {
473 if parseError == nil {
474 //otherwise Parse() already called Usage()
475 flags.Usage()
476 }
477 ctx.Fatalf("Build action already specified, omit: --%s\n", f.name)
478 }
479 buildActionFound = true
480 buildAction = f.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700481 }
482 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400483 if !buildActionFound {
484 if parseError == nil {
485 //otherwise Parse() already called Usage()
486 flags.Usage()
487 }
Patrice Arrudab7b22822019-05-21 17:46:23 -0700488 ctx.Fatalln("Build action not defined.")
489 }
490 if *dir == "" {
491 ctx.Fatalln("-dir not specified.")
492 }
493
494 // Remove the build action flags from the args as they are not recognized by the config.
495 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700496 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700497}
498
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100499func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700500 if config.IsVerbose() {
501 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700502 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
503 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
504 fmt.Fprintln(writer, "!")
505 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
506 fmt.Fprintln(writer, "!")
507 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
508 fmt.Fprintln(writer, "")
Usta Shrestha96ff7222022-08-09 17:41:15 -0400509 ctx.Fatal("done")
Dan Willemsenc6360832019-07-25 14:07:36 -0700510 }
511
512 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
513 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700514 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700515 fmt.Fprintln(writer, "!")
516 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
517 fmt.Fprintln(writer, "!")
518 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
519 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700520 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700521 }
522
Anton Hansson5a7861a2021-06-04 10:09:01 +0100523 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700524}
525
526// getCommand finds the appropriate command based on args[1] flag. args[0]
527// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700528func getCommand(args []string) (*command, []string, error) {
Usta Shrestha675564d2022-08-09 18:03:23 -0400529 listFlags := func() []string {
530 flags := make([]string, len(commands))
531 for i, c := range commands {
532 flags[i] = c.flag
533 }
534 return flags
535 }
536
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700537 if len(args) < 2 {
Usta Shrestha675564d2022-08-09 18:03:23 -0400538 return nil, nil, fmt.Errorf("Too few arguments: %q\nUse one of these: %q", args, listFlags())
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700539 }
540
541 for _, c := range commands {
542 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700543 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700544 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700545 }
Usta Shrestha675564d2022-08-09 18:03:23 -0400546 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 -0700547}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000548
549// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
550func populateExternalDistDir(ctx build.Context, config build.Config) {
551 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
552 var err error
553 var internalDistDirPath string
554 var externalDistDirPath string
555 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
556 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
557 }
558 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
559 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
560 }
561 if externalDistDirPath == internalDistDirPath {
562 return
563 }
564
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000565 // Make sure the internal DIST_DIR actually exists before trying to read from it
566 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
567 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
568 return
569 }
570
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000571 // Make sure the external DIST_DIR actually exists before trying to write to it
572 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
573 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
574 }
575
576 ctx.Println("Populating external DIST_DIR...")
577
578 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
579}
580
581func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
582 files, err := ioutil.ReadDir(internalDistDirPath)
583 if err != nil {
584 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
585 }
586 for _, f := range files {
587 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
588 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
589
590 if f.IsDir() {
591 // Moving a directory - check if there is an existing directory to merge with
592 externalLstat, err := os.Lstat(externalFilePath)
593 if err != nil {
594 if !os.IsNotExist(err) {
595 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
596 }
597 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
598 } else {
599 if externalLstat.IsDir() {
600 // Existing dir - try to merge the directories?
601 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
602 continue
603 } else {
604 // Existing file being replaced with a directory. Delete the existing file...
605 if err := os.RemoveAll(externalFilePath); err != nil {
606 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
607 }
608 }
609 }
610 } else {
611 // Moving a file (not a dir) - delete any existing file or directory
612 if err := os.RemoveAll(externalFilePath); err != nil {
613 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
614 }
615 }
616
617 // The actual move - do a rename instead of a copy in order to save disk space.
618 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
619 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
620 }
621 }
622}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500623
624func setMaxFiles(ctx build.Context) {
625 var limits syscall.Rlimit
626
627 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
628 if err != nil {
629 ctx.Println("Failed to get file limit:", err)
630 return
631 }
632
633 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
634 if limits.Cur == limits.Max {
635 return
636 }
637
638 limits.Cur = limits.Max
639 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
640 if err != nil {
641 ctx.Println("Failed to increase file limit:", err)
642 }
643}