Refactor ProductConfigProperties to use a struct key instead of an
string key with hardcoded patterns.

This fixes a bug with label list conditions_default attrs where the
attribute values get clobbered in a map with the keys
"conditions_default" (with a default empty list) and
"acme__feature__conditions_default" (with a non-empty list) when
generating the LabelListAttribute.

Test: CI
Change-Id: I5429e40f747b7a0ed559f8a468a4831cd32df2c0
diff --git a/android/variable.go b/android/variable.go
index 6ad58c3..89cd59e 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -491,11 +491,11 @@
 type ProductConfigProperty struct {
 	// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
 	// "board"
-	ProductConfigVariable string
+	Name string
 
 	// Namespace of the variable, if this is a soong_config_module_type variable
-	// e.g. "acme", "ANDROID", "vendor_nae"
-	Namespace string // for soong config variables
+	// e.g. "acme", "ANDROID", "vendor_name"
+	Namespace string
 
 	// Unique configuration to identify this product config property (i.e. a
 	// primary key), as just using the product variable name is not sufficient.
@@ -562,9 +562,6 @@
 	// "acme__board__soc_a", "acme__board__soc_b", and
 	// "acme__board__conditions_default"
 	FullConfig string
-
-	// The actual property value: list, bool, string..
-	Property interface{}
 }
 
 func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
@@ -573,14 +570,43 @@
 	} else {
 		// Soong config variables can be uniquely identified by the namespace
 		// (e.g. acme, android) and the product variable name (e.g. board, size)
-		return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.ProductConfigVariable)
+		return bazel.ProductVariableConfigurationAxis(p.Namespace + "__" + p.Name)
 	}
 }
 
-// ProductConfigProperties is a map of property name to a slice of
-// ProductConfigProperty such that all product variable-specific versions of a
-// property are easily accessed together
-type ProductConfigProperties map[string]map[string]ProductConfigProperty
+// SelectKey returns the literal string that represents this variable in a BUILD
+// select statement.
+func (p *ProductConfigProperty) SelectKey() string {
+	if p.Namespace == "" {
+		return strings.ToLower(p.FullConfig)
+	}
+
+	if p.FullConfig == bazel.ConditionsDefaultConfigKey {
+		return bazel.ConditionsDefaultConfigKey
+	}
+
+	value := p.FullConfig
+	if value == p.Name {
+		value = "enabled"
+	}
+	// e.g. acme__feature1__enabled, android__board__soc_a
+	return strings.ToLower(strings.Join([]string{p.Namespace, p.Name, value}, "__"))
+}
+
+// ProductConfigProperties is a map of maps to group property values according
+// their property name and the product config variable they're set under.
+//
+// The outer map key is the name of the property, like "cflags".
+//
+// The inner map key is a ProductConfigProperty, which is a struct of product
+// variable name, namespace, and the "full configuration" of the product
+// variable.
+//
+// e.g. product variable name: board, namespace: acme, full config: vendor_chip_foo
+//
+// The value of the map is the interface{} representing the value of the
+// property, like ["-DDEFINES"] for cflags.
+type ProductConfigProperties map[string]map[ProductConfigProperty]interface{}
 
 // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
 // have been set for the module in the given context.
@@ -630,19 +656,16 @@
 func (p *ProductConfigProperties) AddProductConfigProperty(
 	propertyName, namespace, productVariableName, config string, property interface{}) {
 	if (*p)[propertyName] == nil {
-		(*p)[propertyName] = make(map[string]ProductConfigProperty)
+		(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
 	}
 
-	// Normalize config to be all lowercase. It's the "primary key" of this
-	// unique property value. This can be the conditions_default value of the
-	// product variable as well.
-	config = strings.ToLower(config)
-	(*p)[propertyName][config] = ProductConfigProperty{
-		Namespace:             namespace,           // e.g. acme, android
-		ProductConfigVariable: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
-		FullConfig:            config,              // e.g. size, feature1-x86, size__conditions_default
-		Property:              property,            // e.g. ["-O3"]
+	productConfigProp := ProductConfigProperty{
+		Namespace:  namespace,           // e.g. acme, android
+		Name:       productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
+		FullConfig: config,              // e.g. size, feature1-x86, size__conditions_default
 	}
+
+	(*p)[propertyName][productConfigProp] = property
 }
 
 var (
@@ -783,8 +806,8 @@
 					if field.Field(k).IsZero() {
 						continue
 					}
-					productVariableValue := proptools.PropertyNameForField(propertyName)
-					config := strings.Join([]string{namespace, productVariableName, productVariableValue}, "__")
+					// config can also be "conditions_default".
+					config := proptools.PropertyNameForField(propertyName)
 					actualPropertyName := field.Type().Field(k).Name
 
 					productConfigProperties.AddProductConfigProperty(
@@ -799,9 +822,6 @@
 				// Not a conditions_default or a struct prop, i.e. regular
 				// product variables, or not a string-typed config var.
 				config := productVariableName + suffix
-				if namespace != "" {
-					config = namespace + "__" + config
-				}
 				productConfigProperties.AddProductConfigProperty(
 					propertyName,
 					namespace,