blob: 02c52297774223fcf16f7d2a8508ec7e1905f3fc [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"
Dan Willemsenb82471a2018-05-17 16:37:09 -070033 "android/soong/ui/status"
34 "android/soong/ui/terminal"
Dan Willemsend9f6fa22016-08-21 15:17:17 -070035 "android/soong/ui/tracer"
Dan Willemsen1e704462016-08-21 15:17:17 -070036)
37
Kousik Kumar51c40912021-07-21 03:24:32 -040038const (
39 configDir = "vendor/google/tools/soong_config"
40 jsonSuffix = "json"
41)
42
Patrice Arrudaa5c25422019-04-09 18:49:49 -070043// A command represents an operation to be executed in the soong build
44// system.
45type command struct {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000046 // The flag name (must have double dashes).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070047 flag string
48
Patrice Arrudaf445ba12020-07-28 17:49:01 +000049 // Description for the flag (to display when running help).
Patrice Arrudaa5c25422019-04-09 18:49:49 -070050 description string
51
Patrice Arrudaf445ba12020-07-28 17:49:01 +000052 // Stream the build status output into the simple terminal mode.
53 simpleOutput bool
Colin Crossc0b9f6b2019-09-23 12:44:54 -070054
55 // Sets a prefix string to use for filenames of log files.
56 logsPrefix string
57
Patrice Arrudaa5c25422019-04-09 18:49:49 -070058 // Creates the build configuration based on the args and build context.
59 config func(ctx build.Context, args ...string) build.Config
60
61 // Returns what type of IO redirection this Command requires.
62 stdio func() terminal.StdioInterface
63
64 // run the command
65 run func(ctx build.Context, config build.Config, args []string, logsDir string)
66}
67
Patrice Arrudaa5c25422019-04-09 18:49:49 -070068// list of supported commands (flags) supported by soong ui
69var commands []command = []command{
70 {
Anton Hansson5a7861a2021-06-04 10:09:01 +010071 flag: "--make-mode",
Patrice Arrudaa5c25422019-04-09 18:49:49 -070072 description: "build the modules by the target name (i.e. soong_docs)",
73 config: func(ctx build.Context, args ...string) build.Config {
74 return build.NewConfig(ctx, args...)
75 },
Patrice Arrudab7b22822019-05-21 17:46:23 -070076 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010077 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070078 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000079 flag: "--dumpvar-mode",
80 description: "print the value of the legacy make variable VAR to stdout",
81 simpleOutput: true,
82 logsPrefix: "dumpvars-",
83 config: dumpVarConfig,
84 stdio: customStdio,
85 run: dumpVar,
Patrice Arrudaa5c25422019-04-09 18:49:49 -070086 }, {
Patrice Arrudaf445ba12020-07-28 17:49:01 +000087 flag: "--dumpvars-mode",
88 description: "dump the values of one or more legacy make variables, in shell syntax",
89 simpleOutput: true,
90 logsPrefix: "dumpvars-",
91 config: dumpVarConfig,
92 stdio: customStdio,
93 run: dumpVars,
Patrice Arrudab7b22822019-05-21 17:46:23 -070094 }, {
95 flag: "--build-mode",
96 description: "build modules based on the specified build action",
97 config: buildActionConfig,
98 stdio: stdio,
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +010099 run: runMake,
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700100 },
101}
102
103// indexList returns the index of first found s. -1 is return if s is not
104// found.
Dan Willemsen1e704462016-08-21 15:17:17 -0700105func indexList(s string, list []string) int {
106 for i, l := range list {
107 if l == s {
108 return i
109 }
110 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700111 return -1
112}
113
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700114// inList returns true if one or more of s is in the list.
Dan Willemsen1e704462016-08-21 15:17:17 -0700115func inList(s string, list []string) bool {
116 return indexList(s, list) != -1
117}
118
Kousik Kumar51c40912021-07-21 03:24:32 -0400119func loadEnvConfig() error {
120 bc := os.Getenv("ANDROID_BUILD_ENVIRONMENT_CONFIG")
121 if bc == "" {
122 return nil
123 }
124 cfgFile := filepath.Join(os.Getenv("TOP"), configDir, fmt.Sprintf("%s.%s", bc, jsonSuffix))
125
126 envVarsJSON, err := ioutil.ReadFile(cfgFile)
127 if err != nil {
128 fmt.Fprintf(os.Stderr, "\033[33mWARNING:\033[0m failed to open config file %s: %s\n", cfgFile, err.Error())
129 return nil
130 }
131
132 var envVars map[string]map[string]string
133 if err := json.Unmarshal(envVarsJSON, &envVars); err != nil {
134 return fmt.Errorf("env vars config file: %s did not parse correctly: %s", cfgFile, err.Error())
135 }
136 for k, v := range envVars["env"] {
137 if os.Getenv(k) != "" {
138 continue
139 }
140 if err := os.Setenv(k, v); err != nil {
141 return err
142 }
143 }
144 return nil
145}
146
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700147// Main execution of soong_ui. The command format is as follows:
148//
149// soong_ui <command> [<arg 1> <arg 2> ... <arg n>]
150//
151// Command is the type of soong_ui execution. Only one type of
152// execution is specified. The args are specific to the command.
Dan Willemsen1e704462016-08-21 15:17:17 -0700153func main() {
Lukacs T. Berki7d613bf2021-03-02 10:09:41 +0100154 shared.ReexecWithDelveMaybe(os.Getenv("SOONG_UI_DELVE"), shared.ResolveDelveBinary())
155
Patrice Arruda73c790f2020-07-13 23:01:18 +0000156 buildStarted := time.Now()
Patrice Arruda219eef32020-06-01 17:29:30 +0000157
Liz Kammer0e7993e2020-10-15 11:07:13 -0700158 c, args, err := getCommand(os.Args)
159 if err != nil {
160 fmt.Fprintf(os.Stderr, "Error parsing `soong` args: %s.\n", err)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700161 os.Exit(1)
Dan Willemsenc35b3812018-07-16 19:59:10 -0700162 }
163
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800164 // Create a terminal output that mimics Ninja's.
Patrice Arrudaf445ba12020-07-28 17:49:01 +0000165 output := terminal.NewStatusOutput(c.stdio().Stdout(), os.Getenv("NINJA_STATUS"), c.simpleOutput,
Colin Crosse0df1a32019-06-09 19:40:08 -0700166 build.OsEnvironment().IsEnvTrue("ANDROID_QUIET_BUILD"))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700167
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800168 // Attach a new logger instance to the terminal output.
Colin Crosse0df1a32019-06-09 19:40:08 -0700169 log := logger.New(output)
Dan Willemsen1e704462016-08-21 15:17:17 -0700170 defer log.Cleanup()
171
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800172 // Create a context to simplify the program termination process.
Dan Willemsen1e704462016-08-21 15:17:17 -0700173 ctx, cancel := context.WithCancel(context.Background())
174 defer cancel()
175
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800176 // Create a new trace file writer, making it log events to the log instance.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700177 trace := tracer.New(log)
178 defer trace.Close()
Dan Willemsen1e704462016-08-21 15:17:17 -0700179
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800180 // Create and start a new metric record.
Nan Zhang17f27672018-12-12 16:01:49 -0800181 met := metrics.New()
Patrice Arruda73c790f2020-07-13 23:01:18 +0000182 met.SetBuildDateTime(buildStarted)
Patrice Arrudae92c30d2020-10-29 11:01:32 -0700183 met.SetBuildCommand(os.Args)
Nan Zhang17f27672018-12-12 16:01:49 -0800184
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800185 // Create a new Status instance, which manages action counts and event output channels.
Dan Willemsenb82471a2018-05-17 16:37:09 -0700186 stat := &status.Status{}
187 defer stat.Finish()
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800188 // Hook up the terminal output and tracer to Status.
Colin Crosse0df1a32019-06-09 19:40:08 -0700189 stat.AddOutput(output)
Dan Willemsenb82471a2018-05-17 16:37:09 -0700190 stat.AddOutput(trace.StatusTracer())
191
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800192 // Set up a cleanup procedure in case the normal termination process doesn't work.
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700193 build.SetupSignals(log, cancel, func() {
194 trace.Close()
195 log.Cleanup()
Dan Willemsenb82471a2018-05-17 16:37:09 -0700196 stat.Finish()
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700197 })
198
Dan Willemsen59339a22018-07-22 21:18:45 -0700199 buildCtx := build.Context{ContextImpl: &build.ContextImpl{
Dan Willemsenb82471a2018-05-17 16:37:09 -0700200 Context: ctx,
201 Logger: log,
Nan Zhang17f27672018-12-12 16:01:49 -0800202 Metrics: met,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700203 Tracer: trace,
Colin Crosse0df1a32019-06-09 19:40:08 -0700204 Writer: output,
Dan Willemsenb82471a2018-05-17 16:37:09 -0700205 Status: stat,
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700206 }}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700207
Kousik Kumar51c40912021-07-21 03:24:32 -0400208 if err := loadEnvConfig(); err != nil {
209 fmt.Fprintf(os.Stderr, "failed to parse env config files: %v", err)
210 os.Exit(1)
211 }
212
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700213 config := c.config(buildCtx, args...)
Dan Willemsen1e704462016-08-21 15:17:17 -0700214
Dan Willemsend9f6fa22016-08-21 15:17:17 -0700215 build.SetupOutDir(buildCtx, config)
Dan Willemsen8a073a82017-02-04 17:30:44 -0800216
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000217 if config.UseBazel() && config.Dist() {
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000218 defer populateExternalDistDir(buildCtx, config)
219 }
220
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800221 // Set up files to be outputted in the log directory.
Patrice Arruda83842d72020-12-08 19:42:08 +0000222 logsDir := config.LogsDir()
Dan Willemsen1e704462016-08-21 15:17:17 -0700223
Patrice Arruda40564022020-12-10 00:42:58 +0000224 // Common list of metric file definition.
Patrice Arruda219eef32020-06-01 17:29:30 +0000225 buildErrorFile := filepath.Join(logsDir, c.logsPrefix+"build_error")
226 rbeMetricsFile := filepath.Join(logsDir, c.logsPrefix+"rbe_metrics.pb")
227 soongMetricsFile := filepath.Join(logsDir, c.logsPrefix+"soong_metrics")
Patrice Arruda40564022020-12-10 00:42:58 +0000228
Kousik Kumara0a44a82020-10-08 02:33:29 -0400229 build.PrintOutDirWarning(buildCtx, config)
Patrice Arruda219eef32020-06-01 17:29:30 +0000230
Dan Willemsenb82471a2018-05-17 16:37:09 -0700231 os.MkdirAll(logsDir, 0777)
Colin Crossc0b9f6b2019-09-23 12:44:54 -0700232 log.SetOutput(filepath.Join(logsDir, c.logsPrefix+"soong.log"))
233 trace.SetOutput(filepath.Join(logsDir, c.logsPrefix+"build.trace"))
234 stat.AddOutput(status.NewVerboseLog(log, filepath.Join(logsDir, c.logsPrefix+"verbose.log")))
235 stat.AddOutput(status.NewErrorLog(log, filepath.Join(logsDir, c.logsPrefix+"error.log")))
Patrice Arruda219eef32020-06-01 17:29:30 +0000236 stat.AddOutput(status.NewProtoErrorLog(log, buildErrorFile))
Colin Cross7b624532019-06-21 15:08:30 -0700237 stat.AddOutput(status.NewCriticalPath(log))
Patrice Arruda74b43992020-03-11 08:21:05 -0700238 stat.AddOutput(status.NewBuildProgressLog(log, filepath.Join(logsDir, c.logsPrefix+"build_progress.pb")))
Dan Willemsenb82471a2018-05-17 16:37:09 -0700239
Colin Cross8b8bec32019-11-15 13:18:43 -0800240 buildCtx.Verbosef("Detected %.3v GB total RAM", float32(config.TotalRAM())/(1024*1024*1024))
241 buildCtx.Verbosef("Parallelism (local/remote/highmem): %v/%v/%v",
242 config.Parallel(), config.RemoteParallel(), config.HighmemParallel())
243
Patrice Arruda40564022020-12-10 00:42:58 +0000244 {
245 // The order of the function calls is important. The last defer function call
246 // is the first one that is executed to save the rbe metrics to a protobuf
247 // file. The soong metrics file is then next. Bazel profiles are written
248 // before the uploadMetrics is invoked. The written files are then uploaded
249 // if the uploading of the metrics is enabled.
250 files := []string{
251 buildErrorFile, // build error strings
252 rbeMetricsFile, // high level metrics related to remote build execution.
253 soongMetricsFile, // high level metrics related to this build system.
254 config.BazelMetricsDir(), // directory that contains a set of bazel metrics.
255 }
256 defer build.UploadMetrics(buildCtx, config, c.simpleOutput, buildStarted, files...)
257 defer met.Dump(soongMetricsFile)
258 defer build.DumpRBEMetrics(buildCtx, config, rbeMetricsFile)
259 }
Nan Zhangd50f53b2019-01-07 20:26:51 -0800260
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800261 // Read the time at the starting point.
Dan Willemsen1e704462016-08-21 15:17:17 -0700262 if start, ok := os.LookupEnv("TRACE_BEGIN_SOONG"); ok {
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800263 // soong_ui.bash uses the date command's %N (nanosec) flag when getting the start time,
264 // which Darwin doesn't support. Check if it was executed properly before parsing the value.
Dan Willemsen1e704462016-08-21 15:17:17 -0700265 if !strings.HasSuffix(start, "N") {
266 if start_time, err := strconv.ParseUint(start, 10, 64); err == nil {
267 log.Verbosef("Took %dms to start up.",
268 time.Since(time.Unix(0, int64(start_time))).Nanoseconds()/time.Millisecond.Nanoseconds())
Nan Zhang17f27672018-12-12 16:01:49 -0800269 buildCtx.CompleteTrace(metrics.RunSetupTool, "startup", start_time, uint64(time.Now().UnixNano()))
Dan Willemsen1e704462016-08-21 15:17:17 -0700270 }
271 }
Dan Willemsencae59bc2017-07-13 14:27:31 -0700272
273 if executable, err := os.Executable(); err == nil {
274 trace.ImportMicrofactoryLog(filepath.Join(filepath.Dir(executable), "."+filepath.Base(executable)+".trace"))
275 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700276 }
277
Dan Willemsen6b783c82019-03-08 11:42:28 -0800278 // Fix up the source tree due to a repo bug where it doesn't remove
279 // linkfiles that have been removed
280 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.bp")
281 fixBadDanglingLink(buildCtx, "hardware/qcom/sdm710/Android.mk")
282
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800283 // Create a source finder.
Jeff Gastonb64fc1c2017-08-04 12:30:12 -0700284 f := build.NewSourceFinder(buildCtx, config)
285 defer f.Shutdown()
286 build.FindSources(buildCtx, config, f)
287
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700288 c.run(buildCtx, config, args, logsDir)
Dan Willemsen051133b2017-07-14 11:29:29 -0700289}
290
Dan Willemsen6b783c82019-03-08 11:42:28 -0800291func fixBadDanglingLink(ctx build.Context, name string) {
292 _, err := os.Lstat(name)
293 if err != nil {
294 return
295 }
296 _, err = os.Stat(name)
297 if os.IsNotExist(err) {
298 err = os.Remove(name)
299 if err != nil {
300 ctx.Fatalf("Failed to remove dangling link %q: %v", name, err)
301 }
302 }
303}
304
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700305func dumpVar(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700306 flags := flag.NewFlagSet("dumpvar", flag.ExitOnError)
307 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700308 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvar-mode [--abs] <VAR>\n\n", os.Args[0])
309 fmt.Fprintln(ctx.Writer, "In dumpvar mode, print the value of the legacy make variable VAR to stdout")
310 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700311
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700312 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that prints the human-readable config banner")
313 fmt.Fprintln(ctx.Writer, "from the beginning of the build.")
314 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700315 flags.PrintDefaults()
316 }
317 abs := flags.Bool("abs", false, "Print the absolute path of the value")
318 flags.Parse(args)
319
320 if flags.NArg() != 1 {
321 flags.Usage()
322 os.Exit(1)
323 }
324
325 varName := flags.Arg(0)
326 if varName == "report_config" {
327 varData, err := build.DumpMakeVars(ctx, config, nil, build.BannerVars)
328 if err != nil {
329 ctx.Fatal(err)
330 }
331
332 fmt.Println(build.Banner(varData))
333 } else {
334 varData, err := build.DumpMakeVars(ctx, config, nil, []string{varName})
335 if err != nil {
336 ctx.Fatal(err)
337 }
338
339 if *abs {
340 var res []string
341 for _, path := range strings.Fields(varData[varName]) {
342 if abs, err := filepath.Abs(path); err == nil {
343 res = append(res, abs)
344 } else {
345 ctx.Fatalln("Failed to get absolute path of", path, err)
346 }
347 }
348 fmt.Println(strings.Join(res, " "))
349 } else {
350 fmt.Println(varData[varName])
351 }
352 }
353}
354
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700355func dumpVars(ctx build.Context, config build.Config, args []string, _ string) {
Dan Willemsen051133b2017-07-14 11:29:29 -0700356 flags := flag.NewFlagSet("dumpvars", flag.ExitOnError)
357 flags.Usage = func() {
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700358 fmt.Fprintf(ctx.Writer, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0])
359 fmt.Fprintln(ctx.Writer, "In dumpvars mode, dump the values of one or more legacy make variables, in")
360 fmt.Fprintln(ctx.Writer, "shell syntax. The resulting output may be sourced directly into a shell to")
361 fmt.Fprintln(ctx.Writer, "set corresponding shell variables.")
362 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700363
Patrice Arrudadb4c2f12019-06-17 17:27:09 -0700364 fmt.Fprintln(ctx.Writer, "'report_config' is a special case that dumps a variable containing the")
365 fmt.Fprintln(ctx.Writer, "human-readable config banner from the beginning of the build.")
366 fmt.Fprintln(ctx.Writer, "")
Dan Willemsen051133b2017-07-14 11:29:29 -0700367 flags.PrintDefaults()
368 }
369
370 varsStr := flags.String("vars", "", "Space-separated list of variables to dump")
371 absVarsStr := flags.String("abs-vars", "", "Space-separated list of variables to dump (using absolute paths)")
372
373 varPrefix := flags.String("var-prefix", "", "String to prepend to all variable names when dumping")
374 absVarPrefix := flags.String("abs-var-prefix", "", "String to prepent to all absolute path variable names when dumping")
375
376 flags.Parse(args)
377
378 if flags.NArg() != 0 {
379 flags.Usage()
380 os.Exit(1)
381 }
382
383 vars := strings.Fields(*varsStr)
384 absVars := strings.Fields(*absVarsStr)
385
386 allVars := append([]string{}, vars...)
387 allVars = append(allVars, absVars...)
388
389 if i := indexList("report_config", allVars); i != -1 {
390 allVars = append(allVars[:i], allVars[i+1:]...)
391 allVars = append(allVars, build.BannerVars...)
392 }
393
394 if len(allVars) == 0 {
395 return
396 }
397
398 varData, err := build.DumpMakeVars(ctx, config, nil, allVars)
399 if err != nil {
400 ctx.Fatal(err)
401 }
402
403 for _, name := range vars {
404 if name == "report_config" {
405 fmt.Printf("%sreport_config='%s'\n", *varPrefix, build.Banner(varData))
406 } else {
407 fmt.Printf("%s%s='%s'\n", *varPrefix, name, varData[name])
408 }
409 }
410 for _, name := range absVars {
411 var res []string
412 for _, path := range strings.Fields(varData[name]) {
413 abs, err := filepath.Abs(path)
414 if err != nil {
415 ctx.Fatalln("Failed to get absolute path of", path, err)
416 }
417 res = append(res, abs)
418 }
419 fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " "))
420 }
Dan Willemsen1e704462016-08-21 15:17:17 -0700421}
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700422
Patrice Arrudab7b22822019-05-21 17:46:23 -0700423func stdio() terminal.StdioInterface {
424 return terminal.StdioImpl{}
425}
426
Jaewoong Jung9f98d3f2020-11-17 18:20:14 -0800427// dumpvar and dumpvars use stdout to output variable values, so use stderr instead of stdout when
428// reporting events to keep stdout clean from noise.
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700429func customStdio() terminal.StdioInterface {
430 return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr)
431}
432
433// dumpVarConfig does not require any arguments to be parsed by the NewConfig.
434func dumpVarConfig(ctx build.Context, args ...string) build.Config {
435 return build.NewConfig(ctx)
436}
437
Patrice Arrudab7b22822019-05-21 17:46:23 -0700438func buildActionConfig(ctx build.Context, args ...string) build.Config {
439 flags := flag.NewFlagSet("build-mode", flag.ContinueOnError)
440 flags.Usage = func() {
441 fmt.Fprintf(ctx.Writer, "usage: %s --build-mode --dir=<path> <build action> [<build arg 1> <build arg 2> ...]\n\n", os.Args[0])
442 fmt.Fprintln(ctx.Writer, "In build mode, build the set of modules based on the specified build")
443 fmt.Fprintln(ctx.Writer, "action. The --dir flag is required to determine what is needed to")
444 fmt.Fprintln(ctx.Writer, "build in the source tree based on the build action. See below for")
445 fmt.Fprintln(ctx.Writer, "the list of acceptable build action flags.")
446 fmt.Fprintln(ctx.Writer, "")
447 flags.PrintDefaults()
448 }
449
450 buildActionFlags := []struct {
Dan Willemsence41e942019-07-29 23:39:30 -0700451 name string
452 description string
453 action build.BuildAction
454 set bool
Patrice Arrudab7b22822019-05-21 17:46:23 -0700455 }{{
Dan Willemsence41e942019-07-29 23:39:30 -0700456 name: "all-modules",
457 description: "Build action: build from the top of the source tree.",
458 action: build.BUILD_MODULES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700459 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700460 // This is redirecting to mma build command behaviour. Once it has soaked for a
461 // while, the build command is deleted from here once it has been removed from the
462 // envsetup.sh.
463 name: "modules-in-a-dir-no-deps",
464 description: "Build action: builds all of the modules in the current directory without their dependencies.",
465 action: build.BUILD_MODULES_IN_A_DIRECTORY,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700466 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700467 // This is redirecting to mmma build command behaviour. Once it has soaked for a
468 // while, the build command is deleted from here once it has been removed from the
469 // envsetup.sh.
470 name: "modules-in-dirs-no-deps",
471 description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
472 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700473 }, {
Dan Willemsence41e942019-07-29 23:39:30 -0700474 name: "modules-in-a-dir",
475 description: "Build action: builds all of the modules in the current directory and 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 name: "modules-in-dirs",
479 description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
480 action: build.BUILD_MODULES_IN_DIRECTORIES,
Patrice Arrudab7b22822019-05-21 17:46:23 -0700481 }}
482 for i, flag := range buildActionFlags {
483 flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
484 }
485 dir := flags.String("dir", "", "Directory of the executed build command.")
486
487 // Only interested in the first two args which defines the build action and the directory.
488 // The remaining arguments are passed down to the config.
489 const numBuildActionFlags = 2
490 if len(args) < numBuildActionFlags {
491 flags.Usage()
492 ctx.Fatalln("Improper build action arguments.")
493 }
494 flags.Parse(args[0:numBuildActionFlags])
495
496 // The next block of code is to validate that exactly one build action is set and the dir flag
497 // is specified.
498 buildActionCount := 0
499 var buildAction build.BuildAction
Patrice Arrudab7b22822019-05-21 17:46:23 -0700500 for _, flag := range buildActionFlags {
501 if flag.set {
502 buildActionCount++
503 buildAction = flag.action
Patrice Arrudab7b22822019-05-21 17:46:23 -0700504 }
505 }
506 if buildActionCount != 1 {
507 ctx.Fatalln("Build action not defined.")
508 }
509 if *dir == "" {
510 ctx.Fatalln("-dir not specified.")
511 }
512
513 // Remove the build action flags from the args as they are not recognized by the config.
514 args = args[numBuildActionFlags:]
Dan Willemsence41e942019-07-29 23:39:30 -0700515 return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
Patrice Arrudab7b22822019-05-21 17:46:23 -0700516}
517
Lukacs T. Berkid1e3f1f2021-03-16 08:55:23 +0100518func runMake(ctx build.Context, config build.Config, _ []string, logsDir string) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700519 if config.IsVerbose() {
520 writer := ctx.Writer
Colin Cross097ed2a2019-06-08 21:48:58 -0700521 fmt.Fprintln(writer, "! The argument `showcommands` is no longer supported.")
522 fmt.Fprintln(writer, "! Instead, the verbose log is always written to a compressed file in the output dir:")
523 fmt.Fprintln(writer, "!")
524 fmt.Fprintf(writer, "! gzip -cd %s/verbose.log.gz | less -R\n", logsDir)
525 fmt.Fprintln(writer, "!")
526 fmt.Fprintln(writer, "! Older versions are saved in verbose.log.#.gz files")
527 fmt.Fprintln(writer, "")
Dan Willemsenc6360832019-07-25 14:07:36 -0700528 select {
529 case <-time.After(5 * time.Second):
530 case <-ctx.Done():
531 return
532 }
533 }
534
535 if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
536 writer := ctx.Writer
Dan Willemsence41e942019-07-29 23:39:30 -0700537 fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
Dan Willemsenc6360832019-07-25 14:07:36 -0700538 fmt.Fprintln(writer, "!")
539 fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
540 fmt.Fprintln(writer, "!")
541 fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
542 fmt.Fprintln(writer, "")
Dan Willemsence41e942019-07-29 23:39:30 -0700543 ctx.Fatal("done")
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700544 }
545
Anton Hansson5a7861a2021-06-04 10:09:01 +0100546 build.Build(ctx, config)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700547}
548
549// getCommand finds the appropriate command based on args[1] flag. args[0]
550// is the soong_ui filename.
Liz Kammer0e7993e2020-10-15 11:07:13 -0700551func getCommand(args []string) (*command, []string, error) {
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700552 if len(args) < 2 {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700553 return nil, nil, fmt.Errorf("Too few arguments: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700554 }
555
556 for _, c := range commands {
557 if c.flag == args[1] {
Liz Kammer0e7993e2020-10-15 11:07:13 -0700558 return &c, args[2:], nil
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700559 }
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700560 }
561
562 // command not found
Liz Kammer0e7993e2020-10-15 11:07:13 -0700563 return nil, nil, fmt.Errorf("Command not found: %q", args)
Patrice Arrudaa5c25422019-04-09 18:49:49 -0700564}
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000565
566// For Bazel support, this moves files and directories from e.g. out/dist/$f to DIST_DIR/$f if necessary.
567func populateExternalDistDir(ctx build.Context, config build.Config) {
568 // Make sure that internalDistDirPath and externalDistDirPath are both absolute paths, so we can compare them
569 var err error
570 var internalDistDirPath string
571 var externalDistDirPath string
572 if internalDistDirPath, err = filepath.Abs(config.DistDir()); err != nil {
573 ctx.Fatalf("Unable to find absolute path of %s: %s", internalDistDirPath, err)
574 }
575 if externalDistDirPath, err = filepath.Abs(config.RealDistDir()); err != nil {
576 ctx.Fatalf("Unable to find absolute path of %s: %s", externalDistDirPath, err)
577 }
578 if externalDistDirPath == internalDistDirPath {
579 return
580 }
581
Rupert Shuttleworth534f1572020-12-16 23:07:06 +0000582 // Make sure the internal DIST_DIR actually exists before trying to read from it
583 if _, err = os.Stat(internalDistDirPath); os.IsNotExist(err) {
584 ctx.Println("Skipping Bazel dist dir migration - nothing to do!")
585 return
586 }
587
Rupert Shuttleworth3c9f5ac2020-12-10 11:32:38 +0000588 // Make sure the external DIST_DIR actually exists before trying to write to it
589 if err = os.MkdirAll(externalDistDirPath, 0755); err != nil {
590 ctx.Fatalf("Unable to make directory %s: %s", externalDistDirPath, err)
591 }
592
593 ctx.Println("Populating external DIST_DIR...")
594
595 populateExternalDistDirHelper(ctx, config, internalDistDirPath, externalDistDirPath)
596}
597
598func populateExternalDistDirHelper(ctx build.Context, config build.Config, internalDistDirPath string, externalDistDirPath string) {
599 files, err := ioutil.ReadDir(internalDistDirPath)
600 if err != nil {
601 ctx.Fatalf("Can't read internal distdir %s: %s", internalDistDirPath, err)
602 }
603 for _, f := range files {
604 internalFilePath := filepath.Join(internalDistDirPath, f.Name())
605 externalFilePath := filepath.Join(externalDistDirPath, f.Name())
606
607 if f.IsDir() {
608 // Moving a directory - check if there is an existing directory to merge with
609 externalLstat, err := os.Lstat(externalFilePath)
610 if err != nil {
611 if !os.IsNotExist(err) {
612 ctx.Fatalf("Can't lstat external %s: %s", externalDistDirPath, err)
613 }
614 // Otherwise, if the error was os.IsNotExist, that's fine and we fall through to the rename at the bottom
615 } else {
616 if externalLstat.IsDir() {
617 // Existing dir - try to merge the directories?
618 populateExternalDistDirHelper(ctx, config, internalFilePath, externalFilePath)
619 continue
620 } else {
621 // Existing file being replaced with a directory. Delete the existing file...
622 if err := os.RemoveAll(externalFilePath); err != nil {
623 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
624 }
625 }
626 }
627 } else {
628 // Moving a file (not a dir) - delete any existing file or directory
629 if err := os.RemoveAll(externalFilePath); err != nil {
630 ctx.Fatalf("Unable to remove existing %s: %s", externalFilePath, err)
631 }
632 }
633
634 // The actual move - do a rename instead of a copy in order to save disk space.
635 if err := os.Rename(internalFilePath, externalFilePath); err != nil {
636 ctx.Fatalf("Unable to rename %s -> %s due to error %s", internalFilePath, externalFilePath, err)
637 }
638 }
639}