blob: 68480d47d9edcb67417aad199778e1ab930bf54e [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
28 "android/soong/ui/build"
29 "android/soong/ui/logger"
Nan Zhang17f27672018-12-12 16:01:49 -080030 "android/soong/ui/metrics"
Dan Willemsenb82471a2018-05-17 16:37:09 -070031 "android/soong/ui/status"
32 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070033 "android/soong/ui/tracer"
Dan Willemsen1e704462016-08-21 15:17:17 -070034)
35
Patrice Arrudaa5c25422019-04-09 18:49:49 -070036// A command represents an operation to be executed in the soong build
37// system.
38type command struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000039 // The flag name (must have double dashes).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070040 flag string
41
Patrice Arrudaf445ba12020-07-28 17:49:01 +000042 // Description for the flag (to display when running help).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070043 description string
44
Patrice Arrudaf445ba12020-07-28 17:49:01 +000045 // Stream the build status output into the simple terminal mode.
46 simpleOutput bool
Colin Crossc0b9f6b2019-09-23 12:44:54 -070047
48 // Sets a prefix string to use for filenames of log files.
49 logsPrefix string
50
Patrice Arrudaa5c25422019-04-09 18:49:49 -070051 // Creates the build configuration based on the args and build context.
52 config func(ctx build.Context, args ...string) build.Config
53
54 // Returns what type of IO redirection this Command requires.
55 stdio func() terminal.StdioInterface
56
57 // run the command
58 run func(ctx build.Context, config build.Config, args []string, logsDir string)
59}
60
61const makeModeFlagName = "--make-mode"
62
63// list of supported commands (flags) supported by soong ui
64var commands []command = []command{
65 {
66 flag: makeModeFlagName,
67 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,
72 run: make,
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,
94 run: make,
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() {
Patrice Arruda73c790f2020-07-13 23:01:18 +0000121 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000122
Liz Kammer0e7993e2020-10-15 11:07:13 -0700123 c, args, err := getCommand(os.Args)
124 if err != nil {
125 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700126 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700127 }
128
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800129 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000130 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Crosse0df1a32019-06-09 19:40:08 -0700131 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700132
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800133 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700134 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700135 defer log.Cleanup()
136
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800137 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700138 ctx, cancel := context.WithCancel(context.Background())
139 defer cancel()
140
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800141 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700142 trace := tracer.New(log)
143 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700144
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800145 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800146 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000147 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700148 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800149
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800150 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700151 stat := &status.Status{}
152 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800153 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700154 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700155 stat.AddOutput(trace.StatusTracer())
156
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800157 // Set up a cleanup procedure in case the normal termination process doesn't work.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700158 build.SetupSignals(log, cancel, func() {
159 trace.Close()
160 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700161 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700162 })
163
Dan Willemsen59339a22018-07-22 21:18:45 -0700164 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700165 Context: ctx,
166 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800167 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700168 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700169 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700170 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700171 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700172
173 config := c.config(buildCtx, args...)
Dan Willemsen1e704462016-08-21 15:17:17 -0700174
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700175 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800176
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000177 if config.UseBazel() {
178 defer populateExternalDistDir(buildCtx, config)
179 }
180
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800181 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000182 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700183
Patrice Arruda219eef32020-06-01 17:29:30 +0000184 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
185 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
186 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000187 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, buildErrorFile, rbeMetricsFile, soongMetricsFile)
Kousik Kumara0a44a82020-10-08 02:33:29 -0400188 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000189
Dan Willemsenb82471a2018-05-17 16:37:09 -0700190 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700191 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
192 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
193 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
194 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000195 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700196 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700197 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700198
Colin Cross8b8bec32019-11-15 13:18:43 -0800199 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
200 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
201 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
202
Patrice Arruda219eef32020-06-01 17:29:30 +0000203 defer met.Dump(soongMetricsFile)
Patrice Arruda62f1bf22020-07-07 12:48:26 +0000204 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
Nan Zhangd50f53b2019-01-07 20:26:51 -0800205
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800206 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700207 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800208 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
209 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700210 if !strings.HasSuffix(start, "N") {
211 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
212 log.Verbosef("Took %dms to start up.",
213 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800214 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700215 }
216 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700217
218 if executable, err := os.Executable(); err == nil {
219 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
220 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700221 }
222
Dan Willemsen6b783c82019-03-08 11:42:28 -0800223 // Fix up the source tree due to a repo bug where it doesn't remove
224 // linkfiles that have been removed
225 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
226 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
227
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800228 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700229 f := build.NewSourceFinder(buildCtx, config)
230 defer f.Shutdown()
231 build.FindSources(buildCtx, config, f)
232
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700233 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700234}
235
Dan Willemsen6b783c82019-03-08 11:42:28 -0800236func fixBadDanglingLink(ctx build.Context, name string) {
237 _, err := os.Lstat(name)
238 if err != nil {
239 return
240 }
241 _, err = os.Stat(name)
242 if os.IsNotExist(err) {
243 err = os.Remove(name)
244 if err != nil {
245 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
246 }
247 }
248}
249
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700250func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700251 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
252 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700253 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
254 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
255 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700256
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700257 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
258 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
259 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700260 flags.PrintDefaults()
261 }
262 abs := flags.Bool("abs", false, "Print the absolute path of the value")
263 flags.Parse(args)
264
265 if flags.NArg() != 1 {
266 flags.Usage()
267 os.Exit(1)
268 }
269
270 varName := flags.Arg(0)
271 if varName == "report_config" {
272 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
273 if err != nil {
274 ctx.Fatal(err)
275 }
276
277 fmt.Println(build.Banner(varData))
278 } else {
279 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
280 if err != nil {
281 ctx.Fatal(err)
282 }
283
284 if *abs {
285 var res []string
286 for _, path := range strings.Fields(varData[varName]) {
287 if abs, err := filepath.Abs(path); err == nil {
288 res = append(res, abs)
289 } else {
290 ctx.Fatalln("Failed to get absolute path of", path, err)
291 }
292 }
293 fmt.Println(strings.Join(res, " "))
294 } else {
295 fmt.Println(varData[varName])
296 }
297 }
298}
299
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700300func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700301 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
302 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700303 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
304 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
305 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
306 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
307 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700308
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700309 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
310 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
311 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700312 flags.PrintDefaults()
313 }
314
315 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
316 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
317
318 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
319 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
320
321 flags.Parse(args)
322
323 if flags.NArg() != 0 {
324 flags.Usage()
325 os.Exit(1)
326 }
327
328 vars := strings.Fields(*varsStr)
329 absVars := strings.Fields(*absVarsStr)
330
331 allVars := append([]string{}, vars...)
332 allVars = append(allVars, absVars...)
333
334 if i := indexList("report_config", allVars); i != -1 {
335 allVars = append(allVars[:i], allVars[i+1:]...)
336 allVars = append(allVars, build.BannerVars...)
337 }
338
339 if len(allVars) == 0 {
340 return
341 }
342
343 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
344 if err != nil {
345 ctx.Fatal(err)
346 }
347
348 for _, name := range vars {
349 if name == "report_config" {
350 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
351 } else {
352 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
353 }
354 }
355 for _, name := range absVars {
356 var res []string
357 for _, path := range strings.Fields(varData[name]) {
358 abs, err := filepath.Abs(path)
359 if err != nil {
360 ctx.Fatalln("Failed to get absolute path of", path, err)
361 }
362 res = append(res, abs)
363 }
364 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
365 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700366}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700367
Patrice Arrudab7b22822019-05-21 17:46:23 -0700368func stdio() terminal.StdioInterface {
369 return terminal.StdioImpl{}
370}
371
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800372// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
373// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700374func customStdio() terminal.StdioInterface {
375 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
376}
377
378// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
379func dumpVarConfig(ctx build.Context, args ...string) build.Config {
380 return build.NewConfig(ctx)
381}
382
Patrice Arrudab7b22822019-05-21 17:46:23 -0700383func buildActionConfig(ctx build.Context, args ...string) build.Config {
384 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
385 flags.Usage = func() {
386 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
387 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
388 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
389 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
390 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
391 fmt.Fprintln(ctx.Writer, "")
392 flags.PrintDefaults()
393 }
394
395 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700396 name string
397 description string
398 action build.BuildAction
399 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700400 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700401 name: "all-modules",
402 description: "Build action: build from the top of the source tree.",
403 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700404 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700405 // This is redirecting to mma build command behaviour. Once it has soaked for a
406 // while, the build command is deleted from here once it has been removed from the
407 // envsetup.sh.
408 name: "modules-in-a-dir-no-deps",
409 description: "Build action: builds all of the modules in the current directory without their dependencies.",
410 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700411 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700412 // This is redirecting to mmma build command behaviour. Once it has soaked for a
413 // while, the build command is deleted from here once it has been removed from the
414 // envsetup.sh.
415 name: "modules-in-dirs-no-deps",
416 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
417 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700418 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700419 name: "modules-in-a-dir",
420 description: "Build action: builds all of the modules in the current directory and their dependencies.",
421 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700422 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700423 name: "modules-in-dirs",
424 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
425 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700426 }}
427 for i, flag := range buildActionFlags {
428 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
429 }
430 dir := flags.String("dir", "", "Directory of the executed build command.")
431
432 // Only interested in the first two args which defines the build action and the directory.
433 // The remaining arguments are passed down to the config.
434 const numBuildActionFlags = 2
435 if len(args) < numBuildActionFlags {
436 flags.Usage()
437 ctx.Fatalln("Improper build action arguments.")
438 }
439 flags.Parse(args[0:numBuildActionFlags])
440
441 // The next block of code is to validate that exactly one build action is set and the dir flag
442 // is specified.
443 buildActionCount := 0
444 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700445 for _, flag := range buildActionFlags {
446 if flag.set {
447 buildActionCount++
448 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700449 }
450 }
451 if buildActionCount != 1 {
452 ctx.Fatalln("Build action not defined.")
453 }
454 if *dir == "" {
455 ctx.Fatalln("-dir not specified.")
456 }
457
458 // Remove the build action flags from the args as they are not recognized by the config.
459 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700460 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700461}
462
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700463func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
464 if config.IsVerbose() {
465 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700466 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
467 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
468 fmt.Fprintln(writer, "!")
469 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
470 fmt.Fprintln(writer, "!")
471 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
472 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700473 select {
474 case <-time.After(5 * time.Second):
475 case <-ctx.Done():
476 return
477 }
478 }
479
480 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
481 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700482 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700483 fmt.Fprintln(writer, "!")
484 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
485 fmt.Fprintln(writer, "!")
486 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
487 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700488 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700489 }
490
Patrice Arruda0c1c4562020-11-11 13:01:25 -0800491 toBuild := build.BuildAll
492 if config.UseBazel() {
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000493 toBuild = build.BuildAllWithBazel
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000494 }
495
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700496 if config.Checkbuild() {
497 toBuild |= build.RunBuildTests
498 }
499 build.Build(ctx, config, toBuild)
500}
501
502// getCommand finds the appropriate command based on args[1] flag. args[0]
503// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700504func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700505 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700506 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700507 }
508
509 for _, c := range commands {
510 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700511 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700512 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700513 }
514
515 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700516 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700517}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000518
519// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
520func populateExternalDistDir(ctx build.Context, config build.Config) {
521 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
522 var err error
523 var internalDistDirPath string
524 var externalDistDirPath string
525 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
526 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
527 }
528 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
529 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
530 }
531 if externalDistDirPath == internalDistDirPath {
532 return
533 }
534
535 // Make sure the external DIST_DIR actually exists before trying to write to it
536 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
537 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
538 }
539
540 ctx.Println("Populating external DIST_DIR...")
541
542 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
543}
544
545func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
546 files, err := ioutil.ReadDir(internalDistDirPath)
547 if err != nil {
548 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
549 }
550 for _, f := range files {
551 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
552 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
553
554 if f.IsDir() {
555 // Moving a directory - check if there is an existing directory to merge with
556 externalLstat, err := os.Lstat(externalFilePath)
557 if err != nil {
558 if !os.IsNotExist(err) {
559 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
560 }
561 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
562 } else {
563 if externalLstat.IsDir() {
564 // Existing dir - try to merge the directories?
565 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
566 continue
567 } else {
568 // Existing file being replaced with a directory. Delete the existing file...
569 if err := os.RemoveAll(externalFilePath); err != nil {
570 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
571 }
572 }
573 }
574 } else {
575 // Moving a file (not a dir) - delete any existing file or directory
576 if err := os.RemoveAll(externalFilePath); err != nil {
577 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
578 }
579 }
580
581 // The actual move - do a rename instead of a copy in order to save disk space.
582 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
583 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
584 }
585 }
586}