Fix bp2build select generation for inter-attribute soong config
variable usage.
There's bug a in the current soong_config_variable handling
implementation where a soong_config_variable sets conditions_default
value for an attr, and a non-conditions_default value for another attr.
This results in the former attr not properly setting the zero value for
the non-conditions_default select key, resulting in the pretty printer
omitting the attribute totally.
The current implementation in this CL ensures that the zero value is set
whenever this happens at the module level. This is seen in
library_linking_strategy_cc_defaults (see comments in code, and the new
tests)
Test: CI
Bug: 198556411
Change-Id: Ibaeb94508c51a7429fb7a08df610cbb5470f76d2
diff --git a/android/variable.go b/android/variable.go
index 20b268e..fe828d3 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -564,6 +564,10 @@
FullConfig string
}
+func (p *ProductConfigProperty) AlwaysEmit() bool {
+ return p.Namespace != ""
+}
+
func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
if p.Namespace == "" {
return bazel.ProductVariableConfigurationAxis(p.FullConfig)
@@ -652,9 +656,123 @@
}
}
+ productConfigProperties.zeroValuesForNamespacedVariables()
+
return productConfigProperties
}
+// zeroValuesForNamespacedVariables ensures that selects that contain __only__
+// conditions default values have zero values set for the other non-default
+// values for that select statement.
+//
+// If the ProductConfigProperties map contains these items, as parsed from the .bp file:
+//
+// library_linking_strategy: {
+// prefer_static: {
+// static_libs: [
+// "lib_a",
+// "lib_b",
+// ],
+// },
+// conditions_default: {
+// shared_libs: [
+// "lib_a",
+// "lib_b",
+// ],
+// },
+// },
+//
+// Static_libs {Library_linking_strategy ANDROID prefer_static} [lib_a lib_b]
+// Shared_libs {Library_linking_strategy ANDROID conditions_default} [lib_a lib_b]
+//
+// We need to add this:
+//
+// Shared_libs {Library_linking_strategy ANDROID prefer_static} []
+//
+// so that the following gets generated for the "dynamic_deps" attribute,
+// instead of putting lib_a and lib_b directly into dynamic_deps without a
+// select:
+//
+// dynamic_deps = select({
+// "//build/bazel/product_variables:android__library_linking_strategy__prefer_static": [],
+// "//conditions:default": [
+// "//foo/bar:lib_a",
+// "//foo/bar:lib_b",
+// ],
+// }),
+func (props *ProductConfigProperties) zeroValuesForNamespacedVariables() {
+ // A map of product config properties to the zero values of their respective
+ // property value.
+ zeroValues := make(map[ProductConfigProperty]interface{})
+
+ // A map of prop names (e.g. cflags) to product config properties where the
+ // (prop name, ProductConfigProperty) tuple contains a non-conditions_default key.
+ //
+ // e.g.
+ //
+ // prefer_static: {
+ // static_libs: [
+ // "lib_a",
+ // "lib_b",
+ // ],
+ // },
+ // conditions_default: {
+ // shared_libs: [
+ // "lib_a",
+ // "lib_b",
+ // ],
+ // },
+ //
+ // The tuple of ("static_libs", prefer_static) would be in this map.
+ hasNonDefaultValue := make(map[string]map[ProductConfigProperty]bool)
+
+ // Iterate over all added soong config variables.
+ for propName, v := range *props {
+ for p, intf := range v {
+ if p.Namespace == "" {
+ // If there's no namespace, this isn't a soong config variable,
+ // i.e. this is a product variable. product variables have no
+ // conditions_defaults, so skip them.
+ continue
+ }
+ if p.FullConfig == bazel.ConditionsDefaultConfigKey {
+ // Skip conditions_defaults.
+ continue
+ }
+ if hasNonDefaultValue[propName] == nil {
+ hasNonDefaultValue[propName] = make(map[ProductConfigProperty]bool)
+ hasNonDefaultValue[propName][p] = false
+ }
+ // Create the zero value of the variable.
+ if _, exists := zeroValues[p]; !exists {
+ zeroValue := reflect.Zero(reflect.ValueOf(intf).Type()).Interface()
+ if zeroValue == nil {
+ panic(fmt.Errorf("Expected non-nil zero value for product/config variable %+v\n", intf))
+ }
+ zeroValues[p] = zeroValue
+ }
+ hasNonDefaultValue[propName][p] = true
+ }
+ }
+
+ for propName := range *props {
+ for p, zeroValue := range zeroValues {
+ // Ignore variables that already have a non-default value for that axis
+ if exists, _ := hasNonDefaultValue[propName][p]; !exists {
+ // fmt.Println(propName, p.Namespace, p.Name, p.FullConfig, zeroValue)
+ // Insert the zero value for this propname + product config value.
+ props.AddProductConfigProperty(
+ propName,
+ p.Namespace,
+ p.Name,
+ p.FullConfig,
+ zeroValue,
+ )
+ }
+ }
+ }
+}
+
func (p *ProductConfigProperties) AddProductConfigProperty(
propertyName, namespace, productVariableName, config string, property interface{}) {
if (*p)[propertyName] == nil {
@@ -675,7 +793,7 @@
(*p)[propertyName][productConfigProp] = dst
}
default:
- // TODO(jingwen): Add support for more types.
+ panic(fmt.Errorf("TODO: handle merging value %s", existing))
}
} else {
(*p)[propertyName][productConfigProp] = property
@@ -823,9 +941,10 @@
field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"]
)
}
- } else {
- // Not a conditions_default or a struct prop, i.e. regular
- // product variables, or not a string-typed config var.
+ } else if property.Kind() != reflect.Interface {
+ // If not an interface, then this is not a conditions_default or
+ // a struct prop. That is, this is a regular product variable,
+ // or a bool/value config variable.
config := productVariableName + suffix
productConfigProperties.AddProductConfigProperty(
propertyName,