Merge "Track sources for srcjars across modules"
diff --git a/cc/config/global.go b/cc/config/global.go
index 0a7d984..24075d9 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -84,6 +84,7 @@
 		"-Wl,--fatal-warnings",
 		"-Wl,--no-undefined-version",
 		"-Wl,--exclude-libs,libgcc.a",
+		"-Wl,--exclude-libs,libgcc_stripped.a",
 	}
 
 	deviceGlobalLldflags = append(ClangFilterUnknownLldflags(deviceGlobalLdflags),
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index d6999c5..5f9bd01 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -32,42 +32,89 @@
 	"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> [<arg 1> <arg 2> ... <arg n>]
+//
+// 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 @@
 		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)
 
@@ -136,31 +179,10 @@
 	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 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] <VAR>\n\n", os.Args[0])
@@ -210,7 +232,7 @@
 	}
 }
 
-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])
@@ -277,3 +299,59 @@
 		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<num> 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
+}
diff --git a/java/app.go b/java/app.go
index 4f0b7ac..ec021fc 100644
--- a/java/app.go
+++ b/java/app.go
@@ -188,19 +188,12 @@
 		return false
 	}
 
-	// Uncompress dex in APKs of privileged apps, and modules used by privileged apps.
-	if ctx.Config().UncompressPrivAppDex() &&
-		(Bool(a.appProperties.Privileged) ||
-			inList(ctx.ModuleName(), ctx.Config().ModulesLoadedByPrivilegedModules())) {
+	// Uncompress dex in APKs of privileged apps
+	if ctx.Config().UncompressPrivAppDex() && Bool(a.appProperties.Privileged) {
 		return true
 	}
 
-	// Uncompress if the dex files is preopted on /system.
-	if !a.dexpreopter.dexpreoptDisabled(ctx) && (ctx.Host() || !odexOnSystemOther(ctx, a.dexpreopter.installPath)) {
-		return true
-	}
-
-	return false
+	return shouldUncompressDex(ctx, &a.dexpreopter)
 }
 
 func (a *AndroidApp) aaptBuildActions(ctx android.ModuleContext) {
@@ -683,6 +676,20 @@
 	rule.Build(pctx, ctx, "uncompress-embedded-jni-libs", "Uncompress embedded JIN libs")
 }
 
+// Returns whether this module should have the dex file stored uncompressed in the APK.
+func (a *AndroidAppImport) shouldUncompressDex(ctx android.ModuleContext) bool {
+	if ctx.Config().UnbundledBuild() {
+		return false
+	}
+
+	// Uncompress dex in APKs of privileged apps
+	if ctx.Config().UncompressPrivAppDex() && Bool(a.properties.Privileged) {
+		return true
+	}
+
+	return shouldUncompressDex(ctx, &a.dexpreopter)
+}
+
 func (a *AndroidAppImport) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	if String(a.properties.Certificate) == "" && !Bool(a.properties.Presigned) {
 		ctx.PropertyErrorf("certificate", "No certificate specified for prebuilt")
@@ -705,12 +712,11 @@
 	jnisUncompressed := android.PathForModuleOut(ctx, "jnis-uncompressed", ctx.ModuleName()+".apk")
 	a.uncompressEmbeddedJniLibs(ctx, srcApk, jnisUncompressed.OutputPath)
 
-	// TODO: Uncompress dex if applicable
-
 	installDir := android.PathForModuleInstall(ctx, "app", a.BaseModuleName())
 	a.dexpreopter.installPath = installDir.Join(ctx, a.BaseModuleName()+".apk")
 	a.dexpreopter.isInstallable = true
 	a.dexpreopter.isPresignedPrebuilt = Bool(a.properties.Presigned)
+	a.dexpreopter.uncompressedDex = a.shouldUncompressDex(ctx)
 	dexOutput := a.dexpreopter.dexpreopt(ctx, jnisUncompressed)
 
 	// Sign or align the package