Refactor how bp2build gets arch-specific props.

Then plumb them to LabelAttribute.

This refactoring is required because the previous implementation did not
handle properties in shards other than the first one (e.g.
version_script) well. In addition, it also makes the code paths between
bp2build and analysis more similar.

Bug: 186650430
Test: Presubmits.
Change-Id: Ic4393e8ae47f4e88816bf45c89399efd61494d22
diff --git a/android/arch.go b/android/arch.go
index 74fef3d..c1b2c33 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -1014,35 +1014,19 @@
 	base.customizableProperties = m.GetProperties()
 }
 
-// appendProperties squashes properties from the given field of the given src property struct
-// into the dst property struct.  Returns the reflect.Value of the field in the src property
-// struct to be used for further appendProperties calls on fields of that property struct.
-func (m *ModuleBase) appendProperties(ctx BottomUpMutatorContext,
-	dst interface{}, src reflect.Value, field, srcPrefix string) reflect.Value {
-
-	// Step into non-nil pointers to structs in the src value.
-	if src.Kind() == reflect.Ptr {
-		if src.IsNil() {
-			return src
-		}
-		src = src.Elem()
-	}
-
-	// Find the requested field in the src struct.
-	src = src.FieldByName(field)
-	if !src.IsValid() {
-		ctx.ModuleErrorf("field %q does not exist", srcPrefix)
-		return src
-	}
-
-	// Save the value of the field in the src struct to return.
-	ret := src
-
+func maybeBlueprintEmbed(src reflect.Value) reflect.Value {
 	// If the value of the field is a struct (as opposed to a pointer to a struct) then step
 	// into the BlueprintEmbed field.
 	if src.Kind() == reflect.Struct {
-		src = src.FieldByName("BlueprintEmbed")
+		return src.FieldByName("BlueprintEmbed")
+	} else {
+		return src
 	}
+}
+
+// Merges the property struct in srcValue into dst.
+func mergePropertyStruct(ctx BaseMutatorContext, dst interface{}, srcValue reflect.Value) {
+	src := maybeBlueprintEmbed(srcValue).Interface()
 
 	// order checks the `android:"variant_prepend"` tag to handle properties where the
 	// arch-specific value needs to come before the generic value, for example for lists of
@@ -1058,7 +1042,7 @@
 	}
 
 	// Squash the located property struct into the destination property struct.
-	err := proptools.ExtendMatchingProperties([]interface{}{dst}, src.Interface(), nil, order)
+	err := proptools.ExtendMatchingProperties([]interface{}{dst}, src, nil, order)
 	if err != nil {
 		if propertyErr, ok := err.(*proptools.ExtendPropertyError); ok {
 			ctx.PropertyErrorf(propertyErr.Property, "%s", propertyErr.Err.Error())
@@ -1066,8 +1050,29 @@
 			panic(err)
 		}
 	}
+}
 
-	return ret
+// Returns the immediate child of the input property struct that corresponds to
+// the sub-property "field".
+func getChildPropertyStruct(ctx BaseMutatorContext,
+	src reflect.Value, field, userFriendlyField string) reflect.Value {
+
+	// Step into non-nil pointers to structs in the src value.
+	if src.Kind() == reflect.Ptr {
+		if src.IsNil() {
+			return src
+		}
+		src = src.Elem()
+	}
+
+	// Find the requested field in the src struct.
+	src = src.FieldByName(proptools.FieldNameForProperty(field))
+	if !src.IsValid() {
+		ctx.ModuleErrorf("field %q does not exist", userFriendlyField)
+		return src
+	}
+
+	return src
 }
 
 // Squash the appropriate OS-specific property structs into the matching top level property structs
@@ -1094,7 +1099,8 @@
 			if os.Class == Host {
 				field := "Host"
 				prefix := "target.host"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				hostProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, hostProperties)
 			}
 
 			// Handle target OS generalities of the form:
@@ -1106,13 +1112,15 @@
 			if os.Linux() {
 				field := "Linux"
 				prefix := "target.linux"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				linuxProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, linuxProperties)
 			}
 
 			if os.Bionic() {
 				field := "Bionic"
 				prefix := "target.bionic"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				bionicProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, bionicProperties)
 			}
 
 			// Handle target OS properties in the form:
@@ -1129,12 +1137,14 @@
 			// },
 			field := os.Field
 			prefix := "target." + os.Name
-			m.appendProperties(ctx, genProps, targetProp, field, prefix)
+			osProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+			mergePropertyStruct(ctx, genProps, osProperties)
 
 			if os.Class == Host && os != Windows {
 				field := "Not_windows"
 				prefix := "target.not_windows"
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
+				notWindowsProperties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+				mergePropertyStruct(ctx, genProps, notWindowsProperties)
 			}
 
 			// Handle 64-bit device properties in the form:
@@ -1154,17 +1164,189 @@
 				if ctx.Config().Android64() {
 					field := "Android64"
 					prefix := "target.android64"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+					android64Properties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+					mergePropertyStruct(ctx, genProps, android64Properties)
 				} else {
 					field := "Android32"
 					prefix := "target.android32"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
+					android32Properties := getChildPropertyStruct(ctx, targetProp, field, prefix)
+					mergePropertyStruct(ctx, genProps, android32Properties)
 				}
 			}
 		}
 	}
 }
 
+// Returns the struct containing the properties specific to the given
+// architecture type. These look like this in Blueprint files:
+// arch: {
+//     arm64: {
+//         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 getArchTypeStruct(ctx BaseMutatorContext, archProperties interface{}, archType ArchType) reflect.Value {
+	archPropValues := reflect.ValueOf(archProperties).Elem()
+	archProp := archPropValues.FieldByName("Arch").Elem()
+	prefix := "arch." + archType.Name
+	archStruct := getChildPropertyStruct(ctx, archProp, archType.Name, prefix)
+	return archStruct
+}
+
+// Returns the struct containing the properties specific to a given multilib
+// value. These look like this in the Blueprint file:
+// multilib: {
+//     lib32: {
+//         key: value,
+//     },
+// },
+func getMultilibStruct(ctx BaseMutatorContext, archProperties interface{}, archType ArchType) reflect.Value {
+	archPropValues := reflect.ValueOf(archProperties).Elem()
+	multilibProp := archPropValues.FieldByName("Multilib").Elem()
+	multilibProperties := getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
+	return multilibProperties
+}
+
+// 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 {
+	result := make([]reflect.Value, 0)
+	archPropValues := reflect.ValueOf(archProperties).Elem()
+
+	targetProp := archPropValues.FieldByName("Target").Elem()
+
+	archType := arch.ArchType
+
+	if arch.ArchType != Common {
+		archStruct := getArchTypeStruct(ctx, archProperties, arch.ArchType)
+		result = append(result, archStruct)
+
+		// Handle arch-variant-specific properties in the form:
+		// arch: {
+		//     arm: {
+		//         variant: {
+		//             key: value,
+		//         },
+		//     },
+		// },
+		v := variantReplacer.Replace(arch.ArchVariant)
+		if v != "" {
+			prefix := "arch." + archType.Name + "." + v
+			variantProperties := getChildPropertyStruct(ctx, archStruct, v, prefix)
+			result = append(result, variantProperties)
+		}
+
+		// Handle cpu-variant-specific properties in the form:
+		// arch: {
+		//     arm: {
+		//         variant: {
+		//             key: value,
+		//         },
+		//     },
+		// },
+		if arch.CpuVariant != arch.ArchVariant {
+			c := variantReplacer.Replace(arch.CpuVariant)
+			if c != "" {
+				prefix := "arch." + archType.Name + "." + c
+				cpuVariantProperties := getChildPropertyStruct(ctx, archStruct, c, prefix)
+				result = append(result, cpuVariantProperties)
+			}
+		}
+
+		// Handle arch-feature-specific properties in the form:
+		// arch: {
+		//     arm: {
+		//         feature: {
+		//             key: value,
+		//         },
+		//     },
+		// },
+		for _, feature := range arch.ArchFeatures {
+			prefix := "arch." + archType.Name + "." + feature
+			featureProperties := getChildPropertyStruct(ctx, archStruct, feature, prefix)
+			result = append(result, featureProperties)
+		}
+
+		multilibProperties := getMultilibStruct(ctx, archProperties, archType)
+		result = append(result, multilibProperties)
+
+		// Handle combined OS-feature and arch specific properties in the form:
+		// target: {
+		//     bionic_x86: {
+		//         key: value,
+		//     },
+		// }
+		if os.Linux() {
+			field := "Linux_" + arch.ArchType.Name
+			userFriendlyField := "target.linux_" + arch.ArchType.Name
+			linuxProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, linuxProperties)
+		}
+
+		if os.Bionic() {
+			field := "Bionic_" + archType.Name
+			userFriendlyField := "target.bionic_" + archType.Name
+			bionicProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, bionicProperties)
+		}
+
+		// Handle combined OS and arch specific properties in the form:
+		// target: {
+		//     linux_glibc_x86: {
+		//         key: value,
+		//     },
+		//     linux_glibc_arm: {
+		//         key: value,
+		//     },
+		//     android_arm {
+		//         key: value,
+		//     },
+		//     android_x86 {
+		//         key: value,
+		//     },
+		// },
+		field := os.Field + "_" + archType.Name
+		userFriendlyField := "target." + os.Name + "_" + archType.Name
+		osArchProperties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+		result = append(result, osArchProperties)
+	}
+
+	// Handle arm on x86 properties in the form:
+	// target {
+	//     arm_on_x86 {
+	//         key: value,
+	//     },
+	//     arm_on_x86_64 {
+	//         key: value,
+	//     },
+	// },
+	if os.Class == Device {
+		if arch.ArchType == X86 && (hasArmAbi(arch) ||
+			hasArmAndroidArch(ctx.Config().Targets[Android])) {
+			field := "Arm_on_x86"
+			userFriendlyField := "target.arm_on_x86"
+			armOnX86Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, armOnX86Properties)
+		}
+		if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
+			hasArmAndroidArch(ctx.Config().Targets[Android])) {
+			field := "Arm_on_x86_64"
+			userFriendlyField := "target.arm_on_x86_64"
+			armOnX8664Properties := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField)
+			result = append(result, armOnX8664Properties)
+		}
+		if os == Android && nativeBridgeEnabled {
+			userFriendlyField := "Native_bridge"
+			prefix := "target.native_bridge"
+			nativeBridgeProperties := getChildPropertyStruct(ctx, targetProp, userFriendlyField, prefix)
+			result = append(result, nativeBridgeProperties)
+		}
+	}
+
+	return result
+}
+
 // Squash the appropriate arch-specific property structs into the matching top level property
 // structs based on the CompileTarget value that was annotated on the variant.
 func (m *ModuleBase) setArchProperties(ctx BottomUpMutatorContext) {
@@ -1176,144 +1358,15 @@
 		if m.archProperties[i] == nil {
 			continue
 		}
-		for _, archProperties := range m.archProperties[i] {
-			archPropValues := reflect.ValueOf(archProperties).Elem()
 
-			archProp := archPropValues.FieldByName("Arch").Elem()
-			multilibProp := archPropValues.FieldByName("Multilib").Elem()
-			targetProp := archPropValues.FieldByName("Target").Elem()
+		propStructs := make([]reflect.Value, 0)
+		for _, archProperty := range m.archProperties[i] {
+			propStructShard := getArchProperties(ctx, archProperty, arch, os, m.Target().NativeBridge == NativeBridgeEnabled)
+			propStructs = append(propStructs, propStructShard...)
+		}
 
-			// Handle arch-specific properties in the form:
-			// arch: {
-			//     arm64: {
-			//         key: value,
-			//     },
-			// },
-			t := arch.ArchType
-
-			if arch.ArchType != Common {
-				field := proptools.FieldNameForProperty(t.Name)
-				prefix := "arch." + t.Name
-				archStruct := m.appendProperties(ctx, genProps, archProp, field, prefix)
-
-				// Handle arch-variant-specific properties in the form:
-				// arch: {
-				//     variant: {
-				//         key: value,
-				//     },
-				// },
-				v := variantReplacer.Replace(arch.ArchVariant)
-				if v != "" {
-					field := proptools.FieldNameForProperty(v)
-					prefix := "arch." + t.Name + "." + v
-					m.appendProperties(ctx, genProps, archStruct, field, prefix)
-				}
-
-				// Handle cpu-variant-specific properties in the form:
-				// arch: {
-				//     variant: {
-				//         key: value,
-				//     },
-				// },
-				if arch.CpuVariant != arch.ArchVariant {
-					c := variantReplacer.Replace(arch.CpuVariant)
-					if c != "" {
-						field := proptools.FieldNameForProperty(c)
-						prefix := "arch." + t.Name + "." + c
-						m.appendProperties(ctx, genProps, archStruct, field, prefix)
-					}
-				}
-
-				// Handle arch-feature-specific properties in the form:
-				// arch: {
-				//     feature: {
-				//         key: value,
-				//     },
-				// },
-				for _, feature := range arch.ArchFeatures {
-					field := proptools.FieldNameForProperty(feature)
-					prefix := "arch." + t.Name + "." + feature
-					m.appendProperties(ctx, genProps, archStruct, field, prefix)
-				}
-
-				// Handle multilib-specific properties in the form:
-				// multilib: {
-				//     lib32: {
-				//         key: value,
-				//     },
-				// },
-				field = proptools.FieldNameForProperty(t.Multilib)
-				prefix = "multilib." + t.Multilib
-				m.appendProperties(ctx, genProps, multilibProp, field, prefix)
-			}
-
-			// Handle combined OS-feature and arch specific properties in the form:
-			// target: {
-			//     bionic_x86: {
-			//         key: value,
-			//     },
-			// }
-			if os.Linux() && arch.ArchType != Common {
-				field := "Linux_" + arch.ArchType.Name
-				prefix := "target.linux_" + arch.ArchType.Name
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			if os.Bionic() && arch.ArchType != Common {
-				field := "Bionic_" + t.Name
-				prefix := "target.bionic_" + t.Name
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			// Handle combined OS and arch specific properties in the form:
-			// target: {
-			//     linux_glibc_x86: {
-			//         key: value,
-			//     },
-			//     linux_glibc_arm: {
-			//         key: value,
-			//     },
-			//     android_arm {
-			//         key: value,
-			//     },
-			//     android_x86 {
-			//         key: value,
-			//     },
-			// },
-			if arch.ArchType != Common {
-				field := os.Field + "_" + t.Name
-				prefix := "target." + os.Name + "_" + t.Name
-				m.appendProperties(ctx, genProps, targetProp, field, prefix)
-			}
-
-			// Handle arm on x86 properties in the form:
-			// target {
-			//     arm_on_x86 {
-			//         key: value,
-			//     },
-			//     arm_on_x86_64 {
-			//         key: value,
-			//     },
-			// },
-			if os.Class == Device {
-				if arch.ArchType == X86 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android])) {
-					field := "Arm_on_x86"
-					prefix := "target.arm_on_x86"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-				if arch.ArchType == X86_64 && (hasArmAbi(arch) ||
-					hasArmAndroidArch(ctx.Config().Targets[Android])) {
-					field := "Arm_on_x86_64"
-					prefix := "target.arm_on_x86_64"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-				if os == Android && m.Target().NativeBridge == NativeBridgeEnabled {
-					field := "Native_bridge"
-					prefix := "target.native_bridge"
-					m.appendProperties(ctx, genProps, targetProp, field, prefix)
-				}
-			}
+		for _, propStruct := range propStructs {
+			mergePropertyStruct(ctx, genProps, propStruct)
 		}
 	}
 }
@@ -1810,7 +1863,7 @@
 // For example: `arch: { x86: { Foo: ["bar"] } }, multilib: { lib32: {` Foo: ["baz"] } }`
 // will result in `Foo: ["bar", "baz"]` being returned for architecture x86, if the given
 // propertyset contains `Foo []string`.
-func (m *ModuleBase) GetArchProperties(propertySet interface{}) map[ArchType]interface{} {
+func (m *ModuleBase) GetArchProperties(ctx BaseMutatorContext, propertySet interface{}) map[ArchType]interface{} {
 	// Return value of the arch types to the prop values for that arch.
 	archToProp := map[ArchType]interface{}{}
 
@@ -1819,27 +1872,47 @@
 		return archToProp
 	}
 
-	// For each arch (x86, arm64, etc.),
+	dstType := reflect.ValueOf(propertySet).Type()
+	var archProperties []interface{}
+
+	// First find the property set in the module that corresponds to the requested
+	// one. m.archProperties[i] corresponds to m.generalProperties[i].
+	for i, generalProp := range m.generalProperties {
+		srcType := reflect.ValueOf(generalProp).Type()
+		if srcType == dstType {
+			archProperties = m.archProperties[i]
+			break
+		}
+	}
+
+	if archProperties == nil {
+		// This module does not have the property set requested
+		return archToProp
+	}
+
+	// For each arch type (x86, arm64, etc.)
 	for _, arch := range ArchTypeList() {
-		// Find arch-specific properties matching that property set type. For example, any
-		// matching properties under `arch { x86 { ... } }`.
-		archPropertySet := m.getArchPropertySet(propertySet, arch)
-
-		// Find multilib-specific properties matching that property set type. For example, any
-		// matching properties under `multilib { lib32 { ... } }` for x86, as x86 is 32-bit.
-		multilibPropertySet := m.getMultilibPropertySet(propertySet, arch)
-
-		// Append the multilibPropertySet to archPropertySet. This combines the
-		// arch and multilib properties into a single property struct.
-		err := proptools.ExtendMatchingProperties([]interface{}{archPropertySet}, multilibPropertySet, nil, proptools.OrderAppend)
-		if err != nil {
-			// archPropertySet and multilibPropertySet must be of the same type, or
-			// something horrible went wrong.
-			panic(err)
+		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
+		// Iterate over ever shard and extract a struct with the same type as the
+		// input one that contains the data specific to that arch.
+		propertyStructs := make([]reflect.Value, 0)
+		for _, archProperty := range archProperties {
+			archTypeStruct := getArchTypeStruct(ctx, archProperty, arch)
+			multilibStruct := getMultilibStruct(ctx, archProperty, arch)
+			propertyStructs = append(propertyStructs, archTypeStruct, multilibStruct)
 		}
 
-		archToProp[arch] = archPropertySet
+		// 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)
+		}
+
+		archToProp[arch] = value
 	}
+
 	return archToProp
 }
 
diff --git a/bazel/properties.go b/bazel/properties.go
index 12dfcaf..a71b12b 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -229,11 +229,47 @@
 
 // Represents an attribute whose value is a single label
 type LabelAttribute struct {
-	Value Label
+	Value  Label
+	X86    Label
+	X86_64 Label
+	Arm    Label
+	Arm64  Label
 }
 
-func (LabelAttribute) HasConfigurableValues() bool {
-	return false
+func (attr *LabelAttribute) GetValueForArch(arch string) Label {
+	switch arch {
+	case ARCH_ARM:
+		return attr.Arm
+	case ARCH_ARM64:
+		return attr.Arm64
+	case ARCH_X86:
+		return attr.X86
+	case ARCH_X86_64:
+		return attr.X86_64
+	case CONDITIONS_DEFAULT:
+		return attr.Value
+	default:
+		panic("Invalid arch type")
+	}
+}
+
+func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
+	switch arch {
+	case ARCH_ARM:
+		attr.Arm = value
+	case ARCH_ARM64:
+		attr.Arm64 = value
+	case ARCH_X86:
+		attr.X86 = value
+	case ARCH_X86_64:
+		attr.X86_64 = value
+	default:
+		panic("Invalid arch type")
+	}
+}
+
+func (attr LabelAttribute) HasConfigurableValues() bool {
+	return attr.Arm.Label != "" || attr.Arm64.Label != "" || attr.X86.Label != "" || attr.X86_64.Label != ""
 }
 
 // Arch-specific label_list typed Bazel attribute values. This should correspond
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 050679b..95a2747 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -59,22 +59,30 @@
 func prettyPrintAttribute(v bazel.Attribute, indent int) (string, error) {
 	var value reflect.Value
 	var archSelects, osSelects selects
+	var defaultSelectValue string
 	switch list := v.(type) {
 	case bazel.StringListAttribute:
 		value, archSelects, osSelects = getStringListValues(list)
+		defaultSelectValue = "[]"
 	case bazel.LabelListAttribute:
 		value, archSelects, osSelects = getLabelListValues(list)
+		defaultSelectValue = "[]"
 	case bazel.LabelAttribute:
 		value, archSelects, osSelects = getLabelValue(list)
+		defaultSelectValue = "None"
 	default:
 		return "", fmt.Errorf("Not a supported Bazel attribute type: %s", v)
 	}
 
-	ret, err := prettyPrint(value, indent)
-	if err != nil {
-		return ret, err
-	}
+	ret := ""
+	if value.Kind() != reflect.Invalid {
+		s, err := prettyPrint(value, indent)
+		if err != nil {
+			return ret, err
+		}
 
+		ret += s
+	}
 	// Convenience function to append selects components to an attribute value.
 	appendSelects := func(selectsData selects, defaultValue, s string) (string, error) {
 		selectMap, err := prettyPrintSelectMap(selectsData, defaultValue, indent)
@@ -89,12 +97,12 @@
 		return s, nil
 	}
 
-	ret, err = appendSelects(archSelects, "[]", ret)
+	ret, err := appendSelects(archSelects, defaultSelectValue, ret)
 	if err != nil {
 		return "", err
 	}
 
-	ret, err = appendSelects(osSelects, "[]", ret)
+	ret, err = appendSelects(osSelects, defaultSelectValue, ret)
 	return ret, err
 }
 
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 452f6ed..b925682 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -142,7 +142,7 @@
 
 		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
 
-		for arch, props := range m.GetArchProperties(&customProps{}) {
+		for arch, props := range m.GetArchProperties(ctx, &customProps{}) {
 			if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
 				paths.SetValueForArch(arch.Name, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
 			}
diff --git a/cc/bp2build.go b/cc/bp2build.go
index d52b817..1433f6f 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -58,7 +58,7 @@
 		}
 	}
 
-	for _, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+	for _, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
 		// arch specific linker props
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			allDeps = append(allDeps, baseLinkerProps.Header_libs...)
@@ -198,7 +198,7 @@
 		copts.Value = append(copts.Value, includeFlag("."))
 	}
 
-	for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
+	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 			// If there's arch specific srcs or exclude_srcs, generate a select entry for it.
 			// TODO(b/186153868): do this for OS specific srcs and exclude_srcs too.
@@ -215,7 +215,7 @@
 
 	// After going through all archs, delete the duplicate files in the arch
 	// values that are already in the base srcs.Value.
-	for arch, props := range module.GetArchProperties(&BaseCompilerProperties{}) {
+	for arch, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
 		if _, ok := props.(*BaseCompilerProperties); ok {
 			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value))
 		}
@@ -269,15 +269,13 @@
 			linkopts.Value = baseLinkerProps.Ldflags
 
 			if baseLinkerProps.Version_script != nil {
-				versionScript = bazel.LabelAttribute{
-					Value: android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script),
-				}
+				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
 			}
 			break
 		}
 	}
 
-	for arch, p := range module.GetArchProperties(&BaseLinkerProperties{}) {
+	for arch, p := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
 		if baseLinkerProps, ok := p.(*BaseLinkerProperties); ok {
 			libs := baseLinkerProps.Header_libs
 			libs = append(libs, baseLinkerProps.Export_header_lib_headers...)
@@ -286,6 +284,10 @@
 			libs = android.SortedUniqueStrings(libs)
 			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
 			linkopts.SetValueForArch(arch.Name, baseLinkerProps.Ldflags)
+			if baseLinkerProps.Version_script != nil {
+				versionScript.SetValueForArch(arch.Name,
+					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
+			}
 		}
 	}
 
@@ -343,7 +345,7 @@
 	includeDirs = append(includeDirs, libraryDecorator.flagExporter.Properties.Export_include_dirs...)
 	includeDirsAttribute := bazel.MakeStringListAttribute(includeDirs)
 
-	for arch, props := range module.GetArchProperties(&FlagExporterProperties{}) {
+	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...)