blob: 9ee373e7998e3d7d90cf7f754eed7f38a0746def [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 Cross3c0fe0e2021-02-10 13:11:18 -0800167 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"),
168 build.OsEnvironment().IsEnvTrue("SOONG_UI_ANSI_OUTPUT"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700169
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800170 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700171 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700172 defer log.Cleanup()
173
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800174 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700175 ctx, cancel := context.WithCancel(context.Background())
176 defer cancel()
177
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800178 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700179 trace := tracer.New(log)
180 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700181
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800182 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800183 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000184 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700185 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800186
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800187 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700188 stat := &status.Status{}
189 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800190 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700191 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700192 stat.AddOutput(trace.StatusTracer())
193
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800194 // Set up a cleanup procedure in case the normal termination process doesn't work.
Lukacs T. Berkif656b842021-08-11 11:10:28 +0200195 signal.SetupSignals(log, cancel, func() {
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700196 trace.Close()
197 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700198 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700199 })
200
Dan Willemsen59339a22018-07-22 21:18:45 -0700201 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700202 Context: ctx,
203 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800204 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700205 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700206 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700207 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700208 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700209
Kousik Kumar51c40912021-07-21 03:24:32 -0400210 if err := loadEnvConfig(); err != nil {
211 fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
212 os.Exit(1)
213 }
214
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700215 config := c.config(buildCtx, args...)
Dan Willemsen1e704462016-08-21 15:17:17 -0700216
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700217 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800218
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000219 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000220 defer populateExternalDistDir(buildCtx, config)
221 }
222
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800223 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000224 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700225
Patrice Arruda40564022020-12-10 00:42:58 +0000226 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000227 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
228 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
229 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000230
Kousik Kumara0a44a82020-10-08 02:33:29 -0400231 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000232
Dan Willemsenb82471a2018-05-17 16:37:09 -0700233 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700234 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
235 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
236 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
237 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000238 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700239 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700240 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700241
Colin Cross8b8bec32019-11-15 13:18:43 -0800242 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
243 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
244 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
245
Patrice Arruda40564022020-12-10 00:42:58 +0000246 {
247 // The order of the function calls is important. The last defer function call
248 // is the first one that is executed to save the rbe metrics to a protobuf
249 // file. The soong metrics file is then next. Bazel profiles are written
250 // before the uploadMetrics is invoked. The written files are then uploaded
251 // if the uploading of the metrics is enabled.
252 files := []string{
253 buildErrorFile, // build error strings
254 rbeMetricsFile, // high level metrics related to remote build execution.
255 soongMetricsFile, // high level metrics related to this build system.
256 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
257 }
258 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
259 defer met.Dump(soongMetricsFile)
260 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
261 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800262
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800263 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700264 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800265 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
266 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700267 if !strings.HasSuffix(start, "N") {
268 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
269 log.Verbosef("Took %dms to start up.",
270 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800271 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700272 }
273 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700274
275 if executable, err := os.Executable(); err == nil {
276 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
277 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700278 }
279
Dan Willemsen6b783c82019-03-08 11:42:28 -0800280 // Fix up the source tree due to a repo bug where it doesn't remove
281 // linkfiles that have been removed
282 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
283 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
284
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800285 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700286 f := build.NewSourceFinder(buildCtx, config)
287 defer f.Shutdown()
288 build.FindSources(buildCtx, config, f)
289
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700290 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700291}
292
Dan Willemsen6b783c82019-03-08 11:42:28 -0800293func fixBadDanglingLink(ctx build.Context, name string) {
294 _, err := os.Lstat(name)
295 if err != nil {
296 return
297 }
298 _, err = os.Stat(name)
299 if os.IsNotExist(err) {
300 err = os.Remove(name)
301 if err != nil {
302 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
303 }
304 }
305}
306
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700307func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700308 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
309 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700310 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
311 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
312 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700313
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700314 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
315 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
316 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700317 flags.PrintDefaults()
318 }
319 abs := flags.Bool("abs", false, "Print the absolute path of the value")
320 flags.Parse(args)
321
322 if flags.NArg() != 1 {
323 flags.Usage()
324 os.Exit(1)
325 }
326
327 varName := flags.Arg(0)
328 if varName == "report_config" {
329 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
330 if err != nil {
331 ctx.Fatal(err)
332 }
333
334 fmt.Println(build.Banner(varData))
335 } else {
336 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
337 if err != nil {
338 ctx.Fatal(err)
339 }
340
341 if *abs {
342 var res []string
343 for _, path := range strings.Fields(varData[varName]) {
344 if abs, err := filepath.Abs(path); err == nil {
345 res = append(res, abs)
346 } else {
347 ctx.Fatalln("Failed to get absolute path of", path, err)
348 }
349 }
350 fmt.Println(strings.Join(res, " "))
351 } else {
352 fmt.Println(varData[varName])
353 }
354 }
355}
356
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700357func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700358 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
359 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700360 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
361 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
362 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
363 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
364 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700365
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700366 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
367 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
368 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700369 flags.PrintDefaults()
370 }
371
372 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
373 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
374
375 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
376 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
377
378 flags.Parse(args)
379
380 if flags.NArg() != 0 {
381 flags.Usage()
382 os.Exit(1)
383 }
384
385 vars := strings.Fields(*varsStr)
386 absVars := strings.Fields(*absVarsStr)
387
388 allVars := append([]string{}, vars...)
389 allVars = append(allVars, absVars...)
390
391 if i := indexList("report_config", allVars); i != -1 {
392 allVars = append(allVars[:i], allVars[i+1:]...)
393 allVars = append(allVars, build.BannerVars...)
394 }
395
396 if len(allVars) == 0 {
397 return
398 }
399
400 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
401 if err != nil {
402 ctx.Fatal(err)
403 }
404
405 for _, name := range vars {
406 if name == "report_config" {
407 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
408 } else {
409 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
410 }
411 }
412 for _, name := range absVars {
413 var res []string
414 for _, path := range strings.Fields(varData[name]) {
415 abs, err := filepath.Abs(path)
416 if err != nil {
417 ctx.Fatalln("Failed to get absolute path of", path, err)
418 }
419 res = append(res, abs)
420 }
421 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
422 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700423}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700424
Patrice Arrudab7b22822019-05-21 17:46:23 -0700425func stdio() terminal.StdioInterface {
426 return terminal.StdioImpl{}
427}
428
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800429// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
430// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700431func customStdio() terminal.StdioInterface {
432 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
433}
434
435// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
436func dumpVarConfig(ctx build.Context, args ...string) build.Config {
437 return build.NewConfig(ctx)
438}
439
Patrice Arrudab7b22822019-05-21 17:46:23 -0700440func buildActionConfig(ctx build.Context, args ...string) build.Config {
441 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
442 flags.Usage = func() {
443 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
444 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
445 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
446 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
447 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
448 fmt.Fprintln(ctx.Writer, "")
449 flags.PrintDefaults()
450 }
451
452 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700453 name string
454 description string
455 action build.BuildAction
456 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700457 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700458 name: "all-modules",
459 description: "Build action: build from the top of the source tree.",
460 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700461 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700462 // This is redirecting to mma build command behaviour. Once it has soaked for a
463 // while, the build command is deleted from here once it has been removed from the
464 // envsetup.sh.
465 name: "modules-in-a-dir-no-deps",
466 description: "Build action: builds all of the modules in the current directory without their dependencies.",
467 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700468 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700469 // This is redirecting to mmma build command behaviour. Once it has soaked for a
470 // while, the build command is deleted from here once it has been removed from the
471 // envsetup.sh.
472 name: "modules-in-dirs-no-deps",
473 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
474 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700475 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700476 name: "modules-in-a-dir",
477 description: "Build action: builds all of the modules in the current directory and their dependencies.",
478 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700479 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700480 name: "modules-in-dirs",
481 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
482 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700483 }}
484 for i, flag := range buildActionFlags {
485 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
486 }
487 dir := flags.String("dir", "", "Directory of the executed build command.")
488
489 // Only interested in the first two args which defines the build action and the directory.
490 // The remaining arguments are passed down to the config.
491 const numBuildActionFlags = 2
492 if len(args) < numBuildActionFlags {
493 flags.Usage()
494 ctx.Fatalln("Improper build action arguments.")
495 }
496 flags.Parse(args[0:numBuildActionFlags])
497
498 // The next block of code is to validate that exactly one build action is set and the dir flag
499 // is specified.
500 buildActionCount := 0
501 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700502 for _, flag := range buildActionFlags {
503 if flag.set {
504 buildActionCount++
505 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700506 }
507 }
508 if buildActionCount != 1 {
509 ctx.Fatalln("Build action not defined.")
510 }
511 if *dir == "" {
512 ctx.Fatalln("-dir not specified.")
513 }
514
515 // Remove the build action flags from the args as they are not recognized by the config.
516 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700517 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700518}
519
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100520func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700521 if config.IsVerbose() {
522 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700523 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
524 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
525 fmt.Fprintln(writer, "!")
526 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
527 fmt.Fprintln(writer, "!")
528 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
529 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700530 select {
531 case <-time.After(5 * time.Second):
532 case <-ctx.Done():
533 return
534 }
535 }
536
537 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
538 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700539 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700540 fmt.Fprintln(writer, "!")
541 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
542 fmt.Fprintln(writer, "!")
543 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
544 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700545 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700546 }
547
Anton Hansson5a7861a2021-06-04 10:09:01 +0100548 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700549}
550
551// getCommand finds the appropriate command based on args[1] flag. args[0]
552// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700553func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700554 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700555 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700556 }
557
558 for _, c := range commands {
559 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700560 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700561 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700562 }
563
564 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700565 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700566}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000567
568// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
569func populateExternalDistDir(ctx build.Context, config build.Config) {
570 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
571 var err error
572 var internalDistDirPath string
573 var externalDistDirPath string
574 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
575 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
576 }
577 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
578 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
579 }
580 if externalDistDirPath == internalDistDirPath {
581 return
582 }
583
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000584 // Make sure the internal DIST_DIR actually exists before trying to read from it
585 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
586 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
587 return
588 }
589
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000590 // Make sure the external DIST_DIR actually exists before trying to write to it
591 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
592 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
593 }
594
595 ctx.Println("Populating external DIST_DIR...")
596
597 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
598}
599
600func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
601 files, err := ioutil.ReadDir(internalDistDirPath)
602 if err != nil {
603 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
604 }
605 for _, f := range files {
606 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
607 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
608
609 if f.IsDir() {
610 // Moving a directory - check if there is an existing directory to merge with
611 externalLstat, err := os.Lstat(externalFilePath)
612 if err != nil {
613 if !os.IsNotExist(err) {
614 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
615 }
616 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
617 } else {
618 if externalLstat.IsDir() {
619 // Existing dir - try to merge the directories?
620 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
621 continue
622 } else {
623 // Existing file being replaced with a directory. Delete the existing file...
624 if err := os.RemoveAll(externalFilePath); err != nil {
625 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
626 }
627 }
628 }
629 } else {
630 // Moving a file (not a dir) - delete any existing file or directory
631 if err := os.RemoveAll(externalFilePath); err != nil {
632 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
633 }
634 }
635
636 // The actual move - do a rename instead of a copy in order to save disk space.
637 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
638 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
639 }
640 }
641}