Make bp2build-generated selects() based on product config build settings

...instead of based on constraint settings.

Bug: 269577299
Test: m nothing and ./build/bazel/ci/bp2build.sh
Change-Id: Ib9caec79c92b8fd304e46be841de5612bd1637e3
diff --git a/android/config.go b/android/config.go
index 72ff224..eb89493 100644
--- a/android/config.go
+++ b/android/config.go
@@ -414,6 +414,12 @@
 	return nil
 }
 
+type productVariableStarlarkRepresentation struct {
+	soongType   string
+	selectable  bool
+	archVariant bool
+}
+
 func saveToBazelConfigFile(config *ProductVariables, outDir string) error {
 	dir := filepath.Join(outDir, bazel.SoongInjectionDirName, "product_config")
 	err := createDirIfNonexistent(dir, os.ModePerm)
@@ -421,32 +427,43 @@
 		return fmt.Errorf("Could not create dir %s: %s", dir, err)
 	}
 
-	nonArchVariantProductVariables := []string{}
-	archVariantProductVariables := []string{}
+	allProductVariablesType := reflect.TypeOf((*ProductVariables)(nil)).Elem()
+	productVariablesInfo := make(map[string]productVariableStarlarkRepresentation)
 	p := variableProperties{}
 	t := reflect.TypeOf(p.Product_variables)
 	for i := 0; i < t.NumField(); i++ {
 		f := t.Field(i)
-		nonArchVariantProductVariables = append(nonArchVariantProductVariables, strings.ToLower(f.Name))
-		if proptools.HasTag(f, "android", "arch_variant") {
-			archVariantProductVariables = append(archVariantProductVariables, strings.ToLower(f.Name))
+		if f.Name == "Pdk" {
+			// Pdk is deprecated and has no effect as of aosp/1319667
+			continue
+		}
+		archVariant := proptools.HasTag(f, "android", "arch_variant")
+		if mainProductVariablesStructField, ok := allProductVariablesType.FieldByName(f.Name); ok {
+			productVariablesInfo[f.Name] = productVariableStarlarkRepresentation{
+				soongType:   stringRepresentationOfSimpleType(mainProductVariablesStructField.Type),
+				selectable:  true,
+				archVariant: archVariant,
+			}
+		} else {
+			panic("Unknown variable " + f.Name)
 		}
 	}
 
-	nonArchVariantProductVariablesJson := starlark_fmt.PrintStringList(nonArchVariantProductVariables, 0)
-	if err != nil {
-		return fmt.Errorf("cannot marshal product variable data: %s", err.Error())
-	}
-
-	archVariantProductVariablesJson := starlark_fmt.PrintStringList(archVariantProductVariables, 0)
-	if err != nil {
-		return fmt.Errorf("cannot marshal arch variant product variable data: %s", err.Error())
-	}
-
 	err = pathtools.WriteFileIfChanged(filepath.Join(dir, "product_variable_constants.bzl"), []byte(fmt.Sprintf(`
-product_var_constraints = %s
-arch_variant_product_var_constraints = %s
-`, nonArchVariantProductVariablesJson, archVariantProductVariablesJson)), 0644)
+# product_var_constant_info is a map of product variables to information about them. The fields are:
+# - soongType: The type of the product variable as it appears in soong's ProductVariables struct.
+#              examples are string, bool, int, *bool, *string, []string, etc. This may be an overly
+#              conservative estimation of the type, for example a *bool could oftentimes just be a
+#              bool that defaults to false.
+# - selectable: if this product variable can be selected on in Android.bp/build files. This means
+#               it's listed in the "variableProperties" soong struct. Currently all variables in
+#               this list are selectable because we only need the selectable ones at the moment,
+#               but the list may be expanded later.
+# - archVariant: If the variable is tagged as arch variant in the "variableProperties" struct.
+product_var_constant_info = %s
+product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable]
+arch_variant_product_var_constraints = [k for k, v in product_var_constant_info.items() if v.selectable and v.archVariant]
+`, starlark_fmt.PrintAny(productVariablesInfo, 0))), 0644)
 	if err != nil {
 		return fmt.Errorf("Could not write .bzl config file %s", err)
 	}
@@ -459,6 +476,23 @@
 	return nil
 }
 
+func stringRepresentationOfSimpleType(ty reflect.Type) string {
+	switch ty.Kind() {
+	case reflect.String:
+		return "string"
+	case reflect.Bool:
+		return "bool"
+	case reflect.Int:
+		return "int"
+	case reflect.Slice:
+		return "[]" + stringRepresentationOfSimpleType(ty.Elem())
+	case reflect.Pointer:
+		return "*" + stringRepresentationOfSimpleType(ty.Elem())
+	default:
+		panic("unimplemented type: " + ty.Kind().String())
+	}
+}
+
 // NullConfig returns a mostly empty Config for use by standalone tools like dexpreopt_gen that
 // use the android package.
 func NullConfig(outDir, soongOutDir string) Config {