Merge changes I918b4878,I85238d93,Iefee8a91

* changes:
  Add an output file tag for proguard dictionaries
  Add InstallBypassMake
  Document wokaround for yama ptrace restrictions
diff --git a/Android.bp b/Android.bp
index 62b0fd4..4e44a0d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -282,6 +282,7 @@
         "java/jdeps.go",
         "java/java_resources.go",
         "java/kotlin.go",
+        "java/platform_compat_config.go",
         "java/plugin.go",
         "java/prebuilt_apis.go",
         "java/proto.go",
diff --git a/android/neverallow.go b/android/neverallow.go
index 23b6454..8355bb3 100644
--- a/android/neverallow.go
+++ b/android/neverallow.go
@@ -175,6 +175,8 @@
 	dir := ctx.ModuleDir() + "/"
 	properties := m.GetProperties()
 
+	osClass := ctx.Module().Target().Os.Class
+
 	for _, r := range neverallows {
 		n := r.(*rule)
 		if !n.appliesToPath(dir) {
@@ -189,6 +191,14 @@
 			continue
 		}
 
+		if !n.appliesToOsClass(osClass) {
+			continue
+		}
+
+		if !n.appliesToDirectDeps(ctx) {
+			continue
+		}
+
 		ctx.ModuleErrorf("violates " + n.String())
 	}
 }
@@ -246,6 +256,10 @@
 
 	NotIn(path ...string) Rule
 
+	InDirectDeps(deps ...string) Rule
+
+	WithOsClass(osClasses ...OsClass) Rule
+
 	ModuleType(types ...string) Rule
 
 	NotModuleType(types ...string) Rule
@@ -268,6 +282,10 @@
 	paths       []string
 	unlessPaths []string
 
+	directDeps map[string]bool
+
+	osClasses []OsClass
+
 	moduleTypes       []string
 	unlessModuleTypes []string
 
@@ -277,7 +295,7 @@
 
 // Create a new NeverAllow rule.
 func NeverAllow() Rule {
-	return &rule{}
+	return &rule{directDeps: make(map[string]bool)}
 }
 
 func (r *rule) In(path ...string) Rule {
@@ -290,6 +308,18 @@
 	return r
 }
 
+func (r *rule) InDirectDeps(deps ...string) Rule {
+	for _, d := range deps {
+		r.directDeps[d] = true
+	}
+	return r
+}
+
+func (r *rule) WithOsClass(osClasses ...OsClass) Rule {
+	r.osClasses = append(r.osClasses, osClasses...)
+	return r
+}
+
 func (r *rule) ModuleType(types ...string) Rule {
 	r.moduleTypes = append(r.moduleTypes, types...)
 	return r
@@ -356,6 +386,12 @@
 	for _, v := range r.unlessProps {
 		s += " -" + strings.Join(v.fields, ".") + v.matcher.String()
 	}
+	for k := range r.directDeps {
+		s += " deps:" + k
+	}
+	for _, v := range r.osClasses {
+		s += " os:" + v.String()
+	}
 	if len(r.reason) != 0 {
 		s += " which is restricted because " + r.reason
 	}
@@ -368,6 +404,36 @@
 	return includePath && !excludePath
 }
 
+func (r *rule) appliesToDirectDeps(ctx BottomUpMutatorContext) bool {
+	if len(r.directDeps) == 0 {
+		return true
+	}
+
+	matches := false
+	ctx.VisitDirectDeps(func(m Module) {
+		if !matches {
+			name := ctx.OtherModuleName(m)
+			matches = r.directDeps[name]
+		}
+	})
+
+	return matches
+}
+
+func (r *rule) appliesToOsClass(osClass OsClass) bool {
+	if len(r.osClasses) == 0 {
+		return true
+	}
+
+	for _, c := range r.osClasses {
+		if c == osClass {
+			return true
+		}
+	}
+
+	return false
+}
+
 func (r *rule) appliesToModuleType(moduleType string) bool {
 	return (len(r.moduleTypes) == 0 || InList(moduleType, r.moduleTypes)) && !InList(moduleType, r.unlessModuleTypes)
 }
diff --git a/android/neverallow_test.go b/android/neverallow_test.go
index 02b4362..920b9a5 100644
--- a/android/neverallow_test.go
+++ b/android/neverallow_test.go
@@ -16,13 +16,43 @@
 
 import (
 	"testing"
+
+	"github.com/google/blueprint"
 )
 
+func init() {
+	// Add extra rules needed for testing.
+	AddNeverAllowRules(
+		NeverAllow().InDirectDeps("not_allowed_in_direct_deps"),
+	)
+}
+
 var neverallowTests = []struct {
 	name          string
 	fs            map[string][]byte
 	expectedError string
 }{
+	// Test General Functionality
+
+	// in direct deps tests
+	{
+		name: "not_allowed_in_direct_deps",
+		fs: map[string][]byte{
+			"top/Blueprints": []byte(`
+				cc_library {
+					name: "not_allowed_in_direct_deps",
+				}`),
+			"other/Blueprints": []byte(`
+				cc_library {
+					name: "libother",
+					static_libs: ["not_allowed_in_direct_deps"],
+				}`),
+		},
+		expectedError: `module "libother": violates neverallow deps:not_allowed_in_direct_deps`,
+	},
+
+	// Test specific rules
+
 	// include_dir rule tests
 	{
 		name: "include_dir not allowed to reference art",
@@ -242,6 +272,7 @@
 type mockCcLibraryProperties struct {
 	Include_dirs     []string
 	Vendor_available *bool
+	Static_libs      []string
 
 	Vndk struct {
 		Enabled                *bool
@@ -272,6 +303,19 @@
 	return m
 }
 
+type neverallowTestDependencyTag struct {
+	blueprint.BaseDependencyTag
+	name string
+}
+
+var staticDepTag = neverallowTestDependencyTag{name: "static"}
+
+func (c *mockCcLibraryModule) DepsMutator(ctx BottomUpMutatorContext) {
+	for _, lib := range c.properties.Static_libs {
+		ctx.AddDependency(ctx.Module(), staticDepTag, lib)
+	}
+}
+
 func (p *mockCcLibraryModule) GenerateAndroidBuildActions(ModuleContext) {
 }
 
diff --git a/android/util.go b/android/util.go
index 97bec10..e02cca1 100644
--- a/android/util.go
+++ b/android/util.go
@@ -199,6 +199,13 @@
 	return list[totalSkip:]
 }
 
+// SortedUniqueStrings returns what the name says
+func SortedUniqueStrings(list []string) []string {
+	unique := FirstUniqueStrings(list)
+	sort.Strings(unique)
+	return unique
+}
+
 // checkCalledFromInit panics if a Go package's init function is not on the
 // call stack.
 func checkCalledFromInit() {
diff --git a/android/variable.go b/android/variable.go
index e9379b7..bfff81c 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -59,17 +59,6 @@
 			Cflags []string
 		}
 
-		// Product_is_iot is true for Android Things devices.
-		Product_is_iot struct {
-			Cflags       []string
-			Enabled      bool
-			Exclude_srcs []string
-			Init_rc      []string
-			Shared_libs  []string
-			Srcs         []string
-			Static_libs  []string
-		}
-
 		// treble_linker_namespaces is true when the system/vendor linker namespace separation is
 		// enabled.
 		Treble_linker_namespaces struct {
@@ -262,8 +251,6 @@
 
 	Override_rs_driver *string `json:",omitempty"`
 
-	Product_is_iot *bool `json:",omitempty"`
-
 	Fuchsia *bool `json:",omitempty"`
 
 	DeviceKernelHeaders []string `json:",omitempty"`
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
new file mode 100644
index 0000000..d0223d1
--- /dev/null
+++ b/apex/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "system/apex/apexd"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/apex/apex.go b/apex/apex.go
index 441911b..e4fad83 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -46,13 +46,21 @@
 		Description: "fs_config ${out}",
 	}, "ro_paths", "exec_paths")
 
+	injectApexDependency = pctx.StaticRule("injectApexDependency", blueprint.RuleParams{
+		Command: `rm -f $out && ${jsonmodify} $in ` +
+			`-a provideNativeLibs ${provideNativeLibs} ` +
+			`-a requireNativeLibs ${requireNativeLibs} -o $out`,
+		CommandDeps: []string{"${jsonmodify}"},
+		Description: "Inject dependency into ${out}",
+	}, "provideNativeLibs", "requireNativeLibs")
+
 	// TODO(b/113233103): make sure that file_contexts is sane, i.e., validate
 	// against the binary policy using sefcontext_compiler -p <policy>.
 
 	// TODO(b/114327326): automate the generation of file_contexts
 	apexRule = pctx.StaticRule("apexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
-			`(${copy_commands}) && ` +
+			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
 			`--file_contexts ${file_contexts} ` +
@@ -62,18 +70,22 @@
 		CommandDeps: []string{"${apexer}", "${avbtool}", "${e2fsdroid}", "${merge_zips}",
 			"${mke2fs}", "${resize2fs}", "${sefcontext_compile}",
 			"${soong_zip}", "${zipalign}", "${aapt2}", "prebuilts/sdk/current/public/android.jar"},
-		Description: "APEX ${image_dir} => ${out}",
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "APEX ${image_dir} => ${out}",
 	}, "tool_path", "image_dir", "copy_commands", "manifest", "file_contexts", "canned_fs_config", "key", "opt_flags")
 
 	zipApexRule = pctx.StaticRule("zipApexRule", blueprint.RuleParams{
 		Command: `rm -rf ${image_dir} && mkdir -p ${image_dir} && ` +
-			`(${copy_commands}) && ` +
+			`(. ${out}.copy_commands) && ` +
 			`APEXER_TOOL_PATH=${tool_path} ` +
 			`${apexer} --force --manifest ${manifest} ` +
 			`--payload_type zip ` +
 			`${image_dir} ${out} `,
-		CommandDeps: []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
-		Description: "ZipAPEX ${image_dir} => ${out}",
+		CommandDeps:    []string{"${apexer}", "${merge_zips}", "${soong_zip}", "${zipalign}", "${aapt2}"},
+		Rspfile:        "${out}.copy_commands",
+		RspfileContent: "${copy_commands}",
+		Description:    "ZipAPEX ${image_dir} => ${out}",
 	}, "tool_path", "image_dir", "copy_commands", "manifest")
 
 	apexProtoConvertRule = pctx.AndroidStaticRule("apexProtoConvertRule",
@@ -139,6 +151,7 @@
 	pctx.HostBinToolVariable("soong_zip", "soong_zip")
 	pctx.HostBinToolVariable("zip2zip", "zip2zip")
 	pctx.HostBinToolVariable("zipalign", "zipalign")
+	pctx.HostBinToolVariable("jsonmodify", "jsonmodify")
 
 	android.RegisterModuleType("apex", apexBundleFactory)
 	android.RegisterModuleType("apex_test", testApexBundleFactory)
@@ -427,6 +440,9 @@
 	flattened bool
 
 	testApex bool
+
+	// intermediate path for apex_manifest.json
+	manifestOut android.WritablePath
 }
 
 func addDependenciesForNativeModules(ctx android.BottomUpMutatorContext,
@@ -751,6 +767,10 @@
 
 	handleSpecialLibs := !android.Bool(a.properties.Ignore_system_library_special_case)
 
+	// native lib dependencies
+	var provideNativeLibs []string
+	var requireNativeLibs []string
+
 	// Check if "uses" requirements are met with dependent apexBundles
 	var providedNativeSharedLibs []string
 	useVendor := proptools.Bool(a.properties.Use_vendor)
@@ -783,6 +803,9 @@
 			switch depTag {
 			case sharedLibTag:
 				if cc, ok := child.(*cc.Module); ok {
+					if cc.HasStubsVariants() {
+						provideNativeLibs = append(provideNativeLibs, cc.OutputFile().Path().Base())
+					}
 					fileToCopy, dirInApex := getCopyManifestForNativeLibrary(cc, handleSpecialLibs)
 					filesInfo = append(filesInfo, apexFile{fileToCopy, depName, dirInApex, nativeSharedLib, cc, nil})
 					return true
@@ -894,6 +917,7 @@
 							if !android.DirectlyInAnyApex(ctx, cc.Name()) && !android.InList(cc.Name(), a.externalDeps) {
 								a.externalDeps = append(a.externalDeps, cc.Name())
 							}
+							requireNativeLibs = append(requireNativeLibs, cc.OutputFile().Path().Base())
 							// Don't track further
 							return false
 						}
@@ -954,6 +978,21 @@
 	a.installDir = android.PathForModuleInstall(ctx, "apex")
 	a.filesInfo = filesInfo
 
+	a.manifestOut = android.PathForModuleOut(ctx, "apex_manifest.json")
+	// put dependency({provide|require}NativeLibs) in apex_manifest.json
+	manifestSrc := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
+	provideNativeLibs = android.SortedUniqueStrings(provideNativeLibs)
+	requireNativeLibs = android.SortedUniqueStrings(android.RemoveListFromList(requireNativeLibs, provideNativeLibs))
+	ctx.Build(pctx, android.BuildParams{
+		Rule:   injectApexDependency,
+		Input:  manifestSrc,
+		Output: a.manifestOut,
+		Args: map[string]string{
+			"provideNativeLibs": strings.Join(provideNativeLibs, " "),
+			"requireNativeLibs": strings.Join(requireNativeLibs, " "),
+		},
+	})
+
 	if a.apexTypes.zip() {
 		a.buildUnflattenedApex(ctx, zipApex)
 	}
@@ -1001,8 +1040,6 @@
 		a.container_private_key_file = key
 	}
 
-	manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
-
 	var abis []string
 	for _, target := range ctx.MultiTargets() {
 		if len(target.Arch.Abi) > 0 {
@@ -1032,7 +1069,7 @@
 		}
 	}
 	implicitInputs := append(android.Paths(nil), filesToCopy...)
-	implicitInputs = append(implicitInputs, manifest)
+	implicitInputs = append(implicitInputs, a.manifestOut)
 
 	outHostBinDir := android.PathForOutput(ctx, "host", ctx.Config().PrebuiltOS(), "bin").String()
 	prebuiltSdkToolsBinDir := filepath.Join("prebuilts", "sdk", "tools", runtime.GOOS, "bin")
@@ -1127,7 +1164,7 @@
 				"tool_path":        outHostBinDir + ":" + prebuiltSdkToolsBinDir,
 				"image_dir":        android.PathForModuleOut(ctx, "image"+suffix).String(),
 				"copy_commands":    strings.Join(copyCommands, " && "),
-				"manifest":         manifest.String(),
+				"manifest":         a.manifestOut.String(),
 				"file_contexts":    fileContexts.String(),
 				"canned_fs_config": cannedFsConfig.String(),
 				"key":              a.private_key_file.String(),
@@ -1165,7 +1202,7 @@
 				"tool_path":     outHostBinDir + ":" + prebuiltSdkToolsBinDir,
 				"image_dir":     android.PathForModuleOut(ctx, "image"+suffix).String(),
 				"copy_commands": strings.Join(copyCommands, " && "),
-				"manifest":      manifest.String(),
+				"manifest":      a.manifestOut.String(),
 			},
 		})
 	}
@@ -1196,16 +1233,7 @@
 	if a.installable() {
 		// For flattened APEX, do nothing but make sure that apex_manifest.json and apex_pubkey are also copied along
 		// with other ordinary files.
-		manifest := android.PathForModuleSrc(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json"))
-
-		// rename to apex_manifest.json
-		copiedManifest := android.PathForModuleOut(ctx, "apex_manifest.json")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:   android.Cp,
-			Input:  manifest,
-			Output: copiedManifest,
-		})
-		a.filesInfo = append(a.filesInfo, apexFile{copiedManifest, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
+		a.filesInfo = append(a.filesInfo, apexFile{a.manifestOut, ctx.ModuleName() + ".apex_manifest.json", ".", etc, nil, nil})
 
 		// rename to apex_pubkey
 		copiedPubkey := android.PathForModuleOut(ctx, "apex_pubkey")
@@ -1493,6 +1521,10 @@
 	// to build the prebuilts themselves.
 	forceDisable = forceDisable || ctx.Config().UnbundledBuild()
 
+	// Force disable the prebuilts when coverage is enabled.
+	forceDisable = forceDisable || ctx.DeviceConfig().NativeCoverageEnabled()
+	forceDisable = forceDisable || ctx.Config().IsEnvTrue("EMMA_INSTRUMENT")
+
 	// b/137216042 don't use prebuilts when address sanitizer is on
 	forceDisable = forceDisable || android.InList("address", ctx.Config().SanitizeDevice()) ||
 		android.InList("hwaddress", ctx.Config().SanitizeDevice())
diff --git a/apex/apex_test.go b/apex/apex_test.go
index cecdaaf..38d2bf2 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -270,6 +270,13 @@
 	}
 }
 
+func ensureListEmpty(t *testing.T, result []string) {
+	t.Helper()
+	if len(result) > 0 {
+		t.Errorf("%q is expected to be empty", result)
+	}
+}
+
 // Minimal test
 func TestBasicApex(t *testing.T) {
 	ctx, _ := testApex(t, `
@@ -1060,6 +1067,109 @@
 	ensureContains(t, cFlags, "-Imy_include")
 }
 
+func TestDependenciesInApexManifest(t *testing.T) {
+	ctx, _ := testApex(t, `
+		apex {
+			name: "myapex_nodep",
+			key: "myapex.key",
+			native_shared_libs: ["lib_nodep"],
+			compile_multilib: "both",
+			file_contexts: "myapex",
+		}
+
+		apex {
+			name: "myapex_dep",
+			key: "myapex.key",
+			native_shared_libs: ["lib_dep"],
+			compile_multilib: "both",
+			file_contexts: "myapex",
+		}
+
+		apex {
+			name: "myapex_provider",
+			key: "myapex.key",
+			native_shared_libs: ["libfoo"],
+			compile_multilib: "both",
+			file_contexts: "myapex",
+		}
+
+		apex {
+			name: "myapex_selfcontained",
+			key: "myapex.key",
+			native_shared_libs: ["lib_dep", "libfoo"],
+			compile_multilib: "both",
+			file_contexts: "myapex",
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "lib_nodep",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "lib_dep",
+			srcs: ["mylib.cpp"],
+			shared_libs: ["libfoo"],
+			system_shared_libs: [],
+			stl: "none",
+		}
+
+		cc_library {
+			name: "libfoo",
+			srcs: ["mytest.cpp"],
+			stubs: {
+				versions: ["1"],
+			},
+			system_shared_libs: [],
+			stl: "none",
+		}
+	`)
+
+	names := func(s string) (ns []string) {
+		for _, n := range strings.Split(s, " ") {
+			if len(n) > 0 {
+				ns = append(ns, n)
+			}
+		}
+		return
+	}
+
+	var injectRule android.TestingBuildParams
+	var provideNativeLibs, requireNativeLibs []string
+
+	injectRule = ctx.ModuleForTests("myapex_nodep", "android_common_myapex_nodep").Rule("injectApexDependency")
+	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	ensureListEmpty(t, provideNativeLibs)
+	ensureListEmpty(t, requireNativeLibs)
+
+	injectRule = ctx.ModuleForTests("myapex_dep", "android_common_myapex_dep").Rule("injectApexDependency")
+	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	ensureListEmpty(t, provideNativeLibs)
+	ensureListContains(t, requireNativeLibs, "libfoo.so")
+
+	injectRule = ctx.ModuleForTests("myapex_provider", "android_common_myapex_provider").Rule("injectApexDependency")
+	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	ensureListContains(t, provideNativeLibs, "libfoo.so")
+	ensureListEmpty(t, requireNativeLibs)
+
+	injectRule = ctx.ModuleForTests("myapex_selfcontained", "android_common_myapex_selfcontained").Rule("injectApexDependency")
+	provideNativeLibs = names(injectRule.Args["provideNativeLibs"])
+	requireNativeLibs = names(injectRule.Args["requireNativeLibs"])
+	ensureListContains(t, provideNativeLibs, "libfoo.so")
+	ensureListEmpty(t, requireNativeLibs)
+}
+
 func TestNonTestApex(t *testing.T) {
 	ctx, _ := testApex(t, `
 		apex {
diff --git a/cc/cc.go b/cc/cc.go
index cc2e65f..2bde2d3 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -267,7 +267,7 @@
 	isVndkSp() bool
 	isVndkExt() bool
 	inRecovery() bool
-	shouldCreateVndkSourceAbiDump(config android.Config) bool
+	shouldCreateSourceAbiDump() bool
 	selectedStl() string
 	baseModuleName() string
 	getVndkExtendsModuleName() string
@@ -789,7 +789,7 @@
 }
 
 // Check whether ABI dumps should be created for this module.
-func (ctx *moduleContextImpl) shouldCreateVndkSourceAbiDump(config android.Config) bool {
+func (ctx *moduleContextImpl) shouldCreateSourceAbiDump() bool {
 	if ctx.ctx.Config().IsEnvTrue("SKIP_ABI_CHECKS") {
 		return false
 	}
@@ -815,18 +815,7 @@
 		// Stubs do not need ABI dumps.
 		return false
 	}
-	if ctx.isNdk() {
-		return true
-	}
-	if ctx.isLlndkPublic(config) {
-		return true
-	}
-	if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(config) {
-		// Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext and this is not
-		// VNDK-private.
-		return true
-	}
-	return false
+	return true
 }
 
 func (ctx *moduleContextImpl) selectedStl() string {
@@ -1898,7 +1887,7 @@
 			isVendorPublicLib := inList(libName, *vendorPublicLibraries)
 			bothVendorAndCoreVariantsExist := ccDep.hasVendorVariant() || isLLndk
 
-			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.isVndk() && !ccDep.mustUseVendorVariant() {
+			if ctx.DeviceConfig().VndkUseCoreVariant() && ccDep.isVndk() && !ccDep.mustUseVendorVariant() && !c.inRecovery() {
 				// The vendor module is a no-vendor-variant VNDK library.  Depend on the
 				// core module instead.
 				return libName
diff --git a/cc/compiler.go b/cc/compiler.go
index ffb6ad2..85ff400 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -309,6 +309,7 @@
 		flags.SystemIncludeFlags = append(flags.SystemIncludeFlags,
 			"-isystem "+getCurrentIncludePath(ctx).String(),
 			"-isystem "+getCurrentIncludePath(ctx).Join(ctx, config.NDKTriple(tc)).String())
+		flags.GlobalFlags = append(flags.GlobalFlags, "-D__ANDROID_NDK__")
 	}
 
 	if ctx.useVndk() {
diff --git a/cc/config/global.go b/cc/config/global.go
index a27246e..9ce6896 100644
--- a/cc/config/global.go
+++ b/cc/config/global.go
@@ -150,8 +150,22 @@
 	pctx.StaticVariable("HostGlobalLdflags", strings.Join(hostGlobalLdflags, " "))
 	pctx.StaticVariable("HostGlobalLldflags", strings.Join(hostGlobalLldflags, " "))
 
-	pctx.StaticVariable("CommonClangGlobalCflags",
-		strings.Join(append(ClangFilterUnknownCflags(commonGlobalCflags), "${ClangExtraCflags}"), " "))
+	pctx.VariableFunc("CommonClangGlobalCflags", func(ctx android.PackageVarContext) string {
+		flags := ClangFilterUnknownCflags(commonGlobalCflags)
+		flags = append(flags, "${ClangExtraCflags}")
+
+		// http://b/131390872
+		// Automatically initialize any uninitialized stack variables.
+		// Prefer zero-init if both options are set.
+		if ctx.Config().IsEnvTrue("AUTO_ZERO_INITIALIZE") {
+			flags = append(flags, "-ftrivial-auto-var-init=zero -enable-trivial-auto-var-init-zero-knowing-it-will-be-removed-from-clang")
+		} else if ctx.Config().IsEnvTrue("AUTO_PATTERN_INITIALIZE") {
+			flags = append(flags, "-ftrivial-auto-var-init=pattern")
+		}
+
+		return strings.Join(flags, " ")
+	})
+
 	pctx.VariableFunc("DeviceClangGlobalCflags", func(ctx android.PackageVarContext) string {
 		if ctx.Config().Fuchsia() {
 			return strings.Join(ClangFilterUnknownCflags(deviceGlobalCflags), " ")
diff --git a/cc/library.go b/cc/library.go
index 2b7c9a1..b193ab7 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -432,11 +432,25 @@
 	return flags
 }
 
-func (library *libraryDecorator) shouldCreateVndkSourceAbiDump(ctx ModuleContext) bool {
+func (library *libraryDecorator) shouldCreateSourceAbiDump(ctx ModuleContext) bool {
+	if !ctx.shouldCreateSourceAbiDump() {
+		return false
+	}
 	if library.Properties.Header_abi_checker.Enabled != nil {
 		return Bool(library.Properties.Header_abi_checker.Enabled)
 	}
-	return ctx.shouldCreateVndkSourceAbiDump(ctx.Config())
+	if ctx.isNdk() {
+		return true
+	}
+	if ctx.isLlndkPublic(ctx.Config()) {
+		return true
+	}
+	if ctx.useVndk() && ctx.isVndk() && !ctx.isVndkPrivate(ctx.Config()) {
+		// Return true if this is VNDK-core, VNDK-SP, or VNDK-Ext, and not
+		// VNDK-private.
+		return true
+	}
+	return false
 }
 
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) Objects {
@@ -458,7 +472,7 @@
 		}
 		return Objects{}
 	}
-	if library.shouldCreateVndkSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
+	if library.shouldCreateSourceAbiDump(ctx) || library.sabi.Properties.CreateSAbiDumps {
 		exportIncludeDirs := library.flagExporter.exportedIncludes(ctx)
 		var SourceAbiFlags []string
 		for _, dir := range exportIncludeDirs.Strings() {
@@ -822,7 +836,7 @@
 }
 
 func (library *libraryDecorator) linkSAbiDumpFiles(ctx ModuleContext, objs Objects, fileName string, soFile android.Path) {
-	if library.shouldCreateVndkSourceAbiDump(ctx) {
+	if library.shouldCreateSourceAbiDump(ctx) {
 		vndkVersion := ctx.DeviceConfig().PlatformVndkVersion()
 		if ver := ctx.DeviceConfig().VndkVersion(); ver != "" && ver != "current" {
 			vndkVersion = ver
diff --git a/cc/linker.go b/cc/linker.go
index daacec1..962fcce 100644
--- a/cc/linker.go
+++ b/cc/linker.go
@@ -125,6 +125,10 @@
 			// variant of the C/C++ module.
 			Shared_libs []string
 
+			// list of static libs that only should be used to build the recovery
+			// variant of the C/C++ module.
+			Static_libs []string
+
 			// list of shared libs that should not be used to build
 			// the recovery variant of the C/C++ module.
 			Exclude_shared_libs []string
@@ -211,6 +215,7 @@
 		deps.SharedLibs = append(deps.SharedLibs, linker.Properties.Target.Recovery.Shared_libs...)
 		deps.SharedLibs = removeListFromList(deps.SharedLibs, linker.Properties.Target.Recovery.Exclude_shared_libs)
 		deps.ReexportSharedLibHeaders = removeListFromList(deps.ReexportSharedLibHeaders, linker.Properties.Target.Recovery.Exclude_shared_libs)
+		deps.StaticLibs = append(deps.StaticLibs, linker.Properties.Target.Recovery.Static_libs...)
 		deps.StaticLibs = removeListFromList(deps.StaticLibs, linker.Properties.Target.Recovery.Exclude_static_libs)
 		deps.HeaderLibs = removeListFromList(deps.HeaderLibs, linker.Properties.Target.Recovery.Exclude_header_libs)
 		deps.ReexportHeaderLibHeaders = removeListFromList(deps.ReexportHeaderLibHeaders, linker.Properties.Target.Recovery.Exclude_header_libs)
diff --git a/cc/lto.go b/cc/lto.go
index 1084869..431d70d 100644
--- a/cc/lto.go
+++ b/cc/lto.go
@@ -80,6 +80,12 @@
 }
 
 func (lto *lto) flags(ctx BaseModuleContext, flags Flags) Flags {
+	// TODO(b/131771163): Disable LTO when using explicit fuzzing configurations.
+	// LTO breaks fuzzer builds.
+	if inList("-fsanitize=fuzzer-no-link", flags.CFlags) {
+		return flags
+	}
+
 	if lto.LTO() {
 		var ltoFlag string
 		if Bool(lto.Properties.Lto.Thin) {
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index 969cb3f..3747b41 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -54,6 +54,7 @@
 		"mediandk",
 		"nativewindow",
 		"m",
+		"neuralnetworks",
 		"OpenMAXAL",
 		"OpenSLES",
 		"stdc++",
diff --git a/cc/sanitize.go b/cc/sanitize.go
index b238b7e..261ca88 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -464,7 +464,9 @@
 
 		// TODO(b/131771163): LTO and Fuzzer support is mutually incompatible.
 		_, flags.LdFlags = removeFromList("-flto", flags.LdFlags)
+		_, flags.CFlags = removeFromList("-flto", flags.CFlags)
 		flags.LdFlags = append(flags.LdFlags, "-fno-lto")
+		flags.CFlags = append(flags.CFlags, "-fno-lto")
 
 		// TODO(b/133876586): Experimental PM breaks sanitizer coverage.
 		_, flags.CFlags = removeFromList("-fexperimental-new-pass-manager", flags.CFlags)
diff --git a/cmd/merge_zips/Android.bp b/cmd/merge_zips/Android.bp
index ab658fd..f70c86e 100644
--- a/cmd/merge_zips/Android.bp
+++ b/cmd/merge_zips/Android.bp
@@ -18,6 +18,7 @@
       "android-archive-zip",
       "blueprint-pathtools",
       "soong-jar",
+      "soong-zip",
     ],
     srcs: [
         "merge_zips.go",
diff --git a/cmd/merge_zips/merge_zips.go b/cmd/merge_zips/merge_zips.go
index 68fe259..27179cb 100644
--- a/cmd/merge_zips/merge_zips.go
+++ b/cmd/merge_zips/merge_zips.go
@@ -30,8 +30,566 @@
 
 	"android/soong/jar"
 	"android/soong/third_party/zip"
+	soongZip "android/soong/zip"
 )
 
+// Input zip: we can open it, close it, and obtain an array of entries
+type InputZip interface {
+	Name() string
+	Open() error
+	Close() error
+	Entries() []*zip.File
+	IsOpen() bool
+}
+
+// An entry that can be written to the output zip
+type ZipEntryContents interface {
+	String() string
+	IsDir() bool
+	CRC32() uint32
+	Size() uint64
+	WriteToZip(dest string, zw *zip.Writer) error
+}
+
+// a ZipEntryFromZip is a ZipEntryContents that pulls its content from another zip
+// identified by the input zip and the index of the entry in its entries array
+type ZipEntryFromZip struct {
+	inputZip InputZip
+	index    int
+	name     string
+	isDir    bool
+	crc32    uint32
+	size     uint64
+}
+
+func NewZipEntryFromZip(inputZip InputZip, entryIndex int) *ZipEntryFromZip {
+	fi := inputZip.Entries()[entryIndex]
+	newEntry := ZipEntryFromZip{inputZip: inputZip,
+		index: entryIndex,
+		name:  fi.Name,
+		isDir: fi.FileInfo().IsDir(),
+		crc32: fi.CRC32,
+		size:  fi.UncompressedSize64,
+	}
+	return &newEntry
+}
+
+func (ze ZipEntryFromZip) String() string {
+	return fmt.Sprintf("%s!%s", ze.inputZip.Name(), ze.name)
+}
+
+func (ze ZipEntryFromZip) IsDir() bool {
+	return ze.isDir
+}
+
+func (ze ZipEntryFromZip) CRC32() uint32 {
+	return ze.crc32
+}
+
+func (ze ZipEntryFromZip) Size() uint64 {
+	return ze.size
+}
+
+func (ze ZipEntryFromZip) WriteToZip(dest string, zw *zip.Writer) error {
+	if err := ze.inputZip.Open(); err != nil {
+		return err
+	}
+	return zw.CopyFrom(ze.inputZip.Entries()[ze.index], dest)
+}
+
+// a ZipEntryFromBuffer is a ZipEntryContents that pulls its content from a []byte
+type ZipEntryFromBuffer struct {
+	fh      *zip.FileHeader
+	content []byte
+}
+
+func (be ZipEntryFromBuffer) String() string {
+	return "internal buffer"
+}
+
+func (be ZipEntryFromBuffer) IsDir() bool {
+	return be.fh.FileInfo().IsDir()
+}
+
+func (be ZipEntryFromBuffer) CRC32() uint32 {
+	return crc32.ChecksumIEEE(be.content)
+}
+
+func (be ZipEntryFromBuffer) Size() uint64 {
+	return uint64(len(be.content))
+}
+
+func (be ZipEntryFromBuffer) WriteToZip(dest string, zw *zip.Writer) error {
+	w, err := zw.CreateHeader(be.fh)
+	if err != nil {
+		return err
+	}
+
+	if !be.IsDir() {
+		_, err = w.Write(be.content)
+		if err != nil {
+			return err
+		}
+	}
+
+	return nil
+}
+
+// Processing state.
+type OutputZip struct {
+	outputWriter     *zip.Writer
+	stripDirEntries  bool
+	emulateJar       bool
+	sortEntries      bool
+	ignoreDuplicates bool
+	excludeDirs      []string
+	excludeFiles     []string
+	sourceByDest     map[string]ZipEntryContents
+}
+
+func NewOutputZip(outputWriter *zip.Writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates bool) *OutputZip {
+	return &OutputZip{
+		outputWriter:     outputWriter,
+		stripDirEntries:  stripDirEntries,
+		emulateJar:       emulateJar,
+		sortEntries:      sortEntries,
+		sourceByDest:     make(map[string]ZipEntryContents, 0),
+		ignoreDuplicates: ignoreDuplicates,
+	}
+}
+
+func (oz *OutputZip) setExcludeDirs(excludeDirs []string) {
+	oz.excludeDirs = make([]string, len(excludeDirs))
+	for i, dir := range excludeDirs {
+		oz.excludeDirs[i] = filepath.Clean(dir)
+	}
+}
+
+func (oz *OutputZip) setExcludeFiles(excludeFiles []string) {
+	oz.excludeFiles = excludeFiles
+}
+
+// Adds an entry with given name whose source is given ZipEntryContents. Returns old ZipEntryContents
+// if entry with given name already exists.
+func (oz *OutputZip) addZipEntry(name string, source ZipEntryContents) (ZipEntryContents, error) {
+	if existingSource, exists := oz.sourceByDest[name]; exists {
+		return existingSource, nil
+	}
+	oz.sourceByDest[name] = source
+	// Delay writing an entry if entries need to be rearranged.
+	if oz.emulateJar || oz.sortEntries {
+		return nil, nil
+	}
+	return nil, source.WriteToZip(name, oz.outputWriter)
+}
+
+// Adds an entry for the manifest (META-INF/MANIFEST.MF from the given file
+func (oz *OutputZip) addManifest(manifestPath string) error {
+	if !oz.stripDirEntries {
+		if _, err := oz.addZipEntry(jar.MetaDir, ZipEntryFromBuffer{jar.MetaDirFileHeader(), nil}); err != nil {
+			return err
+		}
+	}
+	contents, err := ioutil.ReadFile(manifestPath)
+	if err == nil {
+		fh, buf, err := jar.ManifestFileContents(contents)
+		if err == nil {
+			_, err = oz.addZipEntry(jar.ManifestFile, ZipEntryFromBuffer{fh, buf})
+		}
+	}
+	return err
+}
+
+// Adds an entry with given name and contents read from given file
+func (oz *OutputZip) addZipEntryFromFile(name string, path string) error {
+	buf, err := ioutil.ReadFile(path)
+	if err == nil {
+		fh := &zip.FileHeader{
+			Name:               name,
+			Method:             zip.Store,
+			UncompressedSize64: uint64(len(buf)),
+		}
+		fh.SetMode(0700)
+		fh.SetModTime(jar.DefaultTime)
+		_, err = oz.addZipEntry(name, ZipEntryFromBuffer{fh, buf})
+	}
+	return err
+}
+
+func (oz *OutputZip) addEmptyEntry(entry string) error {
+	var emptyBuf []byte
+	fh := &zip.FileHeader{
+		Name:               entry,
+		Method:             zip.Store,
+		UncompressedSize64: uint64(len(emptyBuf)),
+	}
+	fh.SetMode(0700)
+	fh.SetModTime(jar.DefaultTime)
+	_, err := oz.addZipEntry(entry, ZipEntryFromBuffer{fh, emptyBuf})
+	return err
+}
+
+// Returns true if given entry is to be excluded
+func (oz *OutputZip) isEntryExcluded(name string) bool {
+	for _, dir := range oz.excludeDirs {
+		dir = filepath.Clean(dir)
+		patterns := []string{
+			dir + "/",      // the directory itself
+			dir + "/**/*",  // files recursively in the directory
+			dir + "/**/*/", // directories recursively in the directory
+		}
+
+		for _, pattern := range patterns {
+			match, err := pathtools.Match(pattern, name)
+			if err != nil {
+				panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+			}
+			if match {
+				if oz.emulateJar {
+					// When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
+					// requested.
+					// TODO(ccross): which files does this affect?
+					if name != jar.MetaDir && name != jar.ManifestFile {
+						return true
+					}
+				}
+				return true
+			}
+		}
+	}
+
+	for _, pattern := range oz.excludeFiles {
+		match, err := pathtools.Match(pattern, name)
+		if err != nil {
+			panic(fmt.Errorf("%s: %s", err.Error(), pattern))
+		}
+		if match {
+			return true
+		}
+	}
+	return false
+}
+
+// Creates a zip entry whose contents is an entry from the given input zip.
+func (oz *OutputZip) copyEntry(inputZip InputZip, index int) error {
+	entry := NewZipEntryFromZip(inputZip, index)
+	if oz.stripDirEntries && entry.IsDir() {
+		return nil
+	}
+	existingEntry, err := oz.addZipEntry(entry.name, entry)
+	if err != nil {
+		return err
+	}
+	if existingEntry == nil {
+		return nil
+	}
+
+	// File types should match
+	if existingEntry.IsDir() != entry.IsDir() {
+		return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
+			entry.name, existingEntry, entry)
+	}
+
+	if oz.ignoreDuplicates ||
+		// Skip manifest and module info files that are not from the first input file
+		(oz.emulateJar && entry.name == jar.ManifestFile || entry.name == jar.ModuleInfoClass) ||
+		// Identical entries
+		(existingEntry.CRC32() == entry.CRC32() && existingEntry.Size() == entry.Size()) ||
+		// Directory entries
+		entry.IsDir() {
+		return nil
+	}
+
+	return fmt.Errorf("Duplicate path %v found in %v and %v\n", entry.name, existingEntry, inputZip.Name())
+}
+
+func (oz *OutputZip) entriesArray() []string {
+	entries := make([]string, len(oz.sourceByDest))
+	i := 0
+	for entry := range oz.sourceByDest {
+		entries[i] = entry
+		i++
+	}
+	return entries
+}
+
+func (oz *OutputZip) jarSorted() []string {
+	entries := oz.entriesArray()
+	sort.SliceStable(entries, func(i, j int) bool { return jar.EntryNamesLess(entries[i], entries[j]) })
+	return entries
+}
+
+func (oz *OutputZip) alphanumericSorted() []string {
+	entries := oz.entriesArray()
+	sort.Strings(entries)
+	return entries
+}
+
+func (oz *OutputZip) writeEntries(entries []string) error {
+	for _, entry := range entries {
+		source, _ := oz.sourceByDest[entry]
+		if err := source.WriteToZip(entry, oz.outputWriter); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
+func (oz *OutputZip) getUninitializedPythonPackages(inputZips []InputZip) ([]string, error) {
+	// the runfiles packages needs to be populated with "__init__.py".
+	// the runfiles dirs have been treated as packages.
+	allPackages := make(map[string]bool)
+	initedPackages := make(map[string]bool)
+	getPackage := func(path string) string {
+		ret := filepath.Dir(path)
+		// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
+		if ret == "." || ret == "/" {
+			return ""
+		}
+		return ret
+	}
+
+	// put existing __init__.py files to a set first. This set is used for preventing
+	// generated __init__.py files from overwriting existing ones.
+	for _, inputZip := range inputZips {
+		if err := inputZip.Open(); err != nil {
+			return nil, err
+		}
+		for _, file := range inputZip.Entries() {
+			pyPkg := getPackage(file.Name)
+			if filepath.Base(file.Name) == "__init__.py" {
+				if _, found := initedPackages[pyPkg]; found {
+					panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q", file.Name))
+				}
+				initedPackages[pyPkg] = true
+			}
+			for pyPkg != "" {
+				if _, found := allPackages[pyPkg]; found {
+					break
+				}
+				allPackages[pyPkg] = true
+				pyPkg = getPackage(pyPkg)
+			}
+		}
+	}
+	noInitPackages := make([]string, 0)
+	for pyPkg := range allPackages {
+		if _, found := initedPackages[pyPkg]; !found {
+			noInitPackages = append(noInitPackages, pyPkg)
+		}
+	}
+	return noInitPackages, nil
+}
+
+// An InputZip owned by the InputZipsManager. Opened ManagedInputZip's are chained in the open order.
+type ManagedInputZip struct {
+	owner        *InputZipsManager
+	realInputZip InputZip
+	older        *ManagedInputZip
+	newer        *ManagedInputZip
+}
+
+// Maintains the array of ManagedInputZips, keeping track of open input ones. When an InputZip is opened,
+// may close some other InputZip to limit the number of open ones.
+type InputZipsManager struct {
+	inputZips     []*ManagedInputZip
+	nOpenZips     int
+	maxOpenZips   int
+	openInputZips *ManagedInputZip
+}
+
+func (miz *ManagedInputZip) unlink() {
+	olderMiz := miz.older
+	newerMiz := miz.newer
+	if newerMiz.older != miz || olderMiz.newer != miz {
+		panic(fmt.Errorf("removing %p:%#v: broken list between %p:%#v and %p:%#v",
+			miz, miz, newerMiz, newerMiz, olderMiz, olderMiz))
+	}
+	olderMiz.newer = newerMiz
+	newerMiz.older = olderMiz
+	miz.newer = nil
+	miz.older = nil
+}
+
+func (miz *ManagedInputZip) link(olderMiz *ManagedInputZip) {
+	if olderMiz.newer != nil || olderMiz.older != nil {
+		panic(fmt.Errorf("inputZip is already open"))
+	}
+	oldOlderMiz := miz.older
+	if oldOlderMiz.newer != miz {
+		panic(fmt.Errorf("broken list between %p:%#v and %p:%#v", miz, oldOlderMiz))
+	}
+	miz.older = olderMiz
+	olderMiz.older = oldOlderMiz
+	oldOlderMiz.newer = olderMiz
+	olderMiz.newer = miz
+}
+
+func NewInputZipsManager(nInputZips, maxOpenZips int) *InputZipsManager {
+	if maxOpenZips < 3 {
+		panic(fmt.Errorf("open zips limit should be above 3"))
+	}
+	// In the dummy element .older points to the most recently opened InputZip, and .newer points to the oldest.
+	head := new(ManagedInputZip)
+	head.older = head
+	head.newer = head
+	return &InputZipsManager{
+		inputZips:     make([]*ManagedInputZip, 0, nInputZips),
+		maxOpenZips:   maxOpenZips,
+		openInputZips: head,
+	}
+}
+
+// InputZip factory
+func (izm *InputZipsManager) Manage(inz InputZip) InputZip {
+	iz := &ManagedInputZip{owner: izm, realInputZip: inz}
+	izm.inputZips = append(izm.inputZips, iz)
+	return iz
+}
+
+// Opens or reopens ManagedInputZip.
+func (izm *InputZipsManager) reopen(miz *ManagedInputZip) error {
+	if miz.realInputZip.IsOpen() {
+		if miz != izm.openInputZips {
+			miz.unlink()
+			izm.openInputZips.link(miz)
+		}
+		return nil
+	}
+	if izm.nOpenZips >= izm.maxOpenZips {
+		if err := izm.close(izm.openInputZips.older); err != nil {
+			return err
+		}
+	}
+	if err := miz.realInputZip.Open(); err != nil {
+		return err
+	}
+	izm.openInputZips.link(miz)
+	izm.nOpenZips++
+	return nil
+}
+
+func (izm *InputZipsManager) close(miz *ManagedInputZip) error {
+	if miz.IsOpen() {
+		err := miz.realInputZip.Close()
+		izm.nOpenZips--
+		miz.unlink()
+		return err
+	}
+	return nil
+}
+
+// Checks that openInputZips deque is valid
+func (izm *InputZipsManager) checkOpenZipsDeque() {
+	nReallyOpen := 0
+	el := izm.openInputZips
+	for {
+		elNext := el.older
+		if elNext.newer != el {
+			panic(fmt.Errorf("Element:\n  %p: %v\nNext:\n  %p %v", el, el, elNext, elNext))
+		}
+		if elNext == izm.openInputZips {
+			break
+		}
+		el = elNext
+		if !el.IsOpen() {
+			panic(fmt.Errorf("Found unopened element"))
+		}
+		nReallyOpen++
+		if nReallyOpen > izm.nOpenZips {
+			panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips))
+		}
+	}
+	if nReallyOpen > izm.nOpenZips {
+		panic(fmt.Errorf("found %d open zips, should be %d", nReallyOpen, izm.nOpenZips))
+	}
+}
+
+func (miz *ManagedInputZip) Name() string {
+	return miz.realInputZip.Name()
+}
+
+func (miz *ManagedInputZip) Open() error {
+	return miz.owner.reopen(miz)
+}
+
+func (miz *ManagedInputZip) Close() error {
+	return miz.owner.close(miz)
+}
+
+func (miz *ManagedInputZip) IsOpen() bool {
+	return miz.realInputZip.IsOpen()
+}
+
+func (miz *ManagedInputZip) Entries() []*zip.File {
+	if !miz.IsOpen() {
+		panic(fmt.Errorf("%s: is not open", miz.Name()))
+	}
+	return miz.realInputZip.Entries()
+}
+
+// Actual processing.
+func mergeZips(inputZips []InputZip, writer *zip.Writer, manifest, pyMain string,
+	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
+	excludeFiles, excludeDirs []string, zipsToNotStrip map[string]bool) error {
+
+	out := NewOutputZip(writer, sortEntries, emulateJar, stripDirEntries, ignoreDuplicates)
+	out.setExcludeFiles(excludeFiles)
+	out.setExcludeDirs(excludeDirs)
+	if manifest != "" {
+		if err := out.addManifest(manifest); err != nil {
+			return err
+		}
+	}
+	if pyMain != "" {
+		if err := out.addZipEntryFromFile("__main__.py", pyMain); err != nil {
+			return err
+		}
+	}
+
+	if emulatePar {
+		noInitPackages, err := out.getUninitializedPythonPackages(inputZips)
+		if err != nil {
+			return err
+		}
+		for _, uninitializedPyPackage := range noInitPackages {
+			if err = out.addEmptyEntry(filepath.Join(uninitializedPyPackage, "__init__.py")); err != nil {
+				return err
+			}
+		}
+	}
+
+	// Finally, add entries from all the input zips.
+	for _, inputZip := range inputZips {
+		_, copyFully := zipsToNotStrip[inputZip.Name()]
+		if err := inputZip.Open(); err != nil {
+			return err
+		}
+
+		for i, entry := range inputZip.Entries() {
+			if copyFully || !out.isEntryExcluded(entry.Name) {
+				if err := out.copyEntry(inputZip, i); err != nil {
+					return err
+				}
+			}
+		}
+		// Unless we need to rearrange the entries, the input zip can now be closed.
+		if !(emulateJar || sortEntries) {
+			if err := inputZip.Close(); err != nil {
+				return err
+			}
+		}
+	}
+
+	if emulateJar {
+		return out.writeEntries(out.jarSorted())
+	} else if sortEntries {
+		return out.writeEntries(out.alphanumericSorted())
+	}
+	return nil
+}
+
+// Process command line
 type fileList []string
 
 func (f *fileList) String() string {
@@ -50,9 +608,8 @@
 	return `""`
 }
 
-func (s zipsToNotStripSet) Set(zip_path string) error {
-	s[zip_path] = true
-
+func (s zipsToNotStripSet) Set(path string) error {
+	s[path] = true
 	return nil
 }
 
@@ -60,8 +617,8 @@
 	sortEntries      = flag.Bool("s", false, "sort entries (defaults to the order from the input zip files)")
 	emulateJar       = flag.Bool("j", false, "sort zip entries using jar ordering (META-INF first)")
 	emulatePar       = flag.Bool("p", false, "merge zip entries based on par format")
-	stripDirs        fileList
-	stripFiles       fileList
+	excludeDirs      fileList
+	excludeFiles     fileList
 	zipsToNotStrip   = make(zipsToNotStripSet)
 	stripDirEntries  = flag.Bool("D", false, "strip directory entries from the output zip file")
 	manifest         = flag.String("m", "", "manifest file to insert in jar")
@@ -71,14 +628,52 @@
 )
 
 func init() {
-	flag.Var(&stripDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
-	flag.Var(&stripFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
+	flag.Var(&excludeDirs, "stripDir", "directories to be excluded from the output zip, accepts wildcards")
+	flag.Var(&excludeFiles, "stripFile", "files to be excluded from the output zip, accepts wildcards")
 	flag.Var(&zipsToNotStrip, "zipToNotStrip", "the input zip file which is not applicable for stripping")
 }
 
+type FileInputZip struct {
+	name   string
+	reader *zip.ReadCloser
+}
+
+func (fiz *FileInputZip) Name() string {
+	return fiz.name
+}
+
+func (fiz *FileInputZip) Close() error {
+	if fiz.IsOpen() {
+		reader := fiz.reader
+		fiz.reader = nil
+		return reader.Close()
+	}
+	return nil
+}
+
+func (fiz *FileInputZip) Entries() []*zip.File {
+	if !fiz.IsOpen() {
+		panic(fmt.Errorf("%s: is not open", fiz.Name()))
+	}
+	return fiz.reader.File
+}
+
+func (fiz *FileInputZip) IsOpen() bool {
+	return fiz.reader != nil
+}
+
+func (fiz *FileInputZip) Open() error {
+	if fiz.IsOpen() {
+		return nil
+	}
+	var err error
+	fiz.reader, err = zip.OpenReader(fiz.Name())
+	return err
+}
+
 func main() {
 	flag.Usage = func() {
-		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] output [inputs...]")
+		fmt.Fprintln(os.Stderr, "usage: merge_zips [-jpsD] [-m manifest] [--prefix script] [-pm __main__.py] OutputZip [inputs...]")
 		flag.PrintDefaults()
 	}
 
@@ -90,16 +685,28 @@
 		os.Exit(1)
 	}
 	outputPath := args[0]
-	inputs := args[1:]
+	inputs := make([]string, 0)
+	for _, input := range args[1:] {
+		if input[0] == '@' {
+			bytes, err := ioutil.ReadFile(input[1:])
+			if err != nil {
+				log.Fatal(err)
+			}
+			inputs = append(inputs, soongZip.ReadRespFile(bytes)...)
+			continue
+		}
+		inputs = append(inputs, input)
+		continue
+	}
 
 	log.SetFlags(log.Lshortfile)
 
 	// make writer
-	output, err := os.Create(outputPath)
+	outputZip, err := os.Create(outputPath)
 	if err != nil {
 		log.Fatal(err)
 	}
-	defer output.Close()
+	defer outputZip.Close()
 
 	var offset int64
 	if *prefix != "" {
@@ -107,13 +714,13 @@
 		if err != nil {
 			log.Fatal(err)
 		}
-		offset, err = io.Copy(output, prefixFile)
+		offset, err = io.Copy(outputZip, prefixFile)
 		if err != nil {
 			log.Fatal(err)
 		}
 	}
 
-	writer := zip.NewWriter(output)
+	writer := zip.NewWriter(outputZip)
 	defer func() {
 		err := writer.Close()
 		if err != nil {
@@ -122,18 +729,6 @@
 	}()
 	writer.SetOffset(offset)
 
-	// make readers
-	readers := []namedZipReader{}
-	for _, input := range inputs {
-		reader, err := zip.OpenReader(input)
-		if err != nil {
-			log.Fatal(err)
-		}
-		defer reader.Close()
-		namedReader := namedZipReader{path: input, reader: &reader.Reader}
-		readers = append(readers, namedReader)
-	}
-
 	if *manifest != "" && !*emulateJar {
 		log.Fatal(errors.New("must specify -j when specifying a manifest via -m"))
 	}
@@ -143,344 +738,15 @@
 	}
 
 	// do merge
-	err = mergeZips(readers, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
-		*stripDirEntries, *ignoreDuplicates, []string(stripFiles), []string(stripDirs), map[string]bool(zipsToNotStrip))
+	inputZipsManager := NewInputZipsManager(len(inputs), 1000)
+	inputZips := make([]InputZip, len(inputs))
+	for i, input := range inputs {
+		inputZips[i] = inputZipsManager.Manage(&FileInputZip{name: input})
+	}
+	err = mergeZips(inputZips, writer, *manifest, *pyMain, *sortEntries, *emulateJar, *emulatePar,
+		*stripDirEntries, *ignoreDuplicates, []string(excludeFiles), []string(excludeDirs),
+		map[string]bool(zipsToNotStrip))
 	if err != nil {
 		log.Fatal(err)
 	}
 }
-
-// a namedZipReader reads a .zip file and can say which file it's reading
-type namedZipReader struct {
-	path   string
-	reader *zip.Reader
-}
-
-// a zipEntryPath refers to a file contained in a zip
-type zipEntryPath struct {
-	zipName   string
-	entryName string
-}
-
-func (p zipEntryPath) String() string {
-	return p.zipName + "/" + p.entryName
-}
-
-// a zipEntry is a zipSource that pulls its content from another zip
-type zipEntry struct {
-	path    zipEntryPath
-	content *zip.File
-}
-
-func (ze zipEntry) String() string {
-	return ze.path.String()
-}
-
-func (ze zipEntry) IsDir() bool {
-	return ze.content.FileInfo().IsDir()
-}
-
-func (ze zipEntry) CRC32() uint32 {
-	return ze.content.FileHeader.CRC32
-}
-
-func (ze zipEntry) Size() uint64 {
-	return ze.content.FileHeader.UncompressedSize64
-}
-
-func (ze zipEntry) WriteToZip(dest string, zw *zip.Writer) error {
-	return zw.CopyFrom(ze.content, dest)
-}
-
-// a bufferEntry is a zipSource that pulls its content from a []byte
-type bufferEntry struct {
-	fh      *zip.FileHeader
-	content []byte
-}
-
-func (be bufferEntry) String() string {
-	return "internal buffer"
-}
-
-func (be bufferEntry) IsDir() bool {
-	return be.fh.FileInfo().IsDir()
-}
-
-func (be bufferEntry) CRC32() uint32 {
-	return crc32.ChecksumIEEE(be.content)
-}
-
-func (be bufferEntry) Size() uint64 {
-	return uint64(len(be.content))
-}
-
-func (be bufferEntry) WriteToZip(dest string, zw *zip.Writer) error {
-	w, err := zw.CreateHeader(be.fh)
-	if err != nil {
-		return err
-	}
-
-	if !be.IsDir() {
-		_, err = w.Write(be.content)
-		if err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-type zipSource interface {
-	String() string
-	IsDir() bool
-	CRC32() uint32
-	Size() uint64
-	WriteToZip(dest string, zw *zip.Writer) error
-}
-
-// a fileMapping specifies to copy a zip entry from one place to another
-type fileMapping struct {
-	dest   string
-	source zipSource
-}
-
-func mergeZips(readers []namedZipReader, writer *zip.Writer, manifest, pyMain string,
-	sortEntries, emulateJar, emulatePar, stripDirEntries, ignoreDuplicates bool,
-	stripFiles, stripDirs []string, zipsToNotStrip map[string]bool) error {
-
-	sourceByDest := make(map[string]zipSource, 0)
-	orderedMappings := []fileMapping{}
-
-	// if dest already exists returns a non-null zipSource for the existing source
-	addMapping := func(dest string, source zipSource) zipSource {
-		mapKey := filepath.Clean(dest)
-		if existingSource, exists := sourceByDest[mapKey]; exists {
-			return existingSource
-		}
-
-		sourceByDest[mapKey] = source
-		orderedMappings = append(orderedMappings, fileMapping{source: source, dest: dest})
-		return nil
-	}
-
-	if manifest != "" {
-		if !stripDirEntries {
-			dirHeader := jar.MetaDirFileHeader()
-			dirSource := bufferEntry{dirHeader, nil}
-			addMapping(jar.MetaDir, dirSource)
-		}
-
-		contents, err := ioutil.ReadFile(manifest)
-		if err != nil {
-			return err
-		}
-
-		fh, buf, err := jar.ManifestFileContents(contents)
-		if err != nil {
-			return err
-		}
-
-		fileSource := bufferEntry{fh, buf}
-		addMapping(jar.ManifestFile, fileSource)
-	}
-
-	if pyMain != "" {
-		buf, err := ioutil.ReadFile(pyMain)
-		if err != nil {
-			return err
-		}
-		fh := &zip.FileHeader{
-			Name:               "__main__.py",
-			Method:             zip.Store,
-			UncompressedSize64: uint64(len(buf)),
-		}
-		fh.SetMode(0700)
-		fh.SetModTime(jar.DefaultTime)
-		fileSource := bufferEntry{fh, buf}
-		addMapping("__main__.py", fileSource)
-	}
-
-	if emulatePar {
-		// the runfiles packages needs to be populated with "__init__.py".
-		newPyPkgs := []string{}
-		// the runfiles dirs have been treated as packages.
-		existingPyPkgSet := make(map[string]bool)
-		// put existing __init__.py files to a set first. This set is used for preventing
-		// generated __init__.py files from overwriting existing ones.
-		for _, namedReader := range readers {
-			for _, file := range namedReader.reader.File {
-				if filepath.Base(file.Name) != "__init__.py" {
-					continue
-				}
-				pyPkg := pathBeforeLastSlash(file.Name)
-				if _, found := existingPyPkgSet[pyPkg]; found {
-					panic(fmt.Errorf("found __init__.py path duplicates during pars merging: %q.", file.Name))
-				} else {
-					existingPyPkgSet[pyPkg] = true
-				}
-			}
-		}
-		for _, namedReader := range readers {
-			for _, file := range namedReader.reader.File {
-				var parentPath string /* the path after trimming last "/" */
-				if filepath.Base(file.Name) == "__init__.py" {
-					// for existing __init__.py files, we should trim last "/" for twice.
-					// eg. a/b/c/__init__.py ---> a/b
-					parentPath = pathBeforeLastSlash(pathBeforeLastSlash(file.Name))
-				} else {
-					parentPath = pathBeforeLastSlash(file.Name)
-				}
-				populateNewPyPkgs(parentPath, existingPyPkgSet, &newPyPkgs)
-			}
-		}
-		for _, pkg := range newPyPkgs {
-			var emptyBuf []byte
-			fh := &zip.FileHeader{
-				Name:               filepath.Join(pkg, "__init__.py"),
-				Method:             zip.Store,
-				UncompressedSize64: uint64(len(emptyBuf)),
-			}
-			fh.SetMode(0700)
-			fh.SetModTime(jar.DefaultTime)
-			fileSource := bufferEntry{fh, emptyBuf}
-			addMapping(filepath.Join(pkg, "__init__.py"), fileSource)
-		}
-	}
-	for _, namedReader := range readers {
-		_, skipStripThisZip := zipsToNotStrip[namedReader.path]
-		for _, file := range namedReader.reader.File {
-			if !skipStripThisZip {
-				if skip, err := shouldStripEntry(emulateJar, stripFiles, stripDirs, file.Name); err != nil {
-					return err
-				} else if skip {
-					continue
-				}
-			}
-
-			if stripDirEntries && file.FileInfo().IsDir() {
-				continue
-			}
-
-			// check for other files or directories destined for the same path
-			dest := file.Name
-
-			// make a new entry to add
-			source := zipEntry{path: zipEntryPath{zipName: namedReader.path, entryName: file.Name}, content: file}
-
-			if existingSource := addMapping(dest, source); existingSource != nil {
-				// handle duplicates
-				if existingSource.IsDir() != source.IsDir() {
-					return fmt.Errorf("Directory/file mismatch at %v from %v and %v\n",
-						dest, existingSource, source)
-				}
-
-				if ignoreDuplicates {
-					continue
-				}
-
-				if emulateJar &&
-					file.Name == jar.ManifestFile || file.Name == jar.ModuleInfoClass {
-					// Skip manifest and module info files that are not from the first input file
-					continue
-				}
-
-				if source.IsDir() {
-					continue
-				}
-
-				if existingSource.CRC32() == source.CRC32() && existingSource.Size() == source.Size() {
-					continue
-				}
-
-				return fmt.Errorf("Duplicate path %v found in %v and %v\n",
-					dest, existingSource, source)
-			}
-		}
-	}
-
-	if emulateJar {
-		jarSort(orderedMappings)
-	} else if sortEntries {
-		alphanumericSort(orderedMappings)
-	}
-
-	for _, entry := range orderedMappings {
-		if err := entry.source.WriteToZip(entry.dest, writer); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
-// Sets the given directory and all its ancestor directories as Python packages.
-func populateNewPyPkgs(pkgPath string, existingPyPkgSet map[string]bool, newPyPkgs *[]string) {
-	for pkgPath != "" {
-		if _, found := existingPyPkgSet[pkgPath]; !found {
-			existingPyPkgSet[pkgPath] = true
-			*newPyPkgs = append(*newPyPkgs, pkgPath)
-			// Gets its ancestor directory by trimming last slash.
-			pkgPath = pathBeforeLastSlash(pkgPath)
-		} else {
-			break
-		}
-	}
-}
-
-func pathBeforeLastSlash(path string) string {
-	ret := filepath.Dir(path)
-	// filepath.Dir("abc") -> "." and filepath.Dir("/abc") -> "/".
-	if ret == "." || ret == "/" {
-		return ""
-	}
-	return ret
-}
-
-func shouldStripEntry(emulateJar bool, stripFiles, stripDirs []string, name string) (bool, error) {
-	for _, dir := range stripDirs {
-		dir = filepath.Clean(dir)
-		patterns := []string{
-			dir + "/",      // the directory itself
-			dir + "/**/*",  // files recursively in the directory
-			dir + "/**/*/", // directories recursively in the directory
-		}
-
-		for _, pattern := range patterns {
-			match, err := pathtools.Match(pattern, name)
-			if err != nil {
-				return false, fmt.Errorf("%s: %s", err.Error(), pattern)
-			} else if match {
-				if emulateJar {
-					// When merging jar files, don't strip META-INF/MANIFEST.MF even if stripping META-INF is
-					// requested.
-					// TODO(ccross): which files does this affect?
-					if name != jar.MetaDir && name != jar.ManifestFile {
-						return true, nil
-					}
-				}
-				return true, nil
-			}
-		}
-	}
-
-	for _, pattern := range stripFiles {
-		if match, err := pathtools.Match(pattern, name); err != nil {
-			return false, fmt.Errorf("%s: %s", err.Error(), pattern)
-		} else if match {
-			return true, nil
-		}
-	}
-	return false, nil
-}
-
-func jarSort(files []fileMapping) {
-	sort.SliceStable(files, func(i, j int) bool {
-		return jar.EntryNamesLess(files[i].dest, files[j].dest)
-	})
-}
-
-func alphanumericSort(files []fileMapping) {
-	sort.SliceStable(files, func(i, j int) bool {
-		return files[i].dest < files[j].dest
-	})
-}
diff --git a/cmd/merge_zips/merge_zips_test.go b/cmd/merge_zips/merge_zips_test.go
index dbde270..cb58436 100644
--- a/cmd/merge_zips/merge_zips_test.go
+++ b/cmd/merge_zips/merge_zips_test.go
@@ -51,6 +51,39 @@
 	moduleInfoFile = testZipEntry{jar.ModuleInfoClass, 0755, []byte("module-info")}
 )
 
+type testInputZip struct {
+	name    string
+	entries []testZipEntry
+	reader  *zip.Reader
+}
+
+func (tiz *testInputZip) Name() string {
+	return tiz.name
+}
+
+func (tiz *testInputZip) Open() error {
+	if tiz.reader == nil {
+		tiz.reader = testZipEntriesToZipReader(tiz.entries)
+	}
+	return nil
+}
+
+func (tiz *testInputZip) Close() error {
+	tiz.reader = nil
+	return nil
+}
+
+func (tiz *testInputZip) Entries() []*zip.File {
+	if tiz.reader == nil {
+		panic(fmt.Errorf("%s: should be open to get entries", tiz.Name()))
+	}
+	return tiz.reader.File
+}
+
+func (tiz *testInputZip) IsOpen() bool {
+	return tiz.reader != nil
+}
+
 func TestMergeZips(t *testing.T) {
 	testCases := []struct {
 		name             string
@@ -207,13 +240,9 @@
 
 	for _, test := range testCases {
 		t.Run(test.name, func(t *testing.T) {
-			var readers []namedZipReader
+			inputZips := make([]InputZip, len(test.in))
 			for i, in := range test.in {
-				r := testZipEntriesToZipReader(in)
-				readers = append(readers, namedZipReader{
-					path:   "in" + strconv.Itoa(i),
-					reader: r,
-				})
+				inputZips[i] = &testInputZip{name: "in" + strconv.Itoa(i), entries: in}
 			}
 
 			want := testZipEntriesToBuf(test.out)
@@ -221,7 +250,7 @@
 			out := &bytes.Buffer{}
 			writer := zip.NewWriter(out)
 
-			err := mergeZips(readers, writer, "", "",
+			err := mergeZips(inputZips, writer, "", "",
 				test.sort, test.jar, false, test.stripDirEntries, test.ignoreDuplicates,
 				test.stripFiles, test.stripDirs, test.zipsToNotStrip)
 
@@ -304,3 +333,60 @@
 
 	return ret
 }
+
+type DummyInpuZip struct {
+	isOpen bool
+}
+
+func (diz *DummyInpuZip) Name() string {
+	return "dummy"
+}
+
+func (diz *DummyInpuZip) Open() error {
+	diz.isOpen = true
+	return nil
+}
+
+func (diz *DummyInpuZip) Close() error {
+	diz.isOpen = false
+	return nil
+}
+
+func (DummyInpuZip) Entries() []*zip.File {
+	panic("implement me")
+}
+
+func (diz *DummyInpuZip) IsOpen() bool {
+	return diz.isOpen
+}
+
+func TestInputZipsManager(t *testing.T) {
+	const nInputZips = 20
+	const nMaxOpenZips = 10
+	izm := NewInputZipsManager(20, 10)
+	managedZips := make([]InputZip, nInputZips)
+	for i := 0; i < nInputZips; i++ {
+		managedZips[i] = izm.Manage(&DummyInpuZip{})
+	}
+
+	t.Run("InputZipsManager", func(t *testing.T) {
+		for i, iz := range managedZips {
+			if err := iz.Open(); err != nil {
+				t.Fatalf("Step %d: open failed: %s", i, err)
+				return
+			}
+			if izm.nOpenZips > nMaxOpenZips {
+				t.Errorf("Step %d: should be <=%d open zips", i, nMaxOpenZips)
+			}
+		}
+		if !managedZips[nInputZips-1].IsOpen() {
+			t.Error("The last input should stay open")
+		}
+		for _, iz := range managedZips {
+			iz.Close()
+		}
+		if izm.nOpenZips > 0 {
+			t.Error("Some input zips are still open")
+		}
+	})
+}
diff --git a/cmd/soong_ui/main.go b/cmd/soong_ui/main.go
index 0bc3606..39303bf 100644
--- a/cmd/soong_ui/main.go
+++ b/cmd/soong_ui/main.go
@@ -331,42 +331,36 @@
 	}
 
 	buildActionFlags := []struct {
-		name              string
-		description       string
-		action            build.BuildAction
-		buildDependencies bool
-		set               bool
+		name        string
+		description string
+		action      build.BuildAction
+		set         bool
 	}{{
-		name:              "all-modules",
-		description:       "Build action: build from the top of the source tree.",
-		action:            build.BUILD_MODULES,
-		buildDependencies: true,
+		name:        "all-modules",
+		description: "Build action: build from the top of the source tree.",
+		action:      build.BUILD_MODULES,
 	}, {
-		// buildDependencies is set to true as mm is being deprecated. This is redirecting to mma build
-		// command behaviour. Once it has soaked for a while, the build command is deleted from here once
-		// it has been removed from the envsetup.sh.
-		name:              "modules-in-a-dir-no-deps",
-		description:       "Build action: builds all of the modules in the current directory without their dependencies.",
-		action:            build.BUILD_MODULES_IN_A_DIRECTORY,
-		buildDependencies: true,
+		// This is redirecting to mma build command behaviour. Once it has soaked for a
+		// while, the build command is deleted from here once it has been removed from the
+		// envsetup.sh.
+		name:        "modules-in-a-dir-no-deps",
+		description: "Build action: builds all of the modules in the current directory without their dependencies.",
+		action:      build.BUILD_MODULES_IN_A_DIRECTORY,
 	}, {
-		// buildDependencies is set to true as mmm is being deprecated. This is redirecting to mmma build
-		// command behaviour. Once it has soaked for a while, the build command is deleted from here once
-		// it has been removed from the envsetup.sh.
-		name:              "modules-in-dirs-no-deps",
-		description:       "Build action: builds all of the modules in the supplied directories without their dependencies.",
-		action:            build.BUILD_MODULES_IN_DIRECTORIES,
-		buildDependencies: true,
+		// This is redirecting to mmma build command behaviour. Once it has soaked for a
+		// while, the build command is deleted from here once it has been removed from the
+		// envsetup.sh.
+		name:        "modules-in-dirs-no-deps",
+		description: "Build action: builds all of the modules in the supplied directories without their dependencies.",
+		action:      build.BUILD_MODULES_IN_DIRECTORIES,
 	}, {
-		name:              "modules-in-a-dir",
-		description:       "Build action: builds all of the modules in the current directory and their dependencies.",
-		action:            build.BUILD_MODULES_IN_A_DIRECTORY,
-		buildDependencies: true,
+		name:        "modules-in-a-dir",
+		description: "Build action: builds all of the modules in the current directory and their dependencies.",
+		action:      build.BUILD_MODULES_IN_A_DIRECTORY,
 	}, {
-		name:              "modules-in-dirs",
-		description:       "Build action: builds all of the modules in the supplied directories and their dependencies.",
-		action:            build.BUILD_MODULES_IN_DIRECTORIES,
-		buildDependencies: true,
+		name:        "modules-in-dirs",
+		description: "Build action: builds all of the modules in the supplied directories and their dependencies.",
+		action:      build.BUILD_MODULES_IN_DIRECTORIES,
 	}}
 	for i, flag := range buildActionFlags {
 		flags.BoolVar(&buildActionFlags[i].set, flag.name, false, flag.description)
@@ -386,12 +380,10 @@
 	// is specified.
 	buildActionCount := 0
 	var buildAction build.BuildAction
-	buildDependency := false
 	for _, flag := range buildActionFlags {
 		if flag.set {
 			buildActionCount++
 			buildAction = flag.action
-			buildDependency = flag.buildDependencies
 		}
 	}
 	if buildActionCount != 1 {
@@ -403,7 +395,7 @@
 
 	// Remove the build action flags from the args as they are not recognized by the config.
 	args = args[numBuildActionFlags:]
-	return build.NewBuildActionConfig(buildAction, *dir, buildDependency, ctx, args...)
+	return build.NewBuildActionConfig(buildAction, *dir, ctx, args...)
 }
 
 func make(ctx build.Context, config build.Config, _ []string, logsDir string) {
@@ -425,17 +417,13 @@
 
 	if _, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
 		writer := ctx.Writer
-		fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is deprecated, and will be removed shortly.")
+		fmt.Fprintln(writer, "! The variable `ONE_SHOT_MAKEFILE` is obsolete.")
 		fmt.Fprintln(writer, "!")
 		fmt.Fprintln(writer, "! If you're using `mm`, you'll need to run `source build/envsetup.sh` to update.")
 		fmt.Fprintln(writer, "!")
 		fmt.Fprintln(writer, "! Otherwise, either specify a module name with m, or use mma / MODULES-IN-...")
 		fmt.Fprintln(writer, "")
-		select {
-		case <-time.After(30 * time.Second):
-		case <-ctx.Done():
-			return
-		}
+		ctx.Fatal("done")
 	}
 
 	toBuild := build.BuildAll
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
new file mode 100644
index 0000000..792edf3
--- /dev/null
+++ b/java/platform_compat_config.go
@@ -0,0 +1,92 @@
+// Copyright 2019 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package java
+
+import (
+	"android/soong/android"
+)
+
+func init() {
+	android.RegisterModuleType("platform_compat_config", platformCompatConfigFactory)
+}
+
+type platformCompatConfigProperties struct {
+	Src    *string `android:"path"`
+	Prefix *string
+}
+
+type platformCompatConfig struct {
+	android.ModuleBase
+
+	properties     platformCompatConfigProperties
+	installDirPath android.OutputPath
+	configFile     android.OutputPath
+}
+
+func (p *platformCompatConfig) GenerateAndroidBuildActions(ctx android.ModuleContext) {
+	rule := android.NewRuleBuilder()
+
+	configFileName := String(p.properties.Prefix) + "_platform_compat_config.xml"
+	p.configFile = android.PathForModuleOut(ctx, configFileName).OutputPath
+	path := android.PathForModuleSrc(ctx, String(p.properties.Src))
+
+	// Use the empty config if the compat config file idoesn't exist (can happen if @ChangeId
+	// annotation is not used).
+	emptyConfig := `<?xml version="1.0" encoding="UTF-8" standalone="no"?><config/>`
+	configPath := `compat/compat_config.xml`
+
+	rule.Command().
+		Text(`unzip`).
+		Flag(`-l`).
+		Input(path).
+		Text(`| grep`).
+		Flag(`-q`).
+		Text(configPath).
+		Text(`; if [ "$?" = "0" ] ; then`).
+		Text(`unzip`).
+		Flag(`-qp`).
+		Input(path).
+		Text(configPath).
+		Text(`>`).
+		Output(p.configFile).
+		Text(`; else echo '`).
+		Text(emptyConfig).
+		Text(`' >`).
+		Output(p.configFile).
+		Text(`; fi`)
+
+	p.installDirPath = android.PathForModuleInstall(ctx, "etc", "sysconfig")
+	rule.Build(pctx, ctx, configFileName, "Extract compat/compat_config.xml and install it")
+
+}
+
+func (p *platformCompatConfig) AndroidMkEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
+		Class:      "ETC",
+		OutputFile: android.OptionalPathForPath(p.configFile),
+		Include:    "$(BUILD_PREBUILT)",
+		AddCustomEntries: func(name, prefix, moduleDir string, entries *android.AndroidMkEntries) {
+			entries.SetString("LOCAL_MODULE_PATH", "$(OUT_DIR)/"+p.installDirPath.RelPathString())
+			entries.SetString("LOCAL_INSTALLED_MODULE_STEM", p.configFile.Base())
+		},
+	}
+}
+
+func platformCompatConfigFactory() android.Module {
+	module := &platformCompatConfig{}
+	module.AddProperties(&module.properties)
+	android.InitAndroidArchModule(module, android.DeviceSupported, android.MultilibFirst)
+	return module
+}
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 31f5922..8c59cbc 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -69,3 +69,19 @@
     },
     test_suites: ["general-tests"],
 }
+
+python_binary_host {
+    name: "jsonmodify",
+    main: "jsonmodify.py",
+    srcs: [
+        "jsonmodify.py",
+    ],
+    version: {
+        py2: {
+            enabled: true,
+        },
+        py3: {
+            enabled: false,
+        },
+    }
+}
diff --git a/scripts/jsonmodify.py b/scripts/jsonmodify.py
new file mode 100755
index 0000000..4b2c3c2
--- /dev/null
+++ b/scripts/jsonmodify.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+#
+# Copyright (C) 2019 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+import argparse
+import collections
+import json
+import sys
+
+def follow_path(obj, path):
+  cur = obj
+  last_key = None
+  for key in path.split('.'):
+    if last_key:
+      if last_key not in cur:
+        return None,None
+      cur = cur[last_key]
+    last_key = key
+  if last_key not in cur:
+    return None,None
+  return cur, last_key
+
+
+def ensure_path(obj, path):
+  cur = obj
+  last_key = None
+  for key in path.split('.'):
+    if last_key:
+      if last_key not in cur:
+        cur[last_key] = dict()
+      cur = cur[last_key]
+    last_key = key
+  return cur, last_key
+
+
+class SetValue(str):
+  def apply(self, obj, val):
+    cur, key = ensure_path(obj, self)
+    cur[key] = val
+
+
+class Replace(str):
+  def apply(self, obj, val):
+    cur, key = follow_path(obj, self)
+    if cur:
+      cur[key] = val
+
+
+class Remove(str):
+  def apply(self, obj):
+    cur, key = follow_path(obj, self)
+    if cur:
+      del cur[key]
+
+
+class AppendList(str):
+  def apply(self, obj, *args):
+    cur, key = ensure_path(obj, self)
+    if key not in cur:
+      cur[key] = list()
+    if not isinstance(cur[key], list):
+      raise ValueError(self + " should be a array.")
+    cur[key].extend(args)
+
+
+def main():
+  parser = argparse.ArgumentParser()
+  parser.add_argument('-o', '--out',
+                      help='write result to a file. If omitted, print to stdout',
+                      metavar='output',
+                      action='store')
+  parser.add_argument('input', nargs='?', help='JSON file')
+  parser.add_argument("-v", "--value", type=SetValue,
+                      help='set value of the key specified by path. If path doesn\'t exist, creates new one.',
+                      metavar=('path', 'value'),
+                      nargs=2, dest='patch', default=[], action='append')
+  parser.add_argument("-s", "--replace", type=Replace,
+                      help='replace value of the key specified by path. If path doesn\'t exist, no op.',
+                      metavar=('path', 'value'),
+                      nargs=2, dest='patch', action='append')
+  parser.add_argument("-r", "--remove", type=Remove,
+                      help='remove the key specified by path. If path doesn\'t exist, no op.',
+                      metavar='path',
+                      nargs=1, dest='patch', action='append')
+  parser.add_argument("-a", "--append_list", type=AppendList,
+                      help='append values to the list specified by path. If path doesn\'t exist, creates new list for it.',
+                      metavar=('path', 'value'),
+                      nargs='+', dest='patch', default=[], action='append')
+  args = parser.parse_args()
+
+  if args.input:
+    with open(args.input) as f:
+      obj = json.load(f, object_pairs_hook=collections.OrderedDict)
+  else:
+    obj = json.load(sys.stdin, object_pairs_hook=collections.OrderedDict)
+
+  for p in args.patch:
+    p[0].apply(obj, *p[1:])
+
+  if args.out:
+    with open(args.out, "w") as f:
+      json.dump(obj, f, indent=2)
+  else:
+    print(json.dumps(obj, indent=2))
+
+
+if __name__ == '__main__':
+  main()
diff --git a/ui/build/config.go b/ui/build/config.go
index bcb1965..434047b 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -253,13 +253,13 @@
 
 // NewBuildActionConfig returns a build configuration based on the build action. The arguments are
 // processed based on the build action and extracts any arguments that belongs to the build action.
-func NewBuildActionConfig(action BuildAction, dir string, buildDependencies bool, ctx Context, args ...string) Config {
-	return NewConfig(ctx, getConfigArgs(action, dir, buildDependencies, ctx, args)...)
+func NewBuildActionConfig(action BuildAction, dir string, ctx Context, args ...string) Config {
+	return NewConfig(ctx, getConfigArgs(action, dir, ctx, args)...)
 }
 
 // getConfigArgs processes the command arguments based on the build action and creates a set of new
 // arguments to be accepted by Config.
-func getConfigArgs(action BuildAction, dir string, buildDependencies bool, ctx Context, args []string) []string {
+func getConfigArgs(action BuildAction, dir string, ctx Context, args []string) []string {
 	// The next block of code verifies that the current directory is the root directory of the source
 	// tree. It then finds the relative path of dir based on the root directory of the source tree
 	// and verify that dir is inside of the source tree.
@@ -295,7 +295,6 @@
 		configArgs = removeFromList("GET-INSTALL-PATH", configArgs)
 	}
 
-	var buildFiles []string
 	var targets []string
 
 	switch action {
@@ -312,20 +311,11 @@
 		if buildFile == "" {
 			ctx.Fatalf("Build file not found for %s directory", relDir)
 		}
-		buildFiles = []string{buildFile}
 		targets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
 	case BUILD_MODULES_IN_DIRECTORIES:
 		newConfigArgs, dirs := splitArgs(configArgs)
 		configArgs = newConfigArgs
-		targets, buildFiles = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
-	}
-
-	// This is to support building modules without building their dependencies. Soon, this will be
-	// deprecated.
-	if !buildDependencies && len(buildFiles) > 0 {
-		if err := os.Setenv("ONE_SHOT_MAKEFILE", strings.Join(buildFiles, " ")); err != nil {
-			ctx.Fatalf("Unable to set ONE_SHOT_MAKEFILE environment variable: %v", err)
-		}
+		targets = getTargetsFromDirs(ctx, relDir, dirs, targetNamePrefix)
 	}
 
 	// Tidy only override all other specified targets.
@@ -435,7 +425,7 @@
 // directory from the dirs list does not exist, a fatal error is raised. relDir is related to the
 // source root tree where the build action command was invoked. Each directory is validated if the
 // build file can be found and follows the format "dir1:target1,target2,...". Target is optional.
-func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string, buildFiles []string) {
+func getTargetsFromDirs(ctx Context, relDir string, dirs []string, targetNamePrefix string) (targets []string) {
 	for _, dir := range dirs {
 		// The directory may have specified specific modules to build. ":" is the separator to separate
 		// the directory and the list of modules.
@@ -466,20 +456,18 @@
 			if !hasBuildFile(ctx, dir) {
 				ctx.Fatalf("Couldn't locate a build file from %s directory", dir)
 			}
-			buildFiles = append(buildFiles, filepath.Join(dir, "Android.mk"))
 		} else {
 			buildFile := findBuildFile(ctx, dir)
 			if buildFile == "" {
 				ctx.Fatalf("Build file not found for %s directory", dir)
 			}
 			newTargets = []string{convertToTarget(filepath.Dir(buildFile), targetNamePrefix)}
-			buildFiles = append(buildFiles, buildFile)
 		}
 
 		targets = append(targets, newTargets...)
 	}
 
-	return targets, buildFiles
+	return targets
 }
 
 func (c *configImpl) parseArgs(ctx Context, args []string) {
diff --git a/ui/build/config_test.go b/ui/build/config_test.go
index 463405a..df618c4 100644
--- a/ui/build/config_test.go
+++ b/ui/build/config_test.go
@@ -363,19 +363,15 @@
 		// Expected targets from the function.
 		expectedTargets []string
 
-		// Expected build from the build system.
-		expectedBuildFiles []string
-
 		// Expecting error from running test case.
 		errStr string
 	}{{
-		description:        "one target dir specified",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3"},
-		curDir:             "0",
-		expectedTargets:    []string{"MODULES-IN-0-1-2-3"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3"},
+		curDir:          "0",
+		expectedTargets: []string{"MODULES-IN-0-1-2-3"},
 	}, {
 		description: "one target dir specified, build file does not exist",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -391,21 +387,19 @@
 		curDir:      "0",
 		errStr:      "1/2/3:t1:t2 not in proper directory:target1,target2,... format (\":\" was specified more than once)",
 	}, {
-		description:        "one target dir specified, no targets specified but has colon",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3:"},
-		curDir:             "0",
-		expectedTargets:    []string{"MODULES-IN-0-1-2-3"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified, no targets specified but has colon",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3:"},
+		curDir:          "0",
+		expectedTargets: []string{"MODULES-IN-0-1-2-3"},
 	}, {
-		description:        "one target dir specified, two targets specified",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3:t1,t2"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified, two targets specified",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3:t1,t2"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2"},
 	}, {
 		description: "one target dir specified, no targets and has a comma",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -428,13 +422,12 @@
 		curDir:      "0",
 		errStr:      "0/1/2/3 not in proper directory:target1,target2,... format",
 	}, {
-		description:        "one target dir specified, many targets specified",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/3/Android.bp"},
-		dirs:               []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk"},
+		description:     "one target dir specified, many targets specified",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/3/Android.bp"},
+		dirs:            []string{"1/2/3:t1,t2,t3,t4,t5,t6,t7,t8,t9,t10"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2", "t3", "t4", "t5", "t6", "t7", "t8", "t9", "t10"},
 	}, {
 		description: "one target dir specified, one target specified, build file does not exist",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -450,29 +443,26 @@
 		curDir:      "0",
 		errStr:      "Couldn't locate a build file from 0/1/2/3 directory",
 	}, {
-		description:        "one target dir specified, build file not in target dir",
-		dirsInTrees:        []string{"0/1/2/3"},
-		buildFiles:         []string{"0/1/2/Android.mk"},
-		dirs:               []string{"1/2/3"},
-		curDir:             "0",
-		expectedTargets:    []string{"MODULES-IN-0-1-2"},
-		expectedBuildFiles: []string{"0/1/2/Android.mk"},
+		description:     "one target dir specified, build file not in target dir",
+		dirsInTrees:     []string{"0/1/2/3"},
+		buildFiles:      []string{"0/1/2/Android.mk"},
+		dirs:            []string{"1/2/3"},
+		curDir:          "0",
+		expectedTargets: []string{"MODULES-IN-0-1-2"},
 	}, {
-		description:        "multiple targets dir specified, targets specified",
-		dirsInTrees:        []string{"0/1/2/3", "0/3/4"},
-		buildFiles:         []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
-		dirs:               []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2", "t3", "t4", "t5"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+		description:     "multiple targets dir specified, targets specified",
+		dirsInTrees:     []string{"0/1/2/3", "0/3/4"},
+		buildFiles:      []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+		dirs:            []string{"1/2/3:t1,t2", "3/4:t3,t4,t5"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2", "t3", "t4", "t5"},
 	}, {
-		description:        "multiple targets dir specified, one directory has targets specified",
-		dirsInTrees:        []string{"0/1/2/3", "0/3/4"},
-		buildFiles:         []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
-		dirs:               []string{"1/2/3:t1,t2", "3/4"},
-		curDir:             "0",
-		expectedTargets:    []string{"t1", "t2", "MODULES-IN-0-3-4"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+		description:     "multiple targets dir specified, one directory has targets specified",
+		dirsInTrees:     []string{"0/1/2/3", "0/3/4"},
+		buildFiles:      []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+		dirs:            []string{"1/2/3:t1,t2", "3/4"},
+		curDir:          "0",
+		expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
 	}, {
 		description: "two dirs specified, only one dir exist",
 		dirsInTrees: []string{"0/1/2/3"},
@@ -481,13 +471,12 @@
 		curDir:      "0",
 		errStr:      "couldn't find directory 0/3/4",
 	}, {
-		description:        "multiple targets dirs specified at root source tree",
-		dirsInTrees:        []string{"0/1/2/3", "0/3/4"},
-		buildFiles:         []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
-		dirs:               []string{"0/1/2/3:t1,t2", "0/3/4"},
-		curDir:             ".",
-		expectedTargets:    []string{"t1", "t2", "MODULES-IN-0-3-4"},
-		expectedBuildFiles: []string{"0/1/2/3/Android.mk", "0/3/4/Android.mk"},
+		description:     "multiple targets dirs specified at root source tree",
+		dirsInTrees:     []string{"0/1/2/3", "0/3/4"},
+		buildFiles:      []string{"0/1/2/3/Android.bp", "0/3/4/Android.mk"},
+		dirs:            []string{"0/1/2/3:t1,t2", "0/3/4"},
+		curDir:          ".",
+		expectedTargets: []string{"t1", "t2", "MODULES-IN-0-3-4"},
 	}, {
 		description: "no directories specified",
 		dirsInTrees: []string{"0/1/2/3", "0/3/4"},
@@ -518,13 +507,10 @@
 			r := setTop(t, topDir)
 			defer r()
 
-			targets, buildFiles := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
+			targets := getTargetsFromDirs(ctx, tt.curDir, tt.dirs, "MODULES-IN-")
 			if !reflect.DeepEqual(targets, tt.expectedTargets) {
 				t.Errorf("expected %v, got %v for targets", tt.expectedTargets, targets)
 			}
-			if !reflect.DeepEqual(buildFiles, tt.expectedBuildFiles) {
-				t.Errorf("expected %v, got %v for build files", tt.expectedBuildFiles, buildFiles)
-			}
 
 			// If the execution reached here and there was an expected error code, the unit test case failed.
 			if tt.errStr != "" {
@@ -732,14 +718,11 @@
 	// Expected arguments to be in Config instance.
 	expectedArgs []string
 
-	// Expected environment variables to be set.
-	expectedEnvVars []envVar
-
 	// Expecting error from running test case.
 	expectedErrStr string
 }
 
-func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction, buildDependencies bool) {
+func testGetConfigArgs(t *testing.T, tt buildActionTestCase, action BuildAction) {
 	ctx := testContext()
 
 	defer logger.Recover(func(err error) {
@@ -753,7 +736,6 @@
 
 	// Environment variables to set it to blank on every test case run.
 	resetEnvVars := []string{
-		"ONE_SHOT_MAKEFILE",
 		"WITH_TIDY_ONLY",
 	}
 
@@ -807,17 +789,11 @@
 		t.Fatalf("failed to create %s file: %v", srcDirFileCheck, err)
 	}
 
-	args := getConfigArgs(action, tt.curDir, buildDependencies, ctx, tt.args)
+	args := getConfigArgs(action, tt.curDir, ctx, tt.args)
 	if !reflect.DeepEqual(tt.expectedArgs, args) {
 		t.Fatalf("expected %v, got %v for config arguments", tt.expectedArgs, args)
 	}
 
-	for _, env := range tt.expectedEnvVars {
-		if val := os.Getenv(env.name); val != env.value {
-			t.Errorf("expecting %s, got %s for environment variable %s", env.value, val, env.name)
-		}
-	}
-
 	// If the execution reached here and there was an expected error code, the unit test case failed.
 	if tt.expectedErrStr != "" {
 		t.Errorf("expecting error %s", tt.expectedErrStr)
@@ -826,299 +802,143 @@
 
 func TestGetConfigArgsBuildModules(t *testing.T) {
 	tests := []buildActionTestCase{{
-		description:     "normal execution from the root source tree directory",
-		dirsInTrees:     []string{"0/1/2", "0/2", "0/3"},
-		buildFiles:      []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
-		args:            []string{"-j", "fake_module", "fake_module2"},
-		curDir:          ".",
-		tidyOnly:        "",
-		expectedArgs:    []string{"-j", "fake_module", "fake_module2"},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution from the root source tree directory",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp", "0/3/Android.mk"},
+		args:         []string{"-j", "fake_module", "fake_module2"},
+		curDir:       ".",
+		tidyOnly:     "",
+		expectedArgs: []string{"-j", "fake_module", "fake_module2"},
 	}, {
-		description:     "normal execution in deep directory",
-		dirsInTrees:     []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
-		buildFiles:      []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
-		args:            []string{"-j", "fake_module", "fake_module2", "-k"},
-		curDir:          "1/2/3/4/5/6/7/8/9",
-		tidyOnly:        "",
-		expectedArgs:    []string{"-j", "fake_module", "fake_module2", "-k"},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution in deep directory",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
+		args:         []string{"-j", "fake_module", "fake_module2", "-k"},
+		curDir:       "1/2/3/4/5/6/7/8/9",
+		tidyOnly:     "",
+		expectedArgs: []string{"-j", "fake_module", "fake_module2", "-k"},
 	}, {
-		description:     "normal execution in deep directory, no targets",
-		dirsInTrees:     []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
-		buildFiles:      []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
-		args:            []string{"-j", "-k"},
-		curDir:          "1/2/3/4/5/6/7/8/9",
-		tidyOnly:        "",
-		expectedArgs:    []string{"-j", "-k"},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution in deep directory, no targets",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp", "1/2/3/4/5/6/7/8/9/1/2/3/4/5/6/Android.mk"},
+		args:         []string{"-j", "-k"},
+		curDir:       "1/2/3/4/5/6/7/8/9",
+		tidyOnly:     "",
+		expectedArgs: []string{"-j", "-k"},
 	}, {
-		description:     "normal execution in root source tree, no args",
-		dirsInTrees:     []string{"0/1/2", "0/2", "0/3"},
-		buildFiles:      []string{"0/1/2/Android.mk", "0/2/Android.bp"},
-		args:            []string{},
-		curDir:          "0/2",
-		tidyOnly:        "",
-		expectedArgs:    []string{},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution in root source tree, no args",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp"},
+		args:         []string{},
+		curDir:       "0/2",
+		tidyOnly:     "",
+		expectedArgs: []string{},
 	}, {
-		description:     "normal execution in symlink root source tree, no args",
-		dirsInTrees:     []string{"0/1/2", "0/2", "0/3"},
-		buildFiles:      []string{"0/1/2/Android.mk", "0/2/Android.bp"},
-		rootSymlink:     true,
-		args:            []string{},
-		curDir:          "0/2",
-		tidyOnly:        "",
-		expectedArgs:    []string{},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution in symlink root source tree, no args",
+		dirsInTrees:  []string{"0/1/2", "0/2", "0/3"},
+		buildFiles:   []string{"0/1/2/Android.mk", "0/2/Android.bp"},
+		rootSymlink:  true,
+		args:         []string{},
+		curDir:       "0/2",
+		tidyOnly:     "",
+		expectedArgs: []string{},
 	}}
 	for _, tt := range tests {
 		t.Run("build action BUILD_MODULES with dependencies, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES, true)
-		})
-	}
-}
-
-// TODO: Remove this test case once mm shell build command has been deprecated.
-func TestGetConfigArgsBuildModulesInDirecotoryNoDeps(t *testing.T) {
-	tests := []buildActionTestCase{{
-		description:  "normal execution in a directory",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/2/Android.mk"},
-		args:         []string{"-j", "-k", "showcommands", "fake-module"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"-j", "-k", "showcommands", "fake-module", "MODULES-IN-0-1-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/Android.mk"}},
-	}, {
-		description:  "makefile in parent directory",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/Android.mk"}},
-	}, {
-		description:  "build file not found",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{},
-		args:         []string{},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/Android.mk"}},
-		expectedErrStr: "Build file not found for 0/1/2 directory",
-	}, {
-		description:  "build action executed at root directory",
-		dirsInTrees:  []string{},
-		buildFiles:   []string{},
-		args:         []string{},
-		curDir:       ".",
-		tidyOnly:     "",
-		expectedArgs: []string{},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
-	}, {
-		description:  "GET-INSTALL-PATH specified,",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{"GET-INSTALL-PATH"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/Android.mk"}},
-	}, {
-		description:  "tidy only environment variable specified,",
-		dirsInTrees:  []string{"0/1/2"},
-		buildFiles:   []string{"0/1/Android.mk"},
-		args:         []string{"GET-INSTALL-PATH"},
-		curDir:       "0/1/2",
-		tidyOnly:     "true",
-		expectedArgs: []string{"tidy_only"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/Android.mk"}},
-	}}
-	for _, tt := range tests {
-		t.Run("build action BUILD_MODULES_IN_DIR without their dependencies, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, false)
+			testGetConfigArgs(t, tt, BUILD_MODULES)
 		})
 	}
 }
 
 func TestGetConfigArgsBuildModulesInDirectory(t *testing.T) {
 	tests := []buildActionTestCase{{
-		description:     "normal execution in a directory",
-		dirsInTrees:     []string{"0/1/2"},
-		buildFiles:      []string{"0/1/2/Android.mk"},
-		args:            []string{"fake-module"},
-		curDir:          "0/1/2",
-		tidyOnly:        "",
-		expectedArgs:    []string{"fake-module", "MODULES-IN-0-1-2"},
-		expectedEnvVars: []envVar{},
+		description:  "normal execution in a directory",
+		dirsInTrees:  []string{"0/1/2"},
+		buildFiles:   []string{"0/1/2/Android.mk"},
+		args:         []string{"fake-module"},
+		curDir:       "0/1/2",
+		tidyOnly:     "",
+		expectedArgs: []string{"fake-module", "MODULES-IN-0-1-2"},
 	}, {
-		description:     "build file in parent directory",
-		dirsInTrees:     []string{"0/1/2"},
-		buildFiles:      []string{"0/1/Android.mk"},
-		args:            []string{},
-		curDir:          "0/1/2",
-		tidyOnly:        "",
-		expectedArgs:    []string{"MODULES-IN-0-1"},
-		expectedEnvVars: []envVar{},
+		description:  "build file in parent directory",
+		dirsInTrees:  []string{"0/1/2"},
+		buildFiles:   []string{"0/1/Android.mk"},
+		args:         []string{},
+		curDir:       "0/1/2",
+		tidyOnly:     "",
+		expectedArgs: []string{"MODULES-IN-0-1"},
 	},
 		{
-			description:     "build file in parent directory, multiple module names passed in",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/1/Android.mk"},
-			args:            []string{"fake-module1", "fake-module2", "fake-module3"},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
-			expectedEnvVars: []envVar{},
+			description:  "build file in parent directory, multiple module names passed in",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{"fake-module1", "fake-module2", "fake-module3"},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"fake-module1", "fake-module2", "fake-module3", "MODULES-IN-0-1"},
 		}, {
-			description:     "build file in 2nd level parent directory",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/Android.bp"},
-			args:            []string{},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"MODULES-IN-0"},
-			expectedEnvVars: []envVar{},
+			description:  "build file in 2nd level parent directory",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/Android.bp"},
+			args:         []string{},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"MODULES-IN-0"},
 		}, {
-			description:     "build action executed at root directory",
-			dirsInTrees:     []string{},
-			buildFiles:      []string{},
-			rootSymlink:     false,
-			args:            []string{},
-			curDir:          ".",
-			tidyOnly:        "",
-			expectedArgs:    []string{},
-			expectedEnvVars: []envVar{},
+			description:  "build action executed at root directory",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			rootSymlink:  false,
+			args:         []string{},
+			curDir:       ".",
+			tidyOnly:     "",
+			expectedArgs: []string{},
 		}, {
-			description:     "build action executed at root directory in symlink",
-			dirsInTrees:     []string{},
-			buildFiles:      []string{},
-			rootSymlink:     true,
-			args:            []string{},
-			curDir:          ".",
-			tidyOnly:        "",
-			expectedArgs:    []string{},
-			expectedEnvVars: []envVar{},
+			description:  "build action executed at root directory in symlink",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			rootSymlink:  true,
+			args:         []string{},
+			curDir:       ".",
+			tidyOnly:     "",
+			expectedArgs: []string{},
 		}, {
-			description:     "build file not found",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{},
-			args:            []string{},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"MODULES-IN-0-1-2"},
-			expectedEnvVars: []envVar{},
-			expectedErrStr:  "Build file not found for 0/1/2 directory",
+			description:    "build file not found",
+			dirsInTrees:    []string{"0/1/2"},
+			buildFiles:     []string{},
+			args:           []string{},
+			curDir:         "0/1/2",
+			tidyOnly:       "",
+			expectedArgs:   []string{"MODULES-IN-0-1-2"},
+			expectedErrStr: "Build file not found for 0/1/2 directory",
 		}, {
-			description:     "GET-INSTALL-PATH specified,",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/1/Android.mk"},
-			args:            []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
-			curDir:          "0/1/2",
-			tidyOnly:        "",
-			expectedArgs:    []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
-			expectedEnvVars: []envVar{},
+			description:  "GET-INSTALL-PATH specified,",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{"GET-INSTALL-PATH", "-j", "-k", "GET-INSTALL-PATH"},
+			curDir:       "0/1/2",
+			tidyOnly:     "",
+			expectedArgs: []string{"-j", "-k", "GET-INSTALL-PATH-IN-0-1"},
 		}, {
-			description:     "tidy only environment variable specified,",
-			dirsInTrees:     []string{"0/1/2"},
-			buildFiles:      []string{"0/1/Android.mk"},
-			args:            []string{"GET-INSTALL-PATH"},
-			curDir:          "0/1/2",
-			tidyOnly:        "true",
-			expectedArgs:    []string{"tidy_only"},
-			expectedEnvVars: []envVar{},
+			description:  "tidy only environment variable specified,",
+			dirsInTrees:  []string{"0/1/2"},
+			buildFiles:   []string{"0/1/Android.mk"},
+			args:         []string{"GET-INSTALL-PATH"},
+			curDir:       "0/1/2",
+			tidyOnly:     "true",
+			expectedArgs: []string{"tidy_only"},
 		}, {
-			description:     "normal execution in root directory with args",
-			dirsInTrees:     []string{},
-			buildFiles:      []string{},
-			args:            []string{"-j", "-k", "fake_module"},
-			curDir:          "",
-			tidyOnly:        "",
-			expectedArgs:    []string{"-j", "-k", "fake_module"},
-			expectedEnvVars: []envVar{},
+			description:  "normal execution in root directory with args",
+			dirsInTrees:  []string{},
+			buildFiles:   []string{},
+			args:         []string{"-j", "-k", "fake_module"},
+			curDir:       "",
+			tidyOnly:     "",
+			expectedArgs: []string{"-j", "-k", "fake_module"},
 		}}
 	for _, tt := range tests {
 		t.Run("build action BUILD_MODULES_IN_DIR, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY, true)
-		})
-	}
-}
-
-// TODO: Remove this test case once mmm shell build command has been deprecated.
-func TestGetConfigArgsBuildModulesInDirectoriesNoDeps(t *testing.T) {
-	tests := []buildActionTestCase{{
-		description:  "normal execution in a directory",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"3.1/:t1,t2", "3.2/:t3,t4", "3.3/:t5,t6"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"t1", "t2", "t3", "t4", "t5", "t6"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}, {
-		description:  "GET-INSTALL-PATH specified",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
-		curDir:       "0/1/2",
-		tidyOnly:     "",
-		expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "t6"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}, {
-		description:  "tidy only environment variable specified",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"GET-INSTALL-PATH", "3.1/", "3.2/", "3.3/:t6"},
-		curDir:       "0/1/2",
-		tidyOnly:     "1",
-		expectedArgs: []string{"tidy_only"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}, {
-		description:  "normal execution from top dir directory",
-		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
-		buildFiles:   []string{"0/1/2/3.1/Android.bp", "0/1/2/3.2/Android.bp", "0/1/2/3.3/Android.bp"},
-		args:         []string{"0/1/2/3.1", "0/1/2/3.2/:t3,t4", "0/1/2/3.3/:t5,t6"},
-		curDir:       ".",
-		tidyOnly:     "",
-		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "t3", "t4", "t5", "t6"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: "0/1/2/3.1/Android.mk 0/1/2/3.2/Android.mk 0/1/2/3.3/Android.mk"}},
-	}}
-	for _, tt := range tests {
-		t.Run("build action BUILD_MODULES_IN_DIRS_NO_DEPS, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, false)
+			testGetConfigArgs(t, tt, BUILD_MODULES_IN_A_DIRECTORY)
 		})
 	}
 }
@@ -1132,10 +952,6 @@
 		curDir:       "0/1/2",
 		tidyOnly:     "",
 		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-2-3.3"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "GET-INSTALL-PATH specified",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3"},
@@ -1144,10 +960,6 @@
 		curDir:       "0/1",
 		tidyOnly:     "",
 		expectedArgs: []string{"GET-INSTALL-PATH-IN-0-1-2-3.1", "GET-INSTALL-PATH-IN-0-1-2-3.2", "GET-INSTALL-PATH-IN-0-1"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "tidy only environment variable specified",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/2/3.3"},
@@ -1156,10 +968,6 @@
 		curDir:       "0/1/2",
 		tidyOnly:     "1",
 		expectedArgs: []string{"tidy_only"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "normal execution from top dir directory",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
@@ -1169,10 +977,6 @@
 		curDir:       ".",
 		tidyOnly:     "",
 		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}, {
 		description:  "normal execution from top dir directory in symlink",
 		dirsInTrees:  []string{"0/1/2/3.1", "0/1/2/3.2", "0/1/3", "0/2"},
@@ -1182,14 +986,10 @@
 		curDir:       ".",
 		tidyOnly:     "",
 		expectedArgs: []string{"MODULES-IN-0-1-2-3.1", "MODULES-IN-0-1-2-3.2", "MODULES-IN-0-1-3", "MODULES-IN-0-2"},
-		expectedEnvVars: []envVar{
-			envVar{
-				name:  "ONE_SHOT_MAKEFILE",
-				value: ""}},
 	}}
 	for _, tt := range tests {
 		t.Run("build action BUILD_MODULES_IN_DIRS, "+tt.description, func(t *testing.T) {
-			testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES, true)
+			testGetConfigArgs(t, tt, BUILD_MODULES_IN_DIRECTORIES)
 		})
 	}
 }
diff --git a/ui/build/kati.go b/ui/build/kati.go
index 5ad966a..9ddbbea 100644
--- a/ui/build/kati.go
+++ b/ui/build/kati.go
@@ -42,9 +42,6 @@
 	if args := config.KatiArgs(); len(args) > 0 {
 		katiSuffix += "-" + spaceSlashReplacer.Replace(strings.Join(args, "_"))
 	}
-	if oneShot, ok := config.Environment().Get("ONE_SHOT_MAKEFILE"); ok {
-		katiSuffix += "-" + spaceSlashReplacer.Replace(oneShot)
-	}
 
 	// If the suffix is too long, replace it with a md5 hash and write a
 	// file that contains the original suffix.