Merge "Revert "Add bp2build support for cc_library_static.""
diff --git a/android/Android.bp b/android/Android.bp
index 2406321..4da0f4e 100644
--- a/android/Android.bp
+++ b/android/Android.bp
@@ -85,6 +85,7 @@
         "androidmk_test.go",
         "apex_test.go",
         "arch_test.go",
+        "bazel_test.go",
         "config_test.go",
         "csuite_config_test.go",
         "depset_test.go",
diff --git a/android/bazel.go b/android/bazel.go
index 683495b..9b06426 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -32,7 +32,13 @@
 
 	// If true, bp2build will generate the converted Bazel target for this module. Note: this may
 	// cause a conflict due to the duplicate targets if label is also set.
-	Bp2build_available bool
+	//
+	// This is a bool pointer to support tristates: true, false, not set.
+	//
+	// To opt-in a module, set bazel_module: { bp2build_available: true }
+	// To opt-out a module, set bazel_module: { bp2build_available: false }
+	// To defer the default setting for the directory, do not set the value.
+	Bp2build_available *bool
 }
 
 // Properties contains common module properties for Bazel migration purposes.
@@ -54,9 +60,9 @@
 	HasHandcraftedLabel() bool
 	HandcraftedLabel() string
 	GetBazelLabel(ctx BazelConversionPathContext, module blueprint.Module) string
-	ConvertWithBp2build() bool
+	ConvertWithBp2build(ctx BazelConversionPathContext) bool
 	GetBazelBuildFileContents(c Config, path, name string) (string, error)
-	ConvertedToBazel() bool
+	ConvertedToBazel(ctx BazelConversionPathContext) bool
 }
 
 // BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
@@ -91,15 +97,91 @@
 	if b.HasHandcraftedLabel() {
 		return b.HandcraftedLabel()
 	}
-	if b.ConvertWithBp2build() {
+	if b.ConvertWithBp2build(ctx) {
 		return bp2buildModuleLabel(ctx, module)
 	}
 	return "" // no label for unconverted module
 }
 
+// Configuration to decide if modules in a directory should default to true/false for bp2build_available
+type Bp2BuildConfig map[string]BazelConversionConfigEntry
+type BazelConversionConfigEntry int
+
+const (
+	// iota + 1 ensures that the int value is not 0 when used in the Bp2buildAllowlist map,
+	// which can also mean that the key doesn't exist in a lookup.
+
+	// all modules in this package and subpackages default to bp2build_available: true.
+	// allows modules to opt-out.
+	Bp2BuildDefaultTrueRecursively BazelConversionConfigEntry = iota + 1
+
+	// all modules in this package (not recursively) default to bp2build_available: false.
+	// allows modules to opt-in.
+	Bp2BuildDefaultFalse
+)
+
+var (
+	// Configure modules in these directories to enable bp2build_available: true or false by default.
+	bp2buildDefaultConfig = Bp2BuildConfig{
+		"bionic":                Bp2BuildDefaultTrueRecursively,
+		"system/core/libcutils": Bp2BuildDefaultTrueRecursively,
+		"system/logging/liblog": Bp2BuildDefaultTrueRecursively,
+	}
+)
+
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
-func (b *BazelModuleBase) ConvertWithBp2build() bool {
-	return b.bazelProperties.Bazel_module.Bp2build_available
+func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
+	// Ensure that the module type of this module has a bp2build converter. This
+	// prevents mixed builds from using auto-converted modules just by matching
+	// the package dir; it also has to have a bp2build mutator as well.
+	if ctx.Config().bp2buildModuleTypeConfig[ctx.ModuleType()] == false {
+		return false
+	}
+
+	packagePath := ctx.ModuleDir()
+	config := ctx.Config().bp2buildPackageConfig
+
+	// This is a tristate value: true, false, or unset.
+	propValue := b.bazelProperties.Bazel_module.Bp2build_available
+	if bp2buildDefaultTrueRecursively(packagePath, config) {
+		// Allow modules to explicitly opt-out.
+		return proptools.BoolDefault(propValue, true)
+	}
+
+	// Allow modules to explicitly opt-in.
+	return proptools.BoolDefault(propValue, false)
+}
+
+// bp2buildDefaultTrueRecursively checks that the package contains a prefix from the
+// set of package prefixes where all modules must be converted. That is, if the
+// package is x/y/z, and the list contains either x, x/y, or x/y/z, this function will
+// return true.
+//
+// However, if the package is x/y, and it matches a Bp2BuildDefaultFalse "x/y" entry
+// exactly, this module will return false early.
+//
+// This function will also return false if the package doesn't match anything in
+// the config.
+func bp2buildDefaultTrueRecursively(packagePath string, config Bp2BuildConfig) bool {
+	ret := false
+
+	if config[packagePath] == Bp2BuildDefaultFalse {
+		return false
+	}
+
+	packagePrefix := ""
+	// e.g. for x/y/z, iterate over x, x/y, then x/y/z, taking the final value from the allowlist.
+	for _, part := range strings.Split(packagePath, "/") {
+		packagePrefix += part
+		if config[packagePrefix] == Bp2BuildDefaultTrueRecursively {
+			// package contains this prefix and this prefix should convert all modules
+			return true
+		}
+		// Continue to the next part of the package dir.
+		packagePrefix += "/"
+	}
+
+	return ret
 }
 
 // GetBazelBuildFileContents returns the file contents of a hand-crafted BUILD file if available or
@@ -126,6 +208,6 @@
 
 // ConvertedToBazel returns whether this module has been converted to Bazel, whether automatically
 // or manually
-func (b *BazelModuleBase) ConvertedToBazel() bool {
-	return b.ConvertWithBp2build() || b.HasHandcraftedLabel()
+func (b *BazelModuleBase) ConvertedToBazel(ctx BazelConversionPathContext) bool {
+	return b.ConvertWithBp2build(ctx) || b.HasHandcraftedLabel()
 }
diff --git a/android/bazel_test.go b/android/bazel_test.go
new file mode 100644
index 0000000..e5d8fbb
--- /dev/null
+++ b/android/bazel_test.go
@@ -0,0 +1,134 @@
+// Copyright 2021 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 android
+
+import "testing"
+
+func TestConvertAllModulesInPackage(t *testing.T) {
+	testCases := []struct {
+		prefixes   Bp2BuildConfig
+		packageDir string
+	}{
+		{
+			prefixes: Bp2BuildConfig{
+				"a": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a/b": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a/b":   Bp2BuildDefaultTrueRecursively,
+				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":     Bp2BuildDefaultTrueRecursively,
+				"d/e/f": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":     Bp2BuildDefaultFalse,
+				"a/b":   Bp2BuildDefaultTrueRecursively,
+				"a/b/c": Bp2BuildDefaultFalse,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":     Bp2BuildDefaultTrueRecursively,
+				"a/b":   Bp2BuildDefaultFalse,
+				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a",
+		},
+	}
+
+	for _, test := range testCases {
+		if !bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+			t.Errorf("Expected to convert all modules in %s based on %v, but failed.", test.packageDir, test.prefixes)
+		}
+	}
+}
+
+func TestModuleOptIn(t *testing.T) {
+	testCases := []struct {
+		prefixes   Bp2BuildConfig
+		packageDir string
+	}{
+		{
+			prefixes: Bp2BuildConfig{
+				"a/b": Bp2BuildDefaultFalse,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":   Bp2BuildDefaultFalse,
+				"a/b": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a/b": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a", // opt-in by default
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":     Bp2BuildDefaultTrueRecursively,
+				"d/e/f": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "foo/bar",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":     Bp2BuildDefaultTrueRecursively,
+				"a/b":   Bp2BuildDefaultFalse,
+				"a/b/c": Bp2BuildDefaultTrueRecursively,
+			},
+			packageDir: "a/b",
+		},
+		{
+			prefixes: Bp2BuildConfig{
+				"a":     Bp2BuildDefaultFalse,
+				"a/b":   Bp2BuildDefaultTrueRecursively,
+				"a/b/c": Bp2BuildDefaultFalse,
+			},
+			packageDir: "a",
+		},
+	}
+
+	for _, test := range testCases {
+		if bp2buildDefaultTrueRecursively(test.packageDir, test.prefixes) {
+			t.Errorf("Expected to allow module opt-in in %s based on %v, but failed.", test.packageDir, test.prefixes)
+		}
+	}
+}
diff --git a/android/config.go b/android/config.go
index e335de0..cfbc37f 100644
--- a/android/config.go
+++ b/android/config.go
@@ -140,6 +140,9 @@
 	fs         pathtools.FileSystem
 	mockBpList string
 
+	bp2buildPackageConfig    Bp2BuildConfig
+	bp2buildModuleTypeConfig map[string]bool
+
 	// If testAllowNonExistentPaths is true then PathForSource and PathForModuleSrc won't error
 	// in tests when a path doesn't exist.
 	TestAllowNonExistentPaths bool
@@ -281,6 +284,8 @@
 
 	config.mockFileSystem(bp, fs)
 
+	config.bp2buildModuleTypeConfig = map[string]bool{}
+
 	return Config{config}
 }
 
@@ -452,6 +457,8 @@
 			Bool(config.productVariables.ClangCoverage))
 
 	config.BazelContext, err = NewBazelContext(config)
+	config.bp2buildPackageConfig = bp2buildDefaultConfig
+	config.bp2buildModuleTypeConfig = make(map[string]bool)
 
 	return Config{config}, err
 }
diff --git a/android/filegroup.go b/android/filegroup.go
index 2eb4741..abbb4d4 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -53,7 +53,7 @@
 
 func FilegroupBp2Build(ctx TopDownMutatorContext) {
 	fg, ok := ctx.Module().(*fileGroup)
-	if !ok || !fg.ConvertWithBp2build() {
+	if !ok || !fg.ConvertWithBp2build(ctx) {
 		return
 	}
 
diff --git a/android/mutator.go b/android/mutator.go
index 9552aa1..9e99bee 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -226,7 +226,7 @@
 
 var bp2buildPreArchMutators = []RegisterMutatorFunc{}
 var bp2buildDepsMutators = []RegisterMutatorFunc{}
-var bp2buildMutators = []RegisterMutatorFunc{}
+var bp2buildMutators = map[string]RegisterMutatorFunc{}
 
 // RegisterBp2BuildMutator registers specially crafted mutators for
 // converting Blueprint/Android modules into special modules that can
@@ -237,7 +237,7 @@
 	f := func(ctx RegisterMutatorsContext) {
 		ctx.TopDown(moduleType, m)
 	}
-	bp2buildMutators = append(bp2buildMutators, f)
+	bp2buildMutators[moduleType] = f
 }
 
 // PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
diff --git a/android/paths.go b/android/paths.go
index babf48c..0ed988a 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -340,6 +340,7 @@
 
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 	Module() Module
+	ModuleType() string
 	OtherModuleName(m blueprint.Module) string
 	OtherModuleDir(m blueprint.Module) string
 }
@@ -450,7 +451,7 @@
 	// TODO(b/165114590): Convert tag (":name{.tag}") to corresponding Bazel implicit output targets.
 	b, ok := module.(Bazelable)
 	// TODO(b/181155349): perhaps return an error here if the module can't be/isn't being converted
-	if !ok || !b.ConvertedToBazel() {
+	if !ok || !b.ConvertedToBazel(ctx) {
 		return bp2buildModuleLabel(ctx, module)
 	}
 	return b.GetBazelLabel(ctx, module)
diff --git a/android/register.go b/android/register.go
index aeaa6ff..35469d4 100644
--- a/android/register.go
+++ b/android/register.go
@@ -174,7 +174,13 @@
 		t.register(ctx)
 	}
 
-	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutators)
+	bp2buildMutatorList := []RegisterMutatorFunc{}
+	for t, f := range bp2buildMutators {
+		ctx.config.bp2buildModuleTypeConfig[t] = true
+		bp2buildMutatorList = append(bp2buildMutatorList, f)
+	}
+
+	RegisterMutatorsForBazelConversion(ctx, bp2buildPreArchMutators, bp2buildDepsMutators, bp2buildMutatorList)
 }
 
 // Register the pipeline of singletons, module types, and mutators for
diff --git a/android/testing.go b/android/testing.go
index 0dfe38c..236e724 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -158,12 +158,17 @@
 	ctx.finalDeps = append(ctx.finalDeps, f)
 }
 
+func (ctx *TestContext) RegisterBp2BuildConfig(config Bp2BuildConfig) {
+	ctx.config.bp2buildPackageConfig = config
+}
+
 // RegisterBp2BuildMutator registers a BazelTargetModule mutator for converting a module
 // type to the equivalent Bazel target.
 func (ctx *TestContext) RegisterBp2BuildMutator(moduleType string, m func(TopDownMutatorContext)) {
 	f := func(ctx RegisterMutatorsContext) {
 		ctx.TopDown(moduleType, m)
 	}
+	ctx.config.bp2buildModuleTypeConfig[moduleType] = true
 	ctx.bp2buildMutators = append(ctx.bp2buildMutators, f)
 }
 
diff --git a/apex/apex.go b/apex/apex.go
index a67fe1f..dca5595 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -29,7 +29,6 @@
 	"android/soong/android"
 	"android/soong/bpf"
 	"android/soong/cc"
-	"android/soong/dexpreopt"
 	prebuilt_etc "android/soong/etc"
 	"android/soong/filesystem"
 	"android/soong/java"
@@ -1573,9 +1572,6 @@
 		if dt, ok := depTag.(dependencyTag); ok && !dt.payload {
 			return false
 		}
-		if depTag == dexpreopt.Dex2oatDepTag {
-			return false
-		}
 
 		ai := ctx.OtherModuleProvider(child, android.ApexInfoProvider).(android.ApexInfo)
 		externalDep := !android.InList(ctx.ModuleName(), ai.InApexes)
diff --git a/bp2build/bp2build.go b/bp2build/bp2build.go
index 97a5137..007d6d8 100644
--- a/bp2build/bp2build.go
+++ b/bp2build/bp2build.go
@@ -18,44 +18,48 @@
 	"android/soong/android"
 	"fmt"
 	"os"
+	"strings"
 )
 
-// The Bazel bp2build code generator is responsible for writing .bzl files that are equivalent to
-// Android.bp files that are capable of being built with Bazel.
+// Codegen is the backend of bp2build. The code generator is responsible for
+// writing .bzl files that are equivalent to Android.bp files that are capable
+// of being built with Bazel.
 func Codegen(ctx *CodegenContext) CodegenMetrics {
 	outputDir := android.PathForOutput(ctx, "bp2build")
 	android.RemoveAllOutputDir(outputDir)
 
-	ruleShims := CreateRuleShims(android.ModuleTypeFactories())
-
 	buildToTargets, metrics := GenerateBazelTargets(ctx)
 
-	filesToWrite := CreateBazelFiles(ruleShims, buildToTargets, ctx.mode)
+	filesToWrite := CreateBazelFiles(nil, buildToTargets, ctx.mode)
+
+	generatedBuildFiles := []string{}
 	for _, f := range filesToWrite {
-		if err := writeFile(outputDir, ctx, f); err != nil {
+		p := getOrCreateOutputDir(outputDir, ctx, f.Dir).Join(ctx, f.Basename)
+		if err := writeFile(ctx, p, f.Contents); err != nil {
 			fmt.Errorf("Failed to write %q (dir %q) due to %q", f.Basename, f.Dir, err)
 		}
+		// if these generated files are modified, regenerate on next run.
+		generatedBuildFiles = append(generatedBuildFiles, p.String())
 	}
 
+	// The MANIFEST file contains the full list of files generated by bp2build, excluding itself.
+	// Its purpose is for downstream tools to understand the set of files converted by bp2build.
+	manifestFile := outputDir.Join(ctx, "MANIFEST")
+	writeFile(ctx, manifestFile, strings.Join(generatedBuildFiles, "\n"))
+	generatedBuildFiles = append(generatedBuildFiles, manifestFile.String())
+
 	return metrics
 }
 
-func writeFile(outputDir android.OutputPath, ctx android.PathContext, f BazelFile) error {
-	return writeReadOnlyFile(ctx, getOutputPath(outputDir, ctx, f.Dir), f.Basename, f.Contents)
+// Get the output directory and create it if it doesn't exist.
+func getOrCreateOutputDir(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
+	dirPath := outputDir.Join(ctx, dir)
+	android.CreateOutputDirIfNonexistent(dirPath, os.ModePerm)
+	return dirPath
 }
 
-func getOutputPath(outputDir android.OutputPath, ctx android.PathContext, dir string) android.OutputPath {
-	return outputDir.Join(ctx, dir)
-}
-
-// The auto-conversion directory should be read-only, sufficient for bazel query. The files
-// are not intended to be edited by end users.
-func writeReadOnlyFile(ctx android.PathContext, dir android.OutputPath, baseName, content string) error {
-	android.CreateOutputDirIfNonexistent(dir, os.ModePerm)
-	pathToFile := dir.Join(ctx, baseName)
-
-	// 0444 is read-only
-	err := android.WriteFileToOutputDir(pathToFile, []byte(content), 0444)
-
-	return err
+func writeFile(ctx android.PathContext, pathToFile android.OutputPath, content string) error {
+	// These files are made editable to allow users to modify and iterate on them
+	// in the source tree.
+	return android.WriteFileToOutputDir(pathToFile, []byte(content), 0644)
 }
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 89acbe9..b9b250a 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -412,7 +412,7 @@
 		config := android.TestConfig(buildDir, nil, testCase.bp, nil)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType("custom", customModuleFactory)
-		ctx.RegisterBp2BuildMutator("custom_starlark", customBp2BuildMutatorFromStarlark)
+		ctx.RegisterBp2BuildMutator("custom", customBp2BuildMutatorFromStarlark)
 		ctx.RegisterForBazelConversion()
 
 		_, errs := ctx.ParseFileList(dir, []string{"Android.bp"})
@@ -1144,7 +1144,7 @@
 	}
 }
 
-func TestAllowlistingBp2buildTargets(t *testing.T) {
+func TestAllowlistingBp2buildTargetsExplicitly(t *testing.T) {
 	testCases := []struct {
 		moduleTypeUnderTest                string
 		moduleTypeUnderTestFactory         android.ModuleFactory
@@ -1222,6 +1222,124 @@
 	}
 }
 
+func TestAllowlistingBp2buildTargetsWithConfig(t *testing.T) {
+	testCases := []struct {
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator bp2buildMutator
+		expectedCount                      map[string]int
+		description                        string
+		bp2buildConfig                     android.Bp2BuildConfig
+		checkDir                           string
+		fs                                 map[string]string
+	}{
+		{
+			description:                        "test bp2build config package and subpackages config",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			expectedCount: map[string]int{
+				"migrated":                           1,
+				"migrated/but_not_really":            0,
+				"migrated/but_not_really/but_really": 1,
+				"not_migrated":                       0,
+				"also_not_migrated":                  0,
+			},
+			bp2buildConfig: android.Bp2BuildConfig{
+				"migrated":                android.Bp2BuildDefaultTrueRecursively,
+				"migrated/but_not_really": android.Bp2BuildDefaultFalse,
+				"not_migrated":            android.Bp2BuildDefaultFalse,
+			},
+			fs: map[string]string{
+				"migrated/Android.bp":                           `filegroup { name: "a" }`,
+				"migrated/but_not_really/Android.bp":            `filegroup { name: "b" }`,
+				"migrated/but_not_really/but_really/Android.bp": `filegroup { name: "c" }`,
+				"not_migrated/Android.bp":                       `filegroup { name: "d" }`,
+				"also_not_migrated/Android.bp":                  `filegroup { name: "e" }`,
+			},
+		},
+		{
+			description:                        "test bp2build config opt-in and opt-out",
+			moduleTypeUnderTest:                "filegroup",
+			moduleTypeUnderTestFactory:         android.FileGroupFactory,
+			moduleTypeUnderTestBp2BuildMutator: android.FilegroupBp2Build,
+			expectedCount: map[string]int{
+				"package-opt-in":             2,
+				"package-opt-in/subpackage":  0,
+				"package-opt-out":            1,
+				"package-opt-out/subpackage": 0,
+			},
+			bp2buildConfig: android.Bp2BuildConfig{
+				"package-opt-in":  android.Bp2BuildDefaultFalse,
+				"package-opt-out": android.Bp2BuildDefaultTrueRecursively,
+			},
+			fs: map[string]string{
+				"package-opt-in/Android.bp": `
+filegroup { name: "opt-in-a" }
+filegroup { name: "opt-in-b", bazel_module: { bp2build_available: true } }
+filegroup { name: "opt-in-c", bazel_module: { bp2build_available: true } }
+`,
+
+				"package-opt-in/subpackage/Android.bp": `
+filegroup { name: "opt-in-d" } // parent package not configured to DefaultTrueRecursively
+`,
+
+				"package-opt-out/Android.bp": `
+filegroup { name: "opt-out-a" }
+filegroup { name: "opt-out-b", bazel_module: { bp2build_available: false } }
+filegroup { name: "opt-out-c", bazel_module: { bp2build_available: false } }
+`,
+
+				"package-opt-out/subpackage/Android.bp": `
+filegroup { name: "opt-out-g", bazel_module: { bp2build_available: false } }
+filegroup { name: "opt-out-h", bazel_module: { bp2build_available: false } }
+`,
+			},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		fs := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.fs {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			fs[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, "", fs)
+		ctx := android.NewTestContext(config)
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterBp2BuildConfig(testCase.bp2buildConfig)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		android.FailIfErrored(t, errs)
+		_, errs = ctx.ResolveDependencies(config)
+		android.FailIfErrored(t, errs)
+
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+
+		// For each directory, test that the expected number of generated targets is correct.
+		for dir, expectedCount := range testCase.expectedCount {
+			bazelTargets := generateBazelTargetsForDir(codegenCtx, dir)
+			if actualCount := len(bazelTargets); actualCount != expectedCount {
+				t.Fatalf(
+					"%s: Expected %d bazel target for %s package, got %d",
+					testCase.description,
+					expectedCount,
+					dir,
+					actualCount)
+			}
+
+		}
+	}
+}
+
 func TestCombineBuildFilesBp2buildTargets(t *testing.T) {
 	testCases := []struct {
 		description                        string
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 7877bb8..787222d 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -21,14 +21,15 @@
 	mode CodegenMode) []BazelFile {
 	files := make([]BazelFile, 0, len(ruleShims)+len(buildToTargets)+numAdditionalFiles)
 
-	// Write top level files: WORKSPACE and BUILD. These files are empty.
+	// Write top level files: WORKSPACE. These files are empty.
 	files = append(files, newFile("", "WORKSPACE", ""))
-	// Used to denote that the top level directory is a package.
-	files = append(files, newFile("", GeneratedBuildFileName, ""))
-
-	files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
 
 	if mode == QueryView {
+		// Used to denote that the top level directory is a package.
+		files = append(files, newFile("", GeneratedBuildFileName, ""))
+
+		files = append(files, newFile(bazelRulesSubDir, GeneratedBuildFileName, ""))
+
 		// These files are only used for queryview.
 		files = append(files, newFile(bazelRulesSubDir, "providers.bzl", providersBzl))
 
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index ec5f27e..a115ddc 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -89,16 +89,8 @@
 	expectedFilePaths := []filepath{
 		{
 			dir:      "",
-			basename: "BUILD",
-		},
-		{
-			dir:      "",
 			basename: "WORKSPACE",
 		},
-		{
-			dir:      bazelRulesSubDir,
-			basename: "BUILD",
-		},
 	}
 
 	assertFilecountsAreEqual(t, files, expectedFilePaths)
diff --git a/bp2build/testing.go b/bp2build/testing.go
index a15a4a5..ede8044 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -126,7 +126,7 @@
 
 func customBp2BuildMutator(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
-		if !m.ConvertWithBp2build() {
+		if !m.ConvertWithBp2build(ctx) {
 			return
 		}
 
@@ -147,7 +147,7 @@
 // module to target.
 func customBp2BuildMutatorFromStarlark(ctx android.TopDownMutatorContext) {
 	if m, ok := ctx.Module().(*customModule); ok {
-		if !m.ConvertWithBp2build() {
+		if !m.ConvertWithBp2build(ctx) {
 			return
 		}
 
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 115b775..0f4d8a6 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -86,7 +86,7 @@
 		return
 	}
 
-	if !module.ConvertWithBp2build() {
+	if !module.ConvertWithBp2build(ctx) {
 		return
 	}
 
diff --git a/cc/object.go b/cc/object.go
index 6bea28b..664be8d 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -131,7 +131,7 @@
 // Bazel equivalent target, plus any necessary include deps for the cc_object.
 func ObjectBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build() {
+	if !ok || !m.ConvertWithBp2build(ctx) {
 		return
 	}
 
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 94efa4d..e2fc78c 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -21,8 +21,10 @@
 	"os"
 	"path/filepath"
 	"strings"
+	"time"
 
 	"android/soong/shared"
+
 	"github.com/google/blueprint/bootstrap"
 
 	"android/soong/android"
@@ -191,13 +193,24 @@
 func writeUsedVariablesFile(path string, configuration android.Config) {
 	data, err := shared.EnvFileContents(configuration.EnvDeps())
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s\n", path, err)
 		os.Exit(1)
 	}
 
 	err = ioutil.WriteFile(path, data, 0666)
 	if err != nil {
-		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s", path, err)
+		fmt.Fprintf(os.Stderr, "error writing used variables file %s: %s\n", path, err)
+		os.Exit(1)
+	}
+
+	// Touch the output Ninja file so that it's not older than the file we just
+	// wrote. We can't write the environment file earlier because one an access
+	// new environment variables while writing it.
+	outputNinjaFile := shared.JoinPath(topDir, bootstrap.CmdlineOutFile())
+	currentTime := time.Now().Local()
+	err = os.Chtimes(outputNinjaFile, currentTime, currentTime)
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "error touching output file %s: %s\n", outputNinjaFile, err)
 		os.Exit(1)
 	}
 }
diff --git a/dexpreopt/config.go b/dexpreopt/config.go
index 888466a..8873b4e 100644
--- a/dexpreopt/config.go
+++ b/dexpreopt/config.go
@@ -352,9 +352,23 @@
 	}
 }
 
-var Dex2oatDepTag = struct {
+type dex2oatDependencyTag struct {
 	blueprint.BaseDependencyTag
-}{}
+}
+
+func (d dex2oatDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+func (d dex2oatDependencyTag) ExcludeFromApexContents() {
+}
+
+// Dex2oatDepTag represents the dependency onto the dex2oatd module. It is added to any module that
+// needs dexpreopting and so it makes no sense for it to be checked for visibility or included in
+// the apex.
+var Dex2oatDepTag = dex2oatDependencyTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = Dex2oatDepTag
+var _ android.ExcludeFromApexContentsTag = Dex2oatDepTag
 
 // RegisterToolDeps adds the necessary dependencies to binary modules for tools
 // that are required later when Get(Cached)GlobalSoongConfig is called. It
diff --git a/dexpreopt/testing.go b/dexpreopt/testing.go
index 8e90295..4801482 100644
--- a/dexpreopt/testing.go
+++ b/dexpreopt/testing.go
@@ -15,39 +15,78 @@
 package dexpreopt
 
 import (
+	"fmt"
+
 	"android/soong/android"
 )
 
-type dummyToolBinary struct {
+type fakeToolBinary struct {
 	android.ModuleBase
 }
 
-func (m *dummyToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
+func (m *fakeToolBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
 
-func (m *dummyToolBinary) HostToolPath() android.OptionalPath {
+func (m *fakeToolBinary) HostToolPath() android.OptionalPath {
 	return android.OptionalPathForPath(android.PathForTesting("dex2oat"))
 }
 
-func dummyToolBinaryFactory() android.Module {
-	module := &dummyToolBinary{}
+func fakeToolBinaryFactory() android.Module {
+	module := &fakeToolBinary{}
 	android.InitAndroidArchModule(module, android.HostSupported, android.MultilibFirst)
 	return module
 }
 
 func RegisterToolModulesForTest(ctx android.RegistrationContext) {
-	ctx.RegisterModuleType("dummy_tool_binary", dummyToolBinaryFactory)
+	ctx.RegisterModuleType("fake_tool_binary", fakeToolBinaryFactory)
 }
 
 func BpToolModulesForTest() string {
 	return `
-		dummy_tool_binary {
+		fake_tool_binary {
 			name: "dex2oatd",
 		}
 	`
 }
 
-// Prepares a test fixture by enabling dexpreopt.
-var PrepareForTestWithDexpreopt = FixtureModifyGlobalConfig(func(*GlobalConfig) {})
+func CompatLibDefinitionsForTest() string {
+	bp := ""
+
+	// For class loader context and <uses-library> tests.
+	dexpreoptModules := []string{"android.test.runner"}
+	dexpreoptModules = append(dexpreoptModules, CompatUsesLibs...)
+	dexpreoptModules = append(dexpreoptModules, OptionalCompatUsesLibs...)
+
+	for _, extra := range dexpreoptModules {
+		bp += fmt.Sprintf(`
+			java_library {
+				name: "%s",
+				srcs: ["a.java"],
+				sdk_version: "none",
+				system_modules: "stable-core-platform-api-stubs-system-modules",
+				compile_dex: true,
+				installable: true,
+			}
+		`, extra)
+	}
+
+	return bp
+}
+
+var PrepareForTestWithDexpreoptCompatLibs = android.GroupFixturePreparers(
+	android.FixtureAddFile("defaults/dexpreopt/compat/a.java", nil),
+	android.FixtureAddTextFile("defaults/dexpreopt/compat/Android.bp", CompatLibDefinitionsForTest()),
+)
+
+var PrepareForTestWithFakeDex2oatd = android.GroupFixturePreparers(
+	android.FixtureRegisterWithContext(RegisterToolModulesForTest),
+	android.FixtureAddTextFile("defaults/dexpreopt/Android.bp", BpToolModulesForTest()),
+)
+
+// Prepares a test fixture by enabling dexpreopt, registering the fake_tool_binary module type and
+// using that to define the `dex2oatd` module.
+var PrepareForTestByEnablingDexpreopt = android.GroupFixturePreparers(
+	FixtureModifyGlobalConfig(func(*GlobalConfig) {}),
+)
 
 // FixtureModifyGlobalConfig enables dexpreopt (unless modified by the mutator) and modifies the
 // configuration.
diff --git a/genrule/genrule.go b/genrule/genrule.go
index b43f28e..cb841ed 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -820,7 +820,7 @@
 
 func GenruleBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build() {
+	if !ok || !m.ConvertWithBp2build(ctx) {
 		return
 	}
 
diff --git a/java/boot_image.go b/java/boot_image.go
index 25a4f17..a14940d 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -96,10 +96,6 @@
 
 func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	tag := ctx.OtherModuleDependencyTag(dep)
-	if tag == dexpreopt.Dex2oatDepTag {
-		// The dex2oat tool is only needed for building and is not required in the apex.
-		return false
-	}
 	if android.IsMetaDependencyTag(tag) {
 		// Cross-cutting metadata dependencies are metadata.
 		return false
diff --git a/java/java_test.go b/java/java_test.go
index b6f639f..1a79f49 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -53,7 +53,7 @@
 	android.FixtureRegisterWithContext(func(ctx android.RegistrationContext) {
 		ctx.RegisterPreSingletonType("sdk_versions", sdkPreSingletonFactory)
 	}),
-	dexpreopt.PrepareForTestWithDexpreopt,
+	PrepareForTestWithDexpreopt,
 )
 
 func TestMain(m *testing.M) {
@@ -68,7 +68,7 @@
 func testJavaError(t *testing.T, pattern string, bp string) (*android.TestContext, android.Config) {
 	t.Helper()
 	result := android.GroupFixturePreparers(
-		prepareForJavaTest, dexpreopt.PrepareForTestWithDexpreopt).
+		prepareForJavaTest, dexpreopt.PrepareForTestByEnablingDexpreopt).
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(pattern)).
 		RunTestWithBp(t, bp)
 	return result.TestContext, result.Config
@@ -831,7 +831,7 @@
 	}
 
 	runTest := func(t *testing.T, info testConfigInfo, expectedErrorPattern string) {
-		t.Run(fmt.Sprintf("%#v", info), func(t *testing.T) {
+		t.Run(fmt.Sprintf("%v", info), func(t *testing.T) {
 			errorHandler := android.FixtureExpectsNoErrors
 			if expectedErrorPattern != "" {
 				errorHandler = android.FixtureExpectsAtLeastOneErrorMatchingPattern(expectedErrorPattern)
diff --git a/java/testing.go b/java/testing.go
index 82a2103..295b8d0 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -43,7 +43,7 @@
 	// Make sure that mutators and module types, e.g. prebuilt mutators available.
 	android.PrepareForTestWithAndroidBuildComponents,
 	// Make java build components available to the test.
-	android.FixtureRegisterWithContext(RegisterRequiredBuildComponentsForTest),
+	android.FixtureRegisterWithContext(registerRequiredBuildComponentsForTest),
 	android.FixtureRegisterWithContext(registerJavaPluginBuildComponents),
 )
 
@@ -52,7 +52,16 @@
 	// Make sure that all the module types used in the defaults are registered.
 	PrepareForTestWithJavaBuildComponents,
 	// The java default module definitions.
-	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", GatherRequiredDepsForTest()),
+	android.FixtureAddTextFile(defaultJavaDir+"/Android.bp", gatherRequiredDepsForTest()),
+	// Add dexpreopt compat libs (android.test.base, etc.) and a fake dex2oatd module.
+	dexpreopt.PrepareForTestWithDexpreoptCompatLibs,
+	dexpreopt.PrepareForTestWithFakeDex2oatd,
+)
+
+// Provides everything needed by dexpreopt.
+var PrepareForTestWithDexpreopt = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+	dexpreopt.PrepareForTestByEnablingDexpreopt,
 )
 
 var PrepareForTestWithOverlayBuildComponents = android.FixtureRegisterWithContext(registerOverlayBuildComponents)
@@ -178,7 +187,22 @@
 //
 // In particular this must register all the components that are used in the `Android.bp` snippet
 // returned by GatherRequiredDepsForTest()
+//
+// deprecated: Use test fixtures instead, e.g. PrepareForTestWithJavaBuildComponents
 func RegisterRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
+	registerRequiredBuildComponentsForTest(ctx)
+
+	// Make sure that any tool related module types needed by dexpreopt have been registered.
+	dexpreopt.RegisterToolModulesForTest(ctx)
+}
+
+// registerRequiredBuildComponentsForTest registers the build components used by
+// PrepareForTestWithJavaDefaultModules.
+//
+// As functionality is moved out of here into separate FixturePreparer instances they should also
+// be moved into GatherRequiredDepsForTest for use by tests that have not yet switched to use test
+// fixtures.
+func registerRequiredBuildComponentsForTest(ctx android.RegistrationContext) {
 	RegisterAARBuildComponents(ctx)
 	RegisterAppBuildComponents(ctx)
 	RegisterAppImportBuildComponents(ctx)
@@ -193,15 +217,32 @@
 	RegisterSdkLibraryBuildComponents(ctx)
 	RegisterStubsBuildComponents(ctx)
 	RegisterSystemModulesBuildComponents(ctx)
-
-	// Make sure that any tool related module types needed by dexpreopt have been registered.
-	dexpreopt.RegisterToolModulesForTest(ctx)
 }
 
 // Gather the module definitions needed by tests that depend upon code from this package.
 //
 // Returns an `Android.bp` snippet that defines the modules that are needed by this package.
+//
+// deprecated: Use test fixtures instead, e.g. PrepareForTestWithJavaDefaultModules
 func GatherRequiredDepsForTest() string {
+	bp := gatherRequiredDepsForTest()
+
+	// For class loader context and <uses-library> tests.
+	bp += dexpreopt.CompatLibDefinitionsForTest()
+
+	// Make sure that any tools needed for dexpreopting are defined.
+	bp += dexpreopt.BpToolModulesForTest()
+
+	return bp
+}
+
+// gatherRequiredDepsForTest gathers the module definitions used by
+// PrepareForTestWithJavaDefaultModules.
+//
+// As functionality is moved out of here into separate FixturePreparer instances they should also
+// be moved into GatherRequiredDepsForTest for use by tests that have not yet switched to use test
+// fixtures.
+func gatherRequiredDepsForTest() string {
 	var bp string
 
 	extraModules := []string{
@@ -233,24 +274,6 @@
 		`, extra)
 	}
 
-	// For class loader context and <uses-library> tests.
-	dexpreoptModules := []string{"android.test.runner"}
-	dexpreoptModules = append(dexpreoptModules, dexpreopt.CompatUsesLibs...)
-	dexpreoptModules = append(dexpreoptModules, dexpreopt.OptionalCompatUsesLibs...)
-
-	for _, extra := range dexpreoptModules {
-		bp += fmt.Sprintf(`
-			java_library {
-				name: "%s",
-				srcs: ["a.java"],
-				sdk_version: "none",
-				system_modules: "stable-core-platform-api-stubs-system-modules",
-				compile_dex: true,
-				installable: true,
-			}
-		`, extra)
-	}
-
 	bp += `
 		java_library {
 			name: "framework",
@@ -287,9 +310,6 @@
 		`, extra)
 	}
 
-	// Make sure that any tools needed for dexpreopting are defined.
-	bp += dexpreopt.BpToolModulesForTest()
-
 	// Make sure that the dex_bootjars singleton module is instantiated for the tests.
 	bp += `
 		dex_bootjars {
diff --git a/python/binary.go b/python/binary.go
index 6061ad4..5b0f080 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -61,7 +61,7 @@
 
 func PythonBinaryBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*Module)
-	if !ok || !m.ConvertWithBp2build() {
+	if !ok || !m.ConvertWithBp2build(ctx) {
 		return
 	}
 
diff --git a/rust/binary.go b/rust/binary.go
index df48916..dfe8744 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -87,7 +87,7 @@
 	deps = binary.baseCompiler.compilerDeps(ctx, deps)
 
 	if ctx.toolchain().Bionic() {
-		deps = bionicDeps(deps, Bool(binary.Properties.Static_executable))
+		deps = bionicDeps(ctx, deps, Bool(binary.Properties.Static_executable))
 		if Bool(binary.Properties.Static_executable) {
 			deps.CrtBegin = "crtbegin_static"
 		} else {
diff --git a/rust/bindgen.go b/rust/bindgen.go
index db69e23..bcc26b8 100644
--- a/rust/bindgen.go
+++ b/rust/bindgen.go
@@ -260,7 +260,7 @@
 func (b *bindgenDecorator) SourceProviderDeps(ctx DepsContext, deps Deps) Deps {
 	deps = b.BaseSourceProvider.SourceProviderDeps(ctx, deps)
 	if ctx.toolchain().Bionic() {
-		deps = bionicDeps(deps, false)
+		deps = bionicDeps(ctx, deps, false)
 	}
 
 	deps.SharedLibs = append(deps.SharedLibs, b.ClangProperties.Shared_libs...)
diff --git a/rust/compiler.go b/rust/compiler.go
index 98ad7ad..200af90 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -281,7 +281,7 @@
 	return deps
 }
 
-func bionicDeps(deps Deps, static bool) Deps {
+func bionicDeps(ctx DepsContext, deps Deps, static bool) Deps {
 	bionicLibs := []string{}
 	bionicLibs = append(bionicLibs, "liblog")
 	bionicLibs = append(bionicLibs, "libc")
@@ -294,9 +294,9 @@
 		deps.SharedLibs = append(deps.SharedLibs, bionicLibs...)
 	}
 
-	//TODO(b/141331117) libstd requires libgcc on Android
-	deps.StaticLibs = append(deps.StaticLibs, "libgcc")
-
+	if libRuntimeBuiltins := config.BuiltinsRuntimeLibrary(ctx.toolchain()); libRuntimeBuiltins != "" {
+		deps.StaticLibs = append(deps.StaticLibs, libRuntimeBuiltins)
+	}
 	return deps
 }
 
diff --git a/rust/config/global.go b/rust/config/global.go
index 12f4972..9208ddb 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -53,6 +53,7 @@
 
 	deviceGlobalRustFlags = []string{
 		"-C panic=abort",
+		"-Z link-native-libraries=no",
 	}
 
 	deviceGlobalLinkFlags = []string{
@@ -62,7 +63,7 @@
 		// Override cc's --no-undefined-version to allow rustc's generated alloc functions
 		"-Wl,--undefined-version",
 
-		"-Bdynamic",
+		"-Wl,-Bdynamic",
 		"-nostdlib",
 		"-Wl,--pack-dyn-relocs=android+relr",
 		"-Wl,--use-android-relr-tags",
diff --git a/rust/config/toolchain.go b/rust/config/toolchain.go
index 9525c38..a769f12 100644
--- a/rust/config/toolchain.go
+++ b/rust/config/toolchain.go
@@ -112,6 +112,10 @@
 	return ""
 }
 
+func BuiltinsRuntimeLibrary(t Toolchain) string {
+	return LibclangRuntimeLibrary(t, "builtins")
+}
+
 func LibFuzzerRuntimeLibrary(t Toolchain) string {
 	return LibclangRuntimeLibrary(t, "fuzzer")
 }
diff --git a/rust/library.go b/rust/library.go
index 7ff13ec..71fe1f5 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -404,7 +404,7 @@
 	deps = library.baseCompiler.compilerDeps(ctx, deps)
 
 	if ctx.toolchain().Bionic() && (library.dylib() || library.shared()) {
-		deps = bionicDeps(deps, false)
+		deps = bionicDeps(ctx, deps, false)
 		deps.CrtBegin = "crtbegin_so"
 		deps.CrtEnd = "crtend_so"
 	}
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index 2be3c9c..fa0eb3f 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -26,91 +26,17 @@
 	PrepareForTestWithSdkBuildComponents,
 )
 
-func testSdkWithJava(t *testing.T, bp string) *android.TestResult {
-	t.Helper()
-
-	fs := map[string][]byte{
-		"Test.java":              nil,
-		"resource.test":          nil,
-		"aidl/foo/bar/Test.aidl": nil,
-
-		// For java_import
-		"prebuilt.jar": nil,
-
-		// For java_sdk_library
-		"api/current.txt":                                   nil,
-		"api/removed.txt":                                   nil,
-		"api/system-current.txt":                            nil,
-		"api/system-removed.txt":                            nil,
-		"api/test-current.txt":                              nil,
-		"api/test-removed.txt":                              nil,
-		"api/module-lib-current.txt":                        nil,
-		"api/module-lib-removed.txt":                        nil,
-		"api/system-server-current.txt":                     nil,
-		"api/system-server-removed.txt":                     nil,
-		"build/soong/scripts/gen-java-current-api-files.sh": nil,
-		"docs/known_doctags":                                nil,
-		"100/public/api/myjavalib.txt":                      nil,
-		"100/public/api/myjavalib-removed.txt":              nil,
-		"100/system/api/myjavalib.txt":                      nil,
-		"100/system/api/myjavalib-removed.txt":              nil,
-		"100/module-lib/api/myjavalib.txt":                  nil,
-		"100/module-lib/api/myjavalib-removed.txt":          nil,
-		"100/system-server/api/myjavalib.txt":               nil,
-		"100/system-server/api/myjavalib-removed.txt":       nil,
-	}
-
-	// for java_sdk_library tests
-	bp = `
-java_system_modules_import {
-	name: "core-current-stubs-system-modules",
-}
-java_system_modules_import {
-	name: "stable-core-platform-api-stubs-system-modules",
-}
-java_import {
-	name: "stable.core.platform.api.stubs",
-}
-java_import {
-	name: "android_stubs_current",
-}
-java_import {
-	name: "android_system_stubs_current",
-}
-java_import {
-	name: "android_test_stubs_current",
-}
-java_import {
-	name: "android_module_lib_stubs_current",
-}
-java_import {
-	name: "android_system_server_stubs_current",
-}
-java_import {
-	name: "core-lambda-stubs", 
-	sdk_version: "none",
-}
-java_import {
-	name: "ext", 
-	sdk_version: "none",
-}
-java_import {
-	name: "framework", 
-	sdk_version: "none",
-}
-prebuilt_apis {
-	name: "sdk",
-	api_dirs: ["100"],
-}
-` + bp
-
-	return testSdkWithFs(t, bp, fs)
-}
+var prepareForSdkTestWithJavaSdkLibrary = android.GroupFixturePreparers(
+	prepareForSdkTestWithJava,
+	java.PrepareForTestWithJavaDefaultModules,
+	java.PrepareForTestWithJavaSdkLibraryFiles,
+	java.FixtureWithLastReleaseApis("myjavalib"),
+)
 
 // Contains tests for SDK members provided by the java package.
 
 func TestSdkDependsOnSourceEvenWhenPrebuiltPreferred(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_header_libs: ["sdkmember"],
@@ -161,7 +87,10 @@
 }
 
 func TestBasicSdkWithJavaLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		prepareForSdkTestWithApex,
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_header_libs: ["sdkmember"],
@@ -242,7 +171,10 @@
 }
 
 func TestSnapshotWithJavaHeaderLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_header_libs: ["myjavalib"],
@@ -296,7 +228,10 @@
 }
 
 func TestHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			device_supported: false,
@@ -358,7 +293,7 @@
 }
 
 func TestDeviceAndHostSnapshotWithJavaHeaderLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			host_supported: true,
@@ -426,7 +361,10 @@
 }
 
 func TestSnapshotWithJavaImplLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+	).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			java_libs: ["myjavalib"],
@@ -481,7 +419,7 @@
 }
 
 func TestSnapshotWithJavaBootLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			java_boot_libs: ["myjavalib"],
@@ -535,7 +473,10 @@
 }
 
 func TestHostSnapshotWithJavaImplLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+	).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			device_supported: false,
@@ -597,7 +538,7 @@
 }
 
 func TestSnapshotWithJavaTest(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			java_tests: ["myjavatests"],
@@ -649,7 +590,7 @@
 }
 
 func TestHostSnapshotWithJavaTest(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			device_supported: false,
@@ -710,7 +651,7 @@
 }
 
 func TestSnapshotWithJavaSystemModules(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_header_libs: ["exported-system-module"],
@@ -808,7 +749,7 @@
 }
 
 func TestHostSnapshotWithJavaSystemModules(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			device_supported: false,
@@ -888,7 +829,7 @@
 }
 
 func TestDeviceAndHostSnapshotWithOsSpecificMembers(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			host_supported: true,
@@ -1021,7 +962,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1125,7 +1066,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_SdkVersion_None(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1191,7 +1132,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_SdkVersion_ForScope(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1260,7 +1201,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_ApiScopes(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1350,7 +1291,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_ModuleLib(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1461,7 +1402,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_SystemServer(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1551,7 +1492,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_NamingScheme(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
@@ -1623,7 +1564,7 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
-	result := testSdkWithJava(t, `
+	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 49f4961..1ae557a 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -521,7 +521,7 @@
 
 func ShBinaryBp2Build(ctx android.TopDownMutatorContext) {
 	m, ok := ctx.Module().(*ShBinary)
-	if !ok || !m.ConvertWithBp2build() {
+	if !ok || !m.ConvertWithBp2build(ctx) {
 		return
 	}