Use maps in bazel *attribute types

This is to simplify the process of resolving label + exclude labels
across the various configuration axes we have and across the various
properties/modules that use this behavior.

Test: ci/bp2build.sh && ci/mixed_droid.sh
Change-Id: I8efae3e75ddb365384f5caaf5bb504a5206618d3
diff --git a/android/arch.go b/android/arch.go
index 9ff439c..2e4a308 100644
--- a/android/arch.go
+++ b/android/arch.go
@@ -15,6 +15,7 @@
 package android
 
 import (
+	"android/soong/bazel"
 	"encoding"
 	"fmt"
 	"reflect"
@@ -897,7 +898,7 @@
 
 			// Add the OS/Arch combinations, e.g. "android_arm64".
 			for _, archType := range osArchTypeMap[os] {
-				targets = append(targets, GetCompoundTargetName(os, archType))
+				targets = append(targets, GetCompoundTargetField(os, archType))
 
 				// Also add the special "linux_<arch>" and "bionic_<arch>" property structs.
 				if os.Linux() {
@@ -1217,7 +1218,7 @@
 	return getChildPropertyStruct(ctx, multilibProp, archType.Multilib, "multilib."+archType.Multilib)
 }
 
-func GetCompoundTargetName(os OsType, arch ArchType) string {
+func GetCompoundTargetField(os OsType, arch ArchType) string {
 	return os.Field + "_" + arch.Name
 }
 
@@ -1327,7 +1328,7 @@
 		//         key: value,
 		//     },
 		// },
-		field := GetCompoundTargetName(os, archType)
+		field := GetCompoundTargetField(os, archType)
 		userFriendlyField := "target." + os.Name + "_" + archType.Name
 		if osArchProperties, ok := getChildPropertyStruct(ctx, targetProp, field, userFriendlyField); ok {
 			result = append(result, osArchProperties)
@@ -1882,27 +1883,38 @@
 	PropertyErrorf(property, fmt string, args ...interface{})
 }
 
-// GetArchProperties returns a map of architectures to the values of the
-// properties of the 'propertySet' struct that are specific to that architecture.
+// ArchVariantProperties represents a map of arch-variant config strings to a property interface{}.
+type ArchVariantProperties map[string]interface{}
+
+// ConfigurationAxisToArchVariantProperties represents a map of bazel.ConfigurationAxis to
+// ArchVariantProperties, such that each independent arch-variant axis maps to the
+// configs/properties for that axis.
+type ConfigurationAxisToArchVariantProperties map[bazel.ConfigurationAxis]ArchVariantProperties
+
+// GetArchVariantProperties returns a ConfigurationAxisToArchVariantProperties where the
+// arch-variant properties correspond to the values of the properties of the 'propertySet' struct
+// that are specific to that axis/configuration. Each axis is independent, containing
+// non-overlapping configs that correspond to the various "arch-variant" support, at this time:
+//    arches (including multilib)
+//    oses
+//    arch+os combinations
 //
-// For example, passing a struct { Foo bool, Bar string } will return an
-// interface{} that can be type asserted back into the same struct, containing
-// the arch specific property value specified by the module if defined.
+// For example, passing a struct { Foo bool, Bar string } will return an interface{} that can be
+// type asserted back into the same struct, containing the config-specific property value specified
+// by the module if defined.
 //
 // Arch-specific properties may come from an arch stanza or a multilib stanza; properties
 // in these stanzas are combined.
 // 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`.
-//
-// Implemented in a way very similar to GetTargetProperties().
-func (m *ModuleBase) GetArchProperties(ctx ArchVariantContext, propertySet interface{}) map[ArchType]interface{} {
+func (m *ModuleBase) GetArchVariantProperties(ctx ArchVariantContext, propertySet interface{}) ConfigurationAxisToArchVariantProperties {
 	// Return value of the arch types to the prop values for that arch.
-	archToProp := map[ArchType]interface{}{}
+	axisToProps := ConfigurationAxisToArchVariantProperties{}
 
 	// Nothing to do for non-arch-specific modules.
 	if !m.ArchSpecific() {
-		return archToProp
+		return axisToProps
 	}
 
 	dstType := reflect.ValueOf(propertySet).Type()
@@ -1920,9 +1932,10 @@
 
 	if archProperties == nil {
 		// This module does not have the property set requested
-		return archToProp
+		return axisToProps
 	}
 
+	archToProp := ArchVariantProperties{}
 	// For each arch type (x86, arm64, etc.)
 	for _, arch := range ArchTypeList() {
 		// Arch properties are sometimes sharded (see createArchPropTypeDesc() ).
@@ -1948,10 +1961,30 @@
 			mergePropertyStruct(ctx, value, propertyStruct)
 		}
 
-		archToProp[arch] = value
+		archToProp[arch.Name] = value
 	}
+	axisToProps[bazel.ArchConfigurationAxis] = archToProp
 
-	return archToProp
+	osToProp := ArchVariantProperties{}
+	archOsToProp := ArchVariantProperties{}
+	// For android, linux, ...
+	for _, os := range osTypeList {
+		if os == CommonOS {
+			// It looks like this OS value is not used in Blueprint files
+			continue
+		}
+		osToProp[os.Name] = getTargetStruct(ctx, propertySet, archProperties, os.Field)
+		// For arm, x86, ...
+		for _, arch := range osArchTypeMap[os] {
+			targetField := GetCompoundTargetField(os, arch)
+			targetName := fmt.Sprintf("%s_%s", os.Name, arch.Name)
+			archOsToProp[targetName] = getTargetStruct(ctx, propertySet, archProperties, targetField)
+		}
+	}
+	axisToProps[bazel.OsConfigurationAxis] = osToProp
+	axisToProps[bazel.OsArchConfigurationAxis] = archOsToProp
+
+	return axisToProps
 }
 
 // Returns a struct matching the propertySet interface, containing properties specific to the targetName
@@ -1989,69 +2022,3 @@
 
 	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
-// values of the properties of the 'propertySet' struct that are specific to
-// that OS target.
-//
-// For example, passing a struct { Foo bool, Bar string } will return an
-// interface{} that can be type asserted back into the same struct, containing
-// the os-specific property value specified by the module if defined.
-//
-// Implemented in a way very similar to GetArchProperties().
-//
-// 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-target-specific modules.
-	if !m.ArchSpecific() {
-		return targetToProp
-	}
-
-	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 targetToProp
-	}
-
-	// For android, linux, ...
-	for _, os := range osTypeList {
-		if os == CommonOS {
-			// It looks like this OS value is not used in Blueprint files
-			continue
-		}
-		targetProperties := TargetProperties{
-			Properties:     getTargetStruct(ctx, propertySet, archProperties, os.Field),
-			ArchProperties: make(map[ArchType]interface{}),
-		}
-		// For arm, x86, ...
-		for _, arch := range osArchTypeMap[os] {
-			targetName := GetCompoundTargetName(os, arch)
-			targetProperties.ArchProperties[arch] = getTargetStruct(ctx, propertySet, archProperties, targetName)
-		}
-		targetToProp[os] = targetProperties
-	}
-
-	return targetToProp
-}
diff --git a/android/variable.go b/android/variable.go
index 7658cdd..6d235d6 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -479,18 +479,11 @@
 
 	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)
+	for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
+		for config, props := range configToProps {
+			// GetArchVariantProperties is creating an instance of the requested type
+			// and productVariablesValues expects an interface, so no need to cast
+			productVariableValues(props, config, &productConfigProperties)
 		}
 	}
 
diff --git a/bazel/Android.bp b/bazel/Android.bp
index b7c185a..b68d65b 100644
--- a/bazel/Android.bp
+++ b/bazel/Android.bp
@@ -7,6 +7,7 @@
     pkgPath: "android/soong/bazel",
     srcs: [
         "aquery.go",
+        "configurability.go",
         "constants.go",
         "properties.go",
     ],
diff --git a/bazel/configurability.go b/bazel/configurability.go
new file mode 100644
index 0000000..df9c9bf
--- /dev/null
+++ b/bazel/configurability.go
@@ -0,0 +1,213 @@
+// 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 bazel
+
+import (
+	"fmt"
+	"strings"
+)
+
+const (
+	// ArchType names in arch.go
+	archArm    = "arm"
+	archArm64  = "arm64"
+	archX86    = "x86"
+	archX86_64 = "x86_64"
+
+	// OsType names in arch.go
+	osAndroid     = "android"
+	osDarwin      = "darwin"
+	osFuchsia     = "fuchsia"
+	osLinux       = "linux_glibc"
+	osLinuxBionic = "linux_bionic"
+	osWindows     = "windows"
+
+	// Targets in arch.go
+	osArchAndroidArm        = "android_arm"
+	osArchAndroidArm64      = "android_arm64"
+	osArchAndroidX86        = "android_x86"
+	osArchAndroidX86_64     = "android_x86_64"
+	osArchDarwinX86_64      = "darwin_x86_64"
+	osArchFuchsiaArm64      = "fuchsia_arm64"
+	osArchFuchsiaX86_64     = "fuchsia_x86_64"
+	osArchLinuxX86          = "linux_glibc_x86"
+	osArchLinuxX86_64       = "linux_glibc_x86_64"
+	osArchLinuxBionicArm64  = "linux_bionic_arm64"
+	osArchLinuxBionicX86_64 = "linux_bionic_x86_64"
+	osArchWindowsX86        = "windows_x86"
+	osArchWindowsX86_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.
+	//
+	// This is consistently named "conditions_default" to mirror the Soong
+	// config variable default key in an Android.bp file, although there's no
+	// integration with Soong config variables (yet).
+	ConditionsDefault = "conditions_default"
+
+	ConditionsDefaultSelectKey = "//conditions:default"
+
+	productVariableBazelPackage = "//build/bazel/product_variables"
+)
+
+var (
+	// These are the list of OSes and architectures with a Bazel config_setting
+	// and constraint value equivalent. These exist in arch.go, but the android
+	// package depends on the bazel package, so a cyclic dependency prevents
+	// using those variables here.
+
+	// A map of architectures to the Bazel label of the constraint_value
+	// for the @platforms//cpu:cpu constraint_setting
+	platformArchMap = map[string]string{
+		archArm:           "//build/bazel/platforms/arch:arm",
+		archArm64:         "//build/bazel/platforms/arch:arm64",
+		archX86:           "//build/bazel/platforms/arch:x86",
+		archX86_64:        "//build/bazel/platforms/arch:x86_64",
+		ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of as arch select map.
+	}
+
+	// A map of target operating systems to the Bazel label of the
+	// constraint_value for the @platforms//os:os constraint_setting
+	platformOsMap = map[string]string{
+		osAndroid:         "//build/bazel/platforms/os:android",
+		osDarwin:          "//build/bazel/platforms/os:darwin",
+		osFuchsia:         "//build/bazel/platforms/os:fuchsia",
+		osLinux:           "//build/bazel/platforms/os:linux",
+		osLinuxBionic:     "//build/bazel/platforms/os:linux_bionic",
+		osWindows:         "//build/bazel/platforms/os:windows",
+		ConditionsDefault: ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+
+	platformOsArchMap = map[string]string{
+		osArchAndroidArm:        "//build/bazel/platforms/os_arch:android_arm",
+		osArchAndroidArm64:      "//build/bazel/platforms/os_arch:android_arm64",
+		osArchAndroidX86:        "//build/bazel/platforms/os_arch:android_x86",
+		osArchAndroidX86_64:     "//build/bazel/platforms/os_arch:android_x86_64",
+		osArchDarwinX86_64:      "//build/bazel/platforms/os_arch:darwin_x86_64",
+		osArchFuchsiaArm64:      "//build/bazel/platforms/os_arch:fuchsia_arm64",
+		osArchFuchsiaX86_64:     "//build/bazel/platforms/os_arch:fuchsia_x86_64",
+		osArchLinuxX86:          "//build/bazel/platforms/os_arch:linux_glibc_x86",
+		osArchLinuxX86_64:       "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
+		osArchLinuxBionicArm64:  "//build/bazel/platforms/os_arch:linux_bionic_arm64",
+		osArchLinuxBionicX86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
+		osArchWindowsX86:        "//build/bazel/platforms/os_arch:windows_x86",
+		osArchWindowsX86_64:     "//build/bazel/platforms/os_arch:windows_x86_64",
+		ConditionsDefault:       ConditionsDefaultSelectKey, // The default condition of an os select map.
+	}
+)
+
+// basic configuration types
+type configurationType int
+
+const (
+	noConfig configurationType = iota
+	arch
+	os
+	osArch
+	productVariables
+)
+
+func (ct configurationType) String() string {
+	return map[configurationType]string{
+		noConfig:         "no_config",
+		arch:             "arch",
+		os:               "os",
+		osArch:           "arch_os",
+		productVariables: "product_variables",
+	}[ct]
+}
+
+func (ct configurationType) validateConfig(config string) {
+	switch ct {
+	case noConfig:
+		if config != "" {
+			panic(fmt.Errorf("Cannot specify config with %s, but got %s", ct, config))
+		}
+	case arch:
+		if _, ok := platformArchMap[config]; !ok {
+			panic(fmt.Errorf("Unknown arch: %s", config))
+		}
+	case os:
+		if _, ok := platformOsMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os: %s", config))
+		}
+	case osArch:
+		if _, ok := platformOsArchMap[config]; !ok {
+			panic(fmt.Errorf("Unknown os+arch: %s", config))
+		}
+	case productVariables:
+		// do nothing
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+	}
+}
+
+// SelectKey returns the Bazel select key for a given configurationType and config string.
+func (ct configurationType) SelectKey(config string) string {
+	ct.validateConfig(config)
+	switch ct {
+	case noConfig:
+		panic(fmt.Errorf("SelectKey is unnecessary for noConfig ConfigurationType "))
+	case arch:
+		return platformArchMap[config]
+	case os:
+		return platformOsMap[config]
+	case osArch:
+		return platformOsArchMap[config]
+	case productVariables:
+		if config == ConditionsDefault {
+			return ConditionsDefaultSelectKey
+		}
+		return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(config))
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationType %d", ct))
+	}
+}
+
+var (
+	// Indicating there is no configuration axis
+	NoConfigAxis = ConfigurationAxis{configurationType: noConfig}
+	// An axis for architecture-specific configurations
+	ArchConfigurationAxis = ConfigurationAxis{configurationType: arch}
+	// An axis for os-specific configurations
+	OsConfigurationAxis = ConfigurationAxis{configurationType: os}
+	// An axis for arch+os-specific configurations
+	OsArchConfigurationAxis = ConfigurationAxis{configurationType: osArch}
+)
+
+// ProductVariableConfigurationAxis returns an axis for the given product variable
+func ProductVariableConfigurationAxis(variable string) ConfigurationAxis {
+	return ConfigurationAxis{
+		configurationType: productVariables,
+		subType:           variable,
+	}
+}
+
+// ConfigurationAxis is an independent axis for configuration, there should be no overlap between
+// elements within an axis.
+type ConfigurationAxis struct {
+	configurationType
+	// some configuration types (e.g. productVariables) have multiple independent axes, subType helps
+	// distinguish between them without needing to list all 17 product variables.
+	subType string
+}
+
+func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {
+	if ca.configurationType < other.configurationType {
+		return true
+	}
+	return ca.subType < other.subType
+}
diff --git a/bazel/properties.go b/bazel/properties.go
index 951081c..c55de95 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -19,7 +19,6 @@
 	"path/filepath"
 	"regexp"
 	"sort"
-	"strings"
 )
 
 // BazelTargetModuleProperties contain properties and metadata used for
@@ -65,6 +64,10 @@
 	Excludes []Label
 }
 
+func (ll *LabelList) IsNil() bool {
+	return ll.Includes == nil && ll.Excludes == nil
+}
+
 // uniqueParentDirectories returns a list of the unique parent directories for
 // all files in ll.Includes.
 func (ll *LabelList) uniqueParentDirectories() []string {
@@ -106,7 +109,27 @@
 	return uniqueLabels
 }
 
-func UniqueBazelLabelList(originalLabelList LabelList) LabelList {
+func FirstUniqueBazelLabels(originalLabels []Label) []Label {
+	var labels []Label
+	found := make(map[Label]bool, len(originalLabels))
+	for _, l := range originalLabels {
+		if _, ok := found[l]; ok {
+			continue
+		}
+		labels = append(labels, l)
+		found[l] = true
+	}
+	return labels
+}
+
+func FirstUniqueBazelLabelList(originalLabelList LabelList) LabelList {
+	var uniqueLabelList LabelList
+	uniqueLabelList.Includes = FirstUniqueBazelLabels(originalLabelList.Includes)
+	uniqueLabelList.Excludes = FirstUniqueBazelLabels(originalLabelList.Excludes)
+	return uniqueLabelList
+}
+
+func UniqueSortedBazelLabelList(originalLabelList LabelList) LabelList {
 	var uniqueLabelList LabelList
 	uniqueLabelList.Includes = UniqueSortedBazelLabels(originalLabelList.Includes)
 	uniqueLabelList.Excludes = UniqueSortedBazelLabels(originalLabelList.Excludes)
@@ -154,16 +177,9 @@
 
 	result.Value = MapLabelList(mapOver.Value, mapFn)
 
-	for arch := range PlatformArchMap {
-		result.SetValueForArch(arch, MapLabelList(mapOver.GetValueForArch(arch), mapFn))
-	}
-
-	for os := range PlatformOsMap {
-		result.SetOsValueForTarget(os, MapLabelList(mapOver.GetOsValueForTarget(os), mapFn))
-
-		// TODO(b/187530594): Should we handle arch=CONDITIONS_DEFAULT here? (not in ArchValues)
-		for _, arch := range AllArches {
-			result.SetOsArchValueForTarget(os, arch, MapLabelList(mapOver.GetOsArchValueForTarget(os, arch), mapFn))
+	for axis, configToLabels := range mapOver.ConfigurableValues {
+		for config, value := range configToLabels {
+			result.SetSelectValue(axis, config, MapLabelList(value, mapFn))
 		}
 	}
 
@@ -184,21 +200,14 @@
 
 // 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 := MakeLabelListAttribute(FilterLabelList(haystack.Value, needleFn))
 
-	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))
+	for config, selects := range haystack.ConfigurableValues {
+		newSelects := make(labelListSelectValues, len(selects))
+		for k, v := range selects {
+			newSelects[k] = FilterLabelList(v, needleFn)
 		}
+		result.ConfigurableValues[config] = newSelects
 	}
 
 	return result
@@ -206,24 +215,18 @@
 
 // Subtract needle from haystack
 func SubtractBazelLabelListAttribute(haystack LabelListAttribute, needle LabelListAttribute) LabelListAttribute {
-	var result LabelListAttribute
+	result := MakeLabelListAttribute(SubtractBazelLabelList(haystack.Value, needle.Value))
 
-	for arch := range PlatformArchMap {
-		result.SetValueForArch(arch,
-			SubtractBazelLabelList(haystack.GetValueForArch(arch), needle.GetValueForArch(arch)))
-	}
+	for config, selects := range haystack.ConfigurableValues {
+		newSelects := make(labelListSelectValues, len(selects))
+		needleSelects := needle.ConfigurableValues[config]
 
-	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)))
+		for k, v := range selects {
+			newSelects[k] = SubtractBazelLabelList(v, needleSelects[k])
 		}
+		result.ConfigurableValues[config] = newSelects
 	}
 
-	result.Value = SubtractBazelLabelList(haystack.Value, needle.Value)
-
 	return result
 }
 
@@ -268,489 +271,202 @@
 	return result
 }
 
-const (
-	// ArchType names in arch.go
-	ARCH_ARM    = "arm"
-	ARCH_ARM64  = "arm64"
-	ARCH_X86    = "x86"
-	ARCH_X86_64 = "x86_64"
-
-	// OsType names in arch.go
-	OS_ANDROID      = "android"
-	OS_DARWIN       = "darwin"
-	OS_FUCHSIA      = "fuchsia"
-	OS_LINUX        = "linux_glibc"
-	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.
-	//
-	// This is consistently named "conditions_default" to mirror the Soong
-	// config variable default key in an Android.bp file, although there's no
-	// integration with Soong config variables (yet).
-	CONDITIONS_DEFAULT = "conditions_default"
-
-	ConditionsDefaultSelectKey = "//conditions:default"
-
-	productVariableBazelPackage = "//build/bazel/product_variables"
-)
-
-var (
-	// These are the list of OSes and architectures with a Bazel config_setting
-	// and constraint value equivalent. These exist in arch.go, but the android
-	// package depends on the bazel package, so a cyclic dependency prevents
-	// using those variables here.
-
-	// A map of architectures to the Bazel label of the constraint_value
-	// for the @platforms//cpu:cpu constraint_setting
-	PlatformArchMap = map[string]string{
-		ARCH_ARM:           "//build/bazel/platforms/arch:arm",
-		ARCH_ARM64:         "//build/bazel/platforms/arch:arm64",
-		ARCH_X86:           "//build/bazel/platforms/arch:x86",
-		ARCH_X86_64:        "//build/bazel/platforms/arch:x86_64",
-		CONDITIONS_DEFAULT: ConditionsDefaultSelectKey, // The default condition of as arch select map.
-	}
-
-	// A map of target operating systems to the Bazel label of the
-	// constraint_value for the @platforms//os:os constraint_setting
-	PlatformOsMap = map[string]string{
-		OS_ANDROID:         "//build/bazel/platforms/os:android",
-		OS_DARWIN:          "//build/bazel/platforms/os:darwin",
-		OS_FUCHSIA:         "//build/bazel/platforms/os:fuchsia",
-		OS_LINUX:           "//build/bazel/platforms/os:linux",
-		OS_LINUX_BIONIC:    "//build/bazel/platforms/os:linux_bionic",
-		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/os_arch:android_arm",
-		TARGET_ANDROID_ARM64:       "//build/bazel/platforms/os_arch:android_arm64",
-		TARGET_ANDROID_X86:         "//build/bazel/platforms/os_arch:android_x86",
-		TARGET_ANDROID_X86_64:      "//build/bazel/platforms/os_arch:android_x86_64",
-		TARGET_DARWIN_X86_64:       "//build/bazel/platforms/os_arch:darwin_x86_64",
-		TARGET_FUCHSIA_ARM64:       "//build/bazel/platforms/os_arch:fuchsia_arm64",
-		TARGET_FUCHSIA_X86_64:      "//build/bazel/platforms/os_arch:fuchsia_x86_64",
-		TARGET_LINUX_X86:           "//build/bazel/platforms/os_arch:linux_glibc_x86",
-		TARGET_LINUX_x86_64:        "//build/bazel/platforms/os_arch:linux_glibc_x86_64",
-		TARGET_LINUX_BIONIC_ARM64:  "//build/bazel/platforms/os_arch:linux_bionic_arm64",
-		TARGET_LINUX_BIONIC_X86_64: "//build/bazel/platforms/os_arch:linux_bionic_x86_64",
-		TARGET_WINDOWS_X86:         "//build/bazel/platforms/os_arch:windows_x86",
-		TARGET_WINDOWS_X86_64:      "//build/bazel/platforms/os_arch: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 {
 	HasConfigurableValues() bool
 }
 
-type labelArchValues struct {
-	X86    Label
-	X86_64 Label
-	Arm    Label
-	Arm64  Label
+type labelSelectValues map[string]*Label
 
-	ConditionsDefault Label
-}
+type configurableLabels map[ConfigurationAxis]labelSelectValues
 
-type labelTargetValue struct {
-	// E.g. for android
-	OsValue Label
-
-	// E.g. for android_arm, android_arm64, ...
-	ArchValues labelArchValues
-}
-
-type labelTargetValues struct {
-	Android     labelTargetValue
-	Darwin      labelTargetValue
-	Fuchsia     labelTargetValue
-	Linux       labelTargetValue
-	LinuxBionic labelTargetValue
-	Windows     labelTargetValue
-
-	ConditionsDefault labelTargetValue
+func (cl configurableLabels) setValueForAxis(axis ConfigurationAxis, config string, value *Label) {
+	if cl[axis] == nil {
+		cl[axis] = make(labelSelectValues)
+	}
+	cl[axis][config] = value
 }
 
 // Represents an attribute whose value is a single label
 type LabelAttribute struct {
-	Value Label
+	Value *Label
 
-	ArchValues labelArchValues
-
-	TargetValues labelTargetValues
+	ConfigurableValues configurableLabels
 }
 
-func (attr *LabelAttribute) GetValueForArch(arch string) Label {
-	var v *Label
-	if v = attr.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
+// HasConfigurableValues returns whether there are configurable values set for this label.
+func (la LabelAttribute) HasConfigurableValues() bool {
+	return len(la.ConfigurableValues) > 0
 }
 
-func (attr *LabelAttribute) SetValueForArch(arch string, value Label) {
-	var v *Label
-	if v = attr.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
+// SetValue sets the base, non-configured value for the Label
+func (la *LabelAttribute) SetValue(value Label) {
+	la.SetSelectValue(NoConfigAxis, "", value)
 }
 
-func (attr *LabelAttribute) archValuePtrs() map[string]*Label {
-	return map[string]*Label{
-		ARCH_X86:           &attr.ArchValues.X86,
-		ARCH_X86_64:        &attr.ArchValues.X86_64,
-		ARCH_ARM:           &attr.ArchValues.Arm,
-		ARCH_ARM64:         &attr.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attr.ArchValues.ConditionsDefault,
-	}
-}
-
-func (attr LabelAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if attr.GetValueForArch(arch).Label != "" {
-			return true
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (la *LabelAttribute) SetSelectValue(axis ConfigurationAxis, config string, value Label) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		la.Value = &value
+	case arch, os, osArch, productVariables:
+		if la.ConfigurableValues == nil {
+			la.ConfigurableValues = make(configurableLabels)
 		}
+		la.ConfigurableValues.setValueForAxis(axis, config, &value)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (la *LabelAttribute) SelectValue(axis ConfigurationAxis, config string) Label {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return *la.Value
+	case arch, os, osArch, productVariables:
+		return *la.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (la *LabelAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(la.ConfigurableValues))
+	for k := range la.ConfigurableValues {
+		keys = append(keys, k)
 	}
 
-	for os := range PlatformOsMap {
-		if attr.GetOsValueForTarget(os).Label != "" {
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
+}
+
+// labelListSelectValues supports config-specific label_list typed Bazel attribute values.
+type labelListSelectValues map[string]LabelList
+
+func (ll labelListSelectValues) appendSelects(other labelListSelectValues) {
+	for k, v := range other {
+		l := ll[k]
+		(&l).Append(v)
+		ll[k] = l
+	}
+}
+
+// HasConfigurableValues returns whether there are configurable values within this set of selects.
+func (ll labelListSelectValues) HasConfigurableValues() bool {
+	for _, v := range ll {
+		if len(v.Includes) > 0 {
 			return true
 		}
-		// TODO(b/187530594): Should we also check arch=CONDITIONS_DEFAULT (not in AllArches)
-		for _, arch := range AllArches {
-			if attr.GetOsArchValueForTarget(os, arch).Label != "" {
-				return true
-			}
-		}
 	}
 	return false
 }
 
-func (attr *LabelAttribute) getValueForTarget(os string) labelTargetValue {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return *v
-}
-
-func (attr *LabelAttribute) GetOsValueForTarget(os string) Label {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	return v.OsValue
-}
-
-func (attr *LabelAttribute) GetOsArchValueForTarget(os string, arch string) Label {
-	var v *labelTargetValue
-	if v = attr.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 (attr *LabelAttribute) setValueForTarget(os string, value labelTargetValue) {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	*v = value
-}
-
-func (attr *LabelAttribute) SetOsValueForTarget(os string, value Label) {
-	var v *labelTargetValue
-	if v = attr.targetValuePtrs()[os]; v == nil {
-		panic(fmt.Errorf("Unknown os: %s", os))
-	}
-	v.OsValue = value
-}
-
-func (attr *LabelAttribute) SetOsArchValueForTarget(os string, arch string, value Label) {
-	var v *labelTargetValue
-	if v = attr.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))
-	}
-}
-
-func (attr *LabelAttribute) targetValuePtrs() map[string]*labelTargetValue {
-	return map[string]*labelTargetValue{
-		OS_ANDROID:         &attr.TargetValues.Android,
-		OS_DARWIN:          &attr.TargetValues.Darwin,
-		OS_FUCHSIA:         &attr.TargetValues.Fuchsia,
-		OS_LINUX:           &attr.TargetValues.Linux,
-		OS_LINUX_BIONIC:    &attr.TargetValues.LinuxBionic,
-		OS_WINDOWS:         &attr.TargetValues.Windows,
-		CONDITIONS_DEFAULT: &attr.TargetValues.ConditionsDefault,
-	}
-}
-
-// Arch-specific label_list typed Bazel attribute values. This should correspond
-// to the types of architectures supported for compilation in arch.go.
-type labelListArchValues struct {
-	X86    LabelList
-	X86_64 LabelList
-	Arm    LabelList
-	Arm64  LabelList
-
-	ConditionsDefault LabelList
-}
-
-type labelListTargetValue struct {
-	// E.g. for android
-	OsValue 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
 // attribute.
 type LabelListAttribute struct {
-	// The non-arch specific attribute label list Value. Required.
+	// The non-configured attribute label list Value. Required.
 	Value LabelList
 
-	// The arch-specific attribute label list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-arch specific
-	// label list Value.
-	ArchValues labelListArchValues
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableLabelLists
+}
 
-	// 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.
-	TargetValues labelListTargetValues
+type configurableLabelLists map[ConfigurationAxis]labelListSelectValues
+
+func (cll configurableLabelLists) setValueForAxis(axis ConfigurationAxis, config string, list LabelList) {
+	if list.IsNil() {
+		if _, ok := cll[axis][config]; ok {
+			delete(cll[axis], config)
+		}
+		return
+	}
+	if cll[axis] == nil {
+		cll[axis] = make(labelListSelectValues)
+	}
+
+	cll[axis][config] = list
+}
+
+func (cll configurableLabelLists) Append(other configurableLabelLists) {
+	for axis, otherSelects := range other {
+		selects := cll[axis]
+		if selects == nil {
+			selects = make(labelListSelectValues, len(otherSelects))
+		}
+		selects.appendSelects(otherSelects)
+		cll[axis] = selects
+	}
 }
 
 // MakeLabelListAttribute initializes a LabelListAttribute with the non-arch specific value.
 func MakeLabelListAttribute(value LabelList) LabelListAttribute {
-	return LabelListAttribute{Value: UniqueBazelLabelList(value)}
+	return LabelListAttribute{
+		Value:              value,
+		ConfigurableValues: make(configurableLabelLists),
+	}
+}
+
+func (lla *LabelListAttribute) SetValue(list LabelList) {
+	lla.SetSelectValue(NoConfigAxis, "", list)
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (lla *LabelListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list LabelList) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		lla.Value = list
+	case arch, os, osArch, productVariables:
+		if lla.ConfigurableValues == nil {
+			lla.ConfigurableValues = make(configurableLabelLists)
+		}
+		lla.ConfigurableValues.setValueForAxis(axis, config, list)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (lla *LabelListAttribute) SelectValue(axis ConfigurationAxis, config string) LabelList {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return lla.Value
+	case arch, os, osArch, productVariables:
+		return lla.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (lla *LabelListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(lla.ConfigurableValues))
+	for k := range lla.ConfigurableValues {
+		keys = append(keys, k)
+	}
+
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
 }
 
 // Append all values, including os and arch specific ones, from another
 // LabelListAttribute to this LabelListAttribute.
-func (attrs *LabelListAttribute) Append(other LabelListAttribute) {
-	for arch := range PlatformArchMap {
-		this := attrs.GetValueForArch(arch)
-		that := other.GetValueForArch(arch)
-		this.Append(that)
-		attrs.SetValueForArch(arch, this)
+func (lla *LabelListAttribute) Append(other LabelListAttribute) {
+	lla.Value.Append(other.Value)
+	if lla.ConfigurableValues == nil {
+		lla.ConfigurableValues = make(configurableLabelLists)
 	}
-
-	for os := range PlatformOsMap {
-		this := attrs.getValueForTarget(os)
-		that := other.getValueForTarget(os)
-		this.Append(that)
-		attrs.setValueForTarget(os, this)
-	}
-
-	attrs.Value.Append(other.Value)
+	lla.ConfigurableValues.Append(other.ConfigurableValues)
 }
 
-// HasArchSpecificValues returns true if the attribute contains
-// architecture-specific label_list values.
-func (attrs LabelListAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if len(attrs.GetValueForArch(arch).Includes) > 0 {
-			return true
-		}
-	}
-
-	for os := range PlatformOsMap {
-		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
-}
-
-func (attrs *LabelListAttribute) archValuePtrs() map[string]*LabelList {
-	return map[string]*LabelList{
-		ARCH_X86:           &attrs.ArchValues.X86,
-		ARCH_X86_64:        &attrs.ArchValues.X86_64,
-		ARCH_ARM:           &attrs.ArchValues.Arm,
-		ARCH_ARM64:         &attrs.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
-	}
-}
-
-// GetValueForArch returns the label_list attribute value for an architecture.
-func (attrs *LabelListAttribute) GetValueForArch(arch string) LabelList {
-	var v *LabelList
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
-}
-
-// SetValueForArch sets the label_list attribute value for an architecture.
-func (attrs *LabelListAttribute) SetValueForArch(arch string, value LabelList) {
-	var v *LabelList
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
-}
-
-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,
-	}
-}
-
-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
-}
-
-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))
-	}
+// HasConfigurableValues returns true if the attribute contains axis-specific label list values.
+func (lla LabelListAttribute) HasConfigurableValues() bool {
+	return len(lla.ConfigurableValues) > 0
 }
 
 // StringListAttribute corresponds to the string_list Bazel attribute type with
@@ -759,256 +475,110 @@
 	// The base value of the string list attribute.
 	Value []string
 
-	// The arch-specific attribute string list values. Optional. If used, these
-	// are generated in a select statement and appended to the non-arch specific
-	// label list Value.
-	ArchValues stringListArchValues
+	// The configured attribute label list Values. Optional
+	// a map of independent configurability axes
+	ConfigurableValues configurableStringLists
+}
 
-	// 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.
-	TargetValues stringListTargetValues
+type configurableStringLists map[ConfigurationAxis]stringListSelectValues
 
-	// list of product-variable string list values. Optional. if used, each will generate a select
-	// statement appended to the label list Value.
-	ProductValues []ProductVariableValues
+func (csl configurableStringLists) Append(other configurableStringLists) {
+	for axis, otherSelects := range other {
+		selects := csl[axis]
+		if selects == nil {
+			selects = make(stringListSelectValues, len(otherSelects))
+		}
+		selects.appendSelects(otherSelects)
+		csl[axis] = selects
+	}
+}
+
+func (csl configurableStringLists) setValueForAxis(axis ConfigurationAxis, config string, list []string) {
+	if csl[axis] == nil {
+		csl[axis] = make(stringListSelectValues)
+	}
+	csl[axis][config] = list
+}
+
+type stringListSelectValues map[string][]string
+
+func (sl stringListSelectValues) appendSelects(other stringListSelectValues) {
+	for k, v := range other {
+		sl[k] = append(sl[k], v...)
+	}
+}
+
+func (sl stringListSelectValues) hasConfigurableValues(other stringListSelectValues) bool {
+	for _, val := range sl {
+		if len(val) > 0 {
+			return true
+		}
+	}
+	return false
 }
 
 // MakeStringListAttribute initializes a StringListAttribute with the non-arch specific value.
 func MakeStringListAttribute(value []string) StringListAttribute {
 	// NOTE: These strings are not necessarily unique or sorted.
-	return StringListAttribute{Value: value}
-}
-
-// Arch-specific string_list typed Bazel attribute values. This should correspond
-// to the types of architectures supported for compilation in arch.go.
-type stringListArchValues struct {
-	X86    []string
-	X86_64 []string
-	Arm    []string
-	Arm64  []string
-
-	ConditionsDefault []string
-}
-
-type stringListTargetValue struct {
-	// E.g. for android
-	OsValue []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
-type ProductVariableValues struct {
-	ProductVariable string
-
-	Values []string
-}
-
-// SelectKey returns the appropriate select key for the receiving ProductVariableValues.
-func (p ProductVariableValues) SelectKey() string {
-	return fmt.Sprintf("%s:%s", productVariableBazelPackage, strings.ToLower(p.ProductVariable))
-}
-
-// HasConfigurableValues returns true if the attribute contains
-// architecture-specific string_list values.
-func (attrs StringListAttribute) HasConfigurableValues() bool {
-	for arch := range PlatformArchMap {
-		if len(attrs.GetValueForArch(arch)) > 0 {
-			return true
-		}
-	}
-
-	for os := range PlatformOsMap {
-		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
-}
-
-func (attrs *StringListAttribute) archValuePtrs() map[string]*[]string {
-	return map[string]*[]string{
-		ARCH_X86:           &attrs.ArchValues.X86,
-		ARCH_X86_64:        &attrs.ArchValues.X86_64,
-		ARCH_ARM:           &attrs.ArchValues.Arm,
-		ARCH_ARM64:         &attrs.ArchValues.Arm64,
-		CONDITIONS_DEFAULT: &attrs.ArchValues.ConditionsDefault,
+	return StringListAttribute{
+		Value:              value,
+		ConfigurableValues: make(configurableStringLists),
 	}
 }
 
-// GetValueForArch returns the string_list attribute value for an architecture.
-func (attrs *StringListAttribute) GetValueForArch(arch string) []string {
-	var v *[]string
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	return *v
-}
-
-// SetValueForArch sets the string_list attribute value for an architecture.
-func (attrs *StringListAttribute) SetValueForArch(arch string, value []string) {
-	var v *[]string
-	if v = attrs.archValuePtrs()[arch]; v == nil {
-		panic(fmt.Errorf("Unknown arch: %s", arch))
-	}
-	*v = value
-}
-
-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,
-	}
-}
-
-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
-}
-
-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))
-	}
+// HasConfigurableValues returns true if the attribute contains axis-specific string_list values.
+func (sla StringListAttribute) HasConfigurableValues() bool {
+	return len(sla.ConfigurableValues) > 0
 }
 
 // Append appends all values, including os and arch specific ones, from another
 // StringListAttribute to this StringListAttribute
-func (attrs *StringListAttribute) Append(other StringListAttribute) {
-	for arch := range PlatformArchMap {
-		this := attrs.GetValueForArch(arch)
-		that := other.GetValueForArch(arch)
-		this = append(this, that...)
-		attrs.SetValueForArch(arch, this)
+func (sla *StringListAttribute) Append(other StringListAttribute) {
+	sla.Value = append(sla.Value, other.Value...)
+	if sla.ConfigurableValues == nil {
+		sla.ConfigurableValues = make(configurableStringLists)
+	}
+	sla.ConfigurableValues.Append(other.ConfigurableValues)
+}
+
+// SetSelectValue set a value for a bazel select for the given axis, config and value.
+func (sla *StringListAttribute) SetSelectValue(axis ConfigurationAxis, config string, list []string) {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		sla.Value = list
+	case arch, os, osArch, productVariables:
+		if sla.ConfigurableValues == nil {
+			sla.ConfigurableValues = make(configurableStringLists)
+		}
+		sla.ConfigurableValues.setValueForAxis(axis, config, list)
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SelectValue gets a value for a bazel select for the given axis and config.
+func (sla *StringListAttribute) SelectValue(axis ConfigurationAxis, config string) []string {
+	axis.validateConfig(config)
+	switch axis.configurationType {
+	case noConfig:
+		return sla.Value
+	case arch, os, osArch, productVariables:
+		return sla.ConfigurableValues[axis][config]
+	default:
+		panic(fmt.Errorf("Unrecognized ConfigurationAxis %s", axis))
+	}
+}
+
+// SortedConfigurationAxes returns all the used ConfigurationAxis in sorted order.
+func (sla *StringListAttribute) SortedConfigurationAxes() []ConfigurationAxis {
+	keys := make([]ConfigurationAxis, 0, len(sla.ConfigurableValues))
+	for k := range sla.ConfigurableValues {
+		keys = append(keys, k)
 	}
 
-	for os := range PlatformOsMap {
-		this := attrs.getValueForTarget(os)
-		that := other.getValueForTarget(os)
-		this.Append(that)
-		attrs.setValueForTarget(os, this)
-	}
-
-	productValues := make(map[string][]string, 0)
-	for _, pv := range attrs.ProductValues {
-		productValues[pv.ProductVariable] = pv.Values
-	}
-	for _, pv := range other.ProductValues {
-		productValues[pv.ProductVariable] = append(productValues[pv.ProductVariable], pv.Values...)
-	}
-	attrs.ProductValues = make([]ProductVariableValues, 0, len(productValues))
-	for pv, vals := range productValues {
-		attrs.ProductValues = append(attrs.ProductValues, ProductVariableValues{
-			ProductVariable: pv,
-			Values:          vals,
-		})
-	}
-
-	attrs.Value = append(attrs.Value, other.Value...)
+	sort.Slice(keys, func(i, j int) bool { return keys[i].less(keys[j]) })
+	return keys
 }
 
 // TryVariableSubstitution, replace string substitution formatting within each string in slice with
diff --git a/bazel/properties_test.go b/bazel/properties_test.go
index 229a4aa..bc556bf 100644
--- a/bazel/properties_test.go
+++ b/bazel/properties_test.go
@@ -122,7 +122,7 @@
 		}
 	}
 }
-func TestUniqueBazelLabelList(t *testing.T) {
+func TestFirstUniqueBazelLabelList(t *testing.T) {
 	testCases := []struct {
 		originalLabelList       LabelList
 		expectedUniqueLabelList LabelList
@@ -157,7 +157,49 @@
 		},
 	}
 	for _, tc := range testCases {
-		actualUniqueLabelList := UniqueBazelLabelList(tc.originalLabelList)
+		actualUniqueLabelList := FirstUniqueBazelLabelList(tc.originalLabelList)
+		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
+			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
+		}
+	}
+}
+
+func TestUniqueSortedBazelLabelList(t *testing.T) {
+	testCases := []struct {
+		originalLabelList       LabelList
+		expectedUniqueLabelList LabelList
+	}{
+		{
+			originalLabelList: LabelList{
+				Includes: []Label{
+					{Label: "c"},
+					{Label: "a"},
+					{Label: "a"},
+					{Label: "b"},
+				},
+				Excludes: []Label{
+					{Label: "y"},
+					{Label: "z"},
+					{Label: "x"},
+					{Label: "x"},
+				},
+			},
+			expectedUniqueLabelList: LabelList{
+				Includes: []Label{
+					{Label: "a"},
+					{Label: "b"},
+					{Label: "c"},
+				},
+				Excludes: []Label{
+					{Label: "x"},
+					{Label: "y"},
+					{Label: "z"},
+				},
+			},
+		},
+	}
+	for _, tc := range testCases {
+		actualUniqueLabelList := UniqueSortedBazelLabelList(tc.originalLabelList)
 		if !reflect.DeepEqual(tc.expectedUniqueLabelList, actualUniqueLabelList) {
 			t.Fatalf("Expected %v, got %v", tc.expectedUniqueLabelList, actualUniqueLabelList)
 		}
diff --git a/bp2build/build_conversion_test.go b/bp2build/build_conversion_test.go
index b1c342c..6d0a9b2 100644
--- a/bp2build/build_conversion_test.go
+++ b/bp2build/build_conversion_test.go
@@ -1109,8 +1109,8 @@
         "out",
     ],
     srcs = [
-        "in1",
         "srcs-from-3",
+        "in1",
     ],
 )`,
 			description: "genrule applies properties from genrule_defaults transitively",
@@ -1535,10 +1535,10 @@
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
     srcs = [
-        "//dir:e.txt",
-        "//dir:f.txt",
         "a.txt",
         "b.txt",
+        "//dir:e.txt",
+        "//dir:f.txt",
     ],
 )`,
 			},
@@ -1575,9 +1575,9 @@
 			expectedBazelTargets: []string{`filegroup(
     name = "fg_foo",
     srcs = [
+        "a.txt",
         "//dir/subdir:e.txt",
         "//dir/subdir:f.txt",
-        "a.txt",
     ],
 )`,
 			},
diff --git a/bp2build/cc_library_static_conversion_test.go b/bp2build/cc_library_static_conversion_test.go
index da38adb..40edec8 100644
--- a/bp2build/cc_library_static_conversion_test.go
+++ b/bp2build/cc_library_static_conversion_test.go
@@ -1286,8 +1286,6 @@
         "//build/bazel/platforms/os_arch:android_arm64": ["android_arm64_src.c"],
         "//build/bazel/platforms/os_arch:android_x86": ["android_x86_src.c"],
         "//build/bazel/platforms/os_arch:android_x86_64": ["android_x86_64_src.c"],
-        "//conditions:default": [],
-    }) + select({
         "//build/bazel/platforms/os_arch:linux_bionic_arm64": ["linux_bionic_arm64_src.c"],
         "//build/bazel/platforms/os_arch:linux_bionic_x86_64": ["linux_bionic_x86_64_src.c"],
         "//conditions:default": [],
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 7e1a298..60f6330 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -17,46 +17,20 @@
 		return value, []selects{}
 	}
 
-	selectValues := make([]selects, 0)
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(list.GetValueForArch(arch))
-	}
-	if len(archSelects) > 0 {
-		selectValues = append(selectValues, archSelects)
-	}
-
-	osSelects := map[string]reflect.Value{}
-	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))
+	var ret []selects
+	for _, axis := range list.SortedConfigurationAxes() {
+		configToLists := list.ConfigurableValues[axis]
+		archSelects := map[string]reflect.Value{}
+		for config, labels := range configToLists {
+			selectKey := axis.SelectKey(config)
+			archSelects[selectKey] = reflect.ValueOf(labels)
 		}
-		osArchSelects = append(osArchSelects, archSelects)
-	}
-	if len(osSelects) > 0 {
-		selectValues = append(selectValues, osSelects)
-	}
-	if len(osArchSelects) > 0 {
-		selectValues = append(selectValues, osArchSelects...)
-	}
-
-	for _, pv := range list.SortedProductVariables() {
-		s := make(selects)
-		if len(pv.Values) > 0 {
-			s[pv.SelectKey()] = reflect.ValueOf(pv.Values)
-			s[bazel.ConditionsDefaultSelectKey] = reflect.ValueOf([]string{})
-			selectValues = append(selectValues, s)
+		if len(archSelects) > 0 {
+			ret = append(ret, archSelects)
 		}
 	}
 
-	return value, selectValues
+	return value, ret
 }
 
 func getLabelValue(label bazel.LabelAttribute) (reflect.Value, []selects) {
@@ -65,105 +39,37 @@
 		return value, []selects{}
 	}
 
-	// Keep track of which arches and oses have been used in case we need to raise a warning
-	usedArches := make(map[string]bool)
-	usedOses := make(map[string]bool)
-
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		archSelects[selectKey] = reflect.ValueOf(label.GetValueForArch(arch))
-		if archSelects[selectKey].IsValid() && !isZero(archSelects[selectKey]) {
-			usedArches[arch] = true
+	ret := selects{}
+	for _, axis := range label.SortedConfigurationAxes() {
+		configToLabels := label.ConfigurableValues[axis]
+		for config, labels := range configToLabels {
+			selectKey := axis.SelectKey(config)
+			ret[selectKey] = reflect.ValueOf(labels)
 		}
 	}
 
-	osSelects := map[string]reflect.Value{}
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		selectKey := bazel.PlatformOsMap[os]
-		osSelects[selectKey] = reflect.ValueOf(label.GetOsValueForTarget(os))
-		if osSelects[selectKey].IsValid() && !isZero(osSelects[selectKey]) {
-			usedOses[os] = true
-		}
-	}
-
-	osArchSelects := make([]selects, 0)
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		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(label.GetOsArchValueForTarget(os, arch))
-			if archSelects[selectKey].IsValid() && !isZero(archSelects[selectKey]) {
-				if _, ok := usedArches[arch]; ok {
-					fmt.Printf("WARNING: Same arch used twice in LabelAttribute select: arch '%s'\n", arch)
-				}
-				if _, ok := usedOses[os]; ok {
-					fmt.Printf("WARNING: Same os used twice in LabelAttribute select: os '%s'\n", os)
-				}
-			}
-		}
-		osArchSelects = append(osArchSelects, archSelects)
-	}
-
-	// Because we have to return a single Label, we can only use one select statement
-	combinedSelects := map[string]reflect.Value{}
-	for k, v := range archSelects {
-		combinedSelects[k] = v
-	}
-	for k, v := range osSelects {
-		combinedSelects[k] = v
-	}
-	for _, osArchSelect := range osArchSelects {
-		for k, v := range osArchSelect {
-			combinedSelects[k] = v
-		}
-	}
-
-	return value, []selects{combinedSelects}
+	return value, []selects{ret}
 }
 
 func getLabelListValues(list bazel.LabelListAttribute) (reflect.Value, []selects) {
 	value := reflect.ValueOf(list.Value.Includes)
-	if !list.HasConfigurableValues() {
-		return value, []selects{}
-	}
 	var ret []selects
-
-	archSelects := map[string]reflect.Value{}
-	for arch, selectKey := range bazel.PlatformArchMap {
-		if use, value := labelListSelectValue(selectKey, list.GetValueForArch(arch)); use {
-			archSelects[selectKey] = value
+	for _, axis := range list.SortedConfigurationAxes() {
+		configToLabels := list.ConfigurableValues[axis]
+		if !configToLabels.HasConfigurableValues() {
+			continue
 		}
-	}
-	if len(archSelects) > 0 {
-		ret = append(ret, archSelects)
-	}
-
-	osSelects := map[string]reflect.Value{}
-	osArchSelects := []selects{}
-	for _, os := range android.SortedStringKeys(bazel.PlatformOsMap) {
-		selectKey := bazel.PlatformOsMap[os]
-		if use, value := labelListSelectValue(selectKey, list.GetOsValueForTarget(os)); use {
-			osSelects[selectKey] = value
-		}
-		selects := 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]
-			if use, value := labelListSelectValue(selectKey, list.GetOsArchValueForTarget(os, arch)); use {
-				selects[selectKey] = value
+		archSelects := map[string]reflect.Value{}
+		for config, labels := range configToLabels {
+			selectKey := axis.SelectKey(config)
+			if use, value := labelListSelectValue(selectKey, labels); use {
+				archSelects[selectKey] = value
 			}
 		}
-		if len(selects) > 0 {
-			osArchSelects = append(osArchSelects, selects)
+		if len(archSelects) > 0 {
+			ret = append(ret, archSelects)
 		}
 	}
-	if len(osSelects) > 0 {
-		ret = append(ret, osSelects)
-	}
-	ret = append(ret, osArchSelects...)
 
 	return value, ret
 }
diff --git a/bp2build/testing.go b/bp2build/testing.go
index e575bc6..861f7d2 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -157,9 +157,11 @@
 
 		paths := bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, m.props.Arch_paths))
 
-		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))
+		for axis, configToProps := range m.GetArchVariantProperties(ctx, &customProps{}) {
+			for config, props := range configToProps {
+				if archProps, ok := props.(*customProps); ok && archProps.Arch_paths != nil {
+					paths.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, archProps.Arch_paths))
+				}
 			}
 		}
 
diff --git a/cc/bp2build.go b/cc/bp2build.go
index a156d54..31e69d8 100644
--- a/cc/bp2build.go
+++ b/cc/bp2build.go
@@ -53,58 +53,29 @@
 
 	var allDeps []string
 
-	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 {
+	for _, configToProps := range module.GetArchVariantProperties(ctx, &BaseCompilerProperties{}) {
+		for _, props := range configToProps {
+			if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
 				allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
 				allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
 			}
 		}
 	}
 
-	for _, props := range module.GetArchProperties(ctx, &BaseCompilerProperties{}) {
-		// arch specific compiler props
-		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			allDeps = append(allDeps, baseCompilerProps.Generated_headers...)
-			allDeps = append(allDeps, baseCompilerProps.Generated_sources...)
-		}
-	}
-
-	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 {
+	for _, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
+		for _, props := range configToProps {
+			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...)
+				allDeps = append(allDeps, baseLinkerProps.Exclude_static_libs...)
 				allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Shared_libs...)
+				allDeps = append(allDeps, baseLinkerProps.Exclude_shared_libs...)
 			}
 		}
 	}
 
-	for _, props := range module.GetArchProperties(ctx, &BaseLinkerProperties{}) {
-		// arch specific linker props
-		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...)
-			allDeps = append(allDeps, baseLinkerProps.Whole_static_libs...)
-		}
-	}
-
 	// Deps in the static: { .. } and shared: { .. } props of a cc_library.
 	if lib, ok := module.compiler.(*libraryDecorator); ok {
 		appendDeps := func(deps []string, p StaticOrSharedProperties) []string {
@@ -124,39 +95,21 @@
 
 		// 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 {
+		for _, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
+			for _, props := range configToProps {
+				if p, ok := props.(*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 {
+
+		for _, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
+			for _, props := range configToProps {
+				if p, ok := props.(*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)...)
@@ -275,65 +228,33 @@
 
 	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)},
+		srcs:             bazel.MakeLabelListAttribute(android.BazelLabelForModuleSrc(ctx, props.Srcs)),
+		staticDeps:       bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Static_libs)),
+		dynamicDeps:      bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, props.Shared_libs)),
+		wholeArchiveDeps: bazel.MakeLabelListAttribute(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))
+	setAttrs := func(axis bazel.ConfigurationAxis, config string, props StaticOrSharedProperties) {
+		attrs.copts.SetSelectValue(axis, config, props.Cflags)
+		attrs.srcs.SetSelectValue(axis, config, android.BazelLabelForModuleSrc(ctx, props.Srcs))
+		attrs.staticDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Static_libs))
+		attrs.dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, props.Shared_libs))
+		attrs.wholeArchiveDeps.SetSelectValue(axis, config, 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)
+		for axis, configToProps := range module.GetArchVariantProperties(ctx, &StaticProperties{}) {
+			for config, props := range configToProps {
+				if staticOrSharedProps, ok := props.(*StaticProperties); ok {
+					setAttrs(axis, config, 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)
+		for axis, configToProps := range module.GetArchVariantProperties(ctx, &SharedProperties{}) {
+			for config, props := range configToProps {
+				if staticOrSharedProps, ok := props.(*SharedProperties); ok {
+					setAttrs(axis, config, staticOrSharedProps.Shared)
 				}
 			}
 		}
@@ -363,45 +284,23 @@
 	}
 
 	if len(prebuiltLinker.properties.Srcs) == 1 {
-		srcLabelAttribute.Value = android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinker.properties.Srcs[0])
-		for arch, props := range module.GetArchProperties(ctx, &prebuiltLinkerProperties{}) {
+		srcLabelAttribute.SetValue(android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinker.properties.Srcs[0]))
+	}
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &prebuiltLinkerProperties{}) {
+		for config, props := range configToProps {
 			if prebuiltLinkerProperties, ok := props.(*prebuiltLinkerProperties); ok {
 				if len(prebuiltLinkerProperties.Srcs) > 1 {
-					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for arch %s\n", arch.Name)
+					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for %s %s\n", axis, config)
+					continue
+				} else if len(prebuiltLinkerProperties.Srcs) == 0 {
+					continue
 				}
-				if len(prebuiltLinkerProperties.Srcs) == 1 {
-					srcLabelAttribute.SetValueForArch(arch.Name, android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0]))
-				}
+				src := android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0])
+				srcLabelAttribute.SetSelectValue(axis, config, src)
 			}
 		}
 	}
 
-	for os, targetProperties := range module.GetTargetProperties(ctx, &prebuiltLinkerProperties{}) {
-		if prebuiltLinkerProperties, ok := targetProperties.Properties.(*prebuiltLinkerProperties); ok {
-			if len(prebuiltLinkerProperties.Srcs) > 1 {
-				ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for os %s\n", os.Name)
-
-			}
-
-			if len(prebuiltLinkerProperties.Srcs) == 1 {
-				srcLabelAttribute.SetOsValueForTarget(os.Name, android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0]))
-			}
-		}
-		for arch, archProperties := range targetProperties.ArchProperties {
-			if prebuiltLinkerProperties, ok := archProperties.(*prebuiltLinkerProperties); ok {
-				if len(prebuiltLinkerProperties.Srcs) > 1 {
-					ctx.ModuleErrorf("Bp2BuildParsePrebuiltLibraryProps: Expected at most once source file for os_arch %s_%s\n", os.Name, arch.Name)
-
-				}
-
-				if len(prebuiltLinkerProperties.Srcs) == 1 {
-					srcLabelAttribute.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleSrcSingle(ctx, prebuiltLinkerProperties.Srcs[0]))
-				}
-			}
-
-		}
-	}
-
 	return prebuiltAttributes{
 		Src: srcLabelAttribute,
 	}
@@ -501,7 +400,7 @@
 
 	for _, props := range module.compiler.compilerProps() {
 		if baseCompilerProps, ok := props.(*BaseCompilerProperties); ok {
-			srcs.Value = parseSrcs(baseCompilerProps)
+			srcs.SetValue(parseSrcs(baseCompilerProps))
 			copts.Value = parseCopts(baseCompilerProps)
 			asFlags.Value = parseCommandLineFlags(baseCompilerProps.Asflags)
 			conlyFlags.Value = parseCommandLineFlags(baseCompilerProps.Conlyflags)
@@ -524,65 +423,45 @@
 		copts.Value = append(copts.Value, includeFlags(".")...)
 	}
 
-	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.
-			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-				srcsList := parseSrcs(baseCompilerProps)
-				srcs.SetValueForArch(arch.Name, srcsList)
-				// The base srcs value should not contain any arch-specific excludes.
-				srcs.Value = bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes})
-			}
+	archVariantCompilerProps := module.GetArchVariantProperties(ctx, &BaseCompilerProperties{})
 
-			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))
+	for axis, configToProps := range archVariantCompilerProps {
+		for config, props := range configToProps {
+			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.
+				if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
+					srcsList := parseSrcs(baseCompilerProps)
+					srcs.SetSelectValue(axis, config, srcsList)
+					// The base srcs value should not contain any arch-specific excludes.
+					srcs.SetValue(bazel.SubtractBazelLabelList(srcs.Value, bazel.LabelList{Includes: srcsList.Excludes}))
+				}
+
+				copts.SetSelectValue(axis, config, parseCopts(baseCompilerProps))
+				asFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Asflags))
+				conlyFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Conlyflags))
+				cppFlags.SetSelectValue(axis, config, parseCommandLineFlags(baseCompilerProps.Cppflags))
+			}
 		}
 	}
 
 	// 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(ctx, &BaseCompilerProperties{}) {
-		if _, ok := props.(*BaseCompilerProperties); ok {
-			srcs.SetValueForArch(arch.Name, bazel.SubtractBazelLabelList(srcs.GetValueForArch(arch.Name), srcs.Value))
+	for axis, configToProps := range archVariantCompilerProps {
+		for config, props := range configToProps {
+			if _, ok := props.(*BaseCompilerProperties); ok {
+				// TODO: handle non-arch
+				srcs.SetSelectValue(axis, config, bazel.SubtractBazelLabelList(srcs.SelectValue(axis, config), srcs.Value))
+			}
 		}
 	}
 
 	// Now that the srcs.Value list is finalized, compare it with the original
 	// list, and put the difference into the default condition for the arch
 	// select.
-	defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
-	// 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 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
-			if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-				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
-				if len(baseCompilerProps.Srcs) > 0 || len(baseCompilerProps.Exclude_srcs) > 0 {
-					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))
-			}
-		}
+	for axis := range archVariantCompilerProps {
+		defaultsSrcs := bazel.SubtractBazelLabelList(baseSrcsLabelList, srcs.Value)
+		srcs.SetSelectValue(axis, bazel.ConditionsDefault, defaultsSrcs)
 	}
 
 	productVarPropNameToAttribute := map[string]*bazel.StringListAttribute{
@@ -599,10 +478,7 @@
 					ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
 				}
 				newFlags, _ := bazel.TryVariableSubstitutions(flags, prop.ProductConfigVariable)
-				attr.ProductValues = append(attr.ProductValues, bazel.ProductVariableValues{
-					ProductVariable: prop.ProductConfigVariable,
-					Values:          newFlags,
-				})
+				attr.SetSelectValue(bazel.ProductVariableConfigurationAxis(prop.ProductConfigVariable), prop.ProductConfigVariable, newFlags)
 			}
 		}
 	}
@@ -667,7 +543,7 @@
 			wholeArchiveDeps = bazel.MakeLabelListAttribute(android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
 
 			if baseLinkerProps.Version_script != nil {
-				versionScript.Value = android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script)
+				versionScript.SetValue(android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
 			}
 
 			sharedLibs := baseLinkerProps.Shared_libs
@@ -677,61 +553,23 @@
 		}
 	}
 
-	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
-			deps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, libs))
-			exportedDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, exportedLibs))
-			linkopts.SetValueForArch(arch.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-			wholeArchiveDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
-
-			if baseLinkerProps.Version_script != nil {
-				versionScript.SetValueForArch(arch.Name,
-					android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
-			}
-
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetValueForArch(arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
-		}
-	}
-
-	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
-			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.SetOsValueForTarget(os.Name, getBp2BuildLinkerFlags(baseLinkerProps))
-
-			if baseLinkerProps.Version_script != nil {
-				versionScript.SetOsValueForTarget(os.Name, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
-			}
-
-			sharedLibs := baseLinkerProps.Shared_libs
-			dynamicDeps.SetOsValueForTarget(os.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
-		}
-		for arch, archProperties := range targetProperties.ArchProperties {
-			if baseLinkerProps, ok := archProperties.(*BaseLinkerProperties); ok {
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &BaseLinkerProperties{}) {
+		for config, props := range configToProps {
+			if baseLinkerProps, ok := props.(*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))
+				deps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, libs))
+				exportedDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, exportedLibs))
+				linkopts.SetSelectValue(axis, config, getBp2BuildLinkerFlags(baseLinkerProps))
+				wholeArchiveDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, wholeArchiveLibs))
 
 				if baseLinkerProps.Version_script != nil {
-					versionScript.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
+					versionScript.SetSelectValue(axis, config, android.BazelLabelForModuleSrcSingle(ctx, *baseLinkerProps.Version_script))
 				}
 
 				sharedLibs := baseLinkerProps.Shared_libs
-				dynamicDeps.SetOsArchValueForTarget(os.Name, arch.Name, android.BazelLabelForModuleDeps(ctx, sharedLibs))
+				dynamicDeps.SetSelectValue(axis, config, android.BazelLabelForModuleDeps(ctx, sharedLibs))
 			}
 		}
 	}
@@ -800,27 +638,12 @@
 		return variantIncludeDirs
 	}
 
-	for arch, props := range module.GetArchProperties(ctx, &FlagExporterProperties{}) {
-		if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
-			archIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
-			if len(archIncludeDirs) > 0 {
-				includeDirsAttribute.SetValueForArch(arch.Name, archIncludeDirs)
-			}
-		}
-	}
-
-	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)
+	for axis, configToProps := range module.GetArchVariantProperties(ctx, &FlagExporterProperties{}) {
+		for config, props := range configToProps {
+			if flagExporterProperties, ok := props.(*FlagExporterProperties); ok {
+				archVariantIncludeDirs := getVariantIncludeDirs(includeDirs, flagExporterProperties)
+				if len(archVariantIncludeDirs) > 0 {
+					includeDirsAttribute.SetSelectValue(axis, config, archVariantIncludeDirs)
 				}
 			}
 		}
diff --git a/cc/library.go b/cc/library.go
index 2b44a91..873489d 100644
--- a/cc/library.go
+++ b/cc/library.go
@@ -305,8 +305,7 @@
 	linkerAttrs := bp2BuildParseLinkerProps(ctx, m)
 	exportedIncludes := bp2BuildParseExportedIncludes(ctx, m)
 
-	var srcs bazel.LabelListAttribute
-	srcs.Append(compilerAttrs.srcs)
+	srcs := compilerAttrs.srcs
 
 	attrs := &bazelCcLibraryAttributes{
 		Srcs:    srcs,