blob: d8cb47a4459ee11c1bccbc38e660bce36dd5d0c5 [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 Kumar7b7dca42022-01-14 00:22:32 -0500120func loadEnvConfig(config build.Config) error {
Kousik Kumar51c40912021-07-21 03:24:32 -0400121 bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
122 if bc == "" {
123 return nil
124 }
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500125 configDirs := []string{
126 os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG_DIR"),
127 config.OutDir(),
128 configDir,
129 }
130 var cfgFile string
131 for _, dir := range configDirs {
132 cfgFile = filepath.Join(os.Getenv("TOP"), dir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
133 if _, err := os.Stat(cfgFile); err == nil {
134 break
135 }
136 }
Kousik Kumar51c40912021-07-21 03:24:32 -0400137
138 envVarsJSON, err := ioutil.ReadFile(cfgFile)
139 if err != nil {
140 fmt.Fprintf(os.Stderr, "\033[33mWARNING:\033[0m failed to open config file %s: %s\n", cfgFile, err.Error())
141 return nil
142 }
143
144 var envVars map[string]map[string]string
145 if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
146 return fmt.Errorf("env vars config file: %s did not parse correctly: %s", cfgFile, err.Error())
147 }
148 for k, v := range envVars["env"] {
149 if os.Getenv(k) != "" {
150 continue
151 }
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500152 config.Environment().Set(k, v)
Kousik Kumar51c40912021-07-21 03:24:32 -0400153 }
154 return nil
155}
156
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700157// Main execution of soong_ui. The command format is as follows:
158//
159// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
160//
161// Command is the type of soong_ui execution. Only one type of
162// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700163func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100164 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
165
Patrice Arruda73c790f2020-07-13 23:01:18 +0000166 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000167
Liz Kammer0e7993e2020-10-15 11:07:13 -0700168 c, args, err := getCommand(os.Args)
169 if err != nil {
170 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700171 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700172 }
173
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800174 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000175 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Cross3c0fe0e2021-02-10 13:11:18 -0800176 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
177 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700178
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800179 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700180 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700181 defer log.Cleanup()
182
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800183 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700184 ctx, cancel := context.WithCancel(context.Background())
185 defer cancel()
186
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800187 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700188 trace := tracer.New(log)
189 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700190
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800191 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800192 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000193 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700194 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800195
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800196 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700197 stat := &status.Status{}
198 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800199 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700200 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700201 stat.AddOutput(trace.StatusTracer())
202
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800203 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200204 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700205 trace.Close()
206 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700207 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700208 })
209
Dan Willemsen59339a22018-07-22 21:18:45 -0700210 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700211 Context: ctx,
212 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800213 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700214 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700215 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700216 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700217 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700218
Kousik Kumar7b7dca42022-01-14 00:22:32 -0500219 config := c.config(buildCtx, args...)
220
221 if err := loadEnvConfig(config); err != nil {
Kousik Kumar51c40912021-07-21 03:24:32 -0400222 fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
223 os.Exit(1)
224 }
225
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700226 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800227
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000228 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000229 defer populateExternalDistDir(buildCtx, config)
230 }
231
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800232 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000233 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700234
Patrice Arruda40564022020-12-10 00:42:58 +0000235 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000236 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
237 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
238 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000239
Kousik Kumara0a44a82020-10-08 02:33:29 -0400240 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000241
Dan Willemsenb82471a2018-05-17 16:37:09 -0700242 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700243 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
244 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
245 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
246 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000247 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700248 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700249 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700250
Colin Cross8b8bec32019-11-15 13:18:43 -0800251 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
252 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
253 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
254
Patrice Arruda40564022020-12-10 00:42:58 +0000255 {
256 // The order of the function calls is important. The last defer function call
257 // is the first one that is executed to save the rbe metrics to a protobuf
258 // file. The soong metrics file is then next. Bazel profiles are written
259 // before the uploadMetrics is invoked. The written files are then uploaded
260 // if the uploading of the metrics is enabled.
261 files := []string{
262 buildErrorFile, // build error strings
263 rbeMetricsFile, // high level metrics related to remote build execution.
264 soongMetricsFile, // high level metrics related to this build system.
265 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
266 }
267 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
268 defer met.Dump(soongMetricsFile)
269 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
270 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800271
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800272 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700273 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800274 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
275 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700276 if !strings.HasSuffix(start, "N") {
277 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
278 log.Verbosef("Took %dms to start up.",
279 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800280 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700281 }
282 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700283
284 if executable, err := os.Executable(); err == nil {
285 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
286 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700287 }
288
Dan Willemsen6b783c82019-03-08 11:42:28 -0800289 // Fix up the source tree due to a repo bug where it doesn't remove
290 // linkfiles that have been removed
291 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
292 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
293
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800294 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700295 f := build.NewSourceFinder(buildCtx, config)
296 defer f.Shutdown()
297 build.FindSources(buildCtx, config, f)
298
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700299 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700300}
301
Dan Willemsen6b783c82019-03-08 11:42:28 -0800302func fixBadDanglingLink(ctx build.Context, name string) {
303 _, err := os.Lstat(name)
304 if err != nil {
305 return
306 }
307 _, err = os.Stat(name)
308 if os.IsNotExist(err) {
309 err = os.Remove(name)
310 if err != nil {
311 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
312 }
313 }
314}
315
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700316func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700317 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
318 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700319 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
320 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
321 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700322
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700323 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
324 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
325 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700326 flags.PrintDefaults()
327 }
328 abs := flags.Bool("abs", false, "Print the absolute path of the value")
329 flags.Parse(args)
330
331 if flags.NArg() != 1 {
332 flags.Usage()
333 os.Exit(1)
334 }
335
336 varName := flags.Arg(0)
337 if varName == "report_config" {
338 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
339 if err != nil {
340 ctx.Fatal(err)
341 }
342
343 fmt.Println(build.Banner(varData))
344 } else {
345 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
346 if err != nil {
347 ctx.Fatal(err)
348 }
349
350 if *abs {
351 var res []string
352 for _, path := range strings.Fields(varData[varName]) {
353 if abs, err := filepath.Abs(path); err == nil {
354 res = append(res, abs)
355 } else {
356 ctx.Fatalln("Failed to get absolute path of", path, err)
357 }
358 }
359 fmt.Println(strings.Join(res, " "))
360 } else {
361 fmt.Println(varData[varName])
362 }
363 }
364}
365
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700366func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700367 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
368 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700369 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
370 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
371 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
372 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
373 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700374
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700375 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
376 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
377 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700378 flags.PrintDefaults()
379 }
380
381 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
382 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
383
384 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
385 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
386
387 flags.Parse(args)
388
389 if flags.NArg() != 0 {
390 flags.Usage()
391 os.Exit(1)
392 }
393
394 vars := strings.Fields(*varsStr)
395 absVars := strings.Fields(*absVarsStr)
396
397 allVars := append([]string{}, vars...)
398 allVars = append(allVars, absVars...)
399
400 if i := indexList("report_config", allVars); i != -1 {
401 allVars = append(allVars[:i], allVars[i+1:]...)
402 allVars = append(allVars, build.BannerVars...)
403 }
404
405 if len(allVars) == 0 {
406 return
407 }
408
409 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
410 if err != nil {
411 ctx.Fatal(err)
412 }
413
414 for _, name := range vars {
415 if name == "report_config" {
416 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
417 } else {
418 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
419 }
420 }
421 for _, name := range absVars {
422 var res []string
423 for _, path := range strings.Fields(varData[name]) {
424 abs, err := filepath.Abs(path)
425 if err != nil {
426 ctx.Fatalln("Failed to get absolute path of", path, err)
427 }
428 res = append(res, abs)
429 }
430 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
431 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700432}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700433
Patrice Arrudab7b22822019-05-21 17:46:23 -0700434func stdio() terminal.StdioInterface {
435 return terminal.StdioImpl{}
436}
437
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800438// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
439// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700440func customStdio() terminal.StdioInterface {
441 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
442}
443
444// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
445func dumpVarConfig(ctx build.Context, args ...string) build.Config {
446 return build.NewConfig(ctx)
447}
448
Patrice Arrudab7b22822019-05-21 17:46:23 -0700449func buildActionConfig(ctx build.Context, args ...string) build.Config {
450 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
451 flags.Usage = func() {
452 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
453 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
454 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
455 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
456 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
457 fmt.Fprintln(ctx.Writer, "")
458 flags.PrintDefaults()
459 }
460
461 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700462 name string
463 description string
464 action build.BuildAction
465 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700466 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700467 name: "all-modules",
468 description: "Build action: build from the top of the source tree.",
469 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700470 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700471 // This is redirecting to mma build command behaviour. Once it has soaked for a
472 // while, the build command is deleted from here once it has been removed from the
473 // envsetup.sh.
474 name: "modules-in-a-dir-no-deps",
475 description: "Build action: builds all of the modules in the current directory without their dependencies.",
476 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700477 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700478 // This is redirecting to mmma build command behaviour. Once it has soaked for a
479 // while, the build command is deleted from here once it has been removed from the
480 // envsetup.sh.
481 name: "modules-in-dirs-no-deps",
482 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
483 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700484 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700485 name: "modules-in-a-dir",
486 description: "Build action: builds all of the modules in the current directory and their dependencies.",
487 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700488 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700489 name: "modules-in-dirs",
490 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
491 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700492 }}
493 for i, flag := range buildActionFlags {
494 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
495 }
496 dir := flags.String("dir", "", "Directory of the executed build command.")
497
498 // Only interested in the first two args which defines the build action and the directory.
499 // The remaining arguments are passed down to the config.
500 const numBuildActionFlags = 2
501 if len(args) < numBuildActionFlags {
502 flags.Usage()
503 ctx.Fatalln("Improper build action arguments.")
504 }
505 flags.Parse(args[0:numBuildActionFlags])
506
507 // The next block of code is to validate that exactly one build action is set and the dir flag
508 // is specified.
509 buildActionCount := 0
510 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700511 for _, flag := range buildActionFlags {
512 if flag.set {
513 buildActionCount++
514 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700515 }
516 }
517 if buildActionCount != 1 {
518 ctx.Fatalln("Build action not defined.")
519 }
520 if *dir == "" {
521 ctx.Fatalln("-dir not specified.")
522 }
523
524 // Remove the build action flags from the args as they are not recognized by the config.
525 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700526 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700527}
528
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100529func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700530 if config.IsVerbose() {
531 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700532 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
533 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
534 fmt.Fprintln(writer, "!")
535 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
536 fmt.Fprintln(writer, "!")
537 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
538 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700539 select {
540 case <-time.After(5 * time.Second):
541 case <-ctx.Done():
542 return
543 }
544 }
545
546 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
547 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700548 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700549 fmt.Fprintln(writer, "!")
550 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
551 fmt.Fprintln(writer, "!")
552 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
553 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700554 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700555 }
556
Anton Hansson5a7861a2021-06-04 10:09:01 +0100557 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700558}
559
560// getCommand finds the appropriate command based on args[1] flag. args[0]
561// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700562func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700563 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700564 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700565 }
566
567 for _, c := range commands {
568 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700569 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700570 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700571 }
572
573 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700574 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700575}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000576
577// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
578func populateExternalDistDir(ctx build.Context, config build.Config) {
579 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
580 var err error
581 var internalDistDirPath string
582 var externalDistDirPath string
583 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
584 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
585 }
586 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
587 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
588 }
589 if externalDistDirPath == internalDistDirPath {
590 return
591 }
592
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000593 // Make sure the internal DIST_DIR actually exists before trying to read from it
594 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
595 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
596 return
597 }
598
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000599 // Make sure the external DIST_DIR actually exists before trying to write to it
600 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
601 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
602 }
603
604 ctx.Println("Populating external DIST_DIR...")
605
606 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
607}
608
609func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
610 files, err := ioutil.ReadDir(internalDistDirPath)
611 if err != nil {
612 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
613 }
614 for _, f := range files {
615 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
616 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
617
618 if f.IsDir() {
619 // Moving a directory - check if there is an existing directory to merge with
620 externalLstat, err := os.Lstat(externalFilePath)
621 if err != nil {
622 if !os.IsNotExist(err) {
623 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
624 }
625 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
626 } else {
627 if externalLstat.IsDir() {
628 // Existing dir - try to merge the directories?
629 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
630 continue
631 } else {
632 // Existing file being replaced with a directory. Delete the existing file...
633 if err := os.RemoveAll(externalFilePath); err != nil {
634 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
635 }
636 }
637 }
638 } else {
639 // Moving a file (not a dir) - delete any existing file or directory
640 if err := os.RemoveAll(externalFilePath); err != nil {
641 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
642 }
643 }
644
645 // The actual move - do a rename instead of a copy in order to save disk space.
646 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
647 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
648 }
649 }
650}