Merge "Move logic to disable flattening in unbundled app mode from Soong to make."
diff --git a/android/arch.go b/android/arch.go
index 10c827b..9ff439c 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -897,7 +897,7 @@
 
 			// Add the OS/Arch combinations, e.g. "android_arm64".
 			for _, archType := range osArchTypeMap[os] {
-				targets = append(targets, os.Field+"_"+archType.Name)
+				targets = append(targets, GetCompoundTargetName(os, archType))
 
 				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
 				if os.Linux() {
@@ -1217,6 +1217,10 @@
 	return getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
 }
 
+func GetCompoundTargetName(os OsType, arch ArchType) string {
+	return os.Field + "_" + arch.Name
+}
+
 // Returns the structs corresponding to the properties specific to the given
 // architecture and OS in archProperties.
 func getArchProperties(ctx BaseMutatorContext, archProperties interface{}, arch Arch, os OsType, nativeBridgeEnabled bool) []reflect.Value {
@@ -1323,7 +1327,7 @@
 		//         key: value,
 		//     },
 		// },
-		field := os.Field + "_" + archType.Name
+		field := GetCompoundTargetName(os, archType)
 		userFriendlyField := "target." + os.Name + "_" + archType.Name
 		if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
 			result = append(result, osArchProperties)
@@ -1950,19 +1954,47 @@
 	return archToProp
 }
 
-// Returns the struct containing the properties specific to the given
-// architecture type. These look like this in Blueprint files:
-// target: {
-//     android: {
-//         key: value,
-//     },
-// },
-// This struct will also contain sub-structs containing to the architecture/CPU
-// variants and features that themselves contain properties specific to those.
-func getTargetStruct(ctx ArchVariantContext, archProperties interface{}, os OsType) (reflect.Value, bool) {
-	archPropValues := reflect.ValueOf(archProperties).Elem()
-	targetProp := archPropValues.FieldByName("Target").Elem()
-	return getChildPropertyStruct(ctx, targetProp, os.Field, os.Field)
+// Returns a struct matching the propertySet interface, containing properties specific to the targetName
+// For example, given these arguments:
+//    propertySet = BaseCompilerProperties
+//    targetName = "android_arm"
+// And given this Android.bp fragment:
+//    target:
+//       android_arm: {
+//          srcs: ["foo.c"],
+//       }
+//       android_arm64: {
+//          srcs: ["bar.c"],
+//      }
+//    }
+// This would return a BaseCompilerProperties with BaseCompilerProperties.Srcs = ["foo.c"]
+func getTargetStruct(ctx ArchVariantContext, propertySet interface{}, archProperties []interface{}, targetName string) interface{} {
+	propertyStructs := make([]reflect.Value, 0)
+	for _, archProperty := range archProperties {
+		archPropValues := reflect.ValueOf(archProperty).Elem()
+		targetProp := archPropValues.FieldByName("Target").Elem()
+		targetStruct, ok := getChildPropertyStruct(ctx, targetProp, targetName, targetName)
+		if ok {
+			propertyStructs = append(propertyStructs, targetStruct)
+		}
+	}
+
+	// Create a new instance of the requested property set
+	value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
+
+	// Merge all the structs together
+	for _, propertyStruct := range propertyStructs {
+		mergePropertyStruct(ctx, value, propertyStruct)
+	}
+
+	return value
+}
+
+// Properties corresponds to e.g. Target: android: {...}
+// ArchProperties corresponds to e.g. Target: android_arm: {...}, android_arm64: {...}, ...
+type TargetProperties struct {
+	Properties     interface{}
+	ArchProperties map[ArchType]interface{}
 }
 
 // GetTargetProperties returns a map of OS target (e.g. android, windows) to the
@@ -1974,13 +2006,15 @@
 // the os-specific property value specified by the module if defined.
 //
 // Implemented in a way very similar to GetArchProperties().
-func (m *ModuleBase) GetTargetProperties(ctx ArchVariantContext, propertySet interface{}) map[OsType]interface{} {
-	// Return value of the arch types to the prop values for that arch.
-	osToProp := map[OsType]interface{}{}
+//
+// NOTE: "Target" == OS
+func (m *ModuleBase) GetTargetProperties(ctx ArchVariantContext, propertySet interface{}) map[OsType]TargetProperties {
+	// Return value of the target types to the prop values for that target.
+	targetToProp := map[OsType]TargetProperties{}
 
-	// Nothing to do for non-OS/arch-specific modules.
+	// Nothing to do for non-target-specific modules.
 	if !m.ArchSpecific() {
-		return osToProp
+		return targetToProp
 	}
 
 	dstType := reflect.ValueOf(propertySet).Type()
@@ -1998,33 +2032,26 @@
 
 	if archProperties == nil {
 		// This module does not have the property set requested
-		return osToProp
+		return targetToProp
 	}
 
+	// For android, linux, ...
 	for _, os := range osTypeList {
 		if os == CommonOS {
 			// It looks like this OS value is not used in Blueprint files
 			continue
 		}
-
-		propertyStructs := make([]reflect.Value, 0)
-		for _, archProperty := range archProperties {
-			targetStruct, ok := getTargetStruct(ctx, archProperty, os)
-			if ok {
-				propertyStructs = append(propertyStructs, targetStruct)
-			}
+		targetProperties := TargetProperties{
+			Properties:     getTargetStruct(ctx, propertySet, archProperties, os.Field),
+			ArchProperties: make(map[ArchType]interface{}),
 		}
-
-		// Create a new instance of the requested property set
-		value := reflect.New(reflect.ValueOf(propertySet).Elem().Type()).Interface()
-
-		// Merge all the structs together
-		for _, propertyStruct := range propertyStructs {
-			mergePropertyStruct(ctx, value, propertyStruct)
+		// For arm, x86, ...
+		for _, arch := range osArchTypeMap[os] {
+			targetName := GetCompoundTargetName(os, arch)
+			targetProperties.ArchProperties[arch] = getTargetStruct(ctx, propertySet, archProperties, targetName)
 		}
-
-		osToProp[os] = value
+		targetToProp[os] = targetProperties
 	}
 
-	return osToProp
+	return targetToProp
 }
diff --git a/android/bazel.go b/android/bazel.go
index f56c24e..ef770bf 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -138,7 +138,6 @@
 		// e.g. ERROR: Analysis of target '@soong_injection//mixed_builds:buildroot' failed
 		"external/bazelbuild-rules_android":/* recursive = */ true,
 
-		"prebuilts/clang/host/linux-x86":/* recursive = */ false,
 		"prebuilts/sdk":/* recursive = */ false,
 		"prebuilts/sdk/tools":/* recursive = */ false,
 	}
@@ -155,6 +154,7 @@
 		"external/fmtlib":                 Bp2BuildDefaultTrueRecursively,
 		"external/arm-optimized-routines": Bp2BuildDefaultTrueRecursively,
 		"external/scudo":                  Bp2BuildDefaultTrueRecursively,
+		"prebuilts/clang/host/linux-x86":  Bp2BuildDefaultTrueRecursively,
 	}
 
 	// Per-module denylist to always opt modules out of both bp2build and mixed builds.
diff --git a/android/variable.go b/android/variable.go
index 672576a..7658cdd 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -467,7 +467,7 @@
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the module in the given context.
-func ProductVariableProperties(ctx ProductConfigContext) ProductConfigProperties {
+func ProductVariableProperties(ctx BaseMutatorContext) ProductConfigProperties {
 	module := ctx.Module()
 	moduleBase := module.base()
 
@@ -477,7 +477,31 @@
 		return productConfigProperties
 	}
 
-	variableValues := reflect.ValueOf(moduleBase.variableProperties).Elem().FieldByName("Product_variables")
+	productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)
+
+	for arch, targetProps := range moduleBase.GetArchProperties(ctx, moduleBase.variableProperties) {
+		// GetArchProperties is creating an instance of the requested type
+		// and productVariablesValues expects an interface, so no need to cast
+		productVariableValues(targetProps, arch.Name, &productConfigProperties)
+	}
+
+	for os, targetProps := range moduleBase.GetTargetProperties(ctx, moduleBase.variableProperties) {
+		// GetTargetProperties is creating an instance of the requested type
+		// and productVariablesValues expects an interface, so no need to cast
+		productVariableValues(targetProps.Properties, os.Name, &productConfigProperties)
+		for arch, archProperties := range targetProps.ArchProperties {
+			productVariableValues(archProperties, os.Name+"_"+arch.Name, &productConfigProperties)
+		}
+	}
+
+	return productConfigProperties
+}
+
+func productVariableValues(variableProps interface{}, suffix string, productConfigProperties *ProductConfigProperties) {
+	if suffix != "" {
+		suffix = "-" + suffix
+	}
+	variableValues := reflect.ValueOf(variableProps).Elem().FieldByName("Product_variables")
 	for i := 0; i < variableValues.NumField(); i++ {
 		variableValue := variableValues.Field(i)
 		// Check if any properties were set for the module
@@ -495,15 +519,13 @@
 
 			// e.g. Asflags, Cflags, Enabled, etc.
 			propertyName := variableValue.Type().Field(j).Name
-			productConfigProperties[propertyName] = append(productConfigProperties[propertyName],
+			(*productConfigProperties)[propertyName] = append((*productConfigProperties)[propertyName],
 				ProductConfigProperty{
-					ProductConfigVariable: productVariableName,
+					ProductConfigVariable: productVariableName + suffix,
 					Property:              property.Interface(),
 				})
 		}
 	}
-
-	return productConfigProperties
 }
 
 func VariableMutator(mctx BottomUpMutatorContext) {
diff --git a/apex/bootclasspath_fragment_test.go b/apex/bootclasspath_fragment_test.go
index 84cf9c4..7aecff6 100644
--- a/apex/bootclasspath_fragment_test.go
+++ b/apex/bootclasspath_fragment_test.go
@@ -382,7 +382,7 @@
 		).RunTest(t)
 
 		ensureExactContents(t, result.TestContext, "com.android.art", "android_common_com.android.art_image", []string{
-			"etc/classpaths/mybootclasspathfragment.pb",
+			"etc/classpaths/bootclasspath.pb",
 			"javalib/arm/boot.art",
 			"javalib/arm/boot.oat",
 			"javalib/arm/boot.vdex",
@@ -594,7 +594,7 @@
 	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
 		// This does not include art, oat or vdex files as they are only included for the art boot
 		// image.
-		"etc/classpaths/mybootclasspathfragment.pb",
+		"etc/classpaths/bootclasspath.pb",
 		"javalib/bar.jar",
 		"javalib/foo.jar",
 	})
diff --git a/apex/systemserver_classpath_fragment_test.go b/apex/systemserver_classpath_fragment_test.go
index e1a101a..95b6e23 100644
--- a/apex/systemserver_classpath_fragment_test.go
+++ b/apex/systemserver_classpath_fragment_test.go
@@ -67,7 +67,7 @@
 	`)
 
 	ensureExactContents(t, result.TestContext, "myapex", "android_common_myapex_image", []string{
-		"etc/classpaths/mysystemserverclasspathfragment.pb",
+		"etc/classpaths/systemserverclasspath.pb",
 		"javalib/foo.jar",
 	})
 
diff --git a/bazel/properties.go b/bazel/properties.go
index 640275f..8adc9d0 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -137,6 +137,63 @@
 	return strings
 }
 
+// Return all needles in a given haystack, where needleFn is true for needles.
+func FilterLabelList(haystack LabelList, needleFn func(string) bool) LabelList {
+	var includes []Label
+
+	for _, inc := range haystack.Includes {
+		if needleFn(inc.Label) {
+			includes = append(includes, inc)
+		}
+	}
+	return LabelList{Includes: includes, Excludes: haystack.Excludes}
+}
+
+// Return all needles in a given haystack, where needleFn is true for needles.
+func FilterLabelListAttribute(haystack LabelListAttribute, needleFn func(string) bool) LabelListAttribute {
+	var result LabelListAttribute
+
+	result.Value = FilterLabelList(haystack.Value, needleFn)
+
+	for arch := range PlatformArchMap {
+		result.SetValueForArch(arch, FilterLabelList(haystack.GetValueForArch(arch), needleFn))
+	}
+
+	for os := range PlatformOsMap {
+		result.SetOsValueForTarget(os, FilterLabelList(haystack.GetOsValueForTarget(os), needleFn))
+
+		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
+		for _, arch := range AllArches {
+			result.SetOsArchValueForTarget(os, arch, FilterLabelList(haystack.GetOsArchValueForTarget(os, arch), needleFn))
+		}
+	}
+
+	return result
+}
+
+// Subtract needle from haystack
+func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
+	var result LabelListAttribute
+
+	for arch := range PlatformArchMap {
+		result.SetValueForArch(arch,
+			SubtractBazelLabelList(haystack.GetValueForArch(arch), needle.GetValueForArch(arch)))
+	}
+
+	for os := range PlatformOsMap {
+		result.SetOsValueForTarget(os, SubtractBazelLabelList(haystack.GetOsValueForTarget(os), needle.GetOsValueForTarget(os)))
+
+		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
+		for _, arch := range AllArches {
+			result.SetOsArchValueForTarget(os, arch, SubtractBazelLabelList(haystack.GetOsArchValueForTarget(os, arch), needle.GetOsArchValueForTarget(os, arch)))
+		}
+	}
+
+	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
+
+	return result
+}
+
 // Subtract needle from haystack
 func SubtractBazelLabels(haystack []Label, needle []Label) []Label {
 	// This is really a set
@@ -193,6 +250,21 @@
 	OS_LINUX_BIONIC = "linux_bionic"
 	OS_WINDOWS      = "windows"
 
+	// Targets in arch.go
+	TARGET_ANDROID_ARM         = "android_arm"
+	TARGET_ANDROID_ARM64       = "android_arm64"
+	TARGET_ANDROID_X86         = "android_x86"
+	TARGET_ANDROID_X86_64      = "android_x86_64"
+	TARGET_DARWIN_X86_64       = "darwin_x86_64"
+	TARGET_FUCHSIA_ARM64       = "fuchsia_arm64"
+	TARGET_FUCHSIA_X86_64      = "fuchsia_x86_64"
+	TARGET_LINUX_X86           = "linux_glibc_x86"
+	TARGET_LINUX_x86_64        = "linux_glibc_x86_64"
+	TARGET_LINUX_BIONIC_ARM64  = "linux_bionic_arm64"
+	TARGET_LINUX_BIONIC_X86_64 = "linux_bionic_x86_64"
+	TARGET_WINDOWS_X86         = "windows_x86"
+	TARGET_WINDOWS_X86_64      = "windows_x86_64"
+
 	// This is the string representation of the default condition wherever a
 	// configurable attribute is used in a select statement, i.e.
 	// //conditions:default for Bazel.
@@ -234,6 +306,26 @@
 		OS_WINDOWS:         "//build/bazel/platforms/os:windows",
 		CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of an os select map.
 	}
+
+	PlatformTargetMap = map[string]string{
+		TARGET_ANDROID_ARM:         "//build/bazel/platforms:android_arm",
+		TARGET_ANDROID_ARM64:       "//build/bazel/platforms:android_arm64",
+		TARGET_ANDROID_X86:         "//build/bazel/platforms:android_x86",
+		TARGET_ANDROID_X86_64:      "//build/bazel/platforms:android_x86_64",
+		TARGET_DARWIN_X86_64:       "//build/bazel/platforms:darwin_x86_64",
+		TARGET_FUCHSIA_ARM64:       "//build/bazel/platforms:fuchsia_arm64",
+		TARGET_FUCHSIA_X86_64:      "//build/bazel/platforms:fuchsia_x86_64",
+		TARGET_LINUX_X86:           "//build/bazel/platforms:linux_glibc_x86",
+		TARGET_LINUX_x86_64:        "//build/bazel/platforms:linux_glibc_x86_64",
+		TARGET_LINUX_BIONIC_ARM64:  "//build/bazel/platforms:linux_bionic_arm64",
+		TARGET_LINUX_BIONIC_X86_64: "//build/bazel/platforms:linux_bionic_x86_64",
+		TARGET_WINDOWS_X86:         "//build/bazel/platforms:windows_x86",
+		TARGET_WINDOWS_X86_64:      "//build/bazel/platforms:windows_x86_64",
+		CONDITIONS_DEFAULT:         ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+
+	// TODO(b/187530594): Should we add CONDITIONS_DEFAULT here?
+	AllArches = []string{ARCH_ARM, ARCH_ARM64, ARCH_X86, ARCH_X86_64}
 )
 
 type Attribute interface {
@@ -297,15 +389,32 @@
 	ConditionsDefault LabelList
 }
 
-type labelListOsValues struct {
-	Android     LabelList
-	Darwin      LabelList
-	Fuchsia     LabelList
-	Linux       LabelList
-	LinuxBionic LabelList
-	Windows     LabelList
+type labelListTargetValue struct {
+	// E.g. for android
+	OsValue LabelList
 
-	ConditionsDefault LabelList
+	// E.g. for android_arm, android_arm64, ...
+	ArchValues labelListArchValues
+}
+
+func (target *labelListTargetValue) Append(other labelListTargetValue) {
+	target.OsValue.Append(other.OsValue)
+	target.ArchValues.X86.Append(other.ArchValues.X86)
+	target.ArchValues.X86_64.Append(other.ArchValues.X86_64)
+	target.ArchValues.Arm.Append(other.ArchValues.Arm)
+	target.ArchValues.Arm64.Append(other.ArchValues.Arm64)
+	target.ArchValues.ConditionsDefault.Append(other.ArchValues.ConditionsDefault)
+}
+
+type labelListTargetValues struct {
+	Android     labelListTargetValue
+	Darwin      labelListTargetValue
+	Fuchsia     labelListTargetValue
+	Linux       labelListTargetValue
+	LinuxBionic labelListTargetValue
+	Windows     labelListTargetValue
+
+	ConditionsDefault labelListTargetValue
 }
 
 // LabelListAttribute is used to represent a list of Bazel labels as an
@@ -322,7 +431,7 @@
 	// The os-specific attribute label list values. Optional. If used, these
 	// are generated in a select statement and appended to the non-os specific
 	// label list Value.
-	OsValues labelListOsValues
+	TargetValues labelListTargetValues
 }
 
 // MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
@@ -341,10 +450,10 @@
 	}
 
 	for os := range PlatformOsMap {
-		this := attrs.GetValueForOS(os)
-		that := other.GetValueForOS(os)
+		this := attrs.getValueForTarget(os)
+		that := other.getValueForTarget(os)
 		this.Append(that)
-		attrs.SetValueForOS(os, this)
+		attrs.setValueForTarget(os, this)
 	}
 
 	attrs.Value.Append(other.Value)
@@ -360,9 +469,15 @@
 	}
 
 	for os := range PlatformOsMap {
-		if len(attrs.GetValueForOS(os).Includes) > 0 {
+		if len(attrs.GetOsValueForTarget(os).Includes) > 0 {
 			return true
 		}
+		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT (not in AllArches)
+		for _, arch := range AllArches {
+			if len(attrs.GetOsArchValueForTarget(os, arch).Includes) > 0 {
+				return true
+			}
+		}
 	}
 	return false
 }
@@ -395,36 +510,92 @@
 	*v = value
 }
 
-func (attrs *LabelListAttribute) osValuePtrs() map[string]*LabelList {
-	return map[string]*LabelList{
-		OS_ANDROID:         &attrs.OsValues.Android,
-		OS_DARWIN:          &attrs.OsValues.Darwin,
-		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
-		OS_LINUX:           &attrs.OsValues.Linux,
-		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
-		OS_WINDOWS:         &attrs.OsValues.Windows,
-		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
+func (attrs *LabelListAttribute) targetValuePtrs() map[string]*labelListTargetValue {
+	return map[string]*labelListTargetValue{
+		OS_ANDROID:         &attrs.TargetValues.Android,
+		OS_DARWIN:          &attrs.TargetValues.Darwin,
+		OS_FUCHSIA:         &attrs.TargetValues.Fuchsia,
+		OS_LINUX:           &attrs.TargetValues.Linux,
+		OS_LINUX_BIONIC:    &attrs.TargetValues.LinuxBionic,
+		OS_WINDOWS:         &attrs.TargetValues.Windows,
+		CONDITIONS_DEFAULT: &attrs.TargetValues.ConditionsDefault,
 	}
 }
 
-// GetValueForOS returns the label_list attribute value for an OS target.
-func (attrs *LabelListAttribute) GetValueForOS(os string) LabelList {
-	var v *LabelList
-	if v = attrs.osValuePtrs()[os]; v == nil {
+func (attrs *LabelListAttribute) getValueForTarget(os string) labelListTargetValue {
+	var v *labelListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
 		panic(fmt.Errorf("Unknown os: %s", os))
 	}
 	return *v
 }
 
-// SetValueForArch sets the label_list attribute value for an OS target.
-func (attrs *LabelListAttribute) SetValueForOS(os string, value LabelList) {
-	var v *LabelList
-	if v = attrs.osValuePtrs()[os]; v == nil {
+func (attrs *LabelListAttribute) GetOsValueForTarget(os string) LabelList {
+	var v *labelListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	return v.OsValue
+}
+
+func (attrs *LabelListAttribute) GetOsArchValueForTarget(os string, arch string) LabelList {
+	var v *labelListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	switch arch {
+	case ARCH_X86:
+		return v.ArchValues.X86
+	case ARCH_X86_64:
+		return v.ArchValues.X86_64
+	case ARCH_ARM:
+		return v.ArchValues.Arm
+	case ARCH_ARM64:
+		return v.ArchValues.Arm64
+	case CONDITIONS_DEFAULT:
+		return v.ArchValues.ConditionsDefault
+	default:
+		panic(fmt.Errorf("Unknown arch: %s\n", arch))
+	}
+}
+
+func (attrs *LabelListAttribute) setValueForTarget(os string, value labelListTargetValue) {
+	var v *labelListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
 		panic(fmt.Errorf("Unknown os: %s", os))
 	}
 	*v = value
 }
 
+func (attrs *LabelListAttribute) SetOsValueForTarget(os string, value LabelList) {
+	var v *labelListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	v.OsValue = value
+}
+
+func (attrs *LabelListAttribute) SetOsArchValueForTarget(os string, arch string, value LabelList) {
+	var v *labelListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	switch arch {
+	case ARCH_X86:
+		v.ArchValues.X86 = value
+	case ARCH_X86_64:
+		v.ArchValues.X86_64 = value
+	case ARCH_ARM:
+		v.ArchValues.Arm = value
+	case ARCH_ARM64:
+		v.ArchValues.Arm64 = value
+	case CONDITIONS_DEFAULT:
+		v.ArchValues.ConditionsDefault = value
+	default:
+		panic(fmt.Errorf("Unknown arch: %s\n", arch))
+	}
+}
+
 // StringListAttribute corresponds to the string_list Bazel attribute type with
 // support for additional metadata, like configurations.
 type StringListAttribute struct {
@@ -439,7 +610,7 @@
 	// The os-specific attribute string list values. Optional. If used, these
 	// are generated in a select statement and appended to the non-os specific
 	// label list Value.
-	OsValues stringListOsValues
+	TargetValues stringListTargetValues
 
 	// list of product-variable string list values. Optional. if used, each will generate a select
 	// statement appended to the label list Value.
@@ -464,15 +635,32 @@
 	ConditionsDefault []string
 }
 
-type stringListOsValues struct {
-	Android     []string
-	Darwin      []string
-	Fuchsia     []string
-	Linux       []string
-	LinuxBionic []string
-	Windows     []string
+type stringListTargetValue struct {
+	// E.g. for android
+	OsValue []string
 
-	ConditionsDefault []string
+	// E.g. for android_arm, android_arm64, ...
+	ArchValues stringListArchValues
+}
+
+func (target *stringListTargetValue) Append(other stringListTargetValue) {
+	target.OsValue = append(target.OsValue, other.OsValue...)
+	target.ArchValues.X86 = append(target.ArchValues.X86, other.ArchValues.X86...)
+	target.ArchValues.X86_64 = append(target.ArchValues.X86_64, other.ArchValues.X86_64...)
+	target.ArchValues.Arm = append(target.ArchValues.Arm, other.ArchValues.Arm...)
+	target.ArchValues.Arm64 = append(target.ArchValues.Arm64, other.ArchValues.Arm64...)
+	target.ArchValues.ConditionsDefault = append(target.ArchValues.ConditionsDefault, other.ArchValues.ConditionsDefault...)
+}
+
+type stringListTargetValues struct {
+	Android     stringListTargetValue
+	Darwin      stringListTargetValue
+	Fuchsia     stringListTargetValue
+	Linux       stringListTargetValue
+	LinuxBionic stringListTargetValue
+	Windows     stringListTargetValue
+
+	ConditionsDefault stringListTargetValue
 }
 
 // Product Variable values for StringListAttribute
@@ -497,9 +685,16 @@
 	}
 
 	for os := range PlatformOsMap {
-		if len(attrs.GetValueForOS(os)) > 0 {
+		if len(attrs.GetOsValueForTarget(os)) > 0 {
 			return true
 		}
+		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT? (Not in AllArches)
+		for _, arch := range AllArches {
+			if len(attrs.GetOsArchValueForTarget(os, arch)) > 0 {
+				return true
+			}
+
+		}
 	}
 
 	return len(attrs.ProductValues) > 0
@@ -533,36 +728,98 @@
 	*v = value
 }
 
-func (attrs *StringListAttribute) osValuePtrs() map[string]*[]string {
-	return map[string]*[]string{
-		OS_ANDROID:         &attrs.OsValues.Android,
-		OS_DARWIN:          &attrs.OsValues.Darwin,
-		OS_FUCHSIA:         &attrs.OsValues.Fuchsia,
-		OS_LINUX:           &attrs.OsValues.Linux,
-		OS_LINUX_BIONIC:    &attrs.OsValues.LinuxBionic,
-		OS_WINDOWS:         &attrs.OsValues.Windows,
-		CONDITIONS_DEFAULT: &attrs.OsValues.ConditionsDefault,
+func (attrs *StringListAttribute) targetValuePtrs() map[string]*stringListTargetValue {
+	return map[string]*stringListTargetValue{
+		OS_ANDROID:         &attrs.TargetValues.Android,
+		OS_DARWIN:          &attrs.TargetValues.Darwin,
+		OS_FUCHSIA:         &attrs.TargetValues.Fuchsia,
+		OS_LINUX:           &attrs.TargetValues.Linux,
+		OS_LINUX_BIONIC:    &attrs.TargetValues.LinuxBionic,
+		OS_WINDOWS:         &attrs.TargetValues.Windows,
+		CONDITIONS_DEFAULT: &attrs.TargetValues.ConditionsDefault,
 	}
 }
 
-// GetValueForOS returns the string_list attribute value for an OS target.
-func (attrs *StringListAttribute) GetValueForOS(os string) []string {
-	var v *[]string
-	if v = attrs.osValuePtrs()[os]; v == nil {
+func (attrs *StringListAttribute) getValueForTarget(os string) stringListTargetValue {
+	var v *stringListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
 		panic(fmt.Errorf("Unknown os: %s", os))
 	}
 	return *v
 }
 
-// SetValueForArch sets the string_list attribute value for an OS target.
-func (attrs *StringListAttribute) SetValueForOS(os string, value []string) {
-	var v *[]string
-	if v = attrs.osValuePtrs()[os]; v == nil {
+func (attrs *StringListAttribute) GetOsValueForTarget(os string) []string {
+	var v *stringListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	return v.OsValue
+}
+
+func (attrs *StringListAttribute) GetOsArchValueForTarget(os string, arch string) []string {
+	var v *stringListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	switch arch {
+	case ARCH_X86:
+		return v.ArchValues.X86
+	case ARCH_X86_64:
+		return v.ArchValues.X86_64
+	case ARCH_ARM:
+		return v.ArchValues.Arm
+	case ARCH_ARM64:
+		return v.ArchValues.Arm64
+	case CONDITIONS_DEFAULT:
+		return v.ArchValues.ConditionsDefault
+	default:
+		panic(fmt.Errorf("Unknown arch: %s\n", arch))
+	}
+}
+
+func (attrs *StringListAttribute) setValueForTarget(os string, value stringListTargetValue) {
+	var v *stringListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
 		panic(fmt.Errorf("Unknown os: %s", os))
 	}
 	*v = value
 }
 
+func (attrs *StringListAttribute) SortedProductVariables() []ProductVariableValues {
+	vals := attrs.ProductValues[:]
+	sort.Slice(vals, func(i, j int) bool { return vals[i].ProductVariable < vals[j].ProductVariable })
+	return vals
+}
+
+func (attrs *StringListAttribute) SetOsValueForTarget(os string, value []string) {
+	var v *stringListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	v.OsValue = value
+}
+
+func (attrs *StringListAttribute) SetOsArchValueForTarget(os string, arch string, value []string) {
+	var v *stringListTargetValue
+	if v = attrs.targetValuePtrs()[os]; v == nil {
+		panic(fmt.Errorf("Unknown os: %s", os))
+	}
+	switch arch {
+	case ARCH_X86:
+		v.ArchValues.X86 = value
+	case ARCH_X86_64:
+		v.ArchValues.X86_64 = value
+	case ARCH_ARM:
+		v.ArchValues.Arm = value
+	case ARCH_ARM64:
+		v.ArchValues.Arm64 = value
+	case CONDITIONS_DEFAULT:
+		v.ArchValues.ConditionsDefault = value
+	default:
+		panic(fmt.Errorf("Unknown arch: %s\n", arch))
+	}
+}
+
 // Append appends all values, including os and arch specific ones, from another
 // StringListAttribute to this StringListAttribute
 func (attrs *StringListAttribute) Append(other StringListAttribute) {
@@ -574,10 +831,10 @@
 	}
 
 	for os := range PlatformOsMap {
-		this := attrs.GetValueForOS(os)
-		that := other.GetValueForOS(os)
-		this = append(this, that...)
-		attrs.SetValueForOS(os, this)
+		this := attrs.getValueForTarget(os)
+		that := other.getValueForTarget(os)
+		this.Append(that)
+		attrs.setValueForTarget(os, this)
 	}
 
 	productValues := make(map[string][]string, 0)
diff --git a/bp2build/build_conversion.go b/bp2build/build_conversion.go
index bddc524..388c8cf 100644
--- a/bp2build/build_conversion.go
+++ b/bp2build/build_conversion.go
@@ -19,6 +19,7 @@
 	"android/soong/bazel"
 	"fmt"
 	"reflect"
+	"sort"
 	"strings"
 
 	"github.com/google/blueprint"
@@ -34,6 +35,7 @@
 	content         string
 	ruleClass       string
 	bzlLoadLocation string
+	handcrafted     bool
 }
 
 // IsLoadedFromStarlark determines if the BazelTarget's rule class is loaded from a .bzl file,
@@ -45,12 +47,47 @@
 // BazelTargets is a typedef for a slice of BazelTarget objects.
 type BazelTargets []BazelTarget
 
+// HasHandcraftedTargetsreturns true if a set of bazel targets contain
+// handcrafted ones.
+func (targets BazelTargets) hasHandcraftedTargets() bool {
+	for _, target := range targets {
+		if target.handcrafted {
+			return true
+		}
+	}
+	return false
+}
+
+// sort a list of BazelTargets in-place, by name, and by generated/handcrafted types.
+func (targets BazelTargets) sort() {
+	sort.Slice(targets, func(i, j int) bool {
+		if targets[i].handcrafted != targets[j].handcrafted {
+			// Handcrafted targets will be generated after the bp2build generated targets.
+			return targets[j].handcrafted
+		}
+		// This will cover all bp2build generated targets.
+		return targets[i].name < targets[j].name
+	})
+}
+
 // String returns the string representation of BazelTargets, without load
 // statements (use LoadStatements for that), since the targets are usually not
 // adjacent to the load statements at the top of the BUILD file.
 func (targets BazelTargets) String() string {
 	var res string
 	for i, target := range targets {
+		// There is only at most 1 handcrafted "target", because its contents
+		// represent the entire BUILD file content from the tree. See
+		// build_conversion.go#getHandcraftedBuildContent for more information.
+		//
+		// Add a header to make it easy to debug where the handcrafted targets
+		// are in a generated BUILD file.
+		if target.handcrafted {
+			res += "# -----------------------------\n"
+			res += "# Section: Handcrafted targets. \n"
+			res += "# -----------------------------\n\n"
+		}
+
 		res += target.content
 		if i != len(targets)-1 {
 			res += "\n\n"
@@ -267,7 +304,8 @@
 	}
 	// TODO(b/181575318): once this is more targeted, we need to include name, rule class, etc
 	return BazelTarget{
-		content: c,
+		content:     c,
+		handcrafted: true,
 	}, nil
 }
 
@@ -294,6 +332,7 @@
 			targetName,
 			attributes,
 		),
+		handcrafted: false,
 	}
 }
 
@@ -529,6 +568,9 @@
 			return true
 		}
 	default:
+		if !value.IsValid() {
+			return true
+		}
 		zeroValue := reflect.Zero(value.Type())
 		result := value.Interface() == zeroValue.Interface()
 		return result
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index 71660a8..b1c342c 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1452,53 +1452,61 @@
 
 	dir := "."
 	for _, testCase := range testCases {
-		fs := make(map[string][]byte)
-		toParse := []string{
-			"Android.bp",
-		}
-		for f, content := range testCase.fs {
-			if strings.HasSuffix(f, "Android.bp") {
-				toParse = append(toParse, f)
+		t.Run(testCase.description, func(t *testing.T) {
+			fs := make(map[string][]byte)
+			toParse := []string{
+				"Android.bp",
 			}
-			fs[f] = []byte(content)
-		}
-		config := android.TestConfig(buildDir, nil, testCase.bp, fs)
-		ctx := android.NewTestContext(config)
-		ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
-		for _, m := range testCase.depsMutators {
-			ctx.DepsBp2BuildMutators(m)
-		}
-		ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
-		ctx.RegisterForBazelConversion()
+			for f, content := range testCase.fs {
+				if strings.HasSuffix(f, "Android.bp") {
+					toParse = append(toParse, f)
+				}
+				fs[f] = []byte(content)
+			}
+			config := android.TestConfig(buildDir, nil, testCase.bp, fs)
+			ctx := android.NewTestContext(config)
+			ctx.RegisterModuleType(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestFactory)
+			for _, m := range testCase.depsMutators {
+				ctx.DepsBp2BuildMutators(m)
+			}
+			ctx.RegisterBp2BuildMutator(testCase.moduleTypeUnderTest, testCase.moduleTypeUnderTestBp2BuildMutator)
+			ctx.RegisterForBazelConversion()
 
-		_, errs := ctx.ParseFileList(dir, toParse)
-		if errored(t, testCase.description, errs) {
-			continue
-		}
-		_, errs = ctx.ResolveDependencies(config)
-		if errored(t, testCase.description, errs) {
-			continue
-		}
+			_, errs := ctx.ParseFileList(dir, toParse)
+			if errored(t, testCase.description, errs) {
+				return
+			}
+			_, errs = ctx.ResolveDependencies(config)
+			if errored(t, testCase.description, errs) {
+				return
+			}
 
-		checkDir := dir
-		if testCase.dir != "" {
-			checkDir = testCase.dir
-		}
-		bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
-		if actualCount, expectedCount := len(bazelTargets), len(testCase.expectedBazelTargets); actualCount != expectedCount {
-			t.Errorf("%s: Expected %d bazel target, got %d\n%s", testCase.description, expectedCount, actualCount, bazelTargets)
-		} else {
+			checkDir := dir
+			if testCase.dir != "" {
+				checkDir = testCase.dir
+			}
+			bazelTargets := generateBazelTargetsForDir(NewCodegenContext(config, *ctx.Context, Bp2Build), checkDir)
+			bazelTargets.sort()
+			actualCount := len(bazelTargets)
+			expectedCount := len(testCase.expectedBazelTargets)
+			if actualCount != expectedCount {
+				t.Errorf("Expected %d bazel target, got %d\n%s", expectedCount, actualCount, bazelTargets)
+			}
+			if !strings.Contains(bazelTargets.String(), "Section: Handcrafted targets. ") {
+				t.Errorf("Expected string representation of bazelTargets to contain handcrafted section header.")
+			}
 			for i, target := range bazelTargets {
-				if w, g := testCase.expectedBazelTargets[i], target.content; w != g {
+				actualContent := target.content
+				expectedContent := testCase.expectedBazelTargets[i]
+				if expectedContent != actualContent {
 					t.Errorf(
-						"%s: Expected generated Bazel target to be '%s', got '%s'",
-						testCase.description,
-						w,
-						g,
+						"Expected generated Bazel target to be '%s', got '%s'",
+						expectedContent,
+						actualContent,
 					)
 				}
 			}
-		}
+		})
 	}
 }
 
diff --git a/bp2build/cc_library_conversion_test.go b/bp2build/cc_library_conversion_test.go
index 0a72937..6aa148e 100644
--- a/bp2build/cc_library_conversion_test.go
+++ b/bp2build/cc_library_conversion_test.go
@@ -41,6 +41,7 @@
 )
 
 func runCcLibraryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
 	runBp2BuildTestCase(t, registerCcLibraryModuleTypes, tc)
 }
 
@@ -52,6 +53,7 @@
 }
 
 func runBp2BuildTestCase(t *testing.T, registerModuleTypes func(ctx android.RegistrationContext), tc bp2buildTestCase) {
+	t.Helper()
 	dir := "."
 	filesystem := make(map[string][]byte)
 	toParse := []string{
@@ -311,7 +313,7 @@
         "//build/bazel/platforms/arch:arm64": ["-DHAVE_FAST_FMA=1"],
         "//conditions:default": [],
     }),
-    srcs = ["math/cosf.c"],
+    srcs_c = ["math/cosf.c"],
 )`},
 	})
 }
@@ -398,6 +400,141 @@
 	})
 }
 
+func TestCcLibrarySharedStaticPropsInArch(t *testing.T) {
+	runCcLibraryTestCase(t, bp2buildTestCase{
+		description:                        "cc_library shared/static props in arch",
+		moduleTypeUnderTest:                "cc_library",
+		moduleTypeUnderTestFactory:         cc.LibraryFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
+		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+		dir:                                "foo/bar",
+		filesystem: map[string]string{
+			"foo/bar/arm.cpp":        "",
+			"foo/bar/x86.cpp":        "",
+			"foo/bar/sharedonly.cpp": "",
+			"foo/bar/staticonly.cpp": "",
+			"foo/bar/Android.bp": `
+cc_library {
+    name: "a",
+    arch: {
+        arm: {
+            shared: {
+                srcs: ["arm_shared.cpp"],
+                cflags: ["-DARM_SHARED"],
+                static_libs: ["arm_static_dep_for_shared"],
+                whole_static_libs: ["arm_whole_static_dep_for_shared"],
+                shared_libs: ["arm_shared_dep_for_shared"],
+            },
+        },
+        x86: {
+            static: {
+                srcs: ["x86_static.cpp"],
+                cflags: ["-DX86_STATIC"],
+                static_libs: ["x86_dep_for_static"],
+            },
+        },
+    },
+    target: {
+        android: {
+            shared: {
+                srcs: ["android_shared.cpp"],
+                cflags: ["-DANDROID_SHARED"],
+                static_libs: ["android_dep_for_shared"],
+            },
+        },
+        android_arm: {
+            shared: {
+                cflags: ["-DANDROID_ARM_SHARED"],
+            },
+        },
+    },
+    srcs: ["both.cpp"],
+    cflags: ["bothflag"],
+    static_libs: ["static_dep_for_both"],
+    static: {
+        srcs: ["staticonly.cpp"],
+        cflags: ["staticflag"],
+        static_libs: ["static_dep_for_static"],
+    },
+    shared: {
+        srcs: ["sharedonly.cpp"],
+        cflags: ["sharedflag"],
+        static_libs: ["static_dep_for_shared"],
+    },
+    bazel_module: { bp2build_available: true },
+}
+
+cc_library_static { name: "static_dep_for_shared" }
+cc_library_static { name: "static_dep_for_static" }
+cc_library_static { name: "static_dep_for_both" }
+
+cc_library_static { name: "arm_static_dep_for_shared" }
+cc_library_static { name: "arm_whole_static_dep_for_shared" }
+cc_library_static { name: "arm_shared_dep_for_shared" }
+
+cc_library_static { name: "x86_dep_for_static" }
+
+cc_library_static { name: "android_dep_for_shared" }
+`,
+		},
+		blueprint: soongCcLibraryPreamble,
+		expectedBazelTargets: []string{`cc_library(
+    name = "a",
+    copts = [
+        "bothflag",
+        "-Ifoo/bar",
+        "-I$(BINDIR)/foo/bar",
+    ],
+    dynamic_deps_for_shared = select({
+        "//build/bazel/platforms/arch:arm": [":arm_shared_dep_for_shared"],
+        "//conditions:default": [],
+    }),
+    implementation_deps = [":static_dep_for_both"],
+    shared_copts = ["sharedflag"] + select({
+        "//build/bazel/platforms/arch:arm": ["-DARM_SHARED"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": ["-DANDROID_SHARED"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms:android_arm": ["-DANDROID_ARM_SHARED"],
+        "//conditions:default": [],
+    }),
+    shared_srcs = ["sharedonly.cpp"] + select({
+        "//build/bazel/platforms/arch:arm": ["arm_shared.cpp"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": ["android_shared.cpp"],
+        "//conditions:default": [],
+    }),
+    srcs = ["both.cpp"],
+    static_copts = ["staticflag"] + select({
+        "//build/bazel/platforms/arch:x86": ["-DX86_STATIC"],
+        "//conditions:default": [],
+    }),
+    static_deps_for_shared = [":static_dep_for_shared"] + select({
+        "//build/bazel/platforms/arch:arm": [":arm_static_dep_for_shared"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms/os:android": [":android_dep_for_shared"],
+        "//conditions:default": [],
+    }),
+    static_deps_for_static = [":static_dep_for_static"] + select({
+        "//build/bazel/platforms/arch:x86": [":x86_dep_for_static"],
+        "//conditions:default": [],
+    }),
+    static_srcs = ["staticonly.cpp"] + select({
+        "//build/bazel/platforms/arch:x86": ["x86_static.cpp"],
+        "//conditions:default": [],
+    }),
+    whole_archive_deps_for_shared = select({
+        "//build/bazel/platforms/arch:arm": [":arm_whole_static_dep_for_shared"],
+        "//conditions:default": [],
+    }),
+)`},
+	})
+}
+
 func TestCcLibraryNonConfiguredVersionScript(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
 		description:                        "cc_library non-configured version script",
@@ -618,7 +755,7 @@
 
 func TestCcLibraryCppFlagsGoesIntoCopts(t *testing.T) {
 	runCcLibraryTestCase(t, bp2buildTestCase{
-		description:                        "cc_library cppflags goes into copts",
+		description:                        "cc_library cppflags usage",
 		moduleTypeUnderTest:                "cc_library",
 		moduleTypeUnderTestFactory:         cc.LibraryFactory,
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryBp2Build,
@@ -654,10 +791,12 @@
     name = "a",
     copts = [
         "-Wall",
-        "-fsigned-char",
-        "-pedantic",
         "-Ifoo/bar",
         "-I$(BINDIR)/foo/bar",
+    ],
+    cppflags = [
+        "-fsigned-char",
+        "-pedantic",
     ] + select({
         "//build/bazel/platforms/arch:arm64": ["-DARM64=1"],
         "//conditions:default": [],
diff --git a/bp2build/cc_library_headers_conversion_test.go b/bp2build/cc_library_headers_conversion_test.go
index 2859bab..264ba6b 100644
--- a/bp2build/cc_library_headers_conversion_test.go
+++ b/bp2build/cc_library_headers_conversion_test.go
@@ -84,6 +84,7 @@
 }
 
 func runCcLibraryHeadersTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
 	runBp2BuildTestCase(t, registerCcLibraryHeadersModuleTypes, tc)
 }
 
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index 44c6ad4..833ceba 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -76,6 +76,7 @@
 }
 
 func runCcLibraryStaticTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
 	runBp2BuildTestCase(t, registerCcLibraryStaticModuleTypes, tc)
 }
 
@@ -650,7 +651,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = [
+    srcs_c = [
         "common.c",
         "foo-a.c",
     ],
@@ -682,7 +683,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["foo-arm.c"],
         "//conditions:default": [],
     }),
@@ -719,7 +720,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-arm.c"],
         "//conditions:default": ["not-for-arm.c"],
     }),
@@ -758,7 +759,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
             "not-for-x86.c",
@@ -813,7 +814,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
             "not-for-arm64.c",
@@ -909,7 +910,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": ["for-lib32.c"],
         "//build/bazel/platforms/arch:x86": ["for-lib32.c"],
         "//conditions:default": ["not-for-lib32.c"],
@@ -948,7 +949,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-lib32.c",
             "not-for-lib64.c",
@@ -1020,7 +1021,7 @@
         "-I$(BINDIR)/.",
     ],
     linkstatic = True,
-    srcs = ["common.c"] + select({
+    srcs_c = ["common.c"] + select({
         "//build/bazel/platforms/arch:arm": [
             "for-arm.c",
             "for-lib32.c",
@@ -1074,10 +1075,10 @@
 		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
 		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
 		filesystem: map[string]string{
-			"common.c":             "",
-			"for-x86.c":            "",
-			"not-for-x86.c":        "",
-			"not-for-everything.c": "",
+			"common.cpp":             "",
+			"for-x86.cpp":            "",
+			"not-for-x86.cpp":        "",
+			"not-for-everything.cpp": "",
 			"dep/Android.bp": `
 genrule {
 	name: "generated_src_other_pkg",
@@ -1118,14 +1119,14 @@
 
 cc_library_static {
    name: "foo_static3",
-   srcs: ["common.c", "not-for-*.c"],
-   exclude_srcs: ["not-for-everything.c"],
+   srcs: ["common.cpp", "not-for-*.cpp"],
+   exclude_srcs: ["not-for-everything.cpp"],
    generated_sources: ["generated_src", "generated_src_other_pkg"],
    generated_headers: ["generated_hdr", "generated_hdr_other_pkg"],
    arch: {
        x86: {
-           srcs: ["for-x86.c"],
-           exclude_srcs: ["not-for-x86.c"],
+           srcs: ["for-x86.cpp"],
+           exclude_srcs: ["not-for-x86.cpp"],
            generated_sources: ["generated_src_x86"],
            generated_headers: ["generated_hdr_other_pkg_x86"],
        },
@@ -1144,14 +1145,74 @@
         "//dep:generated_src_other_pkg",
         ":generated_hdr",
         ":generated_src",
-        "common.c",
+        "common.cpp",
     ] + select({
         "//build/bazel/platforms/arch:x86": [
             "//dep:generated_hdr_other_pkg_x86",
             ":generated_src_x86",
-            "for-x86.c",
+            "for-x86.cpp",
         ],
-        "//conditions:default": ["not-for-x86.c"],
+        "//conditions:default": ["not-for-x86.cpp"],
+    }),
+)`},
+	})
+}
+
+func TestCcLibraryStaticGetTargetProperties(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+
+		description:                        "cc_library_static complex GetTargetProperties",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    target: {
+        android: {
+            srcs: ["android_src.c"],
+        },
+        android_arm: {
+            srcs: ["android_arm_src.c"],
+        },
+        android_arm64: {
+            srcs: ["android_arm64_src.c"],
+        },
+        android_x86: {
+            srcs: ["android_x86_src.c"],
+        },
+        android_x86_64: {
+            srcs: ["android_x86_64_src.c"],
+        },
+        linux_bionic_arm64: {
+            srcs: ["linux_bionic_arm64_src.c"],
+        },
+        linux_bionic_x86_64: {
+            srcs: ["linux_bionic_x86_64_src.c"],
+        },
+    },
+}`,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ],
+    linkstatic = True,
+    srcs_c = select({
+        "//build/bazel/platforms/os:android": ["android_src.c"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms:android_arm": ["android_arm_src.c"],
+        "//build/bazel/platforms:android_arm64": ["android_arm64_src.c"],
+        "//build/bazel/platforms:android_x86": ["android_x86_src.c"],
+        "//build/bazel/platforms:android_x86_64": ["android_x86_64_src.c"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/platforms:linux_bionic_arm64": ["linux_bionic_arm64_src.c"],
+        "//build/bazel/platforms:linux_bionic_x86_64": ["linux_bionic_x86_64_src.c"],
+        "//conditions:default": [],
     }),
 )`},
 	})
@@ -1187,17 +1248,89 @@
         "-I.",
         "-I$(BINDIR)/.",
     ] + select({
+        "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
+        "//conditions:default": [],
+    }) + select({
         "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
         "//conditions:default": [],
     }) + select({
         "//build/bazel/product_variables:malloc_zero_contents": ["-Wmalloc_zero_contents"],
         "//conditions:default": [],
+    }),
+    linkstatic = True,
+    srcs_c = ["common.c"],
+)`},
+	})
+}
+
+func TestCcLibraryStaticProductVariableArchSpecificSelects(t *testing.T) {
+	runCcLibraryStaticTestCase(t, bp2buildTestCase{
+		description:                        "cc_library_static arch-specific product variable selects",
+		moduleTypeUnderTest:                "cc_library_static",
+		moduleTypeUnderTestFactory:         cc.LibraryStaticFactory,
+		moduleTypeUnderTestBp2BuildMutator: cc.CcLibraryStaticBp2Build,
+		depsMutators:                       []android.RegisterMutatorFunc{cc.RegisterDepsBp2Build},
+		filesystem:                         map[string]string{},
+		blueprint: soongCcLibraryStaticPreamble + `
+cc_library_static {
+    name: "foo_static",
+    srcs: ["common.c"],
+    product_variables: {
+      malloc_not_svelte: {
+        cflags: ["-Wmalloc_not_svelte"],
+      },
+    },
+		arch: {
+				arm64: {
+						product_variables: {
+								malloc_not_svelte: {
+										cflags: ["-Warm64_malloc_not_svelte"],
+								},
+						},
+				},
+		},
+		multilib: {
+				lib32: {
+						product_variables: {
+								malloc_not_svelte: {
+										cflags: ["-Wlib32_malloc_not_svelte"],
+								},
+						},
+				},
+		},
+		target: {
+				android: {
+						product_variables: {
+								malloc_not_svelte: {
+										cflags: ["-Wandroid_malloc_not_svelte"],
+								},
+						},
+				}
+		},
+} `,
+		expectedBazelTargets: []string{`cc_library_static(
+    name = "foo_static",
+    copts = [
+        "-I.",
+        "-I$(BINDIR)/.",
+    ] + select({
+        "//build/bazel/product_variables:malloc_not_svelte": ["-Wmalloc_not_svelte"],
+        "//conditions:default": [],
     }) + select({
-        "//build/bazel/product_variables:binder32bit": ["-Wbinder32bit"],
+        "//build/bazel/product_variables:malloc_not_svelte-android": ["-Wandroid_malloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-arm": ["-Wlib32_malloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-arm64": ["-Warm64_malloc_not_svelte"],
+        "//conditions:default": [],
+    }) + select({
+        "//build/bazel/product_variables:malloc_not_svelte-x86": ["-Wlib32_malloc_not_svelte"],
         "//conditions:default": [],
     }),
     linkstatic = True,
-    srcs = ["common.c"],
+    srcs_c = ["common.c"],
 )`},
 	})
 }
diff --git a/bp2build/cc_object_conversion_test.go b/bp2build/cc_object_conversion_test.go
index d4eeb7c..f7e94c2 100644
--- a/bp2build/cc_object_conversion_test.go
+++ b/bp2build/cc_object_conversion_test.go
@@ -27,6 +27,7 @@
 }
 
 func runCcObjectTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
 	runBp2BuildTestCase(t, registerCcObjectModuleTypes, tc)
 }
 
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 3cdc994..b5070b9 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -27,14 +27,27 @@
 	}
 
 	osSelects := map[string]reflect.Value{}
-	for os, selectKey := range bazel.PlatformOsMap {
-		osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os))
+	osArchSelects := make([]selects, 0)
+	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
+		selectKey := bazel.PlatformOsMap[os]
+		osSelects[selectKey] = reflect.ValueOf(list.GetOsValueForTarget(os))
+		archSelects := make(map[string]reflect.Value)
+		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT? (not in AllArches)
+		for _, arch := range bazel.AllArches {
+			target := os + "_" + arch
+			selectKey := bazel.PlatformTargetMap[target]
+			archSelects[selectKey] = reflect.ValueOf(list.GetOsArchValueForTarget(os, arch))
+		}
+		osArchSelects = append(osArchSelects, archSelects)
 	}
 	if len(osSelects) > 0 {
 		selectValues = append(selectValues, osSelects)
 	}
+	if len(osArchSelects) > 0 {
+		selectValues = append(selectValues, osArchSelects...)
+	}
 
-	for _, pv := range list.ProductValues {
+	for _, pv := range list.SortedProductVariables() {
 		s := make(selects)
 		if len(pv.Values) > 0 {
 			s[pv.SelectKey()] = reflect.ValueOf(pv.Values)
@@ -74,11 +87,25 @@
 	}
 
 	osSelects := map[string]reflect.Value{}
-	for os, selectKey := range bazel.PlatformOsMap {
-		osSelects[selectKey] = reflect.ValueOf(list.GetValueForOS(os).Includes)
+	osArchSelects := make([]selects, 0)
+	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
+		selectKey := bazel.PlatformOsMap[os]
+		osSelects[selectKey] = reflect.ValueOf(list.GetOsValueForTarget(os).Includes)
+		archSelects := make(map[string]reflect.Value)
+		// TODO(b/187530594): Should we also check arch=CONDITIOSN_DEFAULT? (not in AllArches)
+		for _, arch := range bazel.AllArches {
+			target := os + "_" + arch
+			selectKey := bazel.PlatformTargetMap[target]
+			archSelects[selectKey] = reflect.ValueOf(list.GetOsArchValueForTarget(os, arch).Includes)
+		}
+		osArchSelects = append(osArchSelects, archSelects)
 	}
 
-	return value, []selects{archSelects, osSelects}
+	var selects []selects
+	selects = append(selects, archSelects)
+	selects = append(selects, osSelects)
+	selects = append(selects, osArchSelects...)
+	return value, selects
 }
 
 // prettyPrintAttribute converts an Attribute to its Bazel syntax. May contain
diff --git a/bp2build/conversion.go b/bp2build/conversion.go
index 101ad3d..bced4c1 100644
--- a/bp2build/conversion.go
+++ b/bp2build/conversion.go
@@ -5,7 +5,6 @@
 	"android/soong/cc/config"
 	"fmt"
 	"reflect"
-	"sort"
 	"strings"
 
 	"github.com/google/blueprint/proptools"
@@ -64,22 +63,28 @@
 			continue
 		}
 		targets := buildToTargets[dir]
-		sort.Slice(targets, func(i, j int) bool {
-			// this will cover all bp2build generated targets
-			if targets[i].name < targets[j].name {
-				return true
-			}
-			// give a strict ordering to content from hand-crafted targets
-			return targets[i].content < targets[j].content
-		})
-		content := soongModuleLoad
+		targets.sort()
+
+		var content string
 		if mode == Bp2Build {
-			content = `# This file was automatically generated by bp2build for the Bazel migration project.
-# Feel free to edit or test it, but do *not* check it into your version control system.`
-			content += "\n\n"
-			content += "package(default_visibility = [\"//visibility:public\"])"
-			content += "\n\n"
+			content = `# READ THIS FIRST:
+# This file was automatically generated by bp2build for the Bazel migration project.
+# Feel free to edit or test it, but do *not* check it into your version control system.
+`
+			if targets.hasHandcraftedTargets() {
+				// For BUILD files with both handcrafted and generated targets,
+				// don't hardcode actual content, like package() declarations.
+				// Leave that responsibility to the checked-in BUILD file
+				// instead.
+				content += `# This file contains generated targets and handcrafted targets that are manually managed in the source tree.`
+			} else {
+				// For fully-generated BUILD files, hardcode the default visibility.
+				content += "package(default_visibility = [\"//visibility:public\"])"
+			}
+			content += "\n"
 			content += targets.LoadStatements()
+		} else if mode == QueryView {
+			content = soongModuleLoad
 		}
 		if content != "" {
 			// If there are load statements, add a couple of newlines.
diff --git a/bp2build/python_binary_conversion_test.go b/bp2build/python_binary_conversion_test.go
index 95bce3c..7bedf71 100644
--- a/bp2build/python_binary_conversion_test.go
+++ b/bp2build/python_binary_conversion_test.go
@@ -8,6 +8,7 @@
 )
 
 func runPythonTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
 	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
 }
 
diff --git a/bp2build/sh_conversion_test.go b/bp2build/sh_conversion_test.go
index 91bba54..82e0a14 100644
--- a/bp2build/sh_conversion_test.go
+++ b/bp2build/sh_conversion_test.go
@@ -49,6 +49,7 @@
 }
 
 func runShBinaryTestCase(t *testing.T, tc bp2buildTestCase) {
+	t.Helper()
 	runBp2BuildTestCase(t, func(ctx android.RegistrationContext) {}, tc)
 }
 
diff --git a/cc/bp2build.go b/cc/bp2build.go
index 0c827c5..339a489 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -50,35 +50,51 @@
 
 	var allDeps []string
 
-	for _, p := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) {
-		// base compiler props
-		if baseCompilerProps, ok := p.(*BaseCompilerProperties); ok {
+	for _, osProps := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) {
+		// os base compiler props
+		if baseCompilerProps, ok := osProps.Properties.(*BaseCompilerProperties); ok {
 			allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
 			allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
 		}
+		// os + arch base compiler props
+		for _, archProps := range osProps.ArchProperties {
+			if baseCompilerProps, ok := archProps.(*BaseCompilerProperties); ok {
+				allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
+				allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
+			}
+		}
 	}
 
-	for _, p := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
+	for _, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
 		// arch specific compiler props
-		if baseCompilerProps, ok := p.(*BaseCompilerProperties); ok {
+		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
 			allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
 		}
 	}
 
-	for _, p := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) {
-		// arch specific linker props
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+	for _, osProps := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) {
+		// os specific linker props
+		if baseLinkerProps, ok := osProps.Properties.(*BaseLinkerProperties); ok {
 			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
 			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
 			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
 			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
 		}
+		// os + arch base compiler props
+		for _, archProps := range osProps.ArchProperties {
+			if baseLinkerProps, ok := archProps.(*BaseLinkerProperties); ok {
+				allDeps = append(allDeps, baseLinkerProps.Header_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
+				allDeps = append(allDeps, baseLinkerProps.Static_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
+			}
+		}
 	}
 
-	for _, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
+	for _, props := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
 		// arch specific linker props
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
+		if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
 			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
 			allDeps = append(allDeps, baseLinkerProps.Export_header_lib_headers...)
 			allDeps = append(allDeps, baseLinkerProps.Static_libs...)
@@ -88,24 +104,64 @@
 
 	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
 	if lib, ok := module.compiler.(*libraryDecorator); ok {
-		allDeps = append(allDeps, lib.SharedProperties.Shared.Static_libs...)
-		allDeps = append(allDeps, lib.SharedProperties.Shared.Whole_static_libs...)
-		allDeps = append(allDeps, lib.SharedProperties.Shared.Shared_libs...)
+		appendDeps := func(deps []string, p StaticOrSharedProperties) []string {
+			deps = append(deps, p.Static_libs...)
+			deps = append(deps, p.Whole_static_libs...)
+			deps = append(deps, p.Shared_libs...)
+			return deps
+		}
 
-		allDeps = append(allDeps, lib.StaticProperties.Static.Static_libs...)
-		allDeps = append(allDeps, lib.StaticProperties.Static.Whole_static_libs...)
-		allDeps = append(allDeps, lib.StaticProperties.Static.Shared_libs...)
+		allDeps = appendDeps(allDeps, lib.SharedProperties.Shared)
+		allDeps = appendDeps(allDeps, lib.StaticProperties.Static)
 
 		// TODO(b/186024507, b/186489250): Temporarily exclude adding
 		// system_shared_libs deps until libc and libm builds.
 		// allDeps = append(allDeps, lib.SharedProperties.Shared.System_shared_libs...)
 		// allDeps = append(allDeps, lib.StaticProperties.Static.System_shared_libs...)
+
+		// Deps in the target/arch nested static: { .. } and shared: { .. } props of a cc_library.
+		// target: { <target>: shared: { ... } }
+		for _, targetProps := range module.GetTargetProperties(ctx, &SharedProperties{}) {
+			if p, ok := targetProps.Properties.(*SharedProperties); ok {
+				allDeps = appendDeps(allDeps, p.Shared)
+			}
+			for _, archProperties := range targetProps.ArchProperties {
+				if p, ok := archProperties.(*SharedProperties); ok {
+					allDeps = appendDeps(allDeps, p.Shared)
+				}
+			}
+		}
+		// target: { <target>: static: { ... } }
+		for _, targetProps := range module.GetTargetProperties(ctx, &StaticProperties{}) {
+			if p, ok := targetProps.Properties.(*StaticProperties); ok {
+				allDeps = appendDeps(allDeps, p.Static)
+			}
+			for _, archProperties := range targetProps.ArchProperties {
+				if p, ok := archProperties.(*StaticProperties); ok {
+					allDeps = appendDeps(allDeps, p.Static)
+				}
+			}
+		}
+		// arch: { <arch>: shared: { ... } }
+		for _, properties := range module.GetArchProperties(ctx, &SharedProperties{}) {
+			if p, ok := properties.(*SharedProperties); ok {
+				allDeps = appendDeps(allDeps, p.Shared)
+			}
+		}
+		// arch: { <arch>: static: { ... } }
+		for _, properties := range module.GetArchProperties(ctx, &StaticProperties{}) {
+			if p, ok := properties.(*StaticProperties); ok {
+				allDeps = appendDeps(allDeps, p.Static)
+			}
+		}
 	}
 
 	ctx.AddDependency(module, nil, android.SortedUniqueStrings(allDeps)...)
 }
 
-type sharedAttributes struct {
+// staticOrSharedAttributes are the Bazel-ified versions of StaticOrSharedProperties --
+// properties which apply to either the shared or static version of a cc_library module.
+type staticOrSharedAttributes struct {
 	copts            bazel.StringListAttribute
 	srcs             bazel.LabelListAttribute
 	staticDeps       bazel.LabelListAttribute
@@ -114,84 +170,124 @@
 }
 
 // bp2buildParseSharedProps returns the attributes for the shared variant of a cc_library.
-func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) sharedAttributes {
+func bp2BuildParseSharedProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
-		return sharedAttributes{}
+		return staticOrSharedAttributes{}
 	}
 
-	copts := bazel.StringListAttribute{Value: lib.SharedProperties.Shared.Cflags}
-
-	srcs := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleSrc(ctx, lib.SharedProperties.Shared.Srcs)}
-
-	staticDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Static_libs)}
-
-	dynamicDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Shared_libs)}
-
-	wholeArchiveDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.SharedProperties.Shared.Whole_static_libs)}
-
-	return sharedAttributes{
-		copts:            copts,
-		srcs:             srcs,
-		staticDeps:       staticDeps,
-		dynamicDeps:      dynamicDeps,
-		wholeArchiveDeps: wholeArchiveDeps,
-	}
-}
-
-type staticAttributes struct {
-	copts            bazel.StringListAttribute
-	srcs             bazel.LabelListAttribute
-	staticDeps       bazel.LabelListAttribute
-	dynamicDeps      bazel.LabelListAttribute
-	wholeArchiveDeps bazel.LabelListAttribute
+	return bp2buildParseStaticOrSharedProps(ctx, module, lib, false)
 }
 
 // bp2buildParseStaticProps returns the attributes for the static variant of a cc_library.
-func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticAttributes {
+func bp2BuildParseStaticProps(ctx android.TopDownMutatorContext, module *Module) staticOrSharedAttributes {
 	lib, ok := module.compiler.(*libraryDecorator)
 	if !ok {
-		return staticAttributes{}
+		return staticOrSharedAttributes{}
 	}
 
-	copts := bazel.StringListAttribute{Value: lib.StaticProperties.Static.Cflags}
+	return bp2buildParseStaticOrSharedProps(ctx, module, lib, true)
+}
 
-	srcs := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleSrc(ctx, lib.StaticProperties.Static.Srcs)}
-
-	staticDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Static_libs)}
-
-	dynamicDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Shared_libs)}
-
-	wholeArchiveDeps := bazel.LabelListAttribute{
-		Value: android.BazelLabelForModuleDeps(ctx, lib.StaticProperties.Static.Whole_static_libs)}
-
-	return staticAttributes{
-		copts:            copts,
-		srcs:             srcs,
-		staticDeps:       staticDeps,
-		dynamicDeps:      dynamicDeps,
-		wholeArchiveDeps: wholeArchiveDeps,
+func bp2buildParseStaticOrSharedProps(ctx android.TopDownMutatorContext, module *Module, lib *libraryDecorator, isStatic bool) staticOrSharedAttributes {
+	var props StaticOrSharedProperties
+	if isStatic {
+		props = lib.StaticProperties.Static
+	} else {
+		props = lib.SharedProperties.Shared
 	}
+
+	attrs := staticOrSharedAttributes{
+		copts:            bazel.StringListAttribute{Value: props.Cflags},
+		srcs:             bazel.LabelListAttribute{Value: android.BazelLabelForModuleSrc(ctx, props.Srcs)},
+		staticDeps:       bazel.LabelListAttribute{Value: android.BazelLabelForModuleDeps(ctx, props.Static_libs)},
+		dynamicDeps:      bazel.LabelListAttribute{Value: android.BazelLabelForModuleDeps(ctx, props.Shared_libs)},
+		wholeArchiveDeps: bazel.LabelListAttribute{Value: android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs)},
+	}
+
+	setArchAttrs := func(arch string, props StaticOrSharedProperties) {
+		attrs.copts.SetValueForArch(arch, props.Cflags)
+		attrs.srcs.SetValueForArch(arch, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+		attrs.staticDeps.SetValueForArch(arch, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+		attrs.dynamicDeps.SetValueForArch(arch, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+		attrs.wholeArchiveDeps.SetValueForArch(arch, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
+	}
+
+	setTargetAttrs := func(target string, props StaticOrSharedProperties) {
+		attrs.copts.SetOsValueForTarget(target, props.Cflags)
+		attrs.srcs.SetOsValueForTarget(target, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+		attrs.staticDeps.SetOsValueForTarget(target, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+		attrs.dynamicDeps.SetOsValueForTarget(target, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+		attrs.wholeArchiveDeps.SetOsValueForTarget(target, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
+	}
+
+	setTargetArchAttrs := func(target, arch string, props StaticOrSharedProperties) {
+		attrs.copts.SetOsArchValueForTarget(target, arch, props.Cflags)
+		attrs.srcs.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+		attrs.staticDeps.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+		attrs.dynamicDeps.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+		attrs.wholeArchiveDeps.SetOsArchValueForTarget(target, arch, android.BazelLabelForModuleDeps(ctx, props.Whole_static_libs))
+	}
+
+	if isStatic {
+		for arch, properties := range module.GetArchProperties(ctx, &StaticProperties{}) {
+			if staticOrSharedProps, ok := properties.(*StaticProperties); ok {
+				setArchAttrs(arch.Name, staticOrSharedProps.Static)
+			}
+		}
+		for target, p := range module.GetTargetProperties(ctx, &StaticProperties{}) {
+			if staticOrSharedProps, ok := p.Properties.(*StaticProperties); ok {
+				setTargetAttrs(target.Name, staticOrSharedProps.Static)
+			}
+			for arch, archProperties := range p.ArchProperties {
+				if staticOrSharedProps, ok := archProperties.(*StaticProperties); ok {
+					setTargetArchAttrs(target.Name, arch.Name, staticOrSharedProps.Static)
+				}
+			}
+		}
+	} else {
+		for arch, p := range module.GetArchProperties(ctx, &SharedProperties{}) {
+			if staticOrSharedProps, ok := p.(*SharedProperties); ok {
+				setArchAttrs(arch.Name, staticOrSharedProps.Shared)
+			}
+		}
+		for target, p := range module.GetTargetProperties(ctx, &SharedProperties{}) {
+			if staticOrSharedProps, ok := p.Properties.(*SharedProperties); ok {
+				setTargetAttrs(target.Name, staticOrSharedProps.Shared)
+			}
+			for arch, archProperties := range p.ArchProperties {
+				if staticOrSharedProps, ok := archProperties.(*SharedProperties); ok {
+					setTargetArchAttrs(target.Name, arch.Name, staticOrSharedProps.Shared)
+				}
+			}
+		}
+	}
+
+	return attrs
 }
 
 // Convenience struct to hold all attributes parsed from compiler properties.
 type compilerAttributes struct {
-	copts    bazel.StringListAttribute
+	// Options for all languages
+	copts bazel.StringListAttribute
+	// Assembly options and sources
+	asFlags bazel.StringListAttribute
+	asSrcs  bazel.LabelListAttribute
+	// C options and sources
+	conlyFlags bazel.StringListAttribute
+	cSrcs      bazel.LabelListAttribute
+	// C++ options and sources
+	cppFlags bazel.StringListAttribute
 	srcs     bazel.LabelListAttribute
-	includes bazel.StringListAttribute
 }
 
 // bp2BuildParseCompilerProps returns copts, srcs and hdrs and other attributes.
 func bp2BuildParseCompilerProps(ctx android.TopDownMutatorContext, module *Module) compilerAttributes {
 	var srcs bazel.LabelListAttribute
 	var copts bazel.StringListAttribute
+	var asFlags bazel.StringListAttribute
+	var conlyFlags bazel.StringListAttribute
+	var cppFlags bazel.StringListAttribute
 
 	// Creates the -I flags for a directory, while making the directory relative
 	// to the exec root for Bazel to work.
@@ -215,15 +311,21 @@
 		return append(includeDirs, baseCompilerProps.Local_include_dirs...)
 	}
 
-	// Parse the list of copts.
-	parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string {
-		var copts []string
-		for _, flag := range append(baseCompilerProps.Cflags, baseCompilerProps.Cppflags...) {
+	parseCommandLineFlags := func(soongFlags []string) []string {
+		var result []string
+		for _, flag := range soongFlags {
 			// Soong's cflags can contain spaces, like `-include header.h`. For
 			// Bazel's copts, split them up to be compatible with the
 			// no_copts_tokenization feature.
-			copts = append(copts, strings.Split(flag, " ")...)
+			result = append(result, strings.Split(flag, " ")...)
 		}
+		return result
+	}
+
+	// Parse the list of copts.
+	parseCopts := func(baseCompilerProps *BaseCompilerProperties) []string {
+		var copts []string
+		copts = append(copts, parseCommandLineFlags(baseCompilerProps.Cflags)...)
 		for _, dir := range parseLocalIncludeDirs(baseCompilerProps) {
 			copts = append(copts, includeFlags(dir)...)
 		}
@@ -260,6 +362,9 @@
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			srcs.Value = parseSrcs(baseCompilerProps)
 			copts.Value = parseCopts(baseCompilerProps)
+			asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags)
+			conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
+			cppFlags.Value = parseCommandLineFlags(baseCompilerProps.Cppflags)
 
 			// Used for arch-specific srcs later.
 			baseSrcs = baseCompilerProps.Srcs
@@ -290,6 +395,9 @@
 			}
 
 			copts.SetValueForArch(arch.Name, parseCopts(baseCompilerProps))
+			asFlags.SetValueForArch(arch.Name, parseCommandLineFlags(baseCompilerProps.Asflags))
+			conlyFlags.SetValueForArch(arch.Name, parseCommandLineFlags(baseCompilerProps.Conlyflags))
+			cppFlags.SetValueForArch(arch.Name, parseCommandLineFlags(baseCompilerProps.Cppflags))
 		}
 	}
 
@@ -308,13 +416,27 @@
 	// TODO(b/186153868): handle the case with multiple variant types, e.g. when arch and os are both used.
 	srcs.SetValueForArch(bazel.CONDITIONS_DEFAULT, defaultsSrcs)
 
-	// Handle OS specific props.
-	for os, props := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) {
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
+	// Handle target specific properties.
+	for os, osProps := range module.GetTargetProperties(ctx, &BaseCompilerProperties{}) {
+		if baseCompilerProps, ok := osProps.Properties.(*BaseCompilerProperties); ok {
 			srcsList := parseSrcs(baseCompilerProps)
 			// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
-			srcs.SetValueForOS(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
-			copts.SetValueForOS(os.Name, parseCopts(baseCompilerProps))
+			srcs.SetOsValueForTarget(os.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
+			copts.SetOsValueForTarget(os.Name, parseCopts(baseCompilerProps))
+			asFlags.SetOsValueForTarget(os.Name, parseCommandLineFlags(baseCompilerProps.Asflags))
+			conlyFlags.SetOsValueForTarget(os.Name, parseCommandLineFlags(baseCompilerProps.Conlyflags))
+			cppFlags.SetOsValueForTarget(os.Name, parseCommandLineFlags(baseCompilerProps.Cppflags))
+		}
+		for arch, archProps := range osProps.ArchProperties {
+			if baseCompilerProps, ok := archProps.(*BaseCompilerProperties); ok {
+				srcsList := parseSrcs(baseCompilerProps)
+				// TODO(b/186153868): add support for os-specific srcs and exclude_srcs
+				srcs.SetOsArchValueForTarget(os.Name, arch.Name, bazel.SubtractBazelLabelList(srcsList, baseSrcsLabelList))
+				copts.SetOsArchValueForTarget(os.Name, arch.Name, parseCopts(baseCompilerProps))
+				asFlags.SetOsArchValueForTarget(os.Name, arch.Name, parseCommandLineFlags(baseCompilerProps.Asflags))
+				conlyFlags.SetOsArchValueForTarget(os.Name, arch.Name, parseCommandLineFlags(baseCompilerProps.Conlyflags))
+				cppFlags.SetOsArchValueForTarget(os.Name, arch.Name, parseCommandLineFlags(baseCompilerProps.Cppflags))
+			}
 		}
 	}
 
@@ -333,9 +455,28 @@
 		}
 	}
 
+	// Branch srcs into three language-specific groups.
+	// C++ is the "catch-all" group, and comprises generated sources because we don't
+	// know the language of these sources until the genrule is executed.
+	// TODO(b/): Handle language detection of sources in a Bazel rule.
+	isCSrc := func(s string) bool {
+		return strings.HasSuffix(s, ".c")
+	}
+	isAsmSrc := func(s string) bool {
+		return strings.HasSuffix(s, ".S") || strings.HasSuffix(s, ".s")
+	}
+	cSrcs := bazel.FilterLabelListAttribute(srcs, isCSrc)
+	asSrcs := bazel.FilterLabelListAttribute(srcs, isAsmSrc)
+	srcs = bazel.SubtractBazelLabelListAttribute(srcs, cSrcs)
+	srcs = bazel.SubtractBazelLabelListAttribute(srcs, asSrcs)
 	return compilerAttributes{
-		srcs:  srcs,
-		copts: copts,
+		copts:      copts,
+		srcs:       srcs,
+		asFlags:    asFlags,
+		asSrcs:     asSrcs,
+		cSrcs:      cSrcs,
+		conlyFlags: conlyFlags,
+		cppFlags:   cppFlags,
 	}
 }
 
@@ -368,13 +509,18 @@
 	var linkopts bazel.StringListAttribute
 	var versionScript bazel.LabelAttribute
 
+	getLibs := func(baseLinkerProps *BaseLinkerProperties) []string {
+		libs := baseLinkerProps.Header_libs
+		libs = append(libs, baseLinkerProps.Static_libs...)
+		libs = android.SortedUniqueStrings(libs)
+		return libs
+	}
+
 	for _, linkerProps := range module.linker.linkerProps() {
 		if baseLinkerProps, ok := linkerProps.(*BaseLinkerProperties); ok {
-			libs := baseLinkerProps.Header_libs
-			libs = append(libs, baseLinkerProps.Static_libs...)
+			libs := getLibs(baseLinkerProps)
 			exportedLibs := baseLinkerProps.Export_header_lib_headers
 			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			libs = android.SortedUniqueStrings(libs)
 			deps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, libs))
 			exportedDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, exportedLibs))
 			linkopts.Value = getBp2BuildLinkerFlags(baseLinkerProps)
@@ -391,13 +537,11 @@
 		}
 	}
 
-	for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
-			libs := baseLinkerProps.Header_libs
-			libs = append(libs, baseLinkerProps.Static_libs...)
+	for arch, props := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
+		if baseLinkerProps, ok := props.(*BaseLinkerProperties); ok {
+			libs := getLibs(baseLinkerProps)
 			exportedLibs := baseLinkerProps.Export_header_lib_headers
 			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
 			exportedDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
 			linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
@@ -413,21 +557,34 @@
 		}
 	}
 
-	for os, p := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) {
-		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
-			libs := baseLinkerProps.Header_libs
-			libs = append(libs, baseLinkerProps.Static_libs...)
+	for os, targetProperties := range module.GetTargetProperties(ctx, &BaseLinkerProperties{}) {
+		if baseLinkerProps, ok := targetProperties.Properties.(*BaseLinkerProperties); ok {
+			libs := getLibs(baseLinkerProps)
 			exportedLibs := baseLinkerProps.Export_header_lib_headers
 			wholeArchiveLibs := baseLinkerProps.Whole_static_libs
-			libs = android.SortedUniqueStrings(libs)
-			wholeArchiveDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
-			deps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
-			exportedDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+			wholeArchiveDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+			deps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, libs))
+			exportedDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
 
-			linkopts.SetValueForOS(os.Name, getBp2BuildLinkerFlags(baseLinkerProps))
+			linkopts.SetOsValueForTarget(os.Name, getBp2BuildLinkerFlags(baseLinkerProps))
 
 			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetValueForOS(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
+			dynamicDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
+		}
+		for arch, archProperties := range targetProperties.ArchProperties {
+			if baseLinkerProps, ok := archProperties.(*BaseLinkerProperties); ok {
+				libs := getLibs(baseLinkerProps)
+				exportedLibs := baseLinkerProps.Export_header_lib_headers
+				wholeArchiveLibs := baseLinkerProps.Whole_static_libs
+				wholeArchiveDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
+				deps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
+				exportedDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+
+				linkopts.SetOsArchValueForTarget(os.Name, arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
+
+				sharedLibs := baseLinkerProps.Shared_libs
+				dynamicDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
+			}
 		}
 	}
 
@@ -476,32 +633,38 @@
 	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
 	includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
 
+	getVariantIncludeDirs := func(includeDirs []string, flagExporterProperties *FlagExporterProperties) []string {
+		variantIncludeDirs := flagExporterProperties.Export_system_include_dirs
+		variantIncludeDirs = append(variantIncludeDirs, flagExporterProperties.Export_include_dirs...)
+
+		// To avoid duplicate includes when base includes + arch includes are combined
+		// TODO: This doesn't take conflicts between arch and os includes into account
+		variantIncludeDirs = bazel.SubtractStrings(variantIncludeDirs, includeDirs)
+		return variantIncludeDirs
+	}
+
 	for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) {
 		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-			archIncludeDirs := flagExporterProperties.Export_system_include_dirs
-			archIncludeDirs = append(archIncludeDirs, flagExporterProperties.Export_include_dirs...)
-
-			// To avoid duplicate includes when base includes + arch includes are combined
-			// FIXME: This doesn't take conflicts between arch and os includes into account
-			archIncludeDirs = bazel.SubtractStrings(archIncludeDirs, includeDirs)
-
+			archIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
 			if len(archIncludeDirs) > 0 {
 				includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
 			}
 		}
 	}
 
-	for os, props := range module.GetTargetProperties(ctx, &FlagExporterProperties{}) {
-		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-			osIncludeDirs := flagExporterProperties.Export_system_include_dirs
-			osIncludeDirs = append(osIncludeDirs, flagExporterProperties.Export_include_dirs...)
-
-			// To avoid duplicate includes when base includes + os includes are combined
-			// FIXME: This doesn't take conflicts between arch and os includes into account
-			osIncludeDirs = bazel.SubtractStrings(osIncludeDirs, includeDirs)
-
-			if len(osIncludeDirs) > 0 {
-				includeDirsAttribute.SetValueForOS(os.Name, osIncludeDirs)
+	for os, targetProperties := range module.GetTargetProperties(ctx, &FlagExporterProperties{}) {
+		if flagExporterProperties, ok := targetProperties.Properties.(*FlagExporterProperties); ok {
+			targetIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
+			if len(targetIncludeDirs) > 0 {
+				includeDirsAttribute.SetOsValueForTarget(os.Name, targetIncludeDirs)
+			}
+		}
+		for arch, archProperties := range targetProperties.ArchProperties {
+			if flagExporterProperties, ok := archProperties.(*FlagExporterProperties); ok {
+				targetIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
+				if len(targetIncludeDirs) > 0 {
+					includeDirsAttribute.SetOsArchValueForTarget(os.Name, arch.Name, targetIncludeDirs)
+				}
 			}
 		}
 	}
diff --git a/cc/cc_test.go b/cc/cc_test.go
index d82619a..5acafbe 100644
--- a/cc/cc_test.go
+++ b/cc/cc_test.go
@@ -554,6 +554,13 @@
 			}
 		}
 
+		cc_library {
+			name: "libclang_rt.hwasan-llndk",
+			llndk: {
+				symbol_file: "libclang_rt.hwasan.map.txt",
+			}
+		}
+
 		cc_library_headers {
 			name: "libllndk_headers",
 			llndk: {
@@ -661,7 +668,7 @@
 		"VNDK-product: libvndk_product.so",
 		"VNDK-product: libvndk_sp_product_private-x.so",
 	})
-	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libdl.so", "libft2.so", "libllndk.so", "libm.so"})
+	checkVndkLibrariesOutput(t, ctx, "llndk.libraries.txt", []string{"libc.so", "libclang_rt.hwasan-llndk.so", "libdl.so", "libft2.so", "libllndk.so", "libm.so"})
 	checkVndkLibrariesOutput(t, ctx, "vndkcore.libraries.txt", []string{"libvndk-private.so", "libvndk.so", "libvndk_product.so"})
 	checkVndkLibrariesOutput(t, ctx, "vndksp.libraries.txt", []string{"libc++.so", "libvndk_sp-x.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
 	checkVndkLibrariesOutput(t, ctx, "vndkprivate.libraries.txt", []string{"libft2.so", "libvndk-private.so", "libvndk_sp_private-x.so", "libvndk_sp_product_private-x.so"})
diff --git a/cc/library.go b/cc/library.go
index c918b96..9fb7a24 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -230,6 +230,13 @@
 	Copts               bazel.StringListAttribute
 	Includes            bazel.StringListAttribute
 	Linkopts            bazel.StringListAttribute
+
+	Cppflags   bazel.StringListAttribute
+	Srcs_c     bazel.LabelListAttribute
+	Conlyflags bazel.StringListAttribute
+	Srcs_as    bazel.LabelListAttribute
+	Asflags    bazel.StringListAttribute
+
 	// Attributes pertaining to shared variant.
 	Shared_copts                  bazel.StringListAttribute
 	Shared_srcs                   bazel.LabelListAttribute
@@ -239,6 +246,7 @@
 	Whole_archive_deps_for_shared bazel.LabelListAttribute
 	User_link_flags               bazel.StringListAttribute
 	Version_script                bazel.LabelAttribute
+
 	// Attributes pertaining to static variant.
 	Static_copts                  bazel.StringListAttribute
 	Static_srcs                   bazel.LabelListAttribute
@@ -294,20 +302,27 @@
 	srcs.Append(compilerAttrs.srcs)
 
 	attrs := &bazelCcLibraryAttributes{
-		Srcs:                          srcs,
-		Implementation_deps:           linkerAttrs.deps,
-		Deps:                          linkerAttrs.exportedDeps,
-		Dynamic_deps:                  linkerAttrs.dynamicDeps,
-		Whole_archive_deps:            linkerAttrs.wholeArchiveDeps,
-		Copts:                         compilerAttrs.copts,
-		Includes:                      exportedIncludes,
-		Linkopts:                      linkerAttrs.linkopts,
+		Srcs:                srcs,
+		Implementation_deps: linkerAttrs.deps,
+		Deps:                linkerAttrs.exportedDeps,
+		Dynamic_deps:        linkerAttrs.dynamicDeps,
+		Whole_archive_deps:  linkerAttrs.wholeArchiveDeps,
+		Copts:               compilerAttrs.copts,
+		Includes:            exportedIncludes,
+		Linkopts:            linkerAttrs.linkopts,
+		Cppflags:            compilerAttrs.cppFlags,
+		Srcs_c:              compilerAttrs.cSrcs,
+		Conlyflags:          compilerAttrs.conlyFlags,
+		Srcs_as:             compilerAttrs.asSrcs,
+		Asflags:             compilerAttrs.asFlags,
+
 		Shared_copts:                  sharedAttrs.copts,
 		Shared_srcs:                   sharedAttrs.srcs,
 		Static_deps_for_shared:        sharedAttrs.staticDeps,
 		Whole_archive_deps_for_shared: sharedAttrs.wholeArchiveDeps,
 		Dynamic_deps_for_shared:       sharedAttrs.dynamicDeps,
 		Version_script:                linkerAttrs.versionScript,
+
 		Static_copts:                  staticAttrs.copts,
 		Static_srcs:                   staticAttrs.srcs,
 		Static_deps_for_static:        staticAttrs.staticDeps,
@@ -2230,6 +2245,12 @@
 	Linkstatic          bool
 	Includes            bazel.StringListAttribute
 	Hdrs                bazel.LabelListAttribute
+
+	Cppflags   bazel.StringListAttribute
+	Srcs_c     bazel.LabelListAttribute
+	Conlyflags bazel.StringListAttribute
+	Srcs_as    bazel.LabelListAttribute
+	Asflags    bazel.StringListAttribute
 }
 
 type bazelCcLibraryStatic struct {
@@ -2259,6 +2280,12 @@
 		Linkopts:   linkerAttrs.linkopts,
 		Linkstatic: true,
 		Includes:   exportedIncludes,
+
+		Cppflags:   compilerAttrs.cppFlags,
+		Srcs_c:     compilerAttrs.cSrcs,
+		Conlyflags: compilerAttrs.conlyFlags,
+		Srcs_as:    compilerAttrs.asSrcs,
+		Asflags:    compilerAttrs.asFlags,
 	}
 
 	props := bazel.BazelTargetModuleProperties{
diff --git a/cc/object.go b/cc/object.go
index 704cb69..cd71161 100644
--- a/cc/object.go
+++ b/cc/object.go
@@ -185,8 +185,14 @@
 	}
 	// TODO(b/183595872) warn/error if we're not handling product variables
 
+	// Don't split cc_object srcs across languages. Doing so would add complexity,
+	// and this isn't typically done for cc_object.
+	srcs := compilerAttrs.srcs
+	srcs.Append(compilerAttrs.cSrcs)
+	srcs.Append(compilerAttrs.asSrcs)
+
 	attrs := &bazelObjectAttributes{
-		Srcs:    compilerAttrs.srcs,
+		Srcs:    srcs,
 		Deps:    deps,
 		Copts:   compilerAttrs.copts,
 		Asflags: asFlags,
diff --git a/cc/vndk.go b/cc/vndk.go
index 0254edc..6a56c34 100644
--- a/cc/vndk.go
+++ b/cc/vndk.go
@@ -234,7 +234,6 @@
 
 var (
 	llndkLibraries                = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsLLNDK && !m.Header() })
-	llndkLibrariesWithoutHWASAN   = vndkModuleListRemover(llndkLibraries, "libclang_rt.hwasan-")
 	vndkSPLibraries               = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKSP })
 	vndkCoreLibraries             = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKCore })
 	vndkPrivateLibraries          = vndkModuleLister(func(m *Module) bool { return m.VendorProperties.IsVNDKPrivate })
@@ -419,10 +418,6 @@
 }
 
 func RegisterVndkLibraryTxtTypes(ctx android.RegistrationContext) {
-	// Make uses LLNDK_LIBRARIES to determine which libraries to install.
-	// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
-	// Therefore, by removing the library here, we cause it to only be installed if libc
-	// depends on it.
 	ctx.RegisterSingletonModuleType("llndk_libraries_txt", llndkLibrariesTxtFactory)
 	ctx.RegisterSingletonModuleType("vndksp_libraries_txt", vndkSPLibrariesTxtFactory)
 	ctx.RegisterSingletonModuleType("vndkcore_libraries_txt", vndkCoreLibrariesTxtFactory)
@@ -434,8 +429,9 @@
 type vndkLibrariesTxt struct {
 	android.SingletonModuleBase
 
-	lister      moduleListerFunc
-	makeVarName string
+	lister               moduleListerFunc
+	makeVarName          string
+	filterOutFromMakeVar string
 
 	properties VndkLibrariesTxtProperties
 
@@ -454,8 +450,12 @@
 // llndk_libraries_txt is a singleton module whose content is a list of LLNDK libraries
 // generated by Soong but can be referenced by other modules.
 // For example, apex_vndk can depend on these files as prebuilt.
+// Make uses LLNDK_LIBRARIES to determine which libraries to install.
+// HWASAN is only part of the LL-NDK in builds in which libc depends on HWASAN.
+// Therefore, by removing the library here, we cause it to only be installed if libc
+// depends on it.
 func llndkLibrariesTxtFactory() android.SingletonModule {
-	return newVndkLibrariesTxt(llndkLibrariesWithoutHWASAN, "LLNDK_LIBRARIES")
+	return newVndkLibrariesWithMakeVarFilter(llndkLibraries, "LLNDK_LIBRARIES", "libclang_rt.hwasan-")
 }
 
 // vndksp_libraries_txt is a singleton module whose content is a list of VNDKSP libraries
@@ -493,16 +493,21 @@
 	return newVndkLibrariesTxt(vndkUsingCoreVariantLibraries, "VNDK_USING_CORE_VARIANT_LIBRARIES")
 }
 
-func newVndkLibrariesTxt(lister moduleListerFunc, makeVarName string) android.SingletonModule {
+func newVndkLibrariesWithMakeVarFilter(lister moduleListerFunc, makeVarName string, filter string) android.SingletonModule {
 	m := &vndkLibrariesTxt{
-		lister:      lister,
-		makeVarName: makeVarName,
+		lister:               lister,
+		makeVarName:          makeVarName,
+		filterOutFromMakeVar: filter,
 	}
 	m.AddProperties(&m.properties)
 	android.InitAndroidModule(m)
 	return m
 }
 
+func newVndkLibrariesTxt(lister moduleListerFunc, makeVarName string) android.SingletonModule {
+	return newVndkLibrariesWithMakeVarFilter(lister, makeVarName, "")
+}
+
 func insertVndkVersion(filename string, vndkVersion string) string {
 	if index := strings.LastIndex(filename, "."); index != -1 {
 		return filename[:index] + "." + vndkVersion + filename[index:]
@@ -542,8 +547,21 @@
 }
 
 func (txt *vndkLibrariesTxt) MakeVars(ctx android.MakeVarsContext) {
-	ctx.Strict(txt.makeVarName, strings.Join(txt.moduleNames, " "))
-
+	filter := func(modules []string, prefix string) []string {
+		if prefix == "" {
+			return modules
+		}
+		var result []string
+		for _, module := range modules {
+			if strings.HasPrefix(module, prefix) {
+				continue
+			} else {
+				result = append(result, module)
+			}
+		}
+		return result
+	}
+	ctx.Strict(txt.makeVarName, strings.Join(filter(txt.moduleNames, txt.filterOutFromMakeVar), " "))
 }
 
 // PrebuiltEtcModule interface
diff --git a/cmd/go2bp/Android.bp b/cmd/go2bp/Android.bp
new file mode 100644
index 0000000..53d70b6
--- /dev/null
+++ b/cmd/go2bp/Android.bp
@@ -0,0 +1,26 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+blueprint_go_binary {
+    name: "go2bp",
+    deps: [
+        "blueprint-proptools",
+        "bpfix-lib",
+    ],
+    srcs: ["go2bp.go"],
+}
diff --git a/cmd/go2bp/go2bp.go b/cmd/go2bp/go2bp.go
new file mode 100644
index 0000000..67138f1
--- /dev/null
+++ b/cmd/go2bp/go2bp.go
@@ -0,0 +1,361 @@
+// Copyright 2021 Google Inc. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/json"
+	"flag"
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"sort"
+	"strings"
+	"text/template"
+
+	"github.com/google/blueprint/proptools"
+
+	"android/soong/bpfix/bpfix"
+)
+
+type RewriteNames []RewriteName
+type RewriteName struct {
+	prefix string
+	repl   string
+}
+
+func (r *RewriteNames) String() string {
+	return ""
+}
+
+func (r *RewriteNames) Set(v string) error {
+	split := strings.SplitN(v, "=", 2)
+	if len(split) != 2 {
+		return fmt.Errorf("Must be in the form of <prefix>=<replace>")
+	}
+	*r = append(*r, RewriteName{
+		prefix: split[0],
+		repl:   split[1],
+	})
+	return nil
+}
+
+func (r *RewriteNames) GoToBp(name string) string {
+	ret := name
+	for _, r := range *r {
+		prefix := r.prefix
+		if name == prefix {
+			ret = r.repl
+			break
+		}
+		prefix += "/"
+		if strings.HasPrefix(name, prefix) {
+			ret = r.repl + "-" + strings.TrimPrefix(name, prefix)
+		}
+	}
+	return strings.ReplaceAll(ret, "/", "-")
+}
+
+var rewriteNames = RewriteNames{}
+
+type Exclude map[string]bool
+
+func (e Exclude) String() string {
+	return ""
+}
+
+func (e Exclude) Set(v string) error {
+	e[v] = true
+	return nil
+}
+
+var excludes = make(Exclude)
+var excludeDeps = make(Exclude)
+var excludeSrcs = make(Exclude)
+
+type GoModule struct {
+	Dir string
+}
+
+type GoPackage struct {
+	Dir         string
+	ImportPath  string
+	Name        string
+	Imports     []string
+	GoFiles     []string
+	TestGoFiles []string
+	TestImports []string
+
+	Module *GoModule
+}
+
+func (g GoPackage) IsCommand() bool {
+	return g.Name == "main"
+}
+
+func (g GoPackage) BpModuleType() string {
+	if g.IsCommand() {
+		return "blueprint_go_binary"
+	}
+	return "bootstrap_go_package"
+}
+
+func (g GoPackage) BpName() string {
+	if g.IsCommand() {
+		return filepath.Base(g.ImportPath)
+	}
+	return rewriteNames.GoToBp(g.ImportPath)
+}
+
+func (g GoPackage) BpDeps(deps []string) []string {
+	var ret []string
+	for _, d := range deps {
+		// Ignore stdlib dependencies
+		if !strings.Contains(d, ".") {
+			continue
+		}
+		if _, ok := excludeDeps[d]; ok {
+			continue
+		}
+		name := rewriteNames.GoToBp(d)
+		ret = append(ret, name)
+	}
+	return ret
+}
+
+func (g GoPackage) BpSrcs(srcs []string) []string {
+	var ret []string
+	prefix, err := filepath.Rel(g.Module.Dir, g.Dir)
+	if err != nil {
+		panic(err)
+	}
+	for _, f := range srcs {
+		f = filepath.Join(prefix, f)
+		if _, ok := excludeSrcs[f]; ok {
+			continue
+		}
+		ret = append(ret, f)
+	}
+	return ret
+}
+
+// AllImports combines Imports and TestImports, as blueprint does not differentiate these.
+func (g GoPackage) AllImports() []string {
+	imports := append([]string(nil), g.Imports...)
+	imports = append(imports, g.TestImports...)
+
+	if len(imports) == 0 {
+		return nil
+	}
+
+	// Sort and de-duplicate
+	sort.Strings(imports)
+	j := 0
+	for i := 1; i < len(imports); i++ {
+		if imports[i] == imports[j] {
+			continue
+		}
+		j++
+		imports[j] = imports[i]
+	}
+	return imports[:j+1]
+}
+
+var bpTemplate = template.Must(template.New("bp").Parse(`
+{{.BpModuleType}} {
+    name: "{{.BpName}}",
+    {{- if not .IsCommand}}
+    pkgPath: "{{.ImportPath}}",
+    {{- end}}
+    {{- if .BpDeps .AllImports}}
+    deps: [
+        {{- range .BpDeps .AllImports}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
+    {{- if .BpSrcs .GoFiles}}
+    srcs: [
+        {{- range .BpSrcs .GoFiles}}
+        "{{.}}",
+        {{- end}}
+    ],
+    {{- end}}
+    {{- if .BpSrcs .TestGoFiles}}
+    testSrcs: [
+    	{{- range .BpSrcs .TestGoFiles}}
+        "{{.}}",
+       {{- end}}
+    ],
+    {{- end}}
+}
+`))
+
+func rerunForRegen(filename string) error {
+	buf, err := ioutil.ReadFile(filename)
+	if err != nil {
+		return err
+	}
+
+	scanner := bufio.NewScanner(bytes.NewBuffer(buf))
+
+	// Skip the first line in the file
+	for i := 0; i < 2; i++ {
+		if !scanner.Scan() {
+			if scanner.Err() != nil {
+				return scanner.Err()
+			} else {
+				return fmt.Errorf("unexpected EOF")
+			}
+		}
+	}
+
+	// Extract the old args from the file
+	line := scanner.Text()
+	if strings.HasPrefix(line, "// go2bp ") {
+		line = strings.TrimPrefix(line, "// go2bp ")
+	} else {
+		return fmt.Errorf("unexpected second line: %q", line)
+	}
+	args := strings.Split(line, " ")
+	lastArg := args[len(args)-1]
+	args = args[:len(args)-1]
+
+	// Append all current command line args except -regen <file> to the ones from the file
+	for i := 1; i < len(os.Args); i++ {
+		if os.Args[i] == "-regen" || os.Args[i] == "--regen" {
+			i++
+		} else {
+			args = append(args, os.Args[i])
+		}
+	}
+	args = append(args, lastArg)
+
+	cmd := os.Args[0] + " " + strings.Join(args, " ")
+	// Re-exec pom2bp with the new arguments
+	output, err := exec.Command("/bin/sh", "-c", cmd).Output()
+	if exitErr, _ := err.(*exec.ExitError); exitErr != nil {
+		return fmt.Errorf("failed to run %s\n%s", cmd, string(exitErr.Stderr))
+	} else if err != nil {
+		return err
+	}
+
+	return ioutil.WriteFile(filename, output, 0666)
+}
+
+func main() {
+	flag.Usage = func() {
+		fmt.Fprintf(os.Stderr, `go2bp, a tool to create Android.bp files from go modules
+
+The tool will extract the necessary information from Go files to create an Android.bp that can
+compile them. This needs to be run from the same directory as the go.mod file.
+
+Usage: %s [--rewrite <pkg-prefix>=<replace>] [-exclude <package>] [-regen <file>]
+
+  -rewrite <pkg-prefix>=<replace>
+     rewrite can be used to specify mappings between go package paths and Android.bp modules. The -rewrite
+     option can be specified multiple times. When determining the Android.bp module for a given Go
+     package, mappings are searched in the order they were specified. The first <pkg-prefix> matching
+     either the package directly, or as the prefix '<pkg-prefix>/' will be replaced with <replace>.
+     After all replacements are finished, all '/' characters are replaced with '-'.
+  -exclude <package>
+     Don't put the specified go package in the Android.bp file.
+  -exclude-deps <package>
+     Don't put the specified go package in the dependency lists.
+  -exclude-srcs <module>
+     Don't put the specified source files in srcs or testSrcs lists.
+  -regen <file>
+     Read arguments from <file> and overwrite it.
+
+`, os.Args[0])
+	}
+
+	var regen string
+
+	flag.Var(&excludes, "exclude", "Exclude go package")
+	flag.Var(&excludeDeps, "exclude-dep", "Exclude go package from deps")
+	flag.Var(&excludeSrcs, "exclude-src", "Exclude go file from source lists")
+	flag.Var(&rewriteNames, "rewrite", "Regex(es) to rewrite artifact names")
+	flag.StringVar(&regen, "regen", "", "Rewrite specified file")
+	flag.Parse()
+
+	if regen != "" {
+		err := rerunForRegen(regen)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, err)
+			os.Exit(1)
+		}
+		os.Exit(0)
+	}
+
+	if flag.NArg() != 0 {
+		fmt.Fprintf(os.Stderr, "Unused argument detected: %v\n", flag.Args())
+		os.Exit(1)
+	}
+
+	if _, err := os.Stat("go.mod"); err != nil {
+		fmt.Fprintln(os.Stderr, "go.mod file not found")
+		os.Exit(1)
+	}
+
+	cmd := exec.Command("go", "list", "-json", "./...")
+	output, err := cmd.Output()
+	if err != nil {
+		fmt.Fprintf(os.Stderr, "Failed to dump the go packages: %v\n", err)
+		os.Exit(1)
+	}
+	decoder := json.NewDecoder(bytes.NewReader(output))
+
+	pkgs := []GoPackage{}
+	for decoder.More() {
+		pkg := GoPackage{}
+		err := decoder.Decode(&pkg)
+		if err != nil {
+			fmt.Fprintf(os.Stderr, "Failed to parse json: %v\n", err)
+			os.Exit(1)
+		}
+		pkgs = append(pkgs, pkg)
+	}
+
+	buf := &bytes.Buffer{}
+
+	fmt.Fprintln(buf, "// Automatically generated with:")
+	fmt.Fprintln(buf, "// go2bp", strings.Join(proptools.ShellEscapeList(os.Args[1:]), " "))
+
+	for _, pkg := range pkgs {
+		if excludes[pkg.ImportPath] {
+			continue
+		}
+		if len(pkg.BpSrcs(pkg.GoFiles)) == 0 && len(pkg.BpSrcs(pkg.TestGoFiles)) == 0 {
+			continue
+		}
+		err := bpTemplate.Execute(buf, pkg)
+		if err != nil {
+			fmt.Fprintln(os.Stderr, "Error writing", pkg.Name, err)
+			os.Exit(1)
+		}
+	}
+
+	out, err := bpfix.Reformat(buf.String())
+	if err != nil {
+		fmt.Fprintln(os.Stderr, "Error formatting output", err)
+		os.Exit(1)
+	}
+
+	os.Stdout.WriteString(out)
+}
diff --git a/cmd/soong_build/main.go b/cmd/soong_build/main.go
index 70c8856..7abb67f 100644
--- a/cmd/soong_build/main.go
+++ b/cmd/soong_build/main.go
@@ -175,6 +175,9 @@
 	writeFakeNinjaFile(extraNinjaDeps, configuration.BuildDir())
 }
 
+// doChosenActivity runs Soong for a specific activity, like bp2build, queryview
+// or the actual Soong build for the build.ninja file. Returns the top level
+// output file of the specific activity.
 func doChosenActivity(configuration android.Config, extraNinjaDeps []string) string {
 	bazelConversionRequested := bp2buildMarker != ""
 	mixedModeBuild := configuration.BazelContext.BazelEnabled()
@@ -187,11 +190,7 @@
 		// Run the alternate pipeline of bp2build mutators and singleton to convert
 		// Blueprint to BUILD files before everything else.
 		runBp2Build(configuration, extraNinjaDeps)
-		if bp2buildMarker != "" {
-			return bp2buildMarker
-		} else {
-			return bootstrap.CmdlineArgs.OutFile
-		}
+		return bp2buildMarker
 	}
 
 	ctx := newContext(configuration, prepareBuildActions)
@@ -327,13 +326,13 @@
 
 	ninjaFileName := "build.ninja"
 	ninjaFile := shared.JoinPath(topDir, buildDir, ninjaFileName)
-	ninjaFileD := shared.JoinPath(topDir, buildDir, ninjaFileName)
+	ninjaFileD := shared.JoinPath(topDir, buildDir, ninjaFileName+".d")
 	// A workaround to create the 'nothing' ninja target so `m nothing` works,
 	// since bp2build runs without Kati, and the 'nothing' target is declared in
 	// a Makefile.
 	ioutil.WriteFile(ninjaFile, []byte("build nothing: phony\n  phony_output = true\n"), 0666)
 	ioutil.WriteFile(ninjaFileD,
-		[]byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFileName, extraNinjaDepsString)),
+		[]byte(fmt.Sprintf("%s: \\\n %s\n", ninjaFile, extraNinjaDepsString)),
 		0666)
 }
 
@@ -520,9 +519,14 @@
 		os.Exit(1)
 	}
 
-	if bp2buildMarker != "" {
-		touch(shared.JoinPath(topDir, bp2buildMarker))
-	} else {
-		writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
-	}
+	// Create an empty bp2build marker file.
+	touch(shared.JoinPath(topDir, bp2buildMarker))
+
+	// bp2build *always* writes a fake Ninja file containing just the nothing
+	// phony target if it ever re-runs. This allows bp2build to exit early with
+	// GENERATE_BAZEL_FILES=1 m nothing.
+	//
+	// If bp2build is invoked as part of an integrated mixed build, the fake
+	// build.ninja file will be rewritten later into the real file anyway.
+	writeFakeNinjaFile(extraNinjaDeps, codegenContext.Config().BuildDir())
 }
diff --git a/java/classpath_fragment.go b/java/classpath_fragment.go
index bc0416a..0e14d24 100644
--- a/java/classpath_fragment.go
+++ b/java/classpath_fragment.go
@@ -105,7 +105,7 @@
 }
 
 func (c *ClasspathFragmentBase) generateClasspathProtoBuildActions(ctx android.ModuleContext, jars []classpathJar) {
-	outputFilename := ctx.ModuleName() + ".pb"
+	outputFilename := strings.ToLower(c.classpathType.String()) + ".pb"
 	c.outputFilepath = android.PathForModuleOut(ctx, outputFilename).OutputPath
 	c.installDirPath = android.PathForModuleInstall(ctx, "etc", "classpaths")
 
diff --git a/java/droidstubs.go b/java/droidstubs.go
index 566f7e3..9531056 100644
--- a/java/droidstubs.go
+++ b/java/droidstubs.go
@@ -546,7 +546,8 @@
 			`\n` +
 			`If it is not possible to do so, there are workarounds:\n` +
 			`\n` +
-			`1. You can suppress the errors with @SuppressLint("<id>")\n`
+			`1. You can suppress the errors with @SuppressLint("<id>")\n` +
+			`   where the <id> is given in brackets in the error message above.\n`
 
 		if baselineFile.Valid() {
 			cmd.FlagWithInput("--baseline:api-lint ", baselineFile.Path())
diff --git a/java/platform_bootclasspath_test.go b/java/platform_bootclasspath_test.go
index d332f63..ed5549d 100644
--- a/java/platform_bootclasspath_test.go
+++ b/java/platform_bootclasspath_test.go
@@ -287,7 +287,7 @@
 	).RunTest(t)
 
 	p := result.Module("platform-bootclasspath", "android_common").(*platformBootclasspathModule)
-	android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base())
+	android.AssertStringEquals(t, "output filepath", "bootclasspath.pb", p.ClasspathFragmentBase.outputFilepath.Base())
 	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
 }
 
@@ -327,7 +327,7 @@
 		want := map[string][]string{
 			"LOCAL_MODULE":                {"platform-bootclasspath"},
 			"LOCAL_MODULE_CLASS":          {"ETC"},
-			"LOCAL_INSTALLED_MODULE_STEM": {"platform-bootclasspath.pb"},
+			"LOCAL_INSTALLED_MODULE_STEM": {"bootclasspath.pb"},
 			// Output and Install paths are tested separately in TestPlatformBootclasspath_ClasspathFragmentPaths
 		}
 
diff --git a/java/systemserver_classpath_fragment.go b/java/systemserver_classpath_fragment.go
index a72b3f6..9111c30 100644
--- a/java/systemserver_classpath_fragment.go
+++ b/java/systemserver_classpath_fragment.go
@@ -53,13 +53,7 @@
 
 func (p *platformSystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
 	global := dexpreopt.GetGlobalConfig(ctx)
-
-	jars := global.SystemServerJars
-	// TODO(satayev): split apex jars into separate configs.
-	for i := 0; i < global.UpdatableSystemServerJars.Len(); i++ {
-		jars = jars.Append(global.UpdatableSystemServerJars.Apex(i), global.UpdatableSystemServerJars.Jar(i))
-	}
-	return jars
+	return global.SystemServerJars
 }
 
 type SystemServerClasspathModule struct {
@@ -101,8 +95,12 @@
 }
 
 func (s *SystemServerClasspathModule) ClasspathFragmentToConfiguredJarList(ctx android.ModuleContext) android.ConfiguredJarList {
-	// TODO(satayev): populate with actual content
-	return android.EmptyConfiguredJarList()
+	global := dexpreopt.GetGlobalConfig(ctx)
+
+	// Only create configs for updatable boot jars. Non-updatable system server jars must be part of the
+	// platform_systemserverclasspath's classpath proto config to guarantee that they come before any
+	// updatable jars at runtime.
+	return global.UpdatableSystemServerJars.Filter(s.properties.Contents)
 }
 
 type systemServerClasspathFragmentContentDependencyTag struct {
diff --git a/java/systemserver_classpath_fragment_test.go b/java/systemserver_classpath_fragment_test.go
index 5272f27..9ad50dd 100644
--- a/java/systemserver_classpath_fragment_test.go
+++ b/java/systemserver_classpath_fragment_test.go
@@ -24,7 +24,7 @@
 	PrepareForTestWithJavaDefaultModules,
 )
 
-func TestPlatformSystemserverClasspathVariant(t *testing.T) {
+func TestPlatformSystemServerClasspathVariant(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -38,7 +38,7 @@
 	android.AssertIntEquals(t, "expect 1 variant", 1, len(variants))
 }
 
-func TestPlatformSystemserverClasspath_ClasspathFragmentPaths(t *testing.T) {
+func TestPlatformSystemServerClasspath_ClasspathFragmentPaths(t *testing.T) {
 	result := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -49,11 +49,11 @@
 	).RunTest(t)
 
 	p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
-	android.AssertStringEquals(t, "output filepath", p.Name()+".pb", p.ClasspathFragmentBase.outputFilepath.Base())
+	android.AssertStringEquals(t, "output filepath", "systemserverclasspath.pb", p.ClasspathFragmentBase.outputFilepath.Base())
 	android.AssertPathRelativeToTopEquals(t, "install filepath", "out/soong/target/product/test_device/system/etc/classpaths", p.ClasspathFragmentBase.installDirPath)
 }
 
-func TestPlatformSystemserverClasspathModule_AndroidMkEntries(t *testing.T) {
+func TestPlatformSystemServerClasspathModule_AndroidMkEntries(t *testing.T) {
 	preparer := android.GroupFixturePreparers(
 		prepareForTestWithSystemServerClasspath,
 		android.FixtureWithRootAndroidBp(`
@@ -78,8 +78,8 @@
 		want := map[string][]string{
 			"LOCAL_MODULE":                {"platform-systemserverclasspath"},
 			"LOCAL_MODULE_CLASS":          {"ETC"},
-			"LOCAL_INSTALLED_MODULE_STEM": {"platform-systemserverclasspath.pb"},
-			// Output and Install paths are tested separately in TestSystemserverClasspath_ClasspathFragmentPaths
+			"LOCAL_INSTALLED_MODULE_STEM": {"systemserverclasspath.pb"},
+			// Output and Install paths are tested separately in TestPlatformSystemServerClasspath_ClasspathFragmentPaths
 		}
 
 		p := result.Module("platform-systemserverclasspath", "android_common").(*platformSystemServerClasspathModule)
@@ -96,7 +96,7 @@
 	})
 }
 
-func TestSystemserverclasspathFragmentWithoutContents(t *testing.T) {
+func TestSystemServerClasspathFragmentWithoutContents(t *testing.T) {
 	prepareForTestWithSystemServerClasspath.
 		ExtendWithErrorHandler(android.FixtureExpectsAtLeastOneErrorMatchingPattern(
 			`\Qempty contents are not allowed\E`)).
diff --git a/sh/sh_binary.go b/sh/sh_binary.go
index 42d5680..4805846 100644
--- a/sh/sh_binary.go
+++ b/sh/sh_binary.go
@@ -104,6 +104,12 @@
 	Recovery_available *bool
 }
 
+// Test option struct.
+type TestOptions struct {
+	// If the test is a hostside(no device required) unittest that shall be run during presubmit check.
+	Unit_test *bool
+}
+
 type TestProperties struct {
 	// list of compatibility suites (for example "cts", "vts") that the module should be
 	// installed into.
@@ -143,6 +149,9 @@
 	// list of device library modules that should be installed alongside the test.
 	// Only available for host sh_test modules.
 	Data_device_libs []string `android:"path,arch_variant"`
+
+	// Test options.
+	Test_options TestOptions
 }
 
 type ShBinary struct {
@@ -440,6 +449,9 @@
 					dir := strings.TrimSuffix(s.dataModules[relPath].String(), relPath)
 					entries.AddStrings("LOCAL_TEST_DATA", dir+":"+relPath)
 				}
+				if Bool(s.testProperties.Test_options.Unit_test) {
+					entries.SetBool("LOCAL_IS_UNIT_TEST", true)
+				}
 			},
 		},
 	}}
diff --git a/sh/sh_binary_test.go b/sh/sh_binary_test.go
index 9e7e594..20317d8 100644
--- a/sh/sh_binary_test.go
+++ b/sh/sh_binary_test.go
@@ -3,6 +3,7 @@
 import (
 	"os"
 	"path/filepath"
+	"strconv"
 	"testing"
 
 	"android/soong/android"
@@ -148,6 +149,9 @@
 				"testdata/data1",
 				"testdata/sub/data2",
 			],
+			test_options: {
+				unit_test: true,
+			},
 		}
 	`)
 
@@ -156,6 +160,9 @@
 	if !mod.Host() {
 		t.Errorf("host bit is not set for a sh_test_host module.")
 	}
+	entries := android.AndroidMkEntriesForTest(t, ctx, mod)[0]
+	actualData, _ := strconv.ParseBool(entries.EntryMap["LOCAL_IS_UNIT_TEST"][0])
+	android.AssertBoolEquals(t, "LOCAL_IS_UNIT_TEST", true, actualData)
 }
 
 func TestShTestHost_dataDeviceModules(t *testing.T) {
diff --git a/tests/bootstrap_test.sh b/tests/bootstrap_test.sh
index 42363e9..8c8dc82 100755
--- a/tests/bootstrap_test.sh
+++ b/tests/bootstrap_test.sh
@@ -493,6 +493,21 @@
   [[ -e out/soong/workspace ]] || fail "Bazel workspace not created"
 }
 
+function test_bp2build_generates_fake_ninja_file {
+  setup
+  create_mock_bazel
+
+  run_bp2build
+
+  if [[ ! -f "./out/soong/build.ninja" ]]; then
+    fail "./out/soong/build.ninja was not generated"
+  fi
+
+  if ! grep "build nothing: phony" "./out/soong/build.ninja"; then
+    fail "missing phony nothing target in out/soong/build.ninja"
+  fi
+}
+
 function test_bp2build_add_android_bp {
   setup
 
@@ -678,6 +693,7 @@
 test_soong_build_rerun_iff_environment_changes
 test_dump_json_module_graph
 test_bp2build_smoke
+test_bp2build_generates_fake_ninja_file
 test_bp2build_null_build
 test_bp2build_add_android_bp
 test_bp2build_add_to_glob
diff --git a/ui/build/soong.go b/ui/build/soong.go
index a41dbe1..cd645eb 100644
--- a/ui/build/soong.go
+++ b/ui/build/soong.go
@@ -155,9 +155,9 @@
 			Outputs: []string{bp2BuildMarkerFile},
 			Args:    bp2buildArgs,
 		}
-		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{
-			bp2buildInvocation,
-			mainSoongBuildInvocation,
+		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{bp2buildInvocation}
+		if config.bazelBuildMode() == mixedBuild {
+			args.PrimaryBuilderInvocations = append(args.PrimaryBuilderInvocations, mainSoongBuildInvocation)
 		}
 	} else {
 		args.PrimaryBuilderInvocations = []bootstrap.PrimaryBuilderInvocation{mainSoongBuildInvocation}