Merge "Export binaries for shippedlibs and bom."
diff --git a/android/arch.go b/android/arch.go
index 67158e0..8aa8d40 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -22,6 +22,7 @@
 	"strings"
 
 	"android/soong/bazel"
+	"android/soong/starlark_fmt"
 
 	"github.com/google/blueprint"
 	"github.com/google/blueprint/bootstrap"
@@ -1518,23 +1519,32 @@
 	targets := make(map[OsType][]Target)
 	var targetErr error
 
-	addTarget := func(os OsType, archName string, archVariant, cpuVariant *string, abi []string,
-		nativeBridgeEnabled NativeBridgeSupport, nativeBridgeHostArchName *string,
-		nativeBridgeRelativePath *string) {
+	type targetConfig struct {
+		os                       OsType
+		archName                 string
+		archVariant              *string
+		cpuVariant               *string
+		abi                      []string
+		nativeBridgeEnabled      NativeBridgeSupport
+		nativeBridgeHostArchName *string
+		nativeBridgeRelativePath *string
+	}
+
+	addTarget := func(target targetConfig) {
 		if targetErr != nil {
 			return
 		}
 
-		arch, err := decodeArch(os, archName, archVariant, cpuVariant, abi)
+		arch, err := decodeArch(target.os, target.archName, target.archVariant, target.cpuVariant, target.abi)
 		if err != nil {
 			targetErr = err
 			return
 		}
-		nativeBridgeRelativePathStr := String(nativeBridgeRelativePath)
-		nativeBridgeHostArchNameStr := String(nativeBridgeHostArchName)
+		nativeBridgeRelativePathStr := String(target.nativeBridgeRelativePath)
+		nativeBridgeHostArchNameStr := String(target.nativeBridgeHostArchName)
 
 		// Use guest arch as relative install path by default
-		if nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
+		if target.nativeBridgeEnabled && nativeBridgeRelativePathStr == "" {
 			nativeBridgeRelativePathStr = arch.ArchType.String()
 		}
 
@@ -1542,11 +1552,11 @@
 		// the currently configured build machine (either because the OS is different or because of
 		// the unsupported arch)
 		hostCross := false
-		if os.Class == Host {
+		if target.os.Class == Host {
 			var osSupported bool
-			if os == config.BuildOS {
+			if target.os == config.BuildOS {
 				osSupported = true
-			} else if config.BuildOS.Linux() && os.Linux() {
+			} else if config.BuildOS.Linux() && target.os.Linux() {
 				// LinuxBionic and Linux are compatible
 				osSupported = true
 			} else {
@@ -1568,11 +1578,11 @@
 			}
 		}
 
-		targets[os] = append(targets[os],
+		targets[target.os] = append(targets[target.os],
 			Target{
-				Os:                       os,
+				Os:                       target.os,
 				Arch:                     arch,
-				NativeBridge:             nativeBridgeEnabled,
+				NativeBridge:             target.nativeBridgeEnabled,
 				NativeBridgeHostArchName: nativeBridgeHostArchNameStr,
 				NativeBridgeRelativePath: nativeBridgeRelativePathStr,
 				HostCross:                hostCross,
@@ -1584,11 +1594,11 @@
 	}
 
 	// The primary host target, which must always exist.
-	addTarget(config.BuildOS, *variables.HostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+	addTarget(targetConfig{os: config.BuildOS, archName: *variables.HostArch, nativeBridgeEnabled: NativeBridgeDisabled})
 
 	// An optional secondary host target.
 	if variables.HostSecondaryArch != nil && *variables.HostSecondaryArch != "" {
-		addTarget(config.BuildOS, *variables.HostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+		addTarget(targetConfig{os: config.BuildOS, archName: *variables.HostSecondaryArch, nativeBridgeEnabled: NativeBridgeDisabled})
 	}
 
 	// Optional cross-compiled host targets, generally Windows.
@@ -1603,45 +1613,65 @@
 		}
 
 		// The primary cross-compiled host target.
-		addTarget(crossHostOs, *variables.CrossHostArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+		addTarget(targetConfig{os: crossHostOs, archName: *variables.CrossHostArch, nativeBridgeEnabled: NativeBridgeDisabled})
 
 		// An optional secondary cross-compiled host target.
 		if variables.CrossHostSecondaryArch != nil && *variables.CrossHostSecondaryArch != "" {
-			addTarget(crossHostOs, *variables.CrossHostSecondaryArch, nil, nil, nil, NativeBridgeDisabled, nil, nil)
+			addTarget(targetConfig{os: crossHostOs, archName: *variables.CrossHostSecondaryArch, nativeBridgeEnabled: NativeBridgeDisabled})
 		}
 	}
 
 	// Optional device targets
 	if variables.DeviceArch != nil && *variables.DeviceArch != "" {
 		// The primary device target.
-		addTarget(Android, *variables.DeviceArch, variables.DeviceArchVariant,
-			variables.DeviceCpuVariant, variables.DeviceAbi, NativeBridgeDisabled, nil, nil)
+		addTarget(targetConfig{
+			os:                  Android,
+			archName:            *variables.DeviceArch,
+			archVariant:         variables.DeviceArchVariant,
+			cpuVariant:          variables.DeviceCpuVariant,
+			abi:                 variables.DeviceAbi,
+			nativeBridgeEnabled: NativeBridgeDisabled,
+		})
 
 		// An optional secondary device target.
 		if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" {
-			addTarget(Android, *variables.DeviceSecondaryArch,
-				variables.DeviceSecondaryArchVariant, variables.DeviceSecondaryCpuVariant,
-				variables.DeviceSecondaryAbi, NativeBridgeDisabled, nil, nil)
+			addTarget(targetConfig{
+				os:                  Android,
+				archName:            *variables.DeviceSecondaryArch,
+				archVariant:         variables.DeviceSecondaryArchVariant,
+				cpuVariant:          variables.DeviceSecondaryCpuVariant,
+				abi:                 variables.DeviceSecondaryAbi,
+				nativeBridgeEnabled: NativeBridgeDisabled,
+			})
 		}
 
 		// An optional NativeBridge device target.
 		if variables.NativeBridgeArch != nil && *variables.NativeBridgeArch != "" {
-			addTarget(Android, *variables.NativeBridgeArch,
-				variables.NativeBridgeArchVariant, variables.NativeBridgeCpuVariant,
-				variables.NativeBridgeAbi, NativeBridgeEnabled, variables.DeviceArch,
-				variables.NativeBridgeRelativePath)
+			addTarget(targetConfig{
+				os:                       Android,
+				archName:                 *variables.NativeBridgeArch,
+				archVariant:              variables.NativeBridgeArchVariant,
+				cpuVariant:               variables.NativeBridgeCpuVariant,
+				abi:                      variables.NativeBridgeAbi,
+				nativeBridgeEnabled:      NativeBridgeEnabled,
+				nativeBridgeHostArchName: variables.DeviceArch,
+				nativeBridgeRelativePath: variables.NativeBridgeRelativePath,
+			})
 		}
 
 		// An optional secondary NativeBridge device target.
 		if variables.DeviceSecondaryArch != nil && *variables.DeviceSecondaryArch != "" &&
 			variables.NativeBridgeSecondaryArch != nil && *variables.NativeBridgeSecondaryArch != "" {
-			addTarget(Android, *variables.NativeBridgeSecondaryArch,
-				variables.NativeBridgeSecondaryArchVariant,
-				variables.NativeBridgeSecondaryCpuVariant,
-				variables.NativeBridgeSecondaryAbi,
-				NativeBridgeEnabled,
-				variables.DeviceSecondaryArch,
-				variables.NativeBridgeSecondaryRelativePath)
+			addTarget(targetConfig{
+				os:                       Android,
+				archName:                 *variables.NativeBridgeSecondaryArch,
+				archVariant:              variables.NativeBridgeSecondaryArchVariant,
+				cpuVariant:               variables.NativeBridgeSecondaryCpuVariant,
+				abi:                      variables.NativeBridgeSecondaryAbi,
+				nativeBridgeEnabled:      NativeBridgeEnabled,
+				nativeBridgeHostArchName: variables.DeviceSecondaryArch,
+				nativeBridgeRelativePath: variables.NativeBridgeSecondaryRelativePath,
+			})
 		}
 	}
 
@@ -1701,11 +1731,11 @@
 }
 
 // decodeArchSettings converts a list of archConfigs into a list of Targets for the given OsType.
-func decodeArchSettings(os OsType, archConfigs []archConfig) ([]Target, error) {
+func decodeAndroidArchSettings(archConfigs []archConfig) ([]Target, error) {
 	var ret []Target
 
 	for _, config := range archConfigs {
-		arch, err := decodeArch(os, config.arch, &config.archVariant,
+		arch, err := decodeArch(Android, config.arch, &config.archVariant,
 			&config.cpuVariant, config.abi)
 		if err != nil {
 			return nil, err
@@ -1765,14 +1795,9 @@
 		}
 	}
 
-	if a.ArchVariant == "" {
-		// Set ArchFeatures from the default arch features.
-		if featureMap, ok := defaultArchFeatureMap[os]; ok {
-			a.ArchFeatures = featureMap[archType]
-		}
-	} else {
-		// Set ArchFeatures from the arch type.
-		if featureMap, ok := archFeatureMap[archType]; ok {
+	// Set ArchFeatures from the arch type. for Android OS, other os-es do not specify features
+	if os == Android {
+		if featureMap, ok := androidArchFeatureMap[archType]; ok {
 			a.ArchFeatures = featureMap[a.ArchVariant]
 		}
 	}
@@ -2210,3 +2235,40 @@
 
 	return value
 }
+
+func printArchTypeStarlarkDict(dict map[ArchType][]string) string {
+	valDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		valDict[k.String()] = starlark_fmt.PrintStringList(v, 1)
+	}
+	return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func printArchTypeNestedStarlarkDict(dict map[ArchType]map[string][]string) string {
+	valDict := make(map[string]string, len(dict))
+	for k, v := range dict {
+		valDict[k.String()] = starlark_fmt.PrintStringListDict(v, 1)
+	}
+	return starlark_fmt.PrintDict(valDict, 0)
+}
+
+func StarlarkArchConfigurations() string {
+	return fmt.Sprintf(`
+_arch_to_variants = %s
+
+_arch_to_cpu_variants = %s
+
+_arch_to_features = %s
+
+_android_arch_feature_for_arch_variant = %s
+
+arch_to_variants = _arch_to_variants
+arch_to_cpu_variants = _arch_to_cpu_variants
+arch_to_features = _arch_to_features
+android_arch_feature_for_arch_variants = _android_arch_feature_for_arch_variant
+`, printArchTypeStarlarkDict(archVariants),
+		printArchTypeStarlarkDict(cpuVariants),
+		printArchTypeStarlarkDict(archFeatures),
+		printArchTypeNestedStarlarkDict(androidArchFeatureMap),
+	)
+}
diff --git a/android/arch_list.go b/android/arch_list.go
index 79ad4af..cbf8e7a 100644
--- a/android/arch_list.go
+++ b/android/arch_list.go
@@ -14,8 +14,6 @@
 
 package android
 
-import "fmt"
-
 var archVariants = map[ArchType][]string{
 	Arm: {
 		"armv7-a",
@@ -128,7 +126,7 @@
 	},
 }
 
-var archFeatureMap = map[ArchType]map[string][]string{
+var androidArchFeatureMap = map[ArchType]map[string][]string{
 	Arm: {
 		"armv7-a-neon": {
 			"neon",
@@ -279,6 +277,13 @@
 		},
 	},
 	X86_64: {
+		"" /*default */ : {
+			"ssse3",
+			"sse4",
+			"sse4_1",
+			"sse4_2",
+			"popcnt",
+		},
 		"amberlake": {
 			"ssse3",
 			"sse4",
@@ -398,23 +403,3 @@
 		},
 	},
 }
-
-var defaultArchFeatureMap = map[OsType]map[ArchType][]string{}
-
-// RegisterDefaultArchVariantFeatures is called by files that define Toolchains to specify the
-// arch features that are available for the default arch variant.  It must be called from an
-// init() function.
-func RegisterDefaultArchVariantFeatures(os OsType, arch ArchType, features ...string) {
-	checkCalledFromInit()
-
-	for _, feature := range features {
-		if !InList(feature, archFeatures[arch]) {
-			panic(fmt.Errorf("Invalid feature %q for arch %q variant \"\"", feature, arch))
-		}
-	}
-
-	if defaultArchFeatureMap[os] == nil {
-		defaultArchFeatureMap[os] = make(map[ArchType][]string)
-	}
-	defaultArchFeatureMap[os][arch] = features
-}
diff --git a/android/arch_test.go b/android/arch_test.go
index a828321..7caf837 100644
--- a/android/arch_test.go
+++ b/android/arch_test.go
@@ -491,11 +491,9 @@
 			arch: {
 				arm: {
 					a:  ["arm"],
-					armv7_a_neon: { a: ["armv7_a_neon"] },
 				},
 				arm64: {
 					a:  ["arm64"],
-					armv8_a: { a: ["armv8_a"] },
 				},
 				x86: { a:  ["x86"] },
 				x86_64: { a:  ["x86_64"] },
@@ -552,12 +550,12 @@
 				{
 					module:   "foo",
 					variant:  "android_arm64_armv8-a",
-					property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "armv8_a", "lib64", "android_arm64"},
+					property: []string{"root", "linux", "bionic", "android", "android64", "arm64", "lib64", "android_arm64"},
 				},
 				{
 					module:   "foo",
 					variant:  "android_arm_armv7-a-neon",
-					property: []string{"root", "linux", "bionic", "android", "android64", "arm", "armv7_a_neon", "lib32", "android_arm"},
+					property: []string{"root", "linux", "bionic", "android", "android64", "arm", "lib32", "android_arm"},
 				},
 			},
 		},
diff --git a/android/bazel.go b/android/bazel.go
index becf988..7714f2c 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -499,10 +499,11 @@
 		// go deps:
 		"apex-protos",                                                                                // depends on soong_zip, a go binary
 		"generated_android_icu4j_src_files", "generated_android_icu4j_test_files", "icu4c_test_data", // 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.
-		"robolectric-sqlite4java-native", // depends on soong_zip, a go binary
-		"robolectric_tzdata",             // depends on soong_zip, a go binary
+		"host_bionic_linker_asm",                                                  // depends on extract_linker, a go binary.
+		"host_bionic_linker_script",                                               // depends on extract_linker, a go binary.
+		"robolectric-sqlite4java-native",                                          // depends on soong_zip, a go binary
+		"robolectric_tzdata",                                                      // depends on soong_zip, a go binary
+		"libc_musl_sysroot_libc++_headers", "libc_musl_sysroot_libc++abi_headers", // depends on soong_zip, zip2zip
 
 		"android_icu4j_srcgen_binary", // Bazel build error: deps not allowed without srcs; move to runtime_deps
 		"core-icu4j-for-host",         // Bazel build error: deps not allowed without srcs; move to runtime_deps
diff --git a/android/bazel_handler.go b/android/bazel_handler.go
index 804a5fb..3d6babc 100644
--- a/android/bazel_handler.go
+++ b/android/bazel_handler.go
@@ -664,7 +664,12 @@
 	if err != nil {
 		return err
 	}
-
+	if metricsDir := context.paths.BazelMetricsDir(); metricsDir != "" {
+		err = os.MkdirAll(metricsDir, 0777)
+		if err != nil {
+			return err
+		}
+	}
 	err = ioutil.WriteFile(filepath.Join(soongInjectionPath, "WORKSPACE.bazel"), []byte{}, 0666)
 	if err != nil {
 		return err
@@ -716,9 +721,9 @@
 		}
 	}
 
-	for val, _ := range context.requests {
+	for val := range context.requests {
 		if cqueryResult, ok := cqueryResults[getCqueryId(val)]; ok {
-			context.results[val] = string(cqueryResult)
+			context.results[val] = cqueryResult
 		} else {
 			return fmt.Errorf("missing result for bazel target %s. query output: [%s], cquery err: [%s]",
 				getCqueryId(val), cqueryOutput, cqueryErr)
diff --git a/android/config.go b/android/config.go
index 4a7e0d9..3d8bc31 100644
--- a/android/config.go
+++ b/android/config.go
@@ -520,7 +520,7 @@
 	}
 
 	if archConfig != nil {
-		androidTargets, err := decodeArchSettings(Android, archConfig)
+		androidTargets, err := decodeAndroidArchSettings(archConfig)
 		if err != nil {
 			return Config{}, err
 		}
@@ -1651,6 +1651,10 @@
 	return c.config.productVariables.BuildBrokenVendorPropertyNamespace
 }
 
+func (c *deviceConfig) BuildBrokenInputDir(name string) bool {
+	return InList(name, c.config.productVariables.BuildBrokenInputDirModules)
+}
+
 func (c *deviceConfig) RequiresInsecureExecmemForSwiftshader() bool {
 	return c.config.productVariables.RequiresInsecureExecmemForSwiftshader
 }
diff --git a/android/paths.go b/android/paths.go
index 4c69de7..05caebd 100644
--- a/android/paths.go
+++ b/android/paths.go
@@ -405,6 +405,13 @@
 	return PathsForModuleSrcExcludes(ctx, paths, nil)
 }
 
+type SourceInput struct {
+	Context      ModuleMissingDepsPathContext
+	Paths        []string
+	ExcludePaths []string
+	IncludeDirs  bool
+}
+
 // PathsForModuleSrcExcludes returns a Paths{} containing the resolved references in paths, minus
 // those listed in excludes. Elements of paths and excludes are resolved as:
 // * filepath, relative to local module directory, resolves as a filepath relative to the local
@@ -423,12 +430,21 @@
 //     missing dependencies
 //   * otherwise, a ModuleError is thrown.
 func PathsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) Paths {
-	ret, missingDeps := PathsAndMissingDepsForModuleSrcExcludes(ctx, paths, excludes)
-	if ctx.Config().AllowMissingDependencies() {
-		ctx.AddMissingDependencies(missingDeps)
+	return PathsRelativeToModuleSourceDir(SourceInput{
+		Context:      ctx,
+		Paths:        paths,
+		ExcludePaths: excludes,
+		IncludeDirs:  true,
+	})
+}
+
+func PathsRelativeToModuleSourceDir(input SourceInput) Paths {
+	ret, missingDeps := PathsAndMissingDepsRelativeToModuleSourceDir(input)
+	if input.Context.Config().AllowMissingDependencies() {
+		input.Context.AddMissingDependencies(missingDeps)
 	} else {
 		for _, m := range missingDeps {
-			ctx.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
+			input.Context.ModuleErrorf(`missing dependency on %q, is the property annotated with android:"path"?`, m)
 		}
 	}
 	return ret
@@ -543,23 +559,31 @@
 // Properties passed as the paths argument must have been annotated with struct tag
 // `android:"path"` so that dependencies on SourceFileProducer modules will have already been handled by the
 // path_deps mutator.
-func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleWithDepsPathContext, paths, excludes []string) (Paths, []string) {
-	prefix := pathForModuleSrc(ctx).String()
+func PathsAndMissingDepsForModuleSrcExcludes(ctx ModuleMissingDepsPathContext, paths, excludes []string) (Paths, []string) {
+	return PathsAndMissingDepsRelativeToModuleSourceDir(SourceInput{
+		Context:      ctx,
+		Paths:        paths,
+		ExcludePaths: excludes,
+		IncludeDirs:  true,
+	})
+}
+
+func PathsAndMissingDepsRelativeToModuleSourceDir(input SourceInput) (Paths, []string) {
+	prefix := pathForModuleSrc(input.Context).String()
 
 	var expandedExcludes []string
-	if excludes != nil {
-		expandedExcludes = make([]string, 0, len(excludes))
+	if input.ExcludePaths != nil {
+		expandedExcludes = make([]string, 0, len(input.ExcludePaths))
 	}
 
 	var missingExcludeDeps []string
-
-	for _, e := range excludes {
+	for _, e := range input.ExcludePaths {
 		if m, t := SrcIsModuleWithTag(e); m != "" {
-			modulePaths, err := getPathsFromModuleDep(ctx, e, m, t)
+			modulePaths, err := getPathsFromModuleDep(input.Context, e, m, t)
 			if m, ok := err.(missingDependencyError); ok {
 				missingExcludeDeps = append(missingExcludeDeps, m.missingDeps...)
 			} else if err != nil {
-				reportPathError(ctx, err)
+				reportPathError(input.Context, err)
 			} else {
 				expandedExcludes = append(expandedExcludes, modulePaths.Strings()...)
 			}
@@ -568,19 +592,24 @@
 		}
 	}
 
-	if paths == nil {
+	if input.Paths == nil {
 		return nil, missingExcludeDeps
 	}
 
 	var missingDeps []string
 
-	expandedSrcFiles := make(Paths, 0, len(paths))
-	for _, s := range paths {
-		srcFiles, err := expandOneSrcPath(ctx, s, expandedExcludes)
+	expandedSrcFiles := make(Paths, 0, len(input.Paths))
+	for _, s := range input.Paths {
+		srcFiles, err := expandOneSrcPath(sourcePathInput{
+			context:          input.Context,
+			path:             s,
+			expandedExcludes: expandedExcludes,
+			includeDirs:      input.IncludeDirs,
+		})
 		if depErr, ok := err.(missingDependencyError); ok {
 			missingDeps = append(missingDeps, depErr.missingDeps...)
 		} else if err != nil {
-			reportPathError(ctx, err)
+			reportPathError(input.Context, err)
 		}
 		expandedSrcFiles = append(expandedSrcFiles, srcFiles...)
 	}
@@ -596,44 +625,59 @@
 	return "missing dependencies: " + strings.Join(e.missingDeps, ", ")
 }
 
+type sourcePathInput struct {
+	context          ModuleWithDepsPathContext
+	path             string
+	expandedExcludes []string
+	includeDirs      bool
+}
+
 // Expands one path string to Paths rooted from the module's local source
 // directory, excluding those listed in the expandedExcludes.
 // Expands globs, references to SourceFileProducer or OutputFileProducer modules using the ":name" and ":name{.tag}" syntax.
-func expandOneSrcPath(ctx ModuleWithDepsPathContext, sPath string, expandedExcludes []string) (Paths, error) {
+func expandOneSrcPath(input sourcePathInput) (Paths, error) {
 	excludePaths := func(paths Paths) Paths {
-		if len(expandedExcludes) == 0 {
+		if len(input.expandedExcludes) == 0 {
 			return paths
 		}
 		remainder := make(Paths, 0, len(paths))
 		for _, p := range paths {
-			if !InList(p.String(), expandedExcludes) {
+			if !InList(p.String(), input.expandedExcludes) {
 				remainder = append(remainder, p)
 			}
 		}
 		return remainder
 	}
-	if m, t := SrcIsModuleWithTag(sPath); m != "" {
-		modulePaths, err := getPathsFromModuleDep(ctx, sPath, m, t)
+	if m, t := SrcIsModuleWithTag(input.path); m != "" {
+		modulePaths, err := getPathsFromModuleDep(input.context, input.path, m, t)
 		if err != nil {
 			return nil, err
 		} else {
 			return excludePaths(modulePaths), nil
 		}
-	} else if pathtools.IsGlob(sPath) {
-		paths := GlobFiles(ctx, pathForModuleSrc(ctx, sPath).String(), expandedExcludes)
-		return PathsWithModuleSrcSubDir(ctx, paths, ""), nil
 	} else {
-		p := pathForModuleSrc(ctx, sPath)
-		if exists, _, err := ctx.Config().fs.Exists(p.String()); err != nil {
-			ReportPathErrorf(ctx, "%s: %s", p, err.Error())
-		} else if !exists && !ctx.Config().TestAllowNonExistentPaths {
-			ReportPathErrorf(ctx, "module source path %q does not exist", p)
-		}
+		p := pathForModuleSrc(input.context, input.path)
+		if pathtools.IsGlob(input.path) {
+			paths := GlobFiles(input.context, p.String(), input.expandedExcludes)
+			return PathsWithModuleSrcSubDir(input.context, paths, ""), nil
+		} else {
+			if exists, _, err := input.context.Config().fs.Exists(p.String()); err != nil {
+				ReportPathErrorf(input.context, "%s: %s", p, err.Error())
+			} else if !exists && !input.context.Config().TestAllowNonExistentPaths {
+				ReportPathErrorf(input.context, "module source path %q does not exist", p)
+			} else if !input.includeDirs {
+				if isDir, err := input.context.Config().fs.IsDir(p.String()); exists && err != nil {
+					ReportPathErrorf(input.context, "%s: %s", p, err.Error())
+				} else if isDir {
+					ReportPathErrorf(input.context, "module source path %q is a directory", p)
+				}
+			}
 
-		if InList(p.String(), expandedExcludes) {
-			return nil, nil
+			if InList(p.String(), input.expandedExcludes) {
+				return nil, nil
+			}
+			return Paths{p}, nil
 		}
-		return Paths{p}, nil
 	}
 }
 
@@ -1315,7 +1359,7 @@
 	// validatePath() will corrupt it, e.g. replace "//" with "/". If the path is not a module
 	// reference then it will be validated by expandOneSrcPath anyway when it calls expandOneSrcPath.
 	p := strings.Join(pathComponents, string(filepath.Separator))
-	paths, err := expandOneSrcPath(ctx, p, nil)
+	paths, err := expandOneSrcPath(sourcePathInput{context: ctx, path: p, includeDirs: true})
 	if err != nil {
 		if depErr, ok := err.(missingDependencyError); ok {
 			if ctx.Config().AllowMissingDependencies() {
diff --git a/android/variable.go b/android/variable.go
index 627d9bd..68f19b9 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -421,9 +421,10 @@
 
 	ShippingApiLevel *string `json:",omitempty"`
 
-	BuildBrokenEnforceSyspropOwner     bool `json:",omitempty"`
-	BuildBrokenTrebleSyspropNeverallow bool `json:",omitempty"`
-	BuildBrokenVendorPropertyNamespace bool `json:",omitempty"`
+	BuildBrokenEnforceSyspropOwner     bool     `json:",omitempty"`
+	BuildBrokenTrebleSyspropNeverallow bool     `json:",omitempty"`
+	BuildBrokenVendorPropertyNamespace bool     `json:",omitempty"`
+	BuildBrokenInputDirModules         []string `json:",omitempty"`
 
 	BuildDebugfsRestrictionsEnabled bool `json:",omitempty"`
 
diff --git a/apex/androidmk.go b/apex/androidmk.go
index 8cca137..8785ca0 100644
--- a/apex/androidmk.go
+++ b/apex/androidmk.go
@@ -309,7 +309,14 @@
 	return moduleNames
 }
 
-func (a *apexBundle) writeRequiredModules(w io.Writer) {
+func (a *apexBundle) writeRequiredModules(w io.Writer, moduleNames []string) {
+	if len(moduleNames) > 0 {
+		fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
+	}
+	if len(a.requiredDeps) > 0 {
+		fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
+	}
+
 	var required []string
 	var targetRequired []string
 	var hostRequired []string
@@ -349,10 +356,7 @@
 				fmt.Fprintln(w, "LOCAL_PATH :=", moduleDir)
 				fmt.Fprintln(w, "LOCAL_MODULE :=", name+a.suffix)
 				data.Entries.WriteLicenseVariables(w)
-				if len(moduleNames) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES :=", strings.Join(moduleNames, " "))
-				}
-				a.writeRequiredModules(w)
+				a.writeRequiredModules(w, moduleNames)
 				fmt.Fprintln(w, "include $(BUILD_PHONY_PACKAGE)")
 
 			} else {
@@ -388,13 +392,7 @@
 				if len(a.overridableProperties.Overrides) > 0 {
 					fmt.Fprintln(w, "LOCAL_OVERRIDES_MODULES :=", strings.Join(a.overridableProperties.Overrides, " "))
 				}
-				if len(moduleNames) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(moduleNames, " "))
-				}
-				if len(a.requiredDeps) > 0 {
-					fmt.Fprintln(w, "LOCAL_REQUIRED_MODULES +=", strings.Join(a.requiredDeps, " "))
-				}
-				a.writeRequiredModules(w)
+				a.writeRequiredModules(w, moduleNames)
 
 				if a.mergedNotices.Merged.Valid() {
 					fmt.Fprintln(w, "LOCAL_NOTICE_FILE :=", a.mergedNotices.Merged.Path().String())
diff --git a/apex/apex.go b/apex/apex.go
index fe4c205..9031a4e 100644
--- a/apex/apex.go
+++ b/apex/apex.go
@@ -108,15 +108,6 @@
 
 	Multilib apexMultilibProperties
 
-	// List of bootclasspath fragments that are embedded inside this APEX bundle.
-	Bootclasspath_fragments []string
-
-	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
-	Systemserverclasspath_fragments []string
-
-	// List of java libraries that are embedded inside this APEX bundle.
-	Java_libs []string
-
 	// List of sh binaries that are embedded inside this APEX bundle.
 	Sh_binaries []string
 
@@ -316,6 +307,15 @@
 	// List of BPF programs inside this APEX bundle.
 	Bpfs []string
 
+	// List of bootclasspath fragments that are embedded inside this APEX bundle.
+	Bootclasspath_fragments []string
+
+	// List of systemserverclasspath fragments that are embedded inside this APEX bundle.
+	Systemserverclasspath_fragments []string
+
+	// List of java libraries that are embedded inside this APEX bundle.
+	Java_libs []string
+
 	// Names of modules to be overridden. Listed modules can only be other binaries (in Make or
 	// Soong). This does not completely prevent installation of the overridden binaries, but if
 	// both binaries would be installed by default (in PRODUCT_PACKAGES) the other binary will
@@ -787,9 +787,6 @@
 
 	// Common-arch dependencies come next
 	commonVariation := ctx.Config().AndroidCommonTarget.Variations()
-	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.properties.Bootclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.properties.Systemserverclasspath_fragments...)
-	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.properties.Java_libs...)
 	ctx.AddFarVariationDependencies(commonVariation, fsTag, a.properties.Filesystems...)
 	ctx.AddFarVariationDependencies(commonVariation, compatConfigTag, a.properties.Compat_configs...)
 
@@ -817,6 +814,9 @@
 	ctx.AddFarVariationDependencies(commonVariation, androidAppTag, a.overridableProperties.Apps...)
 	ctx.AddFarVariationDependencies(commonVariation, bpfTag, a.overridableProperties.Bpfs...)
 	ctx.AddFarVariationDependencies(commonVariation, rroTag, a.overridableProperties.Rros...)
+	ctx.AddFarVariationDependencies(commonVariation, bcpfTag, a.overridableProperties.Bootclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, sscpfTag, a.overridableProperties.Systemserverclasspath_fragments...)
+	ctx.AddFarVariationDependencies(commonVariation, javaLibTag, a.overridableProperties.Java_libs...)
 	if prebuilts := a.overridableProperties.Prebuilts; len(prebuilts) > 0 {
 		// For prebuilt_etc, use the first variant (64 on 64/32bit device, 32 on 32bit device)
 		// regardless of the TARGET_PREFER_* setting. See b/144532908
@@ -2595,9 +2595,9 @@
 
 // Collect information for opening IDE project files in java/jdeps.go.
 func (a *apexBundle) IDEInfo(dpInfo *android.IdeInfo) {
-	dpInfo.Deps = append(dpInfo.Deps, a.properties.Java_libs...)
-	dpInfo.Deps = append(dpInfo.Deps, a.properties.Bootclasspath_fragments...)
-	dpInfo.Deps = append(dpInfo.Deps, a.properties.Systemserverclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Java_libs...)
+	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Bootclasspath_fragments...)
+	dpInfo.Deps = append(dpInfo.Deps, a.overridableProperties.Systemserverclasspath_fragments...)
 	dpInfo.Paths = append(dpInfo.Paths, a.modulePaths...)
 }
 
diff --git a/apex/apex_test.go b/apex/apex_test.go
index b4b5128..1c36c75 100644
--- a/apex/apex_test.go
+++ b/apex/apex_test.go
@@ -6091,6 +6091,9 @@
 			apps: ["app"],
 			bpfs: ["bpf"],
 			prebuilts: ["myetc"],
+			bootclasspath_fragments: ["mybootclasspath_fragment"],
+			systemserverclasspath_fragments: ["mysystemserverclasspath_fragment"],
+			java_libs: ["myjava_library"],
 			overrides: ["oldapex"],
 			updatable: false,
 		}
@@ -6101,6 +6104,9 @@
 			apps: ["override_app"],
 			bpfs: ["override_bpf"],
 			prebuilts: ["override_myetc"],
+			bootclasspath_fragments: ["override_bootclasspath_fragment"],
+			systemserverclasspath_fragments: ["override_systemserverclasspath_fragment"],
+			java_libs: ["override_java_library"],
 			overrides: ["unknownapex"],
 			logging_parent: "com.foo.bar",
 			package_name: "test.overridden.package",
@@ -6159,6 +6165,72 @@
 			name: "override_myetc",
 			src: "override_myprebuilt",
 		}
+
+		java_library {
+			name: "bcplib",
+			srcs: ["a.java"],
+			compile_dex: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["bcp.lib"],
+		}
+
+		bootclasspath_fragment {
+			name: "mybootclasspath_fragment",
+			contents: ["bcplib"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "override_bcplib",
+			srcs: ["a.java"],
+			compile_dex: true,
+			apex_available: ["myapex"],
+			permitted_packages: ["override.bcp.lib"],
+		}
+
+		bootclasspath_fragment {
+			name: "override_bootclasspath_fragment",
+			contents: ["override_bcplib"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "systemserverlib",
+			srcs: ["a.java"],
+			apex_available: ["myapex"],
+		}
+
+		systemserverclasspath_fragment {
+			name: "mysystemserverclasspath_fragment",
+			standalone_contents: ["systemserverlib"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "override_systemserverlib",
+			srcs: ["a.java"],
+			apex_available: ["myapex"],
+		}
+
+		systemserverclasspath_fragment {
+			name: "override_systemserverclasspath_fragment",
+			standalone_contents: ["override_systemserverlib"],
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "myjava_library",
+			srcs: ["a.java"],
+			compile_dex: true,
+			apex_available: ["myapex"],
+		}
+
+		java_library {
+			name: "override_java_library",
+			srcs: ["a.java"],
+			compile_dex: true,
+			apex_available: ["myapex"],
+		}
 	`, withManifestPackageNameOverrides([]string{"myapex:com.android.myapex"}))
 
 	originalVariant := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(android.OverridableModule)
@@ -6193,6 +6265,13 @@
 		t.Errorf("override_myapex should have logging parent (com.foo.bar), but was %q.", apexBundle.overridableProperties.Logging_parent)
 	}
 
+	android.AssertArrayString(t, "Bootclasspath_fragments does not match",
+		[]string{"override_bootclasspath_fragment"}, apexBundle.overridableProperties.Bootclasspath_fragments)
+	android.AssertArrayString(t, "Systemserverclasspath_fragments does not match",
+		[]string{"override_systemserverclasspath_fragment"}, apexBundle.overridableProperties.Systemserverclasspath_fragments)
+	android.AssertArrayString(t, "Java_libs does not match",
+		[]string{"override_java_library"}, apexBundle.overridableProperties.Java_libs)
+
 	optFlags := apexRule.Args["opt_flags"]
 	ensureContains(t, optFlags, "--override_apk_package_name test.overridden.package")
 	ensureContains(t, optFlags, "--pubkey testkey2.avbpubkey")
@@ -6207,12 +6286,18 @@
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_app.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := override_bpf.o.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_bcplib.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.override_myapex")
+	ensureContains(t, androidMk, "LOCAL_MODULE := override_java_library.override_myapex")
 	ensureContains(t, androidMk, "LOCAL_MODULE_STEM := override_myapex.apex")
 	ensureContains(t, androidMk, "LOCAL_OVERRIDES_MODULES := unknownapex myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := bpf.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_app.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE := apex_manifest.pb.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_bcplib.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_systemserverlib.myapex")
+	ensureNotContains(t, androidMk, "LOCAL_MODULE := override_java_library.pb.myapex")
 	ensureNotContains(t, androidMk, "LOCAL_MODULE_STEM := myapex.apex")
 }
 
@@ -8690,6 +8775,38 @@
 	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += otherapex")
 }
 
+func TestAndroidMk_RequiredDeps(t *testing.T) {
+	ctx := testApex(t, `
+		apex {
+			name: "myapex",
+			key: "myapex.key",
+			updatable: false,
+		}
+
+		apex_key {
+			name: "myapex.key",
+			public_key: "testkey.avbpubkey",
+			private_key: "testkey.pem",
+		}
+	`)
+
+	bundle := ctx.ModuleForTests("myapex", "android_common_myapex_image").Module().(*apexBundle)
+	bundle.requiredDeps = append(bundle.requiredDeps, "foo")
+	data := android.AndroidMkDataForTest(t, ctx, bundle)
+	var builder strings.Builder
+	data.Custom(&builder, bundle.BaseModuleName(), "TARGET_", "", data)
+	androidMk := builder.String()
+	ensureContains(t, androidMk, "LOCAL_REQUIRED_MODULES += foo")
+
+	flattenedBundle := ctx.ModuleForTests("myapex", "android_common_myapex_flattened").Module().(*apexBundle)
+	flattenedBundle.requiredDeps = append(flattenedBundle.requiredDeps, "foo")
+	flattenedData := android.AndroidMkDataForTest(t, ctx, flattenedBundle)
+	var flattenedBuilder strings.Builder
+	flattenedData.Custom(&flattenedBuilder, flattenedBundle.BaseModuleName(), "TARGET_", "", flattenedData)
+	flattenedAndroidMk := flattenedBuilder.String()
+	ensureContains(t, flattenedAndroidMk, "LOCAL_REQUIRED_MODULES += foo")
+}
+
 func TestApexOutputFileProducer(t *testing.T) {
 	for _, tc := range []struct {
 		name          string
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 96c12d3..3ab846b 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -28,6 +28,8 @@
 
 	files = append(files, newFile("product_config", "soong_config_variables.bzl", cfg.Bp2buildSoongConfigDefinitions.String()))
 
+	files = append(files, newFile("product_config", "arch_configuration.bzl", android.StarlarkArchConfigurations()))
+
 	apiLevelsContent, err := json.Marshal(android.GetApiLevelsMap(cfg))
 	if err != nil {
 		panic(err)
diff --git a/bp2build/conversion_test.go b/bp2build/conversion_test.go
index 629ca9b..c94a923 100644
--- a/bp2build/conversion_test.go
+++ b/bp2build/conversion_test.go
@@ -103,6 +103,10 @@
 			basename: "soong_config_variables.bzl",
 		},
 		{
+			dir:      "product_config",
+			basename: "arch_configuration.bzl",
+		},
+		{
 			dir:      "api_levels",
 			basename: GeneratedBuildFileName,
 		},
diff --git a/cc/config/arm64_device.go b/cc/config/arm64_device.go
index 979c825..4d0ae1a 100644
--- a/cc/config/arm64_device.go
+++ b/cc/config/arm64_device.go
@@ -104,19 +104,22 @@
 	exportStringListStaticVariable("Arm64Cflags", arm64Cflags)
 	exportStringListStaticVariable("Arm64Cppflags", arm64Cppflags)
 
-	exportedStringListDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflags)
-	exportedStringListDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflags)
+	exportedVariableReferenceDictVars.Set("Arm64ArchVariantCflags", arm64ArchVariantCflagsVar)
+	exportedVariableReferenceDictVars.Set("Arm64CpuVariantCflags", arm64CpuVariantCflagsVar)
+	exportedVariableReferenceDictVars.Set("Arm64CpuVariantLdflags", arm64CpuVariantLdflags)
 
-	pctx.StaticVariable("Arm64Armv8ACflags", strings.Join(arm64ArchVariantCflags["armv8-a"], " "))
-	pctx.StaticVariable("Arm64Armv8ABranchProtCflags", strings.Join(arm64ArchVariantCflags["armv8-a-branchprot"], " "))
-	pctx.StaticVariable("Arm64Armv82ACflags", strings.Join(arm64ArchVariantCflags["armv8-2a"], " "))
-	pctx.StaticVariable("Arm64Armv82ADotprodCflags", strings.Join(arm64ArchVariantCflags["armv8-2a-dotprod"], " "))
+	exportStringListStaticVariable("Arm64Armv8ACflags", arm64ArchVariantCflags["armv8-a"])
+	exportStringListStaticVariable("Arm64Armv8ABranchProtCflags", arm64ArchVariantCflags["armv8-a-branchprot"])
+	exportStringListStaticVariable("Arm64Armv82ACflags", arm64ArchVariantCflags["armv8-2a"])
+	exportStringListStaticVariable("Arm64Armv82ADotprodCflags", arm64ArchVariantCflags["armv8-2a-dotprod"])
 
-	pctx.StaticVariable("Arm64CortexA53Cflags", strings.Join(arm64CpuVariantCflags["cortex-a53"], " "))
-	pctx.StaticVariable("Arm64CortexA55Cflags", strings.Join(arm64CpuVariantCflags["cortex-a55"], " "))
-	pctx.StaticVariable("Arm64KryoCflags", strings.Join(arm64CpuVariantCflags["kryo"], " "))
-	pctx.StaticVariable("Arm64ExynosM1Cflags", strings.Join(arm64CpuVariantCflags["exynos-m1"], " "))
-	pctx.StaticVariable("Arm64ExynosM2Cflags", strings.Join(arm64CpuVariantCflags["exynos-m2"], " "))
+	exportStringListStaticVariable("Arm64CortexA53Cflags", arm64CpuVariantCflags["cortex-a53"])
+	exportStringListStaticVariable("Arm64CortexA55Cflags", arm64CpuVariantCflags["cortex-a55"])
+	exportStringListStaticVariable("Arm64KryoCflags", arm64CpuVariantCflags["kryo"])
+	exportStringListStaticVariable("Arm64ExynosM1Cflags", arm64CpuVariantCflags["exynos-m1"])
+	exportStringListStaticVariable("Arm64ExynosM2Cflags", arm64CpuVariantCflags["exynos-m2"])
+
+	exportStringListStaticVariable("Arm64FixCortexA53Ldflags", []string{"-Wl,--fix-cortex-a53-843419"})
 }
 
 var (
@@ -128,7 +131,6 @@
 	}
 
 	arm64CpuVariantCflagsVar = map[string]string{
-		"":           "",
 		"cortex-a53": "${config.Arm64CortexA53Cflags}",
 		"cortex-a55": "${config.Arm64CortexA55Cflags}",
 		"cortex-a72": "${config.Arm64CortexA53Cflags}",
@@ -140,6 +142,15 @@
 		"exynos-m1":  "${config.Arm64ExynosM1Cflags}",
 		"exynos-m2":  "${config.Arm64ExynosM2Cflags}",
 	}
+
+	arm64CpuVariantLdflags = map[string]string{
+		"cortex-a53": "${config.Arm64FixCortexA53Ldflags}",
+		"cortex-a72": "${config.Arm64FixCortexA53Ldflags}",
+		"cortex-a73": "${config.Arm64FixCortexA53Ldflags}",
+		"kryo":       "${config.Arm64FixCortexA53Ldflags}",
+		"exynos-m1":  "${config.Arm64FixCortexA53Ldflags}",
+		"exynos-m2":  "${config.Arm64FixCortexA53Ldflags}",
+	}
 )
 
 type toolchainArm64 struct {
@@ -214,12 +225,7 @@
 	toolchainCflags = append(toolchainCflags,
 		variantOrDefault(arm64CpuVariantCflagsVar, arch.CpuVariant))
 
-	var extraLdflags string
-	switch arch.CpuVariant {
-	case "cortex-a53", "cortex-a72", "cortex-a73", "kryo", "exynos-m1", "exynos-m2":
-		extraLdflags = "-Wl,--fix-cortex-a53-843419"
-	}
-
+	extraLdflags := variantOrDefault(arm64CpuVariantLdflags, arch.CpuVariant)
 	return &toolchainArm64{
 		ldflags: strings.Join([]string{
 			"${config.Arm64Ldflags}",
diff --git a/cc/config/arm_device.go b/cc/config/arm_device.go
index 0fe5e68..4466632 100644
--- a/cc/config/arm_device.go
+++ b/cc/config/arm_device.go
@@ -39,6 +39,10 @@
 
 	armLldflags = armLdflags
 
+	armFixCortexA8LdFlags = []string{"-Wl,--fix-cortex-a8"}
+
+	armNoFixCortexA8LdFlags = []string{"-Wl,--no-fix-cortex-a8"}
+
 	armArmCflags = []string{
 		"-fstrict-aliasing",
 	}
@@ -179,6 +183,9 @@
 	exportStringListStaticVariable("ArmLdflags", armLdflags)
 	exportStringListStaticVariable("ArmLldflags", armLldflags)
 
+	exportStringListStaticVariable("ArmFixCortexA8LdFlags", armFixCortexA8LdFlags)
+	exportStringListStaticVariable("ArmNoFixCortexA8LdFlags", armNoFixCortexA8LdFlags)
+
 	// Clang cflags
 	exportStringListStaticVariable("ArmToolchainCflags", armToolchainCflags)
 	exportStringListStaticVariable("ArmCflags", armCflags)
@@ -188,8 +195,8 @@
 	exportStringListStaticVariable("ArmArmCflags", armArmCflags)
 	exportStringListStaticVariable("ArmThumbCflags", armThumbCflags)
 
-	exportedStringListDictVars.Set("ArmArchVariantCflags", armArchVariantCflags)
-	exportedStringListDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflags)
+	exportedVariableReferenceDictVars.Set("ArmArchVariantCflags", armArchVariantCflagsVar)
+	exportedVariableReferenceDictVars.Set("ArmCpuVariantCflags", armCpuVariantCflagsVar)
 
 	// Clang arch variant cflags
 	exportStringListStaticVariable("ArmArmv7ACflags", armArchVariantCflags["armv7-a"])
@@ -324,12 +331,12 @@
 		switch arch.CpuVariant {
 		case "cortex-a8", "":
 			// Generic ARM might be a Cortex A8 -- better safe than sorry
-			fixCortexA8 = "-Wl,--fix-cortex-a8"
+			fixCortexA8 = "${config.ArmFixCortexA8LdFlags}"
 		default:
-			fixCortexA8 = "-Wl,--no-fix-cortex-a8"
+			fixCortexA8 = "${config.ArmNoFixCortexA8LdFlags}"
 		}
 	case "armv7-a":
-		fixCortexA8 = "-Wl,--fix-cortex-a8"
+		fixCortexA8 = "${config.ArmFixCortexA8LdFlags}"
 	case "armv8-a", "armv8-2a":
 		// Nothing extra for armv8-a/armv8-2a
 	default:
diff --git a/cc/config/bp2build.go b/cc/config/bp2build.go
index eca5161..73f65f5 100644
--- a/cc/config/bp2build.go
+++ b/cc/config/bp2build.go
@@ -38,6 +38,8 @@
 	exportedStringListVars     = exportedStringListVariables{}
 	exportedStringVars         = exportedStringVariables{}
 	exportedStringListDictVars = exportedStringListDictVariables{}
+	// Note: these can only contain references to other variables and must be printed last
+	exportedVariableReferenceDictVars = exportedVariableReferenceDictVariables{}
 
 	/// Maps containing variables that are dependent on the build config.
 	exportedConfigDependingVars = exportedConfigDependingVariables{}
@@ -62,6 +64,7 @@
 type bazelConstant struct {
 	variableName       string
 	internalDefinition string
+	sortLast           bool
 }
 
 type exportedStringVariables map[string]string
@@ -168,6 +171,36 @@
 	return ret
 }
 
+type exportedVariableReferenceDictVariables map[string]map[string]string
+
+func (m exportedVariableReferenceDictVariables) Set(k string, v map[string]string) {
+	m[k] = v
+}
+
+func (m exportedVariableReferenceDictVariables) asBazel(_ android.Config, _ exportedStringVariables,
+	_ exportedStringListVariables, _ exportedConfigDependingVariables) []bazelConstant {
+	ret := make([]bazelConstant, 0, len(m))
+	for n, dict := range m {
+		for k, v := range dict {
+			matches, err := variableReference(v)
+			if err != nil {
+				panic(err)
+			} else if !matches.matches {
+				panic(fmt.Errorf("Expected a variable reference, got %q", v))
+			} else if len(matches.fullVariableReference) != len(v) {
+				panic(fmt.Errorf("Expected only a variable reference, got %q", v))
+			}
+			dict[k] = "_" + matches.variable
+		}
+		ret = append(ret, bazelConstant{
+			variableName:       n,
+			internalDefinition: starlark_fmt.PrintDict(dict, 0),
+			sortLast:           true,
+		})
+	}
+	return ret
+}
+
 // BazelCcToolchainVars generates bzl file content containing variables for
 // Bazel's cc_toolchain configuration.
 func BazelCcToolchainVars(config android.Config) string {
@@ -175,7 +208,8 @@
 		config,
 		exportedStringListDictVars,
 		exportedStringListVars,
-		exportedStringVars)
+		exportedStringVars,
+		exportedVariableReferenceDictVars)
 }
 
 func bazelToolchainVars(config android.Config, vars ...bazelVarExporter) string {
@@ -186,7 +220,12 @@
 		results = append(results, v.asBazel(config, exportedStringVars, exportedStringListVars, exportedConfigDependingVars)...)
 	}
 
-	sort.Slice(results, func(i, j int) bool { return results[i].variableName < results[j].variableName })
+	sort.Slice(results, func(i, j int) bool {
+		if results[i].sortLast != results[j].sortLast {
+			return !results[i].sortLast
+		}
+		return results[i].variableName < results[j].variableName
+	})
 
 	definitions := make([]string, 0, len(results))
 	constants := make([]string, 0, len(results))
@@ -207,6 +246,32 @@
 	return ret
 }
 
+type match struct {
+	matches               bool
+	fullVariableReference string
+	variable              string
+}
+
+func variableReference(input string) (match, error) {
+	// e.g. "${ExternalCflags}"
+	r := regexp.MustCompile(`\${(?:config\.)?([a-zA-Z0-9_]+)}`)
+
+	matches := r.FindStringSubmatch(input)
+	if len(matches) == 0 {
+		return match{}, nil
+	}
+	if len(matches) != 2 {
+		return match{}, fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", input, len(matches)-1)
+	}
+	return match{
+		matches:               true,
+		fullVariableReference: matches[0],
+		// Index 1 of FindStringSubmatch contains the subexpression match
+		// (variable name) of the capture group.
+		variable: matches[1],
+	}, nil
+}
+
 // expandVar recursively expand interpolated variables in the exportedVars scope.
 //
 // We're using a string slice to track the seen variables to avoid
@@ -216,8 +281,6 @@
 // interpolation stacks are deep (n > 1).
 func expandVar(config android.Config, toExpand string, stringScope exportedStringVariables,
 	stringListScope exportedStringListVariables, exportedVars exportedConfigDependingVariables) ([]string, error) {
-	// e.g. "${ExternalCflags}"
-	r := regexp.MustCompile(`\${([a-zA-Z0-9_]+)}`)
 
 	// Internal recursive function.
 	var expandVarInternal func(string, map[string]bool) (string, error)
@@ -225,20 +288,18 @@
 		var ret string
 		remainingString := toExpand
 		for len(remainingString) > 0 {
-			matches := r.FindStringSubmatch(remainingString)
-			if len(matches) == 0 {
+			matches, err := variableReference(remainingString)
+			if err != nil {
+				panic(err)
+			}
+			if !matches.matches {
 				return ret + remainingString, nil
 			}
-			if len(matches) != 2 {
-				panic(fmt.Errorf("Expected to only match 1 subexpression in %s, got %d", remainingString, len(matches)-1))
-			}
-			matchIndex := strings.Index(remainingString, matches[0])
+			matchIndex := strings.Index(remainingString, matches.fullVariableReference)
 			ret += remainingString[:matchIndex]
-			remainingString = remainingString[matchIndex+len(matches[0]):]
+			remainingString = remainingString[matchIndex+len(matches.fullVariableReference):]
 
-			// Index 1 of FindStringSubmatch contains the subexpression match
-			// (variable name) of the capture group.
-			variable := matches[1]
+			variable := matches.variable
 			// toExpand contains a variable.
 			if _, ok := seenVars[variable]; ok {
 				return ret, fmt.Errorf(
diff --git a/cc/config/bp2build_test.go b/cc/config/bp2build_test.go
index 4cbf0c6..9a8178a 100644
--- a/cc/config/bp2build_test.go
+++ b/cc/config/bp2build_test.go
@@ -48,6 +48,14 @@
 			expectedValues: []string{"bar"},
 		},
 		{
+			description: "single level expansion with short-name for string var",
+			stringScope: exportedStringVariables{
+				"foo": "bar",
+			},
+			toExpand:       "${config.foo}",
+			expectedValues: []string{"bar"},
+		},
+		{
 			description: "single level expansion string list var",
 			stringListScope: exportedStringListVariables{
 				"foo": []string{"bar"},
@@ -224,7 +232,30 @@
 )`,
 		},
 		{
-			name: "sorts across types",
+			name: "exports dict with var refs",
+			vars: []bazelVarExporter{
+				exportedVariableReferenceDictVariables{
+					"a": map[string]string{"b1": "${b2}"},
+					"c": map[string]string{"d1": "${config.d2}"},
+				},
+			},
+			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
+
+_a = {
+    "b1": _b2,
+}
+
+_c = {
+    "d1": _d2,
+}
+
+constants = struct(
+    a = _a,
+    c = _c,
+)`,
+		},
+		{
+			name: "sorts across types with variable references last",
 			vars: []bazelVarExporter{
 				exportedStringVariables{
 					"b": "b-val",
@@ -238,6 +269,10 @@
 					"a": map[string][]string{"a1": []string{"a2"}},
 					"f": map[string][]string{"f1": []string{"f2"}},
 				},
+				exportedVariableReferenceDictVariables{
+					"aa": map[string]string{"b1": "${b}"},
+					"cc": map[string]string{"d1": "${config.d}"},
+				},
 			},
 			expectedOut: `# GENERATED FOR BAZEL FROM SOONG. DO NOT EDIT.
 
@@ -257,6 +292,14 @@
     "f1": ["f2"],
 }
 
+_aa = {
+    "b1": _b,
+}
+
+_cc = {
+    "d1": _d,
+}
+
 constants = struct(
     a = _a,
     b = _b,
@@ -264,6 +307,8 @@
     d = _d,
     e = _e,
     f = _f,
+    aa = _aa,
+    cc = _cc,
 )`,
 		},
 	}
diff --git a/cc/config/x86_64_device.go b/cc/config/x86_64_device.go
index 0da51cb..164e7a6 100644
--- a/cc/config/x86_64_device.go
+++ b/cc/config/x86_64_device.go
@@ -78,14 +78,6 @@
 		"popcnt": []string{"-mpopcnt"},
 		"aes_ni": []string{"-maes"},
 	}
-
-	x86_64DefaultArchVariantFeatures = []string{
-		"ssse3",
-		"sse4",
-		"sse4_1",
-		"sse4_2",
-		"popcnt",
-	}
 )
 
 const (
@@ -93,8 +85,6 @@
 )
 
 func init() {
-	android.RegisterDefaultArchVariantFeatures(android.Android, android.X86_64, x86_64DefaultArchVariantFeatures...)
-	exportedStringListVars.Set("X86_64DefaultArchVariantFeatures", x86_64DefaultArchVariantFeatures)
 
 	pctx.StaticVariable("x86_64GccVersion", x86_64GccVersion)
 
diff --git a/cmd/extract_linker/main.go b/cmd/extract_linker/main.go
index 5603b41..aaca1dd 100644
--- a/cmd/extract_linker/main.go
+++ b/cmd/extract_linker/main.go
@@ -116,7 +116,7 @@
 
 	// Discard the PT_INTERP section so that the linker doesn't need to be passed the
 	// --no-dynamic-linker flag.
-	fmt.Println(script, "    /DISCARD/ : { *(.interp) }")
+	fmt.Fprintln(script, "  /DISCARD/ : { *(.interp) }")
 
 	fmt.Fprintln(script, "}")
 	fmt.Fprintln(script, "INSERT BEFORE .note.android.embedded_linker;")
diff --git a/genrule/genrule.go b/genrule/genrule.go
index 2ddd70e..c52ddee 100644
--- a/genrule/genrule.go
+++ b/genrule/genrule.go
@@ -382,9 +382,12 @@
 		addLocationLabel(toolFile, toolLocation{paths})
 	}
 
+	includeDirInPaths := ctx.DeviceConfig().BuildBrokenInputDir(g.Name())
 	var srcFiles android.Paths
 	for _, in := range g.properties.Srcs {
-		paths, missingDeps := android.PathsAndMissingDepsForModuleSrcExcludes(ctx, []string{in}, g.properties.Exclude_srcs)
+		paths, missingDeps := android.PathsAndMissingDepsRelativeToModuleSourceDir(android.SourceInput{
+			Context: ctx, Paths: []string{in}, ExcludePaths: g.properties.Exclude_srcs, IncludeDirs: includeDirInPaths,
+		})
 		if len(missingDeps) > 0 {
 			if !ctx.Config().AllowMissingDependencies() {
 				panic(fmt.Errorf("should never get here, the missing dependencies %q should have been reported in DepsMutator",
diff --git a/java/base.go b/java/base.go
index 6ff2d03..8747039 100644
--- a/java/base.go
+++ b/java/base.go
@@ -862,7 +862,7 @@
 		}
 		errorProneFlags = append(errorProneFlags, j.properties.Errorprone.Javacflags...)
 
-		flags.errorProneExtraJavacFlags = "${config.ErrorProneFlags} " +
+		flags.errorProneExtraJavacFlags = "${config.ErrorProneHeapFlags} ${config.ErrorProneFlags} " +
 			"'" + strings.Join(errorProneFlags, " ") + "'"
 		flags.errorProneProcessorPath = classpath(android.PathsForSource(ctx, config.ErrorProneClasspath))
 	}
diff --git a/java/config/config.go b/java/config/config.go
index ea2f934..39584cb 100644
--- a/java/config/config.go
+++ b/java/config/config.go
@@ -68,6 +68,12 @@
 
 	pctx.StaticVariable("JavacHeapSize", "2048M")
 	pctx.StaticVariable("JavacHeapFlags", "-J-Xmx${JavacHeapSize}")
+
+	// ErrorProne can use significantly more memory than javac alone, give it a higher heap
+	// size (b/221480398).
+	pctx.StaticVariable("ErrorProneHeapSize", "4096M")
+	pctx.StaticVariable("ErrorProneHeapFlags", "-J-Xmx${ErrorProneHeapSize}")
+
 	pctx.StaticVariable("DexFlags", "-JXX:OnError='cat hs_err_pid%p.log' -JXX:CICompilerCount=6 -JXX:+UseDynamicNumberOfGCThreads")
 
 	pctx.StaticVariable("CommonJdkFlags", strings.Join([]string{
diff --git a/mk2rbc/expr.go b/mk2rbc/expr.go
index 07f7ca1..dc16d1d 100644
--- a/mk2rbc/expr.go
+++ b/mk2rbc/expr.go
@@ -595,6 +595,7 @@
 	for i, arg := range cx.args {
 		argsCopy[i] = arg.transform(transformer)
 	}
+	cx.args = argsCopy
 	if replacement := transformer(cx); replacement != nil {
 		return replacement
 	} else {
diff --git a/mk2rbc/mk2rbc.go b/mk2rbc/mk2rbc.go
index 2b46c2e..74053fe 100644
--- a/mk2rbc/mk2rbc.go
+++ b/mk2rbc/mk2rbc.go
@@ -69,54 +69,55 @@
 var knownFunctions = map[string]interface {
 	parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr
 }{
-	"abspath":                             &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString, addGlobals: false},
-	"add_soong_config_namespace":          &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
-	"add_soong_config_var_value":          &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
-	soongConfigAssign:                     &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
-	soongConfigAppend:                     &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
-	"soong_config_get":                    &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
-	"add-to-product-copy-files-if-exists": &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList, addGlobals: false},
-	"addprefix":                           &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList, addGlobals: false},
-	"addsuffix":                           &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList, addGlobals: false},
-	"copy-files":                          &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList, addGlobals: false},
-	"dir":                                 &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList, addGlobals: false},
-	"dist-for-goals":                      &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
-	"enforce-product-packages-exist":      &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid, addGlobals: false},
-	"error":                               &makeControlFuncParser{name: baseName + ".mkerror"},
-	"findstring":                          &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt, addGlobals: false},
-	"find-copy-subdir-files":              &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList, addGlobals: false},
-	"filter":                              &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList, addGlobals: false},
-	"filter-out":                          &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList, addGlobals: false},
-	"firstword":                           &firstOrLastwordCallParser{isLastWord: false},
-	"foreach":                             &foreachCallPaser{},
-	"if":                                  &ifCallParser{},
-	"info":                                &makeControlFuncParser{name: baseName + ".mkinfo"},
-	"is-board-platform":                   &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
-	"is-board-platform2":                  &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
-	"is-board-platform-in-list":           &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
-	"is-board-platform-in-list2":          &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
-	"is-product-in-list":                  &isProductInListCallParser{},
-	"is-vendor-board-platform":            &isVendorBoardPlatformCallParser{},
-	"is-vendor-board-qcom":                &isVendorBoardQcomCallParser{},
-	"lastword":                            &firstOrLastwordCallParser{isLastWord: true},
-	"notdir":                              &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString, addGlobals: false},
-	"math_max":                            &mathMaxOrMinCallParser{function: "max"},
-	"math_min":                            &mathMaxOrMinCallParser{function: "min"},
-	"math_gt_or_eq":                       &mathComparisonCallParser{op: ">="},
-	"math_gt":                             &mathComparisonCallParser{op: ">"},
-	"math_lt":                             &mathComparisonCallParser{op: "<"},
-	"my-dir":                              &myDirCallParser{},
-	"patsubst":                            &substCallParser{fname: "patsubst"},
-	"product-copy-files-by-pattern":       &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList, addGlobals: false},
-	"require-artifacts-in-path":           &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid, addGlobals: false},
-	"require-artifacts-in-path-relaxed":   &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid, addGlobals: false},
+	"abspath":                              &simpleCallParser{name: baseName + ".abspath", returnType: starlarkTypeString},
+	"add-product-dex-preopt-module-config": &simpleCallParser{name: baseName + ".add_product_dex_preopt_module_config", returnType: starlarkTypeString, addHandle: true},
+	"add_soong_config_namespace":           &simpleCallParser{name: baseName + ".soong_config_namespace", returnType: starlarkTypeVoid, addGlobals: true},
+	"add_soong_config_var_value":           &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	soongConfigAssign:                      &simpleCallParser{name: baseName + ".soong_config_set", returnType: starlarkTypeVoid, addGlobals: true},
+	soongConfigAppend:                      &simpleCallParser{name: baseName + ".soong_config_append", returnType: starlarkTypeVoid, addGlobals: true},
+	"soong_config_get":                     &simpleCallParser{name: baseName + ".soong_config_get", returnType: starlarkTypeString, addGlobals: true},
+	"add-to-product-copy-files-if-exists":  &simpleCallParser{name: baseName + ".copy_if_exists", returnType: starlarkTypeList},
+	"addprefix":                            &simpleCallParser{name: baseName + ".addprefix", returnType: starlarkTypeList},
+	"addsuffix":                            &simpleCallParser{name: baseName + ".addsuffix", returnType: starlarkTypeList},
+	"copy-files":                           &simpleCallParser{name: baseName + ".copy_files", returnType: starlarkTypeList},
+	"dir":                                  &simpleCallParser{name: baseName + ".dir", returnType: starlarkTypeList},
+	"dist-for-goals":                       &simpleCallParser{name: baseName + ".mkdist_for_goals", returnType: starlarkTypeVoid, addGlobals: true},
+	"enforce-product-packages-exist":       &simpleCallParser{name: baseName + ".enforce_product_packages_exist", returnType: starlarkTypeVoid},
+	"error":                                &makeControlFuncParser{name: baseName + ".mkerror"},
+	"findstring":                           &simpleCallParser{name: baseName + ".findstring", returnType: starlarkTypeInt},
+	"find-copy-subdir-files":               &simpleCallParser{name: baseName + ".find_and_copy", returnType: starlarkTypeList},
+	"filter":                               &simpleCallParser{name: baseName + ".filter", returnType: starlarkTypeList},
+	"filter-out":                           &simpleCallParser{name: baseName + ".filter_out", returnType: starlarkTypeList},
+	"firstword":                            &firstOrLastwordCallParser{isLastWord: false},
+	"foreach":                              &foreachCallPaser{},
+	"if":                                   &ifCallParser{},
+	"info":                                 &makeControlFuncParser{name: baseName + ".mkinfo"},
+	"is-board-platform":                    &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform2":                   &simpleCallParser{name: baseName + ".board_platform_is", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform-in-list":            &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+	"is-board-platform-in-list2":           &simpleCallParser{name: baseName + ".board_platform_in", returnType: starlarkTypeBool, addGlobals: true},
+	"is-product-in-list":                   &isProductInListCallParser{},
+	"is-vendor-board-platform":             &isVendorBoardPlatformCallParser{},
+	"is-vendor-board-qcom":                 &isVendorBoardQcomCallParser{},
+	"lastword":                             &firstOrLastwordCallParser{isLastWord: true},
+	"notdir":                               &simpleCallParser{name: baseName + ".notdir", returnType: starlarkTypeString},
+	"math_max":                             &mathMaxOrMinCallParser{function: "max"},
+	"math_min":                             &mathMaxOrMinCallParser{function: "min"},
+	"math_gt_or_eq":                        &mathComparisonCallParser{op: ">="},
+	"math_gt":                              &mathComparisonCallParser{op: ">"},
+	"math_lt":                              &mathComparisonCallParser{op: "<"},
+	"my-dir":                               &myDirCallParser{},
+	"patsubst":                             &substCallParser{fname: "patsubst"},
+	"product-copy-files-by-pattern":        &simpleCallParser{name: baseName + ".product_copy_files_by_pattern", returnType: starlarkTypeList},
+	"require-artifacts-in-path":            &simpleCallParser{name: baseName + ".require_artifacts_in_path", returnType: starlarkTypeVoid},
+	"require-artifacts-in-path-relaxed":    &simpleCallParser{name: baseName + ".require_artifacts_in_path_relaxed", returnType: starlarkTypeVoid},
 	// TODO(asmundak): remove it once all calls are removed from configuration makefiles. see b/183161002
 	"shell":    &shellCallParser{},
-	"strip":    &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString, addGlobals: false},
+	"strip":    &simpleCallParser{name: baseName + ".mkstrip", returnType: starlarkTypeString},
 	"subst":    &substCallParser{fname: "subst"},
 	"warning":  &makeControlFuncParser{name: baseName + ".mkwarning"},
 	"word":     &wordCallParser{},
-	"wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList, addGlobals: false},
+	"wildcard": &simpleCallParser{name: baseName + ".expand_wildcard", returnType: starlarkTypeList},
 }
 
 // These are functions that we don't implement conversions for, but
@@ -806,20 +807,16 @@
 	if len(matchingPaths) > maxMatchingFiles {
 		return []starlarkNode{ctx.newBadNode(v, "there are >%d files matching the pattern, please rewrite it", maxMatchingFiles)}
 	}
-	if len(matchingPaths) == 1 {
-		res := inheritedStaticModule{ctx.newDependentModule(matchingPaths[0], loadAlways && ctx.ifNestLevel == 0), loadAlways}
-		return []starlarkNode{processModule(res)}
-	} else {
-		needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
-		res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
-		for _, p := range matchingPaths {
-			// A product configuration files discovered dynamically may attempt to inherit
-			// from another one which does not exist in this source tree. Prevent load errors
-			// by always loading the dynamic files as optional.
-			res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
-		}
-		return []starlarkNode{processModule(res)}
+
+	needsWarning := pathPattern[0] == "" && len(ctx.includeTops) == 0
+	res := inheritedDynamicModule{*varPath, []*moduleInfo{}, loadAlways, ctx.errorLocation(v), needsWarning}
+	for _, p := range matchingPaths {
+		// A product configuration files discovered dynamically may attempt to inherit
+		// from another one which does not exist in this source tree. Prevent load errors
+		// by always loading the dynamic files as optional.
+		res.candidateModules = append(res.candidateModules, ctx.newDependentModule(p, true))
 	}
+	return []starlarkNode{processModule(res)}
 }
 
 func (ctx *parseContext) findMatchingPaths(pattern []string) []string {
@@ -1310,6 +1307,7 @@
 	name       string
 	returnType starlarkType
 	addGlobals bool
+	addHandle  bool
 }
 
 func (p *simpleCallParser) parse(ctx *parseContext, node mkparser.Node, args *mkparser.MakeString) starlarkExpr {
@@ -1317,6 +1315,9 @@
 	if p.addGlobals {
 		expr.args = append(expr.args, &globalsExpr{})
 	}
+	if p.addHandle {
+		expr.args = append(expr.args, &identifierExpr{name: "handle"})
+	}
 	for _, arg := range args.Split(",") {
 		arg.TrimLeftSpaces()
 		arg.TrimRightSpaces()
@@ -1870,9 +1871,7 @@
 	fmt.Fprintf(&buf, "load(%q, %q)\n", baseUri, baseName)
 	fmt.Fprintf(&buf, "load(%q, \"init\")\n", mainModuleUri)
 	fmt.Fprintf(&buf, "load(%q, input_variables_init = \"init\")\n", inputVariablesUri)
-	fmt.Fprintf(&buf, "globals, cfg, globals_base = %s(init, input_variables_init)\n", cfnBoardMain)
-	fmt.Fprintf(&buf, "# TODO: Some product config variables need to be printed, but most are readonly so we can't just print cfg here.\n")
-	fmt.Fprintf(&buf, "%s((globals, cfg, globals_base))\n", cfnPrintVars)
+	fmt.Fprintf(&buf, "%s(%s(init, input_variables_init))\n", cfnPrintVars, cfnBoardMain)
 	return buf.String()
 }
 
diff --git a/mk2rbc/mk2rbc_test.go b/mk2rbc/mk2rbc_test.go
index 447f658..55d48ff 100644
--- a/mk2rbc/mk2rbc_test.go
+++ b/mk2rbc/mk2rbc_test.go
@@ -739,6 +739,7 @@
 $(call require-artifacts-in-path, foo, bar)
 $(call require-artifacts-in-path-relaxed, foo, bar)
 $(call dist-for-goals, goal, from:to)
+$(call add-product-dex-preopt-module-config,MyModule,disable)
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
 
@@ -749,6 +750,7 @@
   rblf.require_artifacts_in_path("foo", "bar")
   rblf.require_artifacts_in_path_relaxed("foo", "bar")
   rblf.mkdist_for_goals(g, "goal", "from:to")
+  rblf.add_product_dex_preopt_module_config(handle, "MyModule", "disable")
 `,
 	},
 	{
@@ -1079,7 +1081,13 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   g["MY_PATH"] = "foo"
-  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+  _entry = {
+    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+  }.get("%s/cfg.mk" % g["MY_PATH"])
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
+  rblf.inherit(handle, _varmod, _varmod_init)
 `,
 	},
 	{
@@ -1099,8 +1107,20 @@
 def init(g, handle):
   cfg = rblf.cfg(handle)
   g["MY_PATH"] = "foo"
-  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
-  rblf.inherit(handle, "vendor/foo1/cfg", _cfg_init)
+  _entry = {
+    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+  }.get("%s/cfg.mk" % g["MY_PATH"])
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
+  rblf.inherit(handle, _varmod, _varmod_init)
+  _entry = {
+    "vendor/foo1/cfg.mk": ("vendor/foo1/cfg", _cfg_init),
+  }.get("%s/cfg.mk" % g["MY_PATH"])
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/cfg.mk" % g["MY_PATH"]))
+  rblf.inherit(handle, _varmod, _varmod_init)
 `,
 	},
 	{
@@ -1124,9 +1144,21 @@
 
 def init(g, handle):
   cfg = rblf.cfg(handle)
-  rblf.inherit(handle, "foo/font", _font_init)
+  _entry = {
+    "foo/font.mk": ("foo/font", _font_init),
+  }.get("%s/font.mk" % g.get("MY_VAR", ""))
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+  rblf.inherit(handle, _varmod, _varmod_init)
   # There's some space and even this comment between the include_top and the inherit-product
-  rblf.inherit(handle, "foo/font", _font_init)
+  _entry = {
+    "foo/font.mk": ("foo/font", _font_init),
+  }.get("%s/font.mk" % g.get("MY_VAR", ""))
+  (_varmod, _varmod_init) = _entry if _entry else (None, None)
+  if not _varmod_init:
+    rblf.mkerror("product.mk", "Cannot find %s" % ("%s/font.mk" % g.get("MY_VAR", "")))
+  rblf.inherit(handle, _varmod, _varmod_init)
   rblf.mkwarning("product.mk:11", "Please avoid starting an include path with a variable. See https://source.android.com/setup/build/bazel/product_config/issues/includes for details.")
   _entry = {
     "foo/font.mk": ("foo/font", _font_init),
@@ -1228,6 +1260,9 @@
 BOOT_KERNEL_MODULES_LIST += bar.ko
 BOOT_KERNEL_MODULES_FILTER_2 := $(foreach m,$(BOOT_KERNEL_MODULES_LIST),%/$(m))
 
+FOREACH_WITH_IF := $(foreach module,\
+  $(BOOT_KERNEL_MODULES_LIST),\
+  $(if $(filter $(module),foo.ko),,$(error module "$(module)" has an error!)))
 `,
 		expected: `load("//build/make/core:product_config.rbc", "rblf")
 
@@ -1238,6 +1273,7 @@
   g["BOOT_KERNEL_MODULES_LIST"] = ["foo.ko"]
   g["BOOT_KERNEL_MODULES_LIST"] += ["bar.ko"]
   g["BOOT_KERNEL_MODULES_FILTER_2"] = ["%%/%s" % m for m in g["BOOT_KERNEL_MODULES_LIST"]]
+  g["FOREACH_WITH_IF"] = [("" if rblf.filter(module, "foo.ko") else rblf.mkerror("product.mk", "module \"%s\" has an error!" % module)) for module in g["BOOT_KERNEL_MODULES_LIST"]]
 `,
 	},
 	{
diff --git a/rust/config/global.go b/rust/config/global.go
index c1ce13f..1cf773e 100644
--- a/rust/config/global.go
+++ b/rust/config/global.go
@@ -24,7 +24,7 @@
 var pctx = android.NewPackageContext("android/soong/rust/config")
 
 var (
-	RustDefaultVersion = "1.58.1"
+	RustDefaultVersion = "1.59.0"
 	RustDefaultBase    = "prebuilts/rust/"
 	DefaultEdition     = "2021"
 	Stdlibs            = []string{
@@ -49,7 +49,7 @@
 		"-C overflow-checks=on",
 		"-C force-unwind-tables=yes",
 		// Use v0 mangling to distinguish from C++ symbols
-		"-Z symbol-mangling-version=v0",
+		"-C symbol-mangling-version=v0",
 	}
 
 	deviceGlobalRustFlags = []string{
diff --git a/rust/fuzz_test.go b/rust/fuzz_test.go
index 98be7c2..865665e 100644
--- a/rust/fuzz_test.go
+++ b/rust/fuzz_test.go
@@ -47,17 +47,17 @@
 	// 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"], "-Z sanitizer=hwaddress") ||
-		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov'") ||
+		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "-C passes='sancov-module'") ||
 		!strings.Contains(fuzz_libtest.Args["rustcFlags"], "--cfg fuzzing") {
-		t.Errorf("rust_fuzz module does not contain the expected flags (sancov, cfg fuzzing, hwaddress sanitizer).")
+		t.Errorf("rust_fuzz module does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
 
 	}
 
 	// 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"], "-Z sanitizer=hwaddress") ||
-		!strings.Contains(libtest_fuzzer.Args["rustcFlags"], "-C passes='sancov'") ||
+		!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, cfg fuzzing, hwaddress sanitizer).")
+		t.Errorf("rust_fuzz dependent library does not contain the expected flags (sancov-module, cfg fuzzing, hwaddress sanitizer).")
 	}
 }
diff --git a/rust/sanitize.go b/rust/sanitize.go
index be9dc42..39aaf33 100644
--- a/rust/sanitize.go
+++ b/rust/sanitize.go
@@ -57,7 +57,7 @@
 }
 
 var fuzzerFlags = []string{
-	"-C passes='sancov'",
+	"-C passes='sancov-module'",
 
 	"--cfg fuzzing",
 	"-C llvm-args=-sanitizer-coverage-level=3",
@@ -70,7 +70,7 @@
 	"-C link-dead-code",
 
 	// Sancov breaks with lto
-	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov works with LTO
+	// TODO: Remove when https://bugs.llvm.org/show_bug.cgi?id=41734 is resolved and sancov-module works with LTO
 	"-C lto=no",
 }
 
diff --git a/ui/build/config.go b/ui/build/config.go
index 1dd948c..077a4d1 100644
--- a/ui/build/config.go
+++ b/ui/build/config.go
@@ -35,10 +35,10 @@
 )
 
 const (
-	envConfigDir  = "vendor/google/tools/soong_config"
-	jsonSuffix    = "json"
+	envConfigDir = "vendor/google/tools/soong_config"
+	jsonSuffix   = "json"
 
-	configFetcher = "vendor/google/tools/soong/expconfigfetcher"
+	configFetcher         = "vendor/google/tools/soong/expconfigfetcher"
 	envConfigFetchTimeout = 10 * time.Second
 )
 
@@ -62,6 +62,7 @@
 	jsonModuleGraph bool
 	bp2build        bool
 	queryview       bool
+	reportMkMetrics bool // Collect and report mk2bp migration progress metrics.
 	soongDocs       bool
 	skipConfig      bool
 	skipKati        bool
@@ -155,7 +156,7 @@
 	}
 
 	configExists := false
-	outConfigFilePath := filepath.Join(config.OutDir(), envConfigName + jsonSuffix)
+	outConfigFilePath := filepath.Join(config.OutDir(), envConfigName+jsonSuffix)
 	if _, err := os.Stat(outConfigFilePath); err == nil {
 		configExists = true
 	}
@@ -711,6 +712,8 @@
 			c.skipConfig = true
 		} else if arg == "--skip-soong-tests" {
 			c.skipSoongTests = true
+		} else if arg == "--mk-metrics" {
+			c.reportMkMetrics = true
 		} else if len(arg) > 0 && arg[0] == '-' {
 			parseArgNum := func(def int) int {
 				if len(arg) > 2 {
@@ -1381,6 +1384,11 @@
 	return filepath.Join(c.LogsDir(), "bazel_metrics")
 }
 
+// MkFileMetrics returns the file path for make-related metrics.
+func (c *configImpl) MkMetrics() string {
+	return filepath.Join(c.LogsDir(), "mk_metrics.pb")
+}
+
 func (c *configImpl) SetEmptyNinjaFile(v bool) {
 	c.emptyNinjaFile = v
 }
diff --git a/ui/build/finder.go b/ui/build/finder.go
index 68efe21..262de3d 100644
--- a/ui/build/finder.go
+++ b/ui/build/finder.go
@@ -138,6 +138,17 @@
 		ctx.Fatalf("Could not export module list: %v", err)
 	}
 
+	// Gate collecting/reporting mk metrics on builds that specifically request
+	// it, as identifying the total number of mk files adds 4-5ms onto null
+	// builds.
+	if config.reportMkMetrics {
+		androidMksTotal := f.FindNamedAt(".", "Android.mk")
+
+		ctx.Metrics.SetToplevelMakefiles(len(androidMks))
+		ctx.Metrics.SetTotalMakefiles(len(androidMksTotal))
+		ctx.Metrics.DumpMkMetrics(config.MkMetrics())
+	}
+
 	// Stop searching a subdirectory recursively after finding a CleanSpec.mk.
 	cleanSpecs := f.FindFirstNamedAt(".", "CleanSpec.mk")
 	err = dumpListToFile(ctx, config, cleanSpecs, filepath.Join(dumpDir, "CleanSpec.mk.list"))
diff --git a/ui/build/soong.go b/ui/build/soong.go
index 117a2a5..1b993e1 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -507,6 +507,9 @@
 	if shouldCollectBuildSoongMetrics(config) && ctx.Metrics != nil {
 		ctx.Metrics.SetSoongBuildMetrics(soongBuildMetrics)
 	}
+	if config.JsonModuleGraph() {
+		distGzipFile(ctx, config, config.ModuleGraphFile(), "soong")
+	}
 }
 
 func runMicrofactory(ctx Context, config Config, name string, pkg string, mapping map[string]string) {
diff --git a/ui/metrics/Android.bp b/ui/metrics/Android.bp
index 3ba3907..05db1d7 100644
--- a/ui/metrics/Android.bp
+++ b/ui/metrics/Android.bp
@@ -21,9 +21,10 @@
     pkgPath: "android/soong/ui/metrics",
     deps: [
         "golang-protobuf-proto",
+        "soong-ui-bp2build_metrics_proto",
         "soong-ui-metrics_upload_proto",
         "soong-ui-metrics_proto",
-        "soong-ui-bp2build_metrics_proto",
+        "soong-ui-mk_metrics_proto",
         "soong-ui-tracer",
         "soong-shared",
     ],
@@ -71,3 +72,15 @@
         "bp2build_metrics_proto/bp2build_metrics.pb.go",
     ],
 }
+
+bootstrap_go_package {
+    name: "soong-ui-mk_metrics_proto",
+    pkgPath: "android/soong/ui/metrics/mk_metrics_proto",
+    deps: [
+        "golang-protobuf-reflect-protoreflect",
+        "golang-protobuf-runtime-protoimpl",
+    ],
+    srcs: [
+        "mk_metrics_proto/mk_metrics.pb.go",
+    ],
+}
diff --git a/ui/metrics/metrics.go b/ui/metrics/metrics.go
index 80f8c1a..6f1ed60 100644
--- a/ui/metrics/metrics.go
+++ b/ui/metrics/metrics.go
@@ -41,6 +41,7 @@
 	"google.golang.org/protobuf/proto"
 
 	soong_metrics_proto "android/soong/ui/metrics/metrics_proto"
+	mk_metrics_proto "android/soong/ui/metrics/mk_metrics_proto"
 )
 
 const (
@@ -62,14 +63,22 @@
 	Total = "total"
 )
 
-// Metrics is a struct that stores collected metrics during the course
-// of a build which later is dumped to a MetricsBase protobuf file.
-// See ui/metrics/metrics_proto/metrics.proto for further details
-// on what information is collected.
+// Metrics is a struct that stores collected metrics during the course of a
+// build. It is later dumped to protobuf files. See underlying metrics protos
+// for further details on what information is collected.
 type Metrics struct {
-	// The protobuf message that is later written to the file.
+	// Protobuf containing various top-level build metrics. These include:
+	// 1. Build identifiers (ex: branch ID, requested product, hostname,
+	//    originating command)
+	// 2. Per-subprocess top-level metrics (ex: ninja process IO and runtime).
+	//    Note that, since these metrics are reported by soong_ui, there is little
+	//    insight that can be provided into performance breakdowns of individual
+	//    subprocesses.
 	metrics soong_metrics_proto.MetricsBase
 
+	// Protobuf containing metrics pertaining to number of makefiles in a build.
+	mkMetrics mk_metrics_proto.MkMetrics
+
 	// A list of pending build events.
 	EventTracer *EventTracer
 }
@@ -78,11 +87,24 @@
 func New() (metrics *Metrics) {
 	m := &Metrics{
 		metrics:     soong_metrics_proto.MetricsBase{},
+		mkMetrics:   mk_metrics_proto.MkMetrics{},
 		EventTracer: &EventTracer{},
 	}
 	return m
 }
 
+func (m *Metrics) SetTotalMakefiles(total int) {
+	m.mkMetrics.TotalMakefiles = uint32(total)
+}
+
+func (m *Metrics) SetToplevelMakefiles(total int) {
+	m.mkMetrics.ToplevelMakefiles = uint32(total)
+}
+
+func (m *Metrics) DumpMkMetrics(outPath string) {
+	shared.Save(&m.mkMetrics, outPath)
+}
+
 // SetTimeMetrics stores performance information from an executed block of
 // code.
 func (m *Metrics) SetTimeMetrics(perf soong_metrics_proto.PerfInfo) {
diff --git a/ui/metrics/mk_metrics_proto/mk_metrics.pb.go b/ui/metrics/mk_metrics_proto/mk_metrics.pb.go
new file mode 100644
index 0000000..32e136a
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/mk_metrics.pb.go
@@ -0,0 +1,177 @@
+// Copyright 2022 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.
+
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// versions:
+// 	protoc-gen-go v1.27.1
+// 	protoc        v3.9.1
+// source: mk_metrics.proto
+
+package mk_metrics_proto
+
+import (
+	protoreflect "google.golang.org/protobuf/reflect/protoreflect"
+	protoimpl "google.golang.org/protobuf/runtime/protoimpl"
+	reflect "reflect"
+	sync "sync"
+)
+
+const (
+	// Verify that this generated code is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
+	// Verify that runtime/protoimpl is sufficiently up-to-date.
+	_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
+)
+
+// Contains metrics pertaining to makefiles.
+type MkMetrics struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	// Total number of mk files present in the workspace.
+	TotalMakefiles uint32 `protobuf:"varint,1,opt,name=totalMakefiles,proto3" json:"totalMakefiles,omitempty"`
+	// Number of top-level mk files present in the workspace.
+	// A mk file is "top level" if there are no mk files in its parent
+	// direrctories.
+	// This value is equivalent to the number of entries in Android.mk.list.
+	ToplevelMakefiles uint32 `protobuf:"varint,2,opt,name=toplevelMakefiles,proto3" json:"toplevelMakefiles,omitempty"`
+}
+
+func (x *MkMetrics) Reset() {
+	*x = MkMetrics{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_mk_metrics_proto_msgTypes[0]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *MkMetrics) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*MkMetrics) ProtoMessage() {}
+
+func (x *MkMetrics) ProtoReflect() protoreflect.Message {
+	mi := &file_mk_metrics_proto_msgTypes[0]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use MkMetrics.ProtoReflect.Descriptor instead.
+func (*MkMetrics) Descriptor() ([]byte, []int) {
+	return file_mk_metrics_proto_rawDescGZIP(), []int{0}
+}
+
+func (x *MkMetrics) GetTotalMakefiles() uint32 {
+	if x != nil {
+		return x.TotalMakefiles
+	}
+	return 0
+}
+
+func (x *MkMetrics) GetToplevelMakefiles() uint32 {
+	if x != nil {
+		return x.ToplevelMakefiles
+	}
+	return 0
+}
+
+var File_mk_metrics_proto protoreflect.FileDescriptor
+
+var file_mk_metrics_proto_rawDesc = []byte{
+	0x0a, 0x10, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x12, 0x16, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x5f, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f,
+	0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x22, 0x61, 0x0a, 0x09, 0x4d, 0x6b,
+	0x4d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c,
+	0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
+	0x0e, 0x74, 0x6f, 0x74, 0x61, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x12,
+	0x2c, 0x0a, 0x11, 0x74, 0x6f, 0x70, 0x6c, 0x65, 0x76, 0x65, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66,
+	0x69, 0x6c, 0x65, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x11, 0x74, 0x6f, 0x70, 0x6c,
+	0x65, 0x76, 0x65, 0x6c, 0x4d, 0x61, 0x6b, 0x65, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x42, 0x2b, 0x5a,
+	0x29, 0x61, 0x6e, 0x64, 0x72, 0x6f, 0x69, 0x64, 0x2f, 0x73, 0x6f, 0x6f, 0x6e, 0x67, 0x2f, 0x75,
+	0x69, 0x2f, 0x6d, 0x65, 0x74, 0x72, 0x69, 0x63, 0x73, 0x2f, 0x6d, 0x6b, 0x5f, 0x6d, 0x65, 0x74,
+	0x72, 0x69, 0x63, 0x73, 0x5f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
+}
+
+var (
+	file_mk_metrics_proto_rawDescOnce sync.Once
+	file_mk_metrics_proto_rawDescData = file_mk_metrics_proto_rawDesc
+)
+
+func file_mk_metrics_proto_rawDescGZIP() []byte {
+	file_mk_metrics_proto_rawDescOnce.Do(func() {
+		file_mk_metrics_proto_rawDescData = protoimpl.X.CompressGZIP(file_mk_metrics_proto_rawDescData)
+	})
+	return file_mk_metrics_proto_rawDescData
+}
+
+var file_mk_metrics_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
+var file_mk_metrics_proto_goTypes = []interface{}{
+	(*MkMetrics)(nil), // 0: soong_build_mk_metrics.MkMetrics
+}
+var file_mk_metrics_proto_depIdxs = []int32{
+	0, // [0:0] is the sub-list for method output_type
+	0, // [0:0] is the sub-list for method input_type
+	0, // [0:0] is the sub-list for extension type_name
+	0, // [0:0] is the sub-list for extension extendee
+	0, // [0:0] is the sub-list for field type_name
+}
+
+func init() { file_mk_metrics_proto_init() }
+func file_mk_metrics_proto_init() {
+	if File_mk_metrics_proto != nil {
+		return
+	}
+	if !protoimpl.UnsafeEnabled {
+		file_mk_metrics_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*MkMetrics); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+	}
+	type x struct{}
+	out := protoimpl.TypeBuilder{
+		File: protoimpl.DescBuilder{
+			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
+			RawDescriptor: file_mk_metrics_proto_rawDesc,
+			NumEnums:      0,
+			NumMessages:   1,
+			NumExtensions: 0,
+			NumServices:   0,
+		},
+		GoTypes:           file_mk_metrics_proto_goTypes,
+		DependencyIndexes: file_mk_metrics_proto_depIdxs,
+		MessageInfos:      file_mk_metrics_proto_msgTypes,
+	}.Build()
+	File_mk_metrics_proto = out.File
+	file_mk_metrics_proto_rawDesc = nil
+	file_mk_metrics_proto_goTypes = nil
+	file_mk_metrics_proto_depIdxs = nil
+}
diff --git a/ui/metrics/mk_metrics_proto/mk_metrics.proto b/ui/metrics/mk_metrics_proto/mk_metrics.proto
new file mode 100644
index 0000000..df7bca3
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/mk_metrics.proto
@@ -0,0 +1,30 @@
+// Copyright 2022 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.
+
+syntax = "proto3";
+
+package soong_build_mk_metrics;
+option go_package = "android/soong/ui/metrics/mk_metrics_proto";
+
+// Contains metrics pertaining to makefiles.
+message MkMetrics {
+  // Total number of mk files present in the workspace.
+  uint32 totalMakefiles = 1;
+
+  // Number of top-level mk files present in the workspace.
+  // A mk file is "top level" if there are no mk files in its parent
+  // direrctories.
+  // This value is equivalent to the number of entries in Android.mk.list.
+  uint32 toplevelMakefiles = 2;
+}
diff --git a/ui/metrics/mk_metrics_proto/regen.sh b/ui/metrics/mk_metrics_proto/regen.sh
new file mode 100755
index 0000000..64018d4
--- /dev/null
+++ b/ui/metrics/mk_metrics_proto/regen.sh
@@ -0,0 +1,29 @@
+#!/bin/bash -e
+
+# Copyright 2022 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.
+
+# Generates the golang source file of the mk_metrics.proto protobuf file.
+
+function die() { echo "ERROR: $1" >&2; exit 1; }
+
+readonly error_msg="Maybe you need to run 'lunch aosp_arm-eng && m aprotoc blueprint_tools'?"
+
+if ! hash aprotoc &>/dev/null; then
+  die "could not find aprotoc. ${error_msg}"
+fi
+
+if ! aprotoc --go_out=paths=source_relative:. mk_metrics.proto; then
+  die "build failed. ${error_msg}"
+fi