blob: 16f994d5556adfad8d00011c094e0285627be6ac [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)",
69 config: func(ctx build.Context, args ...string) build.Config {
70 return build.NewConfig(ctx, args...)
71 },
Patrice Arrudab7b22822019-05-21 17:46:23 -070072 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010073 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070074 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000075 flag: "--dumpvar-mode",
76 description: "print the value of the legacy make variable VAR to stdout",
77 simpleOutput: true,
78 logsPrefix: "dumpvars-",
79 config: dumpVarConfig,
80 stdio: customStdio,
81 run: dumpVar,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070082 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000083 flag: "--dumpvars-mode",
84 description: "dump the values of one or more legacy make variables, in shell syntax",
85 simpleOutput: true,
86 logsPrefix: "dumpvars-",
87 config: dumpVarConfig,
88 stdio: customStdio,
89 run: dumpVars,
Patrice Arrudab7b22822019-05-21 17:46:23 -070090 }, {
91 flag: "--build-mode",
92 description: "build modules based on the specified build action",
93 config: buildActionConfig,
94 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010095 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070096 },
97}
98
99// indexList returns the index of first found s. -1 is return if s is not
100// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700101func indexList(s string, list []string) int {
102 for i, l := range list {
103 if l == s {
104 return i
105 }
106 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700107 return -1
108}
109
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700110// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700111func inList(s string, list []string) bool {
112 return indexList(s, list) != -1
113}
114
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700115// Main execution of soong_ui. The command format is as follows:
116//
117// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
118//
119// Command is the type of soong_ui execution. Only one type of
120// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700121func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100122 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
123
Patrice Arruda73c790f2020-07-13 23:01:18 +0000124 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000125
Liz Kammer0e7993e2020-10-15 11:07:13 -0700126 c, args, err := getCommand(os.Args)
127 if err != nil {
128 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700129 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700130 }
131
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800132 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000133 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800134 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
135 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700136
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800137 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700138 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700139 defer log.Cleanup()
140
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800141 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700142 ctx, cancel := context.WithCancel(context.Background())
143 defer cancel()
144
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800145 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700146 trace := tracer.New(log)
147 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700148
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800149 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800150 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000151 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700152 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800153
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800154 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700155 stat := &status.Status{}
156 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800157 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700158 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700159 stat.AddOutput(trace.StatusTracer())
160
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800161 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200162 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700163 trace.Close()
164 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700165 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700166 })
167
Dan Willemsen59339a22018-07-22 21:18:45 -0700168 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700169 Context: ctx,
170 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800171 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700172 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700173 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700174 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700175 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700176
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500177 config := c.config(buildCtx, args...)
178
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700179 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800180
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000181 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000182 defer populateExternalDistDir(buildCtx, config)
183 }
184
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800185 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000186 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700187
Patrice Arruda40564022020-12-10 00:42:58 +0000188 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000189 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
190 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
191 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000192
Kousik Kumara0a44a82020-10-08 02:33:29 -0400193 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000194
Dan Willemsenb82471a2018-05-17 16:37:09 -0700195 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700196 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
197 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
198 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
199 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000200 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700201 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700202 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700203
Colin Cross8b8bec32019-11-15 13:18:43 -0800204 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
205 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
206 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
207
Liz Kammer4ae119c2022-02-09 10:54:05 -0500208 setMaxFiles(buildCtx)
Liz Kammera7541782022-02-07 13:38:52 -0500209
210 {
Patrice Arruda40564022020-12-10 00:42:58 +0000211 // The order of the function calls is important. The last defer function call
212 // is the first one that is executed to save the rbe metrics to a protobuf
213 // file. The soong metrics file is then next. Bazel profiles are written
214 // before the uploadMetrics is invoked. The written files are then uploaded
215 // if the uploading of the metrics is enabled.
216 files := []string{
217 buildErrorFile, // build error strings
218 rbeMetricsFile, // high level metrics related to remote build execution.
219 soongMetricsFile, // high level metrics related to this build system.
220 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
221 }
222 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
223 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)
273 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)
323 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700324 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
325 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
326 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
327 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
328 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700329
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700330 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
331 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
332 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700333 flags.PrintDefaults()
334 }
335
336 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
337 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
338
339 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
340 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
341
342 flags.Parse(args)
343
344 if flags.NArg() != 0 {
345 flags.Usage()
346 os.Exit(1)
347 }
348
349 vars := strings.Fields(*varsStr)
350 absVars := strings.Fields(*absVarsStr)
351
352 allVars := append([]string{}, vars...)
353 allVars = append(allVars, absVars...)
354
355 if i := indexList("report_config", allVars); i != -1 {
356 allVars = append(allVars[:i], allVars[i+1:]...)
357 allVars = append(allVars, build.BannerVars...)
358 }
359
360 if len(allVars) == 0 {
361 return
362 }
363
364 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
365 if err != nil {
366 ctx.Fatal(err)
367 }
368
369 for _, name := range vars {
370 if name == "report_config" {
371 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
372 } else {
373 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
374 }
375 }
376 for _, name := range absVars {
377 var res []string
378 for _, path := range strings.Fields(varData[name]) {
379 abs, err := filepath.Abs(path)
380 if err != nil {
381 ctx.Fatalln("Failed to get absolute path of", path, err)
382 }
383 res = append(res, abs)
384 }
385 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
386 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700387}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700388
Patrice Arrudab7b22822019-05-21 17:46:23 -0700389func stdio() terminal.StdioInterface {
390 return terminal.StdioImpl{}
391}
392
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800393// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
394// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700395func customStdio() terminal.StdioInterface {
396 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
397}
398
399// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
400func dumpVarConfig(ctx build.Context, args ...string) build.Config {
401 return build.NewConfig(ctx)
402}
403
Patrice Arrudab7b22822019-05-21 17:46:23 -0700404func buildActionConfig(ctx build.Context, args ...string) build.Config {
405 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
406 flags.Usage = func() {
407 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
408 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
409 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
410 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
411 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
412 fmt.Fprintln(ctx.Writer, "")
413 flags.PrintDefaults()
414 }
415
416 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700417 name string
418 description string
419 action build.BuildAction
420 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700421 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700422 name: "all-modules",
423 description: "Build action: build from the top of the source tree.",
424 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700425 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700426 // This is redirecting to mma build command behaviour. Once it has soaked for a
427 // while, the build command is deleted from here once it has been removed from the
428 // envsetup.sh.
429 name: "modules-in-a-dir-no-deps",
430 description: "Build action: builds all of the modules in the current directory without their dependencies.",
431 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700432 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700433 // This is redirecting to mmma build command behaviour. Once it has soaked for a
434 // while, the build command is deleted from here once it has been removed from the
435 // envsetup.sh.
436 name: "modules-in-dirs-no-deps",
437 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
438 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700439 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700440 name: "modules-in-a-dir",
441 description: "Build action: builds all of the modules in the current directory and their dependencies.",
442 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700443 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700444 name: "modules-in-dirs",
445 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
446 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700447 }}
448 for i, flag := range buildActionFlags {
449 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
450 }
451 dir := flags.String("dir", "", "Directory of the executed build command.")
452
453 // Only interested in the first two args which defines the build action and the directory.
454 // The remaining arguments are passed down to the config.
455 const numBuildActionFlags = 2
456 if len(args) < numBuildActionFlags {
457 flags.Usage()
458 ctx.Fatalln("Improper build action arguments.")
459 }
460 flags.Parse(args[0:numBuildActionFlags])
461
462 // The next block of code is to validate that exactly one build action is set and the dir flag
463 // is specified.
464 buildActionCount := 0
465 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700466 for _, flag := range buildActionFlags {
467 if flag.set {
468 buildActionCount++
469 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700470 }
471 }
472 if buildActionCount != 1 {
473 ctx.Fatalln("Build action not defined.")
474 }
475 if *dir == "" {
476 ctx.Fatalln("-dir not specified.")
477 }
478
479 // Remove the build action flags from the args as they are not recognized by the config.
480 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700481 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700482}
483
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100484func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700485 if config.IsVerbose() {
486 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700487 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
488 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
489 fmt.Fprintln(writer, "!")
490 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
491 fmt.Fprintln(writer, "!")
492 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
493 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700494 select {
495 case <-time.After(5 * time.Second):
496 case <-ctx.Done():
497 return
498 }
499 }
500
501 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
502 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700503 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700504 fmt.Fprintln(writer, "!")
505 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
506 fmt.Fprintln(writer, "!")
507 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
508 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700509 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700510 }
511
Anton Hansson5a7861a2021-06-04 10:09:01 +0100512 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700513}
514
515// getCommand finds the appropriate command based on args[1] flag. args[0]
516// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700517func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700518 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700519 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700520 }
521
522 for _, c := range commands {
523 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700524 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700525 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700526 }
527
528 // command not found
Usta6feae382021-12-13 12:31:50 -0500529 flags := make([]string, len(commands))
530 for i, c := range commands {
531 flags[i] = c.flag
532 }
533 return nil, nil, fmt.Errorf("Command not found: %q\nDid you mean one of these: %q", args, flags)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700534}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000535
536// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
537func populateExternalDistDir(ctx build.Context, config build.Config) {
538 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
539 var err error
540 var internalDistDirPath string
541 var externalDistDirPath string
542 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
543 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
544 }
545 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
546 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
547 }
548 if externalDistDirPath == internalDistDirPath {
549 return
550 }
551
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000552 // Make sure the internal DIST_DIR actually exists before trying to read from it
553 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
554 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
555 return
556 }
557
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000558 // Make sure the external DIST_DIR actually exists before trying to write to it
559 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
560 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
561 }
562
563 ctx.Println("Populating external DIST_DIR...")
564
565 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
566}
567
568func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
569 files, err := ioutil.ReadDir(internalDistDirPath)
570 if err != nil {
571 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
572 }
573 for _, f := range files {
574 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
575 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
576
577 if f.IsDir() {
578 // Moving a directory - check if there is an existing directory to merge with
579 externalLstat, err := os.Lstat(externalFilePath)
580 if err != nil {
581 if !os.IsNotExist(err) {
582 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
583 }
584 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
585 } else {
586 if externalLstat.IsDir() {
587 // Existing dir - try to merge the directories?
588 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
589 continue
590 } else {
591 // Existing file being replaced with a directory. Delete the existing file...
592 if err := os.RemoveAll(externalFilePath); err != nil {
593 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
594 }
595 }
596 }
597 } else {
598 // Moving a file (not a dir) - delete any existing file or directory
599 if err := os.RemoveAll(externalFilePath); err != nil {
600 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
601 }
602 }
603
604 // The actual move - do a rename instead of a copy in order to save disk space.
605 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
606 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
607 }
608 }
609}
Liz Kammer4ae119c2022-02-09 10:54:05 -0500610
611func setMaxFiles(ctx build.Context) {
612 var limits syscall.Rlimit
613
614 err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limits)
615 if err != nil {
616 ctx.Println("Failed to get file limit:", err)
617 return
618 }
619
620 ctx.Verbosef("Current file limits: %d soft, %d hard", limits.Cur, limits.Max)
621 if limits.Cur == limits.Max {
622 return
623 }
624
625 limits.Cur = limits.Max
626 err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &limits)
627 if err != nil {
628 ctx.Println("Failed to increase file limit:", err)
629 }
630}