blob: 22922c0e55fc1b9d042364eefa3ae01c1271adc7 [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"
Dan Willemsenb82471a2018-05-17 16:37:09 -070032 "android/soong/ui/status"
33 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070034 "android/soong/ui/tracer"
Dan Willemsen1e704462016-08-21 15:17:17 -070035)
36
Patrice Arrudaa5c25422019-04-09 18:49:49 -070037// A command represents an operation to be executed in the soong build
38// system.
39type command struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000040 // The flag name (must have double dashes).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070041 flag string
42
Patrice Arrudaf445ba12020-07-28 17:49:01 +000043 // Description for the flag (to display when running help).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070044 description string
45
Patrice Arrudaf445ba12020-07-28 17:49:01 +000046 // Stream the build status output into the simple terminal mode.
47 simpleOutput bool
Colin Crossc0b9f6b2019-09-23 12:44:54 -070048
49 // Sets a prefix string to use for filenames of log files.
50 logsPrefix string
51
Patrice Arrudaa5c25422019-04-09 18:49:49 -070052 // Creates the build configuration based on the args and build context.
53 config func(ctx build.Context, args ...string) build.Config
54
55 // Returns what type of IO redirection this Command requires.
56 stdio func() terminal.StdioInterface
57
58 // run the command
59 run func(ctx build.Context, config build.Config, args []string, logsDir string)
60}
61
Patrice Arrudaa5c25422019-04-09 18:49:49 -070062// list of supported commands (flags) supported by soong ui
63var commands []command = []command{
64 {
Anton Hansson5a7861a2021-06-04 10:09:01 +010065 flag: "--make-mode",
Patrice Arrudaa5c25422019-04-09 18:49:49 -070066 description: "build the modules by the target name (i.e. soong_docs)",
67 config: func(ctx build.Context, args ...string) build.Config {
68 return build.NewConfig(ctx, args...)
69 },
Patrice Arrudab7b22822019-05-21 17:46:23 -070070 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010071 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//
115// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
116//
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 Crosse0df1a32019-06-09 19:40:08 -0700132 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700133
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800134 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700135 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700136 defer log.Cleanup()
137
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800138 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700139 ctx, cancel := context.WithCancel(context.Background())
140 defer cancel()
141
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800142 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700143 trace := tracer.New(log)
144 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700145
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800146 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800147 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000148 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700149 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800150
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800151 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700152 stat := &status.Status{}
153 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800154 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700155 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700156 stat.AddOutput(trace.StatusTracer())
157
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800158 // Set up a cleanup procedure in case the normal termination process doesn't work.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700159 build.SetupSignals(log, cancel, func() {
160 trace.Close()
161 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700162 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700163 })
164
Dan Willemsen59339a22018-07-22 21:18:45 -0700165 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700166 Context: ctx,
167 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800168 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700169 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700170 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700171 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700172 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700173
174 config := c.config(buildCtx, args...)
Dan Willemsen1e704462016-08-21 15:17:17 -0700175
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700176 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800177
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000178 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000179 defer populateExternalDistDir(buildCtx, config)
180 }
181
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800182 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000183 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700184
Patrice Arruda40564022020-12-10 00:42:58 +0000185 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000186 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
187 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
188 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000189
Kousik Kumara0a44a82020-10-08 02:33:29 -0400190 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000191
Dan Willemsenb82471a2018-05-17 16:37:09 -0700192 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700193 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
194 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
195 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
196 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000197 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700198 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700199 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700200
Colin Cross8b8bec32019-11-15 13:18:43 -0800201 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
202 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
203 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
204
Patrice Arruda40564022020-12-10 00:42:58 +0000205 {
206 // The order of the function calls is important. The last defer function call
207 // is the first one that is executed to save the rbe metrics to a protobuf
208 // file. The soong metrics file is then next. Bazel profiles are written
209 // before the uploadMetrics is invoked. The written files are then uploaded
210 // if the uploading of the metrics is enabled.
211 files := []string{
212 buildErrorFile, // build error strings
213 rbeMetricsFile, // high level metrics related to remote build execution.
214 soongMetricsFile, // high level metrics related to this build system.
215 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
216 }
217 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
218 defer met.Dump(soongMetricsFile)
219 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
220 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800221
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800222 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700223 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800224 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
225 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700226 if !strings.HasSuffix(start, "N") {
227 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
228 log.Verbosef("Took %dms to start up.",
229 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800230 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700231 }
232 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700233
234 if executable, err := os.Executable(); err == nil {
235 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
236 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700237 }
238
Dan Willemsen6b783c82019-03-08 11:42:28 -0800239 // Fix up the source tree due to a repo bug where it doesn't remove
240 // linkfiles that have been removed
241 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
242 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
243
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800244 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700245 f := build.NewSourceFinder(buildCtx, config)
246 defer f.Shutdown()
247 build.FindSources(buildCtx, config, f)
248
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700249 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700250}
251
Dan Willemsen6b783c82019-03-08 11:42:28 -0800252func fixBadDanglingLink(ctx build.Context, name string) {
253 _, err := os.Lstat(name)
254 if err != nil {
255 return
256 }
257 _, err = os.Stat(name)
258 if os.IsNotExist(err) {
259 err = os.Remove(name)
260 if err != nil {
261 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
262 }
263 }
264}
265
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700266func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700267 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
268 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700269 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
270 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
271 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700272
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700273 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
274 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
275 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700276 flags.PrintDefaults()
277 }
278 abs := flags.Bool("abs", false, "Print the absolute path of the value")
279 flags.Parse(args)
280
281 if flags.NArg() != 1 {
282 flags.Usage()
283 os.Exit(1)
284 }
285
286 varName := flags.Arg(0)
287 if varName == "report_config" {
288 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
289 if err != nil {
290 ctx.Fatal(err)
291 }
292
293 fmt.Println(build.Banner(varData))
294 } else {
295 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
296 if err != nil {
297 ctx.Fatal(err)
298 }
299
300 if *abs {
301 var res []string
302 for _, path := range strings.Fields(varData[varName]) {
303 if abs, err := filepath.Abs(path); err == nil {
304 res = append(res, abs)
305 } else {
306 ctx.Fatalln("Failed to get absolute path of", path, err)
307 }
308 }
309 fmt.Println(strings.Join(res, " "))
310 } else {
311 fmt.Println(varData[varName])
312 }
313 }
314}
315
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700316func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700317 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
318 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700319 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
320 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
321 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
322 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
323 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700324
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700325 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
326 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
327 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700328 flags.PrintDefaults()
329 }
330
331 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
332 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
333
334 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
335 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
336
337 flags.Parse(args)
338
339 if flags.NArg() != 0 {
340 flags.Usage()
341 os.Exit(1)
342 }
343
344 vars := strings.Fields(*varsStr)
345 absVars := strings.Fields(*absVarsStr)
346
347 allVars := append([]string{}, vars...)
348 allVars = append(allVars, absVars...)
349
350 if i := indexList("report_config", allVars); i != -1 {
351 allVars = append(allVars[:i], allVars[i+1:]...)
352 allVars = append(allVars, build.BannerVars...)
353 }
354
355 if len(allVars) == 0 {
356 return
357 }
358
359 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
360 if err != nil {
361 ctx.Fatal(err)
362 }
363
364 for _, name := range vars {
365 if name == "report_config" {
366 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
367 } else {
368 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
369 }
370 }
371 for _, name := range absVars {
372 var res []string
373 for _, path := range strings.Fields(varData[name]) {
374 abs, err := filepath.Abs(path)
375 if err != nil {
376 ctx.Fatalln("Failed to get absolute path of", path, err)
377 }
378 res = append(res, abs)
379 }
380 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
381 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700382}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700383
Patrice Arrudab7b22822019-05-21 17:46:23 -0700384func stdio() terminal.StdioInterface {
385 return terminal.StdioImpl{}
386}
387
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800388// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
389// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700390func customStdio() terminal.StdioInterface {
391 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
392}
393
394// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
395func dumpVarConfig(ctx build.Context, args ...string) build.Config {
396 return build.NewConfig(ctx)
397}
398
Patrice Arrudab7b22822019-05-21 17:46:23 -0700399func buildActionConfig(ctx build.Context, args ...string) build.Config {
400 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
401 flags.Usage = func() {
402 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
403 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
404 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
405 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
406 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
407 fmt.Fprintln(ctx.Writer, "")
408 flags.PrintDefaults()
409 }
410
411 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700412 name string
413 description string
414 action build.BuildAction
415 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700416 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700417 name: "all-modules",
418 description: "Build action: build from the top of the source tree.",
419 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700420 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700421 // This is redirecting to mma build command behaviour. Once it has soaked for a
422 // while, the build command is deleted from here once it has been removed from the
423 // envsetup.sh.
424 name: "modules-in-a-dir-no-deps",
425 description: "Build action: builds all of the modules in the current directory without their dependencies.",
426 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700427 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700428 // This is redirecting to mmma build command behaviour. Once it has soaked for a
429 // while, the build command is deleted from here once it has been removed from the
430 // envsetup.sh.
431 name: "modules-in-dirs-no-deps",
432 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
433 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700434 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700435 name: "modules-in-a-dir",
436 description: "Build action: builds all of the modules in the current directory and their dependencies.",
437 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700438 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700439 name: "modules-in-dirs",
440 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
441 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700442 }}
443 for i, flag := range buildActionFlags {
444 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
445 }
446 dir := flags.String("dir", "", "Directory of the executed build command.")
447
448 // Only interested in the first two args which defines the build action and the directory.
449 // The remaining arguments are passed down to the config.
450 const numBuildActionFlags = 2
451 if len(args) < numBuildActionFlags {
452 flags.Usage()
453 ctx.Fatalln("Improper build action arguments.")
454 }
455 flags.Parse(args[0:numBuildActionFlags])
456
457 // The next block of code is to validate that exactly one build action is set and the dir flag
458 // is specified.
459 buildActionCount := 0
460 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700461 for _, flag := range buildActionFlags {
462 if flag.set {
463 buildActionCount++
464 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700465 }
466 }
467 if buildActionCount != 1 {
468 ctx.Fatalln("Build action not defined.")
469 }
470 if *dir == "" {
471 ctx.Fatalln("-dir not specified.")
472 }
473
474 // Remove the build action flags from the args as they are not recognized by the config.
475 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700476 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700477}
478
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100479func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700480 if config.IsVerbose() {
481 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700482 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
483 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
484 fmt.Fprintln(writer, "!")
485 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
486 fmt.Fprintln(writer, "!")
487 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
488 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700489 select {
490 case <-time.After(5 * time.Second):
491 case <-ctx.Done():
492 return
493 }
494 }
495
496 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
497 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700498 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700499 fmt.Fprintln(writer, "!")
500 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
501 fmt.Fprintln(writer, "!")
502 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
503 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700504 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700505 }
506
Anton Hansson5a7861a2021-06-04 10:09:01 +0100507 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700508}
509
510// getCommand finds the appropriate command based on args[1] flag. args[0]
511// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700512func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700513 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700514 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700515 }
516
517 for _, c := range commands {
518 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700519 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700520 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700521 }
522
523 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700524 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700525}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000526
527// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
528func populateExternalDistDir(ctx build.Context, config build.Config) {
529 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
530 var err error
531 var internalDistDirPath string
532 var externalDistDirPath string
533 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
534 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
535 }
536 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
537 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
538 }
539 if externalDistDirPath == internalDistDirPath {
540 return
541 }
542
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000543 // Make sure the internal DIST_DIR actually exists before trying to read from it
544 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
545 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
546 return
547 }
548
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000549 // Make sure the external DIST_DIR actually exists before trying to write to it
550 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
551 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
552 }
553
554 ctx.Println("Populating external DIST_DIR...")
555
556 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
557}
558
559func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
560 files, err := ioutil.ReadDir(internalDistDirPath)
561 if err != nil {
562 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
563 }
564 for _, f := range files {
565 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
566 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
567
568 if f.IsDir() {
569 // Moving a directory - check if there is an existing directory to merge with
570 externalLstat, err := os.Lstat(externalFilePath)
571 if err != nil {
572 if !os.IsNotExist(err) {
573 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
574 }
575 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
576 } else {
577 if externalLstat.IsDir() {
578 // Existing dir - try to merge the directories?
579 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
580 continue
581 } else {
582 // Existing file being replaced with a directory. Delete the existing file...
583 if err := os.RemoveAll(externalFilePath); err != nil {
584 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
585 }
586 }
587 }
588 } else {
589 // Moving a file (not a dir) - delete any existing file or directory
590 if err := os.RemoveAll(externalFilePath); err != nil {
591 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
592 }
593 }
594
595 // The actual move - do a rename instead of a copy in order to save disk space.
596 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
597 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
598 }
599 }
600}