blob: 13b20f42f5b7347c0ce3c210156a02ee59d7851a [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"
26 "time"
27
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +010028 "android/soong/shared"
Dan Willemsen1e704462016-08-21 15:17:17 -070029 "android/soong/ui/build"
30 "android/soong/ui/logger"
Nan Zhang17f27672018-12-12 16:01:49 -080031 "android/soong/ui/metrics"
Lukacs T. Berkif656b842021-08-11 11:10:28 +020032 "android/soong/ui/signal"
Dan Willemsenb82471a2018-05-17 16:37:09 -070033 "android/soong/ui/status"
34 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070035 "android/soong/ui/tracer"
Dan Willemsen1e704462016-08-21 15:17:17 -070036)
37
Patrice Arrudaa5c25422019-04-09 18:49:49 -070038// A command represents an operation to be executed in the soong build
39// system.
40type command struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000041 // The flag name (must have double dashes).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070042 flag string
43
Patrice Arrudaf445ba12020-07-28 17:49:01 +000044 // Description for the flag (to display when running help).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070045 description string
46
Patrice Arrudaf445ba12020-07-28 17:49:01 +000047 // Stream the build status output into the simple terminal mode.
48 simpleOutput bool
Colin Crossc0b9f6b2019-09-23 12:44:54 -070049
50 // Sets a prefix string to use for filenames of log files.
51 logsPrefix string
52
Patrice Arrudaa5c25422019-04-09 18:49:49 -070053 // Creates the build configuration based on the args and build context.
54 config func(ctx build.Context, args ...string) build.Config
55
56 // Returns what type of IO redirection this Command requires.
57 stdio func() terminal.StdioInterface
58
59 // run the command
60 run func(ctx build.Context, config build.Config, args []string, logsDir string)
61}
62
Patrice Arrudaa5c25422019-04-09 18:49:49 -070063// list of supported commands (flags) supported by soong ui
Usta6feae382021-12-13 12:31:50 -050064var commands = []command{
Patrice Arrudaa5c25422019-04-09 18:49:49 -070065 {
Anton Hansson5a7861a2021-06-04 10:09:01 +010066 flag: "--make-mode",
Patrice Arrudaa5c25422019-04-09 18:49:49 -070067 description: "build the modules by the target name (i.e. soong_docs)",
68 config: func(ctx build.Context, args ...string) build.Config {
69 return build.NewConfig(ctx, args...)
70 },
Patrice Arrudab7b22822019-05-21 17:46:23 -070071 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010072 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070073 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000074 flag: "--dumpvar-mode",
75 description: "print the value of the legacy make variable VAR to stdout",
76 simpleOutput: true,
77 logsPrefix: "dumpvars-",
78 config: dumpVarConfig,
79 stdio: customStdio,
80 run: dumpVar,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070081 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000082 flag: "--dumpvars-mode",
83 description: "dump the values of one or more legacy make variables, in shell syntax",
84 simpleOutput: true,
85 logsPrefix: "dumpvars-",
86 config: dumpVarConfig,
87 stdio: customStdio,
88 run: dumpVars,
Patrice Arrudab7b22822019-05-21 17:46:23 -070089 }, {
90 flag: "--build-mode",
91 description: "build modules based on the specified build action",
92 config: buildActionConfig,
93 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010094 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070095 },
96}
97
98// indexList returns the index of first found s. -1 is return if s is not
99// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700100func indexList(s string, list []string) int {
101 for i, l := range list {
102 if l == s {
103 return i
104 }
105 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700106 return -1
107}
108
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700109// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700110func inList(s string, list []string) bool {
111 return indexList(s, list) != -1
112}
113
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700114// Main execution of soong_ui. The command format is as follows:
115//
116// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
117//
118// Command is the type of soong_ui execution. Only one type of
119// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700120func main() {
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
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800136 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700137 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700138 defer log.Cleanup()
139
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800140 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700141 ctx, cancel := context.WithCancel(context.Background())
142 defer cancel()
143
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800144 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700145 trace := tracer.New(log)
146 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700147
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800148 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800149 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000150 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700151 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800152
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
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000180 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000181 defer populateExternalDistDir(buildCtx, config)
182 }
183
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800184 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000185 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700186
Patrice Arruda40564022020-12-10 00:42:58 +0000187 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000188 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
189 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
190 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000191
Kousik Kumara0a44a82020-10-08 02:33:29 -0400192 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000193
Dan Willemsenb82471a2018-05-17 16:37:09 -0700194 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700195 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
196 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
197 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
198 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000199 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700200 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700201 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700202
Colin Cross8b8bec32019-11-15 13:18:43 -0800203 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
204 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
205 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
206
Patrice Arruda40564022020-12-10 00:42:58 +0000207 {
208 // 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.
216 soongMetricsFile, // high level metrics related to this build system.
217 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
218 }
219 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
220 defer met.Dump(soongMetricsFile)
221 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
222 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800223
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800224 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700225 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800226 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
227 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700228 if !strings.HasSuffix(start, "N") {
229 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
230 log.Verbosef("Took %dms to start up.",
231 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800232 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700233 }
234 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700235
236 if executable, err := os.Executable(); err == nil {
237 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
238 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700239 }
240
Dan Willemsen6b783c82019-03-08 11:42:28 -0800241 // Fix up the source tree due to a repo bug where it doesn't remove
242 // linkfiles that have been removed
243 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
244 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
245
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800246 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700247 f := build.NewSourceFinder(buildCtx, config)
248 defer f.Shutdown()
249 build.FindSources(buildCtx, config, f)
250
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700251 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700252}
253
Dan Willemsen6b783c82019-03-08 11:42:28 -0800254func fixBadDanglingLink(ctx build.Context, name string) {
255 _, err := os.Lstat(name)
256 if err != nil {
257 return
258 }
259 _, err = os.Stat(name)
260 if os.IsNotExist(err) {
261 err = os.Remove(name)
262 if err != nil {
263 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
264 }
265 }
266}
267
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700268func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700269 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
270 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700271 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
272 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
273 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700274
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700275 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
276 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
277 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700278 flags.PrintDefaults()
279 }
280 abs := flags.Bool("abs", false, "Print the absolute path of the value")
281 flags.Parse(args)
282
283 if flags.NArg() != 1 {
284 flags.Usage()
285 os.Exit(1)
286 }
287
288 varName := flags.Arg(0)
289 if varName == "report_config" {
290 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
291 if err != nil {
292 ctx.Fatal(err)
293 }
294
295 fmt.Println(build.Banner(varData))
296 } else {
297 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
298 if err != nil {
299 ctx.Fatal(err)
300 }
301
302 if *abs {
303 var res []string
304 for _, path := range strings.Fields(varData[varName]) {
305 if abs, err := filepath.Abs(path); err == nil {
306 res = append(res, abs)
307 } else {
308 ctx.Fatalln("Failed to get absolute path of", path, err)
309 }
310 }
311 fmt.Println(strings.Join(res, " "))
312 } else {
313 fmt.Println(varData[varName])
314 }
315 }
316}
317
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700318func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700319 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
320 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700321 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
322 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
323 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
324 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
325 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700326
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700327 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
328 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
329 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700330 flags.PrintDefaults()
331 }
332
333 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
334 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
335
336 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
337 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
338
339 flags.Parse(args)
340
341 if flags.NArg() != 0 {
342 flags.Usage()
343 os.Exit(1)
344 }
345
346 vars := strings.Fields(*varsStr)
347 absVars := strings.Fields(*absVarsStr)
348
349 allVars := append([]string{}, vars...)
350 allVars = append(allVars, absVars...)
351
352 if i := indexList("report_config", allVars); i != -1 {
353 allVars = append(allVars[:i], allVars[i+1:]...)
354 allVars = append(allVars, build.BannerVars...)
355 }
356
357 if len(allVars) == 0 {
358 return
359 }
360
361 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
362 if err != nil {
363 ctx.Fatal(err)
364 }
365
366 for _, name := range vars {
367 if name == "report_config" {
368 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
369 } else {
370 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
371 }
372 }
373 for _, name := range absVars {
374 var res []string
375 for _, path := range strings.Fields(varData[name]) {
376 abs, err := filepath.Abs(path)
377 if err != nil {
378 ctx.Fatalln("Failed to get absolute path of", path, err)
379 }
380 res = append(res, abs)
381 }
382 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
383 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700384}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700385
Patrice Arrudab7b22822019-05-21 17:46:23 -0700386func stdio() terminal.StdioInterface {
387 return terminal.StdioImpl{}
388}
389
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800390// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
391// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700392func customStdio() terminal.StdioInterface {
393 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
394}
395
396// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
397func dumpVarConfig(ctx build.Context, args ...string) build.Config {
398 return build.NewConfig(ctx)
399}
400
Patrice Arrudab7b22822019-05-21 17:46:23 -0700401func buildActionConfig(ctx build.Context, args ...string) build.Config {
402 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
403 flags.Usage = func() {
404 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
405 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
406 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
407 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
408 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
409 fmt.Fprintln(ctx.Writer, "")
410 flags.PrintDefaults()
411 }
412
413 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700414 name string
415 description string
416 action build.BuildAction
417 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700418 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700419 name: "all-modules",
420 description: "Build action: build from the top of the source tree.",
421 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700422 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700423 // This is redirecting to mma build command behaviour. Once it has soaked for a
424 // while, the build command is deleted from here once it has been removed from the
425 // envsetup.sh.
426 name: "modules-in-a-dir-no-deps",
427 description: "Build action: builds all of the modules in the current directory without their dependencies.",
428 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700429 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700430 // This is redirecting to mmma 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-dirs-no-deps",
434 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
435 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700436 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700437 name: "modules-in-a-dir",
438 description: "Build action: builds all of the modules in the current directory and 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 name: "modules-in-dirs",
442 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
443 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700444 }}
445 for i, flag := range buildActionFlags {
446 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
447 }
448 dir := flags.String("dir", "", "Directory of the executed build command.")
449
450 // Only interested in the first two args which defines the build action and the directory.
451 // The remaining arguments are passed down to the config.
452 const numBuildActionFlags = 2
453 if len(args) < numBuildActionFlags {
454 flags.Usage()
455 ctx.Fatalln("Improper build action arguments.")
456 }
457 flags.Parse(args[0:numBuildActionFlags])
458
459 // The next block of code is to validate that exactly one build action is set and the dir flag
460 // is specified.
461 buildActionCount := 0
462 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700463 for _, flag := range buildActionFlags {
464 if flag.set {
465 buildActionCount++
466 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700467 }
468 }
469 if buildActionCount != 1 {
470 ctx.Fatalln("Build action not defined.")
471 }
472 if *dir == "" {
473 ctx.Fatalln("-dir not specified.")
474 }
475
476 // Remove the build action flags from the args as they are not recognized by the config.
477 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700478 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700479}
480
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100481func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700482 if config.IsVerbose() {
483 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700484 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
485 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
486 fmt.Fprintln(writer, "!")
487 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
488 fmt.Fprintln(writer, "!")
489 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
490 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700491 select {
492 case <-time.After(5 * time.Second):
493 case <-ctx.Done():
494 return
495 }
496 }
497
498 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
499 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700500 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700501 fmt.Fprintln(writer, "!")
502 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
503 fmt.Fprintln(writer, "!")
504 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
505 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700506 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700507 }
508
Anton Hansson5a7861a2021-06-04 10:09:01 +0100509 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700510}
511
512// getCommand finds the appropriate command based on args[1] flag. args[0]
513// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700514func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700515 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700516 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700517 }
518
519 for _, c := range commands {
520 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700521 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700522 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700523 }
524
525 // command not found
Usta6feae382021-12-13 12:31:50 -0500526 flags := make([]string, len(commands))
527 for i, c := range commands {
528 flags[i] = c.flag
529 }
530 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 -0700531}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000532
533// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
534func populateExternalDistDir(ctx build.Context, config build.Config) {
535 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
536 var err error
537 var internalDistDirPath string
538 var externalDistDirPath string
539 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
540 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
541 }
542 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
543 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
544 }
545 if externalDistDirPath == internalDistDirPath {
546 return
547 }
548
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000549 // Make sure the internal DIST_DIR actually exists before trying to read from it
550 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
551 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
552 return
553 }
554
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000555 // Make sure the external DIST_DIR actually exists before trying to write to it
556 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
557 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
558 }
559
560 ctx.Println("Populating external DIST_DIR...")
561
562 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
563}
564
565func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
566 files, err := ioutil.ReadDir(internalDistDirPath)
567 if err != nil {
568 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
569 }
570 for _, f := range files {
571 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
572 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
573
574 if f.IsDir() {
575 // Moving a directory - check if there is an existing directory to merge with
576 externalLstat, err := os.Lstat(externalFilePath)
577 if err != nil {
578 if !os.IsNotExist(err) {
579 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
580 }
581 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
582 } else {
583 if externalLstat.IsDir() {
584 // Existing dir - try to merge the directories?
585 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
586 continue
587 } else {
588 // Existing file being replaced with a directory. Delete the existing file...
589 if err := os.RemoveAll(externalFilePath); err != nil {
590 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
591 }
592 }
593 }
594 } else {
595 // Moving a file (not a dir) - delete any existing file or directory
596 if err := os.RemoveAll(externalFilePath); err != nil {
597 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
598 }
599 }
600
601 // The actual move - do a rename instead of a copy in order to save disk space.
602 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
603 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
604 }
605 }
606}