Merge "Update RustDefaultVersion to 1.58.0"
diff --git a/README.md b/README.md
index b820fd1..127c52c 100644
--- a/README.md
+++ b/README.md
@@ -452,10 +452,9 @@
 
 The values of the variables can be set from a product's `BoardConfig.mk` file:
 ```
-$(call add_soong_config_namespace, acme)
-$(call add_soong_config_var_value, acme, board, soc_a)
-$(call add_soong_config_var_value, acme, feature, true)
-$(call add_soong_config_var_value, acme, width, 200)
+$(call soong_config_set,acme,board,soc_a)
+$(call soong_config_set,acme,feature,true)
+$(call soong_config_set,acme,width,200)
 ```
 
 The `acme_cc_defaults` module type can be used anywhere after the definition in
diff --git a/android/bazel.go b/android/bazel.go
index 9f38c3b..99cc30c 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -236,6 +236,8 @@
 	// Configure modules in these directories to enable bp2build_available: true or false by default.
 	bp2buildDefaultConfig = Bp2BuildConfig{
 		"art/libdexfile":                        Bp2BuildDefaultTrueRecursively,
+		"art/runtime":                           Bp2BuildDefaultTrueRecursively,
+		"art/tools":                             Bp2BuildDefaultTrue,
 		"bionic":                                Bp2BuildDefaultTrueRecursively,
 		"bootable/recovery/tools/recovery_l10n": Bp2BuildDefaultTrue,
 		"build/bazel/examples/soong_config_variables":        Bp2BuildDefaultTrueRecursively,
@@ -245,6 +247,7 @@
 		"build/soong/cc/libbuildversion":                     Bp2BuildDefaultTrue, // Skip tests subdir
 		"build/soong/cc/ndkstubgen":                          Bp2BuildDefaultTrue,
 		"build/soong/cc/symbolfile":                          Bp2BuildDefaultTrue,
+		"build/soong/linkerconfig":                           Bp2BuildDefaultTrueRecursively,
 		"build/soong/scripts":                                Bp2BuildDefaultTrueRecursively,
 		"cts/common/device-side/nativetesthelper/jni":        Bp2BuildDefaultTrueRecursively,
 		"development/apps/DevelopmentSettings":               Bp2BuildDefaultTrue,
@@ -323,6 +326,7 @@
 		"packages/apps/DevCamera":                            Bp2BuildDefaultTrue,
 		"packages/apps/HTMLViewer":                           Bp2BuildDefaultTrue,
 		"packages/apps/Protips":                              Bp2BuildDefaultTrue,
+		"packages/modules/StatsD/lib/libstatssocket":         Bp2BuildDefaultTrueRecursively,
 		"packages/modules/adb":                               Bp2BuildDefaultTrue,
 		"packages/modules/adb/apex":                          Bp2BuildDefaultTrue,
 		"packages/modules/adb/crypto":                        Bp2BuildDefaultTrueRecursively,
@@ -336,6 +340,8 @@
 		"packages/services/Car/tests/SampleRearViewCamera":   Bp2BuildDefaultTrue,
 		"prebuilts/clang/host/linux-x86":                     Bp2BuildDefaultTrueRecursively,
 		"system/apex":                                        Bp2BuildDefaultFalse, // TODO(b/207466993): flaky failures
+		"system/apex/proto":                                  Bp2BuildDefaultTrueRecursively,
+		"system/apex/libs":                                   Bp2BuildDefaultTrueRecursively,
 		"system/core/debuggerd":                              Bp2BuildDefaultTrueRecursively,
 		"system/core/diagnose_usb":                           Bp2BuildDefaultTrueRecursively,
 		"system/core/libasyncio":                             Bp2BuildDefaultTrue,
@@ -364,6 +370,16 @@
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
 	bp2buildModuleDoNotConvertList = []string{
+		"libnativehelper_compat_libc", // Broken compile: implicit declaration of function 'strerror_r' is invalid in C99
+
+		"libart",                             // depends on unconverted modules: art_operator_srcs, libodrstatslog, libelffile, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfile, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbase, libprofile, cpp-define-generator-asm-support, apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api
+		"libart-runtime-gtest",               // depends on unconverted modules: libgtest_isolated, libart-compiler, libdexfile, libprofile, libartbase, libbacktrace, libartbase-art-gtest
+		"libart_headers",                     // depends on unconverted modules: art_libartbase_headers
+		"libartd",                            // depends on unconverted modules: apex-info-list-tinyxml, libtinyxml2, libnativeloader-headers, libstatssocket, heapprofd_client_api, art_operator_srcs, libodrstatslog, libelffiled, art_cmdlineparser_headers, cpp-define-generator-definitions, libcpu_features, libdexfiled, libartpalette, libbacktrace, libnativebridge, libnativeloader, libsigchain, libunwindstack, libartbased, libprofiled, cpp-define-generator-asm-support
+		"libartd-runtime-gtest",              // depends on unconverted modules: libgtest_isolated, libartd-compiler, libdexfiled, libprofiled, libartbased, libbacktrace, libartbased-art-gtest
+		"libstatslog_art",                    // depends on unconverted modules: statslog_art.cpp, statslog_art.h
+		"statslog_art.h", "statslog_art.cpp", // depends on unconverted modules: stats-log-api-gen
+
 		"libandroid_runtime_lazy", // depends on unconverted modules: libbinder_headers
 		"libcmd",                  // depends on unconverted modules: libbinder
 
@@ -403,22 +419,23 @@
 		"libdebuggerd",       // depends on unconverted modules libdexfile_support, libunwindstack, gwp_asan_crash_handler, libtombstone_proto, libprotobuf-cpp-lite
 		"libdexfile_static",  // depends on libartpalette, libartbase, libdexfile, which are of unsupported type: art_cc_library.
 
-		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
-		"host_bionic_linker_script", // depends on extract_linker, a go binary.
-		"static_crasher",            // depends on unconverted modules: libdebuggerd_handler
+		"static_crasher", // depends on unconverted modules: libdebuggerd_handler
 
 		"pbtombstone", "crash_dump", // depends on libdebuggerd, libunwindstack
 
 		"libbase_ndk", // http://b/186826477, fails to link libctscamera2_jni for device (required for CtsCameraTestCases)
 
-		"libprotobuf-python",               // contains .proto sources
 		"libprotobuf-internal-protos",      // b/210751803, we don't handle path property for filegroups
 		"libprotobuf-internal-python-srcs", // b/210751803, we don't handle path property for filegroups
 		"libprotobuf-java-full",            // b/210751803, we don't handle path property for filegroups
 		"libprotobuf-java-util-full",       // b/210751803, we don't handle path property for filegroups
 		"conscrypt",                        // b/210751803, we don't handle path property for filegroups
 
-		"conv_linker_config", // depends on linker_config_proto, a python lib with proto sources
+		// python protos
+		"libprotobuf-python",                           // contains .proto sources
+		"conv_linker_config",                           // depends on linker_config_proto, a python lib with proto sources
+		"apex_build_info_proto", "apex_manifest_proto", // a python lib with proto sources
+		"linker_config_proto", // contains .proto sources
 
 		"brotli-fuzzer-corpus", // b/202015218: outputs are in location incompatible with bazel genrule handling.
 
@@ -448,6 +465,11 @@
 
 		"libdexfile",  // depends on unconverted modules: dexfile_operator_srcs, libartbase, libartpalette,
 		"libdexfiled", // depends on unconverted modules: dexfile_operator_srcs, libartbased, libartpalette
+
+		// go deps:
+		"apex-protos",               // depends on unconverted modules: soong_zip
+		"host_bionic_linker_asm",    // depends on extract_linker, a go binary.
+		"host_bionic_linker_script", // depends on extract_linker, a go binary.
 	}
 
 	// Per-module denylist of cc_library modules to only generate the static
diff --git a/android/bazel_paths.go b/android/bazel_paths.go
index 62e6156..f353a9d 100644
--- a/android/bazel_paths.go
+++ b/android/bazel_paths.go
@@ -92,6 +92,7 @@
 	GetDirectDep(name string) (blueprint.Module, blueprint.DependencyTag)
 	ModuleFromName(name string) (blueprint.Module, bool)
 	AddUnconvertedBp2buildDep(string)
+	AddMissingBp2buildDep(dep string)
 }
 
 // BazelLabelForModuleDeps expects a list of reference to other modules, ("<module>"
@@ -129,8 +130,10 @@
 		}
 		if m, t := SrcIsModuleWithTag(module); m != "" {
 			l := getOtherModuleLabel(ctx, m, t, moduleToLabelFn)
-			l.OriginalModuleName = bpText
-			labels.Includes = append(labels.Includes, l)
+			if l != nil {
+				l.OriginalModuleName = bpText
+				labels.Includes = append(labels.Includes, *l)
+			}
 		} else {
 			ctx.ModuleErrorf("%q, is not a module reference", module)
 		}
@@ -157,11 +160,17 @@
 }
 
 func BazelLabelForModuleSrcSingle(ctx BazelConversionPathContext, path string) bazel.Label {
-	return BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+	if srcs := BazelLabelForModuleSrcExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
+		return srcs[0]
+	}
+	return bazel.Label{}
 }
 
 func BazelLabelForModuleDepSingle(ctx BazelConversionPathContext, path string) bazel.Label {
-	return BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes[0]
+	if srcs := BazelLabelForModuleDepsExcludes(ctx, []string{path}, []string(nil)).Includes; len(srcs) > 0 {
+		return srcs[0]
+	}
+	return bazel.Label{}
 }
 
 // BazelLabelForModuleSrc expects a list of path (relative to local module directory) and module
@@ -328,9 +337,9 @@
 	for _, p := range paths {
 		if m, tag := SrcIsModuleWithTag(p); m != "" {
 			l := getOtherModuleLabel(ctx, m, tag, BazelModuleLabel)
-			if !InList(l.Label, expandedExcludes) {
+			if l != nil && !InList(l.Label, expandedExcludes) {
 				l.OriginalModuleName = fmt.Sprintf(":%s", m)
-				labels.Includes = append(labels.Includes, l)
+				labels.Includes = append(labels.Includes, *l)
 			}
 		} else {
 			var expandedPaths []bazel.Label
@@ -354,10 +363,16 @@
 // module. The label will be relative to the current directory if appropriate. The dependency must
 // already be resolved by either deps mutator or path deps mutator.
 func getOtherModuleLabel(ctx BazelConversionPathContext, dep, tag string,
-	labelFromModule func(BazelConversionPathContext, blueprint.Module) string) bazel.Label {
+	labelFromModule func(BazelConversionPathContext, blueprint.Module) string) *bazel.Label {
 	m, _ := ctx.ModuleFromName(dep)
+	// The module was not found in an Android.bp file, this is often due to:
+	//		* a limited manifest
+	//		* a required module not being converted from Android.mk
 	if m == nil {
-		panic(fmt.Errorf("No module named %q found, but was a direct dep of %q", dep, ctx.Module().Name()))
+		ctx.AddMissingBp2buildDep(dep)
+		return &bazel.Label{
+			Label: ":" + dep + "__BP2BUILD__MISSING__DEP",
+		}
 	}
 	if !convertedToBazel(ctx, m) {
 		ctx.AddUnconvertedBp2buildDep(dep)
@@ -371,7 +386,7 @@
 		otherLabel = bazelShortLabel(otherLabel)
 	}
 
-	return bazel.Label{
+	return &bazel.Label{
 		Label: otherLabel,
 	}
 }
diff --git a/android/module.go b/android/module.go
index 71027b2..4da201c 100644
--- a/android/module.go
+++ b/android/module.go
@@ -321,6 +321,9 @@
 	// AddUnconvertedBp2buildDep stores module name of a direct dependency that was not converted via bp2build
 	AddUnconvertedBp2buildDep(dep string)
 
+	// AddMissingBp2buildDep stores the module name of a direct dependency that was not found.
+	AddMissingBp2buildDep(dep string)
+
 	Target() Target
 	TargetPrimary() bool
 
@@ -517,6 +520,7 @@
 	// Bp2buildTargets returns the target(s) generated for Bazel via bp2build for this module
 	Bp2buildTargets() []bp2buildInfo
 	GetUnconvertedBp2buildDeps() []string
+	GetMissingBp2buildDeps() []string
 
 	BuildParamsForTests() []BuildParams
 	RuleParamsForTests() map[blueprint.Rule]blueprint.RuleParams
@@ -858,6 +862,9 @@
 	// UnconvertedBp2buildDep stores the module names of direct dependency that were not converted to
 	// Bazel
 	UnconvertedBp2buildDeps []string `blueprint:"mutated"`
+
+	// MissingBp2buildDep stores the module names of direct dependency that were not found
+	MissingBp2buildDeps []string `blueprint:"mutated"`
 }
 
 // CommonAttributes represents the common Bazel attributes from which properties
@@ -1371,12 +1378,23 @@
 	*unconvertedDeps = append(*unconvertedDeps, dep)
 }
 
+// AddMissingBp2buildDep stores module name of a dependency that was not found in a Android.bp file.
+func (b *baseModuleContext) AddMissingBp2buildDep(dep string) {
+	missingDeps := &b.Module().base().commonProperties.MissingBp2buildDeps
+	*missingDeps = append(*missingDeps, dep)
+}
+
 // GetUnconvertedBp2buildDeps returns the list of module names of this module's direct dependencies that
 // were not converted to Bazel.
 func (m *ModuleBase) GetUnconvertedBp2buildDeps() []string {
 	return FirstUniqueStrings(m.commonProperties.UnconvertedBp2buildDeps)
 }
 
+// GetMissingBp2buildDeps eturns the list of module names that were not found in Android.bp files.
+func (m *ModuleBase) GetMissingBp2buildDeps() []string {
+	return FirstUniqueStrings(m.commonProperties.MissingBp2buildDeps)
+}
+
 func (m *ModuleBase) AddJSONData(d *map[string]interface{}) {
 	(*d)["Android"] = map[string]interface{}{
 		// Properties set in Blueprint or in blueprint of a defaults modules
diff --git a/apex/apex.go b/apex/apex.go
index 635ff30..8668a78 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -1765,13 +1765,17 @@
 				}
 			case bcpfTag:
 				{
-					if _, ok := child.(*java.BootclasspathFragmentModule); !ok {
+					bcpfModule, ok := child.(*java.BootclasspathFragmentModule)
+					if !ok {
 						ctx.PropertyErrorf("bootclasspath_fragments", "%q is not a bootclasspath_fragment module", depName)
 						return false
 					}
 
 					filesToAdd := apexBootclasspathFragmentFiles(ctx, child)
 					filesInfo = append(filesInfo, filesToAdd...)
+					for _, makeModuleName := range bcpfModule.BootImageDeviceInstallMakeModules() {
+						a.requiredDeps = append(a.requiredDeps, makeModuleName)
+					}
 					return true
 				}
 			case sscpfTag:
@@ -2175,13 +2179,15 @@
 	var filesToAdd []apexFile
 
 	// Add the boot image files, e.g. .art, .oat and .vdex files.
-	for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
-		dirInApex := filepath.Join("javalib", arch.String())
-		for _, f := range files {
-			androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
-			// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
-			af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
-			filesToAdd = append(filesToAdd, af)
+	if bootclasspathFragmentInfo.ShouldInstallBootImageInApex() {
+		for arch, files := range bootclasspathFragmentInfo.AndroidBootImageFilesByArchType() {
+			dirInApex := filepath.Join("javalib", arch.String())
+			for _, f := range files {
+				androidMkModuleName := "javalib_" + arch.String() + "_" + filepath.Base(f.String())
+				// TODO(b/177892522) - consider passing in the bootclasspath fragment module here instead of nil
+				af := newApexFile(ctx, f, androidMkModuleName, dirInApex, etc, nil)
+				filesToAdd = append(filesToAdd, af)
+			}
 		}
 	}
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index 727a1f2..59545c2 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -8739,6 +8739,22 @@
 	})
 }
 
+// Verifies that the APEX depends on all the Make modules in the list.
+func ensureContainsRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
+	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
+	for _, dep := range deps {
+		android.AssertStringListContains(t, "", a.requiredDeps, dep)
+	}
+}
+
+// Verifies that the APEX does not depend on any of the Make modules in the list.
+func ensureDoesNotContainRequiredDeps(t *testing.T, ctx *android.TestContext, moduleName, variant string, deps []string) {
+	a := ctx.ModuleForTests(moduleName, variant).Module().(*apexBundle)
+	for _, dep := range deps {
+		android.AssertStringListDoesNotContain(t, "", a.requiredDeps, dep)
+	}
+}
+
 func TestMain(m *testing.M) {
 	os.Exit(m.Run())
 }
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index ce828e1..8f44fc5 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -410,6 +410,7 @@
 			// bootclasspath_fragment's contents property.
 			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
 			addSource("foo", "bar"),
+			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
@@ -437,12 +438,62 @@
 			`mybootclasspathfragment`,
 		})
 
+		// The boot images are installed in the APEX by Soong, so there shouldn't be any dexpreopt-related Make modules.
+		ensureDoesNotContainRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+		})
+
 		// Make sure that the source bootclasspath_fragment copies its dex files to the predefined
 		// locations for the art image.
 		module := result.ModuleForTests("mybootclasspathfragment", "android_common_apex10000")
 		checkCopiesToPredefinedLocationForArt(t, result.Config, module, "bar", "foo")
 	})
 
+	t.Run("boot image files from source no boot image in apex", func(t *testing.T) {
+		result := android.GroupFixturePreparers(
+			commonPreparer,
+
+			// Configure some libraries in the art bootclasspath_fragment that match the source
+			// bootclasspath_fragment's contents property.
+			java.FixtureConfigureBootJars("com.android.art:foo", "com.android.art:bar"),
+			addSource("foo", "bar"),
+			java.FixtureSetBootImageInstallDirOnDevice("art", "system/framework"),
+		).RunTest(t)
+
+		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"etc/boot-image.prof",
+			"etc/classpaths/bootclasspath.pb",
+			"javalib/bar.jar",
+			"javalib/foo.jar",
+		})
+
+		ensureContainsRequiredDeps(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
+			"mybootclasspathfragment-dexpreopt-arm64-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm64-boot-bar.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot.vdex",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.art",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.oat",
+			"mybootclasspathfragment-dexpreopt-arm-boot-bar.vdex",
+		})
+	})
+
 	t.Run("boot image disable generate profile", func(t *testing.T) {
 		result := android.GroupFixturePreparers(
 			commonPreparer,
@@ -472,6 +523,8 @@
 
 			// Make sure that a preferred prebuilt with consistent contents doesn't affect the apex.
 			addPrebuilt(true, "foo", "bar"),
+
+			java.FixtureSetBootImageInstallDirOnDevice("art", "apex/com.android.art/javalib"),
 		).RunTest(t)
 
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
diff --git a/bazel/properties.go b/bazel/properties.go
index 870d293..1300a53 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -492,7 +492,7 @@
 		// Verify post-condition; this should never fail, provided no additional
 		// axes are introduced.
 		if len(ba.ConfigurableValues) > 1 {
-			panic(fmt.Errorf("error in collapsing attribute: %s", ba))
+			panic(fmt.Errorf("error in collapsing attribute: %#v", ba))
 		}
 	}
 	return nil
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index 54b59af..5887d06 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -324,6 +324,15 @@
 						return
 					}
 				}
+				if unconvertedDeps := aModule.GetMissingBp2buildDeps(); len(unconvertedDeps) > 0 {
+					msg := fmt.Sprintf("%q depends on missing modules: %s", m.Name(), strings.Join(unconvertedDeps, ", "))
+					if ctx.unconvertedDepMode == warnUnconvertedDeps {
+						metrics.moduleWithMissingDepsMsgs = append(metrics.moduleWithMissingDepsMsgs, msg)
+					} else if ctx.unconvertedDepMode == errorModulesUnconvertedDeps {
+						errs = append(errs, fmt.Errorf(msg))
+						return
+					}
+				}
 				targets = generateBazelTargets(bpCtx, aModule)
 				for _, t := range targets {
 					// A module can potentially generate more than 1 Bazel
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 1440b6f..b21a477 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -301,6 +301,19 @@
 			},
 		},
 		{
+			description: "non-existent dep",
+			blueprint: `custom {
+  name: "has_dep",
+  arch_paths: [":dep"],
+  bazel_module: { bp2build_available: true },
+}`,
+			expectedBazelTargets: []string{
+				makeBazelTarget("custom", "has_dep", attrNameToString{
+					"arch_paths": `[":dep__BP2BUILD__MISSING__DEP"]`,
+				}),
+			},
+		},
+		{
 			description: "arch-variant srcs",
 			blueprint: `custom {
     name: "arch_paths",
diff --git a/bp2build/metrics.go b/bp2build/metrics.go
index 68ac544..557ea99 100644
--- a/bp2build/metrics.go
+++ b/bp2build/metrics.go
@@ -30,6 +30,10 @@
 	// NOTE: NOT in the .proto
 	moduleWithUnconvertedDepsMsgs []string
 
+	// List of modules with missing deps
+	// NOTE: NOT in the .proto
+	moduleWithMissingDepsMsgs []string
+
 	// List of converted modules
 	convertedModules []string
 }
@@ -54,13 +58,21 @@
 		generatedTargetCount += count
 	}
 	fmt.Printf(
-		"[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules.\n%d converted modules have unconverted deps: \n\t%s",
+		`[bp2build] Converted %d Android.bp modules to %d total generated BUILD targets. Included %d handcrafted BUILD targets. There are %d total Android.bp modules.
+%d converted modules have unconverted deps:
+	%s
+%d converted modules have missing deps:
+	%s
+`,
 		metrics.generatedModuleCount,
 		generatedTargetCount,
 		metrics.handCraftedModuleCount,
 		metrics.TotalModuleCount(),
 		len(metrics.moduleWithUnconvertedDepsMsgs),
-		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"))
+		strings.Join(metrics.moduleWithUnconvertedDepsMsgs, "\n\t"),
+		len(metrics.moduleWithMissingDepsMsgs),
+		strings.Join(metrics.moduleWithMissingDepsMsgs, "\n\t"),
+	)
 }
 
 const bp2buildMetricsFilename = "bp2build_metrics.pb"
diff --git a/java/bootclasspath_fragment.go b/java/bootclasspath_fragment.go
index bfe895c..fee51d7 100644
--- a/java/bootclasspath_fragment.go
+++ b/java/bootclasspath_fragment.go
@@ -219,6 +219,11 @@
 
 	// Collect the module directory for IDE info in java/jdeps.go.
 	modulePaths []string
+
+	// Installs for on-device boot image files. This list has entries only if the installs should be
+	// handled by Make (e.g., the boot image should be installed on the system partition, rather than
+	// in the APEX).
+	bootImageDeviceInstalls []dexpreopterInstall
 }
 
 // commonBootclasspathFragment defines the methods that are implemented by both source and prebuilt
@@ -387,6 +392,9 @@
 	// Map from arch type to the boot image files.
 	bootImageFilesByArch bootImageFilesByArch
 
+	// True if the boot image should be installed in the APEX.
+	shouldInstallBootImageInApex bool
+
 	// Map from the base module name (without prebuilt_ prefix) of a fragment's contents module to the
 	// hidden API encoded dex jar path.
 	contentModuleDexJarPaths bootDexJarByModule
@@ -410,6 +418,11 @@
 	return i.bootImageFilesByArch
 }
 
+// Return true if the boot image should be installed in the APEX.
+func (i *BootclasspathFragmentApexContentInfo) ShouldInstallBootImageInApex() bool {
+	return i.shouldInstallBootImageInApex
+}
+
 // DexBootJarPathForContentModule returns the path to the dex boot jar for specified module.
 //
 // The dex boot jar is one which has had hidden API encoding performed on it.
@@ -550,6 +563,24 @@
 				// Copy the dex jars of this fragment's content modules to their predefined locations.
 				copyBootJarsToPredefinedLocations(ctx, hiddenAPIOutput.EncodedBootDexFilesByModule, imageConfig.dexPathsByModule)
 			}
+
+			for _, variant := range imageConfig.apexVariants() {
+				arch := variant.target.Arch.ArchType.String()
+				for _, install := range variant.deviceInstalls {
+					// Remove the "/" prefix because the path should be relative to $ANDROID_PRODUCT_OUT.
+					installDir := strings.TrimPrefix(filepath.Dir(install.To), "/")
+					installBase := filepath.Base(install.To)
+					installPath := android.PathForModuleInPartitionInstall(ctx, "", installDir)
+
+					b.bootImageDeviceInstalls = append(b.bootImageDeviceInstalls, dexpreopterInstall{
+						name:                arch + "-" + installBase,
+						moduleName:          b.Name(),
+						outputPathOnHost:    install.From,
+						installDirOnDevice:  installPath,
+						installFileOnDevice: installBase,
+					})
+				}
+			}
 		}
 
 		// A prebuilt fragment cannot contribute to an apex.
@@ -599,6 +630,8 @@
 			info.profilePathOnHost = imageConfig.profilePathOnHost
 			info.profileInstallPathInApex = imageConfig.profileInstallPathInApex
 		}
+
+		info.shouldInstallBootImageInApex = imageConfig.shouldInstallInApex()
 	}
 
 	info.bootImageFilesByArch = bootImageFilesByArch
@@ -813,6 +846,23 @@
 	return androidBootImageFilesByArch
 }
 
+func (b *BootclasspathFragmentModule) AndroidMkEntries() []android.AndroidMkEntries {
+	var entriesList []android.AndroidMkEntries
+	for _, install := range b.bootImageDeviceInstalls {
+		entriesList = append(entriesList, install.ToMakeEntries())
+	}
+	return entriesList
+}
+
+// Returns the names of all Make modules that handle the installation of the boot image.
+func (b *BootclasspathFragmentModule) BootImageDeviceInstallMakeModules() []string {
+	var makeModules []string
+	for _, install := range b.bootImageDeviceInstalls {
+		makeModules = append(makeModules, install.FullModuleName())
+	}
+	return makeModules
+}
+
 // Collect information for opening IDE project files in java/jdeps.go.
 func (b *BootclasspathFragmentModule) IDEInfo(dpInfo *android.IdeInfo) {
 	dpInfo.Deps = append(dpInfo.Deps, b.properties.Contents...)
diff --git a/java/dexpreopt.go b/java/dexpreopt.go
index e9bc518..7c5f055 100644
--- a/java/dexpreopt.go
+++ b/java/dexpreopt.go
@@ -57,6 +57,25 @@
 	return "-dexpreopt-" + install.name
 }
 
+// Returns Make entries for installing the file.
+//
+// This function uses a value receiver rather than a pointer receiver to ensure that the object is
+// safe to use in `android.AndroidMkExtraEntriesFunc`.
+func (install dexpreopterInstall) ToMakeEntries() android.AndroidMkEntries {
+	return android.AndroidMkEntries{
+		Class:      "ETC",
+		SubName:    install.SubModuleName(),
+		OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
+		ExtraEntries: []android.AndroidMkExtraEntriesFunc{
+			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
+				entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
+				entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
+				entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
+			},
+		},
+	}
+}
+
 type dexpreopter struct {
 	dexpreoptProperties DexpreoptProperties
 
@@ -383,19 +402,7 @@
 func (d *dexpreopter) AndroidMkEntriesForApex() []android.AndroidMkEntries {
 	var entries []android.AndroidMkEntries
 	for _, install := range d.builtInstalledForApex {
-		install := install
-		entries = append(entries, android.AndroidMkEntries{
-			Class:      "ETC",
-			SubName:    install.SubModuleName(),
-			OutputFile: android.OptionalPathForPath(install.outputPathOnHost),
-			ExtraEntries: []android.AndroidMkExtraEntriesFunc{
-				func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
-					entries.SetString("LOCAL_MODULE_PATH", install.installDirOnDevice.String())
-					entries.SetString("LOCAL_INSTALLED_MODULE_STEM", install.installFileOnDevice)
-					entries.SetString("LOCAL_NOT_AVAILABLE_FOR_PLATFORM", "false")
-				},
-			},
-		})
+		entries = append(entries, install.ToMakeEntries())
 	}
 	return entries
 }
diff --git a/java/dexpreopt_bootjars.go b/java/dexpreopt_bootjars.go
index c599c4d..cad9c33 100644
--- a/java/dexpreopt_bootjars.go
+++ b/java/dexpreopt_bootjars.go
@@ -313,10 +313,13 @@
 	// This is only set for a variant of an image that extends another image.
 	primaryImagesDeps android.Paths
 
-	// Rules which should be used in make to install the outputs.
+	// Rules which should be used in make to install the outputs on host.
 	installs           android.RuleBuilderInstalls
 	vdexInstalls       android.RuleBuilderInstalls
 	unstrippedInstalls android.RuleBuilderInstalls
+
+	// Rules which should be used in make to install the outputs on device.
+	deviceInstalls android.RuleBuilderInstalls
 }
 
 // Get target-specific boot image variant for the given boot image config and target.
@@ -388,6 +391,11 @@
 	return variants
 }
 
+// Returns true if the boot image should be installed in the APEX.
+func (image *bootImageConfig) shouldInstallInApex() bool {
+	return strings.HasPrefix(image.installDirOnDevice, "apex/")
+}
+
 // Return boot image locations (as a list of symbolic paths).
 //
 // The image "location" is a symbolic path that, with multiarchitecture support, doesn't really
@@ -710,6 +718,7 @@
 
 	var vdexInstalls android.RuleBuilderInstalls
 	var unstrippedInstalls android.RuleBuilderInstalls
+	var deviceInstalls android.RuleBuilderInstalls
 
 	for _, artOrOat := range image.moduleFiles(ctx, outputDir, ".art", ".oat") {
 		cmd.ImplicitOutput(artOrOat)
@@ -735,12 +744,21 @@
 			android.RuleBuilderInstall{unstrippedOat, filepath.Join(installDir, unstrippedOat.Base())})
 	}
 
+	if image.installDirOnHost != image.installDirOnDevice && !image.shouldInstallInApex() && !ctx.Config().UnbundledBuild() {
+		installDirOnDevice := filepath.Join("/", image.installDirOnDevice, arch.String())
+		for _, file := range image.moduleFiles(ctx, outputDir, ".art", ".oat", ".vdex") {
+			deviceInstalls = append(deviceInstalls,
+				android.RuleBuilderInstall{file, filepath.Join(installDirOnDevice, file.Base())})
+		}
+	}
+
 	rule.Build(image.name+"JarsDexpreopt_"+image.target.String(), "dexpreopt "+image.name+" jars "+arch.String())
 
 	// save output and installed files for makevars
 	image.installs = rule.Installs()
 	image.vdexInstalls = vdexInstalls
 	image.unstrippedInstalls = unstrippedInstalls
+	image.deviceInstalls = deviceInstalls
 }
 
 const failureMessage = `ERROR: Dex2oat failed to compile a boot image.
diff --git a/java/dexpreopt_config.go b/java/dexpreopt_config.go
index 26c1105..df8d8c8 100644
--- a/java/dexpreopt_config.go
+++ b/java/dexpreopt_config.go
@@ -41,17 +41,14 @@
 
 var (
 	bootImageConfigKey     = android.NewOnceKey("bootImageConfig")
+	bootImageConfigRawKey  = android.NewOnceKey("bootImageConfigRaw")
 	artBootImageName       = "art"
 	frameworkBootImageName = "boot"
 )
 
-// Construct the global boot image configs.
-func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
-	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
-
+func genBootImageConfigRaw(ctx android.PathContext) map[string]*bootImageConfig {
+	return ctx.Config().Once(bootImageConfigRawKey, func() interface{} {
 		global := dexpreopt.GetGlobalConfig(ctx)
-		targets := dexpreoptTargets(ctx)
-		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
 
 		artModules := global.ArtApexJars
 		frameworkModules := global.BootJars.RemoveList(artModules)
@@ -79,10 +76,22 @@
 			modules:            frameworkModules,
 		}
 
-		configs := map[string]*bootImageConfig{
+		return map[string]*bootImageConfig{
 			artBootImageName:       &artCfg,
 			frameworkBootImageName: &frameworkCfg,
 		}
+	}).(map[string]*bootImageConfig)
+}
+
+// Construct the global boot image configs.
+func genBootImageConfigs(ctx android.PathContext) map[string]*bootImageConfig {
+	return ctx.Config().Once(bootImageConfigKey, func() interface{} {
+		targets := dexpreoptTargets(ctx)
+		deviceDir := android.PathForOutput(ctx, ctx.Config().DeviceName())
+
+		configs := genBootImageConfigRaw(ctx)
+		artCfg := configs[artBootImageName]
+		frameworkCfg := configs[frameworkBootImageName]
 
 		// common to all configs
 		for _, c := range configs {
diff --git a/java/testing.go b/java/testing.go
index 7441e44..6c49bc8 100644
--- a/java/testing.go
+++ b/java/testing.go
@@ -506,3 +506,19 @@
 		}
 	}
 }
+
+// Applies the given modifier on the boot image config with the given name.
+func FixtureModifyBootImageConfig(name string, configModifier func(*bootImageConfig)) android.FixturePreparer {
+	return android.FixtureModifyConfig(func(androidConfig android.Config) {
+		pathCtx := android.PathContextForTesting(androidConfig)
+		config := genBootImageConfigRaw(pathCtx)
+		configModifier(config[name])
+	})
+}
+
+// Sets the value of `installDirOnDevice` of the boot image config with the given name.
+func FixtureSetBootImageInstallDirOnDevice(name string, installDir string) android.FixturePreparer {
+	return FixtureModifyBootImageConfig(name, func(config *bootImageConfig) {
+		config.installDirOnDevice = installDir
+	})
+}
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 14988e7..e317cad 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -254,19 +254,19 @@
 	gctx.writef("load(%q, %q)", baseUri, baseName)
 	// Emit exactly one load statement for each URI.
 	loadedSubConfigs := make(map[string]string)
-	for _, sc := range gctx.starScript.inherited {
-		uri := sc.path
+	for _, mi := range gctx.starScript.inherited {
+		uri := mi.path
 		if m, ok := loadedSubConfigs[uri]; ok {
 			// No need to emit load statement, but fix module name.
-			sc.moduleLocalName = m
+			mi.moduleLocalName = m
 			continue
 		}
-		if sc.optional {
+		if mi.optional || mi.missing {
 			uri += "|init"
 		}
 		gctx.newLine()
-		gctx.writef("load(%q, %s = \"init\")", uri, sc.entryName())
-		loadedSubConfigs[uri] = sc.moduleLocalName
+		gctx.writef("load(%q, %s = \"init\")", uri, mi.entryName())
+		loadedSubConfigs[uri] = mi.moduleLocalName
 	}
 	gctx.write("\n")
 }
@@ -298,6 +298,20 @@
 	gctx.writef(`rblf.mk2rbc_error("%s", %q)`, el, message)
 }
 
+func (gctx *generationContext) emitLoadCheck(im inheritedModule) {
+	if !im.needsLoadCheck() {
+		return
+	}
+	gctx.newLine()
+	gctx.writef("if not %s:", im.entryName())
+	gctx.indentLevel++
+	gctx.newLine()
+	gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
+	im.pathExpr().emit(gctx)
+	gctx.write("))")
+	gctx.indentLevel--
+}
+
 type knownVariable struct {
 	name      string
 	class     varClass
@@ -751,11 +765,13 @@
 		moduleLocalName += fmt.Sprintf("%d", n)
 	}
 	ctx.moduleNameCount[moduleName] = n + 1
+	_, err := fs.Stat(ctx.script.sourceFS, path)
 	mi := &moduleInfo{
 		path:            modulePath,
 		originalPath:    path,
 		moduleLocalName: moduleLocalName,
 		optional:        optional,
+		missing:         err != nil,
 	}
 	ctx.dependentModules[modulePath] = mi
 	ctx.script.inherited = append(ctx.script.inherited, mi)
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index ec6dfd0..d62882d 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -121,7 +121,7 @@
 ifdef PRODUCT_NAME
 $(call inherit-product, part1.mk)
 else # Comment
-$(call inherit-product, $(LOCAL_PATH)/part1.mk)
+$(call inherit-product, $(LOCAL_PATH)/part.mk)
 endif
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
@@ -132,10 +132,12 @@
   cfg = rblf.cfg(handle)
   rblf.inherit(handle, "part", _part_init)
   if g.get("PRODUCT_NAME") != None:
+    if not _part1_init:
+      rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
     rblf.inherit(handle, "part1", _part1_init)
   else:
     # Comment
-    rblf.inherit(handle, "part1", _part1_init)
+    rblf.inherit(handle, "part", _part_init)
 `,
 	},
 	{
@@ -173,6 +175,8 @@
   cfg = rblf.cfg(handle)
   _part_init(g, handle)
   if g.get("PRODUCT_NAME") != None:
+    if not _part1_init:
+      rblf.mkerror("product.mk", "Cannot find %s" % (":part1.star"))
     _part1_init(g, handle)
   else:
     if _part1_init != None:
diff --git a/mk2rbc/node.go b/mk2rbc/node.go
index ebc57b2..333a8da 100644
--- a/mk2rbc/node.go
+++ b/mk2rbc/node.go
@@ -47,6 +47,7 @@
 	originalPath    string // Makefile file path
 	moduleLocalName string
 	optional        bool
+	missing         bool // a module may not exist if a module that depends on it is loaded dynamically
 }
 
 func (im moduleInfo) entryName() string {
@@ -57,7 +58,8 @@
 	name() string
 	entryName() string
 	emitSelect(gctx *generationContext)
-	shouldExist() bool
+	pathExpr() starlarkExpr
+	needsLoadCheck() bool
 }
 
 type inheritedStaticModule struct {
@@ -72,8 +74,12 @@
 func (im inheritedStaticModule) emitSelect(_ *generationContext) {
 }
 
-func (im inheritedStaticModule) shouldExist() bool {
-	return im.loadAlways
+func (im inheritedStaticModule) pathExpr() starlarkExpr {
+	return &stringLiteralExpr{im.path}
+}
+
+func (im inheritedStaticModule) needsLoadCheck() bool {
+	return im.missing
 }
 
 type inheritedDynamicModule struct {
@@ -105,20 +111,14 @@
 	gctx.write(")")
 	gctx.newLine()
 	gctx.writef("(%s, %s) = _entry if _entry else (None, None)", i.name(), i.entryName())
-	if i.loadAlways {
-		gctx.newLine()
-		gctx.writef("if not %s:", i.entryName())
-		gctx.indentLevel++
-		gctx.newLine()
-		gctx.write(`rblf.mkerror("`, gctx.starScript.mkFile, `", "Cannot find %s" % (`)
-		i.path.emit(gctx)
-		gctx.write("))")
-		gctx.indentLevel--
-	}
 }
 
-func (i inheritedDynamicModule) shouldExist() bool {
-	return i.loadAlways
+func (i inheritedDynamicModule) pathExpr() starlarkExpr {
+	return &i.path
+}
+
+func (i inheritedDynamicModule) needsLoadCheck() bool {
+	return true
 }
 
 type inheritNode struct {
@@ -128,20 +128,22 @@
 
 func (inn *inheritNode) emit(gctx *generationContext) {
 	// Unconditional case:
+	//    maybe check that loaded
 	//    rblf.inherit(handle, <module>, module_init)
 	// Conditional case:
 	//    if <module>_init != None:
 	//      same as above
 	inn.module.emitSelect(gctx)
-
 	name := inn.module.name()
 	entry := inn.module.entryName()
-	gctx.newLine()
 	if inn.loadAlways {
+		gctx.emitLoadCheck(inn.module)
+		gctx.newLine()
 		gctx.writef("%s(handle, %s, %s)", cfnInherit, name, entry)
 		return
 	}
 
+	gctx.newLine()
 	gctx.writef("if %s:", entry)
 	gctx.indentLevel++
 	gctx.newLine()
@@ -157,12 +159,14 @@
 func (inn *includeNode) emit(gctx *generationContext) {
 	inn.module.emitSelect(gctx)
 	entry := inn.module.entryName()
-	gctx.newLine()
 	if inn.loadAlways {
+		gctx.emitLoadCheck(inn.module)
+		gctx.newLine()
 		gctx.writef("%s(g, handle)", entry)
 		return
 	}
 
+	gctx.newLine()
 	gctx.writef("if %s != None:", entry)
 	gctx.indentLevel++
 	gctx.newLine()
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index b1d1bb2..2ab784d 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -268,6 +268,9 @@
 func (s *ShBinary) GenerateAndroidBuildActions(ctx android.ModuleContext) {
 	s.generateAndroidBuildActions(ctx)
 	installDir := android.PathForModuleInstall(ctx, "bin", proptools.String(s.properties.Sub_dir))
+	if !s.Installable() {
+		s.SkipInstall()
+	}
 	s.installedFile = ctx.InstallExecutable(installDir, s.outputFilePath.Base(), s.outputFilePath)
 	for _, symlink := range s.Symlinks() {
 		ctx.InstallSymlink(installDir, symlink, s.installedFile)
@@ -283,6 +286,7 @@
 			func(ctx android.AndroidMkExtraEntriesContext, entries *android.AndroidMkEntries) {
 				s.customAndroidMkEntries(entries)
 				entries.SetString("LOCAL_MODULE_RELATIVE_PATH", proptools.String(s.properties.Sub_dir))
+				entries.SetBoolIfTrue("LOCAL_UNINSTALLABLE_MODULE", !s.Installable())
 			},
 		},
 	}}
diff --git a/ui/build/rbe.go b/ui/build/rbe.go
index d74f262..8f9a699 100644
--- a/ui/build/rbe.go
+++ b/ui/build/rbe.go
@@ -19,6 +19,7 @@
 	"math/rand"
 	"os"
 	"path/filepath"
+	"runtime"
 	"syscall"
 	"time"
 
@@ -87,6 +88,13 @@
 		}
 		vars["RBE_server_address"] = fmt.Sprintf("unix://%v", name)
 	}
+
+	rf := 1.0
+	if config.Parallel() < runtime.NumCPU() {
+		rf = float64(config.Parallel()) / float64(runtime.NumCPU())
+	}
+	vars["RBE_local_resource_fraction"] = fmt.Sprintf("%.2f", rf)
+
 	k, v := config.rbeAuth()
 	vars[k] = v
 	return vars