Handle soong config vars for string attrs

Previously if there was a value set outside of product variables and one
inside a conditions default soong config var, we would ignore the
conditions default. Now, we maintain the conditions default and use
the root value in cases where there is not a value for a product
variable.

Test: go test soong tests
Change-Id: Ic991e3aebe5bb6039353f4e3d25625e7c5190f96
diff --git a/bazel/properties.go b/bazel/properties.go
index f56a613..8a6d1b0 100644
--- a/bazel/properties.go
+++ b/bazel/properties.go
@@ -1080,14 +1080,10 @@
 	if cs[axis] == nil {
 		cs[axis] = make(stringSelectValues)
 	}
-	var v = ""
-	if str != nil {
-		v = *str
-	}
-	cs[axis][config] = v
+	cs[axis][config] = str
 }
 
-type stringSelectValues map[string]string
+type stringSelectValues map[string]*string
 
 // HasConfigurableValues returns true if the attribute contains axis-specific string values.
 func (sa StringAttribute) HasConfigurableValues() bool {
@@ -1099,6 +1095,11 @@
 	return false
 }
 
+// SetValue sets the base, non-configured value for the Label
+func (sa *StringAttribute) SetValue(value string) {
+	sa.SetSelectValue(NoConfigAxis, "", &value)
+}
+
 // SetSelectValue set a value for a bazel select for the given axis, config and value.
 func (sa *StringAttribute) SetSelectValue(axis ConfigurationAxis, config string, str *string) {
 	axis.validateConfig(config)
@@ -1123,7 +1124,7 @@
 		return sa.Value
 	case arch, os, osArch, productVariables:
 		if v, ok := sa.ConfigurableValues[axis][config]; ok {
-			return &v
+			return v
 		} else {
 			return nil
 		}
@@ -1154,7 +1155,7 @@
 	_, containsProductVariables := axisTypes[productVariables]
 	if containsProductVariables {
 		if containsOs || containsArch || containsOsArch {
-			return fmt.Errorf("boolean attribute could not be collapsed as it has two or more unrelated axes")
+			return fmt.Errorf("string attribute could not be collapsed as it has two or more unrelated axes")
 		}
 	}
 	if (containsOs && containsArch) || (containsOsArch && (containsOs || containsArch)) {
@@ -1181,7 +1182,7 @@
 				}
 			}
 		}
-		// All os_arch values are now set. Clear os and arch axes.
+		/// All os_arch values are now set. Clear os and arch axes.
 		delete(sa.ConfigurableValues, ArchConfigurationAxis)
 		delete(sa.ConfigurableValues, OsConfigurationAxis)
 		// Verify post-condition; this should never fail, provided no additional
@@ -1189,6 +1190,21 @@
 		if len(sa.ConfigurableValues) > 1 {
 			panic(fmt.Errorf("error in collapsing attribute: %#v", sa))
 		}
+	} else if containsProductVariables {
+		usedBaseValue := false
+		for a, configToProp := range sa.ConfigurableValues {
+			if a.configurationType == productVariables {
+				for c, p := range configToProp {
+					if p == nil {
+						sa.SetSelectValue(a, c, sa.Value)
+						usedBaseValue = true
+					}
+				}
+			}
+		}
+		if usedBaseValue {
+			sa.Value = nil
+		}
 	}
 	return nil
 }
diff --git a/bp2build/configurability.go b/bp2build/configurability.go
index 987c903..4244956 100644
--- a/bp2build/configurability.go
+++ b/bp2build/configurability.go
@@ -28,10 +28,13 @@
 			ret[selectKey] = reflect.ValueOf(strs)
 		}
 	}
+
 	// if there is a select, use the base value as the conditions default value
 	if len(ret) > 0 {
-		ret[bazel.ConditionsDefaultSelectKey] = value
-		value = reflect.Zero(value.Type())
+		if _, ok := ret[bazel.ConditionsDefaultSelectKey]; !ok {
+			ret[bazel.ConditionsDefaultSelectKey] = value
+			value = reflect.Zero(value.Type())
+		}
 	}
 
 	return value, []selects{ret}
diff --git a/bp2build/soong_config_module_type_conversion_test.go b/bp2build/soong_config_module_type_conversion_test.go
index 5cc4983..89be767 100644
--- a/bp2build/soong_config_module_type_conversion_test.go
+++ b/bp2build/soong_config_module_type_conversion_test.go
@@ -32,6 +32,7 @@
 	android.RegisterSoongConfigModuleBuildComponents(ctx)
 
 	ctx.RegisterModuleType("cc_library", cc.LibraryFactory)
+	ctx.RegisterModuleType("custom", customModuleFactoryHostAndDevice)
 }
 
 func TestErrorInBpFileDoesNotPanic(t *testing.T) {
@@ -608,6 +609,55 @@
 )`}})
 }
 
+func TestSoongConfigModuleType_Defaults_UseBaselineValueForStringProp(t *testing.T) {
+	bp := `
+soong_config_string_variable {
+    name: "library_linking_strategy",
+    values: [
+        "prefer_static",
+    ],
+}
+
+soong_config_module_type {
+    name: "library_linking_strategy_custom",
+    module_type: "custom",
+    config_namespace: "ANDROID",
+    variables: ["library_linking_strategy"],
+    properties: [
+        "string_literal_prop",
+    ],
+}
+
+library_linking_strategy_custom {
+    name: "foo",
+    string_literal_prop: "29",
+    soong_config_variables: {
+        library_linking_strategy: {
+            prefer_static: {},
+            conditions_default: {
+              string_literal_prop: "30",
+            },
+        },
+    },
+}`
+
+	runSoongConfigModuleTypeTest(t, Bp2buildTestCase{
+		Description:                "soong config variables - generates selects for library_linking_strategy",
+		ModuleTypeUnderTest:        "cc_binary",
+		ModuleTypeUnderTestFactory: cc.BinaryFactory,
+		Blueprint:                  bp,
+		Filesystem:                 map[string]string{},
+		ExpectedBazelTargets: []string{
+			MakeBazelTarget("custom", "foo", AttrNameToString{
+				"string_literal_prop": `select({
+        "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": "29",
+        "//conditions:default": "30",
+    })`,
+			}),
+		},
+	})
+}
+
 func TestSoongConfigModuleType_UnsetConditions(t *testing.T) {
 	bp := `
 soong_config_string_variable {
diff --git a/bp2build/testing.go b/bp2build/testing.go
index 1f9874c..c340a8f 100644
--- a/bp2build/testing.go
+++ b/bp2build/testing.go
@@ -453,6 +453,14 @@
 			}
 		}
 	}
+	productVariableProps := android.ProductVariableProperties(ctx)
+	if props, ok := productVariableProps["String_literal_prop"]; ok {
+		for c, p := range props {
+			if val, ok := p.(*string); ok {
+				strAttr.SetSelectValue(c.ConfigurationAxis(), c.SelectKey(), val)
+			}
+		}
+	}
 
 	paths.ResolveExcludes()