From b1878379ff9ed881bdd9729db9b2f440bfa12f81 Mon Sep 17 00:00:00 2001 From: Patrice Arruda Date: Tue, 9 Apr 2019 18:49:49 -0700 Subject: Soong: Refactor the soong_ui arguments processing to be more like bazel. Currently, the command line argments is being processed in multiple places. In the main soong_ui arguments, there are several if statements that checks if a specific argument was specified in order to execute the requested operation. This does not scale well when adding or removing a command in the near future. In order to support the build commands (m, mma, etc...) from the envsetup.sh in soong_ui, a refactor was required in order to add a command rather quickly and to have the flexibiity to unit test the command. The soong_ui arguments format is as follows: soong_ui [ ... ] The is a specific operation to be executed. The arguments after the command are processed by the command itself. Below is the list of changes made in this commit: * Created a new command infrastructure that allows adding or deprecating a command easily. * Fixed a bug when running ./soong_ui.bash directly would cause a panic due to index being out of range for args list. Bug: b/130049705 Test: Below is the list of testing done on this commit: * Ran lunch and verified that the output is the same as the the output without the modifications. lunch indirectly runs soong_ui passing in the --dumpvar-mode (to read makefile variables such as TARGET_PRODUCT) and --dumpvars-mode (to build the build cache). * Ran ./soong_ui.bash directly (with unsupported flags and no flags) and the proper message appeared that soong native UI is not yet available. * Ran m, mm, mmm, mma, mmma commands. * Ran the make installclean command: make -j80 TARGET_PRODUCT=aosp_arm64 TARGET_BUILD_VARIANT=eng dist DIST_DIR=/tmp/helloworld installclean * Ran ./out/soong_ui -j80 --make-mode PRODUCT-aosp_arm64-eng dist checkbuild tests Change-Id: Iee4de7ec0fa4661206fda8ae1fe6fa4487d9bb22 Merged-In: Iee4de7ec0fa4661206fda8ae1fe6fa4487d9bb22 --- cmd/soong_ui/main.go | 168 +++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 123 insertions(+), 45 deletions(-) diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go index 9f40e33e..4500eb15 100644 --- a/cmd/soong_ui/main.go +++ b/cmd/soong_ui/main.go @@ -32,42 +32,89 @@ import ( "android/soong/ui/tracer" ) +// A command represents an operation to be executed in the soong build +// system. +type command struct { + // the flag name (must have double dashes) + flag string + + // description for the flag (to display when running help) + description string + + // Creates the build configuration based on the args and build context. + config func(ctx build.Context, args ...string) build.Config + + // Returns what type of IO redirection this Command requires. + stdio func() terminal.StdioInterface + + // run the command + run func(ctx build.Context, config build.Config, args []string, logsDir string) +} + +const makeModeFlagName = "--make-mode" + +// list of supported commands (flags) supported by soong ui +var commands []command = []command{ + { + flag: makeModeFlagName, + description: "build the modules by the target name (i.e. soong_docs)", + config: func(ctx build.Context, args ...string) build.Config { + return build.NewConfig(ctx, args...) + }, + stdio: func() terminal.StdioInterface { + return terminal.StdioImpl{} + }, + run: make, + }, { + flag: "--dumpvar-mode", + description: "print the value of the legacy make variable VAR to stdout", + config: dumpVarConfig, + stdio: customStdio, + run: dumpVar, + }, { + flag: "--dumpvars-mode", + description: "dump the values of one or more legacy make variables, in shell syntax", + config: dumpVarConfig, + stdio: customStdio, + run: dumpVars, + }, +} + +// indexList returns the index of first found s. -1 is return if s is not +// found. func indexList(s string, list []string) int { for i, l := range list { if l == s { return i } } - return -1 } +// inList returns true if one or more of s is in the list. func inList(s string, list []string) bool { return indexList(s, list) != -1 } +// Main execution of soong_ui. The command format is as follows: +// +// soong_ui [ ... ] +// +// Command is the type of soong_ui execution. Only one type of +// execution is specified. The args are specific to the command. func main() { - var stdio terminal.StdioInterface - stdio = terminal.StdioImpl{} - - // dumpvar uses stdout, everything else should be in stderr - if os.Args[1] == "--dumpvar-mode" || os.Args[1] == "--dumpvars-mode" { - stdio = terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr) + c, args := getCommand(os.Args) + if c == nil { + fmt.Fprintf(os.Stderr, "The `soong` native UI is not yet available.\n") + os.Exit(1) } - writer := terminal.NewWriter(stdio) + writer := terminal.NewWriter(c.stdio()) defer writer.Finish() log := logger.New(writer) defer log.Cleanup() - if len(os.Args) < 2 || !(inList("--make-mode", os.Args) || - os.Args[1] == "--dumpvars-mode" || - os.Args[1] == "--dumpvar-mode") { - - log.Fatalln("The `soong` native UI is not yet available.") - } - ctx, cancel := context.WithCancel(context.Background()) defer cancel() @@ -96,12 +143,8 @@ func main() { Writer: writer, Status: stat, }} - var config build.Config - if os.Args[1] == "--dumpvars-mode" || os.Args[1] == "--dumpvar-mode" { - config = build.NewConfig(buildCtx) - } else { - config = build.NewConfig(buildCtx, os.Args[1:]...) - } + + config := c.config(buildCtx, args...) build.SetupOutDir(buildCtx, config) @@ -141,28 +184,7 @@ func main() { defer f.Shutdown() build.FindSources(buildCtx, config, f) - if os.Args[1] == "--dumpvar-mode" { - dumpVar(buildCtx, config, os.Args[2:]) - } else if os.Args[1] == "--dumpvars-mode" { - dumpVars(buildCtx, config, os.Args[2:]) - } else { - if config.IsVerbose() { - writer.Print("! The argument `showcommands` is no longer supported.") - writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:") - writer.Print("!") - writer.Print(fmt.Sprintf("! gzip -cd %s/verbose.log.gz | less -R", logsDir)) - writer.Print("!") - writer.Print("! Older versions are saved in verbose.log.#.gz files") - writer.Print("") - time.Sleep(5 * time.Second) - } - - toBuild := build.BuildAll - if config.Checkbuild() { - toBuild |= build.RunBuildTests - } - build.Build(buildCtx, config, toBuild) - } + c.run(buildCtx, config, args, logsDir) } func fixBadDanglingLink(ctx build.Context, name string) { @@ -179,7 +201,7 @@ func fixBadDanglingLink(ctx build.Context, name string) { } } -func dumpVar(ctx build.Context, config build.Config, args []string) { +func dumpVar(ctx build.Context, config build.Config, args []string, _ string) { flags := flag.NewFlagSet("dumpvar", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, "usage: %s --dumpvar-mode [--abs] \n\n", os.Args[0]) @@ -229,7 +251,7 @@ func dumpVar(ctx build.Context, config build.Config, args []string) { } } -func dumpVars(ctx build.Context, config build.Config, args []string) { +func dumpVars(ctx build.Context, config build.Config, args []string, _ string) { flags := flag.NewFlagSet("dumpvars", flag.ExitOnError) flags.Usage = func() { fmt.Fprintf(os.Stderr, "usage: %s --dumpvars-mode [--vars=\"VAR VAR ...\"]\n\n", os.Args[0]) @@ -296,3 +318,59 @@ func dumpVars(ctx build.Context, config build.Config, args []string) { fmt.Printf("%s%s='%s'\n", *absVarPrefix, name, strings.Join(res, " ")) } } + +func customStdio() terminal.StdioInterface { + return terminal.NewCustomStdio(os.Stdin, os.Stderr, os.Stderr) +} + +// dumpVarConfig does not require any arguments to be parsed by the NewConfig. +func dumpVarConfig(ctx build.Context, args ...string) build.Config { + return build.NewConfig(ctx) +} + +func make(ctx build.Context, config build.Config, _ []string, logsDir string) { + if config.IsVerbose() { + writer := ctx.Writer + writer.Print("! The argument `showcommands` is no longer supported.") + writer.Print("! Instead, the verbose log is always written to a compressed file in the output dir:") + writer.Print("!") + writer.Print(fmt.Sprintf("! gzip -cd %s/verbose.log.gz | less -R", logsDir)) + writer.Print("!") + writer.Print("! Older versions are saved in verbose.log.#.gz files") + writer.Print("") + time.Sleep(5 * time.Second) + } + + toBuild := build.BuildAll + if config.Checkbuild() { + toBuild |= build.RunBuildTests + } + build.Build(ctx, config, toBuild) +} + +// getCommand finds the appropriate command based on args[1] flag. args[0] +// is the soong_ui filename. +func getCommand(args []string) (*command, []string) { + if len(args) < 2 { + return nil, args + } + + for _, c := range commands { + if c.flag == args[1] { + return &c, args[2:] + } + + // special case for --make-mode: if soong_ui was called from + // build/make/core/main.mk, the makeparallel with --ninja + // option specified puts the -j before --make-mode. + // TODO: Remove this hack once it has been fixed. + if c.flag == makeModeFlagName { + if inList(makeModeFlagName, args) { + return &c, args[1:] + } + } + } + + // command not found + return nil, args +} -- cgit v1.2.3