blob: d70978727b7eb844a5f8361b61bbb995acebca09 [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"
Kousik Kumar51c40912021-07-21 03:24:32 -040019 "encoding/json"
Dan Willemsen051133b2017-07-14 11:29:29 -070020 "flag"
21 "fmt"
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +000022 "io/ioutil"
Dan Willemsen1e704462016-08-21 15:17:17 -070023 "os"
24 "path/filepath"
25 "strconv"
26 "strings"
27 "time"
28
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +010029 "android/soong/shared"
Dan Willemsen1e704462016-08-21 15:17:17 -070030 "android/soong/ui/build"
31 "android/soong/ui/logger"
Nan Zhang17f27672018-12-12 16:01:49 -080032 "android/soong/ui/metrics"
Lukacs T. Berkif656b842021-08-11 11:10:28 +020033 "android/soong/ui/signal"
Dan Willemsenb82471a2018-05-17 16:37:09 -070034 "android/soong/ui/status"
35 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070036 "android/soong/ui/tracer"
Dan Willemsen1e704462016-08-21 15:17:17 -070037)
38
Kousik Kumar51c40912021-07-21 03:24:32 -040039const (
40 configDir = "vendor/google/tools/soong_config"
41 jsonSuffix = "json"
42)
43
Patrice Arrudaa5c25422019-04-09 18:49:49 -070044// A command represents an operation to be executed in the soong build
45// system.
46type command struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000047 // The flag name (must have double dashes).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070048 flag string
49
Patrice Arrudaf445ba12020-07-28 17:49:01 +000050 // Description for the flag (to display when running help).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070051 description string
52
Patrice Arrudaf445ba12020-07-28 17:49:01 +000053 // Stream the build status output into the simple terminal mode.
54 simpleOutput bool
Colin Crossc0b9f6b2019-09-23 12:44:54 -070055
56 // Sets a prefix string to use for filenames of log files.
57 logsPrefix string
58
Patrice Arrudaa5c25422019-04-09 18:49:49 -070059 // Creates the build configuration based on the args and build context.
60 config func(ctx build.Context, args ...string) build.Config
61
62 // Returns what type of IO redirection this Command requires.
63 stdio func() terminal.StdioInterface
64
65 // run the command
66 run func(ctx build.Context, config build.Config, args []string, logsDir string)
67}
68
Patrice Arrudaa5c25422019-04-09 18:49:49 -070069// list of supported commands (flags) supported by soong ui
70var commands []command = []command{
71 {
Anton Hansson5a7861a2021-06-04 10:09:01 +010072 flag: "--make-mode",
Patrice Arrudaa5c25422019-04-09 18:49:49 -070073 description: "build the modules by the target name (i.e. soong_docs)",
74 config: func(ctx build.Context, args ...string) build.Config {
75 return build.NewConfig(ctx, args...)
76 },
Patrice Arrudab7b22822019-05-21 17:46:23 -070077 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010078 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070079 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000080 flag: "--dumpvar-mode",
81 description: "print the value of the legacy make variable VAR to stdout",
82 simpleOutput: true,
83 logsPrefix: "dumpvars-",
84 config: dumpVarConfig,
85 stdio: customStdio,
86 run: dumpVar,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070087 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000088 flag: "--dumpvars-mode",
89 description: "dump the values of one or more legacy make variables, in shell syntax",
90 simpleOutput: true,
91 logsPrefix: "dumpvars-",
92 config: dumpVarConfig,
93 stdio: customStdio,
94 run: dumpVars,
Patrice Arrudab7b22822019-05-21 17:46:23 -070095 }, {
96 flag: "--build-mode",
97 description: "build modules based on the specified build action",
98 config: buildActionConfig,
99 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100100 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700101 },
102}
103
104// indexList returns the index of first found s. -1 is return if s is not
105// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700106func indexList(s string, list []string) int {
107 for i, l := range list {
108 if l == s {
109 return i
110 }
111 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700112 return -1
113}
114
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700115// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700116func inList(s string, list []string) bool {
117 return indexList(s, list) != -1
118}
119
Kousik Kumar51c40912021-07-21 03:24:32 -0400120func loadEnvConfig() error {
121 bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
122 if bc == "" {
123 return nil
124 }
125 cfgFile := filepath.Join(os.Getenv("TOP"), configDir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
126
127 envVarsJSON, err := ioutil.ReadFile(cfgFile)
128 if err != nil {
129 fmt.Fprintf(os.Stderr, "\033[33mWARNING:\033[0m failed to open config file %s: %s\n", cfgFile, err.Error())
130 return nil
131 }
132
133 var envVars map[string]map[string]string
134 if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
135 return fmt.Errorf("env vars config file: %s did not parse correctly: %s", cfgFile, err.Error())
136 }
137 for k, v := range envVars["env"] {
138 if os.Getenv(k) != "" {
139 continue
140 }
141 if err := os.Setenv(k, v); err != nil {
142 return err
143 }
144 }
145 return nil
146}
147
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700148// Main execution of soong_ui. The command format is as follows:
149//
150// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
151//
152// Command is the type of soong_ui execution. Only one type of
153// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700154func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100155 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
156
Patrice Arruda73c790f2020-07-13 23:01:18 +0000157 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000158
Liz Kammer0e7993e2020-10-15 11:07:13 -0700159 c, args, err := getCommand(os.Args)
160 if err != nil {
161 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700162 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700163 }
164
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800165 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000166 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Crosse0df1a32019-06-09 19:40:08 -0700167 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700168
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800169 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700170 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700171 defer log.Cleanup()
172
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800173 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700174 ctx, cancel := context.WithCancel(context.Background())
175 defer cancel()
176
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800177 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700178 trace := tracer.New(log)
179 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700180
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800181 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800182 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000183 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700184 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800185
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800186 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700187 stat := &status.Status{}
188 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800189 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700190 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700191 stat.AddOutput(trace.StatusTracer())
192
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800193 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200194 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700195 trace.Close()
196 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700197 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700198 })
199
Dan Willemsen59339a22018-07-22 21:18:45 -0700200 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700201 Context: ctx,
202 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800203 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700204 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700205 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700206 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700207 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700208
Kousik Kumar51c40912021-07-21 03:24:32 -0400209 if err := loadEnvConfig(); err != nil {
210 fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
211 os.Exit(1)
212 }
213
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700214 config := c.config(buildCtx, args...)
Dan Willemsen1e704462016-08-21 15:17:17 -0700215
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700216 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800217
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000218 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000219 defer populateExternalDistDir(buildCtx, config)
220 }
221
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800222 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000223 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700224
Patrice Arruda40564022020-12-10 00:42:58 +0000225 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000226 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
227 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
228 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000229
Kousik Kumara0a44a82020-10-08 02:33:29 -0400230 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000231
Dan Willemsenb82471a2018-05-17 16:37:09 -0700232 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700233 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
234 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
235 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
236 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000237 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700238 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700239 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700240
Colin Cross8b8bec32019-11-15 13:18:43 -0800241 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
242 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
243 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
244
Patrice Arruda40564022020-12-10 00:42:58 +0000245 {
246 // The order of the function calls is important. The last defer function call
247 // is the first one that is executed to save the rbe metrics to a protobuf
248 // file. The soong metrics file is then next. Bazel profiles are written
249 // before the uploadMetrics is invoked. The written files are then uploaded
250 // if the uploading of the metrics is enabled.
251 files := []string{
252 buildErrorFile, // build error strings
253 rbeMetricsFile, // high level metrics related to remote build execution.
254 soongMetricsFile, // high level metrics related to this build system.
255 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
256 }
257 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
258 defer met.Dump(soongMetricsFile)
259 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
260 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800261
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800262 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700263 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800264 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
265 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700266 if !strings.HasSuffix(start, "N") {
267 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
268 log.Verbosef("Took %dms to start up.",
269 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800270 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700271 }
272 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700273
274 if executable, err := os.Executable(); err == nil {
275 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
276 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700277 }
278
Dan Willemsen6b783c82019-03-08 11:42:28 -0800279 // Fix up the source tree due to a repo bug where it doesn't remove
280 // linkfiles that have been removed
281 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
282 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
283
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800284 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700285 f := build.NewSourceFinder(buildCtx, config)
286 defer f.Shutdown()
287 build.FindSources(buildCtx, config, f)
288
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700289 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700290}
291
Dan Willemsen6b783c82019-03-08 11:42:28 -0800292func fixBadDanglingLink(ctx build.Context, name string) {
293 _, err := os.Lstat(name)
294 if err != nil {
295 return
296 }
297 _, err = os.Stat(name)
298 if os.IsNotExist(err) {
299 err = os.Remove(name)
300 if err != nil {
301 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
302 }
303 }
304}
305
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700306func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700307 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
308 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700309 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
310 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
311 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700312
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700313 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
314 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
315 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700316 flags.PrintDefaults()
317 }
318 abs := flags.Bool("abs", false, "Print the absolute path of the value")
319 flags.Parse(args)
320
321 if flags.NArg() != 1 {
322 flags.Usage()
323 os.Exit(1)
324 }
325
326 varName := flags.Arg(0)
327 if varName == "report_config" {
328 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
329 if err != nil {
330 ctx.Fatal(err)
331 }
332
333 fmt.Println(build.Banner(varData))
334 } else {
335 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
336 if err != nil {
337 ctx.Fatal(err)
338 }
339
340 if *abs {
341 var res []string
342 for _, path := range strings.Fields(varData[varName]) {
343 if abs, err := filepath.Abs(path); err == nil {
344 res = append(res, abs)
345 } else {
346 ctx.Fatalln("Failed to get absolute path of", path, err)
347 }
348 }
349 fmt.Println(strings.Join(res, " "))
350 } else {
351 fmt.Println(varData[varName])
352 }
353 }
354}
355
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700356func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700357 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
358 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700359 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
360 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
361 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
362 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
363 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700364
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700365 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
366 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
367 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700368 flags.PrintDefaults()
369 }
370
371 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
372 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
373
374 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
375 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
376
377 flags.Parse(args)
378
379 if flags.NArg() != 0 {
380 flags.Usage()
381 os.Exit(1)
382 }
383
384 vars := strings.Fields(*varsStr)
385 absVars := strings.Fields(*absVarsStr)
386
387 allVars := append([]string{}, vars...)
388 allVars = append(allVars, absVars...)
389
390 if i := indexList("report_config", allVars); i != -1 {
391 allVars = append(allVars[:i], allVars[i+1:]...)
392 allVars = append(allVars, build.BannerVars...)
393 }
394
395 if len(allVars) == 0 {
396 return
397 }
398
399 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
400 if err != nil {
401 ctx.Fatal(err)
402 }
403
404 for _, name := range vars {
405 if name == "report_config" {
406 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
407 } else {
408 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
409 }
410 }
411 for _, name := range absVars {
412 var res []string
413 for _, path := range strings.Fields(varData[name]) {
414 abs, err := filepath.Abs(path)
415 if err != nil {
416 ctx.Fatalln("Failed to get absolute path of", path, err)
417 }
418 res = append(res, abs)
419 }
420 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
421 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700422}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700423
Patrice Arrudab7b22822019-05-21 17:46:23 -0700424func stdio() terminal.StdioInterface {
425 return terminal.StdioImpl{}
426}
427
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800428// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
429// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700430func customStdio() terminal.StdioInterface {
431 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
432}
433
434// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
435func dumpVarConfig(ctx build.Context, args ...string) build.Config {
436 return build.NewConfig(ctx)
437}
438
Patrice Arrudab7b22822019-05-21 17:46:23 -0700439func buildActionConfig(ctx build.Context, args ...string) build.Config {
440 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
441 flags.Usage = func() {
442 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
443 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
444 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
445 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
446 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
447 fmt.Fprintln(ctx.Writer, "")
448 flags.PrintDefaults()
449 }
450
451 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700452 name string
453 description string
454 action build.BuildAction
455 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700456 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700457 name: "all-modules",
458 description: "Build action: build from the top of the source tree.",
459 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700460 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700461 // This is redirecting to mma build command behaviour. Once it has soaked for a
462 // while, the build command is deleted from here once it has been removed from the
463 // envsetup.sh.
464 name: "modules-in-a-dir-no-deps",
465 description: "Build action: builds all of the modules in the current directory without their dependencies.",
466 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700467 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700468 // This is redirecting to mmma build command behaviour. Once it has soaked for a
469 // while, the build command is deleted from here once it has been removed from the
470 // envsetup.sh.
471 name: "modules-in-dirs-no-deps",
472 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
473 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700474 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700475 name: "modules-in-a-dir",
476 description: "Build action: builds all of the modules in the current directory and their dependencies.",
477 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700478 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700479 name: "modules-in-dirs",
480 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
481 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700482 }}
483 for i, flag := range buildActionFlags {
484 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
485 }
486 dir := flags.String("dir", "", "Directory of the executed build command.")
487
488 // Only interested in the first two args which defines the build action and the directory.
489 // The remaining arguments are passed down to the config.
490 const numBuildActionFlags = 2
491 if len(args) < numBuildActionFlags {
492 flags.Usage()
493 ctx.Fatalln("Improper build action arguments.")
494 }
495 flags.Parse(args[0:numBuildActionFlags])
496
497 // The next block of code is to validate that exactly one build action is set and the dir flag
498 // is specified.
499 buildActionCount := 0
500 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700501 for _, flag := range buildActionFlags {
502 if flag.set {
503 buildActionCount++
504 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700505 }
506 }
507 if buildActionCount != 1 {
508 ctx.Fatalln("Build action not defined.")
509 }
510 if *dir == "" {
511 ctx.Fatalln("-dir not specified.")
512 }
513
514 // Remove the build action flags from the args as they are not recognized by the config.
515 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700516 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700517}
518
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100519func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700520 if config.IsVerbose() {
521 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700522 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
523 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
524 fmt.Fprintln(writer, "!")
525 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
526 fmt.Fprintln(writer, "!")
527 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
528 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700529 select {
530 case <-time.After(5 * time.Second):
531 case <-ctx.Done():
532 return
533 }
534 }
535
536 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
537 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700538 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700539 fmt.Fprintln(writer, "!")
540 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
541 fmt.Fprintln(writer, "!")
542 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
543 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700544 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700545 }
546
Anton Hansson5a7861a2021-06-04 10:09:01 +0100547 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700548}
549
550// getCommand finds the appropriate command based on args[1] flag. args[0]
551// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700552func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700553 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700554 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700555 }
556
557 for _, c := range commands {
558 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700559 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700560 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700561 }
562
563 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700564 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700565}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000566
567// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
568func populateExternalDistDir(ctx build.Context, config build.Config) {
569 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
570 var err error
571 var internalDistDirPath string
572 var externalDistDirPath string
573 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
574 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
575 }
576 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
577 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
578 }
579 if externalDistDirPath == internalDistDirPath {
580 return
581 }
582
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000583 // Make sure the internal DIST_DIR actually exists before trying to read from it
584 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
585 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
586 return
587 }
588
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000589 // Make sure the external DIST_DIR actually exists before trying to write to it
590 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
591 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
592 }
593
594 ctx.Println("Populating external DIST_DIR...")
595
596 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
597}
598
599func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
600 files, err := ioutil.ReadDir(internalDistDirPath)
601 if err != nil {
602 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
603 }
604 for _, f := range files {
605 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
606 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
607
608 if f.IsDir() {
609 // Moving a directory - check if there is an existing directory to merge with
610 externalLstat, err := os.Lstat(externalFilePath)
611 if err != nil {
612 if !os.IsNotExist(err) {
613 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
614 }
615 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
616 } else {
617 if externalLstat.IsDir() {
618 // Existing dir - try to merge the directories?
619 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
620 continue
621 } else {
622 // Existing file being replaced with a directory. Delete the existing file...
623 if err := os.RemoveAll(externalFilePath); err != nil {
624 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
625 }
626 }
627 }
628 } else {
629 // Moving a file (not a dir) - delete any existing file or directory
630 if err := os.RemoveAll(externalFilePath); err != nil {
631 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
632 }
633 }
634
635 // The actual move - do a rename instead of a copy in order to save disk space.
636 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
637 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
638 }
639 }
640}