blob: 74ede68b14923f1139ad091dc5820d3150953b60 [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 Shuttleworth534f1572020-12-16 23:07:06 +0000177 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000178 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 Arruda40564022020-12-10 00:42:58 +0000184 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000185 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
186 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
187 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000188
Kousik Kumara0a44a82020-10-08 02:33:29 -0400189 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000190
Dan Willemsenb82471a2018-05-17 16:37:09 -0700191 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700192 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
193 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
194 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
195 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000196 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700197 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700198 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700199
Colin Cross8b8bec32019-11-15 13:18:43 -0800200 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
201 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
202 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
203
Patrice Arruda40564022020-12-10 00:42:58 +0000204 {
205 // The order of the function calls is important. The last defer function call
206 // is the first one that is executed to save the rbe metrics to a protobuf
207 // file. The soong metrics file is then next. Bazel profiles are written
208 // before the uploadMetrics is invoked. The written files are then uploaded
209 // if the uploading of the metrics is enabled.
210 files := []string{
211 buildErrorFile, // build error strings
212 rbeMetricsFile, // high level metrics related to remote build execution.
213 soongMetricsFile, // high level metrics related to this build system.
214 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
215 }
216 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
217 defer met.Dump(soongMetricsFile)
218 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
219 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800220
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800221 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700222 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800223 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
224 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700225 if !strings.HasSuffix(start, "N") {
226 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
227 log.Verbosef("Took %dms to start up.",
228 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800229 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700230 }
231 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700232
233 if executable, err := os.Executable(); err == nil {
234 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
235 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700236 }
237
Dan Willemsen6b783c82019-03-08 11:42:28 -0800238 // Fix up the source tree due to a repo bug where it doesn't remove
239 // linkfiles that have been removed
240 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
241 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
242
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800243 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700244 f := build.NewSourceFinder(buildCtx, config)
245 defer f.Shutdown()
246 build.FindSources(buildCtx, config, f)
247
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700248 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700249}
250
Dan Willemsen6b783c82019-03-08 11:42:28 -0800251func fixBadDanglingLink(ctx build.Context, name string) {
252 _, err := os.Lstat(name)
253 if err != nil {
254 return
255 }
256 _, err = os.Stat(name)
257 if os.IsNotExist(err) {
258 err = os.Remove(name)
259 if err != nil {
260 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
261 }
262 }
263}
264
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700265func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700266 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
267 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700268 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
269 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
270 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700271
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700272 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
273 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
274 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700275 flags.PrintDefaults()
276 }
277 abs := flags.Bool("abs", false, "Print the absolute path of the value")
278 flags.Parse(args)
279
280 if flags.NArg() != 1 {
281 flags.Usage()
282 os.Exit(1)
283 }
284
285 varName := flags.Arg(0)
286 if varName == "report_config" {
287 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
288 if err != nil {
289 ctx.Fatal(err)
290 }
291
292 fmt.Println(build.Banner(varData))
293 } else {
294 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
295 if err != nil {
296 ctx.Fatal(err)
297 }
298
299 if *abs {
300 var res []string
301 for _, path := range strings.Fields(varData[varName]) {
302 if abs, err := filepath.Abs(path); err == nil {
303 res = append(res, abs)
304 } else {
305 ctx.Fatalln("Failed to get absolute path of", path, err)
306 }
307 }
308 fmt.Println(strings.Join(res, " "))
309 } else {
310 fmt.Println(varData[varName])
311 }
312 }
313}
314
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700315func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700316 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
317 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700318 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
319 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
320 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
321 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
322 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700323
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700324 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
325 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
326 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700327 flags.PrintDefaults()
328 }
329
330 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
331 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
332
333 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
334 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
335
336 flags.Parse(args)
337
338 if flags.NArg() != 0 {
339 flags.Usage()
340 os.Exit(1)
341 }
342
343 vars := strings.Fields(*varsStr)
344 absVars := strings.Fields(*absVarsStr)
345
346 allVars := append([]string{}, vars...)
347 allVars = append(allVars, absVars...)
348
349 if i := indexList("report_config", allVars); i != -1 {
350 allVars = append(allVars[:i], allVars[i+1:]...)
351 allVars = append(allVars, build.BannerVars...)
352 }
353
354 if len(allVars) == 0 {
355 return
356 }
357
358 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
359 if err != nil {
360 ctx.Fatal(err)
361 }
362
363 for _, name := range vars {
364 if name == "report_config" {
365 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
366 } else {
367 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
368 }
369 }
370 for _, name := range absVars {
371 var res []string
372 for _, path := range strings.Fields(varData[name]) {
373 abs, err := filepath.Abs(path)
374 if err != nil {
375 ctx.Fatalln("Failed to get absolute path of", path, err)
376 }
377 res = append(res, abs)
378 }
379 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
380 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700381}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700382
Patrice Arrudab7b22822019-05-21 17:46:23 -0700383func stdio() terminal.StdioInterface {
384 return terminal.StdioImpl{}
385}
386
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800387// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
388// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700389func customStdio() terminal.StdioInterface {
390 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
391}
392
393// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
394func dumpVarConfig(ctx build.Context, args ...string) build.Config {
395 return build.NewConfig(ctx)
396}
397
Patrice Arrudab7b22822019-05-21 17:46:23 -0700398func buildActionConfig(ctx build.Context, args ...string) build.Config {
399 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
400 flags.Usage = func() {
401 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
402 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
403 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
404 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
405 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
406 fmt.Fprintln(ctx.Writer, "")
407 flags.PrintDefaults()
408 }
409
410 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700411 name string
412 description string
413 action build.BuildAction
414 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700415 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700416 name: "all-modules",
417 description: "Build action: build from the top of the source tree.",
418 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700419 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700420 // This is redirecting to mma build command behaviour. Once it has soaked for a
421 // while, the build command is deleted from here once it has been removed from the
422 // envsetup.sh.
423 name: "modules-in-a-dir-no-deps",
424 description: "Build action: builds all of the modules in the current directory without their dependencies.",
425 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700426 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700427 // This is redirecting to mmma build command behaviour. Once it has soaked for a
428 // while, the build command is deleted from here once it has been removed from the
429 // envsetup.sh.
430 name: "modules-in-dirs-no-deps",
431 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
432 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700433 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700434 name: "modules-in-a-dir",
435 description: "Build action: builds all of the modules in the current directory and their dependencies.",
436 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700437 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700438 name: "modules-in-dirs",
439 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
440 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700441 }}
442 for i, flag := range buildActionFlags {
443 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
444 }
445 dir := flags.String("dir", "", "Directory of the executed build command.")
446
447 // Only interested in the first two args which defines the build action and the directory.
448 // The remaining arguments are passed down to the config.
449 const numBuildActionFlags = 2
450 if len(args) < numBuildActionFlags {
451 flags.Usage()
452 ctx.Fatalln("Improper build action arguments.")
453 }
454 flags.Parse(args[0:numBuildActionFlags])
455
456 // The next block of code is to validate that exactly one build action is set and the dir flag
457 // is specified.
458 buildActionCount := 0
459 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700460 for _, flag := range buildActionFlags {
461 if flag.set {
462 buildActionCount++
463 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700464 }
465 }
466 if buildActionCount != 1 {
467 ctx.Fatalln("Build action not defined.")
468 }
469 if *dir == "" {
470 ctx.Fatalln("-dir not specified.")
471 }
472
473 // Remove the build action flags from the args as they are not recognized by the config.
474 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700475 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700476}
477
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700478func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
479 if config.IsVerbose() {
480 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700481 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
482 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
483 fmt.Fprintln(writer, "!")
484 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
485 fmt.Fprintln(writer, "!")
486 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
487 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700488 select {
489 case <-time.After(5 * time.Second):
490 case <-ctx.Done():
491 return
492 }
493 }
494
495 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
496 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700497 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700498 fmt.Fprintln(writer, "!")
499 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
500 fmt.Fprintln(writer, "!")
501 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
502 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700503 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700504 }
505
Patrice Arruda0c1c4562020-11-11 13:01:25 -0800506 toBuild := build.BuildAll
507 if config.UseBazel() {
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000508 toBuild = build.BuildAllWithBazel
Rupert Shuttleworth680387b2020-10-25 12:31:27 +0000509 }
510
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700511 if config.Checkbuild() {
512 toBuild |= build.RunBuildTests
513 }
514 build.Build(ctx, config, toBuild)
515}
516
517// getCommand finds the appropriate command based on args[1] flag. args[0]
518// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700519func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700520 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700521 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700522 }
523
524 for _, c := range commands {
525 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700526 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700527 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700528 }
529
530 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700531 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700532}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000533
534// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
535func populateExternalDistDir(ctx build.Context, config build.Config) {
536 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
537 var err error
538 var internalDistDirPath string
539 var externalDistDirPath string
540 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
541 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
542 }
543 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
544 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
545 }
546 if externalDistDirPath == internalDistDirPath {
547 return
548 }
549
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000550 // Make sure the internal DIST_DIR actually exists before trying to read from it
551 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
552 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
553 return
554 }
555
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000556 // Make sure the external DIST_DIR actually exists before trying to write to it
557 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
558 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
559 }
560
561 ctx.Println("Populating external DIST_DIR...")
562
563 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
564}
565
566func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
567 files, err := ioutil.ReadDir(internalDistDirPath)
568 if err != nil {
569 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
570 }
571 for _, f := range files {
572 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
573 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
574
575 if f.IsDir() {
576 // Moving a directory - check if there is an existing directory to merge with
577 externalLstat, err := os.Lstat(externalFilePath)
578 if err != nil {
579 if !os.IsNotExist(err) {
580 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
581 }
582 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
583 } else {
584 if externalLstat.IsDir() {
585 // Existing dir - try to merge the directories?
586 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
587 continue
588 } else {
589 // Existing file being replaced with a directory. Delete the existing file...
590 if err := os.RemoveAll(externalFilePath); err != nil {
591 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
592 }
593 }
594 }
595 } else {
596 // Moving a file (not a dir) - delete any existing file or directory
597 if err := os.RemoveAll(externalFilePath); err != nil {
598 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
599 }
600 }
601
602 // The actual move - do a rename instead of a copy in order to save disk space.
603 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
604 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
605 }
606 }
607}