Merge "Default metalava sandboxing to true"
diff --git a/android/apex.go b/android/apex.go
index a5ff442..257bdad 100644
--- a/android/apex.go
+++ b/android/apex.go
@@ -453,6 +453,23 @@
 	}
 }
 
+// AvailableToSameApexes returns true if the two modules are apex_available to
+// exactly the same set of APEXes (and platform), i.e. if their apex_available
+// properties have the same elements.
+func AvailableToSameApexes(mod1, mod2 ApexModule) bool {
+	mod1ApexAvail := SortedUniqueStrings(mod1.apexModuleBase().ApexProperties.Apex_available)
+	mod2ApexAvail := SortedUniqueStrings(mod2.apexModuleBase().ApexProperties.Apex_available)
+	if len(mod1ApexAvail) != len(mod2ApexAvail) {
+		return false
+	}
+	for i, v := range mod1ApexAvail {
+		if v != mod2ApexAvail[i] {
+			return false
+		}
+	}
+	return true
+}
+
 type byApexName []ApexInfo
 
 func (a byApexName) Len() int           { return len(a) }
diff --git a/android/bazel.go b/android/bazel.go
index 9a14e70..a7c6b63 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -129,49 +129,59 @@
 	}
 
 	// Per-module denylist to always opt modules out.
-	bp2buildModuleDoNotConvert = map[string]bool{
-		"libBionicBenchmarksUtils":      true,
-		"libbionic_spawn_benchmark":     true,
-		"libc_jemalloc_wrapper":         true,
-		"libc_bootstrap":                true,
-		"libc_init_static":              true,
-		"libc_init_dynamic":             true,
-		"libc_tzcode":                   true,
-		"lib_dns":                       true,
-		"libc_freebsd":                  true,
-		"libc_freebsd_large_stack":      true,
-		"libc_netbsd":                   true,
-		"libc_openbsd_ndk":              true,
-		"libc_openbsd_large_stack":      true,
-		"libc_openbsd":                  true,
-		"libc_gdtoa":                    true,
-		"libc_fortify":                  true,
-		"libc_bionic":                   true,
-		"libc_bionic_ndk":               true,
-		"libc_bionic_systrace":          true,
-		"libc_pthread":                  true,
-		"libc_syscalls":                 true,
-		"libc_aeabi":                    true,
-		"libc_ndk":                      true,
-		"libc_nopthread":                true,
-		"libc_common":                   true,
-		"libc_static_dispatch":          true,
-		"libc_dynamic_dispatch":         true,
-		"libc_common_static":            true,
-		"libc_common_shared":            true,
-		"libc_unwind_static":            true,
-		"libc_nomalloc":                 true,
-		"libasync_safe":                 true,
-		"libc_malloc_debug_backtrace":   true,
-		"libsystemproperties":           true,
-		"libdl_static":                  true,
-		"liblinker_main":                true,
-		"liblinker_malloc":              true,
-		"liblinker_debuggerd_stub":      true,
-		"libbionic_tests_headers_posix": true,
+
+	bp2buildModuleDoNotConvertList = []string{
+		"libBionicBenchmarksUtils",
+		"libbionic_spawn_benchmark",
+		"libc_jemalloc_wrapper",
+		"libc_bootstrap",
+		"libc_init_static",
+		"libc_init_dynamic",
+		"libc_tzcode",
+		"libc_freebsd",
+		"libc_freebsd_large_stack",
+		"libc_netbsd",
+		"libc_openbsd_ndk",
+		"libc_openbsd_large_stack",
+		"libc_openbsd",
+		"libc_gdtoa",
+		"libc_fortify",
+		"libc_bionic",
+		"libc_bionic_ndk",
+		"libc_bionic_systrace",
+		"libc_pthread",
+		"libc_syscalls",
+		"libc_aeabi",
+		"libc_ndk",
+		"libc_nopthread",
+		"libc_common",
+		"libc_static_dispatch",
+		"libc_dynamic_dispatch",
+		"libc_common_static",
+		"libc_common_shared",
+		"libc_unwind_static",
+		"libc_nomalloc",
+		"libasync_safe",
+		"libc_malloc_debug_backtrace",
+		"libsystemproperties",
+		"libdl_static",
+		"liblinker_main",
+		"liblinker_malloc",
+		"liblinker_debuggerd_stub",
+		"libbionic_tests_headers_posix",
+		"libc_dns",
 	}
+
+	// Used for quicker lookups
+	bp2buildModuleDoNotConvert = map[string]bool{}
 )
 
+func init() {
+	for _, moduleName := range bp2buildModuleDoNotConvertList {
+		bp2buildModuleDoNotConvert[moduleName] = true
+	}
+}
+
 // ConvertWithBp2build returns whether the given BazelModuleBase should be converted with bp2build.
 func (b *BazelModuleBase) ConvertWithBp2build(ctx BazelConversionPathContext) bool {
 	if bp2buildModuleDoNotConvert[ctx.Module().Name()] {
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 0595d68..45dfbaa 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -344,73 +344,39 @@
 # This file is generated by soong_build. Do not edit.
 #####################################################
 
-def _x86_64_transition_impl(settings, attr):
+def _config_node_transition_impl(settings, attr):
     return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86_64",
+        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_%s" % attr.arch,
     }
 
-def _x86_transition_impl(settings, attr):
-    return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_x86",
-    }
-
-def _arm64_transition_impl(settings, attr):
-    return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm64",
-    }
-
-def _arm_transition_impl(settings, attr):
-    return {
-        "//command_line_option:platforms": "@sourceroot//build/bazel/platforms:generic_arm",
-    }
-
-x86_64_transition = transition(
-    implementation = _x86_64_transition_impl,
+_config_node_transition = transition(
+    implementation = _config_node_transition_impl,
     inputs = [],
     outputs = [
         "//command_line_option:platforms",
     ],
 )
 
-x86_transition = transition(
-    implementation = _x86_transition_impl,
-    inputs = [],
-    outputs = [
-        "//command_line_option:platforms",
-    ],
+def _passthrough_rule_impl(ctx):
+    return [DefaultInfo(files = depset(ctx.files.deps))]
+
+config_node = rule(
+    implementation = _passthrough_rule_impl,
+    attrs = {
+        "arch" : attr.string(mandatory = True),
+        "deps" : attr.label_list(cfg = _config_node_transition),
+        "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+    },
 )
 
-arm64_transition = transition(
-    implementation = _arm64_transition_impl,
-    inputs = [],
-    outputs = [
-        "//command_line_option:platforms",
-    ],
-)
-
-arm_transition = transition(
-    implementation = _arm_transition_impl,
-    inputs = [],
-    outputs = [
-        "//command_line_option:platforms",
-    ],
-)
-
-def _mixed_build_root_impl(ctx):
-    all_files = ctx.files.deps_x86_64 + ctx.files.deps_x86 + ctx.files.deps_arm64 + ctx.files.deps_arm
-    return [DefaultInfo(files = depset(all_files))]
 
 # Rule representing the root of the build, to depend on all Bazel targets that
 # are required for the build. Building this target will build the entire Bazel
 # build tree.
 mixed_build_root = rule(
-    implementation = _mixed_build_root_impl,
+    implementation = _passthrough_rule_impl,
     attrs = {
-        "deps_x86_64" : attr.label_list(cfg = x86_64_transition),
-        "deps_x86" : attr.label_list(cfg = x86_transition),
-        "deps_arm64" : attr.label_list(cfg = arm64_transition),
-        "deps_arm" : attr.label_list(cfg = arm_transition),
-        "_allowlist_function_transition": attr.label(default = "@bazel_tools//tools/allowlists/function_transition_allowlist"),
+        "deps" : attr.label_list(),
     },
 )
 
@@ -446,44 +412,42 @@
 	// architecture mapping.
 	formatString := `
 # This file is generated by soong_build. Do not edit.
-load(":main.bzl", "mixed_build_root", "phony_root")
+load(":main.bzl", "config_node", "mixed_build_root", "phony_root")
+
+%s
 
 mixed_build_root(name = "buildroot",
-    deps_x86_64 = [%s],
-    deps_x86 = [%s],
-    deps_arm64 = [%s],
-    deps_arm = [%s],
+    deps = [%s],
 )
 
 phony_root(name = "phonyroot",
     deps = [":buildroot"],
 )
 `
-	var deps_x86_64 []string = nil
-	var deps_x86 []string = nil
-	var deps_arm64 []string = nil
-	var deps_arm []string = nil
+	configNodeFormatString := `
+config_node(name = "%s",
+    arch = "%s",
+    deps = [%s],
+)
+`
+
+	configNodesSection := ""
+
+	labelsByArch := map[string][]string{}
 	for val, _ := range context.requests {
 		labelString := fmt.Sprintf("\"%s\"", canonicalizeLabel(val.label))
-		switch getArchString(val) {
-		case "x86_64":
-			deps_x86_64 = append(deps_x86_64, labelString)
-		case "x86":
-			deps_x86 = append(deps_x86, labelString)
-		case "arm64":
-			deps_arm64 = append(deps_arm64, labelString)
-		case "arm":
-			deps_arm = append(deps_arm, labelString)
-		default:
-			panic(fmt.Sprintf("unhandled architecture %s for %v", getArchString(val), val))
-		}
+		archString := getArchString(val)
+		labelsByArch[archString] = append(labelsByArch[archString], labelString)
 	}
 
-	return []byte(fmt.Sprintf(formatString,
-		strings.Join(deps_x86_64, ",\n            "),
-		strings.Join(deps_x86, ",\n            "),
-		strings.Join(deps_arm64, ",\n            "),
-		strings.Join(deps_arm, ",\n            ")))
+	configNodeLabels := []string{}
+	for archString, labels := range labelsByArch {
+		configNodeLabels = append(configNodeLabels, fmt.Sprintf("\":%s\"", archString))
+		labelsString := strings.Join(labels, ",\n            ")
+		configNodesSection += fmt.Sprintf(configNodeFormatString, archString, archString, labelsString)
+	}
+
+	return []byte(fmt.Sprintf(formatString, configNodesSection, strings.Join(configNodeLabels, ",\n            ")))
 }
 
 func indent(original string) string {
diff --git a/android/filegroup.go b/android/filegroup.go
index abbb4d4..2f13ab8 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -30,7 +30,7 @@
 
 // https://docs.bazel.build/versions/master/be/general.html#filegroup
 type bazelFilegroupAttributes struct {
-	Srcs bazel.LabelList
+	Srcs bazel.LabelListAttribute
 }
 
 type bazelFilegroup struct {
@@ -57,8 +57,10 @@
 		return
 	}
 
+	srcs := bazel.MakeLabelListAttribute(
+		BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
 	attrs := &bazelFilegroupAttributes{
-		Srcs: BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs),
+		Srcs: srcs,
 	}
 
 	props := bazel.BazelTargetModuleProperties{Rule_class: "filegroup"}
diff --git a/android/paths.go b/android/paths.go
index 1278961..db9e06a 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -286,6 +286,16 @@
 	return p.path
 }
 
+// RelativeToPath returns an OptionalPath with the path that was embedded having been replaced by
+// the result of calling Path.RelativeToPath on it.
+func (p OptionalPath) RelativeToPath() OptionalPath {
+	if !p.valid {
+		return p
+	}
+	p.path = p.path.RelativeToTop()
+	return p
+}
+
 // String returns the string version of the Path, or "" if it isn't valid.
 func (p OptionalPath) String() string {
 	if p.valid {
@@ -477,6 +487,9 @@
 // already be resolved by either deps mutator or path deps mutator.
 func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string) bazel.Label {
 	m, _ := ctx.GetDirectDep(dep)
+	if m == nil {
+		panic(fmt.Errorf("cannot get direct dep %s of %s", dep, ctx.Module().Name()))
+	}
 	otherLabel := bazelModuleLabel(ctx, m, tag)
 	label := bazelModuleLabel(ctx, ctx.Module(), "")
 	if samePackage(label, otherLabel) {
@@ -1642,16 +1655,10 @@
 
 // Will panic if called from outside a test environment.
 func ensureTestOnly() {
-	// Normal soong test environment
-	if InList("-test.short", os.Args) {
+	if PrefixInList(os.Args, "-test.") {
 		return
 	}
-	// IntelliJ test environment
-	if InList("-test.v", os.Args) {
-		return
-	}
-
-	panic(fmt.Errorf("Not in test\n%s", strings.Join(os.Args, "\n")))
+	panic(fmt.Errorf("Not in test. Command line:\n  %s", strings.Join(os.Args, "\n  ")))
 }
 
 func (p InstallPath) RelativeToTop() Path {
diff --git a/android/prebuilt.go b/android/prebuilt.go
index 04864a1..ebccaa7 100644
--- a/android/prebuilt.go
+++ b/android/prebuilt.go
@@ -272,11 +272,9 @@
 	}
 }
 
-// PrebuiltPostDepsMutator does two operations.  It replace dependencies on the
-// source module with dependencies on the prebuilt when both modules exist and
-// the prebuilt should be used.  When the prebuilt should not be used, disable
-// installing it.  Secondly, it also adds a sourcegroup to any filegroups found
-// in the prebuilt's 'Srcs' property.
+// PrebuiltPostDepsMutator replaces dependencies on the source module with dependencies on the
+// prebuilt when both modules exist and the prebuilt should be used.  When the prebuilt should not
+// be used, disable installing it.
 func PrebuiltPostDepsMutator(ctx BottomUpMutatorContext) {
 	if m, ok := ctx.Module().(PrebuiltInterface); ok && m.Prebuilt() != nil {
 		p := m.Prebuilt()
diff --git a/android/queryview.go b/android/queryview.go
index 12d14df..224652e 100644
--- a/android/queryview.go
+++ b/android/queryview.go
@@ -68,7 +68,7 @@
 			Command: fmt.Sprintf(
 				`rm -rf "${outDir}/"* && `+
 					`mkdir -p "${outDir}" && `+
-					`echo WORKSPACE: cat "%s" > "${outDir}/.queryview-depfile.d" && `+
+					`echo WORKSPACE: $$(cat "%s") > "${outDir}/.queryview-depfile.d" && `+
 					`BUILDER="%s" && `+
 					`echo BUILDER=$$BUILDER && `+
 					`cd "$$(dirname "$$BUILDER")" && `+
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 9c5ca41..d2a7d8d 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -645,7 +645,7 @@
 		rspFile := "out/soong/.intermediates/foo/rsp"
 		rspFile2 := "out/soong/.intermediates/foo/rsp2"
 		module := result.ModuleForTests("foo", "")
-		check(t, module.Rule("rule").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+		check(t, module.Rule("rule"), module.Output(rspFile2),
 			"cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
 			outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
 	})
@@ -662,7 +662,7 @@
 		cmd := `rm -rf ` + outDir + `/gen && ` +
 			sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
 		module := result.ModuleForTests("foo_sbox", "")
-		check(t, module.Output("gen/foo_sbox").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+		check(t, module.Output("gen/foo_sbox"), module.Output(rspFile2),
 			cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
 	})
 	t.Run("sbox_inputs", func(t *testing.T) {
@@ -679,7 +679,7 @@
 			sbox + ` --sandbox-path ` + sandboxPath + ` --manifest ` + manifest
 
 		module := result.ModuleForTests("foo_sbox_inputs", "")
-		check(t, module.Output("gen/foo_sbox_inputs").RelativeToTop(), module.Output(rspFile2).RelativeToTop(),
+		check(t, module.Output("gen/foo_sbox_inputs"), module.Output(rspFile2),
 			cmd, outFile, depFile, rspFile, rspFile2, false, []string{manifest}, []string{sbox})
 	})
 	t.Run("singleton", func(t *testing.T) {
@@ -687,7 +687,7 @@
 		rspFile := filepath.Join("out/soong/singleton/rsp")
 		rspFile2 := filepath.Join("out/soong/singleton/rsp2")
 		singleton := result.SingletonForTests("rule_builder_test")
-		check(t, singleton.Rule("rule").RelativeToTop(), singleton.Output(rspFile2).RelativeToTop(),
+		check(t, singleton.Rule("rule"), singleton.Output(rspFile2),
 			"cp bar "+outFile+" @"+rspFile+" @"+rspFile2,
 			outFile, outFile+".d", rspFile, rspFile2, true, nil, nil)
 	})
diff --git a/android/testing.go b/android/testing.go
index 3f3f769..ce27fca 100644
--- a/android/testing.go
+++ b/android/testing.go
@@ -539,8 +539,7 @@
 // The parts of this structure which are changed are:
 // * BuildParams
 //   * Args
-//   * Path instances are intentionally not modified, use AssertPathRelativeToTopEquals or
-//     AssertPathsRelativeToTopEquals instead which do something similar.
+//   * All Path, Paths, WritablePath and WritablePaths fields.
 //
 // * RuleParams
 //   * Command
@@ -552,6 +551,8 @@
 //   * CommandOrderOnly
 //
 // See PathRelativeToTop for more details.
+//
+// deprecated: this is no longer needed as TestingBuildParams are created in this form.
 func (p TestingBuildParams) RelativeToTop() TestingBuildParams {
 	// If this is not a valid params then just return it back. That will make it easy to use with the
 	// Maybe...() methods.
@@ -559,11 +560,25 @@
 		return p
 	}
 	if p.config.config == nil {
-		panic("cannot call RelativeToTop() on a TestingBuildParams previously returned by RelativeToTop()")
+		return p
 	}
 	// Take a copy of the build params and replace any args that contains test specific temporary
 	// paths with paths relative to the top.
 	bparams := p.BuildParams
+	bparams.Depfile = normalizeWritablePathRelativeToTop(bparams.Depfile)
+	bparams.Output = normalizeWritablePathRelativeToTop(bparams.Output)
+	bparams.Outputs = bparams.Outputs.RelativeToTop()
+	bparams.SymlinkOutput = normalizeWritablePathRelativeToTop(bparams.SymlinkOutput)
+	bparams.SymlinkOutputs = bparams.SymlinkOutputs.RelativeToTop()
+	bparams.ImplicitOutput = normalizeWritablePathRelativeToTop(bparams.ImplicitOutput)
+	bparams.ImplicitOutputs = bparams.ImplicitOutputs.RelativeToTop()
+	bparams.Input = normalizePathRelativeToTop(bparams.Input)
+	bparams.Inputs = bparams.Inputs.RelativeToTop()
+	bparams.Implicit = normalizePathRelativeToTop(bparams.Implicit)
+	bparams.Implicits = bparams.Implicits.RelativeToTop()
+	bparams.OrderOnly = bparams.OrderOnly.RelativeToTop()
+	bparams.Validation = normalizePathRelativeToTop(bparams.Validation)
+	bparams.Validations = bparams.Validations.RelativeToTop()
 	bparams.Args = normalizeStringMapRelativeToTop(p.config, bparams.Args)
 
 	// Ditto for any fields in the RuleParams.
@@ -582,6 +597,20 @@
 	}
 }
 
+func normalizeWritablePathRelativeToTop(path WritablePath) WritablePath {
+	if path == nil {
+		return nil
+	}
+	return path.RelativeToTop().(WritablePath)
+}
+
+func normalizePathRelativeToTop(path Path) Path {
+	if path == nil {
+		return nil
+	}
+	return path.RelativeToTop()
+}
+
 // baseTestingComponent provides functionality common to both TestingModule and TestingSingleton.
 type baseTestingComponent struct {
 	config   Config
@@ -643,7 +672,7 @@
 		config:      b.config,
 		BuildParams: bparams,
 		RuleParams:  b.provider.RuleParamsForTests()[bparams.Rule],
-	}
+	}.RelativeToTop()
 }
 
 func (b baseTestingComponent) maybeBuildParamsFromRule(rule string) (TestingBuildParams, []string) {
@@ -789,6 +818,22 @@
 	return normalizeStringMapRelativeToTop(m.config, m.module.VariablesForTests())
 }
 
+// OutputFiles calls OutputFileProducer.OutputFiles on the encapsulated module, exits the test
+// immediately if there is an error and otherwise returns the result of calling Paths.RelativeToTop
+// on the returned Paths.
+func (m TestingModule) OutputFiles(t *testing.T, tag string) Paths {
+	producer, ok := m.module.(OutputFileProducer)
+	if !ok {
+		t.Fatalf("%q must implement OutputFileProducer\n", m.module.Name())
+	}
+	paths, err := producer.OutputFiles(tag)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	return paths.RelativeToTop()
+}
+
 // TestingSingleton is wrapper around an android.Singleton that provides methods to find information about individual
 // ctx.Build parameters for verification in tests.
 type TestingSingleton struct {
@@ -1013,3 +1058,20 @@
 	}
 	return result
 }
+
+// StringRelativeToTop will normalize a string containing paths, e.g. ninja command, by replacing
+// any references to the test specific temporary build directory that changes with each run to a
+// fixed path relative to a notional top directory.
+//
+// This is similar to StringPathRelativeToTop except that assumes the string is a single path
+// containing at most one instance of the temporary build directory at the start of the path while
+// this assumes that there can be any number at any position.
+func StringRelativeToTop(config Config, command string) string {
+	return normalizeStringRelativeToTop(config, command)
+}
+
+// StringsRelativeToTop will return a new slice such that each item in the new slice is the result
+// of calling StringRelativeToTop on the corresponding item in the input slice.
+func StringsRelativeToTop(config Config, command []string) []string {
+	return normalizeStringArrayRelativeToTop(config, command)
+}
diff --git a/android/util.go b/android/util.go
index 506f8f7..a0394f6 100644
--- a/android/util.go
+++ b/android/util.go
@@ -193,6 +193,17 @@
 	return
 }
 
+// FilterListPred returns the elements of the given list for which the predicate
+// returns true. Order is kept.
+func FilterListPred(list []string, pred func(s string) bool) (filtered []string) {
+	for _, l := range list {
+		if pred(l) {
+			filtered = append(filtered, l)
+		}
+	}
+	return
+}
+
 // RemoveListFromList removes the strings belonging to the filter list from the
 // given list and returns the result
 func RemoveListFromList(list []string, filter_out []string) (result []string) {
diff --git a/android/util_test.go b/android/util_test.go
index fa26c77..09bec01 100644
--- a/android/util_test.go
+++ b/android/util_test.go
@@ -18,6 +18,7 @@
 	"fmt"
 	"reflect"
 	"strconv"
+	"strings"
 	"testing"
 )
 
@@ -299,6 +300,14 @@
 	}
 }
 
+func TestFilterListPred(t *testing.T) {
+	pred := func(s string) bool { return strings.HasPrefix(s, "a/") }
+	AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "b/a", "a/b"}, pred), []string{"a/c", "a/b"})
+	AssertArrayString(t, "filter", FilterListPred([]string{"b/c", "a/a", "b/b"}, pred), []string{"a/a"})
+	AssertArrayString(t, "filter", FilterListPred([]string{"c/c", "b/a", "c/b"}, pred), []string{})
+	AssertArrayString(t, "filter", FilterListPred([]string{"a/c", "a/a", "a/b"}, pred), []string{"a/c", "a/a", "a/b"})
+}
+
 func TestRemoveListFromList(t *testing.T) {
 	input := []string{"a", "b", "c", "d", "a", "c", "d"}
 	filter := []string{"a", "c"}
diff --git a/apex/apex.go b/apex/apex.go
index dca5595..9d06e1c 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -93,8 +93,14 @@
 	Multilib apexMultilibProperties
 
 	// List of boot images that are embedded inside this APEX bundle.
+	//
+	// deprecated: Use Bootclasspath_fragments
+	// TODO(b/177892522): Remove after has been replaced by Bootclasspath_fragments
 	Boot_images []string
 
+	// List of bootclasspath fragments that are embedded inside this APEX bundle.
+	Bootclasspath_fragments []string
+
 	// List of java libraries that are embedded inside this APEX bundle.
 	Java_libs []string
 
@@ -567,7 +573,7 @@
 	certificateTag  = dependencyTag{name: "certificate"}
 	executableTag   = dependencyTag{name: "executable", payload: true}
 	fsTag           = dependencyTag{name: "filesystem", payload: true}
-	bootImageTag    = dependencyTag{name: "bootImage", payload: true}
+	bootImageTag    = dependencyTag{name: "bootImage", payload: true, sourceOnly: true}
 	compatConfigTag = dependencyTag{name: "compatConfig", payload: true, sourceOnly: true}
 	javaLibTag      = dependencyTag{name: "javaLib", payload: true}
 	jniLibTag       = dependencyTag{name: "jniLib", payload: true}
@@ -748,6 +754,7 @@
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
 	ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Boot_images...)
+	ctx.AddFarVariationDependencies(commonVariation, bootImageTag, a.properties.Bootclasspath_fragments...)
 	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.properties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
@@ -1695,6 +1702,9 @@
 							filesInfo = append(filesInfo, af)
 						}
 					}
+
+					// Track transitive dependencies.
+					return true
 				}
 			case javaLibTag:
 				switch child.(type) {
@@ -1903,6 +1913,21 @@
 						filesInfo = append(filesInfo, af)
 						return true // track transitive dependencies
 					}
+				} else if java.IsbootImageContentDepTag(depTag) {
+					// Add the contents of the boot image to the apex.
+					switch child.(type) {
+					case *java.Library, *java.SdkLibrary:
+						af := apexFileForJavaModule(ctx, child.(javaModule))
+						if !af.ok() {
+							ctx.PropertyErrorf("boot_images", "boot image content %q is not configured to be compiled into dex", depName)
+							return false
+						}
+						filesInfo = append(filesInfo, af)
+						return true // track transitive dependencies
+					default:
+						ctx.PropertyErrorf("boot_images", "boot image content %q of type %q is not supported", depName, ctx.OtherModuleType(child))
+					}
+
 				} else if _, ok := depTag.(android.CopyDirectlyInAnyApexTag); ok {
 					// nothing
 				} else if am.CanHaveApexVariants() && am.IsInstallableToApex() {
diff --git a/apex/apex_test.go b/apex/apex_test.go
index e0cefa1..92f57c6 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -16,7 +16,6 @@
 
 import (
 	"fmt"
-	"io/ioutil"
 	"os"
 	"path"
 	"path/filepath"
@@ -38,8 +37,6 @@
 	"android/soong/sh"
 )
 
-var buildDir string
-
 // names returns name list from white space separated string
 func names(s string) (ns []string) {
 	for _, n := range strings.Split(s, " ") {
@@ -118,7 +115,7 @@
 )
 
 var apexFixtureFactory = android.NewFixtureFactory(
-	&buildDir,
+	nil,
 	// General preparers in alphabetical order as test infrastructure will enforce correct
 	// registration order.
 	android.PrepareForTestWithAndroidBuildComponents,
@@ -140,13 +137,13 @@
 			],
 		}
 	`),
+	prepareForTestWithMyapex,
 	android.FixtureMergeMockFs(android.MockFS{
-		"a.java":                                    nil,
-		"PrebuiltAppFoo.apk":                        nil,
-		"PrebuiltAppFooPriv.apk":                    nil,
-		"apex_manifest.json":                        nil,
-		"AndroidManifest.xml":                       nil,
-		"system/sepolicy/apex/myapex-file_contexts": nil,
+		"a.java":                 nil,
+		"PrebuiltAppFoo.apk":     nil,
+		"PrebuiltAppFooPriv.apk": nil,
+		"apex_manifest.json":     nil,
+		"AndroidManifest.xml":    nil,
 		"system/sepolicy/apex/myapex.updatable-file_contexts":         nil,
 		"system/sepolicy/apex/myapex2-file_contexts":                  nil,
 		"system/sepolicy/apex/otherapex-file_contexts":                nil,
@@ -204,17 +201,9 @@
 	}),
 )
 
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_apex_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	_ = os.RemoveAll(buildDir)
-}
+var prepareForTestWithMyapex = android.FixtureMergeMockFs(android.MockFS{
+	"system/sepolicy/apex/myapex-file_contexts": nil,
+})
 
 // ensure that 'result' equals 'expected'
 func ensureEquals(t *testing.T, result string, expected string) {
@@ -511,7 +500,7 @@
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--pubkey vendor/foo/devkeys/testkey.avbpubkey")
 	// Ensure that the NOTICE output is being packaged as an asset.
-	ensureContains(t, optFlags, "--assets_dir "+buildDir+"/.intermediates/myapex/android_common_myapex_image/NOTICE")
+	ensureContains(t, optFlags, "--assets_dir out/soong/.intermediates/myapex/android_common_myapex_image/NOTICE")
 
 	copyCmds := apexRule.Args["copy_commands"]
 
@@ -2464,8 +2453,8 @@
 	prefix := "TARGET_"
 	var builder strings.Builder
 	data.Custom(&builder, name, prefix, "", data)
-	androidMk := builder.String()
-	installPath := path.Join(buildDir, "../target/product/test_device/vendor/apex")
+	androidMk := android.StringRelativeToTop(ctx.Config(), builder.String())
+	installPath := "out/target/product/test_device/vendor/apex"
 	ensureContains(t, androidMk, "LOCAL_MODULE_PATH := "+installPath)
 
 	apexManifestRule := ctx.ModuleForTests("myapex", "android_common_myapex_image").Rule("apexManifestRule")
@@ -2512,10 +2501,10 @@
 	ldRule := ctx.ModuleForTests("mybin", vendorVariant+"_apex10000").Rule("ld")
 	libs := names(ldRule.Args["libFlags"])
 	// VNDK libs(libvndk/libc++) as they are
-	ensureListContains(t, libs, buildDir+"/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
-	ensureListContains(t, libs, buildDir+"/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
+	ensureListContains(t, libs, "out/soong/.intermediates/libvndk/"+vendorVariant+"_shared/libvndk.so")
+	ensureListContains(t, libs, "out/soong/.intermediates/"+cc.DefaultCcCommonTestModulesDir+"libc++/"+vendorVariant+"_shared/libc++.so")
 	// non-stable Vendor libs as APEX variants
-	ensureListContains(t, libs, buildDir+"/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
+	ensureListContains(t, libs, "out/soong/.intermediates/libvendor/"+vendorVariant+"_shared_apex10000/libvendor.so")
 
 	// VNDK libs are not included when use_vndk_as_stable: true
 	ensureExactContents(t, ctx, "myapex", "android_common_myapex_image", []string{
@@ -4088,15 +4077,15 @@
 			`)
 
 			apex := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
-			expected := buildDir + "/target/product/test_device/" + tc.parition + "/apex"
-			actual := apex.installDir.String()
+			expected := "out/soong/target/product/test_device/" + tc.parition + "/apex"
+			actual := apex.installDir.RelativeToTop().String()
 			if actual != expected {
 				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
 			}
 
 			flattened := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
-			expected = buildDir + "/target/product/test_device/" + tc.flattenedPartition + "/apex"
-			actual = flattened.installDir.String()
+			expected = "out/soong/target/product/test_device/" + tc.flattenedPartition + "/apex"
+			actual = flattened.installDir.RelativeToTop().String()
 			if actual != expected {
 				t.Errorf("wrong install path. expected %q. actual %q", expected, actual)
 			}
@@ -5077,7 +5066,7 @@
 	}
 	// JNI libraries including transitive deps are
 	for _, jni := range []string{"libjni", "libfoo"} {
-		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile()
+		jniOutput := ctx.ModuleForTests(jni, "android_arm64_armv8-a_sdk_shared_apex10000").Module().(*cc.Module).OutputFile().RelativeToPath()
 		// ... embedded inside APK (jnilibs.zip)
 		ensureListContains(t, appZipRule.Implicits.Strings(), jniOutput.String())
 		// ... and not directly inside the APEX
@@ -5715,7 +5704,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -5766,7 +5755,7 @@
 
 	// The bar library should depend on the stubs jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -5856,7 +5845,7 @@
 
 	// The bar library should depend on the implementation jar.
 	barLibrary := ctx.ModuleForTests("bar", "android_common_myapex").Rule("javac")
-	if expected, actual := `^-classpath /[^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath [^:]*/turbine-combined/foo\.impl\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -6327,7 +6316,7 @@
 	)
 
 	m := ctx.ModuleForTests("myapex", "android_common")
-	extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+	extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
 
 	actual := extractedApex.Inputs
 	if len(actual) != 1 {
@@ -6454,7 +6443,7 @@
 	for k, v := range filesForSdkLibrary {
 		fs[k] = v
 	}
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
+	config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 
 	ctx := android.NewTestArchContext(config)
 	ctx.RegisterModuleType("apex", BundleFactory)
@@ -6675,7 +6664,7 @@
 		"system/sepolicy/apex/myapex-file_contexts": nil,
 	}
 
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
+	config := android.TestArchConfig(t.TempDir(), nil, bp, fs)
 	android.SetTestNeverallowRules(config, rules)
 	updatableBootJars := make([]string, 0, len(apexBootJars))
 	for _, apexBootJar := range apexBootJars {
@@ -6859,21 +6848,77 @@
 		}
 	`)
 
-	// the test 'mytest' is a test for the apex, therefore is linked to the
+	ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+		ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+		mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+		android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+	}
+
+	// These modules are tests for the apex, therefore are linked to the
 	// actual implementation of mylib instead of its stub.
-	ldFlags := ctx.ModuleForTests("mytest", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
-	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+	ensureLinkedLibIs("mytest", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+	ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+	ensureLinkedLibIs("mybench", "android_arm64_armv8-a", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
+}
 
-	// The same should be true for cc_library
-	ldFlags = ctx.ModuleForTests("mytestlib", "android_arm64_armv8-a_shared").Rule("ld").Args["libFlags"]
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
-	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+func TestIndirectTestFor(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			native_shared_libs: ["mylib", "myprivlib"],
+			updatable: false,
+		}
 
-	// ... and for cc_benchmark
-	ldFlags = ctx.ModuleForTests("mybench", "android_arm64_armv8-a").Rule("ld").Args["libFlags"]
-	ensureContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared/mylib.so")
-	ensureNotContains(t, ldFlags, "mylib/android_arm64_armv8-a_shared_1/mylib.so")
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		cc_library {
+			name: "mylib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			stubs: {
+				versions: ["1"],
+			},
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "myprivlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			stl: "none",
+			shared_libs: ["mylib"],
+			apex_available: ["myapex"],
+		}
+
+		cc_library {
+			name: "mytestlib",
+			srcs: ["mylib.cpp"],
+			system_shared_libs: [],
+			shared_libs: ["myprivlib"],
+			stl: "none",
+			test_for: ["myapex"],
+		}
+	`)
+
+	ensureLinkedLibIs := func(mod, variant, linkedLib, expectedVariant string) {
+		ldFlags := strings.Split(ctx.ModuleForTests(mod, variant).Rule("ld").Args["libFlags"], " ")
+		mylibLdFlags := android.FilterListPred(ldFlags, func(s string) bool { return strings.HasPrefix(s, linkedLib) })
+		android.AssertArrayString(t, "unexpected "+linkedLib+" link library for "+mod, []string{linkedLib + expectedVariant}, mylibLdFlags)
+	}
+
+	// The platform variant of mytestlib links to the platform variant of the
+	// internal myprivlib.
+	ensureLinkedLibIs("mytestlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/myprivlib/", "android_arm64_armv8-a_shared/myprivlib.so")
+
+	// The platform variant of myprivlib links to the platform variant of mylib
+	// and bypasses its stubs.
+	ensureLinkedLibIs("myprivlib", "android_arm64_armv8-a_shared", "out/soong/.intermediates/mylib/", "android_arm64_armv8-a_shared/mylib.so")
 }
 
 // TODO(jungjw): Move this to proptools
@@ -6904,7 +6949,7 @@
 	m := ctx.ModuleForTests("myapex", "android_common")
 
 	// Check extract_apks tool parameters.
-	extractedApex := m.Output(buildDir + "/.intermediates/myapex/android_common/foo_v2.apex")
+	extractedApex := m.Output("out/soong/.intermediates/myapex/android_common/foo_v2.apex")
 	actual := extractedApex.Args["abis"]
 	expected := "ARMEABI_V7A,ARM64_V8A"
 	if actual != expected {
@@ -7395,12 +7440,5 @@
 }
 
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
+	os.Exit(m.Run())
 }
diff --git a/apex/boot_image_test.go b/apex/boot_image_test.go
index 7e37e42..574166a 100644
--- a/apex/boot_image_test.go
+++ b/apex/boot_image_test.go
@@ -26,23 +26,32 @@
 // Contains tests for boot_image logic from java/boot_image.go as the ART boot image requires
 // modules from the ART apex.
 
+var prepareForTestWithBootImage = android.GroupFixturePreparers(
+	java.PrepareForTestWithDexpreopt,
+	PrepareForTestWithApexBuildComponents,
+)
+
+// Some additional files needed for the art apex.
+var prepareForTestWithArtApex = android.FixtureMergeMockFs(android.MockFS{
+	"com.android.art.avbpubkey":                          nil,
+	"com.android.art.pem":                                nil,
+	"system/sepolicy/apex/com.android.art-file_contexts": nil,
+})
+
 func TestBootImages(t *testing.T) {
-	result := apexFixtureFactory.Extend(
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
 		// Configure some libraries in the art and framework boot images.
 		dexpreopt.FixtureSetArtBootJars("com.android.art:baz", "com.android.art:quuz"),
 		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
-		filesForSdkLibrary.AddToFixture(),
-		// Some additional files needed for the art apex.
-		android.FixtureMergeMockFs(android.MockFS{
-			"com.android.art.avbpubkey":                          nil,
-			"com.android.art.pem":                                nil,
-			"system/sepolicy/apex/com.android.art-file_contexts": nil,
-		}),
+		prepareForTestWithArtApex,
+
+		java.PrepareForTestWithJavaSdkLibraryFiles,
+		java.FixtureWithLastReleaseApis("foo"),
 	).RunTestWithBp(t, `
 		java_sdk_library {
 			name: "foo",
 			srcs: ["b.java"],
-			unsafe_ignore_missing_latest_api: true,
 		}
 
 		java_library {
@@ -86,6 +95,9 @@
 		boot_image {
 			name: "art-boot-image",
 			image_name: "art",
+			apex_available: [
+				"com.android.art",
+			],
 		}
 
 		boot_image {
@@ -151,10 +163,163 @@
 	android.AssertTrimmedStringEquals(t, "invalid paths for "+moduleName, expectedBootImageFiles, strings.Join(allPaths, "\n"))
 }
 
-func TestBootImageInApex(t *testing.T) {
-	result := apexFixtureFactory.Extend(
-		// Configure some libraries in the framework boot image.
-		dexpreopt.FixtureSetBootJars("platform:foo", "platform:bar"),
+func TestBootImageInArtApex(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		prepareForTestWithArtApex,
+
+		// Configure some libraries in the art boot image.
+		dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+	).RunTestWithBp(t, `
+		apex {
+			name: "com.android.art",
+			key: "com.android.art.key",
+			boot_images: [
+				"mybootimage",
+			],
+			// bar (like foo) should be transitively included in this apex because it is part of the
+			// mybootimage boot_image. However, it is kept here to ensure that the apex dedups the files
+			// correctly.
+			java_libs: [
+				"bar",
+			],
+			updatable: false,
+		}
+
+		apex_key {
+			name: "com.android.art.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+
+		java_library {
+			name: "foo",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		java_library {
+			name: "bar",
+			srcs: ["b.java"],
+			installable: true,
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		boot_image {
+			name: "mybootimage",
+			image_name: "art",
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		// Make sure that a preferred prebuilt doesn't affect the apex.
+		prebuilt_boot_image {
+			name: "mybootimage",
+			image_name: "art",
+			prefer: true,
+			apex_available: [
+				"com.android.art",
+			],
+		}
+	`)
+
+	ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+		"javalib/arm/boot.art",
+		"javalib/arm/boot.oat",
+		"javalib/arm/boot.vdex",
+		"javalib/arm/boot-bar.art",
+		"javalib/arm/boot-bar.oat",
+		"javalib/arm/boot-bar.vdex",
+		"javalib/arm64/boot.art",
+		"javalib/arm64/boot.oat",
+		"javalib/arm64/boot.vdex",
+		"javalib/arm64/boot-bar.art",
+		"javalib/arm64/boot-bar.oat",
+		"javalib/arm64/boot-bar.vdex",
+		"javalib/bar.jar",
+		"javalib/foo.jar",
+	})
+
+	java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+		`bar`,
+		`com.android.art.key`,
+		`mybootimage`,
+	})
+}
+
+func TestBootImageInPrebuiltArtApex(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		prepareForTestWithArtApex,
+
+		android.FixtureMergeMockFs(android.MockFS{
+			"com.android.art-arm64.apex": nil,
+			"com.android.art-arm.apex":   nil,
+		}),
+
+		// Configure some libraries in the art boot image.
+		dexpreopt.FixtureSetArtBootJars("com.android.art:foo", "com.android.art:bar"),
+	).RunTestWithBp(t, `
+		prebuilt_apex {
+			name: "com.android.art",
+			arch: {
+				arm64: {
+					src: "com.android.art-arm64.apex",
+				},
+				arm: {
+					src: "com.android.art-arm.apex",
+				},
+			},
+			exported_java_libs: ["foo", "bar"],
+		}
+
+		java_import {
+			name: "foo",
+			jars: ["foo.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		java_import {
+			name: "bar",
+			jars: ["bar.jar"],
+			apex_available: [
+				"com.android.art",
+			],
+		}
+
+		prebuilt_boot_image {
+			name: "mybootimage",
+			image_name: "art",
+			apex_available: [
+				"com.android.art",
+			],
+		}
+	`)
+
+	java.CheckModuleDependencies(t, result.TestContext, "com.android.art", "android_common", []string{
+		`prebuilt_bar`,
+		`prebuilt_foo`,
+	})
+
+	java.CheckModuleDependencies(t, result.TestContext, "mybootimage", "android_common", []string{
+		`dex2oatd`,
+		`prebuilt_bar`,
+		`prebuilt_foo`,
+	})
+}
+
+func TestBootImageContentsNoName(t *testing.T) {
+	result := android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		prepareForTestWithMyapex,
 	).RunTestWithBp(t, `
 		apex {
 			name: "myapex",
@@ -175,17 +340,26 @@
 			name: "foo",
 			srcs: ["b.java"],
 			installable: true,
+			apex_available: [
+				"myapex",
+			],
 		}
 
 		java_library {
 			name: "bar",
 			srcs: ["b.java"],
 			installable: true,
+			apex_available: [
+				"myapex",
+			],
 		}
 
 		boot_image {
 			name: "mybootimage",
-			image_name: "boot",
+			contents: [
+				"foo",
+				"bar",
+			],
 			apex_available: [
 				"myapex",
 			],
@@ -193,18 +367,15 @@
 	`)
 
 	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
-		"javalib/arm/boot-bar.art",
-		"javalib/arm/boot-bar.oat",
-		"javalib/arm/boot-bar.vdex",
-		"javalib/arm/boot-foo.art",
-		"javalib/arm/boot-foo.oat",
-		"javalib/arm/boot-foo.vdex",
-		"javalib/arm64/boot-bar.art",
-		"javalib/arm64/boot-bar.oat",
-		"javalib/arm64/boot-bar.vdex",
-		"javalib/arm64/boot-foo.art",
-		"javalib/arm64/boot-foo.oat",
-		"javalib/arm64/boot-foo.vdex",
+		// This does not include art, oat or vdex files as they are only included for the art boot
+		// image.
+		"javalib/bar.jar",
+		"javalib/foo.jar",
+	})
+
+	java.CheckModuleDependencies(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
+		`myapex.key`,
+		`mybootimage`,
 	})
 }
 
diff --git a/apex/testing.go b/apex/testing.go
index e662cad..926125f 100644
--- a/apex/testing.go
+++ b/apex/testing.go
@@ -19,4 +19,11 @@
 var PrepareForTestWithApexBuildComponents = android.GroupFixturePreparers(
 	android.FixtureRegisterWithContext(registerApexBuildComponents),
 	android.FixtureRegisterWithContext(registerApexKeyBuildComponents),
+	// Additional files needed in tests that disallow non-existent source files.
+	// This includes files that are needed by all, or at least most, instances of an apex module type.
+	android.MockFS{
+		// Needed by apex.
+		"system/core/rootdir/etc/public.libraries.android.txt": nil,
+		"build/soong/scripts/gen_ndk_backedby_apex.sh":         nil,
+	}.AddToFixture(),
 )
diff --git a/bazel/properties.go b/bazel/properties.go
index abdc107..25e110a 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -76,6 +76,92 @@
 	return uniqueLabelList
 }
 
+const (
+	ARCH_X86    = "x86"
+	ARCH_X86_64 = "x86_64"
+	ARCH_ARM    = "arm"
+	ARCH_ARM64  = "arm64"
+)
+
+var (
+	// This is the list of architectures with a Bazel config_setting and
+	// constraint value equivalent. is actually android.ArchTypeList, but the
+	// android package depends on the bazel package, so a cyclic dependency
+	// prevents using that here.
+	selectableArchs = []string{ARCH_X86, ARCH_X86_64, ARCH_ARM, ARCH_ARM64}
+)
+
+// Arch-specific label_list typed Bazel attribute values. This should correspond
+// to the types of architectures supported for compilation in arch.go.
+type labelListArchValues struct {
+	X86    LabelList
+	X86_64 LabelList
+	Arm    LabelList
+	Arm64  LabelList
+	// TODO(b/181299724): this is currently missing the "common" arch, which
+	// doesn't have an equivalent platform() definition yet.
+}
+
+// LabelListAttribute is used to represent a list of Bazel labels as an
+// attribute.
+type LabelListAttribute struct {
+	// The non-arch specific attribute label list Value. Required.
+	Value LabelList
+
+	// The arch-specific attribute label list values. Optional. If used, these
+	// are generated in a select statement and appended to the non-arch specific
+	// label list Value.
+	ArchValues labelListArchValues
+}
+
+// MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
+func MakeLabelListAttribute(value LabelList) LabelListAttribute {
+	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
+}
+
+// HasArchSpecificValues returns true if the attribute contains
+// architecture-specific label_list values.
+func (attrs *LabelListAttribute) HasArchSpecificValues() bool {
+	for _, arch := range selectableArchs {
+		if len(attrs.GetValueForArch(arch).Includes) > 0 || len(attrs.GetValueForArch(arch).Excludes) > 0 {
+			return true
+		}
+	}
+	return false
+}
+
+// GetValueForArch returns the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
+	switch arch {
+	case ARCH_X86:
+		return attrs.ArchValues.X86
+	case ARCH_X86_64:
+		return attrs.ArchValues.X86_64
+	case ARCH_ARM:
+		return attrs.ArchValues.Arm
+	case ARCH_ARM64:
+		return attrs.ArchValues.Arm64
+	default:
+		panic(fmt.Errorf("Unknown arch: %s", arch))
+	}
+}
+
+// SetValueForArch sets the label_list attribute value for an architecture.
+func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
+	switch arch {
+	case "x86":
+		attrs.ArchValues.X86 = value
+	case "x86_64":
+		attrs.ArchValues.X86_64 = value
+	case "arm":
+		attrs.ArchValues.Arm = value
+	case "arm64":
+		attrs.ArchValues.Arm64 = value
+	default:
+		panic(fmt.Errorf("Unknown arch: %s", arch))
+	}
+}
+
 // StringListAttribute corresponds to the string_list Bazel attribute type with
 // support for additional metadata, like configurations.
 type StringListAttribute struct {
@@ -89,11 +175,10 @@
 // Arch-specific string_list typed Bazel attribute values. This should correspond
 // to the types of architectures supported for compilation in arch.go.
 type stringListArchValues struct {
-	X86     []string
-	X86_64  []string
-	Arm     []string
-	Arm64   []string
-	Default []string
+	X86    []string
+	X86_64 []string
+	Arm    []string
+	Arm64  []string
 	// TODO(b/181299724): this is currently missing the "common" arch, which
 	// doesn't have an equivalent platform() definition yet.
 }
@@ -101,7 +186,7 @@
 // HasArchSpecificValues returns true if the attribute contains
 // architecture-specific string_list values.
 func (attrs *StringListAttribute) HasArchSpecificValues() bool {
-	for _, arch := range []string{"x86", "x86_64", "arm", "arm64", "default"} {
+	for _, arch := range selectableArchs {
 		if len(attrs.GetValueForArch(arch)) > 0 {
 			return true
 		}
@@ -112,16 +197,14 @@
 // GetValueForArch returns the string_list attribute value for an architecture.
 func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
 	switch arch {
-	case "x86":
+	case ARCH_X86:
 		return attrs.ArchValues.X86
-	case "x86_64":
+	case ARCH_X86_64:
 		return attrs.ArchValues.X86_64
-	case "arm":
+	case ARCH_ARM:
 		return attrs.ArchValues.Arm
-	case "arm64":
+	case ARCH_ARM64:
 		return attrs.ArchValues.Arm64
-	case "default":
-		return attrs.ArchValues.Default
 	default:
 		panic(fmt.Errorf("Unknown arch: %s", arch))
 	}
@@ -130,16 +213,14 @@
 // SetValueForArch sets the string_list attribute value for an architecture.
 func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
 	switch arch {
-	case "x86":
+	case ARCH_X86:
 		attrs.ArchValues.X86 = value
-	case "x86_64":
+	case ARCH_X86_64:
 		attrs.ArchValues.X86_64 = value
-	case "arm":
+	case ARCH_ARM:
 		attrs.ArchValues.Arm = value
-	case "arm64":
+	case ARCH_ARM64:
 		attrs.ArchValues.Arm64 = value
-	case "default":
-		attrs.ArchValues.Default = value
 	default:
 		panic(fmt.Errorf("Unknown arch: %s", arch))
 	}
diff --git a/bloaty/bloaty.go b/bloaty/bloaty.go
index 21bf4ac..653c489 100644
--- a/bloaty/bloaty.go
+++ b/bloaty/bloaty.go
@@ -22,7 +22,7 @@
 	"github.com/google/blueprint"
 )
 
-const bloatyDescriptorExt = "bloaty.csv"
+const bloatyDescriptorExt = ".bloaty.csv"
 const protoFilename = "binary_sizes.pb"
 
 var (
@@ -75,7 +75,7 @@
 			return
 		}
 		filePath := ctx.ModuleProvider(m, fileSizeMeasurerKey).(android.ModuleOutPath)
-		sizeFile := filePath.ReplaceExtension(ctx, bloatyDescriptorExt)
+		sizeFile := filePath.InSameDir(ctx, filePath.Base()+bloatyDescriptorExt)
 		ctx.Build(pctx, android.BuildParams{
 			Rule:        bloaty,
 			Description: "bloaty " + filePath.Rel(),
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index c74f902..cc616f2 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -27,6 +27,7 @@
         "build_conversion_test.go",
         "bzl_conversion_test.go",
         "cc_library_headers_conversion_test.go",
+        "cc_library_static_conversion_test.go",
         "cc_object_conversion_test.go",
         "conversion_test.go",
         "python_binary_conversion_test.go",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 9c98c76..e93b3dc 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -370,9 +370,20 @@
 		// values for unset properties, like system_shared_libs = ["libc", "libm", "libdl"] at
 		// https://cs.android.com/android/platform/superproject/+/master:build/soong/cc/linker.go;l=281-287;drc=f70926eef0b9b57faf04c17a1062ce50d209e480
 		//
-		// In Bazel-parlance, we would use "attr.<type>(default = <default value>)" to set the default
-		// value of unset attributes.
-		return "", nil
+		// In Bazel-parlance, we would use "attr.<type>(default = <default
+		// value>)" to set the default value of unset attributes. In the cases
+		// where the bp2build converter didn't set the default value within the
+		// mutator when creating the BazelTargetModule, this would be a zero
+		// value. For those cases, we return a non-surprising default value so
+		// generated BUILD files are syntactically correct.
+		switch propertyValue.Kind() {
+		case reflect.Slice:
+			return "[]", nil
+		case reflect.Map:
+			return "{}", nil
+		default:
+			return "", nil
+		}
 	}
 
 	var ret string
@@ -404,9 +415,34 @@
 	case reflect.Struct:
 		// Special cases where the bp2build sends additional information to the codegenerator
 		// by wrapping the attributes in a custom struct type.
-		if labels, ok := propertyValue.Interface().(bazel.LabelList); ok {
+		if labels, ok := propertyValue.Interface().(bazel.LabelListAttribute); ok {
 			// TODO(b/165114590): convert glob syntax
-			return prettyPrint(reflect.ValueOf(labels.Includes), indent)
+			ret, err := prettyPrint(reflect.ValueOf(labels.Value.Includes), indent)
+			if err != nil {
+				return ret, err
+			}
+
+			if !labels.HasArchSpecificValues() {
+				// Select statement not needed.
+				return ret, nil
+			}
+
+			ret += " + " + "select({\n"
+			for _, arch := range android.ArchTypeList() {
+				value := labels.GetValueForArch(arch.Name)
+				if len(value.Includes) > 0 {
+					ret += makeIndent(indent + 1)
+					list, _ := prettyPrint(reflect.ValueOf(value.Includes), indent+1)
+					ret += fmt.Sprintf("\"%s\": %s,\n", platformArchMap[arch], list)
+				}
+			}
+
+			ret += makeIndent(indent + 1)
+			ret += fmt.Sprintf("\"%s\": [],\n", "//conditions:default")
+
+			ret += makeIndent(indent)
+			ret += "})"
+			return ret, err
 		} else if label, ok := propertyValue.Interface().(bazel.Label); ok {
 			return fmt.Sprintf("%q", label.Label), nil
 		} else if stringList, ok := propertyValue.Interface().(bazel.StringListAttribute); ok {
@@ -432,8 +468,7 @@
 			}
 
 			ret += makeIndent(indent + 1)
-			list, _ := prettyPrint(reflect.ValueOf(stringList.GetValueForArch("default")), indent+1)
-			ret += fmt.Sprintf("\"%s\": %s,\n", "//conditions:default", list)
+			ret += fmt.Sprintf("\"%s\": [],\n", "//conditions:default")
 
 			ret += makeIndent(indent)
 			ret += "})"
@@ -533,6 +568,13 @@
 
 func escapeString(s string) string {
 	s = strings.ReplaceAll(s, "\\", "\\\\")
+
+	// b/184026959: Reverse the application of some common control sequences.
+	// These must be generated literally in the BUILD file.
+	s = strings.ReplaceAll(s, "\t", "\\t")
+	s = strings.ReplaceAll(s, "\n", "\\n")
+	s = strings.ReplaceAll(s, "\r", "\\r")
+
 	return strings.ReplaceAll(s, "\"", "\\\"")
 }
 
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index b9b250a..49897b3 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -241,6 +241,22 @@
     string_prop = "a",
 )`,
 		},
+		{
+			bp: `custom {
+	name: "control_characters",
+    string_list_prop: ["\t", "\n"],
+    string_prop: "a\t\n\r",
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTarget: `custom(
+    name = "control_characters",
+    string_list_prop = [
+        "\t",
+        "\n",
+    ],
+    string_prop = "a\t\n\r",
+)`,
+		},
 	}
 
 	dir := "."
@@ -502,8 +518,6 @@
 			expectedBazelTargets: []string{
 				`filegroup(
     name = "fg_foo",
-    srcs = [
-    ],
 )`,
 			},
 		},
@@ -1101,8 +1115,8 @@
         "out",
     ],
     srcs = [
-        "srcs-from-3",
         "in1",
+        "srcs-from-3",
     ],
 )`,
 			description: "genrule applies properties from genrule_defaults transitively",
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 5bf5c80..049f84a 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -123,7 +123,8 @@
     name: "foo_headers",
     export_include_dirs: ["dir-1", "dir-2"],
     header_libs: ["lib-1", "lib-2"],
-    export_header_lib_headers: ["lib-1", "lib-2"],
+
+    // TODO: Also support export_header_lib_headers
     bazel_module: { bp2build_available: true },
 }`,
 			expectedBazelTargets: []string{`cc_library_headers(
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
new file mode 100644
index 0000000..7bf5fd3
--- /dev/null
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -0,0 +1,307 @@
+// 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 bp2build
+
+import (
+	"android/soong/android"
+	"android/soong/cc"
+	"strings"
+	"testing"
+)
+
+const (
+	// See cc/testing.go for more context
+	soongCcLibraryStaticPreamble = `
+cc_defaults {
+	name: "linux_bionic_supported",
+}
+
+toolchain_library {
+	name: "libclang_rt.builtins-x86_64-android",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}
+
+toolchain_library {
+	name: "libatomic",
+	defaults: ["linux_bionic_supported"],
+	vendor_available: true,
+	vendor_ramdisk_available: true,
+	product_available: true,
+	recovery_available: true,
+	native_bridge_supported: true,
+	src: "",
+}`
+)
+
+func TestCcLibraryStaticLoadStatement(t *testing.T) {
+	testCases := []struct {
+		bazelTargets           BazelTargets
+		expectedLoadStatements string
+	}{
+		{
+			bazelTargets: BazelTargets{
+				BazelTarget{
+					name:      "cc_library_static_target",
+					ruleClass: "cc_library_static",
+					// NOTE: No bzlLoadLocation for native rules
+				},
+			},
+			expectedLoadStatements: ``,
+		},
+	}
+
+	for _, testCase := range testCases {
+		actual := testCase.bazelTargets.LoadStatements()
+		expected := testCase.expectedLoadStatements
+		if actual != expected {
+			t.Fatalf("Expected load statements to be %s, got %s", expected, actual)
+		}
+	}
+
+}
+
+func TestCcLibraryStaticBp2Build(t *testing.T) {
+	testCases := []struct {
+		description                        string
+		moduleTypeUnderTest                string
+		moduleTypeUnderTestFactory         android.ModuleFactory
+		moduleTypeUnderTestBp2BuildMutator func(android.TopDownMutatorContext)
+		preArchMutators                    []android.RegisterMutatorFunc
+		depsMutators                       []android.RegisterMutatorFunc
+		bp                                 string
+		expectedBazelTargets               []string
+		filesystem                         map[string]string
+		dir                                string
+	}{
+		{
+			description:                        "cc_library_static test",
+			moduleTypeUnderTest:                "cc_library_static",
+			moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+			moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+			filesystem: map[string]string{
+				// NOTE: include_dir headers *should not* appear in Bazel hdrs later (?)
+				"include_dir_1/include_dir_1_a.h": "",
+				"include_dir_1/include_dir_1_b.h": "",
+				"include_dir_2/include_dir_2_a.h": "",
+				"include_dir_2/include_dir_2_b.h": "",
+				// NOTE: local_include_dir headers *should not* appear in Bazel hdrs later (?)
+				"local_include_dir_1/local_include_dir_1_a.h": "",
+				"local_include_dir_1/local_include_dir_1_b.h": "",
+				"local_include_dir_2/local_include_dir_2_a.h": "",
+				"local_include_dir_2/local_include_dir_2_b.h": "",
+				// NOTE: export_include_dir headers *should* appear in Bazel hdrs later
+				"export_include_dir_1/export_include_dir_1_a.h": "",
+				"export_include_dir_1/export_include_dir_1_b.h": "",
+				"export_include_dir_2/export_include_dir_2_a.h": "",
+				"export_include_dir_2/export_include_dir_2_b.h": "",
+			},
+			bp: soongCcLibraryStaticPreamble + `
+cc_library_headers {
+    name: "header_lib_1",
+    export_include_dirs: ["header_lib_1"],
+}
+
+cc_library_headers {
+    name: "header_lib_2",
+    export_include_dirs: ["header_lib_2"],
+}
+
+cc_library_static {
+    name: "static_lib_1",
+    srcs: ["static_lib_1.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "static_lib_2",
+    srcs: ["static_lib_2.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "whole_static_lib_1",
+    srcs: ["whole_static_lib_1.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "whole_static_lib_2",
+    srcs: ["whole_static_lib_2.cc"],
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static {
+    name: "foo_static",
+    srcs: [
+        "foo_static1.cc",
+	"foo_static2.cc",
+    ],
+    cflags: [
+        "-Dflag1",
+	"-Dflag2"
+    ],
+    static_libs: [
+        "static_lib_1",
+	"static_lib_2"
+    ],
+    whole_static_libs: [
+        "whole_static_lib_1",
+	"whole_static_lib_2"
+    ],
+    include_dirs: [
+	"include_dir_1",
+	"include_dir_2",
+    ],
+    local_include_dirs: [
+        "local_include_dir_1",
+	"local_include_dir_2",
+    ],
+    export_include_dirs: [
+	"export_include_dir_1",
+	"export_include_dir_2"
+    ],
+    header_libs: [
+        "header_lib_1",
+	"header_lib_2"
+    ],
+
+    // TODO: Also support export_header_lib_headers
+
+    bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-Dflag1",
+        "-Dflag2",
+    ],
+    deps = [
+        ":header_lib_1",
+        ":header_lib_2",
+        ":static_lib_1",
+        ":static_lib_2",
+        ":whole_static_lib_1",
+        ":whole_static_lib_2",
+    ],
+    hdrs = [
+        "export_include_dir_1/export_include_dir_1_a.h",
+        "export_include_dir_1/export_include_dir_1_b.h",
+        "export_include_dir_2/export_include_dir_2_a.h",
+        "export_include_dir_2/export_include_dir_2_b.h",
+    ],
+    includes = [
+        "export_include_dir_1",
+        "export_include_dir_2",
+        "include_dir_1",
+        "include_dir_2",
+        "local_include_dir_1",
+        "local_include_dir_2",
+    ],
+    linkstatic = True,
+    srcs = [
+        "foo_static1.cc",
+        "foo_static2.cc",
+    ],
+)`, `cc_library_static(
+    name = "static_lib_1",
+    linkstatic = True,
+    srcs = [
+        "static_lib_1.cc",
+    ],
+)`, `cc_library_static(
+    name = "static_lib_2",
+    linkstatic = True,
+    srcs = [
+        "static_lib_2.cc",
+    ],
+)`, `cc_library_static(
+    name = "whole_static_lib_1",
+    linkstatic = True,
+    srcs = [
+        "whole_static_lib_1.cc",
+    ],
+)`, `cc_library_static(
+    name = "whole_static_lib_2",
+    linkstatic = True,
+    srcs = [
+        "whole_static_lib_2.cc",
+    ],
+)`},
+		},
+	}
+
+	dir := "."
+	for _, testCase := range testCases {
+		filesystem := make(map[string][]byte)
+		toParse := []string{
+			"Android.bp",
+		}
+		for f, content := range testCase.filesystem {
+			if strings.HasSuffix(f, "Android.bp") {
+				toParse = append(toParse, f)
+			}
+			filesystem[f] = []byte(content)
+		}
+		config := android.TestConfig(buildDir, nil, testCase.bp, filesystem)
+		ctx := android.NewTestContext(config)
+
+		cc.RegisterCCBuildComponents(ctx)
+		ctx.RegisterModuleType("toolchain_library", cc.ToolchainLibraryFactory)
+		ctx.RegisterModuleType("cc_library_headers", cc.LibraryHeaderFactory)
+
+		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+		for _, m := range testCase.depsMutators {
+			ctx.DepsBp2BuildMutators(m)
+		}
+		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+		ctx.RegisterForBazelConversion()
+
+		_, errs := ctx.ParseFileList(dir, toParse)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+		_, errs = ctx.ResolveDependencies(config)
+		if Errored(t, testCase.description, errs) {
+			continue
+		}
+
+		checkDir := dir
+		if testCase.dir != "" {
+			checkDir = testCase.dir
+		}
+		codegenCtx := NewCodegenContext(config, *ctx.Context, Bp2Build)
+		bazelTargets := generateBazelTargetsForDir(codegenCtx, checkDir)
+		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
+			t.Errorf("%s: Expected %d bazel target, got %d", testCase.description, expectedCount, actualCount)
+		} else {
+			for i, target := range bazelTargets {
+				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+					t.Errorf(
+						"%s: Expected generated Bazel target to be '%s', got '%s'",
+						testCase.description,
+						w,
+						g,
+					)
+				}
+			}
+		}
+	}
+}
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index 1c058ba..b007033 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -74,8 +74,8 @@
     ],
     srcs = [
         "a/b/bar.h",
-        "a/b/foo.h",
         "a/b/c.c",
+        "a/b/foo.h",
     ],
 )`,
 			},
@@ -278,9 +278,13 @@
 			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 			blueprint: `cc_object {
     name: "foo",
+    srcs: ["a.cpp"],
     arch: {
         x86: {
-            cflags: ["-fPIC"],
+            cflags: ["-fPIC"], // string list
+        },
+        arm: {
+            srcs: ["arch/arm/file.S"], // label list
         },
     },
     bazel_module: { bp2build_available: true },
@@ -295,12 +299,19 @@
         "@bazel_tools//platforms:x86_32": [
             "-fPIC",
         ],
-        "//conditions:default": [
-        ],
+        "//conditions:default": [],
     }),
     local_include_dirs = [
         ".",
     ],
+    srcs = [
+        "a.cpp",
+    ] + select({
+        "@bazel_tools//platforms:arm": [
+            "arch/arm/file.S",
+        ],
+        "//conditions:default": [],
+    }),
 )`,
 			},
 		},
@@ -311,17 +322,22 @@
 			moduleTypeUnderTestBp2BuildMutator: cc.ObjectBp2Build,
 			blueprint: `cc_object {
     name: "foo",
+    srcs: ["base.cpp"],
     arch: {
         x86: {
+            srcs: ["x86.cpp"],
             cflags: ["-fPIC"],
         },
         x86_64: {
+            srcs: ["x86_64.cpp"],
             cflags: ["-fPIC"],
         },
         arm: {
+            srcs: ["arm.cpp"],
             cflags: ["-Wall"],
         },
         arm64: {
+            srcs: ["arm64.cpp"],
             cflags: ["-Wall"],
         },
     },
@@ -346,12 +362,28 @@
         "@bazel_tools//platforms:x86_64": [
             "-fPIC",
         ],
-        "//conditions:default": [
-        ],
+        "//conditions:default": [],
     }),
     local_include_dirs = [
         ".",
     ],
+    srcs = [
+        "base.cpp",
+    ] + select({
+        "@bazel_tools//platforms:arm": [
+            "arm.cpp",
+        ],
+        "@bazel_tools//platforms:aarch64": [
+            "arm64.cpp",
+        ],
+        "@bazel_tools//platforms:x86_32": [
+            "x86.cpp",
+        ],
+        "@bazel_tools//platforms:x86_64": [
+            "x86_64.cpp",
+        ],
+        "//conditions:default": [],
+    }),
 )`,
 			},
 		},
diff --git a/bp2build/constants.go b/bp2build/constants.go
index 23bca83..70f320e 100644
--- a/bp2build/constants.go
+++ b/bp2build/constants.go
@@ -18,7 +18,7 @@
 	// When both a BUILD and BUILD.bazel file are exist in the same package, the BUILD.bazel file will
 	// be preferred for use within a Bazel build.
 
-	// The file name used for automatically generated files. Files with this name are ignored by git.
+	// The file name used for automatically generated files.
 	GeneratedBuildFileName = "BUILD"
 	// The file name used for hand-crafted build targets.
 	HandcraftedBuildFileName = "BUILD.bazel"
diff --git a/cc/Android.bp b/cc/Android.bp
index bdbb3c0..79e92cb 100644
--- a/cc/Android.bp
+++ b/cc/Android.bp
@@ -10,6 +10,7 @@
         "blueprint-pathtools",
         "soong",
         "soong-android",
+        "soong-bazel",
         "soong-cc-config",
         "soong-etc",
         "soong-genrule",
diff --git a/cc/cc.go b/cc/cc.go
index 0c46b24..f074597 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -363,7 +363,7 @@
 	// List of APEXes that this module has private access to for testing purpose. The module
 	// can depend on libraries that are not exported by the APEXes and use private symbols
 	// from the exported libraries.
-	Test_for []string
+	Test_for []string `android:"arch_variant"`
 }
 
 type VendorProperties struct {
@@ -1363,6 +1363,19 @@
 	if ver == "apex_inherit" || ver == "" {
 		ver = ctx.sdkVersion()
 	}
+	// For crt objects, the meaning of min_sdk_version is very different from other types of
+	// module. For them, min_sdk_version defines the oldest version that the build system will
+	// create versioned variants for. For example, if min_sdk_version is 16, then sdk variant of
+	// the crt object has local variants of 16, 17, ..., up to the latest version. sdk_version
+	// and min_sdk_version properties of the variants are set to the corresponding version
+	// numbers. However, the platform (non-sdk) variant of the crt object is left untouched.
+	// min_sdk_version: 16 doesn't actually mean that the platform variant has to support such
+	// an old version. Since the variant is for the platform, it's preferred to target the
+	// latest version.
+	if ctx.mod.SplitPerApiLevel() && !ctx.isSdkVariant() {
+		ver = strconv.Itoa(android.FutureApiLevelInt)
+	}
+
 	// Also make sure that minSdkVersion is not greater than sdkVersion, if they are both numbers
 	sdkVersionInt, err := strconv.Atoi(ctx.sdkVersion())
 	minSdkVersionInt, err2 := strconv.Atoi(ver)
@@ -1927,9 +1940,14 @@
 		return nil
 	}
 	if m.UseSdk() {
+		// Choose the CRT that best satisfies the min_sdk_version requirement of this module
+		minSdkVersion := m.MinSdkVersion()
+		if minSdkVersion == "" || minSdkVersion == "apex_inherit" {
+			minSdkVersion = m.SdkVersion()
+		}
 		return []blueprint.Variation{
 			{Mutator: "sdk", Variation: "sdk"},
-			{Mutator: "version", Variation: m.SdkVersion()},
+			{Mutator: "version", Variation: minSdkVersion},
 		}
 	}
 	return []blueprint.Variation{
@@ -2613,14 +2631,31 @@
 						// However, for host, ramdisk, vendor_ramdisk, recovery or bootstrap modules,
 						// always link to non-stub variant
 						useStubs = dep.(android.ApexModule).NotInPlatform() && !c.bootstrap()
-						// Another exception: if this module is bundled with an APEX, then
-						// it is linked with the non-stub variant of a module in the APEX
-						// as if this is part of the APEX.
-						testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
-						for _, apexContents := range testFor.ApexContents {
-							if apexContents.DirectlyInApex(depName) {
+						if useStubs {
+							// Another exception: if this module is a test for an APEX, then
+							// it is linked with the non-stub variant of a module in the APEX
+							// as if this is part of the APEX.
+							testFor := ctx.Provider(android.ApexTestForInfoProvider).(android.ApexTestForInfo)
+							for _, apexContents := range testFor.ApexContents {
+								if apexContents.DirectlyInApex(depName) {
+									useStubs = false
+									break
+								}
+							}
+						}
+						if useStubs {
+							// Yet another exception: If this module and the dependency are
+							// available to the same APEXes then skip stubs between their
+							// platform variants. This complements the test_for case above,
+							// which avoids the stubs on a direct APEX library dependency, by
+							// avoiding stubs for indirect test dependencies as well.
+							//
+							// TODO(b/183882457): This doesn't work if the two libraries have
+							// only partially overlapping apex_available. For that test_for
+							// modules would need to be split into APEX variants and resolved
+							// separately for each APEX they have access to.
+							if android.AvailableToSameApexes(c, dep.(android.ApexModule)) {
 								useStubs = false
-								break
 							}
 						}
 					} else {
diff --git a/cc/cc_test.go b/cc/cc_test.go
index 7196615..205c71f 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -166,6 +166,15 @@
 	recoveryVariant = "android_recovery_arm64_armv8-a_shared"
 )
 
+// Test that the PrepareForTestWithCcDefaultModules provides all the files that it uses by
+// running it in a fixture that requires all source files to exist.
+func TestPrepareForTestWithCcDefaultModules(t *testing.T) {
+	android.GroupFixturePreparers(
+		PrepareForTestWithCcDefaultModules,
+		android.PrepareForTestDisallowNonExistentPaths,
+	).RunTest(t)
+}
+
 func TestFuchsiaDeps(t *testing.T) {
 	t.Helper()
 
@@ -292,13 +301,9 @@
 
 func checkSnapshotIncludeExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string, include bool, fake bool) {
 	t.Helper()
-	mod, ok := ctx.ModuleForTests(moduleName, variant).Module().(android.OutputFileProducer)
-	if !ok {
-		t.Errorf("%q must have output\n", moduleName)
-		return
-	}
-	outputFiles, err := mod.OutputFiles("")
-	if err != nil || len(outputFiles) != 1 {
+	mod := ctx.ModuleForTests(moduleName, variant)
+	outputFiles := mod.OutputFiles(t, "")
+	if len(outputFiles) != 1 {
 		t.Errorf("%q must have single output\n", moduleName)
 		return
 	}
@@ -324,14 +329,17 @@
 }
 
 func checkSnapshot(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, false)
 }
 
 func checkSnapshotExclude(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, false, false)
 }
 
 func checkSnapshotRule(t *testing.T, ctx *android.TestContext, singleton android.TestingSingleton, moduleName, snapshotFilename, subDir, variant string) {
+	t.Helper()
 	checkSnapshotIncludeExclude(t, ctx, singleton, moduleName, snapshotFilename, subDir, variant, true, true)
 }
 
@@ -2543,7 +2551,7 @@
 func getOutputPaths(ctx *android.TestContext, variant string, moduleNames []string) (paths android.Paths) {
 	for _, moduleName := range moduleNames {
 		module := ctx.ModuleForTests(moduleName, variant).Module().(*Module)
-		output := module.outputFile.Path()
+		output := module.outputFile.Path().RelativeToTop()
 		paths = append(paths, output)
 	}
 	return paths
@@ -2574,7 +2582,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
 	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b", "d"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -2608,7 +2617,8 @@
 
 	variant := "android_arm64_armv8-a_static"
 	moduleA := ctx.ModuleForTests("a", variant).Module().(*Module)
-	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).TransitiveStaticLibrariesForOrdering.ToList()
+	actual := ctx.ModuleProvider(moduleA, StaticLibraryInfoProvider).(StaticLibraryInfo).
+		TransitiveStaticLibrariesForOrdering.ToList().RelativeToTop()
 	expected := getOutputPaths(ctx, variant, []string{"a", "c", "b"})
 
 	if !reflect.DeepEqual(actual, expected) {
@@ -3628,6 +3638,71 @@
 	}
 }
 
+func TestMinSdkVersionInClangTriple(t *testing.T) {
+	ctx := testCc(t, `
+		cc_library_shared {
+			name: "libfoo",
+			srcs: ["foo.c"],
+			min_sdk_version: "29",
+		}`)
+
+	cFlags := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared").Rule("cc").Args["cFlags"]
+	android.AssertStringDoesContain(t, "min sdk version", cFlags, "-target aarch64-linux-android29")
+}
+
+func TestMinSdkVersionsOfCrtObjects(t *testing.T) {
+	ctx := testCc(t, `
+		cc_object {
+			name: "crt_foo",
+			srcs: ["foo.c"],
+			crt: true,
+			stl: "none",
+			min_sdk_version: "28",
+
+		}`)
+
+	arch := "android_arm64_armv8-a"
+	for _, v := range []string{"", "28", "29", "30", "current"} {
+		var variant string
+		if v == "" {
+			variant = arch
+		} else {
+			variant = arch + "_sdk_" + v
+		}
+		cflags := ctx.ModuleForTests("crt_foo", variant).Rule("cc").Args["cFlags"]
+		vNum := v
+		if v == "current" || v == "" {
+			vNum = "10000"
+		}
+		expected := "-target aarch64-linux-android" + vNum + " "
+		android.AssertStringDoesContain(t, "cflag", cflags, expected)
+	}
+}
+
+func TestUseCrtObjectOfCorrectVersion(t *testing.T) {
+	ctx := testCc(t, `
+		cc_binary {
+			name: "bin",
+			srcs: ["foo.c"],
+			stl: "none",
+			min_sdk_version: "29",
+			sdk_version: "current",
+		}
+		`)
+
+	// Sdk variant uses the crt object of the matching min_sdk_version
+	variant := "android_arm64_armv8-a_sdk"
+	crt := ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+	android.AssertStringDoesContain(t, "crt dep of sdk variant", crt,
+		variant+"_29/crtbegin_dynamic.o")
+
+	// platform variant uses the crt object built for platform
+	variant = "android_arm64_armv8-a"
+	crt = ctx.ModuleForTests("bin", variant).Rule("ld").Args["crtBegin"]
+	android.AssertStringDoesContain(t, "crt dep of platform variant", crt,
+		variant+"/crtbegin_dynamic.o")
+}
+
 type MemtagNoteType int
 
 const (
diff --git a/cc/compiler.go b/cc/compiler.go
index b09b58e..78a5a5d 100644
--- a/cc/compiler.go
+++ b/cc/compiler.go
@@ -414,12 +414,7 @@
 
 	target := "-target " + tc.ClangTriple()
 	if ctx.Os().Class == android.Device {
-		// When built for the non-updateble part of platform, minSdkVersion doesn't matter.
-		// It matters only when building we are building for modules that can be unbundled.
-		version := "current"
-		if !ctx.isForPlatform() || ctx.isSdkVariant() {
-			version = ctx.minSdkVersion()
-		}
+		version := ctx.minSdkVersion()
 		if version == "" || version == "current" {
 			target += strconv.Itoa(android.FutureApiLevelInt)
 		} else {
diff --git a/cc/gen_test.go b/cc/gen_test.go
index 41ef95c..40a5716 100644
--- a/cc/gen_test.go
+++ b/cc/gen_test.go
@@ -36,8 +36,10 @@
 		aidl := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Rule("aidl")
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
-			t.Errorf("missing aidl includes in global flags")
+		expected := "-I" + filepath.Dir(aidl.Output.String())
+		actual := android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)
+		if !inList(expected, actual) {
+			t.Errorf("missing aidl includes in global flags, expected %q, actual %q", expected, actual)
 		}
 	})
 
@@ -61,7 +63,7 @@
 		aidlManifest := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Output("aidl.sbox.textproto")
 		libfoo := ctx.ModuleForTests("libfoo", "android_arm_armv7-a-neon_shared").Module().(*Module)
 
-		if !inList("-I"+filepath.Dir(aidl.Output.String()), libfoo.flags.Local.CommonFlags) {
+		if !inList("-I"+filepath.Dir(aidl.Output.String()), android.StringsRelativeToTop(ctx.Config(), libfoo.flags.Local.CommonFlags)) {
 			t.Errorf("missing aidl includes in global flags")
 		}
 
diff --git a/cc/library.go b/cc/library.go
index 22a36c6..28e4f61 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -27,6 +27,7 @@
 	"github.com/google/blueprint/pathtools"
 
 	"android/soong/android"
+	"android/soong/bazel"
 	"android/soong/cc/config"
 )
 
@@ -200,6 +201,8 @@
 
 func init() {
 	RegisterLibraryBuildComponents(android.InitRegistrationContext)
+
+	android.RegisterBp2BuildMutator("cc_library_static", CcLibraryStaticBp2Build)
 }
 
 func RegisterLibraryBuildComponents(ctx android.RegistrationContext) {
@@ -1914,6 +1917,7 @@
 
 	for i, module := range modules {
 		module.(*Module).Properties.Sdk_version = StringPtr(versionStrs[i])
+		module.(*Module).Properties.Min_sdk_version = StringPtr(versionStrs[i])
 	}
 }
 
@@ -2024,3 +2028,135 @@
 
 	return outputFile
 }
+
+func Bp2BuildParseHeaderLibs(ctx android.TopDownMutatorContext, module *Module) bazel.LabelListAttribute {
+	var headerLibs []string
+	for _, linkerProps := range module.linker.linkerProps() {
+		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
+			headerLibs = baseLinkerProps.Header_libs
+			// FIXME: re-export include dirs from baseLinkerProps.Export_header_lib_headers?
+			break
+		}
+	}
+	headerLibsLabels := bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, headerLibs))
+	return headerLibsLabels
+}
+
+func Bp2BuildParseExportedIncludes(ctx android.TopDownMutatorContext, module *Module) (bazel.LabelListAttribute, bazel.LabelListAttribute) {
+	libraryDecorator := module.linker.(*libraryDecorator)
+
+	includeDirs := libraryDecorator.flagExporter.Properties.Export_system_include_dirs
+	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
+
+	includeDirsLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
+
+	var includeDirGlobs []string
+	for _, includeDir := range includeDirs {
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.inc")
+		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.hpp")
+	}
+
+	headersLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
+	return bazel.MakeLabelListAttribute(includeDirsLabels), bazel.MakeLabelListAttribute(headersLabels)
+}
+
+type bazelCcLibraryStaticAttributes struct {
+	Copts      []string
+	Srcs       bazel.LabelListAttribute
+	Deps       bazel.LabelListAttribute
+	Linkstatic bool
+	Includes   bazel.LabelListAttribute
+	Hdrs       bazel.LabelListAttribute
+}
+
+type bazelCcLibraryStatic struct {
+	android.BazelTargetModuleBase
+	bazelCcLibraryStaticAttributes
+}
+
+func BazelCcLibraryStaticFactory() android.Module {
+	module := &bazelCcLibraryStatic{}
+	module.AddProperties(&module.bazelCcLibraryStaticAttributes)
+	android.InitBazelTargetModule(module)
+	return module
+}
+
+func CcLibraryStaticBp2Build(ctx android.TopDownMutatorContext) {
+	module, ok := ctx.Module().(*Module)
+	if !ok {
+		// Not a cc module
+		return
+	}
+	if !module.ConvertWithBp2build(ctx) {
+		return
+	}
+	if ctx.ModuleType() != "cc_library_static" {
+		return
+	}
+
+	var copts []string
+	var srcs []string
+	var includeDirs []string
+	var localIncludeDirs []string
+	for _, props := range module.compiler.compilerProps() {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+			copts = baseCompilerProps.Cflags
+			srcs = baseCompilerProps.Srcs
+			includeDirs = baseCompilerProps.Include_dirs
+			localIncludeDirs = baseCompilerProps.Local_include_dirs
+			break
+		}
+	}
+	srcsLabels := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, srcs))
+
+	var staticLibs []string
+	var wholeStaticLibs []string
+	for _, props := range module.linker.linkerProps() {
+		if baseLinkerProperties, ok := props.(*BaseLinkerProperties); ok {
+			staticLibs = baseLinkerProperties.Static_libs
+			wholeStaticLibs = baseLinkerProperties.Whole_static_libs
+			break
+		}
+	}
+
+	// FIXME: Treat Static_libs and Whole_static_libs differently?
+	allDeps := staticLibs
+	allDeps = append(allDeps, wholeStaticLibs...)
+
+	depsLabels := android.BazelLabelForModuleDeps(ctx, allDeps)
+
+	// FIXME: Unify absolute vs relative paths
+	// FIXME: Use -I copts instead of setting includes= ?
+	allIncludes := includeDirs
+	allIncludes = append(allIncludes, localIncludeDirs...)
+	includesLabels := android.BazelLabelForModuleSrc(ctx, allIncludes)
+
+	exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
+	includesLabels.Append(exportedIncludesLabels.Value)
+
+	headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
+	depsLabels.Append(headerLibsLabels.Value)
+
+	attrs := &bazelCcLibraryStaticAttributes{
+		Copts:      copts,
+		Srcs:       srcsLabels,
+		Deps:       bazel.MakeLabelListAttribute(depsLabels),
+		Linkstatic: true,
+		Includes:   bazel.MakeLabelListAttribute(includesLabels),
+		Hdrs:       exportedIncludesHeadersLabels,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		Rule_class:        "cc_library_static",
+		Bzl_load_location: "//build/bazel/rules:cc_library_static.bzl",
+	}
+
+	ctx.CreateBazelTargetModule(BazelCcLibraryStaticFactory, module.Name(), props, attrs)
+}
+
+func (m *bazelCcLibraryStatic) Name() string {
+	return m.BaseModuleName()
+}
+
+func (m *bazelCcLibraryStatic) GenerateAndroidBuildActions(ctx android.ModuleContext) {}
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 0f4d8a6..8286848 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -62,9 +62,9 @@
 }
 
 type bazelCcLibraryHeadersAttributes struct {
-	Hdrs     bazel.LabelList
-	Includes bazel.LabelList
-	Deps     bazel.LabelList
+	Hdrs     bazel.LabelListAttribute
+	Includes bazel.LabelListAttribute
+	Deps     bazel.LabelListAttribute
 }
 
 type bazelCcLibraryHeaders struct {
@@ -94,35 +94,14 @@
 		return
 	}
 
-	lib, _ := module.linker.(*libraryDecorator)
+	exportedIncludesLabels, exportedIncludesHeadersLabels := Bp2BuildParseExportedIncludes(ctx, module)
 
-	// list of directories that will be added to the include path (using -I) for this
-	// module and any module that links against this module.
-	includeDirs := lib.flagExporter.Properties.Export_system_include_dirs
-	includeDirs = append(includeDirs, lib.flagExporter.Properties.Export_include_dirs...)
-	includeDirLabels := android.BazelLabelForModuleSrc(ctx, includeDirs)
-
-	var includeDirGlobs []string
-	for _, includeDir := range includeDirs {
-		includeDirGlobs = append(includeDirGlobs, includeDir+"/**/*.h")
-	}
-
-	headerLabels := android.BazelLabelForModuleSrc(ctx, includeDirGlobs)
-
-	// list of modules that should only provide headers for this module.
-	var headerLibs []string
-	for _, linkerProps := range lib.linkerProps() {
-		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
-			headerLibs = baseLinkerProps.Export_header_lib_headers
-			break
-		}
-	}
-	headerLibLabels := android.BazelLabelForModuleDeps(ctx, headerLibs)
+	headerLibsLabels := Bp2BuildParseHeaderLibs(ctx, module)
 
 	attrs := &bazelCcLibraryHeadersAttributes{
-		Includes: includeDirLabels,
-		Hdrs:     headerLabels,
-		Deps:     headerLibLabels,
+		Includes: exportedIncludesLabels,
+		Hdrs:     exportedIncludesHeadersLabels,
+		Deps:     headerLibsLabels,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/linkable.go b/cc/linkable.go
index 58919a0..6aa238b 100644
--- a/cc/linkable.go
+++ b/cc/linkable.go
@@ -111,6 +111,7 @@
 	InProduct() bool
 
 	SdkVersion() string
+	MinSdkVersion() string
 	AlwaysSdk() bool
 	IsSdkVariant() bool
 
@@ -157,8 +158,16 @@
 }
 
 // StaticDepTag returns the dependency tag for any C++ static libraries.
-func StaticDepTag() blueprint.DependencyTag {
-	return libraryDependencyTag{Kind: staticLibraryDependency}
+func StaticDepTag(wholeStatic bool) blueprint.DependencyTag {
+	return libraryDependencyTag{Kind: staticLibraryDependency, wholeStatic: wholeStatic}
+}
+
+// IsWholeStaticLib whether a dependency tag is a whole static library dependency.
+func IsWholeStaticLib(depTag blueprint.DependencyTag) bool {
+	if tag, ok := depTag.(libraryDependencyTag); ok {
+		return tag.wholeStatic
+	}
+	return false
 }
 
 // HeaderDepTag returns the dependency tag for any C++ "header-only" libraries.
diff --git a/cc/object.go b/cc/object.go
index 664be8d..abc3e83 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -103,8 +103,8 @@
 
 // For bp2build conversion.
 type bazelObjectAttributes struct {
-	Srcs               bazel.LabelList
-	Deps               bazel.LabelList
+	Srcs               bazel.LabelListAttribute
+	Deps               bazel.LabelListAttribute
 	Copts              bazel.StringListAttribute
 	Local_include_dirs []string
 }
@@ -147,14 +147,16 @@
 
 	// Set arch-specific configurable attributes
 	var copts bazel.StringListAttribute
-	var srcs []string
-	var excludeSrcs []string
+	var srcs bazel.LabelListAttribute
 	var localIncludeDirs []string
 	for _, props := range m.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			copts.Value = baseCompilerProps.Cflags
-			srcs = baseCompilerProps.Srcs
-			excludeSrcs = baseCompilerProps.Exclude_srcs
+			srcs = bazel.MakeLabelListAttribute(
+				android.BazelLabelForModuleSrcExcludes(
+					ctx,
+					baseCompilerProps.Srcs,
+					baseCompilerProps.Exclude_srcs))
 			localIncludeDirs = baseCompilerProps.Local_include_dirs
 			break
 		}
@@ -164,22 +166,23 @@
 		localIncludeDirs = append(localIncludeDirs, ".")
 	}
 
-	var deps bazel.LabelList
+	var deps bazel.LabelListAttribute
 	for _, props := range m.linker.linkerProps() {
 		if objectLinkerProps, ok := props.(*ObjectLinkerProperties); ok {
-			deps = android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs)
+			deps = bazel.MakeLabelListAttribute(
+				android.BazelLabelForModuleDeps(ctx, objectLinkerProps.Objs))
 		}
 	}
 
 	for arch, p := range m.GetArchProperties(&BaseCompilerProperties{}) {
 		if cProps, ok := p.(*BaseCompilerProperties); ok {
+			srcs.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcExcludes(ctx, cProps.Srcs, cProps.Exclude_srcs))
 			copts.SetValueForArch(arch.Name, cProps.Cflags)
 		}
 	}
-	copts.SetValueForArch("default", []string{})
 
 	attrs := &bazelObjectAttributes{
-		Srcs:               android.BazelLabelForModuleSrcExcludes(ctx, srcs, excludeSrcs),
+		Srcs:               srcs,
 		Deps:               deps,
 		Copts:              copts,
 		Local_include_dirs: localIncludeDirs,
diff --git a/cc/prebuilt_test.go b/cc/prebuilt_test.go
index 20274b2..f72371a 100644
--- a/cc/prebuilt_test.go
+++ b/cc/prebuilt_test.go
@@ -15,7 +15,6 @@
 package cc
 
 import (
-	"path/filepath"
 	"testing"
 
 	"android/soong/android"
@@ -302,8 +301,7 @@
 	})
 
 	fooRule := ctx.ModuleForTests("foo", "linux_glibc_x86_64").Rule("Symlink")
-	assertString(t, fooRule.Output.String(),
-		filepath.Join(buildDir, ".intermediates/foo/linux_glibc_x86_64/foo"))
+	assertString(t, fooRule.Output.String(), "out/soong/.intermediates/foo/linux_glibc_x86_64/foo")
 	assertString(t, fooRule.Args["fromPath"], "$$PWD/linux_glibc_x86_64/bin/foo")
 
 	var libfooDep android.Path
@@ -313,8 +311,7 @@
 			break
 		}
 	}
-	assertString(t, libfooDep.String(),
-		filepath.Join(buildDir, ".intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so"))
+	assertString(t, libfooDep.String(), "out/soong/.intermediates/libfoo/linux_glibc_x86_64_shared/libfoo.so")
 }
 
 func TestPrebuiltLibrarySanitized(t *testing.T) {
diff --git a/cc/proto_test.go b/cc/proto_test.go
index f8bbd26..3d636fb 100644
--- a/cc/proto_test.go
+++ b/cc/proto_test.go
@@ -61,7 +61,7 @@
 			t.Errorf("expected %q in %q", w, cmd)
 		}
 
-		foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().String()
+		foobarPath := foobar.Module().(android.HostToolProvider).HostToolPath().RelativeToPath().String()
 
 		if w := "--plugin=protoc-gen-foobar=" + foobarPath; !strings.Contains(cmd, w) {
 			t.Errorf("expected %q in %q", w, cmd)
diff --git a/cc/sanitize.go b/cc/sanitize.go
index cd09e6e..e1ac9f0 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -1129,6 +1129,9 @@
 			Bool(c.sanitize.Properties.Sanitize.Undefined) ||
 			Bool(c.sanitize.Properties.Sanitize.All_undefined) {
 			runtimeLibrary = config.UndefinedBehaviorSanitizerRuntimeLibrary(toolchain)
+			if c.staticBinary() {
+				runtimeLibrary += ".static"
+			}
 		}
 
 		if runtimeLibrary != "" && (toolchain.Bionic() || c.sanitize.Properties.UbsanRuntimeDep) {
diff --git a/cc/sdk_test.go b/cc/sdk_test.go
index 5a3c181..61925e3 100644
--- a/cc/sdk_test.go
+++ b/cc/sdk_test.go
@@ -66,6 +66,7 @@
 		} else {
 			toFile = m.outputFile.Path()
 		}
+		toFile = toFile.RelativeToTop()
 
 		rule := from.Description("link")
 		for _, dep := range rule.Implicits {
diff --git a/cc/stl.go b/cc/stl.go
index 75fab17..594231d 100644
--- a/cc/stl.go
+++ b/cc/stl.go
@@ -188,12 +188,7 @@
 		if needsLibAndroidSupport(ctx) {
 			deps.StaticLibs = append(deps.StaticLibs, "ndk_libandroid_support")
 		}
-		// TODO: Switch the NDK over to the LLVM unwinder for non-arm32 architectures.
-		if ctx.Arch().ArchType == android.Arm {
-			deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
-		} else {
-			deps.StaticLibs = append(deps.StaticLibs, "libgcc_stripped")
-		}
+		deps.StaticLibs = append(deps.StaticLibs, "ndk_libunwind")
 	default:
 		panic(fmt.Errorf("Unknown stl: %q", stl.Properties.SelectedStl))
 	}
diff --git a/cc/testing.go b/cc/testing.go
index d8adc61..6e35655 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -619,11 +619,26 @@
 
 		RegisterVndkLibraryTxtTypes(ctx)
 	}),
+
+	// Additional files needed in tests that disallow non-existent source files.
+	// This includes files that are needed by all, or at least most, instances of a cc module type.
+	android.MockFS{
+		// Needed for ndk_prebuilt_(shared|static)_stl.
+		"prebuilts/ndk/current/sources/cxx-stl/llvm-libc++/libs": nil,
+	}.AddToFixture(),
 )
 
 // Preparer that will define default cc modules, e.g. standard prebuilt modules.
 var PrepareForTestWithCcDefaultModules = android.GroupFixturePreparers(
 	PrepareForTestWithCcBuildComponents,
+
+	// Additional files needed in tests that disallow non-existent source.
+	android.MockFS{
+		"defaults/cc/common/libc.map.txt":  nil,
+		"defaults/cc/common/libdl.map.txt": nil,
+		"defaults/cc/common/libm.map.txt":  nil,
+	}.AddToFixture(),
+
 	// Place the default cc test modules that are common to all platforms in a location that will not
 	// conflict with default test modules defined by other packages.
 	android.FixtureAddTextFile(DefaultCcCommonTestModulesDir+"Android.bp", commonDefaultModules()),
diff --git a/cmd/pom2bp/pom2bp.go b/cmd/pom2bp/pom2bp.go
index d341b8c..d9116b0 100644
--- a/cmd/pom2bp/pom2bp.go
+++ b/cmd/pom2bp/pom2bp.go
@@ -144,6 +144,7 @@
 var hostAndDeviceModuleNames = HostAndDeviceModuleNames{}
 
 var sdkVersion string
+var defaultMinSdkVersion string
 var useVersion string
 var staticDeps bool
 var jetifier bool
@@ -286,6 +287,10 @@
 	return sdkVersion
 }
 
+func (p Pom) DefaultMinSdkVersion() string {
+	return defaultMinSdkVersion
+}
+
 func (p Pom) Jetifier() bool {
 	return jetifier
 }
@@ -457,7 +462,7 @@
     min_sdk_version: "{{.MinSdkVersion}}",
     manifest: "manifests/{{.BpName}}/AndroidManifest.xml",
     {{- else if not .IsHostOnly}}
-    min_sdk_version: "24",
+    min_sdk_version: "{{.DefaultMinSdkVersion}}",
     {{- end}}
     {{- end}}
     static_libs: [
@@ -598,6 +603,8 @@
      This may be specified multiple times to declare these dependencies.
   -sdk-version <version>
      Sets sdk_version: "<version>" for all modules.
+  -default-min-sdk-version
+     The default min_sdk_version to use for a module if one cannot be mined from AndroidManifest.xml
   -use-version <version>
      If the maven directory contains multiple versions of artifacts and their pom files,
      -use-version can be used to only write Android.bp files for a specific version of those artifacts.
@@ -622,6 +629,7 @@
 	flag.Var(&hostModuleNames, "host", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is a host module")
 	flag.Var(&hostAndDeviceModuleNames, "host-and-device", "Specifies that the corresponding module (specified in the form 'module.group:module.artifact') is both a host and device module.")
 	flag.StringVar(&sdkVersion, "sdk-version", "", "What to write to sdk_version")
+	flag.StringVar(&defaultMinSdkVersion, "default-min-sdk-version", "24", "Default min_sdk_version to use, if one is not available from AndroidManifest.xml. Default: 24")
 	flag.StringVar(&useVersion, "use-version", "", "Only read artifacts of a specific version")
 	flag.BoolVar(&staticDeps, "static-deps", false, "Statically include direct dependencies")
 	flag.BoolVar(&jetifier, "jetifier", false, "Sets jetifier: true on all modules")
diff --git a/dexpreopt/dexpreopt.go b/dexpreopt/dexpreopt.go
index 4999bc7..81a63b0 100644
--- a/dexpreopt/dexpreopt.go
+++ b/dexpreopt/dexpreopt.go
@@ -259,35 +259,53 @@
 			Implicits(clcHost).
 			Text("stored_class_loader_context_arg=--stored-class-loader-context=PCL[" + strings.Join(clcTarget, ":") + "]")
 
-	} else if module.EnforceUsesLibraries {
-		// Generate command that saves target SDK version in a shell variable.
-		manifestOrApk := module.ManifestPath
-		if manifestOrApk == nil {
-			// No manifest to extract targetSdkVersion from, hope that dexjar is an APK.
+	} else {
+		// There are three categories of Java modules handled here:
+		//
+		// - Modules that have passed verify_uses_libraries check. They are AOT-compiled and
+		//   expected to be loaded on device without CLC mismatch errors.
+		//
+		// - Modules that have failed the check in relaxed mode, so it didn't cause a build error.
+		//   They are dexpreopted with "verify" filter and not AOT-compiled.
+		//   TODO(b/132357300): ensure that CLC mismatch errors are ignored with "verify" filter.
+		//
+		// - Modules that didn't run the check. They are AOT-compiled, but it's unknown if they
+		//   will have CLC mismatch errors on device (the check is disabled by default).
+		//
+		// TODO(b/132357300): enable the check by default and eliminate the last category, so that
+		// no time/space is wasted on AOT-compiling modules that will fail CLC check on device.
+
+		var manifestOrApk android.Path
+		if module.ManifestPath != nil {
+			// Ok, there is an XML manifest.
+			manifestOrApk = module.ManifestPath
+		} else if filepath.Ext(base) == ".apk" {
+			// Ok, there is is an APK with the manifest inside.
 			manifestOrApk = module.DexPath
 		}
-		rule.Command().Text(`target_sdk_version="$(`).
-			Tool(globalSoong.ManifestCheck).
-			Flag("--extract-target-sdk-version").
-			Input(manifestOrApk).
-			FlagWithInput("--aapt ", ctx.Config().HostToolPath(ctx, "aapt")).
-			Text(`)"`)
+
+		// Generate command that saves target SDK version in a shell variable.
+		if manifestOrApk == nil {
+			// There is neither an XML manifest nor APK => nowhere to extract targetSdkVersion from.
+			// Set the latest ("any") version: then construct_context will not add any compatibility
+			// libraries (if this is incorrect, there will be a CLC mismatch and dexopt on device).
+			rule.Command().Textf(`target_sdk_version=%d`, AnySdkVersion)
+		} else {
+			rule.Command().Text(`target_sdk_version="$(`).
+				Tool(globalSoong.ManifestCheck).
+				Flag("--extract-target-sdk-version").
+				Input(manifestOrApk).
+				FlagWithInput("--aapt ", globalSoong.Aapt).
+				Text(`)"`)
+		}
 
 		// Generate command that saves host and target class loader context in shell variables.
 		clc, paths := ComputeClassLoaderContext(module.ClassLoaderContexts)
 		rule.Command().
-			Text("if ! test -s ").Input(module.EnforceUsesLibrariesStatusFile).
-			Text(` ; then eval "$(`).Tool(globalSoong.ConstructContext).
+			Text(`eval "$(`).Tool(globalSoong.ConstructContext).
 			Text(` --target-sdk-version ${target_sdk_version}`).
 			Text(clc).Implicits(paths).
-			Text(`)" ; fi`)
-
-	} else {
-		// Other libraries or APKs for which the exact <uses-library> list is unknown.
-		// We assume the class loader context is empty.
-		rule.Command().
-			Text(`class_loader_context_arg=--class-loader-context=PCL[]`).
-			Text(`stored_class_loader_context_arg=""`)
+			Text(`)"`)
 	}
 
 	// Devices that do not have a product partition use a symlink from /product to /system/product.
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 9019a83..5d438ea 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -798,9 +798,9 @@
 }
 
 type bazelGenruleAttributes struct {
-	Srcs  bazel.LabelList
+	Srcs  bazel.LabelListAttribute
 	Outs  []string
-	Tools bazel.LabelList
+	Tools bazel.LabelListAttribute
 	Cmd   string
 }
 
@@ -828,15 +828,16 @@
 	}
 
 	// Bazel only has the "tools" attribute.
-	tools := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
-	tool_files := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
-	tools.Append(tool_files)
+	tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
+	tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
+	tools_prop.Append(tool_files_prop)
 
-	srcs := android.BazelLabelForModuleSrc(ctx, m.properties.Srcs)
+	tools := bazel.MakeLabelListAttribute(tools_prop)
+	srcs := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.properties.Srcs))
 
 	var allReplacements bazel.LabelList
-	allReplacements.Append(tools)
-	allReplacements.Append(srcs)
+	allReplacements.Append(tools.Value)
+	allReplacements.Append(srcs.Value)
 
 	// Replace in and out variables with $< and $@
 	var cmd string
@@ -844,9 +845,9 @@
 		cmd = strings.Replace(*m.properties.Cmd, "$(in)", "$(SRCS)", -1)
 		cmd = strings.Replace(cmd, "$(out)", "$(OUTS)", -1)
 		cmd = strings.Replace(cmd, "$(genDir)", "$(GENDIR)", -1)
-		if len(tools.Includes) > 0 {
-			cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Includes[0].Label), -1)
-			cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Includes[0].Label), -1)
+		if len(tools.Value.Includes) > 0 {
+			cmd = strings.Replace(cmd, "$(location)", fmt.Sprintf("$(location %s)", tools.Value.Includes[0].Label), -1)
+			cmd = strings.Replace(cmd, "$(locations)", fmt.Sprintf("$(locations %s)", tools.Value.Includes[0].Label), -1)
 		}
 		for _, l := range allReplacements.Includes {
 			bpLoc := fmt.Sprintf("$(location %s)", l.Bp_text)
diff --git a/java/app.go b/java/app.go
index 1b6e0e3..b849b98 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1294,7 +1294,8 @@
 	// check is not necessary, and although it is good to have, it is difficult to maintain on
 	// non-linux build platforms where dexpreopt is generally disabled (the check may fail due to
 	// various unrelated reasons, such as a failure to get manifest from an APK).
-	if dexpreopt.GetGlobalConfig(ctx).DisablePreopt {
+	global := dexpreopt.GetGlobalConfig(ctx)
+	if global.DisablePreopt || global.OnlyPreoptBootImageAndSystemServer {
 		return inputFile
 	}
 
diff --git a/java/app_test.go b/java/app_test.go
index 825ad20..a99ac62 100644
--- a/java/app_test.go
+++ b/java/app_test.go
@@ -652,7 +652,7 @@
 			} else {
 				aapt2link = m.Output("package-res.apk")
 			}
-			aapt2link = aapt2link.RelativeToTop()
+			aapt2link = aapt2link
 			aapt2Flags := aapt2link.Args["flags"]
 			if test.assetFlag != "" {
 				android.AssertStringDoesContain(t, "asset flag", aapt2Flags, test.assetFlag)
@@ -1993,14 +1993,14 @@
 		`)
 
 	// Verify baz, which depends on the overridden module foo, has the correct classpath javac arg.
-	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac").RelativeToTop()
+	javac := ctx.ModuleForTests("baz", "android_common").Rule("javac")
 	fooTurbine := "out/soong/.intermediates/foo/android_common/turbine-combined/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], fooTurbine) {
 		t.Errorf("baz classpath %v does not contain %q", javac.Args["classpath"], fooTurbine)
 	}
 
 	// Verify qux, which depends on the overriding module bar, has the correct classpath javac arg.
-	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac").RelativeToTop()
+	javac = ctx.ModuleForTests("qux", "android_common").Rule("javac")
 	barTurbine := "out/soong/.intermediates/foo/android_common_bar/turbine-combined/foo.jar"
 	if !strings.Contains(javac.Args["classpath"], barTurbine) {
 		t.Errorf("qux classpath %v does not contain %q", javac.Args["classpath"], barTurbine)
@@ -2077,7 +2077,7 @@
 		}
 
 		// Check if javac classpath has the correct jar file path. This checks instrumentation_for overrides.
-		javac := variant.Rule("javac").RelativeToTop()
+		javac := variant.Rule("javac")
 		turbine := filepath.Join("out", "soong", ".intermediates", "foo", expected.targetVariant, "turbine-combined", "foo.jar")
 		if !strings.Contains(javac.Args["classpath"], turbine) {
 			t.Errorf("classpath %q does not contain %q", javac.Args["classpath"], turbine)
@@ -2151,7 +2151,7 @@
 
 	for _, test := range testCases {
 		variant := ctx.ModuleForTests(test.moduleName, test.variantName)
-		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml").RelativeToTop()
+		params := variant.MaybeOutput("test_config_fixer/AndroidTest.xml")
 
 		if len(test.expectedFlags) > 0 {
 			if params.Rule == nil {
@@ -2647,14 +2647,14 @@
 		t.Errorf("GENRULE_NOTICE is missing from notice files, %q", noticeInputs)
 	}
 	// aapt2 flags should include -A <NOTICE dir> so that its contents are put in the APK's /assets.
-	res := foo.Output("package-res.apk").RelativeToTop()
+	res := foo.Output("package-res.apk")
 	aapt2Flags := res.Args["flags"]
 	e := "-A out/soong/.intermediates/foo/android_common/NOTICE"
 	android.AssertStringDoesContain(t, "expected.apkPath", aapt2Flags, e)
 
 	// bar has NOTICE files to process, but embed_notices is not set.
 	bar := result.ModuleForTests("bar", "android_common")
-	res = bar.Output("package-res.apk").RelativeToTop()
+	res = bar.Output("package-res.apk")
 	aapt2Flags = res.Args["flags"]
 	e = "-A out/soong/.intermediates/bar/android_common/NOTICE"
 	android.AssertStringDoesNotContain(t, "bar shouldn't have the asset dir flag for NOTICE", aapt2Flags, e)
diff --git a/java/boot_image.go b/java/boot_image.go
index a14940d..0c47976 100644
--- a/java/boot_image.go
+++ b/java/boot_image.go
@@ -20,6 +20,7 @@
 
 	"android/soong/android"
 	"android/soong/dexpreopt"
+	"github.com/google/blueprint/proptools"
 
 	"github.com/google/blueprint"
 )
@@ -27,24 +28,62 @@
 func init() {
 	RegisterBootImageBuildComponents(android.InitRegistrationContext)
 
+	// TODO(b/177892522): Remove after has been replaced by bootclasspath_fragments
 	android.RegisterSdkMemberType(&bootImageMemberType{
 		SdkMemberTypeBase: android.SdkMemberTypeBase{
 			PropertyName: "boot_images",
 			SupportsSdk:  true,
 		},
 	})
+
+	android.RegisterSdkMemberType(&bootImageMemberType{
+		SdkMemberTypeBase: android.SdkMemberTypeBase{
+			PropertyName: "bootclasspath_fragments",
+			SupportsSdk:  true,
+		},
+	})
 }
 
 func RegisterBootImageBuildComponents(ctx android.RegistrationContext) {
+	// TODO(b/177892522): Remove after has been replaced by bootclasspath_fragment
 	ctx.RegisterModuleType("boot_image", bootImageFactory)
 	ctx.RegisterModuleType("prebuilt_boot_image", prebuiltBootImageFactory)
+
+	ctx.RegisterModuleType("bootclasspath_fragment", bootImageFactory)
+	ctx.RegisterModuleType("prebuilt_bootclasspath_fragment", prebuiltBootImageFactory)
+}
+
+type bootImageContentDependencyTag struct {
+	blueprint.BaseDependencyTag
+}
+
+// Avoid having to make boot image content visible to the boot image.
+//
+// This is a temporary workaround to make it easier to migrate to boot image modules with proper
+// dependencies.
+// TODO(b/177892522): Remove this and add needed visibility.
+func (b bootImageContentDependencyTag) ExcludeFromVisibilityEnforcement() {
+}
+
+// The tag used for the dependency between the boot image module and its contents.
+var bootImageContentDepTag = bootImageContentDependencyTag{}
+
+var _ android.ExcludeFromVisibilityEnforcementTag = bootImageContentDepTag
+
+func IsbootImageContentDepTag(tag blueprint.DependencyTag) bool {
+	return tag == bootImageContentDepTag
 }
 
 type bootImageProperties struct {
 	// The name of the image this represents.
 	//
-	// Must be one of "art" or "boot".
-	Image_name string
+	// If specified then it must be one of "art" or "boot".
+	Image_name *string
+
+	// The contents of this boot image, could be either java_library, java_sdk_library, or boot_image.
+	//
+	// The order of this list matters as it is the order that is used in the bootclasspath.
+	Contents []string
 }
 
 type BootImageModule struct {
@@ -60,9 +99,59 @@
 	android.InitApexModule(m)
 	android.InitSdkAwareModule(m)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+
+	// Perform some consistency checking to ensure that the configuration is correct.
+	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+		bootImageConsistencyCheck(ctx, m)
+	})
 	return m
 }
 
+func bootImageConsistencyCheck(ctx android.EarlyModuleContext, m *BootImageModule) {
+	contents := m.properties.Contents
+	if m.properties.Image_name == nil && len(contents) == 0 {
+		ctx.ModuleErrorf(`neither of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+	}
+	if m.properties.Image_name != nil && len(contents) != 0 {
+		ctx.ModuleErrorf(`both of the "image_name" and "contents" properties have been supplied, please supply exactly one`)
+	}
+	imageName := proptools.String(m.properties.Image_name)
+	if imageName == "art" {
+		// Get the configuration for the art apex jars. Do not use getImageConfig(ctx) here as this is
+		// too early in the Soong processing for that to work.
+		global := dexpreopt.GetGlobalConfig(ctx)
+		modules := global.ArtApexJars
+
+		// Make sure that the apex specified in the configuration is consistent and is one for which
+		// this boot image is available.
+		jars := []string{}
+		commonApex := ""
+		for i := 0; i < modules.Len(); i++ {
+			apex := modules.Apex(i)
+			jar := modules.Jar(i)
+			if apex == "platform" {
+				ctx.ModuleErrorf("ArtApexJars is invalid as it requests a platform variant of %q", jar)
+				continue
+			}
+			if !m.AvailableFor(apex) {
+				ctx.ModuleErrorf("incompatible with ArtApexJars which expects this to be in apex %q but this is only in apexes %q",
+					apex, m.ApexAvailable())
+				continue
+			}
+			if commonApex == "" {
+				commonApex = apex
+			} else if commonApex != apex {
+				ctx.ModuleErrorf("ArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex %q and %q",
+					commonApex, apex)
+			}
+			jars = append(jars, jar)
+		}
+
+		// Store the jars in the Contents property so that they can be used to add dependencies.
+		m.properties.Contents = jars
+	}
+}
+
 var BootImageInfoProvider = blueprint.NewProvider(BootImageInfo{})
 
 type BootImageInfo struct {
@@ -96,6 +185,10 @@
 
 func (b *BootImageModule) DepIsInSameApex(ctx android.BaseModuleContext, dep android.Module) bool {
 	tag := ctx.OtherModuleDependencyTag(dep)
+	if tag == bootImageContentDepTag {
+		// Boot image contents are automatically added to apex.
+		return true
+	}
 	if android.IsMetaDependencyTag(tag) {
 		// Cross-cutting metadata dependencies are metadata.
 		return false
@@ -108,6 +201,8 @@
 }
 
 func (b *BootImageModule) DepsMutator(ctx android.BottomUpMutatorContext) {
+	ctx.AddDependency(ctx.Module(), bootImageContentDepTag, b.properties.Contents...)
+
 	if SkipDexpreoptBootJars(ctx) {
 		return
 	}
@@ -127,14 +222,8 @@
 	// GenerateSingletonBuildActions method as it cannot create it for itself.
 	dexpreopt.GetGlobalSoongConfig(ctx)
 
-	// Get a map of the image configs that are supported.
-	imageConfigs := genBootImageConfigs(ctx)
-
-	// Retrieve the config for this image.
-	imageName := b.properties.Image_name
-	imageConfig := imageConfigs[imageName]
+	imageConfig := b.getImageConfig(ctx)
 	if imageConfig == nil {
-		ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
 		return
 	}
 
@@ -145,6 +234,25 @@
 	ctx.SetProvider(BootImageInfoProvider, info)
 }
 
+func (b *BootImageModule) getImageConfig(ctx android.EarlyModuleContext) *bootImageConfig {
+	// Get a map of the image configs that are supported.
+	imageConfigs := genBootImageConfigs(ctx)
+
+	// Retrieve the config for this image.
+	imageNamePtr := b.properties.Image_name
+	if imageNamePtr == nil {
+		return nil
+	}
+
+	imageName := *imageNamePtr
+	imageConfig := imageConfigs[imageName]
+	if imageConfig == nil {
+		ctx.PropertyErrorf("image_name", "Unknown image name %q, expected one of %s", imageName, strings.Join(android.SortedStringKeys(imageConfigs), ", "))
+		return nil
+	}
+	return imageConfig
+}
+
 type bootImageMemberType struct {
 	android.SdkMemberTypeBase
 }
@@ -159,7 +267,11 @@
 }
 
 func (b *bootImageMemberType) AddPrebuiltModule(ctx android.SdkMemberContext, member android.SdkMember) android.BpModule {
-	return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_boot_image")
+	if b.PropertyName == "boot_images" {
+		return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_boot_image")
+	} else {
+		return ctx.SnapshotBuilder().AddPrebuiltModule(member, "prebuilt_bootclasspath_fragment")
+	}
 }
 
 func (b *bootImageMemberType) CreateVariantPropertiesStruct() android.SdkMemberProperties {
@@ -169,7 +281,7 @@
 type bootImageSdkMemberProperties struct {
 	android.SdkMemberPropertiesBase
 
-	Image_name string
+	Image_name *string
 }
 
 func (b *bootImageSdkMemberProperties) PopulateFromVariant(ctx android.SdkMemberContext, variant android.Module) {
@@ -179,8 +291,8 @@
 }
 
 func (b *bootImageSdkMemberProperties) AddToPropertySet(ctx android.SdkMemberContext, propertySet android.BpPropertySet) {
-	if b.Image_name != "" {
-		propertySet.AddProperty("image_name", b.Image_name)
+	if b.Image_name != nil {
+		propertySet.AddProperty("image_name", *b.Image_name)
 	}
 }
 
@@ -213,5 +325,10 @@
 	android.InitApexModule(m)
 	android.InitSdkAwareModule(m)
 	android.InitAndroidArchModule(m, android.HostAndDeviceSupported, android.MultilibCommon)
+
+	// Perform some consistency checking to ensure that the configuration is correct.
+	android.AddLoadHook(m, func(ctx android.LoadHookContext) {
+		bootImageConsistencyCheck(ctx, &m.BootImageModule)
+	})
 	return m
 }
diff --git a/java/boot_image_test.go b/java/boot_image_test.go
index 65e590d..e1866de 100644
--- a/java/boot_image_test.go
+++ b/java/boot_image_test.go
@@ -16,25 +16,112 @@
 
 import (
 	"testing"
+
+	"android/soong/android"
+	"android/soong/dexpreopt"
 )
 
 // Contains some simple tests for boot_image logic, additional tests can be found in
 // apex/boot_image_test.go as the ART boot image requires modules from the ART apex.
 
+var prepareForTestWithBootImage = android.GroupFixturePreparers(
+	PrepareForTestWithJavaDefaultModules,
+	dexpreopt.PrepareForTestByEnablingDexpreopt,
+)
+
 func TestUnknownBootImage(t *testing.T) {
-	testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", `
-		boot_image {
-			name: "unknown-boot-image",
-			image_name: "unknown",
-		}
-`)
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "unknown-boot-image",
+				image_name: "unknown",
+			}
+		`)
+}
+
+func TestUnknownBootclasspathFragmentImageName(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+		RunTestWithBp(t, `
+			bootclasspath_fragment {
+				name: "unknown-boot-image",
+				image_name: "unknown",
+			}
+		`)
 }
 
 func TestUnknownPrebuiltBootImage(t *testing.T) {
-	testJavaError(t, "image_name: Unknown image name \\\"unknown\\\", expected one of art, boot", `
-		prebuilt_boot_image {
-			name: "unknown-boot-image",
-			image_name: "unknown",
-		}
-`)
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qimage_name: Unknown image name "unknown", expected one of art, boot\E`)).
+		RunTestWithBp(t, `
+			prebuilt_boot_image {
+				name: "unknown-boot-image",
+				image_name: "unknown",
+			}
+		`)
+}
+
+func TestBootImageInconsistentArtConfiguration_Platform(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		dexpreopt.FixtureSetArtBootJars("platform:foo", "apex:bar"),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\QArtApexJars is invalid as it requests a platform variant of "foo"\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+				image_name: "art",
+				apex_available: [
+					"apex",
+				],
+			}
+		`)
+}
+
+func TestBootImageInconsistentArtConfiguration_ApexMixture(t *testing.T) {
+	android.GroupFixturePreparers(
+		prepareForTestWithBootImage,
+		dexpreopt.FixtureSetArtBootJars("apex1:foo", "apex2:bar"),
+	).
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\QArtApexJars configuration is inconsistent, expected all jars to be in the same apex but it specifies apex "apex1" and "apex2"\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+				image_name: "art",
+				apex_available: [
+					"apex1",
+					"apex2",
+				],
+			}
+		`)
+}
+
+func TestBootImageWithoutImageNameOrContents(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qneither of the "image_name" and "contents" properties\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+			}
+		`)
+}
+
+func TestBootImageWithImageNameAndContents(t *testing.T) {
+	prepareForTestWithBootImage.
+		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
+			`\Qboth of the "image_name" and "contents" properties\E`)).
+		RunTestWithBp(t, `
+			boot_image {
+				name: "boot-image",
+				image_name: "boot",
+				contents: ["other"],
+			}
+		`)
 }
diff --git a/java/droiddoc_test.go b/java/droiddoc_test.go
index 2b324ae..8d1f591 100644
--- a/java/droiddoc_test.go
+++ b/java/droiddoc_test.go
@@ -80,7 +80,7 @@
 
 	barStubsOutput := barStubsOutputs[0]
 	barDoc := ctx.ModuleForTests("bar-doc", "android_common")
-	javaDoc := barDoc.Rule("javadoc").RelativeToTop()
+	javaDoc := barDoc.Rule("javadoc")
 	if g, w := android.PathsRelativeToTop(javaDoc.Implicits), android.PathRelativeToTop(barStubsOutput); !inList(w, g) {
 		t.Errorf("implicits of bar-doc must contain %q, but was %q.", w, g)
 	}
diff --git a/java/hiddenapi_singleton_test.go b/java/hiddenapi_singleton_test.go
index d6c6a2d..dc4e8aa 100644
--- a/java/hiddenapi_singleton_test.go
+++ b/java/hiddenapi_singleton_test.go
@@ -50,7 +50,7 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
@@ -145,7 +145,7 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	want := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, want)
 }
@@ -169,7 +169,7 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	fromSourceJarArg := "--boot-dex=out/soong/.intermediates/foo/android_common/aligned/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, fromSourceJarArg)
 
@@ -196,7 +196,7 @@
 	`)
 
 	hiddenAPI := result.SingletonForTests("hiddenapi")
-	hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+	hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 	prebuiltJarArg := "--boot-dex=out/soong/.intermediates/prebuilt_foo/android_common/dex/foo.jar"
 	android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, prebuiltJarArg)
 
@@ -244,7 +244,7 @@
 			).RunTest(t)
 
 			hiddenAPI := result.SingletonForTests("hiddenapi")
-			hiddenapiRule := hiddenAPI.Rule("hiddenapi").RelativeToTop()
+			hiddenapiRule := hiddenAPI.Rule("hiddenapi")
 			wantPublicStubs := "--public-stub-classpath=" + generateSdkDexPath(tc.publicStub, tc.unbundledBuild)
 			android.AssertStringDoesContain(t, "hiddenapi command", hiddenapiRule.RuleParams.Command, wantPublicStubs)
 
@@ -307,7 +307,7 @@
 	cpRule := hiddenAPI.Rule("Cp")
 	actualCpInput := cpRule.BuildParams.Input
 	actualCpOutput := cpRule.BuildParams.Output
-	encodeDexRule := foo.Rule("hiddenAPIEncodeDex").RelativeToTop()
+	encodeDexRule := foo.Rule("hiddenAPIEncodeDex")
 	actualFlagsCsv := encodeDexRule.BuildParams.Args["flagsCsv"]
 
 	android.AssertPathRelativeToTopEquals(t, "hiddenapi cp rule input", expectedCpInput, actualCpInput)
diff --git a/java/java_test.go b/java/java_test.go
index 2ade0fe..d1d26ba 100644
--- a/java/java_test.go
+++ b/java/java_test.go
@@ -250,7 +250,7 @@
 		}
 	`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac").RelativeToTop()
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
@@ -976,7 +976,7 @@
 		}
 		`)
 
-	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac").RelativeToTop()
+	javac := ctx.ModuleForTests("foo", "android_common").Rule("javac")
 	combineJar := ctx.ModuleForTests("foo", "android_common").Description("for javac")
 
 	if len(javac.Inputs) != 1 || javac.Inputs[0].String() != "a.java" {
@@ -1336,11 +1336,11 @@
 		}
 		`)
 
-	fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine").RelativeToTop()
-	barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine").RelativeToTop()
-	barJavac := result.ModuleForTests("bar", "android_common").Rule("javac").RelativeToTop()
-	barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine").RelativeToTop()
-	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac").RelativeToTop()
+	fooTurbine := result.ModuleForTests("foo", "android_common").Rule("turbine")
+	barTurbine := result.ModuleForTests("bar", "android_common").Rule("turbine")
+	barJavac := result.ModuleForTests("bar", "android_common").Rule("javac")
+	barTurbineCombined := result.ModuleForTests("bar", "android_common").Description("for turbine")
+	bazJavac := result.ModuleForTests("baz", "android_common").Rule("javac")
 
 	android.AssertPathsRelativeToTopEquals(t, "foo inputs", []string{"a.java"}, fooTurbine.Inputs)
 
@@ -1363,7 +1363,7 @@
 
 	barHeaderJar := filepath.Join("out", "soong", ".intermediates", "bar", "android_common", "turbine-combined", "bar.jar")
 	for i := 0; i < 3; i++ {
-		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i)).RelativeToTop()
+		barJavac := ctx.ModuleForTests("bar", "android_common").Description("javac" + strconv.Itoa(i))
 		if !strings.Contains(barJavac.Args["classpath"], barHeaderJar) {
 			t.Errorf("bar javac classpath %v does not contain %q", barJavac.Args["classpath"], barHeaderJar)
 		}
@@ -1670,7 +1670,7 @@
 
 	// The bar library should depend on the stubs jar.
 	barLibrary := result.ModuleForTests("bar", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs\.jar$`, barLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
@@ -1973,7 +1973,7 @@
 		`)
 	// The baz library should depend on the system stubs jar.
 	bazLibrary := result.ModuleForTests("baz", "android_common").Rule("javac")
-	if expected, actual := `^-classpath .*:/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
+	if expected, actual := `^-classpath .*:out/soong/[^:]*/turbine-combined/foo\.stubs.system\.jar$`, bazLibrary.Args["classpath"]; !regexp.MustCompile(expected).MatchString(actual) {
 		t.Errorf("expected %q, found %#q", expected, actual)
 	}
 }
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 03e82c2..c3d13ae 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -221,12 +221,40 @@
 	metadata android.Path
 }
 
+// isModulePreferredByCompatConfig checks to see whether the module is preferred for use by
+// platform compat config.
+func isModulePreferredByCompatConfig(module android.Module) bool {
+	// A versioned prebuilt_platform_compat_config, i.e. foo-platform-compat-config@current should be
+	// ignored.
+	if s, ok := module.(android.SdkAware); ok {
+		if !s.ContainingSdk().Unversioned() {
+			return false
+		}
+	}
+
+	// A prebuilt module should only be used when it is preferred.
+	if pi, ok := module.(android.PrebuiltInterface); ok {
+		if p := pi.Prebuilt(); p != nil {
+			return p.UsePrebuilt()
+		}
+	}
+
+	// Otherwise, a module should only be used if it has not been replaced by a prebuilt.
+	return !module.IsReplacedByPrebuilt()
+}
+
 func (p *platformCompatConfigSingleton) GenerateBuildActions(ctx android.SingletonContext) {
 
 	var compatConfigMetadata android.Paths
 
 	ctx.VisitAllModules(func(module android.Module) {
+		if !module.Enabled() {
+			return
+		}
 		if c, ok := module.(platformCompatConfigMetadataProvider); ok {
+			if !isModulePreferredByCompatConfig(module) {
+				return
+			}
 			metadata := c.compatConfigMetadata()
 			compatConfigMetadata = append(compatConfigMetadata, metadata)
 		}
diff --git a/java/rro_test.go b/java/rro_test.go
index 0a10d93..bad60bc 100644
--- a/java/rro_test.go
+++ b/java/rro_test.go
@@ -68,7 +68,7 @@
 	m := result.ModuleForTests("foo", "android_common")
 
 	// Check AAPT2 link flags.
-	aapt2Flags := m.Output("package-res.apk").RelativeToTop().Args["flags"]
+	aapt2Flags := m.Output("package-res.apk").Args["flags"]
 	expectedFlags := []string{"--keep-raw-values", "--no-resource-deduping", "--no-resource-removal"}
 	absentFlags := android.RemoveListFromList(expectedFlags, strings.Split(aapt2Flags, " "))
 	if len(absentFlags) > 0 {
diff --git a/java/sdk_test.go b/java/sdk_test.go
index 028c4fe..e1ec41b 100644
--- a/java/sdk_test.go
+++ b/java/sdk_test.go
@@ -313,10 +313,10 @@
 
 			checkClasspath := func(t *testing.T, result *android.TestResult, isJava8 bool) {
 				foo := result.ModuleForTests("foo", variant)
-				javac := foo.Rule("javac").RelativeToTop()
+				javac := foo.Rule("javac")
 				var deps []string
 
-				aidl := foo.MaybeRule("aidl").RelativeToTop()
+				aidl := foo.MaybeRule("aidl")
 				if aidl.Rule != nil {
 					deps = append(deps, android.PathRelativeToTop(aidl.Output))
 				}
@@ -376,7 +376,7 @@
 				checkClasspath(t, result, true /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl").RelativeToTop()
+					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
@@ -389,7 +389,7 @@
 				checkClasspath(t, result, false /* isJava8 */)
 
 				if testcase.host != android.Host {
-					aidl := result.ModuleForTests("foo", variant).Rule("aidl").RelativeToTop()
+					aidl := result.ModuleForTests("foo", variant).Rule("aidl")
 
 					android.AssertStringDoesContain(t, "aidl command", aidl.RuleParams.Command, testcase.aidl+" -I.")
 				}
diff --git a/java/system_modules_test.go b/java/system_modules_test.go
index 7d8935a..120bbf7 100644
--- a/java/system_modules_test.go
+++ b/java/system_modules_test.go
@@ -20,12 +20,12 @@
 	"android/soong/android"
 )
 
-func getModuleHeaderJarsAsNormalizedPaths(result *android.TestResult, moduleNames ...string) []string {
+func getModuleHeaderJarsAsRelativeToTopPaths(result *android.TestResult, moduleNames ...string) []string {
 	paths := []string{}
 	for _, moduleName := range moduleNames {
 		module := result.Module(moduleName, "android_common")
 		info := result.ModuleProvider(module, JavaInfoProvider).(JavaInfo)
-		paths = append(paths, result.NormalizePathsForTesting(info.HeaderJars)...)
+		paths = append(paths, info.HeaderJars.RelativeToTop().Strings()...)
 	}
 	return paths
 }
@@ -57,8 +57,8 @@
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
-	expectedSourcePaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
-	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+	expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
 }
 
 var addPrebuiltSystemModules = android.FixtureAddTextFile("prebuilts/Android.bp", `
@@ -84,8 +84,8 @@
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
-	expectedPrebuiltPaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
-	android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+	expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+	android.AssertArrayString(t, "renamed prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
 }
 
 func TestJavaSystemModulesMixSourceAndPrebuilt(t *testing.T) {
@@ -99,14 +99,14 @@
 	sourceInputs := sourceSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the source input modules.
-	expectedSourcePaths := getModuleHeaderJarsAsNormalizedPaths(result, "system-module1", "system-module2")
-	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, result.NormalizePathsForTesting(sourceInputs))
+	expectedSourcePaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "system-module1", "system-module2")
+	android.AssertArrayString(t, "source system modules inputs", expectedSourcePaths, sourceInputs.RelativeToTop().Strings())
 
 	// check the existence of the renamed prebuilt module
 	prebuiltSystemModules := result.ModuleForTests("prebuilt_system-modules", "android_common")
 	prebuiltInputs := prebuiltSystemModules.Rule("jarsTosystemModules").Inputs
 
 	// The expected paths are the header jars from the renamed prebuilt input modules.
-	expectedPrebuiltPaths := getModuleHeaderJarsAsNormalizedPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
-	android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, result.NormalizePathsForTesting(prebuiltInputs))
+	expectedPrebuiltPaths := getModuleHeaderJarsAsRelativeToTopPaths(result, "prebuilt_system-module1", "prebuilt_system-module2")
+	android.AssertArrayString(t, "prebuilt system modules inputs", expectedPrebuiltPaths, prebuiltInputs.RelativeToTop().Strings())
 }
diff --git a/kernel/prebuilt_kernel_modules.go b/kernel/prebuilt_kernel_modules.go
index 14ac021..5bcca04 100644
--- a/kernel/prebuilt_kernel_modules.go
+++ b/kernel/prebuilt_kernel_modules.go
@@ -27,8 +27,12 @@
 )
 
 func init() {
-	android.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
 	pctx.Import("android/soong/cc/config")
+	registerKernelBuildComponents(android.InitRegistrationContext)
+}
+
+func registerKernelBuildComponents(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
 }
 
 type prebuiltKernelModules struct {
diff --git a/kernel/prebuilt_kernel_modules_test.go b/kernel/prebuilt_kernel_modules_test.go
index 433548b..90b9886 100644
--- a/kernel/prebuilt_kernel_modules_test.go
+++ b/kernel/prebuilt_kernel_modules_test.go
@@ -15,73 +15,29 @@
 package kernel
 
 import (
-	"io/ioutil"
 	"os"
-	"reflect"
-	"strings"
 	"testing"
 
 	"android/soong/android"
 	"android/soong/cc"
 )
 
-func testKernelModules(t *testing.T, bp string, fs map[string][]byte) (*android.TestContext, android.Config) {
-	bp = bp + `
-		cc_binary_host {
-			name: "depmod",
-			srcs: ["depmod.cpp"],
-			stl: "none",
-			static_executable: true,
-			system_shared_libs: [],
-		}
-	`
-	bp = bp + cc.GatherRequiredDepsForTest(android.Android)
-
-	fs["depmod.cpp"] = nil
-	cc.GatherRequiredFilesForTest(fs)
-
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
-
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("prebuilt_kernel_modules", prebuiltKernelModulesFactory)
-	ctx.PreArchMutators(android.RegisterDefaultsPreArchMutators)
-	cc.RegisterRequiredBuildComponentsForTest(ctx)
-
-	ctx.Register()
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	return ctx, config
-}
-
-func ensureListContains(t *testing.T, result []string, expected string) {
-	t.Helper()
-	if !android.InList(expected, result) {
-		t.Errorf("%q is not found in %v", expected, result)
-	}
-}
-
-func ensureContains(t *testing.T, result string, expected string) {
-	t.Helper()
-	if !strings.Contains(result, expected) {
-		t.Errorf("%q is not found in %q", expected, result)
-	}
-}
-
 func TestKernelModulesFilelist(t *testing.T) {
-	ctx, _ := testKernelModules(t, `
+	ctx := android.GroupFixturePreparers(
+		cc.PrepareForTestWithCcDefaultModules,
+		android.FixtureRegisterWithContext(registerKernelBuildComponents),
+		android.MockFS{
+			"depmod.cpp": nil,
+			"mod1.ko":    nil,
+			"mod2.ko":    nil,
+		}.AddToFixture(),
+	).RunTestWithBp(t, `
 		prebuilt_kernel_modules {
 			name: "foo",
 			srcs: ["*.ko"],
 			kernel_version: "5.10",
 		}
-	`,
-		map[string][]byte{
-			"mod1.ko": nil,
-			"mod2.ko": nil,
-		})
+	`)
 
 	expected := []string{
 		"lib/modules/5.10/mod1.ko",
@@ -98,32 +54,9 @@
 	}
 	actual = android.SortedUniqueStrings(actual)
 	expected = android.SortedUniqueStrings(expected)
-	if !reflect.DeepEqual(actual, expected) {
-		t.Errorf("\ngot: %v\nexpected: %v\n", actual, expected)
-	}
-}
-
-var buildDir string
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_kernel_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
+	android.AssertDeepEquals(t, "foo packaging specs", expected, actual)
 }
 
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
+	os.Exit(m.Run())
 }
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index ff548e5..da80a47 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -27,7 +27,11 @@
 
 func init() {
 	pctx.HostBinToolVariable("conv_linker_config", "conv_linker_config")
-	android.RegisterModuleType("linker_config", linkerConfigFactory)
+	registerLinkerConfigBuildComponent(android.InitRegistrationContext)
+}
+
+func registerLinkerConfigBuildComponent(ctx android.RegistrationContext) {
+	ctx.RegisterModuleType("linker_config", linkerConfigFactory)
 }
 
 type linkerConfigProperties struct {
diff --git a/linkerconfig/linkerconfig_test.go b/linkerconfig/linkerconfig_test.go
index 8eed4b5..939e4bb 100644
--- a/linkerconfig/linkerconfig_test.go
+++ b/linkerconfig/linkerconfig_test.go
@@ -15,65 +15,29 @@
 package linkerconfig
 
 import (
-	"android/soong/android"
-	"io/ioutil"
 	"os"
 	"reflect"
 	"testing"
+
+	"android/soong/android"
 )
 
-var buildDir string
-
-func setUp() {
-	var err error
-	buildDir, err = ioutil.TempDir("", "soong_etc_test")
-	if err != nil {
-		panic(err)
-	}
-}
-
-func tearDown() {
-	os.RemoveAll(buildDir)
-}
-
 func TestMain(m *testing.M) {
-	run := func() int {
-		setUp()
-		defer tearDown()
-
-		return m.Run()
-	}
-
-	os.Exit(run())
+	os.Exit(m.Run())
 }
 
-func testContext(t *testing.T, bp string) *android.TestContext {
-	t.Helper()
-
-	fs := map[string][]byte{
-		"linker.config.json": nil,
-	}
-
-	config := android.TestArchConfig(buildDir, nil, bp, fs)
-
-	ctx := android.NewTestArchContext(config)
-	ctx.RegisterModuleType("linker_config", linkerConfigFactory)
-	ctx.Register()
-
-	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
-	android.FailIfErrored(t, errs)
-	_, errs = ctx.PrepareBuildActions(config)
-	android.FailIfErrored(t, errs)
-
-	return ctx
-}
+var prepareForLinkerConfigTest = android.GroupFixturePreparers(
+	android.PrepareForTestWithAndroidBuildComponents,
+	android.FixtureRegisterWithContext(registerLinkerConfigBuildComponent),
+	android.FixtureAddFile("linker.config.json", nil),
+)
 
 func TestBaseLinkerConfig(t *testing.T) {
-	ctx := testContext(t, `
-	linker_config {
-		name: "linker-config-base",
-		src: "linker.config.json",
-	}
+	result := prepareForLinkerConfigTest.RunTestWithBp(t, `
+		linker_config {
+			name: "linker-config-base",
+			src: "linker.config.json",
+		}
 	`)
 
 	expected := map[string][]string{
@@ -82,13 +46,13 @@
 		"LOCAL_INSTALLED_MODULE_STEM": {"linker.config.pb"},
 	}
 
-	p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+	p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
 
 	if p.outputFilePath.Base() != "linker.config.pb" {
 		t.Errorf("expected linker.config.pb, got %q", p.outputFilePath.Base())
 	}
 
-	entries := android.AndroidMkEntriesForTest(t, ctx, p)[0]
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
 	for k, expectedValue := range expected {
 		if value, ok := entries.EntryMap[k]; ok {
 			if !reflect.DeepEqual(value, expectedValue) {
@@ -105,18 +69,18 @@
 }
 
 func TestUninstallableLinkerConfig(t *testing.T) {
-	ctx := testContext(t, `
-	linker_config {
-		name: "linker-config-base",
-		src: "linker.config.json",
-		installable: false,
-	}
+	result := prepareForLinkerConfigTest.RunTestWithBp(t, `
+		linker_config {
+			name: "linker-config-base",
+			src: "linker.config.json",
+			installable: false,
+		}
 	`)
 
 	expected := []string{"true"}
 
-	p := ctx.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
-	entries := android.AndroidMkEntriesForTest(t, ctx, p)[0]
+	p := result.ModuleForTests("linker-config-base", "android_arm64_armv8-a").Module().(*linkerConfig)
+	entries := android.AndroidMkEntriesForTest(t, result.TestContext, p)[0]
 	if value, ok := entries.EntryMap["LOCAL_UNINSTALLABLE_MODULE"]; ok {
 		if !reflect.DeepEqual(value, expected) {
 			t.Errorf("LOCAL_UNINSTALLABLE_MODULE is expected to be true but %s", value)
diff --git a/python/binary.go b/python/binary.go
index 5b0f080..e955492 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -36,8 +36,8 @@
 
 type bazelPythonBinaryAttributes struct {
 	Main           string
-	Srcs           bazel.LabelList
-	Data           bazel.LabelList
+	Srcs           bazel.LabelListAttribute
+	Data           bazel.LabelListAttribute
 	Python_version string
 }
 
@@ -97,10 +97,13 @@
 		// do nothing, since python_version defaults to PY3.
 	}
 
+	srcs := android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs)
+	data := android.BazelLabelForModuleSrc(ctx, m.properties.Data)
+
 	attrs := &bazelPythonBinaryAttributes{
 		Main:           main,
-		Srcs:           android.BazelLabelForModuleSrcExcludes(ctx, m.properties.Srcs, m.properties.Exclude_srcs),
-		Data:           android.BazelLabelForModuleSrc(ctx, m.properties.Data),
+		Srcs:           bazel.MakeLabelListAttribute(srcs),
+		Data:           bazel.MakeLabelListAttribute(data),
 		Python_version: python_version,
 	}
 
diff --git a/rust/compiler.go b/rust/compiler.go
index 200af90..41b7371 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -97,13 +97,25 @@
 	// list of C shared library dependencies
 	Shared_libs []string `android:"arch_variant"`
 
-	// list of C static library dependencies. Note, static libraries prefixed by "lib" will be passed to rustc
-	// along with "-lstatic=<name>". This will bundle the static library into rlib/static libraries so dependents do
-	// not need to also declare the static library as a dependency. Static libraries which are not prefixed by "lib"
-	// cannot be passed to rustc with this flag and will not be bundled into rlib/static libraries, and thus must
-	// be redeclared in dependents.
+	// list of C static library dependencies. These dependencies do not normally propagate to dependents
+	// and may need to be redeclared. See whole_static_libs for bundling static dependencies into a library.
 	Static_libs []string `android:"arch_variant"`
 
+	// Similar to static_libs, but will bundle the static library dependency into a library. This is helpful
+	// to avoid having to redeclare the dependency for dependents of this library, but in some cases may also
+	// result in bloat if multiple dependencies all include the same static library whole.
+	//
+	// The common use case for this is when the static library is unlikely to be a dependency of other modules to avoid
+	// having to redeclare the static library dependency for every dependent module.
+	// If you are not sure what to, for rust_library modules most static dependencies should go in static_libraries,
+	// and for rust_ffi modules most static dependencies should go into whole_static_libraries.
+	//
+	// For rust_ffi static variants, these libraries will be included in the resulting static library archive.
+	//
+	// For rust_library rlib variants, these libraries will be bundled into the resulting rlib library. This will
+	// include all of the static libraries symbols in any dylibs or binaries which use this rlib as well.
+	Whole_static_libs []string `android:"arch_variant"`
+
 	// crate name, required for modules which produce Rust libraries: rust_library, rust_ffi and SourceProvider
 	// modules which create library variants (rust_bindgen). This must be the expected extern crate name used in
 	// source, and is required to conform to an enforced format matching library output files (if the output file is
@@ -266,6 +278,7 @@
 	deps.Rustlibs = append(deps.Rustlibs, compiler.Properties.Rustlibs...)
 	deps.ProcMacros = append(deps.ProcMacros, compiler.Properties.Proc_macros...)
 	deps.StaticLibs = append(deps.StaticLibs, compiler.Properties.Static_libs...)
+	deps.WholeStaticLibs = append(deps.WholeStaticLibs, compiler.Properties.Whole_static_libs...)
 	deps.SharedLibs = append(deps.SharedLibs, compiler.Properties.Shared_libs...)
 
 	if !Bool(compiler.Properties.No_stdlibs) {
diff --git a/rust/project_json.go b/rust/project_json.go
index 8d3d250..c28bc7b 100644
--- a/rust/project_json.go
+++ b/rust/project_json.go
@@ -49,7 +49,7 @@
 	RootModule  string            `json:"root_module"`
 	Edition     string            `json:"edition,omitempty"`
 	Deps        []rustProjectDep  `json:"deps"`
-	Cfgs        []string          `json:"cfgs"`
+	Cfg         []string          `json:"cfg"`
 	Env         map[string]string `json:"env"`
 }
 
@@ -230,7 +230,7 @@
 		RootModule:  rootModule,
 		Edition:     comp.edition(),
 		Deps:        make([]rustProjectDep, 0),
-		Cfgs:        make([]string, 0),
+		Cfg:         make([]string, 0),
 		Env:         make(map[string]string),
 	}
 
@@ -238,6 +238,10 @@
 		crate.Env["OUT_DIR"] = comp.CargoOutDir().String()
 	}
 
+	for _, feature := range comp.Properties.Features {
+		crate.Cfg = append(crate.Cfg, "feature=\""+feature+"\"")
+	}
+
 	deps := make(map[string]int)
 	singleton.mergeDependencies(ctx, rModule, &crate, deps)
 
diff --git a/rust/project_json_test.go b/rust/project_json_test.go
index 8f64f56..7af4635 100644
--- a/rust/project_json_test.go
+++ b/rust/project_json_test.go
@@ -18,6 +18,7 @@
 	"encoding/json"
 	"io/ioutil"
 	"path/filepath"
+	"sort"
 	"strings"
 	"testing"
 
@@ -115,6 +116,41 @@
 	validateJsonCrates(t, jsonContent)
 }
 
+func TestProjectJsonFeature(t *testing.T) {
+	bp := `
+	rust_library {
+		name: "liba",
+		srcs: ["a/src/lib.rs"],
+		crate_name: "a",
+		features: ["f1", "f2"]
+	}
+	`
+	jsonContent := testProjectJson(t, bp)
+	crates := validateJsonCrates(t, jsonContent)
+	for _, c := range crates {
+		crate := validateCrate(t, c)
+		cfgs, ok := crate["cfg"].([]interface{})
+		if !ok {
+			t.Fatalf("Unexpected type for cfgs: %v", crate)
+		}
+		expectedCfgs := []string{"feature=\"f1\"", "feature=\"f2\""}
+		foundCfgs := []string{}
+		for _, cfg := range cfgs {
+			cfg, ok := cfg.(string)
+			if !ok {
+				t.Fatalf("Unexpected type for cfg: %v", cfg)
+			}
+			foundCfgs = append(foundCfgs, cfg)
+		}
+		sort.Strings(foundCfgs)
+		for i, foundCfg := range foundCfgs {
+			if foundCfg != expectedCfgs[i] {
+				t.Errorf("Incorrect features: got %v; want %v", foundCfg, expectedCfgs[i])
+			}
+		}
+	}
+}
+
 func TestProjectJsonBinary(t *testing.T) {
 	bp := `
 	rust_binary {
diff --git a/rust/rust.go b/rust/rust.go
index 8ebdb72..f0d0e36 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -256,6 +256,10 @@
 	return ""
 }
 
+func (mod *Module) MinSdkVersion() string {
+	return ""
+}
+
 func (mod *Module) AlwaysSdk() bool {
 	return false
 }
@@ -269,14 +273,15 @@
 }
 
 type Deps struct {
-	Dylibs     []string
-	Rlibs      []string
-	Rustlibs   []string
-	Stdlibs    []string
-	ProcMacros []string
-	SharedLibs []string
-	StaticLibs []string
-	HeaderLibs []string
+	Dylibs          []string
+	Rlibs           []string
+	Rustlibs        []string
+	Stdlibs         []string
+	ProcMacros      []string
+	SharedLibs      []string
+	StaticLibs      []string
+	WholeStaticLibs []string
+	HeaderLibs      []string
 
 	CrtBegin, CrtEnd string
 }
@@ -751,7 +756,7 @@
 	deps.ProcMacros = android.LastUniqueStrings(deps.ProcMacros)
 	deps.SharedLibs = android.LastUniqueStrings(deps.SharedLibs)
 	deps.StaticLibs = android.LastUniqueStrings(deps.StaticLibs)
-
+	deps.WholeStaticLibs = android.LastUniqueStrings(deps.WholeStaticLibs)
 	return deps
 
 }
@@ -911,16 +916,13 @@
 			exportDep := false
 			switch {
 			case cc.IsStaticDepTag(depTag):
-				// Only pass -lstatic for rlibs as it results in dylib bloat.
-				if lib, ok := ctx.Module().(*Module).compiler.(libraryInterface); ok && lib.rlib() {
-					// Link cc static libraries using "-lstatic" so rustc can reason about how to handle these
-					// (for example, bundling them into rlibs).
-					//
-					// rustc does not support linking libraries with the "-l" flag unless they are prefixed by "lib".
-					// If we need to link a library that isn't prefixed by "lib", we'll just link to it directly through
-					// linkObjects; such a library may need to be redeclared by static dependents.
+				if cc.IsWholeStaticLib(depTag) {
+					// rustc will bundle static libraries when they're passed with "-lstatic=<lib>". This will fail
+					// if the library is not prefixed by "lib".
 					if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
 						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+					} else {
+						ctx.ModuleErrorf("'%q' cannot be listed as a whole_static_library in Rust modules unless the output is prefixed by 'lib'", depName, ctx.ModuleName())
 					}
 				}
 
@@ -1099,7 +1101,10 @@
 		cc.SharedDepTag(), deps.SharedLibs...)
 	actx.AddVariationDependencies(append(commonDepVariations,
 		blueprint.Variation{Mutator: "link", Variation: "static"}),
-		cc.StaticDepTag(), deps.StaticLibs...)
+		cc.StaticDepTag(false), deps.StaticLibs...)
+	actx.AddVariationDependencies(append(commonDepVariations,
+		blueprint.Variation{Mutator: "link", Variation: "static"}),
+		cc.StaticDepTag(true), deps.WholeStaticLibs...)
 
 	actx.AddVariationDependencies(nil, cc.HeaderDepTag(), deps.HeaderLibs...)
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index bed28ec..418bd93 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -149,6 +149,11 @@
 			srcs: ["foo.rs"],
 			crate_name: "static",
 		}
+		rust_ffi_host_static {
+			name: "libwholestatic",
+			srcs: ["foo.rs"],
+			crate_name: "wholestatic",
+		}
 		rust_ffi_host_shared {
 			name: "libshared",
 			srcs: ["foo.rs"],
@@ -164,6 +169,7 @@
 			srcs: ["foo.rs"],
 			crate_name: "rlib",
 			static_libs: ["libstatic"],
+			whole_static_libs: ["libwholestatic"],
 		}
 		rust_proc_macro {
 			name: "libpm",
@@ -204,8 +210,8 @@
 		t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
 	}
 
-	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=static") {
-		t.Errorf("-lstatic flag not being passed to rustc for static library")
+	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") {
+		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
 	}
 
 }
diff --git a/scripts/OWNERS b/scripts/OWNERS
index 8198083..2b9c2de 100644
--- a/scripts/OWNERS
+++ b/scripts/OWNERS
@@ -3,3 +3,4 @@
 per-file build-aml-prebuilts.sh = ngeoffray@google.com,paulduffin@google.com,mast@google.com
 per-file construct_context.py = ngeoffray@google.com,calin@google.com,mathieuc@google.com,skvadrik@google.com
 per-file conv_linker_config.py = kiyoungkim@google.com, jiyong@google.com, jooyung@google.com
+per-file gen_ndk*.sh = sophiez@google.com, allenhair@google.com
diff --git a/scripts/construct_context.py b/scripts/construct_context.py
index 6f9edc4..f0658ba 100755
--- a/scripts/construct_context.py
+++ b/scripts/construct_context.py
@@ -66,9 +66,9 @@
     if not args.sdk:
       raise SystemExit('target sdk version is not set')
     if not args.host_contexts:
-      raise SystemExit('host context is not set')
+      args.host_contexts = []
     if not args.target_contexts:
-      raise SystemExit('target context is not set')
+      args.target_contexts = []
 
     print(construct_contexts(args))
 
diff --git a/scripts/gen_ndk_usedby_apex.sh b/scripts/gen_ndk_usedby_apex.sh
index f143161..0d3ed5a 100755
--- a/scripts/gen_ndk_usedby_apex.sh
+++ b/scripts/gen_ndk_usedby_apex.sh
@@ -33,7 +33,7 @@
   do
       if [[ $line = *FUNC*GLOBAL*UND*@* ]] ;
       then
-          echo "$line" | sed -r 's/.*UND (.*)@.*/\1/g' >> "$2"
+          echo "$line" | sed -r 's/.*UND (.*@.*)/\1/g' >> "$2"
       fi
   done < "$1"
   echo "" >> "$2"
diff --git a/scripts/manifest_check.py b/scripts/manifest_check.py
index 1343f35..907f239 100755
--- a/scripts/manifest_check.py
+++ b/scripts/manifest_check.py
@@ -289,7 +289,12 @@
             f.write("%s\n" % errmsg)
 
     if args.extract_target_sdk_version:
-      print(extract_target_sdk_version(manifest, is_apk))
+      try:
+        print(extract_target_sdk_version(manifest, is_apk))
+      except:
+        # Failed; don't crash, return "any" SDK version. This will result in
+        # dexpreopt not adding any compatibility libraries.
+        print(10000)
 
     if args.output:
       # XML output is supposed to be written only when this script is invoked
diff --git a/sdk/cc_sdk_test.go b/sdk/cc_sdk_test.go
index 9626a04..a886a18 100644
--- a/sdk/cc_sdk_test.go
+++ b/sdk/cc_sdk_test.go
@@ -486,6 +486,9 @@
 		}
 	`)
 
+	// TODO(b/183322862): Remove this and fix the issue.
+	errorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`module source path "snapshot/include_gen/generated_foo/gen/protos" does not exist`)
+
 	CheckSnapshot(t, result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -518,6 +521,9 @@
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 .intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
 `),
+		snapshotTestErrorHandler(checkSnapshotWithoutSource, errorHandler),
+		snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, errorHandler),
+		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, errorHandler),
 	)
 }
 
@@ -1816,7 +1822,10 @@
 .intermediates/mynativelib/android_arm64_armv8-a_static/mynativelib.a -> arm64/lib/mynativelib.a
 .intermediates/mynativelib/android_arm64_armv8-a_shared/mynativelib.so -> arm64/lib/mynativelib.so
 .intermediates/mynativelib/android_arm_armv7-a-neon_static/mynativelib.a -> arm/lib/mynativelib.a
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+		// TODO(b/183315522): Remove this and fix the issue.
+		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\Qunrecognized property "arch.arm.shared.export_include_dirs"\E`)),
 	)
 }
 
@@ -2664,6 +2673,11 @@
 		}
 	`)
 
+	// Mixing the snapshot with the source (irrespective of which one is preferred) causes a problem
+	// due to missing variants.
+	// TODO(b/183204176): Remove this and fix the cause.
+	snapshotWithSourceErrorHandler := android.FixtureExpectsAtLeastOneErrorMatchingPattern(`\QReplaceDependencies could not find identical variant {os:android,image:,arch:arm64_armv8-a,sdk:,link:shared,version:} for module mynativelib\E`)
+
 	CheckSnapshot(t, result, "mysdk", "",
 		checkUnversionedAndroidBpContents(`
 // This is auto-generated. DO NOT EDIT.
@@ -2688,6 +2702,9 @@
 		checkAllCopyRules(`
 myinclude/Test.h -> include/myinclude/Test.h
 arm64/include/Arm64Test.h -> arm64/include/arm64/include/Arm64Test.h
-.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so`),
+.intermediates/mynativelib/android_arm_armv7-a-neon_shared/mynativelib.so -> arm/lib/mynativelib.so
+`),
+		snapshotTestErrorHandler(checkSnapshotWithSourcePreferred, snapshotWithSourceErrorHandler),
+		snapshotTestErrorHandler(checkSnapshotPreferredWithSource, snapshotWithSourceErrorHandler),
 	)
 }
diff --git a/sdk/compat_config_sdk_test.go b/sdk/compat_config_sdk_test.go
index dffc02a..00073c2 100644
--- a/sdk/compat_config_sdk_test.go
+++ b/sdk/compat_config_sdk_test.go
@@ -66,5 +66,26 @@
 		checkAllCopyRules(`
 .intermediates/myconfig/android_common/myconfig_meta.xml -> compat_configs/myconfig/myconfig_meta.xml
 `),
+		snapshotTestChecker(checkSnapshotWithoutSource,
+			func(t *testing.T, result *android.TestResult) {
+				// Make sure that the snapshot metadata is collated by the platform compat config singleton.
+				java.CheckMergedCompatConfigInputs(t, result, "snapshot module", "snapshot/compat_configs/myconfig/myconfig_meta.xml")
+			}),
+
+		snapshotTestChecker(checkSnapshotWithSourcePreferred,
+			func(t *testing.T, result *android.TestResult) {
+				// Make sure that the snapshot metadata is collated by the platform compat config singleton.
+				java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+					"out/soong/.intermediates/myconfig/android_common/myconfig_meta.xml",
+				)
+			}),
+
+		snapshotTestChecker(checkSnapshotPreferredWithSource,
+			func(t *testing.T, result *android.TestResult) {
+				// Make sure that the snapshot metadata is collated by the platform compat config singleton.
+				java.CheckMergedCompatConfigInputs(t, result, "snapshot module",
+					"snapshot/compat_configs/myconfig/myconfig_meta.xml",
+				)
+			}),
 	)
 }
diff --git a/sdk/java_sdk_test.go b/sdk/java_sdk_test.go
index ad45131..208cd58 100644
--- a/sdk/java_sdk_test.go
+++ b/sdk/java_sdk_test.go
@@ -24,6 +24,16 @@
 var prepareForSdkTestWithJava = android.GroupFixturePreparers(
 	java.PrepareForTestWithJavaBuildComponents,
 	PrepareForTestWithSdkBuildComponents,
+
+	// Ensure that all source paths are provided. This helps ensure that the snapshot generation is
+	// consistent and all files referenced from the snapshot's Android.bp file have actually been
+	// copied into the snapshot.
+	android.PrepareForTestDisallowNonExistentPaths,
+
+	// Files needs by most of the tests.
+	android.MockFS{
+		"Test.java": nil,
+	}.AddToFixture(),
 )
 
 var prepareForSdkTestWithJavaSdkLibrary = android.GroupFixturePreparers(
@@ -48,42 +58,17 @@
 			system_modules: "none",
 			sdk_version: "none",
 		}
-
-		java_import {
-			name: "sdkmember",
-			prefer: true,
-			jars: ["prebuilt.jar"],
-		}
 	`)
 
 	// Make sure that the mysdk module depends on "sdkmember" and not "prebuilt_sdkmember".
-	java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+	sdkChecker := func(t *testing.T, result *android.TestResult) {
+		java.CheckModuleDependencies(t, result.TestContext, "mysdk", "android_common", []string{"sdkmember"})
+	}
 
 	CheckSnapshot(t, result, "mysdk", "",
-		checkAndroidBpContents(`// This is auto-generated. DO NOT EDIT.
-
-java_import {
-    name: "mysdk_sdkmember@current",
-    sdk_member_name: "sdkmember",
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/sdkmember.jar"],
-}
-
-java_import {
-    name: "sdkmember",
-    prefer: false,
-    visibility: ["//visibility:public"],
-    apex_available: ["//apex_available:platform"],
-    jars: ["java/sdkmember.jar"],
-}
-
-sdk_snapshot {
-    name: "mysdk@current",
-    visibility: ["//visibility:public"],
-    java_header_libs: ["mysdk_sdkmember@current"],
-}
-`))
+		snapshotTestChecker(checkSnapshotWithSourcePreferred, sdkChecker),
+		snapshotTestChecker(checkSnapshotPreferredWithSource, sdkChecker),
+	)
 }
 
 func TestBasicSdkWithJavaLibrary(t *testing.T) {
@@ -364,6 +349,7 @@
 	result := android.GroupFixturePreparers(
 		prepareForSdkTestWithJava,
 		android.FixtureAddFile("aidl/foo/bar/Test.aidl", nil),
+		android.FixtureAddFile("resource.txt", nil),
 	).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
@@ -419,7 +405,11 @@
 }
 
 func TestSnapshotWithJavaBootLibrary(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJava).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJava,
+		android.FixtureAddFile("aidl", nil),
+		android.FixtureAddFile("resource.txt", nil),
+	).RunTestWithBp(t, `
 		module_exports {
 			name: "myexports",
 			java_boot_libs: ["myjavalib"],
@@ -1564,7 +1554,10 @@
 }
 
 func TestSnapshotWithJavaSdkLibrary_DoctagFiles(t *testing.T) {
-	result := android.GroupFixturePreparers(prepareForSdkTestWithJavaSdkLibrary).RunTestWithBp(t, `
+	result := android.GroupFixturePreparers(
+		prepareForSdkTestWithJavaSdkLibrary,
+		android.FixtureAddFile("docs/known_doctags", nil),
+	).RunTestWithBp(t, `
 		sdk {
 			name: "mysdk",
 			java_sdk_libs: ["myjavalib"],
diff --git a/sdk/sdk.go b/sdk/sdk.go
index e561529..b60fb18 100644
--- a/sdk/sdk.go
+++ b/sdk/sdk.go
@@ -486,6 +486,15 @@
 			// sdk containing sdkmember.
 			memberName := versionedSdkMember.MemberName()
 
+			// Convert a panic into a normal error to allow it to be more easily tested for. This is a
+			// temporary workaround, once http://b/183204176 has been fixed this can be removed.
+			// TODO(b/183204176): Remove this after fixing.
+			defer func() {
+				if r := recover(); r != nil {
+					mctx.ModuleErrorf("%s", r)
+				}
+			}()
+
 			// Replace dependencies on sdkmember with a dependency on the current module which
 			// is a versioned prebuilt of the sdkmember if required.
 			mctx.ReplaceDependenciesIf(memberName, func(from blueprint.Module, tag blueprint.DependencyTag, to blueprint.Module) bool {
diff --git a/sdk/testing.go b/sdk/testing.go
index ba40f67..44970f7 100644
--- a/sdk/testing.go
+++ b/sdk/testing.go
@@ -48,10 +48,10 @@
 		"system/sepolicy/apex/myapex-file_contexts":    nil,
 		"system/sepolicy/apex/myapex2-file_contexts":   nil,
 		"system/sepolicy/apex/mysdkapex-file_contexts": nil,
-		"myapex.avbpubkey":                             nil,
-		"myapex.pem":                                   nil,
-		"myapex.x509.pem":                              nil,
-		"myapex.pk8":                                   nil,
+		"sdk/tests/myapex.avbpubkey":                   nil,
+		"sdk/tests/myapex.pem":                         nil,
+		"sdk/tests/myapex.x509.pem":                    nil,
+		"sdk/tests/myapex.pk8":                         nil,
 	}),
 )
 
@@ -80,6 +80,12 @@
 			{android.Windows, android.Arch{ArchType: android.X86_64}, android.NativeBridgeDisabled, "", "", true},
 		}
 	}),
+
+	// Make sure that every test provides all the source files.
+	android.PrepareForTestDisallowNonExistentPaths,
+	android.MockFS{
+		"Test.java": nil,
+	}.AddToFixture(),
 )
 
 var PrepareForTestWithSdkBuildComponents = android.GroupFixturePreparers(
@@ -125,6 +131,7 @@
 		androidBpContents:            sdk.GetAndroidBpContentsForTests(),
 		androidUnversionedBpContents: sdk.GetUnversionedAndroidBpContentsForTests(),
 		androidVersionedBpContents:   sdk.GetVersionedAndroidBpContentsForTests(),
+		snapshotTestCustomizations:   map[snapshotTest]*snapshotTestCustomization{},
 	}
 
 	buildParams := sdk.BuildParamsForTests()
@@ -183,6 +190,24 @@
 	return info
 }
 
+// The enum of different sdk snapshot tests performed by CheckSnapshot.
+type snapshotTest int
+
+const (
+	// The enumeration of the different test configurations.
+	// A test with the snapshot/Android.bp file but without the original Android.bp file.
+	checkSnapshotWithoutSource snapshotTest = iota
+
+	// A test with both the original source and the snapshot, with the source preferred.
+	checkSnapshotWithSourcePreferred
+
+	// A test with both the original source and the snapshot, with the snapshot preferred.
+	checkSnapshotPreferredWithSource
+
+	// The directory into which the snapshot will be 'unpacked'.
+	snapshotSubDir = "snapshot"
+)
+
 // Check the snapshot build rules.
 //
 // Takes a list of functions which check different facets of the snapshot build rules.
@@ -214,31 +239,58 @@
 	// Populate a mock filesystem with the files that would have been copied by
 	// the rules.
 	fs := android.MockFS{}
-	snapshotSubDir := "snapshot"
 	for _, dest := range snapshotBuildInfo.snapshotContents {
 		fs[filepath.Join(snapshotSubDir, dest)] = nil
 	}
 	fs[filepath.Join(snapshotSubDir, "Android.bp")] = []byte(snapshotBuildInfo.androidBpContents)
 
-	preparer := result.Preparer()
+	// The preparers from the original source fixture.
+	sourcePreparers := result.Preparer()
 
-	// Process the generated bp file to make sure it is valid. Use the same preparer as was used to
-	// produce this result.
+	// Preparer to combine the snapshot and the source.
+	snapshotPreparer := android.GroupFixturePreparers(sourcePreparers, fs.AddToFixture())
+
+	var runSnapshotTestWithCheckers = func(t *testing.T, testConfig snapshotTest, extraPreparer android.FixturePreparer) {
+		customization := snapshotBuildInfo.snapshotTestCustomization(testConfig)
+
+		// TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
+		//  files the snapshot needs are actually copied into the snapshot.
+
+		// Run the snapshot with the snapshot preparer and the extra preparer, which must come after as
+		// it may need to modify parts of the MockFS populated by the snapshot preparer.
+		result := android.GroupFixturePreparers(snapshotPreparer, extraPreparer).
+			ExtendWithErrorHandler(customization.errorHandler).
+			RunTest(t)
+
+		// Perform any additional checks the test need on the result of processing the snapshot.
+		for _, checker := range customization.checkers {
+			checker(t, result)
+		}
+	}
+
 	t.Run("snapshot without source", func(t *testing.T) {
-		android.GroupFixturePreparers(
-			preparer,
-			// TODO(b/183184375): Set Config.TestAllowNonExistentPaths = false to verify that all the
-			//  files the snapshot needs are actually copied into the snapshot.
+		// Remove the source Android.bp file to make sure it works without.
+		removeSourceAndroidBp := android.FixtureModifyMockFS(func(fs android.MockFS) {
+			delete(fs, "Android.bp")
+		})
 
-			// Add the files (including bp) created for this snapshot to the test fixture.
-			fs.AddToFixture(),
+		runSnapshotTestWithCheckers(t, checkSnapshotWithoutSource, removeSourceAndroidBp)
+	})
 
-			// Remove the source Android.bp file to make sure it works without.
-			// TODO(b/183184375): Add a test with the source.
-			android.FixtureModifyMockFS(func(fs android.MockFS) {
-				delete(fs, "Android.bp")
-			}),
-		).RunTest(t)
+	t.Run("snapshot with source preferred", func(t *testing.T) {
+		runSnapshotTestWithCheckers(t, checkSnapshotWithSourcePreferred, android.NullFixturePreparer)
+	})
+
+	t.Run("snapshot preferred with source", func(t *testing.T) {
+		// Replace the snapshot/Android.bp file with one where "prefer: false," has been replaced with
+		// "prefer: true,"
+		preferPrebuilts := android.FixtureModifyMockFS(func(fs android.MockFS) {
+			snapshotBpFile := filepath.Join(snapshotSubDir, "Android.bp")
+			unpreferred := string(fs[snapshotBpFile])
+			fs[snapshotBpFile] = []byte(strings.ReplaceAll(unpreferred, "prefer: false,", "prefer: true,"))
+		})
+
+		runSnapshotTestWithCheckers(t, checkSnapshotPreferredWithSource, preferPrebuilts)
 	})
 }
 
@@ -312,6 +364,46 @@
 	}
 }
 
+type resultChecker func(t *testing.T, result *android.TestResult)
+
+// snapshotTestChecker registers a checker that will be run against the result of processing the
+// generated snapshot for the specified snapshotTest.
+func snapshotTestChecker(snapshotTest snapshotTest, checker resultChecker) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		customization := info.snapshotTestCustomization(snapshotTest)
+		customization.checkers = append(customization.checkers, checker)
+	}
+}
+
+// snapshotTestErrorHandler registers an error handler to use when processing the snapshot
+// in the specific test case.
+//
+// Generally, the snapshot should work with all the test cases but some do not and just in case
+// there are a lot of issues to resolve, or it will take a lot of time this is a
+// get-out-of-jail-free card that allows progress to be made.
+//
+// deprecated: should only be used as a temporary workaround with an attached to do and bug.
+func snapshotTestErrorHandler(snapshotTest snapshotTest, handler android.FixtureErrorHandler) snapshotBuildInfoChecker {
+	return func(info *snapshotBuildInfo) {
+		customization := info.snapshotTestCustomization(snapshotTest)
+		customization.errorHandler = handler
+	}
+}
+
+// Encapsulates information provided by each test to customize a specific snapshotTest.
+type snapshotTestCustomization struct {
+	// Checkers that are run on the result of processing the preferred snapshot in a specific test
+	// case.
+	checkers []resultChecker
+
+	// Specify an error handler for when processing a specific test case.
+	//
+	// In some cases the generated snapshot cannot be used in a test configuration. Those cases are
+	// invariably bugs that need to be resolved but sometimes that can take a while. This provides a
+	// mechanism to temporarily ignore that error.
+	errorHandler android.FixtureErrorHandler
+}
+
 // Encapsulates information about the snapshot build structure in order to insulate tests from
 // knowing too much about internal structures.
 //
@@ -355,4 +447,21 @@
 
 	// The final output zip.
 	outputZip string
+
+	// The test specific customizations for each snapshot test.
+	snapshotTestCustomizations map[snapshotTest]*snapshotTestCustomization
+}
+
+// snapshotTestCustomization gets the test specific customization for the specified snapshotTest.
+//
+// If no customization was created previously then it creates a default customization.
+func (i *snapshotBuildInfo) snapshotTestCustomization(snapshotTest snapshotTest) *snapshotTestCustomization {
+	customization := i.snapshotTestCustomizations[snapshotTest]
+	if customization == nil {
+		customization = &snapshotTestCustomization{
+			errorHandler: android.FixtureExpectsNoErrors,
+		}
+		i.snapshotTestCustomizations[snapshotTest] = customization
+	}
+	return customization
 }
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 1ae557a..6623381 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -485,7 +485,7 @@
 }
 
 type bazelShBinaryAttributes struct {
-	Srcs bazel.LabelList
+	Srcs bazel.LabelListAttribute
 	// Bazel also supports the attributes below, but (so far) these are not required for Bionic
 	// deps
 	// data
@@ -525,7 +525,8 @@
 		return
 	}
 
-	srcs := android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src})
+	srcs := bazel.MakeLabelListAttribute(
+		android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
 
 	attrs := &bazelShBinaryAttributes{
 		Srcs: srcs,