Merge "mixed builds correctly reference stubs libs"
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 33209c3..14c713f 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -375,8 +375,9 @@
 		"system/tools/xsdc/utils":                                Bp2BuildDefaultTrueRecursively,
 		"system/unwinding/libunwindstack":                        Bp2BuildDefaultTrueRecursively,
 
-		"tools/apksig":   Bp2BuildDefaultTrue,
-		"tools/metalava": Bp2BuildDefaultTrue,
+		"tools/apksig":                               Bp2BuildDefaultTrue,
+		"tools/external_updater":                     Bp2BuildDefaultTrueRecursively,
+		"tools/metalava":                             Bp2BuildDefaultTrue,
 		"tools/platform-compat/java/android/compat":  Bp2BuildDefaultTrueRecursively,
 		"tools/tradefederation/prebuilts/filegroups": Bp2BuildDefaultTrueRecursively,
 	}
@@ -408,8 +409,6 @@
 		// this BUILD file is globbed by //external/icu/icu4c/source:icu4c_test_data's "data/**/*".
 		"external/icu/icu4c/source/data/unidata/norm2":/* recursive = */ false,
 
-		"frameworks/ex/common":/* recursive = */ true,
-
 		// Building manually due to b/179889880: resource files cross package boundary
 		"packages/apps/Music":/* recursive = */ true,
 
@@ -690,7 +689,6 @@
 		"libcodec2_soft_common",
 
 		// kotlin srcs in java libs
-		"CtsPkgInstallerConstants",
 		"kotlinx_atomicfu",
 
 		// kotlin srcs in java binary
@@ -703,6 +701,9 @@
 		//kotlin srcs in android_binary
 		"MusicKotlin",
 
+		// java_library with prebuilt sdk_version
+		"android-common",
+
 		// checked in current.txt for merged_txts
 		"non-updatable-current.txt",
 		"non-updatable-system-current.txt",
@@ -725,7 +726,6 @@
 
 		// min_sdk_version in android_app
 		"CtsShimUpgrade",
-		"fake-framework",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
@@ -779,7 +779,8 @@
 		"tjbench", // TODO(b/240563612): Stem property
 
 		// java bugs
-		"libbase_ndk", // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
+		"libbase_ndk",  // TODO(b/186826477): fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
+		"bouncycastle", // TODO(b/274474005): Need support for custom system_modules.
 
 		// python protos
 		"libprotobuf-python", // Has a handcrafted alternative
@@ -805,6 +806,7 @@
 
 		// go deps:
 		"analyze_bcpf",              // depends on bpmodify a blueprint_go_binary.
+		"analyze_bcpf_test",         // depends on bpmodify a blueprint_go_binary.
 		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
 		"host_bionic_linker_script", // depends on extract_linker, a go binary.
 
@@ -815,13 +817,15 @@
 		"libtombstoned_client_rust_bridge_code", "libtombstoned_client_wrapper", // rust conversions are not supported
 
 		// unconverted deps
-		"CarHTMLViewer",                                              // depends on unconverted modules android.car-stubs, car-ui-lib
+		"apexer_with_DCLA_preprocessing_test",                        // depends on unconverted modules: apexer_test_host_tools, com.android.example.apex
 		"adb",                                                        // depends on unconverted modules: AdbWinApi, libandroidfw, libopenscreen-discovery, libopenscreen-platform-impl, libusb, bin2c_fastdeployagent, AdbWinUsbApi
 		"android_icu4j_srcgen",                                       // depends on unconverted modules: currysrc
 		"android_icu4j_srcgen_binary",                                // depends on unconverted modules: android_icu4j_srcgen, currysrc
+		"apex_compression_test",                                      // depends on unconverted modules: soong_zip, com.android.example.apex
 		"apex_manifest_proto_java",                                   // b/210751803, depends on libprotobuf-java-full
 		"art-script",                                                 // depends on unconverted modules: dalvikvm, dex2oat
 		"bin2c_fastdeployagent",                                      // depends on unconverted modules: deployagent
+		"CarHTMLViewer",                                              // depends on unconverted modules android.car-stubs, car-ui-lib
 		"com.android.runtime",                                        // depends on unconverted modules: bionic-linker-config, linkerconfig
 		"currysrc",                                                   // depends on unconverted modules: currysrc_org.eclipse, guavalib, jopt-simple-4.9
 		"dex2oat-script",                                             // depends on unconverted modules: dex2oat
@@ -846,12 +850,12 @@
 		"libgmock_ndk",                                            // depends on unconverted modules: libgtest_ndk_c++
 		"libnativehelper_lazy_mts_jni", "libnativehelper_mts_jni", // depends on unconverted modules: libnativetesthelper_jni, libgmock_ndk
 		"libnativetesthelper_jni",   // depends on unconverted modules: libgtest_ndk_c++
-		"libprotobuf-java-nano",     // b/220869005, depends on non-public_current SDK
 		"libstatslog",               // depends on unconverted modules: libstatspull, statsd-aidl-ndk
 		"libstatslog_art",           // depends on unconverted modules: statslog_art.cpp, statslog_art.h
 		"linker_reloc_bench_main",   // depends on unconverted modules: liblinker_reloc_bench_*
 		"malloc-rss-benchmark",      // depends on unconverted modules: libmeminfo
 		"pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
+		"releasetools_test",             // depends on unconverted modules: com.android.apex.compressed.v1
 		"robolectric-sqlite4java-0.282", // depends on unconverted modules: robolectric-sqlite4java-import, robolectric-sqlite4java-native
 		"static_crasher",                // depends on unconverted modules: libdebuggerd_handler
 		"test_fips",                     // depends on unconverted modules: adb
@@ -1396,6 +1400,26 @@
 
 		// TODO(b/266459895): depends on libunwindstack
 		"libutils_test",
+
+		// Has dependencies on other tools like ziptool, bp2build'd data properties don't work with these tests atm
+		"ziparchive_tests_large",
+		"mkbootimg_test",
+		"certify_bootimg_test",
+
+		// Despite being _host module types, these require devices to run
+		"logd_integration_test",
+		"mobly-hello-world-test",
+		"mobly-multidevice-test",
+
+		// TODO(b/274805756): Support core_platform and current java APIs
+		"fake-framework",
+
+		// TODO(b/277616982): These modules depend on private java APIs, but maybe they don't need to.
+		"StreamingProtoTest",
+		"textclassifierprotoslite",
+		"styleprotoslite",
+		"CtsPkgInstallerConstants",
+		"guava-android-testlib",
 	}
 
 	MixedBuildsDisabledList = []string{
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 44dc055..ade2c49 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -84,8 +84,12 @@
 func mixedBuildsPrepareMutator(ctx BottomUpMutatorContext) {
 	if m := ctx.Module(); m.Enabled() {
 		if mixedBuildMod, ok := m.(MixedBuildBuildable); ok {
-			if mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx) {
+			queueMixedBuild := mixedBuildMod.IsMixedBuildSupported(ctx) && MixedBuildsEnabled(ctx)
+			if queueMixedBuild {
 				mixedBuildMod.QueueBazelCall(ctx)
+			} else if _, ok := ctx.Config().bazelForceEnabledModules[m.Name()]; ok {
+				// TODO(b/273910287) - remove this once --ensure_allowlist_integrity is added
+				ctx.ModuleErrorf("Attempted to force enable an unready module: %s. Did you forget to Bp2BuildDefaultTrue its directory?\n", m.Name())
 			}
 		}
 	}
@@ -960,9 +964,13 @@
 // request type.
 func (context *mixedBuildBazelContext) cqueryStarlarkFileContents() []byte {
 	requestTypeToCqueryIdEntries := map[cqueryRequest][]string{}
+	requestTypes := []cqueryRequest{}
 	for _, val := range context.requests {
 		cqueryId := getCqueryId(val)
 		mapEntryString := fmt.Sprintf("%q : True", cqueryId)
+		if _, seenKey := requestTypeToCqueryIdEntries[val.requestType]; !seenKey {
+			requestTypes = append(requestTypes, val.requestType)
+		}
 		requestTypeToCqueryIdEntries[val.requestType] =
 			append(requestTypeToCqueryIdEntries[val.requestType], mapEntryString)
 	}
@@ -984,7 +992,7 @@
     return id_string + ">>" + %s(target, id_string)
 `
 
-	for requestType := range requestTypeToCqueryIdEntries {
+	for _, requestType := range requestTypes {
 		labelMapName := requestType.Name() + "_Labels"
 		functionName := requestType.Name() + "_Fn"
 		labelRegistrationMapSection += fmt.Sprintf(mapDeclarationFormatString,
diff --git a/android/config.go b/android/config.go
index 2ccb732..032172d 100644
--- a/android/config.go
+++ b/android/config.go
@@ -592,12 +592,11 @@
 	setBazelMode(cmdArgs.BazelMode, "--bazel-mode", BazelProdMode)
 	setBazelMode(cmdArgs.BazelModeStaging, "--bazel-mode-staging", BazelStagingMode)
 
-	config.BazelContext, err = NewBazelContext(config)
-	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
-
 	for _, module := range strings.Split(cmdArgs.BazelForceEnabledModules, ",") {
 		config.bazelForceEnabledModules[module] = struct{}{}
 	}
+	config.BazelContext, err = NewBazelContext(config)
+	config.Bp2buildPackageConfig = GetBp2BuildAllowList()
 
 	return Config{config}, err
 }
@@ -833,6 +832,10 @@
 	return uncheckedFinalApiLevel(*c.productVariables.Platform_sdk_version)
 }
 
+func (c *config) RawPlatformSdkVersion() *int {
+	return c.productVariables.Platform_sdk_version
+}
+
 func (c *config) PlatformSdkFinal() bool {
 	return Bool(c.productVariables.Platform_sdk_final)
 }
@@ -1930,3 +1933,8 @@
 func (c *config) SetBuildFromTextStub(b bool) {
 	c.buildFromTextStub = b
 }
+func (c *config) AddForceEnabledModules(forceEnabled []string) {
+	for _, forceEnabledModule := range forceEnabled {
+		c.bazelForceEnabledModules[forceEnabledModule] = struct{}{}
+	}
+}
diff --git a/android/test_config.go b/android/test_config.go
index 07ca33d..28d9ec4 100644
--- a/android/test_config.go
+++ b/android/test_config.go
@@ -65,6 +65,7 @@
 		BuildMode:                 BazelProdMode,
 		mixedBuildDisabledModules: make(map[string]struct{}),
 		mixedBuildEnabledModules:  make(map[string]struct{}),
+		bazelForceEnabledModules:  make(map[string]struct{}),
 	}
 	config.deviceConfig = &deviceConfig{
 		config: config,
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 8a02a4a..557265c 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -1001,7 +1001,7 @@
 	// Ensure that stub dependency from a rust module is not included
 	ensureNotContains(t, copyCmds, "image.apex/lib64/libfoo.shared_from_rust.so")
 	// The rust module is linked to the stub cc library
-	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").Args["linkFlags"]
 	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
 	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
 
@@ -1077,7 +1077,7 @@
 	mylibLdFlags := ctx.ModuleForTests("mylib", "android_arm64_armv8-a_shared_apex10000").Rule("ld").Args["libFlags"]
 	ensureNotContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared_current/mylib2.so")
 	ensureContains(t, mylibLdFlags, "mylib2/android_arm64_armv8-a_shared/mylib2.so")
-	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").Args["linkFlags"]
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustLink").Args["linkFlags"]
 	ensureNotContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared_current/libfoo.shared_from_rust.so")
 	ensureContains(t, rustDeps, "libfoo.shared_from_rust/android_arm64_armv8-a_shared/libfoo.shared_from_rust.so")
 }
@@ -7515,6 +7515,7 @@
 				"some-updatable-apex",
 			],
 			permitted_packages: ["some.updatable.apex.lib"],
+			min_sdk_version: "33",
 		}
 
 		java_library {
@@ -7554,6 +7555,7 @@
 			],
 			hostdex: true,
 			compile_dex: true,
+			min_sdk_version: "33",
 		}
 
 		apex {
@@ -7561,7 +7563,7 @@
 			key: "some-updatable-apex.key",
 			java_libs: ["some-updatable-apex-lib"],
 			updatable: true,
-			min_sdk_version: "current",
+			min_sdk_version: "33",
 		}
 
 		apex {
@@ -7584,7 +7586,7 @@
 			key: "com.android.art.debug.key",
 			bootclasspath_fragments: ["art-bootclasspath-fragment"],
 			updatable: true,
-			min_sdk_version: "current",
+			min_sdk_version: "33",
 		}
 
 		bootclasspath_fragment {
diff --git a/bp2build/Android.bp b/bp2build/Android.bp
index 598ca32..b6635c4 100644
--- a/bp2build/Android.bp
+++ b/bp2build/Android.bp
@@ -76,6 +76,7 @@
         "prebuilt_etc_conversion_test.go",
         "python_binary_conversion_test.go",
         "python_library_conversion_test.go",
+        "python_test_conversion_test.go",
         "sh_conversion_test.go",
         "soong_config_module_type_conversion_test.go",
     ],
diff --git a/bp2build/aar_conversion_test.go b/bp2build/aar_conversion_test.go
index 6020ee5..09d9dc1 100644
--- a/bp2build/aar_conversion_test.go
+++ b/bp2build/aar_conversion_test.go
@@ -66,9 +66,12 @@
 					"resource_files": `["res/res.png"]`,
 					"deps":           `[":static_lib_dep"]`,
 					"exports":        `[":static_lib_dep"]`,
-					"javacopts":      `["-source 1.7 -target 1.7"]`,
+					"java_version":   `"7"`,
 				}),
-			MakeNeverlinkDuplicateTarget("android_library", "TestLib"),
+			MakeNeverlinkDuplicateTargetWithAttrs(
+				"android_library",
+				"TestLib",
+				AttrNameToString{"java_version": `"7"`}),
 		}})
 }
 
diff --git a/bp2build/android_app_conversion_test.go b/bp2build/android_app_conversion_test.go
index ef3f124..928a1f2 100644
--- a/bp2build/android_app_conversion_test.go
+++ b/bp2build/android_app_conversion_test.go
@@ -53,6 +53,7 @@
 				"srcs":           `["app.java"]`,
 				"manifest":       `"AndroidManifest.xml"`,
 				"resource_files": `["res/res.png"]`,
+				"sdk_version":    `"current"`,
 			}),
 		}})
 }
@@ -91,7 +92,8 @@
     ]`,
 				"custom_package":   `"com.google"`,
 				"deps":             `[":static_lib_dep"]`,
-				"javacopts":        `["-source 1.7 -target 1.7"]`,
+				"java_version":     `"7"`,
+				"sdk_version":      `"current"`,
 				"certificate_name": `"foocert"`,
 			}),
 		}})
@@ -131,6 +133,7 @@
     })`,
 				"manifest":       `"AndroidManifest.xml"`,
 				"resource_files": `["res/res.png"]`,
+				"sdk_version":    `"current"`,
 			}),
 		}})
 }
@@ -365,6 +368,7 @@
 				"manifest_values": `{
         "minSdkVersion": "24",
     }`,
+				"sdk_version": `"current"`,
 			}),
 		}})
 }
@@ -388,6 +392,7 @@
 				"manifest_values": `{
         "minSdkVersion": "30",
     }`,
+				"sdk_version": `"30"`,
 			}),
 		}})
 }
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index ab2f821..3abef9d 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -68,16 +68,6 @@
 	"@//build/bazel/product_config:__subpackages__",
 	"@soong_injection//product_config_platforms:__subpackages__",
 ])
-
-# TODO(b/249685973): Remove this. It was only added for a platform_mappings file,
-# which can possibly be replaced with autogenerating the platform_mappings file,
-# or removing that file entirely.
-alias(
-	name = "current_android_platform",
-	# TODO: When we start generating the platforms for more than just the
-	# currently lunched, product, turn this into a select with an arm for each product.
-	actual = "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}",
-)
 `)),
 		newFile(
 			"product_config_platforms",
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index fde9b69..b7678a4 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -321,6 +321,9 @@
 					// target, each of a different rule class.
 					metrics.IncrementRuleClassCount(t.ruleClass)
 				}
+			} else if _, ok := ctx.Config().BazelModulesForceEnabledByFlag()[m.Name()]; ok && m.Name() != "" {
+				err := fmt.Errorf("Force Enabled Module %s not converted", m.Name())
+				errs = append(errs, err)
 			} else {
 				metrics.AddUnconvertedModule(moduleType)
 				return
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index d312169..73ee26b 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1175,6 +1175,8 @@
 		bp2buildConfig             allowlists.Bp2BuildConfig
 		checkDir                   string
 		fs                         map[string]string
+		forceEnabledModules        []string
+		expectedErrorMessages      []string
 	}{
 		{
 			description:                "test bp2build config package and subpackages config",
@@ -1237,6 +1239,24 @@
 `,
 			},
 		},
+		{
+			description:                "test force-enabled errors out",
+			moduleTypeUnderTest:        "filegroup",
+			moduleTypeUnderTestFactory: android.FileGroupFactory,
+			expectedCount: map[string]int{
+				"migrated":     0,
+				"not_migrated": 0,
+			},
+			bp2buildConfig: allowlists.Bp2BuildConfig{
+				"migrated/but_not_really": allowlists.Bp2BuildDefaultFalse,
+				"not_migrated":            allowlists.Bp2BuildDefaultFalse,
+			},
+			fs: map[string]string{
+				"migrated/Android.bp": `filegroup { name: "a" }`,
+			},
+			forceEnabledModules:   []string{"a"},
+			expectedErrorMessages: []string{"Force Enabled Module a not converted"},
+		},
 	}
 
 	dir := "."
@@ -1252,6 +1272,7 @@
 			fs[f] = []byte(content)
 		}
 		config := android.TestConfig(buildDir, nil, "", fs)
+		config.AddForceEnabledModules(testCase.forceEnabledModules)
 		ctx := android.NewTestContext(config)
 		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
 		allowlist := android.NewBp2BuildAllowlist().SetDefaultConfig(testCase.bp2buildConfig)
@@ -1268,7 +1289,7 @@
 		// For each directory, test that the expected number of generated targets is correct.
 		for dir, expectedCount := range testCase.expectedCount {
 			bazelTargets, err := generateBazelTargetsForDir(codegenCtx, dir)
-			android.FailIfErrored(t, err)
+			android.CheckErrorsAgainstExpectations(t, err, testCase.expectedErrorMessages)
 			if actualCount := len(bazelTargets); actualCount != expectedCount {
 				t.Fatalf(
 					"%s: Expected %d bazel target for %s package, got %d",
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 815461a..608fcd8 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -5,6 +5,7 @@
 	"encoding/json"
 	"fmt"
 	"reflect"
+	"strconv"
 	"strings"
 
 	"android/soong/android"
@@ -87,14 +88,19 @@
 		platformVersionActiveCodenames = append(platformVersionActiveCodenames, fmt.Sprintf("%q", codename))
 	}
 
+	platformSdkVersion := "None"
+	if cfg.RawPlatformSdkVersion() != nil {
+		platformSdkVersion = strconv.Itoa(*cfg.RawPlatformSdkVersion())
+	}
+
 	return fmt.Sprintf(`
 platform_versions = struct(
     platform_sdk_final = %s,
-    platform_sdk_version = %d,
+    platform_sdk_version = %s,
     platform_sdk_codename = %q,
     platform_version_active_codenames = [%s],
 )
-`, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), cfg.PlatformSdkVersion().FinalInt(), cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
+`, starlark_fmt.PrintBool(cfg.PlatformSdkFinal()), platformSdkVersion, cfg.PlatformSdkCodename(), strings.Join(platformVersionActiveCodenames, ", "))
 }
 
 func CreateBazelFiles(
diff --git a/bp2build/java_binary_host_conversion_test.go b/bp2build/java_binary_host_conversion_test.go
index 1b9777c..c821f59 100644
--- a/bp2build/java_binary_host_conversion_test.go
+++ b/bp2build/java_binary_host_conversion_test.go
@@ -57,24 +57,24 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_library", "java-binary-host-1_lib", AttrNameToString{
-				"srcs": `["a.java"]`,
-				"deps": `["//other:jni-lib-1"]`,
-				"javacopts": `[
-        "-Xdoclint:all/protected",
-        "-source 1.8 -target 1.8",
-    ]`,
+				"srcs":         `["a.java"]`,
+				"deps":         `["//other:jni-lib-1"]`,
+				"java_version": `"8"`,
+				"javacopts":    `["-Xdoclint:all/protected"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
-    })`}),
+    })`,
+			}),
 			MakeBazelTarget("java_binary", "java-binary-host-1", AttrNameToString{
-				"main_class":   `"com.android.test.MainClass"`,
-				"jvm_flags":    `["-Djava.library.path=$${RUNPATH}other"]`,
-				"runtime_deps": `[":java-binary-host-1_lib"]`,
+				"main_class": `"com.android.test.MainClass"`,
+				"jvm_flags":  `["-Djava.library.path=$${RUNPATH}other"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
-    })`}),
+    })`,
+				"runtime_deps": `[":java-binary-host-1_lib"]`,
+			}),
 		},
 	})
 }
diff --git a/bp2build/java_host_for_device_conversion_test.go b/bp2build/java_host_for_device_conversion_test.go
index d908d00..448cba4 100644
--- a/bp2build/java_host_for_device_conversion_test.go
+++ b/bp2build/java_host_for_device_conversion_test.go
@@ -51,9 +51,11 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_host_for_device", "java-lib-1", AttrNameToString{
-				"deps": `[":java-lib-2"]`,
+				"exports": `[":java-lib-2"]`,
 			}),
-			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+			MakeNeverlinkDuplicateTargetWithAttrs("java_library", "java-lib-1", AttrNameToString{
+				"sdk_version": `"none"`,
+			}),
 			MakeBazelTarget("java_library", "java-lib-2", AttrNameToString{
 				"srcs": `["b.java"]`,
 			}),
diff --git a/bp2build/java_import_conversion_test.go b/bp2build/java_import_conversion_test.go
index a5c01cb..5661620 100644
--- a/bp2build/java_import_conversion_test.go
+++ b/bp2build/java_import_conversion_test.go
@@ -49,8 +49,9 @@
 				"jars": `["import.jar"]`,
 			}),
 			MakeBazelTarget("java_library", "example_import-neverlink", AttrNameToString{
-				"exports":   `[":example_import"]`,
-				"neverlink": `True`,
+				"exports":     `[":example_import"]`,
+				"neverlink":   `True`,
+				"sdk_version": `"none"`,
 			}),
 		}})
 }
@@ -86,8 +87,9 @@
     })`,
 			}),
 			MakeBazelTarget("java_library", "example_import-neverlink", AttrNameToString{
-				"exports":   `[":example_import"]`,
-				"neverlink": `True`,
+				"exports":     `[":example_import"]`,
+				"neverlink":   `True`,
+				"sdk_version": `"none"`,
 			}),
 		}})
 }
@@ -112,8 +114,9 @@
 				"jars": `["import.jar"]`,
 			}),
 			MakeBazelTarget("java_library", "example_import-neverlink", AttrNameToString{
-				"exports":   `[":example_import"]`,
-				"neverlink": `True`,
+				"exports":     `[":example_import"]`,
+				"neverlink":   `True`,
+				"sdk_version": `"none"`,
 			}),
 		}})
 }
diff --git a/bp2build/java_library_conversion_test.go b/bp2build/java_library_conversion_test.go
index 683ee27..24b763b 100644
--- a/bp2build/java_library_conversion_test.go
+++ b/bp2build/java_library_conversion_test.go
@@ -172,10 +172,13 @@
 }`,
 		ExpectedBazelTargets: []string{
 			MakeBazelTarget("java_library", "java-lib-1", AttrNameToString{
-				"srcs":      `["a.java"]`,
-				"javacopts": `["-source 11 -target 11"]`,
+				"srcs":         `["a.java"]`,
+				"java_version": `"11"`,
 			}),
-			MakeNeverlinkDuplicateTarget("java_library", "java-lib-1"),
+			MakeNeverlinkDuplicateTargetWithAttrs(
+				"java_library",
+				"java-lib-1",
+				AttrNameToString{"java_version": `"11"`}),
 		},
 	})
 }
diff --git a/bp2build/java_library_host_conversion_test.go b/bp2build/java_library_host_conversion_test.go
index 14854c0..9e47b09 100644
--- a/bp2build/java_library_host_conversion_test.go
+++ b/bp2build/java_library_host_conversion_test.go
@@ -63,8 +63,8 @@
     })`,
 			}),
 			MakeBazelTarget("java_library", "java-lib-host-2", AttrNameToString{
-				"javacopts": `["-source 1.9 -target 1.9"]`,
-				"srcs":      `["c.java"]`,
+				"java_version": `"9"`,
+				"srcs":         `["c.java"]`,
 				"target_compatible_with": `select({
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
@@ -77,6 +77,7 @@
         "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
         "//conditions:default": [],
     })`,
+				"java_version": `"9"`,
 			}),
 		},
 	})
diff --git a/bp2build/java_plugin_conversion_test.go b/bp2build/java_plugin_conversion_test.go
index 8c6337b..f2b6f20 100644
--- a/bp2build/java_plugin_conversion_test.go
+++ b/bp2build/java_plugin_conversion_test.go
@@ -67,7 +67,7 @@
         "a.java",
         "b.java",
     ]`,
-				"javacopts": `["-source 1.7 -target 1.7"]`,
+				"java_version": `"7"`,
 			}),
 		},
 	})
diff --git a/bp2build/java_proto_conversion_test.go b/bp2build/java_proto_conversion_test.go
index d25b7c4..f546cf4 100644
--- a/bp2build/java_proto_conversion_test.go
+++ b/bp2build/java_proto_conversion_test.go
@@ -114,13 +114,17 @@
 				"java_lite_proto_library",
 				"java-protos_java_proto_lite",
 				AttrNameToString{
-					"deps": `[":java-protos_proto"]`,
+					"deps":         `[":java-protos_proto"]`,
+					"java_version": `"7"`,
 				}),
 			MakeBazelTarget("java_library", "java-protos", AttrNameToString{
-				"exports":   `[":java-protos_java_proto_lite"]`,
-				"javacopts": `["-source 1.7 -target 1.7"]`,
+				"exports":      `[":java-protos_java_proto_lite"]`,
+				"java_version": `"7"`,
 			}),
-			MakeNeverlinkDuplicateTarget("java_library", "java-protos"),
+			MakeNeverlinkDuplicateTargetWithAttrs(
+				"java_library",
+				"java-protos",
+				AttrNameToString{"java_version": `"7"`}),
 		},
 	})
 }
diff --git a/bp2build/python_test_conversion_test.go b/bp2build/python_test_conversion_test.go
new file mode 100644
index 0000000..4ff1fa1
--- /dev/null
+++ b/bp2build/python_test_conversion_test.go
@@ -0,0 +1,66 @@
+// Copyright 2023 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/python"
+	"testing"
+)
+
+func TestPythonTestHostSimple(t *testing.T) {
+	runBp2BuildTestCaseWithPythonLibraries(t, Bp2buildTestCase{
+		Description:                "simple python_test_host converts to a native py_test",
+		ModuleTypeUnderTest:        "python_test_host",
+		ModuleTypeUnderTestFactory: python.PythonTestHostFactory,
+		Filesystem: map[string]string{
+			"a.py":           "",
+			"b/c.py":         "",
+			"b/d.py":         "",
+			"b/e.py":         "",
+			"files/data.txt": "",
+		},
+		Blueprint: `python_test_host {
+    name: "foo",
+    main: "a.py",
+    srcs: ["**/*.py"],
+    exclude_srcs: ["b/e.py"],
+    data: ["files/data.txt",],
+    libs: ["bar"],
+    bazel_module: { bp2build_available: true },
+}
+    python_library_host {
+      name: "bar",
+      srcs: ["b/e.py"],
+      bazel_module: { bp2build_available: false },
+    }`,
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("py_test", "foo", AttrNameToString{
+				"data":    `["files/data.txt"]`,
+				"deps":    `[":bar"]`,
+				"main":    `"a.py"`,
+				"imports": `["."]`,
+				"srcs": `[
+        "a.py",
+        "b/c.py",
+        "b/d.py",
+    ]`,
+				"target_compatible_with": `select({
+        "//build/bazel/platforms/os:android": ["@platforms//:incompatible"],
+        "//conditions:default": [],
+    })`,
+			}),
+		},
+	})
+}
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 856b6ee..6e919db 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -642,10 +642,14 @@
 }
 
 func MakeNeverlinkDuplicateTarget(moduleType string, name string) string {
-	return MakeBazelTarget(moduleType, name+"-neverlink", AttrNameToString{
-		"neverlink": `True`,
-		"exports":   `[":` + name + `"]`,
-	})
+	return MakeNeverlinkDuplicateTargetWithAttrs(moduleType, name, AttrNameToString{})
+}
+
+func MakeNeverlinkDuplicateTargetWithAttrs(moduleType string, name string, extraAttrs AttrNameToString) string {
+	attrs := extraAttrs
+	attrs["neverlink"] = `True`
+	attrs["exports"] = `[":` + name + `"]`
+	return MakeBazelTarget(moduleType, name+"-neverlink", attrs)
 }
 
 func getTargetName(targetContent string) string {
diff --git a/cc/cc.go b/cc/cc.go
index 631bc7a..4641480 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -1858,6 +1858,10 @@
 		if c.SplitPerApiLevel() {
 			subName += "." + c.SdkVersion()
 		}
+	} else if c.IsStubs() && c.IsSdkVariant() {
+		// Public API surface (NDK)
+		// Add a suffix to this stub variant to distinguish it from the module-lib stub variant.
+		subName = sdkSuffix
 	}
 
 	return subName
diff --git a/cc/sanitize.go b/cc/sanitize.go
index c478e58..4601ee6 100644
--- a/cc/sanitize.go
+++ b/cc/sanitize.go
@@ -616,6 +616,10 @@
 	if (ctx.Arch().ArchType != android.Arm64 && ctx.Arch().ArchType != android.Riscv64) || !ctx.toolchain().Bionic() {
 		s.Scs = nil
 	}
+	// ...but temporarily globally disabled on riscv64 (http://b/277909695).
+	if ctx.Arch().ArchType == android.Riscv64 {
+		s.Scs = nil
+	}
 
 	// Memtag_heap is only implemented on AArch64.
 	// Memtag ABI is Android specific for now, so disable for host.
diff --git a/cc/sanitize_test.go b/cc/sanitize_test.go
index 718186d..29b17d4 100644
--- a/cc/sanitize_test.go
+++ b/cc/sanitize_test.go
@@ -1166,3 +1166,83 @@
 	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_disable", variant), Sync)
 	checkHasMemtagNote(t, ctx.ModuleForTests("unset_test_override_default_sync", variant), Sync)
 }
+
+func TestCfi(t *testing.T) {
+	t.Parallel()
+
+	bp := `
+	cc_library_shared {
+		name: "shared_with_cfi",
+		static_libs: [
+			"static_dep_with_cfi",
+			"static_dep_no_cfi",
+		],
+		sanitize: {
+			cfi: true,
+		},
+	}
+
+	cc_library_shared {
+		name: "shared_no_cfi",
+		static_libs: [
+			"static_dep_with_cfi",
+			"static_dep_no_cfi",
+		],
+	}
+
+	cc_library_static {
+		name: "static_dep_with_cfi",
+		sanitize: {
+			cfi: true,
+		},
+	}
+
+	cc_library_static {
+		name: "static_dep_no_cfi",
+	}
+
+	cc_library_shared {
+		name: "shared_rdep_no_cfi",
+		static_libs: ["static_dep_with_cfi_2"],
+	}
+
+	cc_library_static {
+		name: "static_dep_with_cfi_2",
+		sanitize: {
+			cfi: true,
+		},
+	}
+`
+	preparer := android.GroupFixturePreparers(
+		prepareForCcTest,
+	)
+	result := preparer.RunTestWithBp(t, bp)
+	ctx := result.TestContext
+
+	buildOs := "android_arm64_armv8-a"
+	shared_suffix := "_shared"
+	cfi_suffix := "_cfi"
+	static_suffix := "_static"
+
+	sharedWithCfiLib := result.ModuleForTests("shared_with_cfi", buildOs+shared_suffix+cfi_suffix)
+	sharedNoCfiLib := result.ModuleForTests("shared_no_cfi", buildOs+shared_suffix)
+	staticWithCfiLib := result.ModuleForTests("static_dep_with_cfi", buildOs+static_suffix)
+	staticWithCfiLibCfiVariant := result.ModuleForTests("static_dep_with_cfi", buildOs+static_suffix+cfi_suffix)
+	staticNoCfiLib := result.ModuleForTests("static_dep_no_cfi", buildOs+static_suffix)
+	staticNoCfiLibCfiVariant := result.ModuleForTests("static_dep_no_cfi", buildOs+static_suffix+cfi_suffix)
+	sharedRdepNoCfi := result.ModuleForTests("shared_rdep_no_cfi", buildOs+shared_suffix)
+	staticDepWithCfi2Lib := result.ModuleForTests("static_dep_with_cfi_2", buildOs+static_suffix)
+
+	// Confirm assumptions about propagation of CFI enablement
+	expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticWithCfiLibCfiVariant)
+	expectStaticLinkDep(t, ctx, sharedNoCfiLib, staticWithCfiLib)
+	expectStaticLinkDep(t, ctx, sharedWithCfiLib, staticNoCfiLibCfiVariant)
+	expectStaticLinkDep(t, ctx, sharedNoCfiLib, staticNoCfiLib)
+	expectStaticLinkDep(t, ctx, sharedRdepNoCfi, staticDepWithCfi2Lib)
+
+	// Confirm that non-CFI variants do not add CFI flags
+	bazLibCflags := staticWithCfiLib.Rule("cc").Args["cFlags"]
+	if strings.Contains(bazLibCflags, "-fsanitize-cfi-cross-dso") {
+		t.Errorf("non-CFI variant of baz not expected to contain CFI flags ")
+	}
+}
diff --git a/java/aar.go b/java/aar.go
index 47e6efa..f1b137d 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -1015,9 +1015,10 @@
 }
 
 type bazelAndroidLibraryImport struct {
-	Aar     bazel.Label
-	Deps    bazel.LabelListAttribute
-	Exports bazel.LabelListAttribute
+	Aar         bazel.Label
+	Deps        bazel.LabelListAttribute
+	Exports     bazel.LabelListAttribute
+	Sdk_version bazel.StringAttribute
 }
 
 func (a *aapt) convertAaptAttrsWithBp2Build(ctx android.TopDownMutatorContext) *bazelAapt {
@@ -1059,9 +1060,10 @@
 		},
 		android.CommonAttributes{Name: name},
 		&bazelAndroidLibraryImport{
-			Aar:     aars.Includes[0],
-			Deps:    bazel.MakeLabelListAttribute(deps),
-			Exports: bazel.MakeLabelListAttribute(exports),
+			Aar:         aars.Includes[0],
+			Deps:        bazel.MakeLabelListAttribute(deps),
+			Exports:     bazel.MakeLabelListAttribute(exports),
+			Sdk_version: bazel.StringAttribute{Value: a.properties.Sdk_version},
 		},
 	)
 
@@ -1073,6 +1075,9 @@
 			javaLibraryAttributes: &javaLibraryAttributes{
 				Neverlink: bazel.BoolAttribute{Value: &neverlink},
 				Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+				javaCommonAttributes: &javaCommonAttributes{
+					Sdk_version: bazel.StringAttribute{Value: a.properties.Sdk_version},
+				},
 			},
 		},
 	)
@@ -1119,6 +1124,10 @@
 			javaLibraryAttributes: &javaLibraryAttributes{
 				Neverlink: bazel.BoolAttribute{Value: &neverlink},
 				Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+				javaCommonAttributes: &javaCommonAttributes{
+					Sdk_version:  bazel.StringAttribute{Value: a.deviceProperties.Sdk_version},
+					Java_version: bazel.StringAttribute{Value: a.properties.Java_version},
+				},
 			},
 		},
 	)
diff --git a/java/app.go b/java/app.go
index 52caf6d..03e2330 100755
--- a/java/app.go
+++ b/java/app.go
@@ -1565,6 +1565,9 @@
 
 		appAttrs.bazelAapt = &bazelAapt{Manifest: aapt.Manifest}
 		appAttrs.Deps = bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + ktName})
+		appAttrs.javaCommonAttributes = &javaCommonAttributes{
+			Sdk_version: commonAttrs.Sdk_version,
+		}
 	}
 
 	ctx.CreateBazelTargetModule(
diff --git a/java/app_import.go b/java/app_import.go
index c1de667..85b35eb 100644
--- a/java/app_import.go
+++ b/java/app_import.go
@@ -17,9 +17,10 @@
 // This file contains the module implementations for android_app_import and android_test_import.
 
 import (
-	"github.com/google/blueprint"
 	"reflect"
 
+	"github.com/google/blueprint"
+
 	"github.com/google/blueprint/proptools"
 
 	"android/soong/android"
@@ -177,10 +178,6 @@
 	}
 }
 
-func (a *AndroidAppImport) isPrebuiltFrameworkRes() bool {
-	return a.Name() == "prebuilt_framework-res"
-}
-
 func (a *AndroidAppImport) DepsMutator(ctx android.BottomUpMutatorContext) {
 	cert := android.SrcIsModule(String(a.properties.Certificate))
 	if cert != "" {
@@ -197,7 +194,7 @@
 		}
 	}
 
-	a.usesLibrary.deps(ctx, !a.isPrebuiltFrameworkRes())
+	a.usesLibrary.deps(ctx, true)
 }
 
 func (a *AndroidAppImport) uncompressEmbeddedJniLibs(
@@ -243,6 +240,10 @@
 }
 
 func (a *AndroidAppImport) generateAndroidBuildActions(ctx android.ModuleContext) {
+	if a.Name() == "prebuilt_framework-res" {
+		ctx.ModuleErrorf("prebuilt_framework-res found. This used to have special handling in soong, but was removed due to prebuilt_framework-res no longer existing. This check is to ensure it doesn't come back without readding the special handling.")
+	}
+
 	apexInfo := ctx.Provider(android.ApexInfoProvider).(android.ApexInfo)
 	if !apexInfo.IsForPlatform() {
 		a.hideApexVariantFromMake = true
@@ -278,14 +279,7 @@
 	var pathFragments []string
 	relInstallPath := String(a.properties.Relative_install_path)
 
-	if a.isPrebuiltFrameworkRes() {
-		// framework-res.apk is installed as system/framework/framework-res.apk
-		if relInstallPath != "" {
-			ctx.PropertyErrorf("relative_install_path", "Relative_install_path cannot be set for framework-res")
-		}
-		pathFragments = []string{"framework"}
-		a.preprocessed = true
-	} else if Bool(a.properties.Privileged) {
+	if Bool(a.properties.Privileged) {
 		pathFragments = []string{"priv-app", relInstallPath, a.BaseModuleName()}
 	} else if ctx.InstallInTestcases() {
 		pathFragments = []string{relInstallPath, a.BaseModuleName(), ctx.DeviceConfig().DeviceArch()}
@@ -323,13 +317,7 @@
 
 	// Sign or align the package if package has not been preprocessed
 
-	if a.isPrebuiltFrameworkRes() {
-		a.outputFile = srcApk
-		a.certificate, certificates = processMainCert(a.ModuleBase, String(a.properties.Certificate), certificates, ctx)
-		if len(certificates) != 1 {
-			ctx.ModuleErrorf("Unexpected number of certificates were extracted: %q", certificates)
-		}
-	} else if a.preprocessed {
+	if a.preprocessed {
 		a.outputFile = srcApk
 		a.certificate = PresignedCertificate
 	} else if !Bool(a.properties.Presigned) {
diff --git a/java/app_import_test.go b/java/app_import_test.go
index 528fffe..8093024 100644
--- a/java/app_import_test.go
+++ b/java/app_import_test.go
@@ -505,67 +505,6 @@
 	}
 }
 
-func TestAndroidAppImport_frameworkRes(t *testing.T) {
-	ctx, _ := testJava(t, `
-		android_app_import {
-			name: "framework-res",
-			certificate: "platform",
-			apk: "package-res.apk",
-			prefer: true,
-			export_package_resources: true,
-			// Disable dexpreopt and verify_uses_libraries check as the app
-			// contains no Java code to be dexpreopted.
-			enforce_uses_libs: false,
-			dex_preopt: {
-				enabled: false,
-			},
-		}
-		`)
-
-	mod := ctx.ModuleForTests("prebuilt_framework-res", "android_common").Module()
-	a := mod.(*AndroidAppImport)
-
-	if !a.preprocessed {
-		t.Errorf("prebuilt framework-res is not preprocessed")
-	}
-
-	expectedInstallPath := "out/soong/target/product/test_device/system/framework/framework-res.apk"
-
-	android.AssertPathRelativeToTopEquals(t, "prebuilt framework-res install location", expectedInstallPath, a.dexpreopter.installPath)
-
-	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
-
-	expectedPath := "."
-	// From apk property above, in the root of the source tree.
-	expectedPrebuiltModuleFile := "package-res.apk"
-	// Verify that the apk is preprocessed: The export package is the same
-	// as the prebuilt.
-	expectedSoongResourceExportPackage := expectedPrebuiltModuleFile
-
-	actualPath := entries.EntryMap["LOCAL_PATH"]
-	actualPrebuiltModuleFile := entries.EntryMap["LOCAL_PREBUILT_MODULE_FILE"]
-	actualSoongResourceExportPackage := entries.EntryMap["LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE"]
-
-	if len(actualPath) != 1 {
-		t.Errorf("LOCAL_PATH incorrect len %d", len(actualPath))
-	} else if actualPath[0] != expectedPath {
-		t.Errorf("LOCAL_PATH mismatch, actual: %s, expected: %s", actualPath[0], expectedPath)
-	}
-
-	if len(actualPrebuiltModuleFile) != 1 {
-		t.Errorf("LOCAL_PREBUILT_MODULE_FILE incorrect len %d", len(actualPrebuiltModuleFile))
-	} else if actualPrebuiltModuleFile[0] != expectedPrebuiltModuleFile {
-		t.Errorf("LOCAL_PREBUILT_MODULE_FILE mismatch, actual: %s, expected: %s", actualPrebuiltModuleFile[0], expectedPrebuiltModuleFile)
-	}
-
-	if len(actualSoongResourceExportPackage) != 1 {
-		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE incorrect len %d", len(actualSoongResourceExportPackage))
-	} else if actualSoongResourceExportPackage[0] != expectedSoongResourceExportPackage {
-		t.Errorf("LOCAL_SOONG_RESOURCE_EXPORT_PACKAGE mismatch, actual: %s, expected: %s", actualSoongResourceExportPackage[0], expectedSoongResourceExportPackage)
-	}
-	android.AssertStringEquals(t, "unexpected LOCAL_SOONG_MODULE_TYPE", "android_app_import", entries.EntryMap["LOCAL_SOONG_MODULE_TYPE"][0])
-}
-
 func TestAndroidAppImport_relativeInstallPath(t *testing.T) {
 	bp := `
 		android_app_import {
@@ -582,13 +521,6 @@
 		}
 
 		android_app_import {
-			name: "framework-res",
-			apk: "prebuilts/apk/app.apk",
-			presigned: true,
-			prefer: true,
-		}
-
-		android_app_import {
 			name: "privileged_relative_install_path",
 			apk: "prebuilts/apk/app.apk",
 			presigned: true,
@@ -612,11 +544,6 @@
 			errorMessage:        "Install path is not correct for app when relative_install_path is present",
 		},
 		{
-			name:                "prebuilt_framework-res",
-			expectedInstallPath: "out/soong/target/product/test_device/system/framework/framework-res.apk",
-			errorMessage:        "Install path is not correct for framework-res",
-		},
-		{
 			name:                "privileged_relative_install_path",
 			expectedInstallPath: "out/soong/target/product/test_device/system/priv-app/my/path/privileged_relative_install_path/privileged_relative_install_path.apk",
 			errorMessage:        "Install path is not correct for privileged app when relative_install_path is present",
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 656c866..3581040 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -21,6 +21,8 @@
 	"android/soong/android"
 	"android/soong/bazel"
 	"android/soong/dexpreopt"
+
+	"github.com/google/blueprint/proptools"
 )
 
 type DeviceHostConverter struct {
@@ -191,7 +193,7 @@
 }
 
 type bazelDeviceHostConverterAttributes struct {
-	Deps bazel.LabelListAttribute
+	Exports bazel.LabelListAttribute
 }
 
 func (d *DeviceHostConverter) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
@@ -202,13 +204,15 @@
 		},
 		android.CommonAttributes{Name: d.Name()},
 		&bazelDeviceHostConverterAttributes{
-			Deps: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, d.properties.Libs)),
+			Exports: bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, d.properties.Libs)),
 		},
 	)
-	neverlinkProp := true
 	neverLinkAttrs := &javaLibraryAttributes{
 		Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + d.Name()}),
-		Neverlink: bazel.BoolAttribute{Value: &neverlinkProp},
+		Neverlink: bazel.BoolAttribute{Value: proptools.BoolPtr(true)},
+		javaCommonAttributes: &javaCommonAttributes{
+			Sdk_version: bazel.StringAttribute{Value: proptools.StringPtr("none")},
+		},
 	}
 	ctx.CreateBazelTargetModule(
 		javaLibraryBazelTargetModuleProperties(),
diff --git a/java/java.go b/java/java.go
index d400b0c..2de4ea9 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2740,9 +2740,11 @@
 type javaCommonAttributes struct {
 	*javaResourcesAttributes
 	*kotlinAttributes
-	Srcs      bazel.LabelListAttribute
-	Plugins   bazel.LabelListAttribute
-	Javacopts bazel.StringListAttribute
+	Srcs         bazel.LabelListAttribute
+	Plugins      bazel.LabelListAttribute
+	Javacopts    bazel.StringListAttribute
+	Sdk_version  bazel.StringAttribute
+	Java_version bazel.StringAttribute
 }
 
 type javaDependencyLabels struct {
@@ -2873,10 +2875,6 @@
 	if m.properties.Javacflags != nil {
 		javacopts = append(javacopts, m.properties.Javacflags...)
 	}
-	if m.properties.Java_version != nil {
-		javaVersion := normalizeJavaVersion(ctx, *m.properties.Java_version).String()
-		javacopts = append(javacopts, fmt.Sprintf("-source %s -target %s", javaVersion, javaVersion))
-	}
 
 	epEnabled := m.properties.Errorprone.Enabled
 	//TODO(b/227504307) add configuration that depends on RUN_ERROR_PRONE environment variable
@@ -2890,7 +2888,9 @@
 		Plugins: bazel.MakeLabelListAttribute(
 			android.BazelLabelForModuleDeps(ctx, m.properties.Plugins),
 		),
-		Javacopts: bazel.MakeStringListAttribute(javacopts),
+		Javacopts:    bazel.MakeStringListAttribute(javacopts),
+		Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
+		Sdk_version:  bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
 	}
 
 	for axis, configToProps := range archVariantProps {
@@ -2981,19 +2981,9 @@
 	deps := depLabels.Deps
 	if !commonAttrs.Srcs.IsEmpty() {
 		deps.Append(depLabels.StaticDeps) // we should only append these if there are sources to use them
-
-		sdkVersion := m.SdkVersion(ctx)
-		if sdkVersion.Kind == android.SdkPublic && sdkVersion.ApiLevel == android.FutureApiLevel {
-			// TODO(b/220869005) remove forced dependency on current public android.jar
-			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:public_current_android_sdk_java_import"))
-		} else if sdkVersion.Kind == android.SdkSystem && sdkVersion.ApiLevel == android.FutureApiLevel {
-			// TODO(b/215230098) remove forced dependency on current public android.jar
-			deps.Add(bazel.MakeLabelAttribute("//prebuilts/sdk:system_current_android_sdk_java_import"))
-		}
 	} else if !deps.IsEmpty() {
 		ctx.ModuleErrorf("Module has direct dependencies but no sources. Bazel will not allow this.")
 	}
-
 	var props bazel.BazelTargetModuleProperties
 	attrs := &javaLibraryAttributes{
 		javaCommonAttributes: commonAttrs,
@@ -3013,6 +3003,10 @@
 	neverLinkAttrs := &javaLibraryAttributes{
 		Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
 		Neverlink: bazel.BoolAttribute{Value: &neverlinkProp},
+		javaCommonAttributes: &javaCommonAttributes{
+			Sdk_version:  bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
+			Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
+		},
 	}
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: name + "-neverlink"}, neverLinkAttrs)
 
@@ -3152,6 +3146,9 @@
 	neverlinkAttrs := &javaLibraryAttributes{
 		Neverlink: bazel.BoolAttribute{Value: &neverlink},
 		Exports:   bazel.MakeSingleLabelListAttribute(bazel.Label{Label: ":" + name}),
+		javaCommonAttributes: &javaCommonAttributes{
+			Sdk_version: bazel.StringAttribute{Value: proptools.StringPtr("none")},
+		},
 	}
 	ctx.CreateBazelTargetModule(
 		javaLibraryBazelTargetModuleProperties(),
diff --git a/java/proto.go b/java/proto.go
index 5280077..c732d98 100644
--- a/java/proto.go
+++ b/java/proto.go
@@ -143,7 +143,9 @@
 }
 
 type protoAttributes struct {
-	Deps bazel.LabelListAttribute
+	Deps         bazel.LabelListAttribute
+	Sdk_version  bazel.StringAttribute
+	Java_version bazel.StringAttribute
 }
 
 func bp2buildProto(ctx android.Bp2buildMutatorContext, m *Module, protoSrcs bazel.LabelListAttribute) *bazel.Label {
@@ -175,8 +177,11 @@
 	}
 
 	protoLabel := bazel.Label{Label: ":" + m.Name() + "_proto"}
-	var protoAttrs protoAttributes
-	protoAttrs.Deps.SetValue(bazel.LabelList{Includes: []bazel.Label{protoLabel}})
+	protoAttrs := &protoAttributes{
+		Deps:         bazel.MakeSingleLabelListAttribute(protoLabel),
+		Java_version: bazel.StringAttribute{Value: m.properties.Java_version},
+		Sdk_version:  bazel.StringAttribute{Value: m.deviceProperties.Sdk_version},
+	}
 
 	name := m.Name() + suffix
 
@@ -186,7 +191,7 @@
 			Bzl_load_location: "//build/bazel/rules/java:proto.bzl",
 		},
 		android.CommonAttributes{Name: name},
-		&protoAttrs)
+		protoAttrs)
 
 	return &bazel.Label{Label: ":" + name}
 }
diff --git a/mk2rbc/soong_variables.go b/mk2rbc/soong_variables.go
index a52ec4f..7a6aa5f 100644
--- a/mk2rbc/soong_variables.go
+++ b/mk2rbc/soong_variables.go
@@ -67,7 +67,11 @@
 	var valueType starlarkType
 	switch typeString {
 	case "bool":
-		valueType = starlarkTypeBool
+		// TODO: We run into several issues later on if we type this as a bool:
+		//    - We still assign bool-typed variables to strings
+		//    - When emitting the final results as make code, some bool's false values have to
+		//      be an empty string, and some have to be false in order to match the make variables.
+		valueType = starlarkTypeString
 	case "csv":
 		// Only PLATFORM_VERSION_ALL_CODENAMES, and it's a list
 		valueType = starlarkTypeList
diff --git a/python/binary.go b/python/binary.go
index 75135f3..a5db2f6 100644
--- a/python/binary.go
+++ b/python/binary.go
@@ -37,7 +37,7 @@
 	// this file must also be listed in srcs.
 	// If left unspecified, module name is used instead.
 	// If name doesn’t match any filename in srcs, main must be specified.
-	Main *string `android:"arch_variant"`
+	Main *string
 
 	// set the name of the output binary.
 	Stem *string `android:"arch_variant"`
diff --git a/python/bp2build.go b/python/bp2build.go
index bdac2dc..cd3f2a1 100644
--- a/python/bp2build.go
+++ b/python/bp2build.go
@@ -15,7 +15,6 @@
 package python
 
 import (
-	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -118,42 +117,19 @@
 	return attrs
 }
 
-func pythonLibBp2Build(ctx android.TopDownMutatorContext, m *PythonLibraryModule) {
-	// TODO(b/182306917): this doesn't fully handle all nested props versioned
-	// by the python version, which would have been handled by the version split
-	// mutator. This is sufficient for very simple python_library modules under
-	// Bionic.
+func (m *PythonLibraryModule) bp2buildPythonVersion(ctx android.TopDownMutatorContext) *string {
 	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
 	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
-	var python_version *string
 	if py2Enabled && !py3Enabled {
-		python_version = &pyVersion2
+		return &pyVersion2
 	} else if !py2Enabled && py3Enabled {
-		python_version = &pyVersion3
+		return &pyVersion3
 	} else if !py2Enabled && !py3Enabled {
 		ctx.ModuleErrorf("bp2build converter doesn't understand having neither py2 nor py3 enabled")
+		return &pyVersion3
 	} else {
-		// do nothing, since python_version defaults to PY2ANDPY3
+		return &pyVersion2And3
 	}
-
-	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
-
-	attrs := &bazelPythonLibraryAttributes{
-		Srcs:         baseAttrs.Srcs,
-		Deps:         baseAttrs.Deps,
-		Srcs_version: python_version,
-		Imports:      baseAttrs.Imports,
-	}
-
-	props := bazel.BazelTargetModuleProperties{
-		// Use the native py_library rule.
-		Rule_class: "py_library",
-	}
-
-	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: m.Name(),
-		Data: baseAttrs.Data,
-	}, attrs)
 }
 
 type bazelPythonBinaryAttributes struct {
@@ -164,43 +140,71 @@
 	Imports        bazel.StringListAttribute
 }
 
-func pythonBinaryBp2Build(ctx android.TopDownMutatorContext, m *PythonBinaryModule) {
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	// TODO(b/182306917): this doesn't fully handle all nested props versioned
+	// by the python version, which would have been handled by the version split
+	// mutator. This is sufficient for very simple python_library modules under
+	// Bionic.
+	baseAttrs := p.makeArchVariantBaseAttributes(ctx)
+	pyVersion := p.bp2buildPythonVersion(ctx)
+	if *pyVersion == pyVersion2And3 {
+		// Libraries default to python 2 and 3
+		pyVersion = nil
+	}
+
+	attrs := &bazelPythonLibraryAttributes{
+		Srcs:         baseAttrs.Srcs,
+		Deps:         baseAttrs.Deps,
+		Srcs_version: pyVersion,
+		Imports:      baseAttrs.Imports,
+	}
+
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_library rule.
+		Rule_class: "py_library",
+	}
+
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: p.Name(),
+		Data: baseAttrs.Data,
+	}, attrs)
+}
+
+func (p *PythonBinaryModule) bp2buildBinaryProperties(ctx android.TopDownMutatorContext) (*bazelPythonBinaryAttributes, bazel.LabelListAttribute) {
 	// TODO(b/182306917): this doesn't fully handle all nested props versioned
 	// by the python version, which would have been handled by the version split
 	// mutator. This is sufficient for very simple python_binary_host modules
 	// under Bionic.
-	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, false)
-	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
-	var python_version *string
-	if py3Enabled && py2Enabled {
-		panic(fmt.Errorf(
-			"error for '%s' module: bp2build's python_binary_host converter does not support "+
-				"converting a module that is enabled for both Python 2 and 3 at the same time.", m.Name()))
-	} else if py2Enabled {
-		python_version = &pyVersion2
-	} else {
-		// do nothing, since python_version defaults to PY3.
+
+	baseAttrs := p.makeArchVariantBaseAttributes(ctx)
+	pyVersion := p.bp2buildPythonVersion(ctx)
+	if *pyVersion == pyVersion3 {
+		// Binaries default to python 3
+		pyVersion = nil
+	} else if *pyVersion == pyVersion2And3 {
+		ctx.ModuleErrorf("error for '%s' module: bp2build's python_binary_host converter "+
+			"does not support converting a module that is enabled for both Python 2 and 3 at the "+
+			"same time.", p.Name())
 	}
 
-	baseAttrs := m.makeArchVariantBaseAttributes(ctx)
 	attrs := &bazelPythonBinaryAttributes{
 		Main:           nil,
 		Srcs:           baseAttrs.Srcs,
 		Deps:           baseAttrs.Deps,
-		Python_version: python_version,
+		Python_version: pyVersion,
 		Imports:        baseAttrs.Imports,
 	}
 
-	for _, propIntf := range m.GetProperties() {
-		if props, ok := propIntf.(*BinaryProperties); ok {
-			// main is optional.
-			if props.Main != nil {
-				main := android.BazelLabelForModuleSrcSingle(ctx, *props.Main)
-				attrs.Main = &main
-				break
-			}
-		}
+	// main is optional.
+	if p.binaryProperties.Main != nil {
+		main := android.BazelLabelForModuleSrcSingle(ctx, *p.binaryProperties.Main)
+		attrs.Main = &main
 	}
+	return attrs, baseAttrs.Data
+}
+
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	attrs, data := p.bp2buildBinaryProperties(ctx)
 
 	props := bazel.BazelTargetModuleProperties{
 		// Use the native py_binary rule.
@@ -208,19 +212,22 @@
 	}
 
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
-		Name: m.Name(),
-		Data: baseAttrs.Data,
+		Name: p.Name(),
+		Data: data,
 	}, attrs)
 }
 
-func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	pythonLibBp2Build(ctx, p)
-}
+func (p *PythonTestModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+	// Python tests are currently exactly the same as binaries, but with a different module type
+	attrs, data := p.bp2buildBinaryProperties(ctx)
 
-func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
-	pythonBinaryBp2Build(ctx, p)
-}
+	props := bazel.BazelTargetModuleProperties{
+		// Use the native py_binary rule.
+		Rule_class: "py_test",
+	}
 
-func (p *PythonTestModule) ConvertWithBp2build(_ android.TopDownMutatorContext) {
-	// Tests are currently unsupported
+	ctx.CreateBazelTargetModule(props, android.CommonAttributes{
+		Name: p.Name(),
+		Data: data,
+	}, attrs)
 }
diff --git a/python/python.go b/python/python.go
index c7c523d..1a12973 100644
--- a/python/python.go
+++ b/python/python.go
@@ -239,6 +239,7 @@
 	protoExt                 = ".proto"
 	pyVersion2               = "PY2"
 	pyVersion3               = "PY3"
+	pyVersion2And3           = "PY2ANDPY3"
 	internalPath             = "internal"
 )
 
diff --git a/rust/binary.go b/rust/binary.go
index 056888e..2de92c1 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -72,11 +72,14 @@
 func (binary *binaryDecorator) compilerFlags(ctx ModuleContext, flags Flags) Flags {
 	flags = binary.baseCompiler.compilerFlags(ctx, flags)
 
+	if ctx.Os().Linux() {
+		flags.LinkFlags = append(flags.LinkFlags, "-Wl,--gc-sections")
+	}
+
 	if ctx.toolchain().Bionic() {
 		// no-undefined-version breaks dylib compilation since __rust_*alloc* functions aren't defined,
 		// but we can apply this to binaries.
 		flags.LinkFlags = append(flags.LinkFlags,
-			"-Wl,--gc-sections",
 			"-Wl,-z,nocopyreloc",
 			"-Wl,--no-undefined-version")
 
@@ -136,7 +139,7 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
-	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects.Strings()...)
 
 	if binary.stripper.NeedsStrip(ctx) {
 		strippedOutputFile := outputFile
diff --git a/rust/binary_test.go b/rust/binary_test.go
index 7dac249..dd4f993 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -123,7 +123,7 @@
 			bootstrap: true,
 		}`)
 
-	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustLink")
 
 	flag := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker64"
 	if !strings.Contains(foo.Args["linkFlags"], flag) {
@@ -140,10 +140,11 @@
 		}`)
 
 	fizzOut := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
+	fizzOutLink := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustLink")
 	fizzMod := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Module().(*Module)
 
 	flags := fizzOut.Args["rustcFlags"]
-	linkFlags := fizzOut.Args["linkFlags"]
+	linkFlags := fizzOutLink.Args["linkFlags"]
 	if !strings.Contains(flags, "-C relocation-model=static") {
 		t.Errorf("static binary missing '-C relocation-model=static' in rustcFlags, found: %#v", flags)
 	}
@@ -173,7 +174,7 @@
 			name: "libfoo",
 		}`)
 
-	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
+	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustLink")
 	linkFlags := fizzBuzz.Args["linkFlags"]
 	if !strings.Contains(linkFlags, "/libfoo.so") {
 		t.Errorf("missing shared dependency 'libfoo.so' in linkFlags: %#v", linkFlags)
diff --git a/rust/builder.go b/rust/builder.go
index 0aef13d..0aa2225 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -26,14 +26,14 @@
 
 var (
 	_     = pctx.SourcePathVariable("rustcCmd", "${config.RustBin}/rustc")
+	_     = pctx.SourcePathVariable("mkcraterspCmd", "build/soong/scripts/mkcratersp.py")
 	rustc = pctx.AndroidStaticRule("rustc",
 		blueprint.RuleParams{
 			Command: "$envVars $rustcCmd " +
-				"-C linker=${config.RustLinker} " +
-				"-C link-args=\"${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}\" " +
+				"-C linker=$mkcraterspCmd " +
 				"--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" +
 				" && grep \"^$out:\" $out.d.raw > $out.d",
-			CommandDeps: []string{"$rustcCmd"},
+			CommandDeps: []string{"$rustcCmd", "$mkcraterspCmd"},
 			// Rustc deps-info writes out make compatible dep files: https://github.com/rust-lang/rust/issues/7633
 			// Rustc emits unneeded dependency lines for the .d and input .rs files.
 			// Those extra lines cause ninja warning:
@@ -42,7 +42,12 @@
 			Deps:    blueprint.DepsGCC,
 			Depfile: "$out.d",
 		},
-		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
+		"rustcFlags", "libFlags", "envVars")
+	rustLink = pctx.AndroidStaticRule("rustLink",
+		blueprint.RuleParams{
+			Command: "${config.RustLinker} -o $out ${crtBegin} ${config.RustLinkerArgs} @$in ${linkFlags} ${crtEnd}",
+		},
+		"linkFlags", "crtBegin", "crtEnd")
 
 	_       = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
 	rustdoc = pctx.AndroidStaticRule("rustdoc",
@@ -101,14 +106,13 @@
 				`KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` +
 				`$rustExtractor $envVars ` +
 				`$rustcCmd ` +
-				`-C linker=${config.RustLinker} ` +
-				`-C link-args="${crtBegin} ${config.RustLinkerArgs} ${linkFlags} ${crtEnd}" ` +
+				`-C linker=true ` +
 				`$in ${libFlags} $rustcFlags`,
 			CommandDeps:    []string{"$rustExtractor", "$kytheVnames"},
 			Rspfile:        "${out}.rsp",
 			RspfileContent: "$in",
 		},
-		"rustcFlags", "linkFlags", "libFlags", "crtBegin", "crtEnd", "envVars")
+		"rustcFlags", "libFlags", "envVars")
 )
 
 type buildOutput struct {
@@ -220,11 +224,9 @@
 	outputFile android.WritablePath, crateType string) buildOutput {
 
 	var inputs android.Paths
-	var implicits android.Paths
-	var orderOnly android.Paths
+	var implicits, linkImplicits, linkOrderOnly android.Paths
 	var output buildOutput
 	var rustcFlags, linkFlags []string
-	var implicitOutputs android.WritablePaths
 
 	output.outputFile = outputFile
 	crateName := ctx.RustModule().CrateName()
@@ -281,15 +283,15 @@
 	implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
 	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
 	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
-	implicits = append(implicits, deps.StaticLibs...)
-	implicits = append(implicits, deps.SharedLibDeps...)
-	implicits = append(implicits, deps.srcProviderFiles...)
 	implicits = append(implicits, deps.AfdoProfiles...)
+	implicits = append(implicits, deps.srcProviderFiles...)
+	implicits = append(implicits, deps.WholeStaticLibs...)
 
-	implicits = append(implicits, deps.CrtBegin...)
-	implicits = append(implicits, deps.CrtEnd...)
+	linkImplicits = append(linkImplicits, deps.LibDeps...)
+	linkImplicits = append(linkImplicits, deps.CrtBegin...)
+	linkImplicits = append(linkImplicits, deps.CrtEnd...)
 
-	orderOnly = append(orderOnly, deps.SharedLibs...)
+	linkOrderOnly = append(linkOrderOnly, deps.linkObjects...)
 
 	if len(deps.SrcDeps) > 0 {
 		moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
@@ -328,16 +330,16 @@
 		}
 	}
 
+	envVars = append(envVars, "AR=${cc_config.ClangBin}/llvm-ar")
+
 	if flags.Clippy {
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
 		ctx.Build(pctx, android.BuildParams{
-			Rule:            clippyDriver,
-			Description:     "clippy " + main.Rel(),
-			Output:          clippyFile,
-			ImplicitOutputs: nil,
-			Inputs:          inputs,
-			Implicits:       implicits,
-			OrderOnly:       orderOnly,
+			Rule:        clippyDriver,
+			Description: "clippy " + main.Rel(),
+			Output:      clippyFile,
+			Inputs:      inputs,
+			Implicits:   implicits,
 			Args: map[string]string{
 				"rustcFlags":  strings.Join(rustcFlags, " "),
 				"libFlags":    strings.Join(libFlags, " "),
@@ -349,24 +351,41 @@
 		implicits = append(implicits, clippyFile)
 	}
 
+	rustcOutputFile := outputFile
+	usesLinker := crateType == "bin" || crateType == "dylib" || crateType == "cdylib" || crateType == "proc-macro"
+	if usesLinker {
+		rustcOutputFile = android.PathForModuleOut(ctx, outputFile.Base()+".rsp")
+	}
+
 	ctx.Build(pctx, android.BuildParams{
-		Rule:            rustc,
-		Description:     "rustc " + main.Rel(),
-		Output:          outputFile,
-		ImplicitOutputs: implicitOutputs,
-		Inputs:          inputs,
-		Implicits:       implicits,
-		OrderOnly:       orderOnly,
+		Rule:        rustc,
+		Description: "rustc " + main.Rel(),
+		Output:      rustcOutputFile,
+		Inputs:      inputs,
+		Implicits:   implicits,
 		Args: map[string]string{
 			"rustcFlags": strings.Join(rustcFlags, " "),
-			"linkFlags":  strings.Join(linkFlags, " "),
 			"libFlags":   strings.Join(libFlags, " "),
-			"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
-			"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
 			"envVars":    strings.Join(envVars, " "),
 		},
 	})
 
+	if usesLinker {
+		ctx.Build(pctx, android.BuildParams{
+			Rule:        rustLink,
+			Description: "rustLink " + main.Rel(),
+			Output:      outputFile,
+			Inputs:      android.Paths{rustcOutputFile},
+			Implicits:   linkImplicits,
+			OrderOnly:   linkOrderOnly,
+			Args: map[string]string{
+				"linkFlags": strings.Join(linkFlags, " "),
+				"crtBegin":  strings.Join(deps.CrtBegin.Strings(), " "),
+				"crtEnd":    strings.Join(deps.CrtEnd.Strings(), " "),
+			},
+		})
+	}
+
 	if flags.EmitXrefs {
 		kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
 		ctx.Build(pctx, android.BuildParams{
@@ -375,13 +394,9 @@
 			Output:      kytheFile,
 			Inputs:      inputs,
 			Implicits:   implicits,
-			OrderOnly:   orderOnly,
 			Args: map[string]string{
 				"rustcFlags": strings.Join(rustcFlags, " "),
-				"linkFlags":  strings.Join(linkFlags, " "),
 				"libFlags":   strings.Join(libFlags, " "),
-				"crtBegin":   strings.Join(deps.CrtBegin.Strings(), " "),
-				"crtEnd":     strings.Join(deps.CrtEnd.Strings(), " "),
 				"envVars":    strings.Join(envVars, " "),
 			},
 		})
diff --git a/rust/coverage.go b/rust/coverage.go
index bc6504d..5216d60 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -65,7 +65,7 @@
 			"-C instrument-coverage", "-g")
 		flags.LinkFlags = append(flags.LinkFlags,
 			profileInstrFlag, "-g", coverage.OutputFile().Path().String(), "-Wl,--wrap,open")
-		deps.StaticLibs = append(deps.StaticLibs, coverage.OutputFile().Path())
+		deps.LibDeps = append(deps.LibDeps, coverage.OutputFile().Path())
 
 		// no_std modules are missing libprofiler_builtins which provides coverage, so we need to add it as a dependency.
 		if rustModule, ok := ctx.Module().(*Module); ok && rustModule.compiler.noStdlibs() {
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 0f599d7..64077cf 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -55,6 +55,10 @@
 	libbarNoCov := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
 	fizzCov := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
 	buzzNoCov := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
+	libfooCovLink := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustLink")
+	libbarNoCovLink := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustLink")
+	fizzCovLink := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustLink")
+	buzzNoCovLink := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustLink")
 
 	rustcCoverageFlags := []string{"-C instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
@@ -80,17 +84,17 @@
 		missingErrorStr := "missing rust linker flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rust linker flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
 
-		if !strings.Contains(fizzCov.Args["linkFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["linkFlags"])
+		if !strings.Contains(fizzCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCovLink.Args["linkFlags"])
 		}
-		if !strings.Contains(libfooCov.Args["linkFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["linkFlags"])
+		if !strings.Contains(libfooCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCovLink.Args["linkFlags"])
 		}
-		if strings.Contains(buzzNoCov.Args["linkFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["linkFlags"])
+		if strings.Contains(buzzNoCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCovLink.Args["linkFlags"])
 		}
-		if strings.Contains(libbarNoCov.Args["linkFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["linkFlags"])
+		if strings.Contains(libbarNoCovLink.Args["linkFlags"], flag) {
+			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCovLink.Args["linkFlags"])
 		}
 	}
 
@@ -103,7 +107,7 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustLink")
 	if !strings.Contains(fizz.Args["linkFlags"], "libprofile-clang-extras.a") {
 		t.Fatalf("missing expected coverage 'libprofile-clang-extras' dependency in linkFlags: %#v", fizz.Args["linkFlags"])
 	}
diff --git a/rust/library.go b/rust/library.go
index bc9c9aa..a3a5672 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -520,7 +520,7 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
-	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects...)
+	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects.Strings()...)
 
 	if library.dylib() {
 		// We need prefer-dynamic for now to avoid linking in the static stdlib. See:
@@ -543,6 +543,7 @@
 	if library.rlib() || library.dylib() {
 		library.flagExporter.exportLinkDirs(deps.linkDirs...)
 		library.flagExporter.exportLinkObjects(deps.linkObjects...)
+		library.flagExporter.exportLibDeps(deps.LibDeps...)
 	}
 
 	if library.static() || library.shared() {
diff --git a/rust/library_test.go b/rust/library_test.go
index e3e4d0f..d4b525f 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -148,7 +148,7 @@
 
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
 
-	libfooOutput := libfoo.Rule("rustc")
+	libfooOutput := libfoo.Rule("rustLink")
 	if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
 		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v",
 			libfooOutput.Args["linkFlags"])
diff --git a/rust/rust.go b/rust/rust.go
index 56b4631..7b520cd 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -420,13 +420,12 @@
 }
 
 type PathDeps struct {
-	DyLibs        RustLibraries
-	RLibs         RustLibraries
-	SharedLibs    android.Paths
-	SharedLibDeps android.Paths
-	StaticLibs    android.Paths
-	ProcMacros    RustLibraries
-	AfdoProfiles  android.Paths
+	DyLibs          RustLibraries
+	RLibs           RustLibraries
+	LibDeps         android.Paths
+	WholeStaticLibs android.Paths
+	ProcMacros      RustLibraries
+	AfdoProfiles    android.Paths
 
 	// depFlags and depLinkFlags are rustc and linker (clang) flags.
 	depFlags     []string
@@ -435,7 +434,7 @@
 	// linkDirs are link paths passed via -L to rustc. linkObjects are objects passed directly to the linker.
 	// Both of these are exported and propagate to dependencies.
 	linkDirs    []string
-	linkObjects []string
+	linkObjects android.Paths
 
 	// Used by bindgen modules which call clang
 	depClangFlags         []string
@@ -498,7 +497,7 @@
 
 type exportedFlagsProducer interface {
 	exportLinkDirs(...string)
-	exportLinkObjects(...string)
+	exportLinkObjects(...android.Path)
 }
 
 type xref interface {
@@ -507,21 +506,27 @@
 
 type flagExporter struct {
 	linkDirs    []string
-	linkObjects []string
+	linkObjects android.Paths
+	libDeps     android.Paths
 }
 
 func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
 	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
 }
 
-func (flagExporter *flagExporter) exportLinkObjects(flags ...string) {
-	flagExporter.linkObjects = android.FirstUniqueStrings(append(flagExporter.linkObjects, flags...))
+func (flagExporter *flagExporter) exportLinkObjects(flags ...android.Path) {
+	flagExporter.linkObjects = android.FirstUniquePaths(append(flagExporter.linkObjects, flags...))
+}
+
+func (flagExporter *flagExporter) exportLibDeps(paths ...android.Path) {
+	flagExporter.libDeps = android.FirstUniquePaths(append(flagExporter.libDeps, paths...))
 }
 
 func (flagExporter *flagExporter) setProvider(ctx ModuleContext) {
 	ctx.SetProvider(FlagExporterInfoProvider, FlagExporterInfo{
 		LinkDirs:    flagExporter.linkDirs,
 		LinkObjects: flagExporter.linkObjects,
+		LibDeps:     flagExporter.libDeps,
 	})
 }
 
@@ -534,7 +539,8 @@
 type FlagExporterInfo struct {
 	Flags       []string
 	LinkDirs    []string // TODO: this should be android.Paths
-	LinkObjects []string // TODO: this should be android.Paths
+	LinkObjects android.Paths
+	LibDeps     android.Paths
 }
 
 var FlagExporterInfoProvider = blueprint.NewProvider(FlagExporterInfo{})
@@ -1250,6 +1256,7 @@
 				depPaths.linkDirs = append(depPaths.linkDirs, exportedInfo.LinkDirs...)
 				depPaths.depFlags = append(depPaths.depFlags, exportedInfo.Flags...)
 				depPaths.linkObjects = append(depPaths.linkObjects, exportedInfo.LinkObjects...)
+				depPaths.LibDeps = append(depPaths.LibDeps, exportedInfo.LibDeps...)
 			}
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
@@ -1293,6 +1300,7 @@
 						depPaths.depLinkFlags = append(depPaths.depLinkFlags, []string{"-Wl,--whole-archive", linkObject.Path().String(), "-Wl,--no-whole-archive"}...)
 					} else if libName, ok := libNameFromFilePath(linkObject.Path()); ok {
 						depPaths.depFlags = append(depPaths.depFlags, "-lstatic="+libName)
+						depPaths.WholeStaticLibs = append(depPaths.WholeStaticLibs, linkObject.Path())
 					} 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())
 					}
@@ -1300,7 +1308,7 @@
 
 				// Add this to linkObjects to pass the library directly to the linker as well. This propagates
 				// to dependencies to avoid having to redeclare static libraries for dependents of the dylib variant.
-				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 
 				exportedInfo := ctx.OtherModuleProvider(dep, cc.FlagExporterInfoProvider).(cc.FlagExporterInfo)
@@ -1326,7 +1334,7 @@
 				linkPath = linkPathFromFilePath(linkObject.Path())
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
-				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.String())
+				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
 				depPaths.depIncludePaths = append(depPaths.depIncludePaths, exportedInfo.IncludeDirs...)
 				depPaths.depSystemIncludePaths = append(depPaths.depSystemIncludePaths, exportedInfo.SystemIncludeDirs...)
 				depPaths.depClangFlags = append(depPaths.depClangFlags, exportedInfo.Flags...)
@@ -1352,7 +1360,9 @@
 			// Make sure these dependencies are propagated
 			if lib, ok := mod.compiler.(exportedFlagsProducer); ok && exportDep {
 				lib.exportLinkDirs(linkPath)
-				lib.exportLinkObjects(linkObject.String())
+				if linkObject.Valid() {
+					lib.exportLinkObjects(linkObject.Path())
+				}
 			}
 		} else {
 			switch {
@@ -1384,19 +1394,16 @@
 		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
 	}
 
-	var staticLibDepFiles android.Paths
+	var libDepFiles android.Paths
 	for _, dep := range directStaticLibDeps {
-		staticLibDepFiles = append(staticLibDepFiles, dep.OutputFile().Path())
+		libDepFiles = append(libDepFiles, dep.OutputFile().Path())
 	}
 
-	var sharedLibFiles android.Paths
-	var sharedLibDepFiles android.Paths
 	for _, dep := range directSharedLibDeps {
-		sharedLibFiles = append(sharedLibFiles, dep.SharedLibrary)
 		if dep.TableOfContents.Valid() {
-			sharedLibDepFiles = append(sharedLibDepFiles, dep.TableOfContents.Path())
+			libDepFiles = append(libDepFiles, dep.TableOfContents.Path())
 		} else {
-			sharedLibDepFiles = append(sharedLibDepFiles, dep.SharedLibrary)
+			libDepFiles = append(libDepFiles, dep.SharedLibrary)
 		}
 	}
 
@@ -1412,15 +1419,13 @@
 
 	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
 	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
-	depPaths.SharedLibs = append(depPaths.SharedLibs, sharedLibFiles...)
-	depPaths.SharedLibDeps = append(depPaths.SharedLibDeps, sharedLibDepFiles...)
-	depPaths.StaticLibs = append(depPaths.StaticLibs, staticLibDepFiles...)
+	depPaths.LibDeps = append(depPaths.LibDeps, libDepFiles...)
 	depPaths.ProcMacros = append(depPaths.ProcMacros, procMacroDepFiles...)
 	depPaths.SrcDeps = append(depPaths.SrcDeps, srcProviderDepFiles...)
 
 	// Dedup exported flags from dependencies
 	depPaths.linkDirs = android.FirstUniqueStrings(depPaths.linkDirs)
-	depPaths.linkObjects = android.FirstUniqueStrings(depPaths.linkObjects)
+	depPaths.linkObjects = android.FirstUniquePaths(depPaths.linkObjects)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
diff --git a/rust/rust_test.go b/rust/rust_test.go
index e8e5800..2a38b89 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -258,6 +258,7 @@
 	`)
 	module := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Module().(*Module)
 	rustc := ctx.ModuleForTests("librlib", "linux_glibc_x86_64_rlib_rlib-std").Rule("rustc")
+	rustLink := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustLink")
 
 	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 	if !android.InList("libdylib", module.Properties.AndroidMkDylibs) {
@@ -284,16 +285,16 @@
 		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
 	}
 
-	if !strings.Contains(rustc.Args["linkFlags"], "cc_stubs_dep.so") {
-		t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustc.Args["linkFlags"])
+	if !strings.Contains(rustLink.Args["linkFlags"], "cc_stubs_dep.so") {
+		t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustLink.Args["linkFlags"])
 	}
 
-	if !android.SuffixInList(rustc.OrderOnly.Strings(), "cc_stubs_dep.so") {
-		t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustc.OrderOnly.Strings())
+	if !android.SuffixInList(rustLink.OrderOnly.Strings(), "cc_stubs_dep.so") {
+		t.Errorf("shared cc dep not being passed as order-only to rustc %#v", rustLink.OrderOnly.Strings())
 	}
 
-	if !android.SuffixInList(rustc.Implicits.Strings(), "cc_stubs_dep.so.toc") {
-		t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustc.Implicits.Strings())
+	if !android.SuffixInList(rustLink.Implicits.Strings(), "cc_stubs_dep.so.toc") {
+		t.Errorf("shared cc dep TOC not being passed as implicit to rustc %#v", rustLink.Implicits.Strings())
 	}
 }
 
diff --git a/rust/sanitize_test.go b/rust/sanitize_test.go
index d6a14b2..43e95f4 100644
--- a/rust/sanitize_test.go
+++ b/rust/sanitize_test.go
@@ -35,7 +35,7 @@
 	note_sync := "note_memtag_heap_sync"
 
 	found := None
-	implicits := m.Rule("rustc").Implicits
+	implicits := m.Rule("rustLink").Implicits
 	for _, lib := range implicits {
 		if strings.Contains(lib.Rel(), note_async) {
 			found = Async
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index e1b3c86..2e7a330 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -941,7 +941,7 @@
 	ctx := testRustVndkFsVersions(t, "", mockFS, "30", "current", "31")
 
 	// libclient uses libvndk.vndk.30.arm64, libvendor.vendor_static.30.arm64, libvendor_without_snapshot
-	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").Args["linkFlags"]
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustLink").Args["linkFlags"]
 	for _, input := range [][]string{
 		[]string{sharedVariant, "libvndk.vndk.30.arm64"},
 		[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
@@ -997,7 +997,7 @@
 		t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkRlibName, expectedRustVendorSnapshotName)
 	}
 
-	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").Args["linkFlags"]
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustLink").Args["linkFlags"]
 	libVndkStaticOutputPaths := cc.GetOutputPaths(ctx, staticVariant, []string{"libvndk.vendor_static.30.arm64"})
 	if !strings.Contains(binWithoutSnapshotLdFlags, libVndkStaticOutputPaths[0].String()) {
 		t.Errorf("libflags for bin_without_snapshot must contain %#v, but was %#v",
diff --git a/scripts/Android.bp b/scripts/Android.bp
index 26fe432..9367ff0 100644
--- a/scripts/Android.bp
+++ b/scripts/Android.bp
@@ -189,6 +189,7 @@
     libs: [
         "linker_config_proto",
     ],
+    visibility: ["//system/linkerconfig"],
 }
 
 python_test_host {
diff --git a/scripts/mkcratersp.py b/scripts/mkcratersp.py
new file mode 100755
index 0000000..86b4aa3
--- /dev/null
+++ b/scripts/mkcratersp.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""
+This script is used as a replacement for the Rust linker. It converts a linker
+command line into a rspfile that can be used during the link phase.
+"""
+
+import os
+import shutil
+import subprocess
+import sys
+
+def create_archive(out, objects, archives):
+  mricmd = f'create {out}\n'
+  for o in objects:
+    mricmd += f'addmod {o}\n'
+  for a in archives:
+    mricmd += f'addlib {a}\n'
+  mricmd += 'save\nend\n'
+  subprocess.run([os.getenv('AR'), '-M'], encoding='utf-8', input=mricmd, check=True)
+
+objects = []
+archives = []
+linkdirs = []
+libs = []
+temp_archives = []
+version_script = None
+
+for i, arg in enumerate(sys.argv):
+  if arg == '-o':
+    out = sys.argv[i+1]
+  if arg == '-L':
+    linkdirs.append(sys.argv[i+1])
+  if arg.startswith('-l') or arg == '-shared':
+    libs.append(arg)
+  if arg.startswith('-Wl,--version-script='):
+    version_script = arg[21:]
+  if arg[0] == '-':
+    continue
+  if arg.endswith('.o') or arg.endswith('.rmeta'):
+    objects.append(arg)
+  if arg.endswith('.rlib'):
+    if arg.startswith(os.getenv('TMPDIR')):
+      temp_archives.append(arg)
+    else:
+      archives.append(arg)
+
+create_archive(f'{out}.whole.a', objects, [])
+create_archive(f'{out}.a', [], temp_archives)
+
+with open(out, 'w') as f:
+  print(f'-Wl,--whole-archive', file=f)
+  print(f'{out}.whole.a', file=f)
+  print(f'-Wl,--no-whole-archive', file=f)
+  print(f'{out}.a', file=f)
+  for a in archives:
+    print(a, file=f)
+  for linkdir in linkdirs:
+    print(f'-L{linkdir}', file=f)
+  for l in libs:
+    print(l, file=f)
+  if version_script:
+    shutil.copyfile(version_script, f'{out}.version_script')
+    print(f'-Wl,--version-script={out}.version_script', file=f)
diff --git a/tests/mixed_mode_test.sh b/tests/mixed_mode_test.sh
index 7b3151b..05d3a66 100755
--- a/tests/mixed_mode_test.sh
+++ b/tests/mixed_mode_test.sh
@@ -63,4 +63,37 @@
   fi
 }
 
+function test_force_enabled_modules {
+  setup
+  # b/273910287 - test force enable modules
+  mkdir -p soong_tests/a/b
+  cat > soong_tests/a/b/Android.bp <<'EOF'
+genrule {
+    name: "touch-file",
+    out: ["fake-out.txt"],
+    cmd: "touch $(out)",
+    bazel_module: { bp2build_available: true },
+}
+
+genrule {
+    name: "unenabled-touch-file",
+    out: ["fake-out2.txt"],
+    cmd: "touch $(out)",
+    bazel_module: { bp2build_available: false },
+}
+EOF
+  run_soong --bazel-mode-staging --bazel-force-enabled-modules=touch-file nothing
+  local bazel_contained=`grep out/soong/.intermediates/soong_tests/a/b/touch-file/gen/fake-out.txt out/soong/build.ninja`
+  if [[ $bazel_contained == '' ]]; then
+    fail "Bazel actions not found for force-enabled module"
+  fi
+
+  local exit_code=`run_soong --bazel-force-enabled-modules=unenabled-touch-file nothing`
+
+  if [[ $exit_code -ne 1 ]]; then
+    fail "Expected failure due to force-enabling an unenabled module "
+  fi
+}
+
+
 scan_and_run_tests
\ No newline at end of file