Merge "Add keep sorted tags" into main
diff --git a/aconfig/Android.bp b/aconfig/Android.bp
index d2ddfdf..faa4ddb 100644
--- a/aconfig/Android.bp
+++ b/aconfig/Android.bp
@@ -32,6 +32,8 @@
         "aconfig_values_test.go",
         "aconfig_value_set_test.go",
         "java_aconfig_library_test.go",
+        "cc_aconfig_library_test.go",
+        "rust_aconfig_library_test.go",
     ],
     pluginFor: ["soong_build"],
 }
diff --git a/aconfig/aconfig_declarations.go b/aconfig/aconfig_declarations.go
index ed0961b..f19ddb8 100644
--- a/aconfig/aconfig_declarations.go
+++ b/aconfig/aconfig_declarations.go
@@ -168,7 +168,7 @@
 	Package string
 }
 
-func (module *DeclarationsModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (module *DeclarationsModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() != "aconfig_declarations" {
 		return
 	}
diff --git a/aconfig/aconfig_value_set.go b/aconfig/aconfig_value_set.go
index af9ddd3..cd178d4 100644
--- a/aconfig/aconfig_value_set.go
+++ b/aconfig/aconfig_value_set.go
@@ -96,7 +96,7 @@
 	Values bazel.LabelListAttribute
 }
 
-func (module *ValueSetModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (module *ValueSetModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() != "aconfig_value_set" {
 		return
 	}
diff --git a/aconfig/aconfig_values.go b/aconfig/aconfig_values.go
index 0aa6a72..03a930d 100644
--- a/aconfig/aconfig_values.go
+++ b/aconfig/aconfig_values.go
@@ -75,7 +75,7 @@
 	Package string
 }
 
-func (module *ValuesModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (module *ValuesModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() != "aconfig_values" {
 		return
 	}
diff --git a/aconfig/cc_aconfig_library.go b/aconfig/cc_aconfig_library.go
index 14090bc..ec86af7 100644
--- a/aconfig/cc_aconfig_library.go
+++ b/aconfig/cc_aconfig_library.go
@@ -17,7 +17,9 @@
 import (
 	"android/soong/android"
 	"android/soong/cc"
+
 	"github.com/google/blueprint"
+	"github.com/google/blueprint/proptools"
 
 	"fmt"
 	"strings"
@@ -32,6 +34,9 @@
 type CcAconfigLibraryProperties struct {
 	// name of the aconfig_declarations module to generate a library for
 	Aconfig_declarations string
+
+	// whether to generate test mode version of the library
+	Test *bool
 }
 
 type CcAconfigLibraryCallbacks struct {
@@ -113,6 +118,12 @@
 	}
 	declarations := ctx.OtherModuleProvider(declarationsModules[0], declarationsProviderKey).(declarationsProviderData)
 
+	var mode string
+	if proptools.Bool(this.properties.Test) {
+		mode = "test"
+	} else {
+		mode = "production"
+	}
 	ctx.Build(pctx, android.BuildParams{
 		Rule:  cppRule,
 		Input: declarations.IntermediatePath,
@@ -123,6 +134,7 @@
 		Description: "cc_aconfig_library",
 		Args: map[string]string{
 			"gendir": this.generatedDir.String(),
+			"mode":   mode,
 		},
 	})
 }
diff --git a/aconfig/cc_aconfig_library_test.go b/aconfig/cc_aconfig_library_test.go
new file mode 100644
index 0000000..6f17c75
--- /dev/null
+++ b/aconfig/cc_aconfig_library_test.go
@@ -0,0 +1,67 @@
+// 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 aconfig
+
+import (
+	"fmt"
+	"testing"
+
+	"android/soong/android"
+	"android/soong/cc"
+)
+
+var codegenModeTestData = []struct {
+	setting, expected string
+}{
+	{"", "production"},
+	{"test: false,", "production"},
+	{"test: true,", "test"},
+}
+
+func TestCCCodegenMode(t *testing.T) {
+	for _, testData := range codegenModeTestData {
+		testCCCodegenModeHelper(t, testData.setting, testData.expected)
+	}
+}
+
+func testCCCodegenModeHelper(t *testing.T, bpMode string, ruleMode string) {
+	t.Helper()
+	result := android.GroupFixturePreparers(
+		PrepareForTestWithAconfigBuildComponents,
+		cc.PrepareForTestWithCcDefaultModules).
+		ExtendWithErrorHandler(android.FixtureExpectsNoErrors).
+		RunTestWithBp(t, fmt.Sprintf(`
+			aconfig_declarations {
+				name: "my_aconfig_declarations",
+				package: "com.example.package",
+				srcs: ["foo.aconfig"],
+			}
+
+			cc_library {
+    		name: "server_configurable_flags",
+    		srcs: ["server_configurable_flags.cc"],
+			}
+
+			cc_aconfig_library {
+				name: "my_cc_aconfig_library",
+				aconfig_declarations: "my_aconfig_declarations",
+				%s
+			}
+		`, bpMode))
+
+	module := result.ModuleForTests("my_cc_aconfig_library", "android_arm64_armv8-a_shared")
+	rule := module.Rule("cc_aconfig_library")
+	android.AssertStringEquals(t, "rule must contain test mode", rule.Args["mode"], ruleMode)
+}
diff --git a/aconfig/init.go b/aconfig/init.go
index c14f8ae..3d62714 100644
--- a/aconfig/init.go
+++ b/aconfig/init.go
@@ -64,13 +64,14 @@
 			Command: `rm -rf ${gendir}` +
 				` && mkdir -p ${gendir}` +
 				` && ${aconfig} create-cpp-lib` +
+				`    --mode ${mode}` +
 				`    --cache ${in}` +
 				`    --out ${gendir}`,
 			CommandDeps: []string{
 				"$aconfig",
 				"$soong_zip",
 			},
-		}, "gendir")
+		}, "gendir", "mode")
 
 	rustRule = pctx.AndroidStaticRule("rust_aconfig_library",
 		blueprint.RuleParams{
diff --git a/aconfig/rust_aconfig_library_test.go b/aconfig/rust_aconfig_library_test.go
index 17385c3..90b09c8 100644
--- a/aconfig/rust_aconfig_library_test.go
+++ b/aconfig/rust_aconfig_library_test.go
@@ -50,11 +50,11 @@
 	}
 
 	for _, variant := range variants {
-		android.AssertStringEquals(
+		android.AssertStringListContains(
 			t,
 			"dylib variant builds from generated rust code",
+			variant.Rule("rustc").Implicits.RelativeToTop().Strings(),
 			"out/soong/.intermediates/libmy_rust_aconfig_library/android_arm64_armv8-a_source/gen/src/lib.rs",
-			variant.Rule("rustc").Inputs[0].RelativeToTop().String(),
 		)
 	}
 }
diff --git a/aidl_library/aidl_library.go b/aidl_library/aidl_library.go
index 7449d67..2c0aef7 100644
--- a/aidl_library/aidl_library.go
+++ b/aidl_library/aidl_library.go
@@ -64,7 +64,7 @@
 	Deps                bazel.LabelListAttribute
 }
 
-func (lib *AidlLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (lib *AidlLibrary) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
 		android.BazelLabelForModuleSrc(
 			ctx,
diff --git a/android/allowlists/allowlists.go b/android/allowlists/allowlists.go
index 3b64a6f..d70787d 100644
--- a/android/allowlists/allowlists.go
+++ b/android/allowlists/allowlists.go
@@ -433,6 +433,7 @@
 
 		"tools/apifinder":                             Bp2BuildDefaultTrue,
 		"tools/apksig":                                Bp2BuildDefaultTrue,
+		"tools/dexter/slicer":                         Bp2BuildDefaultTrueRecursively,
 		"tools/external_updater":                      Bp2BuildDefaultTrueRecursively,
 		"tools/metalava":                              Bp2BuildDefaultTrueRecursively,
 		"tools/platform-compat/java/android/compat":   Bp2BuildDefaultTrueRecursively,
@@ -915,6 +916,11 @@
 		"androidx.test.monitor-nodeps",
 		"androidx.test.annotation",
 		"androidx.test.annotation-nodeps",
+
+		// jni deps of an internal android_test (b/297405812)
+		"libdexmakerjvmtiagent",
+		"libopenjdkjvmti_headers",
+		"libstaticjvmtiagent",
 	}
 
 	Bp2buildModuleTypeAlwaysConvertList = []string{
diff --git a/android/bazel.go b/android/bazel.go
index e764b18..8634dab 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -162,7 +162,7 @@
 	// Modules must implement this function to be bp2build convertible. The function
 	// must either create at least one Bazel target module (using ctx.CreateBazelTargetModule or
 	// its related functions), or declare itself unconvertible using ctx.MarkBp2buildUnconvertible.
-	ConvertWithBp2build(ctx TopDownMutatorContext)
+	ConvertWithBp2build(ctx Bp2buildMutatorContext)
 
 	// namespacedVariableProps is a map from a soong config variable namespace
 	// (e.g. acme, android) to a map of interfaces{}, which are really
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index c0eabdd..4ac5840 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -588,7 +588,7 @@
 // For the first two cases, they are defined using the label attribute. For the third case,
 // it's defined with the string attribute.
 func BazelStringOrLabelFromProp(
-	ctx TopDownMutatorContext,
+	ctx Bp2buildMutatorContext,
 	propToDistinguish *string) (bazel.LabelAttribute, bazel.StringAttribute) {
 
 	var labelAttr bazel.LabelAttribute
diff --git a/android/bazel_test.go b/android/bazel_test.go
index 15d3a6b..e0145b5 100644
--- a/android/bazel_test.go
+++ b/android/bazel_test.go
@@ -469,7 +469,7 @@
 	return m
 }
 
-func (m *mixedBuildModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+func (m *mixedBuildModule) ConvertWithBp2build(ctx Bp2buildMutatorContext) {
 }
 
 func (m *mixedBuildModule) DepsMutator(ctx BottomUpMutatorContext) {
diff --git a/android/defaults.go b/android/defaults.go
index e0e6e5c..cc723f7 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -180,7 +180,7 @@
 
 // ConvertWithBp2build to fulfill Bazelable interface; however, at this time defaults module are
 // *NOT* converted with bp2build
-func (defaultable *DefaultsModuleBase) ConvertWithBp2build(ctx TopDownMutatorContext) {
+func (defaultable *DefaultsModuleBase) ConvertWithBp2build(ctx Bp2buildMutatorContext) {
 	// Defaults types are never convertible.
 	ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "")
 }
diff --git a/android/depset_generic.go b/android/depset_generic.go
index 45c1937..9f07596 100644
--- a/android/depset_generic.go
+++ b/android/depset_generic.go
@@ -95,6 +95,12 @@
 	}
 }
 
+// AddDirectToDepSet returns a new DepSet with additional elements added to its direct set.
+// The transitive sets remain untouched.
+func AddDirectToDepSet[T depSettableType](d *DepSet[T], direct ...T) *DepSet[T] {
+	return NewDepSet[T](d.order, Concat(d.direct, direct), d.transitive)
+}
+
 // DepSetBuilder is used to create an immutable DepSet.
 type DepSetBuilder[T depSettableType] struct {
 	order      DepSetOrder
@@ -188,3 +194,14 @@
 	}
 	return list
 }
+
+// ToListDirect returns the direct elements of a DepSet flattened to a list.
+func (d *DepSet[T]) ToListDirect() []T {
+	if d == nil {
+		return nil
+	}
+	list := make([]T, len(d.direct))
+	copy(list, d.direct)
+	list = firstUniqueInPlace(list)
+	return list
+}
diff --git a/android/filegroup.go b/android/filegroup.go
index 6cc9232..a4bbcae 100644
--- a/android/filegroup.go
+++ b/android/filegroup.go
@@ -93,7 +93,7 @@
 }
 
 // ConvertWithBp2build performs bp2build conversion of filegroup
-func (fg *fileGroup) ConvertWithBp2build(ctx TopDownMutatorContext) {
+func (fg *fileGroup) ConvertWithBp2build(ctx Bp2buildMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
 		BazelLabelForModuleSrcExcludes(ctx, fg.properties.Srcs, fg.properties.Exclude_srcs))
 
@@ -209,10 +209,10 @@
 }
 
 type FileGroupPath interface {
-	GetPath(ctx TopDownMutatorContext) string
+	GetPath(ctx Bp2buildMutatorContext) string
 }
 
-func (fg *fileGroup) GetPath(ctx TopDownMutatorContext) string {
+func (fg *fileGroup) GetPath(ctx Bp2buildMutatorContext) string {
 	if fg.properties.Path != nil {
 		return *fg.properties.Path
 	}
diff --git a/android/license.go b/android/license.go
index a09422b..76f5115 100644
--- a/android/license.go
+++ b/android/license.go
@@ -71,7 +71,7 @@
 	Visibility       []string
 }
 
-func (m *licenseModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+func (m *licenseModule) ConvertWithBp2build(ctx Bp2buildMutatorContext) {
 	attrs := &bazelLicenseAttributes{
 		License_kinds:    m.properties.License_kinds,
 		Copyright_notice: m.properties.Copyright_notice,
diff --git a/android/license_kind.go b/android/license_kind.go
index 24b91e4..78df938 100644
--- a/android/license_kind.go
+++ b/android/license_kind.go
@@ -50,7 +50,7 @@
 	Visibility []string
 }
 
-func (m *licenseKindModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+func (m *licenseKindModule) ConvertWithBp2build(ctx Bp2buildMutatorContext) {
 	attrs := &bazelLicenseKindAttributes{
 		Conditions: m.properties.Conditions,
 		Url:        m.properties.Url,
diff --git a/android/mutator.go b/android/mutator.go
index e185cce..336f8f7 100644
--- a/android/mutator.go
+++ b/android/mutator.go
@@ -15,9 +15,10 @@
 package android
 
 import (
+	"path/filepath"
+
 	"android/soong/bazel"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
-	"path/filepath"
 
 	"github.com/google/blueprint"
 )
@@ -230,37 +231,8 @@
 // A minimal context for Bp2build conversion
 type Bp2buildMutatorContext interface {
 	BazelConversionPathContext
-
-	CreateBazelTargetModule(bazel.BazelTargetModuleProperties, CommonAttributes, interface{})
-	CreateBazelTargetModuleWithRestrictions(bazel.BazelTargetModuleProperties, CommonAttributes, interface{}, bazel.BoolAttribute)
-}
-
-// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
-// into Bazel BUILD targets that should run prior to deps and conversion.
-func PreArchBp2BuildMutators(f RegisterMutatorFunc) {
-	bp2buildPreArchMutators = append(bp2buildPreArchMutators, f)
-}
-
-type BaseMutatorContext interface {
-	BaseModuleContext
-
-	// MutatorName returns the name that this mutator was registered with.
-	MutatorName() string
-
-	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
-	// AddDependency or OtherModuleName until after this mutator pass is complete.
-	Rename(name string)
-}
-
-type TopDownMutator func(TopDownMutatorContext)
-
-type TopDownMutatorContext interface {
 	BaseMutatorContext
 
-	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
-	// the specified property structs to it as if the properties were set in a blueprint file.
-	CreateModule(ModuleFactory, ...interface{}) Module
-
 	// CreateBazelTargetModule creates a BazelTargetModule by calling the
 	// factory method, just like in CreateModule, but also requires
 	// BazelTargetModuleProperties containing additional metadata for the
@@ -291,6 +263,34 @@
 	CreateBazelConfigSetting(csa bazel.ConfigSettingAttributes, ca CommonAttributes, dir string)
 }
 
+// PreArchBp2BuildMutators adds mutators to be register for converting Android Blueprint modules
+// into Bazel BUILD targets that should run prior to deps and conversion.
+func PreArchBp2BuildMutators(f RegisterMutatorFunc) {
+	bp2buildPreArchMutators = append(bp2buildPreArchMutators, f)
+}
+
+type BaseMutatorContext interface {
+	BaseModuleContext
+
+	// MutatorName returns the name that this mutator was registered with.
+	MutatorName() string
+
+	// Rename all variants of a module.  The new name is not visible to calls to ModuleName,
+	// AddDependency or OtherModuleName until after this mutator pass is complete.
+	Rename(name string)
+}
+
+type TopDownMutator func(TopDownMutatorContext)
+
+type TopDownMutatorContext interface {
+	BaseMutatorContext
+	Bp2buildMutatorContext
+
+	// CreateModule creates a new module by calling the factory method for the specified moduleType, and applies
+	// the specified property structs to it as if the properties were set in a blueprint file.
+	CreateModule(ModuleFactory, ...interface{}) Module
+}
+
 type topDownMutatorContext struct {
 	bp blueprint.TopDownMutatorContext
 	baseModuleContext
diff --git a/android/package.go b/android/package.go
index 7fbc700..ce0b150 100644
--- a/android/package.go
+++ b/android/package.go
@@ -54,7 +54,7 @@
 
 var _ Bazelable = &packageModule{}
 
-func (p *packageModule) ConvertWithBp2build(ctx TopDownMutatorContext) {
+func (p *packageModule) ConvertWithBp2build(ctx Bp2buildMutatorContext) {
 	defaultPackageMetadata := bazel.MakeLabelListAttribute(BazelLabelForModuleDeps(ctx, p.properties.Default_applicable_licenses))
 	// If METADATA file exists in the package, add it to package(default_package_metadata=) using a
 	// filegroup(name="default_metadata_file") which can be accessed later on each module in Bazel
diff --git a/android/paths.go b/android/paths.go
index 325a953..d4b1d6e 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -171,6 +171,9 @@
 	// Base returns the last element of the path
 	Base() string
 
+	// Dir returns a path pointing the directory containing the path
+	Dir() Path
+
 	// Rel returns the portion of the path relative to the directory it was created from.  For
 	// example, Rel on a PathsForModuleSrc would return the path relative to the module source
 	// directory, and OutputPath.Join("foo").Rel() would return "foo".
@@ -1012,6 +1015,12 @@
 	return filepath.Base(p.path)
 }
 
+func (p basePath) Dir() Path {
+	p.path = filepath.Dir(p.path)
+	p.rel = filepath.Dir(p.rel)
+	return p
+}
+
 func (p basePath) Rel() string {
 	if p.rel != "" {
 		return p.rel
@@ -1046,6 +1055,11 @@
 	return p
 }
 
+func (p SourcePath) Dir() Path {
+	p.basePath = p.basePath.Dir().(basePath)
+	return p
+}
+
 // safePathForSource is for paths that we expect are safe -- only for use by go
 // code that is embedding ninja variables in paths
 func safePathForSource(ctx PathContext, pathComponents ...string) (SourcePath, error) {
@@ -1248,6 +1262,12 @@
 	return p
 }
 
+func (p OutputPath) Dir() Path {
+	p.basePath = p.basePath.Dir().(basePath)
+	p.fullPath = filepath.Dir(p.fullPath)
+	return p
+}
+
 func (p OutputPath) WithoutRel() OutputPath {
 	p.basePath.rel = filepath.Base(p.basePath.path)
 	return p
@@ -1280,6 +1300,11 @@
 	basePath
 }
 
+func (p toolDepPath) Dir() Path {
+	p.basePath = p.basePath.Dir().(basePath)
+	return p
+}
+
 func (t toolDepPath) RelativeToTop() Path {
 	ensureTestOnly()
 	return t
@@ -1463,6 +1488,11 @@
 	OutputPath
 }
 
+func (p ModuleOutPath) Dir() Path {
+	p.OutputPath = p.OutputPath.Dir().(OutputPath)
+	return p
+}
+
 func (p ModuleOutPath) RelativeToTop() Path {
 	p.OutputPath = p.outputPathRelativeToTop()
 	return p
@@ -1507,6 +1537,11 @@
 	ModuleOutPath
 }
 
+func (p ModuleGenPath) Dir() Path {
+	p.ModuleOutPath = p.ModuleOutPath.Dir().(ModuleOutPath)
+	return p
+}
+
 func (p ModuleGenPath) RelativeToTop() Path {
 	p.OutputPath = p.outputPathRelativeToTop()
 	return p
@@ -1546,6 +1581,11 @@
 	ModuleOutPath
 }
 
+func (p ModuleObjPath) Dir() Path {
+	p.ModuleOutPath = p.ModuleOutPath.Dir().(ModuleOutPath)
+	return p
+}
+
 func (p ModuleObjPath) RelativeToTop() Path {
 	p.OutputPath = p.outputPathRelativeToTop()
 	return p
@@ -1570,6 +1610,11 @@
 	ModuleOutPath
 }
 
+func (p ModuleResPath) Dir() Path {
+	p.ModuleOutPath = p.ModuleOutPath.Dir().(ModuleOutPath)
+	return p
+}
+
 func (p ModuleResPath) RelativeToTop() Path {
 	p.OutputPath = p.outputPathRelativeToTop()
 	return p
@@ -1606,6 +1651,11 @@
 	makePath bool
 }
 
+func (p InstallPath) Dir() Path {
+	p.basePath = p.basePath.Dir().(basePath)
+	return p
+}
+
 // Will panic if called from outside a test environment.
 func ensureTestOnly() {
 	if PrefixInList(os.Args, "-test.") {
@@ -1922,6 +1972,11 @@
 	basePath
 }
 
+func (p PhonyPath) Dir() Path {
+	p.basePath = p.basePath.Dir().(basePath)
+	return p
+}
+
 func (p PhonyPath) writablePath() {}
 
 func (p PhonyPath) getSoongOutDir() string {
@@ -1947,6 +2002,11 @@
 	basePath
 }
 
+func (p testPath) Dir() Path {
+	p.basePath = p.basePath.Dir().(basePath)
+	return p
+}
+
 func (p testPath) RelativeToTop() Path {
 	ensureTestOnly()
 	return p
diff --git a/android/prebuilt_build_tool.go b/android/prebuilt_build_tool.go
index aeae20f..c00b22b 100644
--- a/android/prebuilt_build_tool.go
+++ b/android/prebuilt_build_tool.go
@@ -14,10 +14,14 @@
 
 package android
 
-import "path/filepath"
+import (
+	"path/filepath"
+
+	"github.com/google/blueprint"
+)
 
 func init() {
-	RegisterModuleType("prebuilt_build_tool", prebuiltBuildToolFactory)
+	RegisterModuleType("prebuilt_build_tool", NewPrebuiltBuildTool)
 }
 
 type prebuiltBuildToolProperties struct {
@@ -55,6 +59,13 @@
 	}
 }
 
+type PrebuiltBuildToolInfo struct {
+	Src  Path
+	Deps Paths
+}
+
+var PrebuiltBuildToolInfoProvider = blueprint.NewProvider(PrebuiltBuildToolInfo{})
+
 func (t *prebuiltBuildTool) GenerateAndroidBuildActions(ctx ModuleContext) {
 	sourcePath := t.prebuilt.SingleSourcePath(ctx)
 	installedPath := PathForModuleOut(ctx, t.BaseModuleName())
@@ -82,6 +93,11 @@
 	}
 
 	t.toolPath = OptionalPathForPath(installedPath)
+
+	ctx.SetProvider(PrebuiltBuildToolInfoProvider, PrebuiltBuildToolInfo{
+		Src:  sourcePath,
+		Deps: deps,
+	})
 }
 
 func (t *prebuiltBuildTool) MakeVars(ctx MakeVarsModuleContext) {
@@ -101,10 +117,6 @@
 
 // prebuilt_build_tool is to declare prebuilts to be used during the build, particularly for use
 // in genrules with the "tools" property.
-func prebuiltBuildToolFactory() Module {
-	return NewPrebuiltBuildTool()
-}
-
 func NewPrebuiltBuildTool() Module {
 	module := &prebuiltBuildTool{}
 	module.AddProperties(&module.properties)
diff --git a/android/rule_builder.go b/android/rule_builder.go
index 777c1cf..245b759 100644
--- a/android/rule_builder.go
+++ b/android/rule_builder.go
@@ -474,13 +474,23 @@
 		Inputs(depFiles.Paths())
 }
 
+// BuildWithNinjaVars adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
+// Outputs. This function will not escape Ninja variables, so it may be used to write sandbox manifests using Ninja variables.
+func (r *RuleBuilder) BuildWithUnescapedNinjaVars(name string, desc string) {
+	r.build(name, desc, false)
+}
+
 // Build adds the built command line to the build graph, with dependencies on Inputs and Tools, and output files for
 // Outputs.
 func (r *RuleBuilder) Build(name string, desc string) {
+	r.build(name, desc, true)
+}
+
+func (r *RuleBuilder) build(name string, desc string, ninjaEscapeCommandString bool) {
 	name = ninjaNameEscape(name)
 
 	if len(r.missingDeps) > 0 {
-		r.ctx.Build(pctx, BuildParams{
+		r.ctx.Build(r.pctx, BuildParams{
 			Rule:        ErrorRule,
 			Outputs:     r.Outputs(),
 			Description: desc,
@@ -619,12 +629,35 @@
 				name, r.sboxManifestPath.String(), r.outDir.String())
 		}
 
-		// Create a rule to write the manifest as a the textproto.
+		// Create a rule to write the manifest as textproto.
 		pbText, err := prototext.Marshal(&manifest)
 		if err != nil {
 			ReportPathErrorf(r.ctx, "sbox manifest failed to marshal: %q", err)
 		}
-		WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
+		if ninjaEscapeCommandString {
+			WriteFileRule(r.ctx, r.sboxManifestPath, string(pbText))
+		} else {
+			// We need  to have a rule to write files that is
+			// defined on the RuleBuilder's pctx in order to
+			// write Ninja variables in the string.
+			// The WriteFileRule function above rule can only write
+			// raw strings because it is defined on the android
+			// package's pctx, and it can't access variables defined
+			// in another context.
+			r.ctx.Build(r.pctx, BuildParams{
+				Rule: r.ctx.Rule(r.pctx, "unescapedWriteFile", blueprint.RuleParams{
+					Command:        `rm -rf ${out} && cat ${out}.rsp > ${out}`,
+					Rspfile:        "${out}.rsp",
+					RspfileContent: "${content}",
+					Description:    "write file",
+				}, "content"),
+				Output:      r.sboxManifestPath,
+				Description: "write sbox manifest " + r.sboxManifestPath.Base(),
+				Args: map[string]string{
+					"content": string(pbText),
+				},
+			})
+		}
 
 		// Generate a new string to use as the command line of the sbox rule.  This uses
 		// a RuleBuilderCommand as a convenience method of building the command line, then
@@ -723,9 +756,13 @@
 		pool = localPool
 	}
 
+	if ninjaEscapeCommandString {
+		commandString = proptools.NinjaEscape(commandString)
+	}
+
 	r.ctx.Build(r.pctx, BuildParams{
-		Rule: r.ctx.Rule(pctx, name, blueprint.RuleParams{
-			Command:        proptools.NinjaEscape(commandString),
+		Rule: r.ctx.Rule(r.pctx, name, blueprint.RuleParams{
+			Command:        commandString,
 			CommandDeps:    proptools.NinjaEscapeList(tools.Strings()),
 			Restat:         r.restat,
 			Rspfile:        proptools.NinjaEscape(rspFile),
diff --git a/android/rule_builder_test.go b/android/rule_builder_test.go
index 86647eb..a6b3a27 100644
--- a/android/rule_builder_test.go
+++ b/android/rule_builder_test.go
@@ -28,6 +28,17 @@
 	"android/soong/shared"
 )
 
+var (
+	pctx_ruleBuilderTest           = NewPackageContext("android/soong/rule_builder")
+	pctx_ruleBuilderTestSubContext = NewPackageContext("android/soong/rule_builder/config")
+)
+
+func init() {
+	pctx_ruleBuilderTest.Import("android/soong/rule_builder/config")
+	pctx_ruleBuilderTest.StaticVariable("cmdFlags", "${config.ConfigFlags}")
+	pctx_ruleBuilderTestSubContext.StaticVariable("ConfigFlags", "--some-clang-flag")
+}
+
 func builderContext() BuilderContext {
 	return BuilderContextForTesting(TestConfig("out", nil, "", map[string][]byte{
 		"ld":      nil,
@@ -496,11 +507,13 @@
 type testRuleBuilderModule struct {
 	ModuleBase
 	properties struct {
-		Srcs []string
+		Srcs  []string
+		Flags []string
 
-		Restat      bool
-		Sbox        bool
-		Sbox_inputs bool
+		Restat              bool
+		Sbox                bool
+		Sbox_inputs         bool
+		Unescape_ninja_vars bool
 	}
 }
 
@@ -518,8 +531,9 @@
 	rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
 	manifestPath := PathForModuleOut(ctx, "sbox.textproto")
 
-	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir,
-		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs,
+	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, t.properties.Flags,
+		out, outDep, outDir,
+		manifestPath, t.properties.Restat, t.properties.Sbox, t.properties.Sbox_inputs, t.properties.Unescape_ninja_vars,
 		rspFile, rspFileContents, rspFile2, rspFileContents2)
 }
 
@@ -543,17 +557,18 @@
 	rspFileContents2 := PathsForSource(ctx, []string{"rsp_in2"})
 	manifestPath := PathForOutput(ctx, "singleton/sbox.textproto")
 
-	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, out, outDep, outDir,
-		manifestPath, true, false, false,
+	testRuleBuilder_Build(ctx, in, implicit, orderOnly, validation, nil, out, outDep, outDir,
+		manifestPath, true, false, false, false,
 		rspFile, rspFileContents, rspFile2, rspFileContents2)
 }
 
 func testRuleBuilder_Build(ctx BuilderContext, in Paths, implicit, orderOnly, validation Path,
+	flags []string,
 	out, outDep, outDir, manifestPath WritablePath,
-	restat, sbox, sboxInputs bool,
+	restat, sbox, sboxInputs, unescapeNinjaVars bool,
 	rspFile WritablePath, rspFileContents Paths, rspFile2 WritablePath, rspFileContents2 Paths) {
 
-	rule := NewRuleBuilder(pctx, ctx)
+	rule := NewRuleBuilder(pctx_ruleBuilderTest, ctx)
 
 	if sbox {
 		rule.Sbox(outDir, manifestPath)
@@ -564,6 +579,7 @@
 
 	rule.Command().
 		Tool(PathForSource(ctx, "cp")).
+		Flags(flags).
 		Inputs(in).
 		Implicit(implicit).
 		OrderOnly(orderOnly).
@@ -577,7 +593,11 @@
 		rule.Restat()
 	}
 
-	rule.Build("rule", "desc")
+	if unescapeNinjaVars {
+		rule.BuildWithUnescapedNinjaVars("rule", "desc")
+	} else {
+		rule.Build("rule", "desc")
+	}
 }
 
 var prepareForRuleBuilderTest = FixtureRegisterWithContext(func(ctx RegistrationContext) {
@@ -792,3 +812,47 @@
 		})
 	}
 }
+
+func TestRuleBuilderWithNinjaVarEscaping(t *testing.T) {
+	bp := `
+		rule_builder_test {
+			name: "foo_sbox_escaped_ninja",
+			flags: ["${cmdFlags}"],
+			sbox: true,
+			sbox_inputs: true,
+		}
+		rule_builder_test {
+			name: "foo_sbox",
+			flags: ["${cmdFlags}"],
+			sbox: true,
+			sbox_inputs: true,
+			unescape_ninja_vars: true,
+		}
+	`
+	result := GroupFixturePreparers(
+		prepareForRuleBuilderTest,
+		FixtureWithRootAndroidBp(bp),
+	).RunTest(t)
+
+	escapedNinjaMod := result.ModuleForTests("foo_sbox_escaped_ninja", "").Rule("writeFile")
+	AssertStringDoesContain(
+		t,
+		"",
+		escapedNinjaMod.BuildParams.Args["content"],
+		"$${cmdFlags}",
+	)
+
+	unescapedNinjaMod := result.ModuleForTests("foo_sbox", "").Rule("unescapedWriteFile")
+	AssertStringDoesContain(
+		t,
+		"",
+		unescapedNinjaMod.BuildParams.Args["content"],
+		"${cmdFlags}",
+	)
+	AssertStringDoesNotContain(
+		t,
+		"",
+		unescapedNinjaMod.BuildParams.Args["content"],
+		"$${cmdFlags}",
+	)
+}
diff --git a/android/util.go b/android/util.go
index 5375373..7f6af2d 100644
--- a/android/util.go
+++ b/android/util.go
@@ -33,12 +33,17 @@
 	return append([]T{}, s...)
 }
 
-// Concat returns a new slice concatenated from the two input slices. It does not change the input
+// Concat returns a new slice concatenated from the input slices. It does not change the input
 // slices.
-func Concat[T any](s1, s2 []T) []T {
-	res := make([]T, 0, len(s1)+len(s2))
-	res = append(res, s1...)
-	res = append(res, s2...)
+func Concat[T any](slices ...[]T) []T {
+	newLength := 0
+	for _, s := range slices {
+		newLength += len(s)
+	}
+	res := make([]T, 0, newLength)
+	for _, s := range slices {
+		res = append(res, s...)
+	}
 	return res
 }
 
diff --git a/android/variable.go b/android/variable.go
index e521ca2..d33294c 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -484,6 +484,12 @@
 	KeepVndk *bool `json:",omitempty"`
 
 	CheckVendorSeappViolations *bool `json:",omitempty"`
+
+	// PartitionsVars are extra variables that are used to define the partition images. They should
+	// not be read from soong modules.
+	PartitionVars struct {
+		ProductDirectory string `json:",omitempty"`
+	} `json:",omitempty"`
 }
 
 func boolPtr(v bool) *bool {
diff --git a/apex/apex.go b/apex/apex.go
index aaa55bb..090d9c4 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -2623,7 +2623,7 @@
 	return m
 }
 
-func (o *OverrideApex) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (o *OverrideApex) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() != "override_apex" {
 		return
 	}
@@ -3261,7 +3261,7 @@
 )
 
 // ConvertWithBp2build performs bp2build conversion of an apex
-func (a *apexBundle) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *apexBundle) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	// We only convert apex and apex_test modules at this time
 	if ctx.ModuleType() != "apex" && ctx.ModuleType() != "apex_test" {
 		return
@@ -3272,7 +3272,7 @@
 	ctx.CreateBazelTargetModule(props, commonAttrs, &attrs)
 }
 
-func convertWithBp2build(a *apexBundle, ctx android.TopDownMutatorContext) (bazelApexBundleAttributes, bazel.BazelTargetModuleProperties, android.CommonAttributes) {
+func convertWithBp2build(a *apexBundle, ctx android.Bp2buildMutatorContext) (bazelApexBundleAttributes, bazel.BazelTargetModuleProperties, android.CommonAttributes) {
 	var manifestLabelAttribute bazel.LabelAttribute
 	manifestLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, proptools.StringDefault(a.properties.Manifest, "apex_manifest.json")))
 
@@ -3432,7 +3432,7 @@
 // both,                    32/32,     64/none,   32&64/32, 64/32
 // first,                   32/32,     64/none,   64/32,    64/32
 
-func convert32Libs(ctx android.TopDownMutatorContext, compileMultilb string,
+func convert32Libs(ctx android.Bp2buildMutatorContext, compileMultilb string,
 	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
 	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
 	switch compileMultilb {
@@ -3447,7 +3447,7 @@
 	}
 }
 
-func convert64Libs(ctx android.TopDownMutatorContext, compileMultilb string,
+func convert64Libs(ctx android.Bp2buildMutatorContext, compileMultilb string,
 	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
 	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
 	switch compileMultilb {
@@ -3460,7 +3460,7 @@
 	}
 }
 
-func convertBothLibs(ctx android.TopDownMutatorContext, compileMultilb string,
+func convertBothLibs(ctx android.Bp2buildMutatorContext, compileMultilb string,
 	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
 	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
 	switch compileMultilb {
@@ -3478,7 +3478,7 @@
 	}
 }
 
-func convertFirstLibs(ctx android.TopDownMutatorContext, compileMultilb string,
+func convertFirstLibs(ctx android.Bp2buildMutatorContext, compileMultilb string,
 	libs []string, nativeSharedLibs *convertedNativeSharedLibs) {
 	libsLabelList := android.BazelLabelForModuleDeps(ctx, libs)
 	switch compileMultilb {
@@ -3521,7 +3521,7 @@
 	labelListAttr.Append(list)
 }
 
-func invalidCompileMultilib(ctx android.TopDownMutatorContext, value string) {
+func invalidCompileMultilib(ctx android.Bp2buildMutatorContext, value string) {
 	ctx.PropertyErrorf("compile_multilib", "Invalid value: %s", value)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 9475f5d..3a6af1e 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -948,7 +948,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("rustLink").Args["linkFlags"]
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").RuleParams.Command
 	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")
 
@@ -1024,7 +1024,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("rustLink").Args["linkFlags"]
+	rustDeps := ctx.ModuleForTests("foo.rust", "android_arm64_armv8-a_apex10000").Rule("rustc").RuleParams.Command
 	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")
 }
diff --git a/apex/key.go b/apex/key.go
index 65e739a..fc1456b 100644
--- a/apex/key.go
+++ b/apex/key.go
@@ -208,11 +208,11 @@
 }
 
 // ConvertWithBp2build performs conversion apexKey for bp2build
-func (m *apexKey) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *apexKey) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	apexKeyBp2BuildInternal(ctx, m)
 }
 
-func apexKeyBp2BuildInternal(ctx android.TopDownMutatorContext, module *apexKey) {
+func apexKeyBp2BuildInternal(ctx android.Bp2buildMutatorContext, module *apexKey) {
 	privateKeyLabelAttribute, privateKeyNameAttribute :=
 		android.BazelStringOrLabelFromProp(ctx, module.properties.Private_key)
 
diff --git a/bp2build/bp2build_product_config.go b/bp2build/bp2build_product_config.go
index 3622e67..3d9cae0 100644
--- a/bp2build/bp2build_product_config.go
+++ b/bp2build/bp2build_product_config.go
@@ -53,9 +53,10 @@
 		return res, err
 	}
 
-	// TODO(b/249685973): the name is product_config_platforms because product_config
-	// was already used for other files. Deduplicate them.
-	currentProductFolder := fmt.Sprintf("product_config_platforms/products/%s-%s", targetProduct, targetBuildVariant)
+	currentProductFolder := fmt.Sprintf("build/bazel/products/%s-%s", targetProduct, targetBuildVariant)
+	if len(productVariables.PartitionVars.ProductDirectory) > 0 {
+		currentProductFolder = fmt.Sprintf("%s%s-%s", productVariables.PartitionVars.ProductDirectory, targetProduct, targetBuildVariant)
+	}
 
 	productReplacer := strings.NewReplacer(
 		"{PRODUCT}", targetProduct,
@@ -72,7 +73,7 @@
 	}
 
 	productLabelsToVariables := make(map[string]*android.ProductVariables)
-	productLabelsToVariables[productReplacer.Replace("@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}")] = &productVariables
+	productLabelsToVariables[productReplacer.Replace("@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}")] = &productVariables
 	for product, productVariablesStarlark := range productsForTestingMap {
 		productVariables, err := starlarkMapToProductVariables(productVariablesStarlark)
 		if err != nil {
@@ -81,7 +82,30 @@
 		productLabelsToVariables["@//build/bazel/tests/products:"+product] = &productVariables
 	}
 
-	res.bp2buildTargets = createTargets(productLabelsToVariables)
+	res.bp2buildTargets = make(map[string]BazelTargets)
+	res.bp2buildTargets[currentProductFolder] = append(res.bp2buildTargets[currentProductFolder], BazelTarget{
+		name:        productReplacer.Replace("{PRODUCT}-{VARIANT}"),
+		packageName: currentProductFolder,
+		content: productReplacer.Replace(`android_product(
+    name = "{PRODUCT}-{VARIANT}",
+    soong_variables = _soong_variables,
+)`),
+		ruleClass: "android_product",
+		loads: []BazelLoad{
+			{
+				file: ":soong.variables.bzl",
+				symbols: []BazelLoadSymbol{{
+					symbol: "variables",
+					alias:  "_soong_variables",
+				}},
+			},
+			{
+				file:    "//build/bazel/product_config:android_product.bzl",
+				symbols: []BazelLoadSymbol{{symbol: "android_product"}},
+			},
+		},
+	})
+	createTargets(productLabelsToVariables, res.bp2buildTargets)
 
 	platformMappingContent, err := platformMappingContent(
 		productLabelsToVariables,
@@ -93,26 +117,6 @@
 
 	res.injectionFiles = []BazelFile{
 		newFile(
-			currentProductFolder,
-			"soong.variables.bzl",
-			`variables = json.decode("""`+strings.ReplaceAll(string(productVariablesBytes), "\\", "\\\\")+`""")`),
-		newFile(
-			currentProductFolder,
-			"BUILD",
-			productReplacer.Replace(`
-package(default_visibility=[
-    "@soong_injection//product_config_platforms:__subpackages__",
-    "@//build/bazel/product_config:__subpackages__",
-])
-load(":soong.variables.bzl", _soong_variables = "variables")
-load("@//build/bazel/product_config:android_product.bzl", "android_product")
-
-android_product(
-    name = "{PRODUCT}-{VARIANT}",
-    soong_variables = _soong_variables,
-)
-`)),
-		newFile(
 			"product_config_platforms",
 			"BUILD.bazel",
 			productReplacer.Replace(`
@@ -121,7 +125,7 @@
 	"@soong_injection//product_config_platforms:__subpackages__",
 ])
 
-load("//{PRODUCT_FOLDER}:soong.variables.bzl", _soong_variables = "variables")
+load("@//{PRODUCT_FOLDER}:soong.variables.bzl", _soong_variables = "variables")
 load("@//build/bazel/product_config:android_product.bzl", "android_product")
 
 # Bazel will qualify its outputs by the platform name. When switching between products, this
@@ -145,33 +149,33 @@
 # currently lunched product, they should all be listed here
 product_labels = [
   "@soong_injection//product_config_platforms:mixed_builds_product-{VARIANT}",
-  "@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}",
+  "@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}",
 `)+strings.Join(productsForTesting, "\n")+"\n]\n"),
 		newFile(
 			"product_config_platforms",
 			"common.bazelrc",
 			productReplacer.Replace(`
 build --platform_mappings=platform_mappings
-build --platforms @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+build --platforms @//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
 
-build:android --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
-build:linux_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86
-build:linux_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
-build:linux_bionic_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
-build:linux_musl_x86 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
-build:linux_musl_x86_64 --platforms=@soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
+build:android --platforms=@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}
+build:linux_x86 --platforms=@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86
+build:linux_x86_64 --platforms=@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+build:linux_bionic_x86_64 --platforms=@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_bionic_x86_64
+build:linux_musl_x86 --platforms=@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86
+build:linux_musl_x86_64 --platforms=@//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_musl_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
 			"linux.bazelrc",
 			productReplacer.Replace(`
-build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
+build --host_platform @//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_linux_x86_64
 `)),
 		newFile(
 			"product_config_platforms",
 			"darwin.bazelrc",
 			productReplacer.Replace(`
-build --host_platform @soong_injection//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
+build --host_platform @//{PRODUCT_FOLDER}:{PRODUCT}-{VARIANT}_darwin_x86_64
 `)),
 	}
 	res.bp2buildFiles = []BazelFile{
@@ -179,6 +183,10 @@
 			"",
 			"platform_mappings",
 			platformMappingContent),
+		newFile(
+			currentProductFolder,
+			"soong.variables.bzl",
+			`variables = json.decode("""`+strings.ReplaceAll(string(productVariablesBytes), "\\", "\\\\")+`""")`),
 	}
 
 	return res, nil
@@ -421,8 +429,11 @@
 	return result, nil
 }
 
-func createTargets(productLabelsToVariables map[string]*android.ProductVariables) map[string]BazelTargets {
-	res := make(map[string]BazelTargets)
+func createTargets(productLabelsToVariables map[string]*android.ProductVariables, res map[string]BazelTargets) {
+	createGeneratedAndroidCertificateDirectories(productLabelsToVariables, res)
+}
+
+func createGeneratedAndroidCertificateDirectories(productLabelsToVariables map[string]*android.ProductVariables, targets map[string]BazelTargets) {
 	var allDefaultAppCertificateDirs []string
 	for _, productVariables := range productLabelsToVariables {
 		if proptools.String(productVariables.DefaultAppCertificate) != "" {
@@ -433,20 +444,20 @@
 		}
 	}
 	for _, dir := range allDefaultAppCertificateDirs {
-		content := fmt.Sprintf(ruleTargetTemplate, "filegroup", "generated_android_certificate_directory", propsToAttributes(map[string]string{
-			"srcs": `glob([
+		content := `filegroup(
+    name = "generated_android_certificate_directory",
+    srcs = glob([
         "*.pk8",
         "*.pem",
         "*.avbpubkey",
-    ])`,
-			"visibility": `["//visibility:public"]`,
-		}))
-		res[dir] = append(res[dir], BazelTarget{
+    ]),
+    visibility = ["//visibility:public"],
+)`
+		targets[dir] = append(targets[dir], BazelTarget{
 			name:        "generated_android_certificate_directory",
 			packageName: dir,
 			content:     content,
 			ruleClass:   "filegroup",
 		})
 	}
-	return res
 }
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 9060363..15b7766 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -39,18 +39,24 @@
 	Attrs map[string]string
 }
 
-type BazelTarget struct {
-	name            string
-	packageName     string
-	content         string
-	ruleClass       string
-	bzlLoadLocation string
+type BazelLoadSymbol struct {
+	// The name of the symbol in the file being loaded
+	symbol string
+	// The name the symbol wil have in this file. Can be left blank to use the same name as symbol.
+	alias string
 }
 
-// IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
-// as opposed to a native rule built into Bazel.
-func (t BazelTarget) IsLoadedFromStarlark() bool {
-	return t.bzlLoadLocation != ""
+type BazelLoad struct {
+	file    string
+	symbols []BazelLoadSymbol
+}
+
+type BazelTarget struct {
+	name        string
+	packageName string
+	content     string
+	ruleClass   string
+	loads       []BazelLoad
 }
 
 // Label is the fully qualified Bazel label constructed from the BazelTarget's
@@ -110,30 +116,62 @@
 // LoadStatements return the string representation of the sorted and deduplicated
 // Starlark rule load statements needed by a group of BazelTargets.
 func (targets BazelTargets) LoadStatements() string {
-	bzlToLoadedSymbols := map[string][]string{}
+	// First, merge all the load statements from all the targets onto one list
+	bzlToLoadedSymbols := map[string][]BazelLoadSymbol{}
 	for _, target := range targets {
-		if target.IsLoadedFromStarlark() {
-			bzlToLoadedSymbols[target.bzlLoadLocation] =
-				append(bzlToLoadedSymbols[target.bzlLoadLocation], target.ruleClass)
+		for _, load := range target.loads {
+		outer:
+			for _, symbol := range load.symbols {
+				alias := symbol.alias
+				if alias == "" {
+					alias = symbol.symbol
+				}
+				for _, otherSymbol := range bzlToLoadedSymbols[load.file] {
+					otherAlias := otherSymbol.alias
+					if otherAlias == "" {
+						otherAlias = otherSymbol.symbol
+					}
+					if symbol.symbol == otherSymbol.symbol && alias == otherAlias {
+						continue outer
+					} else if alias == otherAlias {
+						panic(fmt.Sprintf("Conflicting destination (%s) for loads of %s and %s", alias, symbol.symbol, otherSymbol.symbol))
+					}
+				}
+				bzlToLoadedSymbols[load.file] = append(bzlToLoadedSymbols[load.file], symbol)
+			}
 		}
 	}
 
-	var loadStatements []string
-	for bzl, ruleClasses := range bzlToLoadedSymbols {
-		loadStatement := "load(\""
-		loadStatement += bzl
-		loadStatement += "\", "
-		ruleClasses = android.SortedUniqueStrings(ruleClasses)
-		for i, ruleClass := range ruleClasses {
-			loadStatement += "\"" + ruleClass + "\""
-			if i != len(ruleClasses)-1 {
-				loadStatement += ", "
+	var loadStatements strings.Builder
+	for i, bzl := range android.SortedKeys(bzlToLoadedSymbols) {
+		symbols := bzlToLoadedSymbols[bzl]
+		loadStatements.WriteString("load(\"")
+		loadStatements.WriteString(bzl)
+		loadStatements.WriteString("\", ")
+		sort.Slice(symbols, func(i, j int) bool {
+			if symbols[i].symbol < symbols[j].symbol {
+				return true
+			}
+			return symbols[i].alias < symbols[j].alias
+		})
+		for j, symbol := range symbols {
+			if symbol.alias != "" && symbol.alias != symbol.symbol {
+				loadStatements.WriteString(symbol.alias)
+				loadStatements.WriteString(" = ")
+			}
+			loadStatements.WriteString("\"")
+			loadStatements.WriteString(symbol.symbol)
+			loadStatements.WriteString("\"")
+			if j != len(symbols)-1 {
+				loadStatements.WriteString(", ")
 			}
 		}
-		loadStatement += ")"
-		loadStatements = append(loadStatements, loadStatement)
+		loadStatements.WriteString(")")
+		if i != len(bzlToLoadedSymbols)-1 {
+			loadStatements.WriteString("\n")
+		}
 	}
-	return strings.Join(android.SortedUniqueStrings(loadStatements), "\n")
+	return loadStatements.String()
 }
 
 type bpToBuildContext interface {
@@ -799,7 +837,7 @@
 		for dir := range dirs {
 			buildFileToTargets[dir] = append(buildFileToTargets[dir], BazelTarget{
 				name:      "bp2build_all_srcs",
-				content:   `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]))`,
+				content:   `filegroup(name = "bp2build_all_srcs", srcs = glob(["**/*"]), tags = ["manual"])`,
 				ruleClass: "filegroup",
 			})
 		}
@@ -857,12 +895,19 @@
 	} else {
 		content = fmt.Sprintf(unnamedRuleTargetTemplate, ruleClass, attributes)
 	}
+	var loads []BazelLoad
+	if bzlLoadLocation != "" {
+		loads = append(loads, BazelLoad{
+			file:    bzlLoadLocation,
+			symbols: []BazelLoadSymbol{{symbol: ruleClass}},
+		})
+	}
 	return BazelTarget{
-		name:            targetName,
-		packageName:     m.TargetPackage(),
-		ruleClass:       ruleClass,
-		bzlLoadLocation: bzlLoadLocation,
-		content:         content,
+		name:        targetName,
+		packageName: m.TargetPackage(),
+		ruleClass:   ruleClass,
+		loads:       loads,
+		content:     content,
 	}, nil
 }
 
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 23eae1d..329c907 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -773,9 +773,12 @@
 		{
 			bazelTargets: BazelTargets{
 				BazelTarget{
-					name:            "foo",
-					ruleClass:       "cc_library",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "foo",
+					ruleClass: "cc_library",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_library"}},
+					}},
 				},
 			},
 			expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`,
@@ -783,14 +786,20 @@
 		{
 			bazelTargets: BazelTargets{
 				BazelTarget{
-					name:            "foo",
-					ruleClass:       "cc_library",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "foo",
+					ruleClass: "cc_library",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_library"}},
+					}},
 				},
 				BazelTarget{
-					name:            "bar",
-					ruleClass:       "cc_library",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "bar",
+					ruleClass: "cc_library",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_library"}},
+					}},
 				},
 			},
 			expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_library")`,
@@ -798,14 +807,20 @@
 		{
 			bazelTargets: BazelTargets{
 				BazelTarget{
-					name:            "foo",
-					ruleClass:       "cc_library",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "foo",
+					ruleClass: "cc_library",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_library"}},
+					}},
 				},
 				BazelTarget{
-					name:            "bar",
-					ruleClass:       "cc_binary",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "bar",
+					ruleClass: "cc_binary",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_binary"}},
+					}},
 				},
 			},
 			expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")`,
@@ -813,19 +828,28 @@
 		{
 			bazelTargets: BazelTargets{
 				BazelTarget{
-					name:            "foo",
-					ruleClass:       "cc_library",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "foo",
+					ruleClass: "cc_library",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_library"}},
+					}},
 				},
 				BazelTarget{
-					name:            "bar",
-					ruleClass:       "cc_binary",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "bar",
+					ruleClass: "cc_binary",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_binary"}},
+					}},
 				},
 				BazelTarget{
-					name:            "baz",
-					ruleClass:       "java_binary",
-					bzlLoadLocation: "//build/bazel/rules:java.bzl",
+					name:      "baz",
+					ruleClass: "java_binary",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:java.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "java_binary"}},
+					}},
 				},
 			},
 			expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary", "cc_library")
@@ -834,19 +858,25 @@
 		{
 			bazelTargets: BazelTargets{
 				BazelTarget{
-					name:            "foo",
-					ruleClass:       "cc_binary",
-					bzlLoadLocation: "//build/bazel/rules:cc.bzl",
+					name:      "foo",
+					ruleClass: "cc_binary",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:cc.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "cc_binary"}},
+					}},
 				},
 				BazelTarget{
-					name:            "bar",
-					ruleClass:       "java_binary",
-					bzlLoadLocation: "//build/bazel/rules:java.bzl",
+					name:      "bar",
+					ruleClass: "java_binary",
+					loads: []BazelLoad{{
+						file:    "//build/bazel/rules:java.bzl",
+						symbols: []BazelLoadSymbol{{symbol: "java_binary"}},
+					}},
 				},
 				BazelTarget{
 					name:      "baz",
 					ruleClass: "genrule",
-					// Note: no bzlLoadLocation for native rules
+					// Note: no loads for native rules
 				},
 			},
 			expectedLoadStatements: `load("//build/bazel/rules:cc.bzl", "cc_binary")
diff --git a/bp2build/cc_library_shared_conversion_test.go b/bp2build/cc_library_shared_conversion_test.go
index 6f600da..345c226 100644
--- a/bp2build/cc_library_shared_conversion_test.go
+++ b/bp2build/cc_library_shared_conversion_test.go
@@ -1635,7 +1635,7 @@
         ],
         "//build/bazel/rules/apex:unbundled_app": [
             ":libHasApexStubs",
-            "//.:libHasApexAndNdkStubs.ndk_stub_libs",
+            "//.:libHasApexAndNdkStubs.ndk_stub_libs-current",
         ],
         "//conditions:default": [
             ":libHasApexStubs",
diff --git a/bp2build/testing.go b/bp2build/testing.go
index eca6022..a810709 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -497,7 +497,7 @@
 	return m.props.Dir
 }
 
-func (m *customModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *customModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if p := m.props.One_to_many_prop; p != nil && *p {
 		customBp2buildOneToMany(ctx, m)
 		return
@@ -555,7 +555,7 @@
 
 }
 
-func (m *customModule) createConfigSetting(ctx android.TopDownMutatorContext) {
+func (m *customModule) createConfigSetting(ctx android.Bp2buildMutatorContext) {
 	csa := bazel.ConfigSettingAttributes{
 		Flag_values: bazel.StringMapAttribute{
 			"//build/bazel/rules/my_string_setting": m.Name(),
@@ -590,7 +590,7 @@
 
 // A bp2build mutator that uses load statements and creates a 1:M mapping from
 // module to target.
-func customBp2buildOneToMany(ctx android.TopDownMutatorContext, m *customModule) {
+func customBp2buildOneToMany(ctx android.Bp2buildMutatorContext, m *customModule) {
 
 	baseName := m.Name()
 	attrs := &customBazelModuleAttributes{}
diff --git a/bpf/bpf.go b/bpf/bpf.go
index d135d5f..38777ff 100644
--- a/bpf/bpf.go
+++ b/bpf/bpf.go
@@ -313,7 +313,7 @@
 }
 
 // bpf bp2build converter
-func (b *bpf) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (b *bpf) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() != "bpf" {
 		return
 	}
diff --git a/cc/binary.go b/cc/binary.go
index 4606b62..0722f81 100644
--- a/cc/binary.go
+++ b/cc/binary.go
@@ -599,7 +599,7 @@
 	handler.module.setAndroidMkVariablesFromCquery(info.CcAndroidMkInfo)
 }
 
-func binaryBp2buildAttrs(ctx android.TopDownMutatorContext, m *Module) binaryAttributes {
+func binaryBp2buildAttrs(ctx android.Bp2buildMutatorContext, m *Module) binaryAttributes {
 	baseAttrs := bp2BuildParseBaseProps(ctx, m)
 	binaryLinkerAttrs := bp2buildBinaryLinkerProps(ctx, m)
 
@@ -661,7 +661,7 @@
 	return attrs
 }
 
-func binaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+func binaryBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
 	// shared with cc_test
 	binaryAttrs := binaryBp2buildAttrs(ctx, m)
 
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 6a49915..569f721 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -1657,9 +1657,11 @@
 			if depC, ok := dep.(*Module); ok && hasNdkStubs(ctx, depC) {
 				// If the dependency has ndk stubs, build against the ndk stubs
 				// https://cs.android.com/android/_/android/platform/build/soong/+/main:cc/cc.go;l=2642-2643;drc=e12d252e22dd8afa654325790d3298a0d67bd9d6;bpv=1;bpt=0
+				ver := proptools.String(c.Properties.Sdk_version)
+				// TODO - b/298085502: Add bp2build support for sdk_version: "minimum"
 				ndkLibModule, _ := ctx.ModuleFromName(dep.Name() + ndkLibrarySuffix)
 				label = bazel.Label{
-					Label: "//" + ctx.OtherModuleDir(ndkLibModule) + ":" + ndkLibModule.Name() + "_stub_libs",
+					Label: "//" + ctx.OtherModuleDir(ndkLibModule) + ":" + ndkLibModule.Name() + "_stub_libs-" + ver,
 				}
 			}
 			// add the ndk lib label to this axis
diff --git a/cc/cc.go b/cc/cc.go
index e28d056..9aa0cac 100644
--- a/cc/cc.go
+++ b/cc/cc.go
@@ -4214,7 +4214,7 @@
 }
 
 // ConvertWithBp2build converts Module to Bazel for bp2build.
-func (c *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (c *Module) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	prebuilt := c.IsPrebuilt()
 	switch c.typ() {
 	case binary:
diff --git a/cc/fdo_profile.go b/cc/fdo_profile.go
index d61af7e..05a8f46 100644
--- a/cc/fdo_profile.go
+++ b/cc/fdo_profile.go
@@ -45,7 +45,7 @@
 	Profile bazel.StringAttribute
 }
 
-func (fp *fdoProfile) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (fp *fdoProfile) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	var profileAttr bazel.StringAttribute
 
 	archVariantProps := fp.GetArchVariantProperties(ctx, &fdoProfileProperties{})
diff --git a/cc/library.go b/cc/library.go
index 7e0c55a..b9dc71b 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -308,7 +308,7 @@
 	}
 }
 
-func libraryBp2Build(ctx android.TopDownMutatorContext, m *Module) {
+func libraryBp2Build(ctx android.Bp2buildMutatorContext, m *Module) {
 	sharedAttrs := bp2BuildParseSharedProps(ctx, m)
 	staticAttrs := bp2BuildParseStaticProps(ctx, m)
 	baseAttributes := bp2BuildParseBaseProps(ctx, m)
@@ -480,7 +480,7 @@
 	createStubsBazelTargetIfNeeded(ctx, m, compilerAttrs, exportedIncludes, baseAttributes)
 }
 
-func createStubsBazelTargetIfNeeded(ctx android.TopDownMutatorContext, m *Module, compilerAttrs compilerAttributes, exportedIncludes BazelIncludes, baseAttributes baseAttributes) {
+func createStubsBazelTargetIfNeeded(ctx android.Bp2buildMutatorContext, m *Module, compilerAttrs compilerAttributes, exportedIncludes BazelIncludes, baseAttributes baseAttributes) {
 	if compilerAttrs.stubsSymbolFile != nil && len(compilerAttrs.stubsVersions.Value) > 0 {
 		stubSuitesProps := bazel.BazelTargetModuleProperties{
 			Rule_class:        "cc_stub_suite",
@@ -2886,7 +2886,7 @@
 	return outputFile
 }
 
-func bp2buildParseAbiCheckerProps(ctx android.TopDownMutatorContext, module *Module) bazelCcHeaderAbiCheckerAttributes {
+func bp2buildParseAbiCheckerProps(ctx android.Bp2buildMutatorContext, module *Module) bazelCcHeaderAbiCheckerAttributes {
 	lib, ok := module.linker.(*libraryDecorator)
 	if !ok {
 		return bazelCcHeaderAbiCheckerAttributes{}
@@ -2909,7 +2909,7 @@
 	return abiCheckerAttrs
 }
 
-func sharedOrStaticLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module, isStatic bool) {
+func sharedOrStaticLibraryBp2Build(ctx android.Bp2buildMutatorContext, module *Module, isStatic bool) {
 	baseAttributes := bp2BuildParseBaseProps(ctx, module)
 	compilerAttrs := baseAttributes.compilerAttributes
 	linkerAttrs := baseAttributes.linkerAttributes
diff --git a/cc/library_headers.go b/cc/library_headers.go
index 52198fc..5eba6ab 100644
--- a/cc/library_headers.go
+++ b/cc/library_headers.go
@@ -129,7 +129,7 @@
 	sdkAttributes
 }
 
-func libraryHeadersBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+func libraryHeadersBp2Build(ctx android.Bp2buildMutatorContext, module *Module) {
 	baseAttributes := bp2BuildParseBaseProps(ctx, module)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, &baseAttributes.includes)
 	linkerAttrs := baseAttributes.linkerAttributes
diff --git a/cc/ndk_library.go b/cc/ndk_library.go
index bb40b45..b3bb2da 100644
--- a/cc/ndk_library.go
+++ b/cc/ndk_library.go
@@ -415,7 +415,7 @@
 	// Also ensure that the ABI of the next API level (if there is one) matches
 	// this API level. *New* ABI is allowed, but any changes to APIs that exist
 	// in this API level are disallowed.
-	if !this.apiLevel.IsCurrent() {
+	if !this.apiLevel.IsCurrent() && prebuiltAbiDump.Valid() {
 		nextApiLevel := findNextApiLevel(ctx, this.apiLevel)
 		if nextApiLevel == nil {
 			panic(fmt.Errorf("could not determine which API level follows "+
@@ -435,10 +435,12 @@
 		} else {
 			ctx.Build(pctx, android.BuildParams{
 				Rule: stgdiff,
-				Description: fmt.Sprintf("abidiff %s %s", this.abiDumpPath,
-					nextAbiDump),
+				Description: fmt.Sprintf(
+					"Comparing ABI to the next API level %s %s",
+					prebuiltAbiDump, nextAbiDump),
 				Output: nextAbiDiffPath,
-				Inputs: android.Paths{this.abiDumpPath, nextAbiDump.Path()},
+				Inputs: android.Paths{
+					prebuiltAbiDump.Path(), nextAbiDump.Path()},
 				Args: map[string]string{
 					"args": "--format=small --ignore=interface_addition",
 				},
@@ -582,7 +584,7 @@
 	return android.BazelLabelForModuleDepsWithFn(ctx, hdrLibs, addSuffix)
 }
 
-func ndkLibraryBp2build(ctx android.TopDownMutatorContext, c *Module) {
+func ndkLibraryBp2build(ctx android.Bp2buildMutatorContext, c *Module) {
 	ndk, _ := c.linker.(*stubDecorator)
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_stub_suite",
diff --git a/cc/ndk_prebuilt.go b/cc/ndk_prebuilt.go
index c2382b3..c3e6510 100644
--- a/cc/ndk_prebuilt.go
+++ b/cc/ndk_prebuilt.go
@@ -148,7 +148,7 @@
 
 // stlSrcBp2build returns a bazel label for the checked-in .so/.a file
 // It contains a select statement for each ABI
-func stlSrcBp2build(ctx android.TopDownMutatorContext, c *Module) bazel.LabelAttribute {
+func stlSrcBp2build(ctx android.Bp2buildMutatorContext, c *Module) bazel.LabelAttribute {
 	libName := strings.TrimPrefix(c.Name(), "ndk_")
 	libExt := ".so" // TODO - b/201079053: Support windows
 	if ctx.ModuleType() == "ndk_prebuilt_static_stl" {
@@ -180,7 +180,7 @@
 	return bazel.MakeStringListAttribute(android.FirstUniqueStrings(includeDirs))
 }
 
-func ndkPrebuiltStlBp2build(ctx android.TopDownMutatorContext, c *Module) {
+func ndkPrebuiltStlBp2build(ctx android.Bp2buildMutatorContext, c *Module) {
 	if ctx.ModuleType() == "ndk_prebuilt_static_stl" {
 		ndkPrebuiltStaticStlBp2build(ctx, c)
 	} else {
@@ -188,7 +188,7 @@
 	}
 }
 
-func ndkPrebuiltStaticStlBp2build(ctx android.TopDownMutatorContext, c *Module) {
+func ndkPrebuiltStaticStlBp2build(ctx android.Bp2buildMutatorContext, c *Module) {
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_prebuilt_library_static",
 		Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_library_static.bzl",
@@ -201,7 +201,7 @@
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: c.Name()}, attrs)
 }
 
-func ndkPrebuiltSharedStlBp2build(ctx android.TopDownMutatorContext, c *Module) {
+func ndkPrebuiltSharedStlBp2build(ctx android.Bp2buildMutatorContext, c *Module) {
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "cc_prebuilt_library_shared",
 		Bzl_load_location: "//build/bazel/rules/cc:cc_prebuilt_library_shared.bzl",
diff --git a/cc/object.go b/cc/object.go
index ca14845..a3000e0 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -156,7 +156,7 @@
 
 // objectBp2Build is the bp2build converter from cc_object modules to the
 // Bazel equivalent target, plus any necessary include deps for the cc_object.
-func objectBp2Build(ctx android.TopDownMutatorContext, m *Module) {
+func objectBp2Build(ctx android.Bp2buildMutatorContext, m *Module) {
 	if m.compiler == nil {
 		// a cc_object must have access to the compiler decorator for its props.
 		ctx.ModuleErrorf("compiler must not be nil for a cc_object module")
diff --git a/cc/prebuilt.go b/cc/prebuilt.go
index a4ca590..b4819b0 100644
--- a/cc/prebuilt.go
+++ b/cc/prebuilt.go
@@ -363,12 +363,12 @@
 //     all variants
 //
 // In all cases, cc_prebuilt_library_static target names will be appended with "_bp2build_cc_library_static".
-func prebuiltLibraryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+func prebuiltLibraryBp2Build(ctx android.Bp2buildMutatorContext, module *Module) {
 	prebuiltLibraryStaticBp2Build(ctx, module, true)
 	prebuiltLibrarySharedBp2Build(ctx, module)
 }
 
-func prebuiltLibraryStaticBp2Build(ctx android.TopDownMutatorContext, module *Module, fullBuild bool) {
+func prebuiltLibraryStaticBp2Build(ctx android.Bp2buildMutatorContext, module *Module, fullBuild bool) {
 	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, true)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, nil)
 
@@ -404,7 +404,7 @@
 	Export_system_includes bazel.StringListAttribute
 }
 
-func prebuiltLibrarySharedBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+func prebuiltLibrarySharedBp2Build(ctx android.Bp2buildMutatorContext, module *Module) {
 	prebuiltAttrs := Bp2BuildParsePrebuiltLibraryProps(ctx, module, false)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, module, nil)
 
@@ -637,7 +637,7 @@
 	Src bazel.LabelAttribute
 }
 
-func prebuiltObjectBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+func prebuiltObjectBp2Build(ctx android.Bp2buildMutatorContext, module *Module) {
 	prebuiltAttrs := bp2BuildParsePrebuiltObjectProps(ctx, module)
 
 	attrs := &bazelPrebuiltObjectAttributes{
@@ -797,7 +797,7 @@
 	Strip stripAttributes
 }
 
-func prebuiltBinaryBp2Build(ctx android.TopDownMutatorContext, module *Module) {
+func prebuiltBinaryBp2Build(ctx android.Bp2buildMutatorContext, module *Module) {
 	prebuiltAttrs := bp2BuildParsePrebuiltBinaryProps(ctx, module)
 
 	var la linkerAttributes
diff --git a/cc/test.go b/cc/test.go
index ae62128..7a6cf1b 100644
--- a/cc/test.go
+++ b/cc/test.go
@@ -700,7 +700,7 @@
 // TODO(b/244432609): handle `isolated` property.
 // TODO(b/244432134): handle custom runpaths for tests that assume runfile layouts not
 // default to bazel. (see linkerInit function)
-func testBinaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+func testBinaryBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
 	var testBinaryAttrs testBinaryAttributes
 	testBinaryAttrs.binaryAttributes = binaryBp2buildAttrs(ctx, m)
 
diff --git a/cc/testing.go b/cc/testing.go
index 07bee01..21745c3 100644
--- a/cc/testing.go
+++ b/cc/testing.go
@@ -35,6 +35,7 @@
 
 	multitree.RegisterApiImportsModule(ctx)
 
+	ctx.RegisterModuleType("prebuilt_build_tool", android.NewPrebuiltBuildTool)
 	ctx.RegisterModuleType("cc_benchmark", BenchmarkFactory)
 	ctx.RegisterModuleType("cc_object", ObjectFactory)
 	ctx.RegisterModuleType("cc_genrule", GenRuleFactory)
@@ -67,6 +68,26 @@
 
 func commonDefaultModules() string {
 	return `
+		prebuilt_build_tool {
+			name: "clang++",
+			src: "bin/clang++",
+		}
+		prebuilt_build_tool {
+			name: "clang++.real",
+			src: "bin/clang++.real",
+		}
+		prebuilt_build_tool {
+			name: "lld",
+			src: "bin/lld",
+		}
+		prebuilt_build_tool {
+			name: "ld.lld",
+			src: "bin/ld.lld",
+		}
+		prebuilt_build_tool {
+			name: "llvm-ar",
+			src: "bin/llvm-ar",
+		}
 		cc_defaults {
 			name: "toolchain_libs_defaults",
 			host_supported: true,
@@ -568,6 +589,12 @@
 
 	// Additional files needed in tests that disallow non-existent source.
 	android.MockFS{
+		"defaults/cc/common/bin/clang++":      nil,
+		"defaults/cc/common/bin/clang++.real": nil,
+		"defaults/cc/common/bin/lld":          nil,
+		"defaults/cc/common/bin/ld.lld":       nil,
+		"defaults/cc/common/bin/llvm-ar":      nil,
+
 		"defaults/cc/common/libc.map.txt":      nil,
 		"defaults/cc/common/libdl.map.txt":     nil,
 		"defaults/cc/common/libm.map.txt":      nil,
diff --git a/cmd/sbox/sbox.go b/cmd/sbox/sbox.go
index fc56dd5..3364f50 100644
--- a/cmd/sbox/sbox.go
+++ b/cmd/sbox/sbox.go
@@ -119,6 +119,9 @@
 	}
 
 	manifest, err := readManifest(manifestFile)
+	if err != nil {
+		return err
+	}
 
 	if len(manifest.Commands) == 0 {
 		return fmt.Errorf("at least one commands entry is required in %q", manifestFile)
diff --git a/etc/prebuilt_etc.go b/etc/prebuilt_etc.go
index c6318d6..9423531 100644
--- a/etc/prebuilt_etc.go
+++ b/etc/prebuilt_etc.go
@@ -712,7 +712,7 @@
 // Bp2buildHelper returns a bazelPrebuiltFileAttributes used for the conversion
 // of prebuilt_*  modules. bazelPrebuiltFileAttributes has the common attributes
 // used by both prebuilt_etc_xml and other prebuilt_* moodules
-func (module *PrebuiltEtc) Bp2buildHelper(ctx android.TopDownMutatorContext) (*bazelPrebuiltFileAttributes, bool) {
+func (module *PrebuiltEtc) Bp2buildHelper(ctx android.Bp2buildMutatorContext) (*bazelPrebuiltFileAttributes, bool) {
 	var src bazel.LabelAttribute
 	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltEtcProperties{}) {
 		for config, p := range configToProps {
@@ -794,7 +794,7 @@
 // ConvertWithBp2build performs bp2build conversion of PrebuiltEtc
 // prebuilt_* modules (except prebuilt_etc_xml) are PrebuiltEtc,
 // which we treat as *PrebuiltFile*
-func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (module *PrebuiltEtc) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	var dir = module.installDirBase
 	// prebuilt_file supports only `etc` or `usr/share`
 	if !(dir == "etc" || dir == "usr/share") {
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 0d484c4..01cac5b 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -920,7 +920,7 @@
 }
 
 // ConvertWithBp2build converts a Soong module -> Bazel target.
-func (m *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *Module) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	// Bazel only has the "tools" attribute.
 	tools_prop := android.BazelLabelForModuleDeps(ctx, m.properties.Tools)
 	tool_files_prop := android.BazelLabelForModuleSrc(ctx, m.properties.Tool_files)
diff --git a/java/aar.go b/java/aar.go
index 262657d..f28d971 100644
--- a/java/aar.go
+++ b/java/aar.go
@@ -1239,7 +1239,7 @@
 	Sdk_version bazel.StringAttribute
 }
 
-func (a *aapt) convertAaptAttrsWithBp2Build(ctx android.TopDownMutatorContext) (*bazelAapt, bool) {
+func (a *aapt) convertAaptAttrsWithBp2Build(ctx android.Bp2buildMutatorContext) (*bazelAapt, bool) {
 	manifest := proptools.StringDefault(a.aaptProperties.Manifest, "AndroidManifest.xml")
 
 	resourceFiles := bazel.LabelList{
@@ -1275,7 +1275,7 @@
 	}, true
 }
 
-func (a *AARImport) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *AARImport) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	aars := android.BazelLabelForModuleSrcExcludes(ctx, a.properties.Aars, []string{})
 	exportableStaticLibs := []string{}
 	// TODO(b/240716882): investigate and handle static_libs deps that are not imports. They are not supported for export by Bazel.
@@ -1328,7 +1328,7 @@
 	}
 }
 
-func (a *AndroidLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *AndroidLibrary) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	commonAttrs, bp2buildInfo, supported := a.convertLibraryAttrsBp2Build(ctx)
 	if !supported {
 		return
diff --git a/java/app.go b/java/app.go
index 1b4d279..8716316 100755
--- a/java/app.go
+++ b/java/app.go
@@ -31,6 +31,7 @@
 	"android/soong/dexpreopt"
 	"android/soong/genrule"
 	"android/soong/tradefed"
+	"android/soong/ui/metrics/bp2build_metrics_proto"
 )
 
 func init() {
@@ -1594,11 +1595,11 @@
 	Certificate string
 }
 
-func (m *AndroidAppCertificate) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *AndroidAppCertificate) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	androidAppCertificateBp2Build(ctx, m)
 }
 
-func androidAppCertificateBp2Build(ctx android.TopDownMutatorContext, module *AndroidAppCertificate) {
+func androidAppCertificateBp2Build(ctx android.Bp2buildMutatorContext, module *AndroidAppCertificate) {
 	var certificate string
 	if module.properties.Certificate != nil {
 		certificate = *module.properties.Certificate
@@ -1634,11 +1635,26 @@
 	Proguard_specs   bazel.LabelListAttribute
 }
 
-func convertWithBp2build(ctx android.TopDownMutatorContext, a *AndroidApp) (bool, android.CommonAttributes, *bazelAndroidAppAttributes) {
+func convertWithBp2build(ctx android.Bp2buildMutatorContext, a *AndroidApp) (bool, android.CommonAttributes, *bazelAndroidAppAttributes) {
 	aapt, supported := a.convertAaptAttrsWithBp2Build(ctx)
 	if !supported {
 		return false, android.CommonAttributes{}, &bazelAndroidAppAttributes{}
 	}
+	if a.appProperties.Jni_uses_platform_apis != nil {
+		ctx.MarkBp2buildUnconvertible(
+			bp2build_metrics_proto.UnconvertedReasonType_UNSUPPORTED,
+			"TODO - b/299360988: Add bp2build support for jni_uses_platform_apis",
+		)
+		return false, android.CommonAttributes{}, &bazelAndroidAppAttributes{}
+	}
+	if a.appProperties.Jni_uses_sdk_apis != nil {
+		ctx.MarkBp2buildUnconvertible(
+			bp2build_metrics_proto.UnconvertedReasonType_UNSUPPORTED,
+			"TODO - b/299360988: Add bp2build support for jni_uses_sdk_apis",
+		)
+		return false, android.CommonAttributes{}, &bazelAndroidAppAttributes{}
+	}
+
 	certificate, certificateName := android.BazelStringOrLabelFromProp(ctx, a.overridableAppProperties.Certificate)
 
 	manifestValues := &manifestValueAttribute{}
@@ -1745,7 +1761,7 @@
 }
 
 // ConvertWithBp2build is used to convert android_app to Bazel.
-func (a *AndroidApp) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (a *AndroidApp) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ok, commonAttrs, appAttrs := convertWithBp2build(ctx, a); ok {
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "android_binary",
@@ -1758,7 +1774,7 @@
 }
 
 // ConvertWithBp2build is used to convert android_test to Bazel.
-func (at *AndroidTest) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (at *AndroidTest) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ok, commonAttrs, appAttrs := convertWithBp2build(ctx, &at.AndroidApp); ok {
 		props := bazel.BazelTargetModuleProperties{
 			Rule_class:        "android_test",
diff --git a/java/base.go b/java/base.go
index 8f48398..a110aff 100644
--- a/java/base.go
+++ b/java/base.go
@@ -1077,8 +1077,8 @@
 
 }
 
-func (module *Module) addGeneratedSrcJars(path android.Path) {
-	module.properties.Generated_srcjars = append(module.properties.Generated_srcjars, path)
+func (j *Module) addGeneratedSrcJars(path android.Path) {
+	j.properties.Generated_srcjars = append(j.properties.Generated_srcjars, path)
 }
 
 func (j *Module) compile(ctx android.ModuleContext, extraSrcJars, extraClasspathJars, extraCombinedJars android.Paths) {
@@ -2359,7 +2359,7 @@
 
 var _ ModuleWithStem = (*Module)(nil)
 
-func (j *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (j *Module) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	switch ctx.ModuleType() {
 	case "java_library", "java_library_host", "java_library_static", "tradefed_java_library_host":
 		if lib, ok := ctx.Module().(*Library); ok {
diff --git a/java/device_host_converter.go b/java/device_host_converter.go
index 5460dc9..c5ba245 100644
--- a/java/device_host_converter.go
+++ b/java/device_host_converter.go
@@ -198,7 +198,7 @@
 	Exports bazel.LabelListAttribute
 }
 
-func (d *DeviceHostConverter) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (d *DeviceHostConverter) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	ctx.CreateBazelTargetModule(
 		bazel.BazelTargetModuleProperties{
 			Rule_class:        "java_host_for_device",
diff --git a/java/droiddoc.go b/java/droiddoc.go
index fe0643a..d5547d0 100644
--- a/java/droiddoc.go
+++ b/java/droiddoc.go
@@ -871,7 +871,7 @@
 }
 
 // ConvertWithBp2build implements android.BazelModule.
-func (d *ExportedDroiddocDir) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (d *ExportedDroiddocDir) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	props := bazel.BazelTargetModuleProperties{
 		// Use the native py_library rule.
 		Rule_class:        "droiddoc_exported_dir",
diff --git a/java/java.go b/java/java.go
index 4f31af6..d5aeb7c 100644
--- a/java/java.go
+++ b/java/java.go
@@ -2775,7 +2775,7 @@
 	Additional_resources  bazel.LabelListAttribute
 }
 
-func (m *Library) getResourceFilegroupStripPrefix(ctx android.TopDownMutatorContext, resourceFilegroup string) (*string, bool) {
+func (m *Library) getResourceFilegroupStripPrefix(ctx android.Bp2buildMutatorContext, resourceFilegroup string) (*string, bool) {
 	if otherM, ok := ctx.ModuleFromName(resourceFilegroup); ok {
 		if fg, isFilegroup := otherM.(android.FileGroupPath); isFilegroup {
 			return proptools.StringPtr(filepath.Join(ctx.OtherModuleDir(otherM), fg.GetPath(ctx))), true
@@ -2784,7 +2784,7 @@
 	return proptools.StringPtr(""), false
 }
 
-func (m *Library) convertJavaResourcesAttributes(ctx android.TopDownMutatorContext) *javaResourcesAttributes {
+func (m *Library) convertJavaResourcesAttributes(ctx android.Bp2buildMutatorContext) *javaResourcesAttributes {
 	var resources bazel.LabelList
 	var resourceStripPrefix *string
 
@@ -2915,7 +2915,7 @@
 // which has other non-attribute information needed for bp2build conversion
 // that needs different handling depending on the module types, and thus needs
 // to be returned to the calling function.
-func (m *Library) convertLibraryAttrsBp2Build(ctx android.TopDownMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo, bool) {
+func (m *Library) convertLibraryAttrsBp2Build(ctx android.Bp2buildMutatorContext) (*javaCommonAttributes, *bp2BuildJavaInfo, bool) {
 	var srcs bazel.LabelListAttribute
 	var deps bazel.LabelListAttribute
 	var staticDeps bazel.LabelListAttribute
@@ -3136,7 +3136,7 @@
 	}
 }
 
-func javaLibraryBp2Build(ctx android.TopDownMutatorContext, m *Library) {
+func javaLibraryBp2Build(ctx android.Bp2buildMutatorContext, m *Library) {
 	commonAttrs, bp2BuildInfo, supported := m.convertLibraryAttrsBp2Build(ctx)
 	if !supported {
 		return
@@ -3192,7 +3192,7 @@
 }
 
 // JavaBinaryHostBp2Build is for java_binary_host bp2build.
-func javaBinaryHostBp2Build(ctx android.TopDownMutatorContext, m *Binary) {
+func javaBinaryHostBp2Build(ctx android.Bp2buildMutatorContext, m *Binary) {
 	commonAttrs, bp2BuildInfo, supported := m.convertLibraryAttrsBp2Build(ctx)
 	if !supported {
 		return
@@ -3279,7 +3279,7 @@
 }
 
 // javaTestHostBp2Build is for java_test_host bp2build.
-func javaTestHostBp2Build(ctx android.TopDownMutatorContext, m *TestHost) {
+func javaTestHostBp2Build(ctx android.Bp2buildMutatorContext, m *TestHost) {
 	commonAttrs, bp2BuildInfo, supported := m.convertLibraryAttrsBp2Build(ctx)
 	if !supported {
 		return
@@ -3332,7 +3332,7 @@
 
 // helper function that creates java_library target from java_binary_host or java_test_host,
 // and returns the library target name,
-func createLibraryTarget(ctx android.TopDownMutatorContext, libInfo libraryCreationInfo) string {
+func createLibraryTarget(ctx android.Bp2buildMutatorContext, libInfo libraryCreationInfo) string {
 	libName := libInfo.baseName + "_lib"
 	var libProps bazel.BazelTargetModuleProperties
 	if libInfo.hasKotlin {
@@ -3355,7 +3355,7 @@
 }
 
 // java_import bp2Build converter.
-func (i *Import) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (i *Import) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	var jars bazel.LabelListAttribute
 	archVariantProps := i.GetArchVariantProperties(ctx, &ImportProperties{})
 	for axis, configToProps := range archVariantProps {
diff --git a/java/kotlin.go b/java/kotlin.go
index 3637e2e..aa2db0e 100644
--- a/java/kotlin.go
+++ b/java/kotlin.go
@@ -145,7 +145,7 @@
 			`$kaptProcessorPath ` +
 			`$kaptProcessor ` +
 			`-Xbuild-file=$kotlinBuildFile && ` +
-			`${config.SoongZipCmd} -jar -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
+			`${config.SoongZipCmd} -jar -write_if_changed -o $out -C $kaptDir/stubs -D $kaptDir/stubs && ` +
 			`rm -rf "$srcJarDir"`,
 		CommandDeps: []string{
 			"${config.KotlincCmd}",
@@ -157,6 +157,7 @@
 		},
 		Rspfile:        "$out.rsp",
 		RspfileContent: `$in`,
+		Restat:         true,
 	},
 	"kotlincFlags", "encodedJavacFlags", "kaptProcessorPath", "kaptProcessor",
 	"classpath", "srcJars", "commonSrcFilesArg", "srcJarDir", "kaptDir", "kotlinJvmTarget",
diff --git a/java/platform_compat_config.go b/java/platform_compat_config.go
index 1248275..662a2d7 100644
--- a/java/platform_compat_config.go
+++ b/java/platform_compat_config.go
@@ -130,7 +130,7 @@
 	Src bazel.LabelAttribute
 }
 
-func (p *platformCompatConfig) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (p *platformCompatConfig) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	props := bazel.BazelTargetModuleProperties{
 		Rule_class:        "platform_compat_config",
 		Bzl_load_location: "//build/bazel/rules/java:platform_compat_config.bzl",
diff --git a/java/plugin.go b/java/plugin.go
index 5127298..4d4c199 100644
--- a/java/plugin.go
+++ b/java/plugin.go
@@ -64,7 +64,7 @@
 }
 
 // ConvertWithBp2build is used to convert android_app to Bazel.
-func (p *Plugin) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (p *Plugin) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	pluginName := p.Name()
 	commonAttrs, bp2BuildInfo, supported := p.convertLibraryAttrsBp2Build(ctx)
 	if !supported {
diff --git a/java/sdk_library.go b/java/sdk_library.go
index e3e2427..27f8626 100644
--- a/java/sdk_library.go
+++ b/java/sdk_library.go
@@ -2284,7 +2284,7 @@
 }
 
 // java_sdk_library bp2build converter
-func (module *SdkLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (module *SdkLibrary) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() != "java_sdk_library" {
 		ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_TYPE_UNSUPPORTED, "")
 		return
@@ -2449,7 +2449,7 @@
 }
 
 // java_sdk_library bp2build converter
-func (i *SdkLibraryImport) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (i *SdkLibraryImport) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	nameToAttr := make(map[string]*bazel.Label)
 
 	for scope, props := range i.scopeProperties {
diff --git a/linkerconfig/linkerconfig.go b/linkerconfig/linkerconfig.go
index 165697d..dad5892 100644
--- a/linkerconfig/linkerconfig.go
+++ b/linkerconfig/linkerconfig.go
@@ -107,7 +107,7 @@
 	Src bazel.LabelAttribute
 }
 
-func (l *linkerConfig) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (l *linkerConfig) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if l.properties.Src == nil {
 		ctx.PropertyErrorf("src", "empty src is not supported")
 		ctx.MarkBp2buildUnconvertible(bp2build_metrics_proto.UnconvertedReasonType_UNSUPPORTED, "")
diff --git a/python/bp2build.go b/python/bp2build.go
index 8bc3d0a..3b283e4 100644
--- a/python/bp2build.go
+++ b/python/bp2build.go
@@ -54,7 +54,7 @@
 	Imports bazel.StringListAttribute
 }
 
-func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.TopDownMutatorContext) baseAttributes {
+func (m *PythonLibraryModule) makeArchVariantBaseAttributes(ctx android.Bp2buildMutatorContext) baseAttributes {
 	var attrs baseAttributes
 	archVariantBaseProps := m.GetArchVariantProperties(ctx, &BaseProperties{})
 	for axis, configToProps := range archVariantBaseProps {
@@ -123,7 +123,7 @@
 	return attrs
 }
 
-func (m *PythonLibraryModule) bp2buildPythonVersion(ctx android.TopDownMutatorContext) *string {
+func (m *PythonLibraryModule) bp2buildPythonVersion(ctx android.Bp2buildMutatorContext) *string {
 	py3Enabled := proptools.BoolDefault(m.properties.Version.Py3.Enabled, true)
 	py2Enabled := proptools.BoolDefault(m.properties.Version.Py2.Enabled, false)
 	if py2Enabled && !py3Enabled {
@@ -146,7 +146,7 @@
 	Imports        bazel.StringListAttribute
 }
 
-func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (p *PythonLibraryModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	// 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
@@ -176,7 +176,7 @@
 	}, attrs)
 }
 
-func (p *PythonBinaryModule) bp2buildBinaryProperties(ctx android.TopDownMutatorContext) (*bazelPythonBinaryAttributes, bazel.LabelListAttribute) {
+func (p *PythonBinaryModule) bp2buildBinaryProperties(ctx android.Bp2buildMutatorContext) (*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
@@ -209,7 +209,7 @@
 	return attrs, baseAttrs.Data
 }
 
-func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (p *PythonBinaryModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	attrs, data := p.bp2buildBinaryProperties(ctx)
 
 	props := bazel.BazelTargetModuleProperties{
@@ -223,7 +223,7 @@
 	}, attrs)
 }
 
-func (p *PythonTestModule) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (p *PythonTestModule) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	// Python tests are currently exactly the same as binaries, but with a different module type
 	attrs, data := p.bp2buildBinaryProperties(ctx)
 
diff --git a/rust/afdo_test.go b/rust/afdo_test.go
index 0cdf704..80327af 100644
--- a/rust/afdo_test.go
+++ b/rust/afdo_test.go
@@ -54,8 +54,8 @@
 
 	expectedCFlag := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo.afdo")
 
-	if !strings.Contains(foo.Args["rustcFlags"], expectedCFlag) {
-		t.Errorf("Expected 'foo' to enable afdo, but did not find %q in cflags %q", expectedCFlag, foo.Args["rustcFlags"])
+	if !strings.Contains(foo.RuleParams.Command, expectedCFlag) {
+		t.Errorf("Expected 'foo' to enable afdo, but did not find %q in command %q", expectedCFlag, foo.RuleParams.Command)
 	}
 }
 
@@ -96,17 +96,17 @@
 		rustMockedFiles.AddToFixture(),
 	).RunTestWithBp(t, bp)
 
-	fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon").Rule("rustc")
-	fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
+	fooArm := result.ModuleForTests("foo", "android_arm_armv7-a-neon").Description("rustc")
+	fooArm64 := result.ModuleForTests("foo", "android_arm64_armv8-a").Description("rustc")
 
 	expectedCFlagArm := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm.afdo")
 	expectedCFlagArm64 := fmt.Sprintf(afdoFlagFormat, "afdo_profiles_package/foo_arm64.afdo")
 
-	if !strings.Contains(fooArm.Args["rustcFlags"], expectedCFlagArm) {
-		t.Errorf("Expected 'fooArm' to enable afdo, but did not find %q in cflags %q", expectedCFlagArm, fooArm.Args["rustcFlags"])
+	if !strings.Contains(fooArm.RuleParams.Command, expectedCFlagArm) {
+		t.Errorf("Expected 'fooArm' to enable afdo, but did not find %q in command %q", expectedCFlagArm, fooArm.RuleParams.Command)
 	}
 
-	if !strings.Contains(fooArm64.Args["rustcFlags"], expectedCFlagArm64) {
-		t.Errorf("Expected 'fooArm64' to enable afdo, but did not find %q in cflags %q", expectedCFlagArm64, fooArm64.Args["rustcFlags"])
+	if !strings.Contains(fooArm64.RuleParams.Command, expectedCFlagArm64) {
+		t.Errorf("Expected 'fooArm64' to enable afdo, but did not find %q in command %q", expectedCFlagArm64, fooArm.RuleParams.Command)
 	}
 }
diff --git a/rust/binary.go b/rust/binary.go
index 1e24beb..353381d 100644
--- a/rust/binary.go
+++ b/rust/binary.go
@@ -15,9 +15,10 @@
 package rust
 
 import (
+	"fmt"
+
 	"android/soong/android"
 	"android/soong/bazel"
-	"fmt"
 )
 
 func init() {
@@ -137,13 +138,17 @@
 
 func (binary *binaryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
 	fileName := binary.getStem(ctx) + ctx.toolchain().ExecutableSuffix()
-	srcPath, _ := srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
 	outputFile := android.PathForModuleOut(ctx, fileName)
 	ret := buildOutput{outputFile: outputFile}
+	var crateRootPath android.Path
+	if binary.baseCompiler.Properties.Crate_root == nil {
+		crateRootPath, _ = srcPathFromModuleSrcs(ctx, binary.baseCompiler.Properties.Srcs)
+	} else {
+		crateRootPath = android.PathForModuleSrc(ctx, *binary.baseCompiler.Properties.Crate_root)
+	}
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
-	flags.LinkFlags = append(flags.LinkFlags, deps.linkObjects.Strings()...)
 
 	if binary.stripper.NeedsStrip(ctx) {
 		strippedOutputFile := outputFile
@@ -154,7 +159,7 @@
 	}
 	binary.baseCompiler.unstrippedOutputFile = outputFile
 
-	ret.kytheFile = TransformSrcToBinary(ctx, srcPath, deps, flags, outputFile).kytheFile
+	ret.kytheFile = TransformSrcToBinary(ctx, binary, crateRootPath, deps, flags, outputFile).kytheFile
 	return ret
 }
 
@@ -199,7 +204,7 @@
 	Rustc_flags     bazel.StringListAttribute
 }
 
-func binaryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+func binaryBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
 	binary := m.compiler.(*binaryDecorator)
 
 	var srcs bazel.LabelList
diff --git a/rust/binary_test.go b/rust/binary_test.go
index dff94ac..ab1d2bc 100644
--- a/rust/binary_test.go
+++ b/rust/binary_test.go
@@ -135,7 +135,7 @@
 
 	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustc")
 
-	flags := fizzBuzz.Args["rustcFlags"]
+	flags := fizzBuzz.RuleParams.Command
 	if strings.Contains(flags, "--test") {
 		t.Errorf("extra --test flag, rustcFlags: %#v", flags)
 	}
@@ -150,11 +150,11 @@
 			bootstrap: true,
 		}`)
 
-	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustLink")
+	foo := ctx.ModuleForTests("foo", "android_arm64_armv8-a").Rule("rustc")
 
 	flag := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker64"
-	if !strings.Contains(foo.Args["linkFlags"], flag) {
-		t.Errorf("missing link flag to use bootstrap linker, expecting %#v, linkFlags: %#v", flag, foo.Args["linkFlags"])
+	if !strings.Contains(foo.RuleParams.Command, flag) {
+		t.Errorf("missing link flag to use bootstrap linker, expecting %#v, command: %#v", flag, foo.RuleParams.Command)
 	}
 }
 
@@ -167,19 +167,17 @@
 		}`)
 
 	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 := fizzOutLink.Args["linkFlags"]
+	flags := fizzOut.RuleParams.Command
 	if !strings.Contains(flags, "-C relocation-model=static") {
-		t.Errorf("static binary missing '-C relocation-model=static' in rustcFlags, found: %#v", flags)
+		t.Errorf("static binary missing '-C relocation-model=static' in command, found: %#v", flags)
 	}
 	if !strings.Contains(flags, "-C panic=abort") {
-		t.Errorf("static binary missing '-C panic=abort' in rustcFlags, found: %#v", flags)
+		t.Errorf("static binary missing '-C panic=abort' in command, found: %#v", flags)
 	}
-	if !strings.Contains(linkFlags, "-static") {
-		t.Errorf("static binary missing '-static' in linkFlags, found: %#v", flags)
+	if !strings.Contains(flags, "-static") {
+		t.Errorf("static binary missing '-static' in command, found: %#v", flags)
 	}
 
 	if !android.InList("libc", fizzMod.Properties.AndroidMkStaticLibs) {
@@ -201,10 +199,9 @@
 			name: "libfoo",
 		}`)
 
-	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)
+	fizzBuzz := ctx.ModuleForTests("fizz-buzz", "android_arm64_armv8-a").Rule("rustc")
+	if !strings.Contains(fizzBuzz.RuleParams.Command, "/libfoo.so") {
+		t.Errorf("missing shared dependency 'libfoo.so' in command: %#v", fizzBuzz.RuleParams.Command)
 	}
 }
 
diff --git a/rust/builder.go b/rust/builder.go
index b1f049d..0740518 100644
--- a/rust/builder.go
+++ b/rust/builder.go
@@ -15,6 +15,7 @@
 package rust
 
 import (
+	"fmt"
 	"path/filepath"
 	"strings"
 
@@ -25,54 +26,6 @@
 )
 
 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=$mkcraterspCmd " +
-				"--emit link -o $out --emit dep-info=$out.d.raw $in ${libFlags} $rustcFlags" +
-				" && grep \"^$out:\" $out.d.raw > $out.d",
-			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:
-			//     "warning: depfile has multiple output paths"
-			// For ninja, we keep/grep only the dependency rule for the rust $out file.
-			Deps:    blueprint.DepsGCC,
-			Depfile: "$out.d",
-		},
-		"rustcFlags", "libFlags", "envVars")
-	rustLink = pctx.AndroidStaticRule("rustLink",
-		blueprint.RuleParams{
-			Command: "${config.RustLinker} -o $out ${crtBegin} ${earlyLinkFlags} @$in ${linkFlags} ${crtEnd}",
-		},
-		"earlyLinkFlags", "linkFlags", "crtBegin", "crtEnd")
-
-	_       = pctx.SourcePathVariable("rustdocCmd", "${config.RustBin}/rustdoc")
-	rustdoc = pctx.AndroidStaticRule("rustdoc",
-		blueprint.RuleParams{
-			Command: "$envVars $rustdocCmd $rustdocFlags $in -o $outDir && " +
-				"touch $out",
-			CommandDeps: []string{"$rustdocCmd"},
-		},
-		"rustdocFlags", "outDir", "envVars")
-
-	_            = pctx.SourcePathVariable("clippyCmd", "${config.RustBin}/clippy-driver")
-	clippyDriver = pctx.AndroidStaticRule("clippy",
-		blueprint.RuleParams{
-			Command: "$envVars $clippyCmd " +
-				// Because clippy-driver uses rustc as backend, we need to have some output even during the linting.
-				// Use the metadata output as it has the smallest footprint.
-				"--emit metadata -o $out --emit dep-info=$out.d.raw $in ${libFlags} " +
-				"$rustcFlags $clippyFlags" +
-				" && grep \"^$out:\" $out.d.raw > $out.d",
-			CommandDeps: []string{"$clippyCmd"},
-			Deps:        blueprint.DepsGCC,
-			Depfile:     "$out.d",
-		},
-		"rustcFlags", "libFlags", "clippyFlags", "envVars")
-
 	zip = pctx.AndroidStaticRule("zip",
 		blueprint.RuleParams{
 			Command:        "cat $out.rsp | tr ' ' '\\n' | tr -d \\' | sort -u > ${out}.tmp && ${SoongZipCmd} -o ${out} -C $$OUT_DIR -l ${out}.tmp",
@@ -81,7 +34,7 @@
 			RspfileContent: "$in",
 		})
 
-	cp = pctx.AndroidStaticRule("cp",
+	cpDir = pctx.AndroidStaticRule("cpDir",
 		blueprint.RuleParams{
 			Command:        "cp `cat $outDir.rsp` $outDir",
 			Rspfile:        "${outDir}.rsp",
@@ -89,6 +42,12 @@
 		},
 		"outDir")
 
+	cp = pctx.AndroidStaticRule("cp",
+		blueprint.RuleParams{
+			Command:     "rm -f $out && cp $in $out",
+			Description: "cp $out",
+		})
+
 	// Cross-referencing:
 	_ = pctx.SourcePathVariable("rustExtractor",
 		"prebuilts/build-tools/${config.HostPrebuiltTag}/bin/rust_extractor")
@@ -96,23 +55,6 @@
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCorpusName() })
 	_ = pctx.VariableFunc("kytheCuEncoding",
 		func(ctx android.PackageVarContext) string { return ctx.Config().XrefCuEncoding() })
-	_            = pctx.SourcePathVariable("kytheVnames", "build/soong/vnames.json")
-	kytheExtract = pctx.AndroidStaticRule("kythe",
-		blueprint.RuleParams{
-			Command: `KYTHE_CORPUS=${kytheCorpus} ` +
-				`KYTHE_OUTPUT_FILE=$out ` +
-				`KYTHE_VNAMES=$kytheVnames ` +
-				`KYTHE_KZIP_ENCODING=${kytheCuEncoding} ` +
-				`KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative ` +
-				`$rustExtractor $envVars ` +
-				`$rustcCmd ` +
-				`-C linker=true ` +
-				`$in ${libFlags} $rustcFlags`,
-			CommandDeps:    []string{"$rustExtractor", "$kytheVnames"},
-			Rspfile:        "${out}.rsp",
-			RspfileContent: "$in",
-		},
-		"rustcFlags", "libFlags", "envVars")
 )
 
 type buildOutput struct {
@@ -124,40 +66,40 @@
 	pctx.HostBinToolVariable("SoongZipCmd", "soong_zip")
 }
 
-func TransformSrcToBinary(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+func TransformSrcToBinary(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "bin")
+	return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "bin")
 }
 
-func TransformSrctoRlib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+func TransformSrctoRlib(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "rlib")
+	return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "rlib")
 }
 
-func TransformSrctoDylib(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+func TransformSrctoDylib(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
 
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "dylib")
+	return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "dylib")
 }
 
-func TransformSrctoStatic(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+func TransformSrctoStatic(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "staticlib")
+	return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "staticlib")
 }
 
-func TransformSrctoShared(ctx ModuleContext, mainSrc android.Path, deps PathDeps, flags Flags,
+func TransformSrctoShared(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath) buildOutput {
 	flags.GlobalRustFlags = append(flags.GlobalRustFlags, "-C lto=thin")
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "cdylib")
+	return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "cdylib")
 }
 
-func TransformSrctoProcMacro(ctx ModuleContext, mainSrc android.Path, deps PathDeps,
+func TransformSrctoProcMacro(ctx ModuleContext, c compiler, mainSrc android.Path, deps PathDeps,
 	flags Flags, outputFile android.WritablePath) buildOutput {
-	return transformSrctoCrate(ctx, mainSrc, deps, flags, outputFile, "proc-macro")
+	return transformSrctoCrate(ctx, c, mainSrc, deps, flags, outputFile, "proc-macro")
 }
 
 func rustLibsToPaths(libs RustLibraries) android.Paths {
@@ -168,28 +110,46 @@
 	return paths
 }
 
-func makeLibFlags(deps PathDeps) []string {
+func makeLibFlags(deps PathDeps, ruleCmd *android.RuleBuilderCommand) []string {
 	var libFlags []string
 
 	// Collect library/crate flags
-	for _, lib := range deps.RLibs {
-		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+	for _, lib := range deps.Rlibs.ToListDirect() {
+		libPath := ruleCmd.PathForInput(lib.Path)
+		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+libPath)
 	}
-	for _, lib := range deps.DyLibs {
-		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+lib.Path.String())
+	for _, lib := range deps.Dylibs.ToListDirect() {
+		libPath := ruleCmd.PathForInput(lib.Path)
+		libFlags = append(libFlags, "--extern "+lib.CrateName+"="+libPath)
 	}
-	for _, proc_macro := range deps.ProcMacros {
-		libFlags = append(libFlags, "--extern "+proc_macro.CrateName+"="+proc_macro.Path.String())
+	for _, procMacro := range deps.ProcMacros.ToListDirect() {
+		procMacroPath := ruleCmd.PathForInput(procMacro.Path)
+		libFlags = append(libFlags, "--extern "+procMacro.CrateName+"="+procMacroPath)
 	}
 
 	for _, path := range deps.linkDirs {
-		libFlags = append(libFlags, "-L "+path)
+		libFlags = append(libFlags, "-L "+ruleCmd.PathForInput(path))
 	}
 
 	return libFlags
 }
 
-func rustEnvVars(ctx ModuleContext, deps PathDeps) []string {
+func collectImplicits(deps PathDeps) android.Paths {
+	depPaths := android.Paths{}
+	depPaths = append(depPaths, rustLibsToPaths(deps.Rlibs.ToList())...)
+	depPaths = append(depPaths, rustLibsToPaths(deps.Dylibs.ToList())...)
+	depPaths = append(depPaths, rustLibsToPaths(deps.ProcMacros.ToList())...)
+	depPaths = append(depPaths, deps.AfdoProfiles...)
+	depPaths = append(depPaths, deps.WholeStaticLibs...)
+	depPaths = append(depPaths, deps.SrcDeps...)
+	depPaths = append(depPaths, deps.srcProviderFiles...)
+	depPaths = append(depPaths, deps.LibDeps...)
+	depPaths = append(depPaths, deps.linkObjects...)
+	depPaths = append(depPaths, deps.BuildToolSrcDeps...)
+	return depPaths
+}
+
+func rustEnvVars(ctx ModuleContext, deps PathDeps, cmd *android.RuleBuilderCommand) []string {
 	var envVars []string
 
 	// libstd requires a specific environment variable to be set. This is
@@ -203,15 +163,17 @@
 		moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
 		// We must calculate an absolute path for OUT_DIR since Rust's include! macro (which normally consumes this)
 		// assumes that paths are relative to the source file.
-		var outDirPrefix string
-		if !filepath.IsAbs(moduleGenDir.String()) {
-			// If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
-			outDirPrefix = "$$PWD/"
-		} else {
+		var outDir string
+		if filepath.IsAbs(moduleGenDir.String()) {
 			// If OUT_DIR is absolute, then moduleGenDir will be an absolute path, so we don't need to set this to anything.
-			outDirPrefix = ""
+			outDir = moduleGenDir.String()
+		} else if moduleGenDir.Valid() {
+			// If OUT_DIR is not absolute, we use $$PWD to generate an absolute path (os.Getwd() returns '/')
+			outDir = filepath.Join("$$PWD/", cmd.PathForInput(moduleGenDir.Path()))
+		} else {
+			outDir = "$$PWD/"
 		}
-		envVars = append(envVars, "OUT_DIR="+filepath.Join(outDirPrefix, moduleGenDir.String()))
+		envVars = append(envVars, "OUT_DIR="+outDir)
 	} else {
 		// TODO(pcc): Change this to "OUT_DIR=" after fixing crates to not rely on this value.
 		envVars = append(envVars, "OUT_DIR=out")
@@ -242,7 +204,7 @@
 		}
 	}
 
-	envVars = append(envVars, "AR=${cc_config.ClangBin}/llvm-ar")
+	envVars = append(envVars, "AR="+cmd.PathForTool(deps.Llvm_ar))
 
 	if ctx.Darwin() {
 		envVars = append(envVars, "ANDROID_RUST_DARWIN=true")
@@ -251,21 +213,18 @@
 	return envVars
 }
 
-func transformSrctoCrate(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags,
+func transformSrctoCrate(ctx ModuleContext, comp compiler, main android.Path, deps PathDeps, flags Flags,
 	outputFile android.WritablePath, crateType string) buildOutput {
 
 	var inputs android.Paths
-	var implicits, linkImplicits, linkOrderOnly android.Paths
 	var output buildOutput
 	var rustcFlags, linkFlags []string
-	var earlyLinkFlags string
+	var earlyLinkFlags []string
 
 	output.outputFile = outputFile
 	crateName := ctx.RustModule().CrateName()
 	targetTriple := ctx.toolchain().RustTriple()
 
-	envVars := rustEnvVars(ctx, deps)
-
 	inputs = append(inputs, main)
 
 	// Collect rustc flags
@@ -286,7 +245,6 @@
 	// Enable incremental compilation if requested by user
 	if ctx.Config().IsEnvTrue("SOONG_RUSTC_INCREMENTAL") {
 		incrementalPath := android.PathForOutput(ctx, "rustc").String()
-
 		rustcFlags = append(rustcFlags, "-Cincremental="+incrementalPath)
 	}
 
@@ -298,36 +256,16 @@
 
 	// Collect linker flags
 	if !ctx.Darwin() {
-		earlyLinkFlags = "-Wl,--as-needed"
+		earlyLinkFlags = append(earlyLinkFlags, "-Wl,--as-needed")
 	}
 
-	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
-	linkFlags = append(linkFlags, flags.LinkFlags...)
-
-	// Check if this module needs to use the bootstrap linker
-	if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() {
-		dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker"
-		if ctx.toolchain().Is64Bit() {
-			dynamicLinker += "64"
-		}
-		linkFlags = append(linkFlags, dynamicLinker)
-	}
-
-	libFlags := makeLibFlags(deps)
-
 	// Collect dependencies
-	implicits = append(implicits, rustLibsToPaths(deps.RLibs)...)
-	implicits = append(implicits, rustLibsToPaths(deps.DyLibs)...)
-	implicits = append(implicits, rustLibsToPaths(deps.ProcMacros)...)
-	implicits = append(implicits, deps.AfdoProfiles...)
-	implicits = append(implicits, deps.srcProviderFiles...)
-	implicits = append(implicits, deps.WholeStaticLibs...)
-
-	linkImplicits = append(linkImplicits, deps.LibDeps...)
+	var linkImplicits android.Paths
+	implicits := collectImplicits(deps)
+	toolImplicits := android.Concat(deps.BuildToolDeps)
 	linkImplicits = append(linkImplicits, deps.CrtBegin...)
 	linkImplicits = append(linkImplicits, deps.CrtEnd...)
-
-	linkOrderOnly = append(linkOrderOnly, deps.linkObjects...)
+	implicits = append(implicits, comp.compilationSourcesAndData(ctx)...)
 
 	if len(deps.SrcDeps) > 0 {
 		moduleGenDir := ctx.RustModule().compiler.CargoOutDir()
@@ -342,7 +280,7 @@
 		}
 
 		ctx.Build(pctx, android.BuildParams{
-			Rule:        cp,
+			Rule:        cpDir,
 			Description: "cp " + moduleGenDir.Path().Rel(),
 			Outputs:     outputs,
 			Inputs:      deps.SrcDeps,
@@ -354,81 +292,176 @@
 	}
 
 	if flags.Clippy {
+		// TODO(b/298461712) remove this hack to let slim manifest branches build
+		if deps.Clippy_driver == nil {
+			deps.Clippy_driver = config.RustPath(ctx, "bin/clippy-driver")
+		}
+
+		clippyRule := getRuleBuilder(ctx, pctx, false, "clippy")
+		clippyCmd := clippyRule.Command()
 		clippyFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        clippyDriver,
-			Description: "clippy " + main.Rel(),
-			Output:      clippyFile,
-			Inputs:      inputs,
-			Implicits:   implicits,
-			Args: map[string]string{
-				"rustcFlags":  strings.Join(rustcFlags, " "),
-				"libFlags":    strings.Join(libFlags, " "),
-				"clippyFlags": strings.Join(flags.ClippyFlags, " "),
-				"envVars":     strings.Join(envVars, " "),
-			},
-		})
+		clippyDepInfoFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy.d.raw")
+		clippyDepFile := android.PathForModuleOut(ctx, outputFile.Base()+".clippy.d")
+
+		clippyCmd.
+			Flags(rustEnvVars(ctx, deps, clippyCmd)).
+			Tool(deps.Clippy_driver).
+			Flag("--emit metadata").
+			FlagWithOutput("-o ", clippyFile).
+			FlagWithOutput("--emit dep-info=", clippyDepInfoFile).
+			Inputs(inputs).
+			Flags(makeLibFlags(deps, clippyCmd)).
+			Flags(rustcFlags).
+			Flags(flags.ClippyFlags).
+			ImplicitTools(toolImplicits).
+			Implicits(implicits)
+
+		depfileCreationCmd := clippyRule.Command()
+		depfileCreationCmd.
+			Flag(fmt.Sprintf(
+				`grep "^%s:" %s >`,
+				depfileCreationCmd.PathForOutput(clippyFile),
+				depfileCreationCmd.PathForOutput(clippyDepInfoFile),
+			)).
+			DepFile(clippyDepFile)
+
+		clippyRule.BuildWithUnescapedNinjaVars("clippy", "clippy "+main.Rel())
+
 		// Declare the clippy build as an implicit dependency of the original crate.
 		implicits = append(implicits, clippyFile)
 	}
 
-	rustcOutputFile := outputFile
+	sboxDirectory := "rustc"
+	rustSboxOutputFile := android.PathForModuleOut(ctx, sboxDirectory, outputFile.Base())
+	depFile := android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".d")
+	depInfoFile := android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".d.raw")
+	var rustcImplicitOutputs android.WritablePaths
+
+	sandboxedCompilation := comp.crateRoot(ctx) != nil
+	rustcRule := getRuleBuilder(ctx, pctx, sandboxedCompilation, sboxDirectory)
+	rustcCmd := rustcRule.Command()
+
+	linkFlags = append(linkFlags, flags.GlobalLinkFlags...)
+	linkFlags = append(linkFlags, flags.LinkFlags...)
+	linkFlags = append(linkFlags, rustcCmd.PathsForInputs(deps.linkObjects)...)
+
+	// Check if this module needs to use the bootstrap linker
+	if ctx.RustModule().Bootstrap() && !ctx.RustModule().InRecovery() && !ctx.RustModule().InRamdisk() && !ctx.RustModule().InVendorRamdisk() {
+		dynamicLinker := "-Wl,-dynamic-linker,/system/bin/bootstrap/linker"
+		if ctx.toolchain().Is64Bit() {
+			dynamicLinker += "64"
+		}
+		linkFlags = append(linkFlags, dynamicLinker)
+	}
+
+	libFlags := makeLibFlags(deps, rustcCmd)
+
 	usesLinker := crateType == "bin" || crateType == "dylib" || crateType == "cdylib" || crateType == "proc-macro"
 	if usesLinker {
-		rustcOutputFile = android.PathForModuleOut(ctx, outputFile.Base()+".rsp")
+		rustSboxOutputFile = android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".rsp")
+		rustcImplicitOutputs = android.WritablePaths{
+			android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".whole.a"),
+			android.PathForModuleOut(ctx, sboxDirectory, rustSboxOutputFile.Base()+".a"),
+		}
 	}
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        rustc,
-		Description: "rustc " + main.Rel(),
-		Output:      rustcOutputFile,
-		Inputs:      inputs,
-		Implicits:   implicits,
-		Args: map[string]string{
-			"rustcFlags": strings.Join(rustcFlags, " "),
-			"libFlags":   strings.Join(libFlags, " "),
-			"envVars":    strings.Join(envVars, " "),
-		},
-	})
+	// TODO(b/298461712) remove this hack to let slim manifest branches build
+	if deps.Rustc == nil {
+		deps.Rustc = config.RustPath(ctx, "bin/rustc")
+	}
 
-	if usesLinker {
+	rustcCmd.
+		Flags(rustEnvVars(ctx, deps, rustcCmd)).
+		Tool(deps.Rustc).
+		FlagWithInput("-C linker=", android.PathForSource(ctx, "build", "soong", "scripts", "mkcratersp.py")).
+		Flag("--emit link").
+		Flag("-o").
+		Output(rustSboxOutputFile).
+		FlagWithOutput("--emit dep-info=", depInfoFile).
+		Inputs(inputs).
+		Flags(libFlags).
+		ImplicitTools(toolImplicits).
+		Implicits(implicits).
+		Flags(rustcFlags).
+		ImplicitOutputs(rustcImplicitOutputs)
+
+	depfileCreationCmd := rustcRule.Command()
+	depfileCreationCmd.
+		Flag(fmt.Sprintf(
+			`grep "^%s:" %s >`,
+			depfileCreationCmd.PathForOutput(rustSboxOutputFile),
+			depfileCreationCmd.PathForOutput(depInfoFile),
+		)).
+		DepFile(depFile)
+
+	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{
-				"earlyLinkFlags": earlyLinkFlags,
-				"linkFlags":      strings.Join(linkFlags, " "),
-				"crtBegin":       strings.Join(deps.CrtBegin.Strings(), " "),
-				"crtEnd":         strings.Join(deps.CrtEnd.Strings(), " "),
-			},
+			Rule:   cp,
+			Input:  rustSboxOutputFile,
+			Output: outputFile,
+		})
+	} else {
+		// TODO: delmerico - separate rustLink into its own rule
+		// mkcratersp.py hardcodes paths to files within the sandbox, so
+		// those need to be renamed/symlinked to something in the rustLink sandbox
+		// if we want to separate the rules
+		linkerSboxOutputFile := android.PathForModuleOut(ctx, sboxDirectory, outputFile.Base())
+		rustLinkCmd := rustcRule.Command()
+		rustLinkCmd.
+			Tool(deps.Clang).
+			Flag("-o").
+			Output(linkerSboxOutputFile).
+			Inputs(deps.CrtBegin).
+			Flags(earlyLinkFlags).
+			FlagWithInput("@", rustSboxOutputFile).
+			Flags(linkFlags).
+			Inputs(deps.CrtEnd).
+			ImplicitTools(toolImplicits).
+			Implicits(rustcImplicitOutputs.Paths()).
+			Implicits(implicits).
+			Implicits(linkImplicits)
+		ctx.Build(pctx, android.BuildParams{
+			Rule:   cp,
+			Input:  linkerSboxOutputFile,
+			Output: outputFile,
 		})
 	}
 
+	rustcRule.BuildWithUnescapedNinjaVars("rustc", "rustc "+main.Rel())
+
 	if flags.EmitXrefs {
+		kytheRule := getRuleBuilder(ctx, pctx, false, "kythe")
+		kytheCmd := kytheRule.Command()
 		kytheFile := android.PathForModuleOut(ctx, outputFile.Base()+".kzip")
-		ctx.Build(pctx, android.BuildParams{
-			Rule:        kytheExtract,
-			Description: "Xref Rust extractor " + main.Rel(),
-			Output:      kytheFile,
-			Inputs:      inputs,
-			Implicits:   implicits,
-			Args: map[string]string{
-				"rustcFlags": strings.Join(rustcFlags, " "),
-				"libFlags":   strings.Join(libFlags, " "),
-				"envVars":    strings.Join(envVars, " "),
-			},
-		})
+		kytheCmd.
+			Flag("KYTHE_CORPUS=${kytheCorpus}").
+			FlagWithOutput("KYTHE_OUTPUT_FILE=", kytheFile).
+			FlagWithInput("KYTHE_VNAMES=", android.PathForSource(ctx, "build", "soong", "vnames.json")).
+			Flag("KYTHE_KZIP_ENCODING=${kytheCuEncoding}").
+			Flag("KYTHE_CANONICALIZE_VNAME_PATHS=prefer-relative").
+			Tool(ctx.Config().PrebuiltBuildTool(ctx, "rust_extractor")).
+			Flags(rustEnvVars(ctx, deps, kytheCmd)).
+			Tool(deps.Rustc).
+			Flag("-C linker=true").
+			Inputs(inputs).
+			Flags(makeLibFlags(deps, kytheCmd)).
+			Flags(rustcFlags).
+			ImplicitTools(toolImplicits).
+			Implicits(implicits)
+		kytheRule.BuildWithUnescapedNinjaVars("kythe", "Xref Rust extractor "+main.Rel())
 		output.kytheFile = kytheFile
 	}
 	return output
 }
 
-func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps,
-	flags Flags) android.ModuleOutPath {
+func Rustdoc(ctx ModuleContext, main android.Path, deps PathDeps, flags Flags) android.ModuleOutPath {
+	// TODO(b/298461712) remove this hack to let slim manifest branches build
+	if deps.Rustdoc == nil {
+		deps.Rustdoc = config.RustPath(ctx, "bin/rustdoc")
+	}
+
+	rustdocRule := getRuleBuilder(ctx, pctx, false, "rustdoc")
+	rustdocCmd := rustdocRule.Command()
 
 	rustdocFlags := append([]string{}, flags.RustdocFlags...)
 	rustdocFlags = append(rustdocFlags, "--sysroot=/dev/null")
@@ -447,7 +480,7 @@
 	crateName := ctx.RustModule().CrateName()
 	rustdocFlags = append(rustdocFlags, "--crate-name "+crateName)
 
-	rustdocFlags = append(rustdocFlags, makeLibFlags(deps)...)
+	rustdocFlags = append(rustdocFlags, makeLibFlags(deps, rustdocCmd)...)
 	docTimestampFile := android.PathForModuleOut(ctx, "rustdoc.timestamp")
 
 	// Silence warnings about renamed lints for third-party crates
@@ -463,18 +496,26 @@
 	// https://github.com/rust-lang/rust/blob/master/src/librustdoc/html/render/write_shared.rs#L144-L146
 	docDir := android.PathForOutput(ctx, "rustdoc")
 
-	ctx.Build(pctx, android.BuildParams{
-		Rule:        rustdoc,
-		Description: "rustdoc " + main.Rel(),
-		Output:      docTimestampFile,
-		Input:       main,
-		Implicit:    ctx.RustModule().UnstrippedOutputFile(),
-		Args: map[string]string{
-			"rustdocFlags": strings.Join(rustdocFlags, " "),
-			"outDir":       docDir.String(),
-			"envVars":      strings.Join(rustEnvVars(ctx, deps), " "),
-		},
-	})
+	rustdocCmd.
+		Flags(rustEnvVars(ctx, deps, rustdocCmd)).
+		Tool(deps.Rustdoc).
+		Flags(rustdocFlags).
+		Input(main).
+		Flag("-o "+docDir.String()).
+		FlagWithOutput("&& touch ", docTimestampFile).
+		Implicit(ctx.RustModule().UnstrippedOutputFile())
 
+	rustdocRule.BuildWithUnescapedNinjaVars("rustdoc", "rustdoc "+main.Rel())
 	return docTimestampFile
 }
+
+func getRuleBuilder(ctx android.ModuleContext, pctx android.PackageContext, sbox bool, sboxDirectory string) *android.RuleBuilder {
+	r := android.NewRuleBuilder(pctx, ctx)
+	if sbox {
+		r = r.Sbox(
+			android.PathForModuleOut(ctx, sboxDirectory),
+			android.PathForModuleOut(ctx, sboxDirectory+".sbox.textproto"),
+		).SandboxInputs()
+	}
+	return r
+}
diff --git a/rust/clippy_test.go b/rust/clippy_test.go
index bd3bfb1..2703a1c 100644
--- a/rust/clippy_test.go
+++ b/rust/clippy_test.go
@@ -63,14 +63,14 @@
 			).RunTest(t)
 
 			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-			android.AssertStringEquals(t, "libfoo flags", tc.fooFlags, r.Args["clippyFlags"])
+			android.AssertStringDoesContain(t, "libfoo flags", r.RuleParams.Command, tc.fooFlags)
 
 			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
-			android.AssertStringEquals(t, "libbar flags", "${config.ClippyDefaultLints}", r.Args["clippyFlags"])
+			android.AssertStringDoesContain(t, "libbar flags", r.RuleParams.Command, "${config.ClippyDefaultLints}")
 
 			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("clippy")
 			if r.Rule != nil {
-				t.Errorf("libfoobar is setup to use clippy when explicitly disabled: clippyFlags=%q", r.Args["clippyFlags"])
+				t.Errorf("libfoobar is setup to use clippy when explicitly disabled: command=%q", r.RuleParams.Command)
 			}
 		})
 	}
diff --git a/rust/compiler.go b/rust/compiler.go
index e6a7a93..3fa3ccd 100644
--- a/rust/compiler.go
+++ b/rust/compiler.go
@@ -73,6 +73,18 @@
 	// If no source file is defined, a single generated source module can be defined to be used as the main source.
 	Srcs []string `android:"path,arch_variant"`
 
+	// Entry point that is passed to rustc to begin the compilation. E.g. main.rs or lib.rs.
+	// When this property is set,
+	//    * sandboxing is enabled for this module, and
+	//    * the srcs attribute is interpreted as a list of all source files potentially
+	//          used in compilation, including the entrypoint, and
+	//    * compile_data can be used to add additional files used in compilation that
+	//          not directly used as source files.
+	Crate_root *string `android:"path,arch_variant"`
+
+	// Additional data files that are used during compilation only. These are not accessible at runtime.
+	Compile_data []string `android:"path,arch_variant"`
+
 	// name of the lint set that should be used to validate this module.
 	//
 	// Possible values are "default" (for using a sensible set of lints
@@ -334,6 +346,23 @@
 	panic(fmt.Errorf("baseCrater doesn't know how to crate things!"))
 }
 
+func (compile *baseCompiler) crateRoot(ctx ModuleContext) android.Path {
+	if compile.Properties.Crate_root != nil {
+		return android.PathForModuleSrc(ctx, *compile.Properties.Crate_root)
+	}
+	return nil
+}
+
+// compilationSourcesAndData returns a list of files necessary to complete the compilation.
+// This includes the rust source files as well as any other data files that
+// are referenced during the build.
+func (compile *baseCompiler) compilationSourcesAndData(ctx ModuleContext) android.Paths {
+	return android.PathsForModuleSrc(ctx, android.Concat(
+		compile.Properties.Srcs,
+		compile.Properties.Compile_data,
+	))
+}
+
 func (compiler *baseCompiler) rustdoc(ctx ModuleContext, flags Flags,
 	deps PathDeps) android.OptionalPath {
 
@@ -511,6 +540,8 @@
 		ctx.PropertyErrorf("srcs", "only a single generated source module can be defined without a main source file.")
 	}
 
+	// TODO: b/297264540 - once all modules are sandboxed, we need to select the proper
+	// entry point file from Srcs rather than taking the first one
 	paths := android.PathsForModuleSrc(ctx, srcs)
 	return paths[srcIndex], paths[1:]
 }
diff --git a/rust/compiler_test.go b/rust/compiler_test.go
index ec6829a..e5cc888 100644
--- a/rust/compiler_test.go
+++ b/rust/compiler_test.go
@@ -36,9 +36,9 @@
 
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
 
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"fizz\"'") ||
-		!strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'feature=\"buzz\"'") {
-		t.Fatalf("missing fizz and buzz feature flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	if !strings.Contains(libfooDylib.RuleParams.Command, "cfg 'feature=\"fizz\"'") ||
+		!strings.Contains(libfooDylib.RuleParams.Command, "cfg 'feature=\"buzz\"'") {
+		t.Fatalf("missing fizz and buzz feature flags for libfoo dylib, command: %#v", libfooDylib.RuleParams.Command)
 	}
 }
 
@@ -57,9 +57,9 @@
 
 	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
 
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'std'") ||
-		!strings.Contains(libfooDylib.Args["rustcFlags"], "cfg 'cfg1=\"one\"'") {
-		t.Fatalf("missing std and cfg1 flags for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	if !strings.Contains(libfooDylib.RuleParams.Command, "cfg 'std'") ||
+		!strings.Contains(libfooDylib.RuleParams.Command, "cfg 'cfg1=\"one\"'") {
+		t.Fatalf("missing std and cfg1 flags for libfoo dylib, rustcFlags: %#v", libfooDylib.RuleParams.Command)
 	}
 }
 
@@ -146,14 +146,14 @@
 
 	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a").Rule("rustc")
 
-	if !strings.Contains(fizz.Args["envVars"], "CARGO_BIN_NAME=fizz") {
-		t.Fatalf("expected 'CARGO_BIN_NAME=fizz' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	if !strings.Contains(fizz.RuleParams.Command, "CARGO_BIN_NAME=fizz") {
+		t.Fatalf("expected 'CARGO_BIN_NAME=fizz' in envVars, actual command: %#v", fizz.RuleParams.Command)
 	}
-	if !strings.Contains(fizz.Args["envVars"], "CARGO_CRATE_NAME=foo") {
-		t.Fatalf("expected 'CARGO_CRATE_NAME=foo' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	if !strings.Contains(fizz.RuleParams.Command, "CARGO_CRATE_NAME=foo") {
+		t.Fatalf("expected 'CARGO_CRATE_NAME=foo' in envVars, actual command: %#v", fizz.RuleParams.Command)
 	}
-	if !strings.Contains(fizz.Args["envVars"], "CARGO_PKG_VERSION=1.0.0") {
-		t.Fatalf("expected 'CARGO_PKG_VERSION=1.0.0' in envVars, actual envVars: %#v", fizz.Args["envVars"])
+	if !strings.Contains(fizz.RuleParams.Command, "CARGO_PKG_VERSION=1.0.0") {
+		t.Fatalf("expected 'CARGO_PKG_VERSION=1.0.0' in envVars, actual command: %#v", fizz.RuleParams.Command)
 	}
 }
 
@@ -230,13 +230,13 @@
 			).RunTest(t)
 
 			r := result.ModuleForTests("libfoo", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			android.AssertStringDoesContain(t, "libfoo flags", r.Args["rustcFlags"], tc.fooFlags)
+			android.AssertStringDoesContain(t, "libfoo flags", r.RuleParams.Command, tc.fooFlags)
 
 			r = result.ModuleForTests("libbar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			android.AssertStringDoesContain(t, "libbar flags", r.Args["rustcFlags"], "${config.RustDefaultLints}")
+			android.AssertStringDoesContain(t, "libbar flags", r.RuleParams.Command, "${config.RustDefaultLints}")
 
 			r = result.ModuleForTests("libfoobar", "android_arm64_armv8-a_dylib").MaybeRule("rustc")
-			android.AssertStringDoesContain(t, "libfoobar flags", r.Args["rustcFlags"], "${config.RustAllowAllLints}")
+			android.AssertStringDoesContain(t, "libfoobar flags", r.RuleParams.Command, "${config.RustAllowAllLints}")
 		})
 	}
 }
diff --git a/rust/config/global.go b/rust/config/global.go
index c976617..0ddc116 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -81,13 +81,7 @@
 
 func init() {
 	pctx.SourcePathVariable("RustDefaultBase", RustDefaultBase)
-	pctx.VariableConfigMethod("HostPrebuiltTag", func(config android.Config) string {
-		if config.UseHostMusl() {
-			return "linux-musl-x86"
-		} else {
-			return config.PrebuiltOS()
-		}
-	})
+	pctx.VariableConfigMethod("HostPrebuiltTag", HostPrebuiltTag)
 
 	pctx.VariableFunc("RustBase", func(ctx android.PackageVarContext) string {
 		if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
@@ -109,6 +103,14 @@
 	exportedVars.ExportStringStaticVariable("RUST_DEFAULT_VERSION", RustDefaultVersion)
 }
 
+func HostPrebuiltTag(config android.Config) string {
+	if config.UseHostMusl() {
+		return "linux-musl-x86"
+	} else {
+		return config.PrebuiltOS()
+	}
+}
+
 func getRustVersionPctx(ctx android.PackageVarContext) string {
 	return GetRustVersion(ctx)
 }
@@ -124,3 +126,27 @@
 func BazelRustToolchainVars(config android.Config) string {
 	return android.BazelToolchainVars(config, exportedVars)
 }
+
+func RustPath(ctx android.PathContext, file string) android.SourcePath {
+	type rustToolKey string
+	key := android.NewCustomOnceKey(rustToolKey(file))
+	return ctx.Config().OnceSourcePath(key, func() android.SourcePath {
+		return rustPath(ctx).Join(ctx, file)
+	})
+}
+
+var rustPathKey = android.NewOnceKey("clangPath")
+
+func rustPath(ctx android.PathContext) android.SourcePath {
+	return ctx.Config().OnceSourcePath(rustPathKey, func() android.SourcePath {
+		rustBase := RustDefaultBase
+		if override := ctx.Config().Getenv("RUST_PREBUILTS_BASE"); override != "" {
+			rustBase = override
+		}
+		rustVersion := RustDefaultVersion
+		if override := ctx.Config().Getenv("RUST_DEFAULT_VERSION"); override != "" {
+			rustVersion = override
+		}
+		return android.PathForSource(ctx, rustBase, ctx.Config().PrebuiltOS(), rustVersion)
+	})
+}
diff --git a/rust/coverage.go b/rust/coverage.go
index 5216d60..b312194 100644
--- a/rust/coverage.go
+++ b/rust/coverage.go
@@ -17,6 +17,7 @@
 import (
 	"github.com/google/blueprint"
 
+	"android/soong/android"
 	"android/soong/cc"
 )
 
@@ -70,7 +71,10 @@
 		// 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() {
 			profiler_builtins := ctx.GetDirectDepWithTag(ProfilerBuiltins, rlibDepTag).(*Module)
-			deps.RLibs = append(deps.RLibs, RustLibrary{Path: profiler_builtins.OutputFile().Path(), CrateName: profiler_builtins.CrateName()})
+			deps.Rlibs = android.AddDirectToDepSet[RustLibrary](deps.Rlibs, RustLibrary{
+				Path:      profiler_builtins.OutputFile().Path(),
+				CrateName: profiler_builtins.CrateName(),
+			})
 		}
 
 		if cc.EnableContinuousCoverage(ctx) {
diff --git a/rust/coverage_test.go b/rust/coverage_test.go
index 64077cf..1466e0c 100644
--- a/rust/coverage_test.go
+++ b/rust/coverage_test.go
@@ -55,27 +55,27 @@
 	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")
+	libfooCovLink := ctx.ModuleForTests("libfoo_cov", "android_arm64_armv8-a_dylib_cov").Rule("rustc")
+	libbarNoCovLink := ctx.ModuleForTests("libbar_nocov", "android_arm64_armv8-a_dylib").Rule("rustc")
+	fizzCovLink := ctx.ModuleForTests("fizz_cov", "android_arm64_armv8-a_cov").Rule("rustc")
+	buzzNoCovLink := ctx.ModuleForTests("buzzNoCov", "android_arm64_armv8-a").Rule("rustc")
 
 	rustcCoverageFlags := []string{"-C instrument-coverage", " -g "}
 	for _, flag := range rustcCoverageFlags {
 		missingErrorStr := "missing rustc flag '%s' for '%s' module with coverage enabled; rustcFlags: %#v"
 		containsErrorStr := "contains rustc flag '%s' for '%s' module with coverage disabled; rustcFlags: %#v"
 
-		if !strings.Contains(fizzCov.Args["rustcFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.Args["rustcFlags"])
+		if !strings.Contains(fizzCov.RuleParams.Command, flag) {
+			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCov.RuleParams.Command)
 		}
-		if !strings.Contains(libfooCov.Args["rustcFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.Args["rustcFlags"])
+		if !strings.Contains(libfooCov.RuleParams.Command, flag) {
+			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCov.RuleParams.Command)
 		}
-		if strings.Contains(buzzNoCov.Args["rustcFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.Args["rustcFlags"])
+		if strings.Contains(buzzNoCov.RuleParams.Command, flag) {
+			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCov.RuleParams.Command)
 		}
-		if strings.Contains(libbarNoCov.Args["rustcFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.Args["rustcFlags"])
+		if strings.Contains(libbarNoCov.RuleParams.Command, flag) {
+			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCov.RuleParams.Command)
 		}
 	}
 
@@ -84,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(fizzCovLink.Args["linkFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCovLink.Args["linkFlags"])
+		if !strings.Contains(fizzCovLink.RuleParams.Command, flag) {
+			t.Fatalf(missingErrorStr, flag, "fizz_cov", fizzCovLink.RuleParams.Command)
 		}
-		if !strings.Contains(libfooCovLink.Args["linkFlags"], flag) {
-			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCovLink.Args["linkFlags"])
+		if !strings.Contains(libfooCovLink.RuleParams.Command, flag) {
+			t.Fatalf(missingErrorStr, flag, "libfoo_cov dylib", libfooCovLink.RuleParams.Command)
 		}
-		if strings.Contains(buzzNoCovLink.Args["linkFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCovLink.Args["linkFlags"])
+		if strings.Contains(buzzNoCovLink.RuleParams.Command, flag) {
+			t.Fatalf(containsErrorStr, flag, "buzzNoCov", buzzNoCovLink.RuleParams.Command)
 		}
-		if strings.Contains(libbarNoCovLink.Args["linkFlags"], flag) {
-			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCovLink.Args["linkFlags"])
+		if strings.Contains(libbarNoCovLink.RuleParams.Command, flag) {
+			t.Fatalf(containsErrorStr, flag, "libbar_cov", libbarNoCovLink.RuleParams.Command)
 		}
 	}
 
@@ -107,8 +107,8 @@
 			srcs: ["foo.rs"],
 		}`)
 
-	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"])
+	fizz := ctx.ModuleForTests("fizz", "android_arm64_armv8-a_cov").Rule("rustc")
+	if !strings.Contains(fizz.RuleParams.Command, "libprofile-clang-extras.a") {
+		t.Fatalf("missing expected coverage 'libprofile-clang-extras' dependency in linkFlags: %#v", fizz.RuleParams.Command)
 	}
 }
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index ee28c6d..ea35905 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -51,23 +51,23 @@
 
 	// Check that compiler flags are set appropriately .
 	fuzz_libtest := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
-	if !strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
-		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
+	if !strings.Contains(fuzz_libtest.RuleParams.Command, "-C passes='sancov-module'") ||
+		!strings.Contains(fuzz_libtest.RuleParams.Command, "--cfg fuzzing") {
 		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing).")
 	}
 
 	// Check that host modules support fuzzing.
 	host_fuzzer := ctx.ModuleForTests("fuzz_libtest", "android_arm64_armv8-a_fuzzer").Rule("rustc")
-	if !strings.Contains(host_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
-		!strings.Contains(host_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
+	if !strings.Contains(host_fuzzer.RuleParams.Command, "-C passes='sancov-module'") ||
+		!strings.Contains(host_fuzzer.RuleParams.Command, "--cfg fuzzing") {
 		t.Errorf("rust_fuzz_host module does not contain the expected flags (sancov-module, cfg fuzzing).")
 	}
 
 	// Check that dependencies have 'fuzzer' variants produced for them as well.
-	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Output("libtest_fuzzing.rlib")
-	if !strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov-module'") ||
-		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing).")
+	libtest_fuzzer := ctx.ModuleForTests("libtest_fuzzing", "android_arm64_armv8-a_rlib_rlib-std_fuzzer").Rule("rustc")
+	if !strings.Contains(libtest_fuzzer.RuleParams.Command, "-C passes='sancov-module'") ||
+		!strings.Contains(libtest_fuzzer.RuleParams.Command, "--cfg fuzzing") {
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing). command: %q", libtest_fuzzer.RuleParams.Command)
 	}
 }
 
diff --git a/rust/image_test.go b/rust/image_test.go
index fb4d9c1..813c5bc 100644
--- a/rust/image_test.go
+++ b/rust/image_test.go
@@ -59,36 +59,36 @@
 
 	vendor := ctx.ModuleForTests("libfoo", "android_vendor.29_arm64_armv8-a_static").Rule("rustc")
 
-	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vndk'") {
-		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+	if !strings.Contains(vendor.RuleParams.Command, "--cfg 'android_vndk'") {
+		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo vendor variant, rustcFlags: %#v", vendor.RuleParams.Command)
 	}
-	if !strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_vendor'") {
-		t.Errorf("missing \"--cfg 'android_vendor'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+	if !strings.Contains(vendor.RuleParams.Command, "--cfg 'android_vendor'") {
+		t.Errorf("missing \"--cfg 'android_vendor'\" for libfoo vendor variant, rustcFlags: %#v", vendor.RuleParams.Command)
 	}
-	if strings.Contains(vendor.Args["rustcFlags"], "--cfg 'android_product'") {
-		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.Args["rustcFlags"])
+	if strings.Contains(vendor.RuleParams.Command, "--cfg 'android_product'") {
+		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo vendor variant, rustcFlags: %#v", vendor.RuleParams.Command)
 	}
 
 	product := ctx.ModuleForTests("libfoo", "android_product.29_arm64_armv8-a_static").Rule("rustc")
-	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vndk'") {
-		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	if !strings.Contains(product.RuleParams.Command, "--cfg 'android_vndk'") {
+		t.Errorf("missing \"--cfg 'android_vndk'\" for libfoo product variant, rustcFlags: %#v", product.RuleParams.Command)
 	}
-	if strings.Contains(product.Args["rustcFlags"], "--cfg 'android_vendor'") {
-		t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	if strings.Contains(product.RuleParams.Command, "--cfg 'android_vendor'") {
+		t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo product variant, rustcFlags: %#v", product.RuleParams.Command)
 	}
-	if !strings.Contains(product.Args["rustcFlags"], "--cfg 'android_product'") {
-		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	if !strings.Contains(product.RuleParams.Command, "--cfg 'android_product'") {
+		t.Errorf("missing \"--cfg 'android_product'\" for libfoo product variant, rustcFlags: %#v", product.RuleParams.Command)
 	}
 
 	system := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_static").Rule("rustc")
-	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vndk'") {
-		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
+	if strings.Contains(system.RuleParams.Command, "--cfg 'android_vndk'") {
+		t.Errorf("unexpected \"--cfg 'android_vndk'\" for libfoo system variant, rustcFlags: %#v", system.RuleParams.Command)
 	}
-	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_vendor'") {
-		t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo system variant, rustcFlags: %#v", system.Args["rustcFlags"])
+	if strings.Contains(system.RuleParams.Command, "--cfg 'android_vendor'") {
+		t.Errorf("unexpected \"--cfg 'android_vendor'\" for libfoo system variant, rustcFlags: %#v", system.RuleParams.Command)
 	}
-	if strings.Contains(system.Args["rustcFlags"], "--cfg 'android_product'") {
-		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo system variant, rustcFlags: %#v", product.Args["rustcFlags"])
+	if strings.Contains(system.RuleParams.Command, "--cfg 'android_product'") {
+		t.Errorf("unexpected \"--cfg 'android_product'\" for libfoo system variant, rustcFlags: %#v", product.RuleParams.Command)
 	}
 
 }
diff --git a/rust/library.go b/rust/library.go
index 3f031c1..7bb82bc 100644
--- a/rust/library.go
+++ b/rust/library.go
@@ -485,11 +485,28 @@
 	return flags
 }
 
+func (library *libraryDecorator) compilationSourcesAndData(ctx ModuleContext) android.Paths {
+	var extraSrcs android.Paths
+	if library.rlib() {
+		extraSrcs = android.PathsForModuleSrc(ctx, library.Properties.Rlib.Srcs)
+	} else if library.dylib() {
+		extraSrcs = android.PathsForModuleSrc(ctx, library.Properties.Dylib.Srcs)
+	} else if library.static() {
+		extraSrcs = android.PathsForModuleSrc(ctx, library.Properties.Static.Srcs)
+	} else if library.shared() {
+		extraSrcs = android.PathsForModuleSrc(ctx, library.Properties.Shared.Srcs)
+	}
+	return android.Concat(
+		library.baseCompiler.compilationSourcesAndData(ctx),
+		extraSrcs,
+	)
+}
+
 func (library *libraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
 	var outputFile android.ModuleOutPath
 	var ret buildOutput
 	var fileName string
-	srcPath := library.srcPath(ctx, deps)
+	crateRootPath := library.crateRootPath(ctx, deps)
 
 	if library.sourceProvider != nil {
 		deps.srcProviderFiles = append(deps.srcProviderFiles, library.sourceProvider.Srcs()...)
@@ -525,7 +542,6 @@
 
 	flags.RustFlags = append(flags.RustFlags, deps.depFlags...)
 	flags.LinkFlags = append(flags.LinkFlags, deps.depLinkFlags...)
-	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:
@@ -536,13 +552,13 @@
 
 	// Call the appropriate builder for this library type
 	if library.rlib() {
-		ret.kytheFile = TransformSrctoRlib(ctx, srcPath, deps, flags, outputFile).kytheFile
+		ret.kytheFile = TransformSrctoRlib(ctx, library, crateRootPath, deps, flags, outputFile).kytheFile
 	} else if library.dylib() {
-		ret.kytheFile = TransformSrctoDylib(ctx, srcPath, deps, flags, outputFile).kytheFile
+		ret.kytheFile = TransformSrctoDylib(ctx, library, crateRootPath, deps, flags, outputFile).kytheFile
 	} else if library.static() {
-		ret.kytheFile = TransformSrctoStatic(ctx, srcPath, deps, flags, outputFile).kytheFile
+		ret.kytheFile = TransformSrctoStatic(ctx, library, crateRootPath, deps, flags, outputFile).kytheFile
 	} else if library.shared() {
-		ret.kytheFile = TransformSrctoShared(ctx, srcPath, deps, flags, outputFile).kytheFile
+		ret.kytheFile = TransformSrctoShared(ctx, library, crateRootPath, deps, flags, outputFile).kytheFile
 	}
 
 	if library.rlib() || library.dylib() {
@@ -585,13 +601,15 @@
 	return ret
 }
 
-func (library *libraryDecorator) srcPath(ctx ModuleContext, _ PathDeps) android.Path {
+func (library *libraryDecorator) crateRootPath(ctx ModuleContext, _ PathDeps) android.Path {
 	if library.sourceProvider != nil {
 		// Assume the first source from the source provider is the library entry point.
 		return library.sourceProvider.Srcs()[0]
-	} else {
+	} else if library.baseCompiler.Properties.Crate_root == nil {
 		path, _ := srcPathFromModuleSrcs(ctx, library.baseCompiler.Properties.Srcs)
 		return path
+	} else {
+		return android.PathForModuleSrc(ctx, *library.baseCompiler.Properties.Crate_root)
 	}
 }
 
@@ -606,7 +624,7 @@
 		return android.OptionalPath{}
 	}
 
-	return android.OptionalPathForPath(Rustdoc(ctx, library.srcPath(ctx, deps),
+	return android.OptionalPathForPath(Rustdoc(ctx, library.crateRootPath(ctx, deps),
 		deps, flags))
 }
 
@@ -810,7 +828,7 @@
 	Proc_macro_deps bazel.LabelListAttribute
 }
 
-func libraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+func libraryBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
 	lib := m.compiler.(*libraryDecorator)
 
 	srcs, compileData := srcsAndCompileDataAttrs(ctx, *lib.baseCompiler)
@@ -887,7 +905,7 @@
 	Version bazel.StringAttribute
 }
 
-func cargoBuildScriptBp2build(ctx android.TopDownMutatorContext, m *Module) *string {
+func cargoBuildScriptBp2build(ctx android.Bp2buildMutatorContext, m *Module) *string {
 	// Soong treats some crates like libprotobuf as special in that they have
 	// cargo build script ran to produce an out folder and check it into AOSP
 	// For example, https://cs.android.com/android/platform/superproject/main/+/main:external/rust/crates/protobuf/out/
diff --git a/rust/library_test.go b/rust/library_test.go
index 30ef333..dab9381 100644
--- a/rust/library_test.go
+++ b/rust/library_test.go
@@ -48,23 +48,23 @@
 	staticCrateType := "staticlib"
 
 	// Test crate type for rlib is correct.
-	if !strings.Contains(libfooRlib.Args["rustcFlags"], "crate-type="+rlibCrateType) {
-		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooRlib.Args["rustcFlags"])
+	if !strings.Contains(libfooRlib.RuleParams.Command, "crate-type="+rlibCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", rlibCrateType, libfooRlib.RuleParams.Command)
 	}
 
 	// Test crate type for dylib is correct.
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "crate-type="+dylibCrateType) {
-		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", dylibCrateType, libfooDylib.Args["rustcFlags"])
+	if !strings.Contains(libfooDylib.RuleParams.Command, "crate-type="+dylibCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", dylibCrateType, libfooDylib.RuleParams.Command)
 	}
 
 	// Test crate type for C static libraries is correct.
-	if !strings.Contains(libfooStatic.Args["rustcFlags"], "crate-type="+staticCrateType) {
-		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.Args["rustcFlags"])
+	if !strings.Contains(libfooStatic.RuleParams.Command, "crate-type="+staticCrateType) {
+		t.Errorf("missing crate-type for static variant, expecting %#v, rustcFlags: %#v", staticCrateType, libfooStatic.RuleParams.Command)
 	}
 
 	// Test crate type for C shared libraries is correct.
-	if !strings.Contains(libfooShared.Args["rustcFlags"], "crate-type="+sharedCrateType) {
-		t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.Args["rustcFlags"])
+	if !strings.Contains(libfooShared.RuleParams.Command, "crate-type="+sharedCrateType) {
+		t.Errorf("missing crate-type for shared variant, expecting %#v, got rustcFlags: %#v", sharedCrateType, libfooShared.RuleParams.Command)
 	}
 
 }
@@ -78,10 +78,10 @@
 			crate_name: "foo",
 		}`)
 
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Description("rustc")
 
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "prefer-dynamic") {
-		t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	if !strings.Contains(libfooDylib.RuleParams.Command, "prefer-dynamic") {
+		t.Errorf("missing prefer-dynamic flag for libfoo dylib, rustcFlags: %#v", libfooDylib.RuleParams.Command)
 	}
 }
 
@@ -94,10 +94,10 @@
 			crate_name: "foo",
 		}`)
 
-	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Rule("rustc")
+	libfooDylib := ctx.ModuleForTests("libfoo", "linux_glibc_x86_64_dylib").Description("rustc")
 
-	if !strings.Contains(libfooDylib.Args["rustcFlags"], "--cfg 'android_dylib'") {
-		t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.Args["rustcFlags"])
+	if !strings.Contains(libfooDylib.RuleParams.Command, "--cfg 'android_dylib'") {
+		t.Errorf("missing android_dylib cfg flag for libfoo dylib, rustcFlags: %#v", libfooDylib.RuleParams.Command)
 	}
 }
 
@@ -148,10 +148,10 @@
 
 	libfoo := ctx.ModuleForTests("libfoo", "android_arm64_armv8-a_shared")
 
-	libfooOutput := libfoo.Rule("rustLink")
-	if !strings.Contains(libfooOutput.Args["linkFlags"], "-Wl,-soname=libfoo.so") {
+	libfooOutput := libfoo.Rule("rustc")
+	if !strings.Contains(libfooOutput.RuleParams.Command, "-Wl,-soname=libfoo.so") {
 		t.Errorf("missing expected -Wl,-soname linker flag for libfoo shared lib, linkFlags: %#v",
-			libfooOutput.Args["linkFlags"])
+			libfooOutput.RuleParams.Command)
 	}
 
 	if !android.InList("libstd", libfoo.Module().(*Module).Properties.AndroidMkDylibs) {
@@ -237,19 +237,21 @@
 	// The build system assumes the  cc deps will be at the final linkage (either a shared library or binary)
 	// Hence, these flags are no-op
 	// TODO: We could consider removing these flags
+	expectedSharedFlag := "-L out/soong/.intermediates/shared_cc_dep/android_arm64_armv8-a_shared"
+	expectedStaticFlag := "-L out/soong/.intermediates/static_cc_dep/android_arm64_armv8-a_static"
 	for _, module := range modules {
-		if !strings.Contains(module.Rule("rustc").Args["libFlags"],
-			"-L out/soong/.intermediates/shared_cc_dep/android_arm64_armv8-a_shared/") {
+		if !strings.Contains(module.Rule("rustc").RuleParams.Command, expectedSharedFlag) {
 			t.Errorf(
-				"missing -L flag for shared_cc_dep, rustcFlags: %#v",
-				rustRlibRlibStd.Rule("rustc").Args["libFlags"],
+				"expected to find shared library linkdir flag %q, rustcFlags: %#v",
+				expectedSharedFlag,
+				rustRlibRlibStd.Rule("rustc").RuleParams.Command,
 			)
 		}
-		if !strings.Contains(module.Rule("rustc").Args["libFlags"],
-			"-L out/soong/.intermediates/static_cc_dep/android_arm64_armv8-a_static/") {
+		if !strings.Contains(module.Rule("rustc").RuleParams.Command, expectedStaticFlag) {
 			t.Errorf(
-				"missing -L flag for static_cc_dep, rustcFlags: %#v",
-				rustRlibRlibStd.Rule("rustc").Args["libFlags"],
+				"expected to find static library linkdir flag %q, rustcFlags: %#v",
+				expectedStaticFlag,
+				rustRlibRlibStd.Rule("rustc").RuleParams.Command,
 			)
 		}
 	}
diff --git a/rust/prebuilt.go b/rust/prebuilt.go
index fe9d0b5..d012680 100644
--- a/rust/prebuilt.go
+++ b/rust/prebuilt.go
@@ -146,7 +146,10 @@
 }
 
 func (prebuilt *prebuiltLibraryDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
-	prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
+	deps.linkDirs = append(deps.linkDirs, android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs)...)
+	prebuilt.flagExporter.exportLinkDirs(deps.linkDirs...)
+	prebuilt.flagExporter.exportLinkObjects(deps.linkObjects...)
+	prebuilt.flagExporter.exportLibDeps(deps.LibDeps...)
 	prebuilt.flagExporter.setProvider(ctx)
 
 	srcPath, paths := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
@@ -203,7 +206,7 @@
 }
 
 func (prebuilt *prebuiltProcMacroDecorator) compile(ctx ModuleContext, flags Flags, deps PathDeps) buildOutput {
-	prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs).Strings()...)
+	prebuilt.flagExporter.exportLinkDirs(android.PathsForModuleSrc(ctx, prebuilt.Properties.Link_dirs)...)
 	prebuilt.flagExporter.setProvider(ctx)
 
 	srcPath, paths := srcPathFromModuleSrcs(ctx, prebuilt.prebuiltSrcs())
diff --git a/rust/proc_macro.go b/rust/proc_macro.go
index 26227d0..3f0d17a 100644
--- a/rust/proc_macro.go
+++ b/rust/proc_macro.go
@@ -15,9 +15,10 @@
 package rust
 
 import (
+	"fmt"
+
 	"android/soong/android"
 	"android/soong/bazel"
-	"fmt"
 )
 
 func init() {
@@ -79,7 +80,7 @@
 	outputFile := android.PathForModuleOut(ctx, fileName)
 
 	srcPath, _ := srcPathFromModuleSrcs(ctx, procMacro.baseCompiler.Properties.Srcs)
-	ret := TransformSrctoProcMacro(ctx, srcPath, deps, flags, outputFile)
+	ret := TransformSrctoProcMacro(ctx, procMacro, srcPath, deps, flags, outputFile)
 	procMacro.baseCompiler.unstrippedOutputFile = outputFile
 	return ret
 }
@@ -114,7 +115,7 @@
 	Rustc_flags    bazel.StringListAttribute
 }
 
-func procMacroBp2build(ctx android.TopDownMutatorContext, m *Module) {
+func procMacroBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
 	procMacro := m.compiler.(*procMacroDecorator)
 	srcs, compileData := srcsAndCompileDataAttrs(ctx, *procMacro.baseCompiler)
 	deps := android.BazelLabelForModuleDeps(ctx, append(
diff --git a/rust/proc_macro_test.go b/rust/proc_macro_test.go
index cc81938..a547926 100644
--- a/rust/proc_macro_test.go
+++ b/rust/proc_macro_test.go
@@ -30,7 +30,7 @@
 
 	libprocmacro := ctx.ModuleForTests("libprocmacro", "linux_glibc_x86_64").Rule("rustc")
 
-	if !strings.Contains(libprocmacro.Args["rustcFlags"], "--extern proc_macro") {
-		t.Errorf("--extern proc_macro flag not being passed to rustc for proc macro %#v", libprocmacro.Args["rustcFlags"])
+	if !strings.Contains(libprocmacro.RuleParams.Command, "--extern proc_macro") {
+		t.Errorf("--extern proc_macro flag not being passed to rustc for proc macro %#v", libprocmacro.RuleParams.Command)
 	}
 }
diff --git a/rust/protobuf.go b/rust/protobuf.go
index ae82844..c8d2bda 100644
--- a/rust/protobuf.go
+++ b/rust/protobuf.go
@@ -282,7 +282,7 @@
 	Srcs bazel.LabelListAttribute
 }
 
-func protoLibraryBp2build(ctx android.TopDownMutatorContext, m *Module) {
+func protoLibraryBp2build(ctx android.Bp2buildMutatorContext, m *Module) {
 	var protoFiles []string
 
 	for _, propsInterface := range m.sourceProvider.SourceProviderProps() {
diff --git a/rust/rust.go b/rust/rust.go
index 1ee99cd..6d6b55e 100644
--- a/rust/rust.go
+++ b/rust/rust.go
@@ -15,11 +15,12 @@
 package rust
 
 import (
+	"fmt"
+	"strings"
+
 	"android/soong/bazel"
 	"android/soong/bloaty"
 	"android/soong/ui/metrics/bp2build_metrics_proto"
-	"fmt"
-	"strings"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/proptools"
@@ -437,12 +438,18 @@
 }
 
 type PathDeps struct {
-	DyLibs          RustLibraries
-	RLibs           RustLibraries
+	Dylibs          *android.DepSet[RustLibrary]
+	Rlibs           *android.DepSet[RustLibrary]
+	ProcMacros      *android.DepSet[RustLibrary]
 	LibDeps         android.Paths
 	WholeStaticLibs android.Paths
-	ProcMacros      RustLibraries
 	AfdoProfiles    android.Paths
+	// These paths are files needed to run the build tools and will be located under
+	// __SBOX_SANDBOX_DIR__/tools/...
+	BuildToolDeps android.Paths
+	// These paths are files needed to run the build tools and will be located under
+	// __SBOX_SANDBOX_DIR__/...
+	BuildToolSrcDeps android.Paths
 
 	// depFlags and depLinkFlags are rustc and linker (clang) flags.
 	depFlags     []string
@@ -450,7 +457,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
+	linkDirs    android.Paths
 	linkObjects android.Paths
 
 	// Used by bindgen modules which call clang
@@ -465,6 +472,13 @@
 	// Paths to generated source files
 	SrcDeps          android.Paths
 	srcProviderFiles android.Paths
+
+	// Paths to specific build tools
+	Rustc         android.Path
+	Clang         android.Path
+	Llvm_ar       android.Path
+	Clippy_driver android.Path
+	Rustdoc       android.Path
 }
 
 type RustLibraries []RustLibrary
@@ -484,6 +498,8 @@
 	compilerDeps(ctx DepsContext, deps Deps) Deps
 	crateName() string
 	rustdoc(ctx ModuleContext, flags Flags, deps PathDeps) android.OptionalPath
+	crateRoot(ctx ModuleContext) android.Path
+	compilationSourcesAndData(ctx ModuleContext) android.Paths
 
 	// Output directory in which source-generated code from dependencies is
 	// copied. This is equivalent to Cargo's OUT_DIR variable.
@@ -513,7 +529,7 @@
 }
 
 type exportedFlagsProducer interface {
-	exportLinkDirs(...string)
+	exportLinkDirs(...android.Path)
 	exportLinkObjects(...android.Path)
 }
 
@@ -522,13 +538,13 @@
 }
 
 type flagExporter struct {
-	linkDirs    []string
+	linkDirs    android.Paths
 	linkObjects android.Paths
 	libDeps     android.Paths
 }
 
-func (flagExporter *flagExporter) exportLinkDirs(dirs ...string) {
-	flagExporter.linkDirs = android.FirstUniqueStrings(append(flagExporter.linkDirs, dirs...))
+func (flagExporter *flagExporter) exportLinkDirs(dirs ...android.Path) {
+	flagExporter.linkDirs = android.FirstUniquePaths(append(flagExporter.linkDirs, dirs...))
 }
 
 func (flagExporter *flagExporter) exportLinkObjects(flags ...android.Path) {
@@ -555,7 +571,7 @@
 
 type FlagExporterInfo struct {
 	Flags       []string
-	LinkDirs    []string // TODO: this should be android.Paths
+	LinkDirs    android.Paths
 	LinkObjects android.Paths
 	LibDeps     android.Paths
 }
@@ -925,6 +941,14 @@
 func (d *Defaults) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 }
 
+type RustInfo struct {
+	TransitiveRlibs      *android.DepSet[RustLibrary]
+	TransitiveDylibs     *android.DepSet[RustLibrary]
+	TransitiveProcMacros *android.DepSet[RustLibrary]
+}
+
+var RustInfoProvider = blueprint.NewProvider(RustInfo{})
+
 func (mod *Module) GenerateAndroidBuildActions(actx android.ModuleContext) {
 	ctx := &moduleContext{
 		ModuleContext: actx,
@@ -1034,6 +1058,12 @@
 
 		ctx.Phony("rust", ctx.RustModule().OutputFile().Path())
 	}
+
+	ctx.SetProvider(RustInfoProvider, RustInfo{
+		TransitiveRlibs:      deps.Rlibs,
+		TransitiveDylibs:     deps.Dylibs,
+		TransitiveProcMacros: deps.ProcMacros,
+	})
 }
 
 func (mod *Module) deps(ctx DepsContext) Deps {
@@ -1092,6 +1122,7 @@
 var _ android.LicenseAnnotationsDependencyTag = dependencyTag{}
 
 var (
+	buildToolDepTag     = dependencyTag{name: "buildToolTag"}
 	customBindgenDepTag = dependencyTag{name: "customBindgenTag"}
 	rlibDepTag          = dependencyTag{name: "rlibTag", library: true}
 	dylibDepTag         = dependencyTag{name: "dylib", library: true, dynamic: true}
@@ -1225,7 +1256,9 @@
 
 	var transitiveAndroidMkSharedLibs []*android.DepSet[string]
 	var directAndroidMkSharedLibs []string
-
+	transitiveRlibs := android.NewDepSetBuilder[RustLibrary](android.PREORDER)
+	transitiveDylibs := android.NewDepSetBuilder[RustLibrary](android.PREORDER)
+	transitiveProcMacros := android.NewDepSetBuilder[RustLibrary](android.PREORDER)
 	ctx.VisitDirectDeps(func(dep android.Module) {
 		depName := ctx.OtherModuleName(dep)
 		depTag := ctx.OtherModuleDependencyTag(dep)
@@ -1238,6 +1271,17 @@
 			return
 		}
 
+		rustInfo := ctx.OtherModuleProvider(dep, RustInfoProvider).(RustInfo)
+		if rustInfo.TransitiveDylibs != nil {
+			transitiveDylibs.Transitive(rustInfo.TransitiveDylibs)
+		}
+		if rustInfo.TransitiveRlibs != nil {
+			transitiveRlibs.Transitive(rustInfo.TransitiveRlibs)
+		}
+		if rustInfo.TransitiveProcMacros != nil {
+			transitiveProcMacros.Transitive(rustInfo.TransitiveProcMacros)
+		}
+
 		if rustDep, ok := dep.(*Module); ok && !rustDep.CcLibraryInterface() {
 			//Handle Rust Modules
 			makeLibName := rustMakeLibName(ctx, mod, rustDep, depName+rustDep.Properties.RustSubName)
@@ -1252,9 +1296,12 @@
 				directDylibDeps = append(directDylibDeps, rustDep)
 				mod.Properties.AndroidMkDylibs = append(mod.Properties.AndroidMkDylibs, makeLibName)
 				mod.Properties.SnapshotDylibs = append(mod.Properties.SnapshotDylibs, cc.BaseLibName(depName))
+				transitiveDylibs.Direct(RustLibrary{
+					Path:      rustDep.UnstrippedOutputFile(),
+					CrateName: rustDep.CrateName(),
+				})
 
 			case rlibDepTag:
-
 				rlib, ok := rustDep.compiler.(libraryInterface)
 				if !ok || !rlib.rlib() {
 					ctx.ModuleErrorf("mod %q not an rlib library", makeLibName)
@@ -1263,10 +1310,18 @@
 				directRlibDeps = append(directRlibDeps, rustDep)
 				mod.Properties.AndroidMkRlibs = append(mod.Properties.AndroidMkRlibs, makeLibName)
 				mod.Properties.SnapshotRlibs = append(mod.Properties.SnapshotRlibs, cc.BaseLibName(depName))
+				transitiveRlibs.Direct(RustLibrary{
+					Path:      rustDep.UnstrippedOutputFile(),
+					CrateName: rustDep.CrateName(),
+				})
 
 			case procMacroDepTag:
 				directProcMacroDeps = append(directProcMacroDeps, rustDep)
 				mod.Properties.AndroidMkProcMacroLibs = append(mod.Properties.AndroidMkProcMacroLibs, makeLibName)
+				transitiveProcMacros.Direct(RustLibrary{
+					Path:      rustDep.UnstrippedOutputFile(),
+					CrateName: rustDep.CrateName(),
+				})
 			}
 
 			transitiveAndroidMkSharedLibs = append(transitiveAndroidMkSharedLibs, rustDep.transitiveAndroidMkSharedLibs)
@@ -1302,9 +1357,8 @@
 
 			if depTag == dylibDepTag || depTag == rlibDepTag || depTag == procMacroDepTag {
 				linkFile := rustDep.UnstrippedOutputFile()
-				linkDir := linkPathFromFilePath(linkFile)
 				if lib, ok := mod.compiler.(exportedFlagsProducer); ok {
-					lib.exportLinkDirs(linkDir)
+					lib.exportLinkDirs(linkFile.Dir())
 				}
 			}
 
@@ -1331,7 +1385,7 @@
 				return
 			}
 
-			linkPath := linkPathFromFilePath(linkObject.Path())
+			linkPath := linkObject.Path().Dir()
 
 			exportDep := false
 			switch {
@@ -1385,7 +1439,7 @@
 					}
 					return
 				}
-				linkPath = linkPathFromFilePath(linkObject.Path())
+				linkPath = linkObject.Path().Dir()
 
 				depPaths.linkDirs = append(depPaths.linkDirs, linkPath)
 				depPaths.linkObjects = append(depPaths.linkObjects, linkObject.AsPaths()...)
@@ -1420,6 +1474,25 @@
 			}
 		} else {
 			switch {
+			case depTag == buildToolDepTag:
+				buildTool := ctx.OtherModuleProvider(dep, android.PrebuiltBuildToolInfoProvider).(android.PrebuiltBuildToolInfo)
+				depPaths.BuildToolDeps = append(depPaths.BuildToolDeps, buildTool.Src)
+				depPaths.BuildToolDeps = append(depPaths.BuildToolDeps, buildTool.Deps...)
+				switch android.RemoveOptionalPrebuiltPrefix(dep.Name()) {
+				case "rustc":
+					depPaths.Rustc = buildTool.Src
+					// rustc expects the standard cc toolchain libraries (libdl, libm, libc, etc.)
+					// not to be under the __SBOX_SANDBOX_DIR__/ directory
+					depPaths.BuildToolSrcDeps = append(depPaths.BuildToolSrcDeps, buildTool.Deps...)
+				case "clang++":
+					depPaths.Clang = buildTool.Src
+				case "llvm-ar":
+					depPaths.Llvm_ar = buildTool.Src
+				case "clippy-driver":
+					depPaths.Clippy_driver = buildTool.Src
+				case "rustdoc":
+					depPaths.Rustdoc = buildTool.Src
+				}
 			case depTag == cc.CrtBeginDepTag:
 				depPaths.CrtBegin = append(depPaths.CrtBegin, android.OutputFileForModule(ctx, dep, ""))
 			case depTag == cc.CrtEndDepTag:
@@ -1435,21 +1508,6 @@
 		}
 	})
 
-	mod.transitiveAndroidMkSharedLibs = android.NewDepSet[string](android.PREORDER, directAndroidMkSharedLibs, transitiveAndroidMkSharedLibs)
-
-	var rlibDepFiles RustLibraries
-	for _, dep := range directRlibDeps {
-		rlibDepFiles = append(rlibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
-	}
-	var dylibDepFiles RustLibraries
-	for _, dep := range directDylibDeps {
-		dylibDepFiles = append(dylibDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
-	}
-	var procMacroDepFiles RustLibraries
-	for _, dep := range directProcMacroDeps {
-		procMacroDepFiles = append(procMacroDepFiles, RustLibrary{Path: dep.UnstrippedOutputFile(), CrateName: dep.CrateName()})
-	}
-
 	var libDepFiles android.Paths
 	for _, dep := range directStaticLibDeps {
 		libDepFiles = append(libDepFiles, dep.OutputFile().Path())
@@ -1473,20 +1531,22 @@
 		srcProviderDepFiles = append(srcProviderDepFiles, srcs...)
 	}
 
-	depPaths.RLibs = append(depPaths.RLibs, rlibDepFiles...)
-	depPaths.DyLibs = append(depPaths.DyLibs, dylibDepFiles...)
 	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.linkDirs = android.FirstUniquePaths(depPaths.linkDirs)
 	depPaths.linkObjects = android.FirstUniquePaths(depPaths.linkObjects)
 	depPaths.depFlags = android.FirstUniqueStrings(depPaths.depFlags)
 	depPaths.depClangFlags = android.FirstUniqueStrings(depPaths.depClangFlags)
 	depPaths.depIncludePaths = android.FirstUniquePaths(depPaths.depIncludePaths)
 	depPaths.depSystemIncludePaths = android.FirstUniquePaths(depPaths.depSystemIncludePaths)
 
+	depPaths.Rlibs = transitiveRlibs.Build()
+	depPaths.Dylibs = transitiveDylibs.Build()
+	depPaths.ProcMacros = transitiveProcMacros.Build()
+	mod.transitiveAndroidMkSharedLibs = android.NewDepSet[string](android.PREORDER, directAndroidMkSharedLibs, transitiveAndroidMkSharedLibs)
+
 	return depPaths
 }
 
@@ -1509,10 +1569,6 @@
 	return mod.InRecovery()
 }
 
-func linkPathFromFilePath(filepath android.Path) string {
-	return strings.Split(filepath.String(), filepath.Base())[0]
-}
-
 // usePublicApi returns true if the rust variant should link against NDK (publicapi)
 func (r *Module) usePublicApi() bool {
 	return r.Device() && r.UseSdk()
@@ -1555,6 +1611,15 @@
 			blueprint.Variation{Mutator: "rust_stdlinkage", Variation: stdLinkage})
 	}
 
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "rustc")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "clippy-driver")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "rustdoc")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "clang++")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "clang++.real")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "lld")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "ld.lld")
+	ctx.AddFarVariationDependencies([]blueprint.Variation{}, buildToolDepTag, "llvm-ar")
+
 	// rlibs
 	rlibDepVariations = append(rlibDepVariations, blueprint.Variation{Mutator: "rust_libraries", Variation: rlibVariation})
 	for _, lib := range deps.Rlibs {
@@ -1845,7 +1910,7 @@
 	return ""
 }
 
-func (m *Module) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *Module) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	if ctx.ModuleType() == "rust_library_host" || ctx.ModuleType() == "rust_library" {
 		libraryBp2build(ctx, m)
 	} else if ctx.ModuleType() == "rust_proc_macro" {
@@ -1864,7 +1929,7 @@
 // TODO(b/297344471): When crate_root prop is set which enforces inputs sandboxing,
 // always use `srcs` and `compile_data` props to generate `srcs` and `compile_data` attributes
 // instead of using globs.
-func srcsAndCompileDataAttrs(ctx android.TopDownMutatorContext, c baseCompiler) (bazel.LabelList, bazel.LabelList) {
+func srcsAndCompileDataAttrs(ctx android.Bp2buildMutatorContext, c baseCompiler) (bazel.LabelList, bazel.LabelList) {
 	var srcs bazel.LabelList
 	var compileData bazel.LabelList
 
diff --git a/rust/rust_test.go b/rust/rust_test.go
index 835114c..576209d 100644
--- a/rust/rust_test.go
+++ b/rust/rust_test.go
@@ -15,14 +15,17 @@
 package rust
 
 import (
+	"fmt"
 	"os"
 	"runtime"
 	"strings"
 	"testing"
 
 	"github.com/google/blueprint/proptools"
+	"google.golang.org/protobuf/encoding/prototext"
 
 	"android/soong/android"
+	"android/soong/cmd/sbox/sbox_proto"
 	"android/soong/genrule"
 )
 
@@ -64,11 +67,14 @@
 
 // testRust returns a TestContext in which a basic environment has been setup.
 // This environment contains a few mocked files. See rustMockedFiles for the list of these files.
-func testRust(t *testing.T, bp string) *android.TestContext {
+func testRust(t *testing.T, bp string, preparers ...android.FixturePreparer) *android.TestContext {
 	skipTestIfOsNotSupported(t)
 	result := android.GroupFixturePreparers(
 		prepareForRustTest,
 		rustMockedFiles.AddToFixture(),
+		android.GroupFixturePreparers(
+			preparers...,
+		),
 	).
 		RunTestWithBp(t, bp)
 	return result.TestContext
@@ -202,11 +208,11 @@
 // Test that we can extract the link path from a lib path.
 func TestLinkPathFromFilePath(t *testing.T) {
 	barPath := android.PathForTesting("out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/libbar.so")
-	libName := linkPathFromFilePath(barPath)
-	expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared/"
+	libName := barPath.Dir()
+	expectedResult := "out/soong/.intermediates/external/libbar/libbar/linux_glibc_x86_64_shared"
 
-	if libName != expectedResult {
-		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName)
+	if libName.String() != expectedResult {
+		t.Errorf("libNameFromFilePath returned the wrong name; expected '%#v', got '%#v'", expectedResult, libName.String())
 	}
 }
 
@@ -256,7 +262,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")
+	rustLink := ctx.ModuleForTests("fizz-buzz", "linux_glibc_x86_64").Rule("rustc")
 
 	// Since dependencies are added to AndroidMk* properties, we can check these to see if they've been picked up.
 	if !android.InList("librlib.rlib-std", module.Properties.AndroidMkRlibs) {
@@ -275,16 +281,16 @@
 		t.Errorf("Static library dependency not detected (dependency missing from AndroidMkStaticLibs)")
 	}
 
-	if !strings.Contains(rustc.Args["rustcFlags"], "-lstatic=wholestatic") {
-		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.Args["rustcFlags"])
+	if !strings.Contains(rustc.RuleParams.Command, "-lstatic=wholestatic") {
+		t.Errorf("-lstatic flag not being passed to rustc for static library %#v", rustc.RuleParams.Command)
 	}
 
-	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 !strings.Contains(rustLink.RuleParams.Command, "cc_stubs_dep.so") {
+		t.Errorf("shared cc_library not being passed to rustc linkFlags %#v", rustLink.RuleParams.Command)
 	}
 
-	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(rustLink.Implicits.Strings(), "cc_stubs_dep.so") {
+		t.Errorf("shared cc dep not being passed as implicit to rustc %#v", rustLink.OrderOnly.Strings())
 	}
 
 	if !android.SuffixInList(rustLink.Implicits.Strings(), "cc_stubs_dep.so.toc") {
@@ -427,7 +433,7 @@
 	`)
 	rustc := ctx.ModuleForTests("libpm", "linux_glibc_x86_64").Rule("rustc")
 
-	if !strings.Contains(rustc.Args["libFlags"], "libbar/linux_glibc_x86_64") {
+	if !strings.Contains(rustc.RuleParams.Command, "libbar/linux_glibc_x86_64") {
 		t.Errorf("Proc_macro is not using host variant of dependent modules.")
 	}
 }
@@ -480,3 +486,396 @@
 		t.Errorf("expected %q got %q", expected, got)
 	}
 }
+
+var (
+	sboxCompilationFiles = []string{
+		"out/soong/.intermediates/defaults/rust/libaddr2line/android_arm64_armv8-a_rlib/libaddr2line.rlib",
+		"out/soong/.intermediates/defaults/rust/libadler/android_arm64_armv8-a_rlib/libadler.rlib",
+		"out/soong/.intermediates/defaults/rust/liballoc/android_arm64_armv8-a_rlib/liballoc.rlib",
+		"out/soong/.intermediates/defaults/rust/libcfg_if/android_arm64_armv8-a_rlib/libcfg_if.rlib",
+		"out/soong/.intermediates/defaults/rust/libcompiler_builtins/android_arm64_armv8-a_rlib/libcompiler_builtins.rlib",
+		"out/soong/.intermediates/defaults/rust/libcore/android_arm64_armv8-a_rlib/libcore.rlib",
+		"out/soong/.intermediates/defaults/rust/libgimli/android_arm64_armv8-a_rlib/libgimli.rlib",
+		"out/soong/.intermediates/defaults/rust/libhashbrown/android_arm64_armv8-a_rlib/libhashbrown.rlib",
+		"out/soong/.intermediates/defaults/rust/liblibc/android_arm64_armv8-a_rlib/liblibc.rlib",
+		"out/soong/.intermediates/defaults/rust/libmemchr/android_arm64_armv8-a_rlib/libmemchr.rlib",
+		"out/soong/.intermediates/defaults/rust/libminiz_oxide/android_arm64_armv8-a_rlib/libminiz_oxide.rlib",
+		"out/soong/.intermediates/defaults/rust/libobject/android_arm64_armv8-a_rlib/libobject.rlib",
+		"out/soong/.intermediates/defaults/rust/libpanic_unwind/android_arm64_armv8-a_rlib/libpanic_unwind.rlib",
+		"out/soong/.intermediates/defaults/rust/librustc_demangle/android_arm64_armv8-a_rlib/librustc_demangle.rlib",
+		"out/soong/.intermediates/defaults/rust/librustc_std_workspace_alloc/android_arm64_armv8-a_rlib/librustc_std_workspace_alloc.rlib",
+		"out/soong/.intermediates/defaults/rust/librustc_std_workspace_core/android_arm64_armv8-a_rlib/librustc_std_workspace_core.rlib",
+		"out/soong/.intermediates/defaults/rust/libstd_detect/android_arm64_armv8-a_rlib/libstd_detect.rlib",
+		"build/soong/scripts/mkcratersp.py",
+		"defaults/rust/linux-x86/1.69.0/bin/rustc",
+		"defaults/rust/linux-x86/1.69.0/lib/libstd.so",
+		"defaults/rust/linux-x86/1.69.0/lib64/libc++.so.1",
+	}
+	sboxCompilationFilesWithCc = []string{
+		"defaults/cc/common",
+		"out/soong/.intermediates/defaults/cc/common/libc/android_arm64_armv8-a_shared/libc.so",
+		"out/soong/.intermediates/defaults/cc/common/libc/android_arm64_armv8-a_shared/libc.so.toc",
+		"out/soong/.intermediates/defaults/cc/common/libdl/android_arm64_armv8-a_shared/libdl.so",
+		"out/soong/.intermediates/defaults/cc/common/libdl/android_arm64_armv8-a_shared/libdl.so.toc",
+		"out/soong/.intermediates/defaults/cc/common/libm/android_arm64_armv8-a_shared/libm.so",
+		"out/soong/.intermediates/defaults/cc/common/libm/android_arm64_armv8-a_shared/libm.so.toc",
+		"out/soong/.intermediates/defaults/rust/liblog/android_arm64_armv8-a_shared/liblog.so",
+		"out/soong/.intermediates/defaults/rust/liblog/android_arm64_armv8-a_shared/liblog.so.toc",
+	}
+)
+
+func TestSandboxCompilation(t *testing.T) {
+	ctx := testRust(t, `
+		filegroup {
+			name: "libsrcs1",
+			srcs: ["src_filegroup1.rs"],
+		}
+		filegroup {
+			name: "libsrcs2",
+			srcs: ["src_filegroup2.rs"],
+		}
+		rust_library {
+			name: "libfizz_buzz",
+			crate_name:"fizz_buzz",
+			crate_root: "foo.rs",
+			srcs: [
+				"src_lib*.rs",
+				":libsrcs1",
+				":libsrcs2",
+			],
+			compile_data: [
+				"compile_data1.txt",
+				"compile_data2.txt",
+			],
+			dylib: {
+				srcs: ["dylib_only.rs"],
+			},
+			rlib: {
+				srcs: ["rlib_only.rs"],
+			},
+		}
+		rust_binary {
+			name: "fizz_buzz",
+			crate_name:"fizz_buzz",
+			crate_root: "foo.rs",
+			srcs: [
+				"src_lib*.rs",
+				":libsrcs1",
+				":libsrcs2",
+			],
+		}
+		rust_ffi {
+			name: "librust_ffi",
+			crate_name: "rust_ffi",
+			crate_root: "foo.rs",
+			static: {
+				srcs: ["static_only.rs"],
+			},
+			shared: {
+				srcs: ["shared_only.rs"],
+			},
+			srcs: ["src1.rs"],
+		}
+		cc_library_static {
+			name: "cc_dep_static",
+		}
+		cc_library_shared {
+			name: "cc_dep_shared",
+		}
+		rust_library {
+			name: "libfizz_buzz_cc_deps",
+			crate_name:"fizz_buzz",
+			crate_root: "foo.rs",
+			srcs: ["src*.rs"],
+			shared_libs: ["cc_dep_shared"],
+			static_libs: ["cc_dep_static"],
+		}
+		rust_library {
+			name: "libfizz_buzz_intermediate_cc_deps",
+			crate_name:"fizz_buzz",
+			crate_root: "foo.rs",
+			srcs: ["src*.rs"],
+			rustlibs: ["libfizz_buzz_cc_deps"],
+		}
+		rust_library {
+			name: "libfizz_buzz_transitive_cc_deps",
+			crate_name:"fizz_buzz",
+			crate_root: "foo.rs",
+			srcs: ["src*.rs"],
+			rustlibs: ["libfizz_buzz_intermediate_cc_deps"],
+		}
+	`,
+		android.FixtureMergeMockFs(android.MockFS{
+			"src_lib1.rs":       nil,
+			"src_lib2.rs":       nil,
+			"src_lib3.rs":       nil,
+			"src_lib4.rs":       nil,
+			"src_filegroup1.rs": nil,
+			"src_filegroup2.rs": nil,
+			"static_only.rs":    nil,
+			"shared_only.rs":    nil,
+		}),
+	)
+
+	testcases := []struct {
+		name                     string
+		moduleName               string
+		variant                  string
+		rustcExpectedFilesToCopy []string
+		expectedFlags            []string
+	}{
+		{
+			name:       "rust_library (dylib)",
+			moduleName: "libfizz_buzz",
+			variant:    "android_arm64_armv8-a_dylib",
+			rustcExpectedFilesToCopy: android.Concat(sboxCompilationFiles, sboxCompilationFilesWithCc, []string{
+				"foo.rs",
+				"src_lib1.rs",
+				"src_lib2.rs",
+				"src_lib3.rs",
+				"src_lib4.rs",
+				"src_filegroup1.rs",
+				"src_filegroup2.rs",
+				"compile_data1.txt",
+				"compile_data2.txt",
+				"dylib_only.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/out/src_filegroup1.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/out/src_filegroup2.rs",
+
+				"out/soong/.intermediates/defaults/cc/common/libc/android_arm64_armv8-a_shared/libc.so",
+				"out/soong/.intermediates/defaults/cc/common/libc/android_arm64_armv8-a_shared/libc.so.toc",
+				"out/soong/.intermediates/defaults/cc/common/libm/android_arm64_armv8-a_shared/libm.so",
+				"out/soong/.intermediates/defaults/cc/common/libm/android_arm64_armv8-a_shared/libm.so.toc",
+				"out/soong/.intermediates/defaults/cc/common/libdl/android_arm64_armv8-a_shared/libdl.so",
+				"out/soong/.intermediates/defaults/cc/common/libdl/android_arm64_armv8-a_shared/libdl.so.toc",
+				"out/soong/.intermediates/defaults/rust/libstd/android_arm64_armv8-a_dylib/unstripped/libstd.dylib.so",
+				"out/soong/.intermediates/defaults/cc/common/crtbegin_so/android_arm64_armv8-a/crtbegin_so.o",
+				"out/soong/.intermediates/defaults/cc/common/crtend_so/android_arm64_armv8-a/crtend_so.o",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_dylib/libfizz_buzz.dylib.so.clippy",
+			}),
+			expectedFlags: []string{
+				"-C linker=build/soong/scripts/mkcratersp.py",
+				"--emit link",
+				"-o __SBOX_SANDBOX_DIR__/out/libfizz_buzz.dylib.so.rsp",
+				"--emit dep-info=__SBOX_SANDBOX_DIR__/out/libfizz_buzz.dylib.so.d.raw",
+				"foo.rs", // this is the entry point
+			},
+		},
+		{
+			name:       "rust_library (rlib dylib-std)",
+			moduleName: "libfizz_buzz",
+			variant:    "android_arm64_armv8-a_rlib_dylib-std",
+			rustcExpectedFilesToCopy: android.Concat(sboxCompilationFiles, []string{
+				"foo.rs",
+				"src_lib1.rs",
+				"src_lib2.rs",
+				"src_lib3.rs",
+				"src_lib4.rs",
+				"src_filegroup1.rs",
+				"src_filegroup2.rs",
+				"rlib_only.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_rlib_dylib-std/out/src_filegroup1.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_rlib_dylib-std/out/src_filegroup2.rs",
+				"out/soong/.intermediates/defaults/rust/libstd/android_arm64_armv8-a_dylib/unstripped/libstd.dylib.so",
+			}),
+			expectedFlags: []string{
+				"--emit link",
+				"-o __SBOX_SANDBOX_DIR__/out/libfizz_buzz.rlib",
+				"--emit dep-info=__SBOX_SANDBOX_DIR__/out/libfizz_buzz.rlib.d.raw",
+				"foo.rs", // this is the entry point
+			},
+		},
+		{
+			name:       "rust_library (rlib rlib-std)",
+			moduleName: "libfizz_buzz",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
+			rustcExpectedFilesToCopy: android.Concat(sboxCompilationFiles, []string{
+				"foo.rs",
+				"src_lib1.rs",
+				"src_lib2.rs",
+				"src_lib3.rs",
+				"src_lib4.rs",
+				"src_filegroup1.rs",
+				"src_filegroup2.rs",
+				"rlib_only.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_rlib_rlib-std/out/src_filegroup1.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_rlib_rlib-std/out/src_filegroup2.rs",
+				"out/soong/.intermediates/libfizz_buzz/android_arm64_armv8-a_rlib_rlib-std/libfizz_buzz.rlib.clippy",
+				"out/soong/.intermediates/defaults/rust/libstd/android_arm64_armv8-a_rlib/libstd.rlib",
+			}),
+			expectedFlags: []string{
+				"--emit link",
+				"-o __SBOX_SANDBOX_DIR__/out/libfizz_buzz.rlib",
+				"--emit dep-info=__SBOX_SANDBOX_DIR__/out/libfizz_buzz.rlib.d.raw",
+				"foo.rs", // this is the entry point
+			},
+		},
+		{
+			name:       "rust_binary",
+			moduleName: "fizz_buzz",
+			variant:    "android_arm64_armv8-a",
+			rustcExpectedFilesToCopy: android.Concat(sboxCompilationFiles, sboxCompilationFilesWithCc, []string{
+				"foo.rs",
+				"src_lib1.rs",
+				"src_lib2.rs",
+				"src_lib3.rs",
+				"src_lib4.rs",
+				"src_filegroup1.rs",
+				"src_filegroup2.rs",
+				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/out/src_filegroup1.rs",
+				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/out/src_filegroup2.rs",
+
+				"out/soong/.intermediates/defaults/rust/libstd/android_arm64_armv8-a_dylib/unstripped/libstd.dylib.so",
+				"out/soong/.intermediates/defaults/cc/common/crtbegin_dynamic/android_arm64_armv8-a/crtbegin_dynamic.o",
+				"out/soong/.intermediates/defaults/cc/common/crtend_android/android_arm64_armv8-a/crtend_android.o",
+				"out/soong/.intermediates/fizz_buzz/android_arm64_armv8-a/fizz_buzz.clippy",
+			}),
+			expectedFlags: []string{
+				"--emit link",
+				"-o __SBOX_SANDBOX_DIR__/out/fizz_buzz",
+				"--emit dep-info=__SBOX_SANDBOX_DIR__/out/fizz_buzz.d.raw",
+				"foo.rs", // this is the entry point
+			},
+		},
+		{
+			name:       "rust_ffi static lib variant",
+			moduleName: "librust_ffi",
+			variant:    "android_arm64_armv8-a_static",
+			rustcExpectedFilesToCopy: android.Concat(sboxCompilationFiles, []string{
+				"foo.rs",
+				"src1.rs",
+				"static_only.rs",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_static/librust_ffi.a.clippy",
+				"out/soong/.intermediates/defaults/rust/libstd/android_arm64_armv8-a_rlib/libstd.rlib",
+			}),
+			expectedFlags: []string{
+				"--emit link",
+				"-o __SBOX_SANDBOX_DIR__/out/librust_ffi.a",
+				"--emit dep-info=__SBOX_SANDBOX_DIR__/out/librust_ffi.a.d.raw",
+				"foo.rs", // this is the entry point
+			},
+		},
+		{
+			name:       "rust_ffi shared lib variant",
+			moduleName: "librust_ffi",
+			variant:    "android_arm64_armv8-a_shared",
+			rustcExpectedFilesToCopy: android.Concat(sboxCompilationFiles, sboxCompilationFilesWithCc, []string{
+				"foo.rs",
+				"src1.rs",
+				"shared_only.rs",
+
+				"out/soong/.intermediates/defaults/rust/libstd/android_arm64_armv8-a_dylib/unstripped/libstd.dylib.so",
+				"out/soong/.intermediates/defaults/cc/common/crtbegin_so/android_arm64_armv8-a/crtbegin_so.o",
+				"out/soong/.intermediates/defaults/cc/common/crtend_so/android_arm64_armv8-a/crtend_so.o",
+				"out/soong/.intermediates/librust_ffi/android_arm64_armv8-a_shared/librust_ffi.so.clippy",
+			}),
+			expectedFlags: []string{
+				"--emit link",
+				"-o __SBOX_SANDBOX_DIR__/out/librust_ffi.so",
+				"--emit dep-info=__SBOX_SANDBOX_DIR__/out/librust_ffi.so.d.raw",
+				"foo.rs", // this is the entry point
+			},
+		},
+		{
+			name:       "rust_library with cc deps (dylib)",
+			moduleName: "libfizz_buzz_cc_deps",
+			variant:    "android_arm64_armv8-a_dylib",
+			rustcExpectedFilesToCopy: []string{
+				"out/soong/.intermediates/cc_dep_static/android_arm64_armv8-a_static/cc_dep_static.a",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so.toc",
+			},
+		},
+		{
+			name:       "rust_library with cc deps (rlib rlib-std)",
+			moduleName: "libfizz_buzz_cc_deps",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
+			rustcExpectedFilesToCopy: []string{
+				"out/soong/.intermediates/cc_dep_static/android_arm64_armv8-a_static/cc_dep_static.a",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so.toc",
+			},
+		},
+		{
+			name:       "rust_library with cc deps (rlib dylib-std)",
+			moduleName: "libfizz_buzz_cc_deps",
+			variant:    "android_arm64_armv8-a_rlib_dylib-std",
+			rustcExpectedFilesToCopy: []string{
+				"out/soong/.intermediates/cc_dep_static/android_arm64_armv8-a_static/cc_dep_static.a",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so.toc",
+			},
+		},
+		{
+			name:       "rust_library with transitive cc deps (dylib)",
+			moduleName: "libfizz_buzz_transitive_cc_deps",
+			variant:    "android_arm64_armv8-a_dylib",
+			rustcExpectedFilesToCopy: []string{
+				"out/soong/.intermediates/cc_dep_static/android_arm64_armv8-a_static/cc_dep_static.a",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so.toc",
+			},
+		},
+		{
+			name:       "rust_library with transitive cc deps (rlib rlib-std)",
+			moduleName: "libfizz_buzz_transitive_cc_deps",
+			variant:    "android_arm64_armv8-a_rlib_rlib-std",
+			rustcExpectedFilesToCopy: []string{
+				"out/soong/.intermediates/cc_dep_static/android_arm64_armv8-a_static/cc_dep_static.a",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so.toc",
+			},
+		},
+		{
+			name:       "rust_library with transitive cc deps (rlib dylib-std)",
+			moduleName: "libfizz_buzz_transitive_cc_deps",
+			variant:    "android_arm64_armv8-a_rlib_dylib-std",
+			rustcExpectedFilesToCopy: []string{
+				"out/soong/.intermediates/cc_dep_static/android_arm64_armv8-a_static/cc_dep_static.a",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so",
+				"out/soong/.intermediates/cc_dep_shared/android_arm64_armv8-a_shared/cc_dep_shared.so.toc",
+			},
+		},
+	}
+
+	for _, tc := range testcases {
+		t.Run(tc.name, func(t *testing.T) {
+			writeFile := ctx.ModuleForTests(tc.moduleName, tc.variant).Rule("unescapedWriteFile")
+			contents := writeFile.BuildParams.Args["content"]
+			manifestProto := sbox_proto.Manifest{}
+			err := prototext.Unmarshal([]byte(contents), &manifestProto)
+			if err != nil {
+				t.Errorf("expected no errors unmarshaling manifest proto; got %v", err)
+			}
+
+			if len(manifestProto.Commands) != 1 {
+				t.Errorf("expected 1 command; got %v", len(manifestProto.Commands))
+			}
+
+			// check that sandbox contains correct files
+			rustc := manifestProto.Commands[0]
+			actualFilesToCopy := []string{}
+			for _, copy := range rustc.CopyBefore {
+				actualFilesToCopy = append(actualFilesToCopy, copy.GetFrom())
+			}
+			_, expectedFilesNotCopied, _ := android.ListSetDifference(tc.rustcExpectedFilesToCopy, actualFilesToCopy)
+			if len(expectedFilesNotCopied) > 0 {
+				t.Errorf("did not copy expected files to sbox: %v;\n files copied: %v", expectedFilesNotCopied, actualFilesToCopy)
+			}
+
+			rustcCmd := proptools.String(rustc.Command)
+			for _, flag := range tc.expectedFlags {
+				android.AssertStringDoesContain(
+					t,
+					fmt.Sprintf(
+						"missing flag in rustc invocation; expected to find substring %q; got %q",
+						flag,
+						rustcCmd,
+					),
+					rustcCmd,
+					flag,
+				)
+			}
+		})
+	}
+}
diff --git a/rust/sanitize_test.go b/rust/sanitize_test.go
index 43e95f4..d6a14b2 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("rustLink").Implicits
+	implicits := m.Rule("rustc").Implicits
 	for _, lib := range implicits {
 		if strings.Contains(lib.Rel(), note_async) {
 			found = Async
diff --git a/rust/testing.go b/rust/testing.go
index 3fe751e..9951937 100644
--- a/rust/testing.go
+++ b/rust/testing.go
@@ -52,6 +52,22 @@
 
 func GatherRequiredDepsForTest() string {
 	bp := `
+		prebuilt_build_tool {
+			name: "rustc",
+			src: "linux-x86/1.69.0/bin/rustc",
+			deps: [
+				"linux-x86/1.69.0/lib/libstd.so",
+				"linux-x86/1.69.0/lib64/libc++.so.1",
+			],
+		}
+		prebuilt_build_tool {
+			name: "clippy-driver",
+			src: "linux-x86/1.69.0/bin/clippy-driver",
+		}
+		prebuilt_build_tool {
+			name: "rustdoc",
+			src: "linux-x86/1.69.0/bin/rustdoc",
+		}
 		rust_prebuilt_library {
 				name: "libstd",
 				crate_name: "std",
@@ -63,6 +79,25 @@
 				},
 				host_supported: true,
 				sysroot: true,
+			rlibs: [
+			    "libaddr2line",
+			    "libadler",
+			    "liballoc",
+			    "libcfg_if",
+			    "libcompiler_builtins",
+			    "libcore",
+			    "libgimli",
+			    "libhashbrown",
+			    "liblibc",
+			    "libmemchr",
+			    "libminiz_oxide",
+			    "libobject",
+			    "libpanic_unwind",
+			    "librustc_demangle",
+			    "librustc_std_workspace_alloc",
+			    "librustc_std_workspace_core",
+			    "libstd_detect",
+			],
 		}
 		//////////////////////////////
 		// Device module requirements
@@ -99,6 +134,278 @@
 			nocrt: true,
 			system_shared_libs: [],
 		}
+		rust_library_rlib {
+			name: "libaddr2line",
+			crate_name: "addr2line",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libadler",
+			crate_name: "adler",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "liballoc",
+			crate_name: "alloc",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libcfg_if",
+			crate_name: "cfg_if",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libcompiler_builtins",
+			crate_name: "compiler_builtins",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libcore",
+			crate_name: "core",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libgimli",
+			crate_name: "gimli",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libhashbrown",
+			crate_name: "hashbrown",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "liblibc",
+			crate_name: "libc",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libmemchr",
+			crate_name: "memchr",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libminiz_oxide",
+			crate_name: "miniz_oxide",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libobject",
+			crate_name: "object",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libpanic_unwind",
+			crate_name: "panic_unwind",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "librustc_demangle",
+			crate_name: "rustc_demangle",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "librustc_std_workspace_alloc",
+			crate_name: "rustc_std_workspace_alloc",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "librustc_std_workspace_core",
+			crate_name: "rustc_std_workspace_core",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
+		rust_library_rlib {
+			name: "libstd_detect",
+			crate_name: "std_detect",
+			enabled:true,
+			srcs: ["foo.rs"],
+			no_stdlibs: true,
+			product_available: true,
+			host_supported: true,
+			vendor_available: true,
+			vendor_ramdisk_available: true,
+			recovery_available: true,
+			native_coverage: false,
+			sysroot: true,
+			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
+			min_sdk_version: "29",
+		}
 		rust_library {
 			name: "libstd",
 			crate_name: "std",
@@ -113,6 +420,25 @@
 			sysroot: true,
 			apex_available: ["//apex_available:platform", "//apex_available:anyapex"],
 			min_sdk_version: "29",
+			rlibs: [
+			    "libaddr2line",
+			    "libadler",
+			    "liballoc",
+			    "libcfg_if",
+			    "libcompiler_builtins",
+			    "libcore",
+			    "libgimli",
+			    "libhashbrown",
+			    "liblibc",
+			    "libmemchr",
+			    "libminiz_oxide",
+			    "libobject",
+			    "libpanic_unwind",
+			    "librustc_demangle",
+			    "librustc_std_workspace_alloc",
+			    "librustc_std_workspace_core",
+			    "libstd_detect",
+			],
 		}
 		rust_library {
 			name: "libtest",
diff --git a/rust/toolchain_library.go b/rust/toolchain_library.go
index 326d529..cb345a4 100644
--- a/rust/toolchain_library.go
+++ b/rust/toolchain_library.go
@@ -18,9 +18,12 @@
 
 import (
 	"path"
+	"path/filepath"
 
 	"android/soong/android"
 	"android/soong/rust/config"
+
+	"github.com/google/blueprint/proptools"
 )
 
 // This module is used to compile the rust toolchain libraries
@@ -33,11 +36,15 @@
 		rustToolchainLibraryRlibFactory)
 	android.RegisterModuleType("rust_toolchain_library_dylib",
 		rustToolchainLibraryDylibFactory)
+	android.RegisterModuleType("rust_toolchain_rustc_prebuilt",
+		rustToolchainRustcPrebuiltFactory)
 }
 
 type toolchainLibraryProperties struct {
-	// path to the toolchain source, relative to the top of the toolchain source
-	Toolchain_src *string `android:"arch_variant"`
+	// path to the toolchain crate root, relative to the top of the toolchain source
+	Toolchain_crate_root *string `android:"arch_variant"`
+	// path to the rest of the toolchain srcs, relative to the top of the toolchain source
+	Toolchain_srcs []string `android:"arch_variant"`
 }
 
 type toolchainLibraryDecorator struct {
@@ -82,16 +89,21 @@
 
 func rustSetToolchainSource(ctx android.LoadHookContext) {
 	if toolchainLib, ok := ctx.Module().(*Module).compiler.(*toolchainLibraryDecorator); ok {
-		prefix := "linux-x86/" + GetRustPrebuiltVersion(ctx)
-		newSrcs := []string{path.Join(prefix, android.String(toolchainLib.Properties.Toolchain_src))}
+		prefix := filepath.Join(config.HostPrebuiltTag(ctx.Config()), GetRustPrebuiltVersion(ctx))
+		versionedCrateRoot := path.Join(prefix, android.String(toolchainLib.Properties.Toolchain_crate_root))
+		versionedSrcs := make([]string, len(toolchainLib.Properties.Toolchain_srcs))
+		for i, src := range toolchainLib.Properties.Toolchain_srcs {
+			versionedSrcs[i] = path.Join(prefix, src)
+		}
 
 		type props struct {
-			Srcs []string
+			Crate_root *string
+			Srcs       []string
 		}
 		p := &props{}
-		p.Srcs = newSrcs
+		p.Crate_root = &versionedCrateRoot
+		p.Srcs = versionedSrcs
 		ctx.AppendProperties(p)
-
 	} else {
 		ctx.ModuleErrorf("Called rustSetToolchainSource on a non-Rust Module.")
 	}
@@ -101,3 +113,47 @@
 func GetRustPrebuiltVersion(ctx android.LoadHookContext) string {
 	return ctx.AConfig().GetenvWithDefault("RUST_PREBUILTS_VERSION", config.RustDefaultVersion)
 }
+
+type toolchainRustcPrebuiltProperties struct {
+	// path to rustc prebuilt, relative to the top of the toolchain source
+	Toolchain_prebuilt_src *string
+	// path to deps, relative to the top of the toolchain source
+	Toolchain_deps []string
+	// path to deps, relative to module directory
+	Deps []string
+}
+
+func rustToolchainRustcPrebuiltFactory() android.Module {
+	module := android.NewPrebuiltBuildTool()
+	module.AddProperties(&toolchainRustcPrebuiltProperties{})
+	android.AddLoadHook(module, func(ctx android.LoadHookContext) {
+		var toolchainProps *toolchainRustcPrebuiltProperties
+		for _, p := range ctx.Module().GetProperties() {
+			toolchainProperties, ok := p.(*toolchainRustcPrebuiltProperties)
+			if ok {
+				toolchainProps = toolchainProperties
+			}
+		}
+
+		if toolchainProps.Toolchain_prebuilt_src == nil {
+			ctx.PropertyErrorf("toolchain_prebuilt_src", "must set path to rustc prebuilt")
+		}
+
+		prefix := filepath.Join(config.HostPrebuiltTag(ctx.Config()), GetRustPrebuiltVersion(ctx))
+		deps := make([]string, 0, len(toolchainProps.Toolchain_deps)+len(toolchainProps.Deps))
+		for _, d := range toolchainProps.Toolchain_deps {
+			deps = append(deps, path.Join(prefix, d))
+		}
+		deps = append(deps, toolchainProps.Deps...)
+
+		props := struct {
+			Src  *string
+			Deps []string
+		}{
+			Src:  proptools.StringPtr(path.Join(prefix, *toolchainProps.Toolchain_prebuilt_src)),
+			Deps: deps,
+		}
+		ctx.AppendProperties(&props)
+	})
+	return module
+}
diff --git a/rust/vendor_snapshot_test.go b/rust/vendor_snapshot_test.go
index 1e7e7d3..621a724 100644
--- a/rust/vendor_snapshot_test.go
+++ b/rust/vendor_snapshot_test.go
@@ -1051,7 +1051,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("rustLink").Args["linkFlags"]
+	libclientLdFlags := ctx.ModuleForTests("libclient", sharedVariant).Rule("rustc").RuleParams.Command
 	for _, input := range [][]string{
 		[]string{sharedVariant, "libvndk.vndk.30.arm64"},
 		[]string{staticVariant, "libvendor.vendor_static.30.arm64"},
@@ -1119,7 +1119,7 @@
 		t.Errorf("Unexpected rust rlib name in AndroidMk: %q, expected: %q\n", rustVendorBinMkDylibName, expectedRustVendorSnapshotName)
 	}
 
-	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustLink").Args["linkFlags"]
+	binWithoutSnapshotLdFlags := ctx.ModuleForTests("bin_without_snapshot", binaryVariant).Rule("rustc").RuleParams.Command
 	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/sh/sh_binary.go b/sh/sh_binary.go
index 4fe6fdd..79a885f 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -583,7 +583,7 @@
 	Auto_gen_config      *bool
 }
 
-func (m *ShBinary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *ShBinary) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
 		android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
 
@@ -611,7 +611,7 @@
 	ctx.CreateBazelTargetModule(props, android.CommonAttributes{Name: m.Name()}, attrs)
 }
 
-func (m *ShTest) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *ShTest) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	srcs := bazel.MakeLabelListAttribute(
 		android.BazelLabelForModuleSrc(ctx, []string{*m.properties.Src}))
 
diff --git a/sysprop/sysprop_library.go b/sysprop/sysprop_library.go
index a2c0fb7..d16bf32 100644
--- a/sysprop/sysprop_library.go
+++ b/sysprop/sysprop_library.go
@@ -591,7 +591,7 @@
 }
 
 // TODO(b/240463568): Additional properties will be added for API validation
-func (m *syspropLibrary) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (m *syspropLibrary) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	labels := cc.SyspropLibraryLabels{
 		SyspropLibraryLabel: m.BaseModuleName(),
 		SharedLibraryLabel:  m.CcImplementationModuleName(),
diff --git a/tradefed/autogen_bazel.go b/tradefed/autogen_bazel.go
index 8283984..cc16176 100644
--- a/tradefed/autogen_bazel.go
+++ b/tradefed/autogen_bazel.go
@@ -49,7 +49,7 @@
 }
 
 func GetTestConfigAttributes(
-	ctx android.TopDownMutatorContext,
+	ctx android.Bp2buildMutatorContext,
 	testConfig *string,
 	extraTestConfigs []string,
 	autoGenConfig *bool,
@@ -93,7 +93,7 @@
 }
 
 func GetTestConfig(
-	ctx android.TopDownMutatorContext,
+	ctx android.Bp2buildMutatorContext,
 	testConfig *string,
 ) *bazel.Label {
 
diff --git a/xml/xml.go b/xml/xml.go
index 20a26f5..65fe12a 100644
--- a/xml/xml.go
+++ b/xml/xml.go
@@ -145,7 +145,7 @@
 	Schema            *string
 }
 
-func (p *prebuiltEtcXml) ConvertWithBp2build(ctx android.TopDownMutatorContext) {
+func (p *prebuiltEtcXml) ConvertWithBp2build(ctx android.Bp2buildMutatorContext) {
 	baseAttrs, convertible := p.PrebuiltEtc.Bp2buildHelper(ctx)
 
 	if !convertible {