Add support for merging defaults soong_config_module_types into bp2build
select statements.
This supports defaults from the same or different namespaces, and
transitively defaults as well.
Test: soong unit tests
Test: CI
Change-Id: I99435bacfcfbfe20ad753b8021a1779531d7595a
diff --git a/android/bazel.go b/android/bazel.go
index bf214a5..32a55ee 100644
--- a/android/bazel.go
+++ b/android/bazel.go
@@ -33,10 +33,10 @@
}
// namespacedVariableProperties is a map from a string representing a Soong
-// config variable namespace, like "android" or "vendor_name" to a struct
-// pointer representing the soong_config_variables property of a module created
-// by a soong_config_module_type or soong_config_module_type_import.
-type namespacedVariableProperties map[string]interface{}
+// config variable namespace, like "android" or "vendor_name" to a slice of
+// pointer to a struct containing a single field called Soong_config_variables
+// whose value mirrors the structure in the Blueprint file.
+type namespacedVariableProperties map[string][]interface{}
// BazelModuleBase contains the property structs with metadata for modules which can be converted to
// Bazel.
@@ -68,11 +68,19 @@
convertWithBp2build(ctx BazelConversionContext, module blueprint.Module) bool
GetBazelBuildFileContents(c Config, path, name string) (string, error)
- // For namespaced config variable support
+ // namespacedVariableProps is a map from a soong config variable namespace
+ // (e.g. acme, android) to a map of interfaces{}, which are really
+ // reflect.Struct pointers, representing the value of the
+ // soong_config_variables property of a module. The struct pointer is the
+ // one with the single member called Soong_config_variables, which itself is
+ // a struct containing fields for each supported feature in that namespace.
+ //
+ // The reason for using an slice of interface{} is to support defaults
+ // propagation of the struct pointers.
namespacedVariableProps() namespacedVariableProperties
setNamespacedVariableProps(props namespacedVariableProperties)
BaseModuleType() string
- SetBaseModuleType(string)
+ SetBaseModuleType(baseModuleType string)
}
// BazelModule is a lightweight wrapper interface around Module for Bazel-convertible modules.
diff --git a/android/defaults.go b/android/defaults.go
index be80cf1..9046002 100644
--- a/android/defaults.go
+++ b/android/defaults.go
@@ -213,10 +213,60 @@
var _ Defaults = (*DefaultsModuleBase)(nil)
+// applyNamespacedVariableDefaults only runs in bp2build mode for
+// defaultable/defaults modules. Its purpose is to merge namespaced product
+// variable props from defaults deps, even if those defaults are custom module
+// types created from soong_config_module_type, e.g. one that's wrapping a
+// cc_defaults or java_defaults.
+func applyNamespacedVariableDefaults(defaultDep Defaults, ctx TopDownMutatorContext) {
+ var dep, b Bazelable
+
+ dep, ok := defaultDep.(Bazelable)
+ if !ok {
+ if depMod, ok := defaultDep.(Module); ok {
+ // Track that this dependency hasn't been converted to bp2build yet.
+ ctx.AddUnconvertedBp2buildDep(depMod.Name())
+ return
+ } else {
+ panic("Expected default dep to be a Module.")
+ }
+ }
+
+ b, ok = ctx.Module().(Bazelable)
+ if !ok {
+ return
+ }
+
+ // namespacedVariableProps is a map from namespaces (e.g. acme, android,
+ // vendor_foo) to a slice of soong_config_variable struct pointers,
+ // containing properties for that particular module.
+ src := dep.namespacedVariableProps()
+ dst := b.namespacedVariableProps()
+ if dst == nil {
+ dst = make(namespacedVariableProperties)
+ }
+
+ // Propagate all soong_config_variable structs from the dep. We'll merge the
+ // actual property values later in variable.go.
+ for namespace := range src {
+ if dst[namespace] == nil {
+ dst[namespace] = []interface{}{}
+ }
+ for _, i := range src[namespace] {
+ dst[namespace] = append(dst[namespace], i)
+ }
+ }
+
+ b.setNamespacedVariableProps(dst)
+}
+
func (defaultable *DefaultableModuleBase) applyDefaults(ctx TopDownMutatorContext,
defaultsList []Defaults) {
for _, defaults := range defaultsList {
+ if ctx.Config().runningAsBp2Build {
+ applyNamespacedVariableDefaults(defaults, ctx)
+ }
for _, prop := range defaultable.defaultableProperties {
if prop == defaultable.defaultableVariableProperties {
defaultable.applyDefaultVariableProperties(ctx, defaults, prop)
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 065440d..dcf2223 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -434,7 +434,7 @@
// Instead of applying all properties, keep the entire conditionalProps struct as
// part of the custom module so dependent modules can create the selects accordingly
m.setNamespacedVariableProps(namespacedVariableProperties{
- moduleType.ConfigNamespace: conditionalProps.Interface(),
+ moduleType.ConfigNamespace: []interface{}{conditionalProps.Interface()},
})
}
})
diff --git a/android/variable.go b/android/variable.go
index 89cd59e..20b268e 100644
--- a/android/variable.go
+++ b/android/variable.go
@@ -640,13 +640,15 @@
}
if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
- for namespace, namespacedVariableProp := range m.namespacedVariableProps() {
- productVariableValues(
- soongconfig.SoongConfigProperty,
- namespacedVariableProp,
- namespace,
- "",
- &productConfigProperties)
+ for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
+ for _, namespacedVariableProp := range namespacedVariableProps {
+ productVariableValues(
+ soongconfig.SoongConfigProperty,
+ namespacedVariableProp,
+ namespace,
+ "",
+ &productConfigProperties)
+ }
}
}
@@ -665,7 +667,19 @@
FullConfig: config, // e.g. size, feature1-x86, size__conditions_default
}
- (*p)[propertyName][productConfigProp] = property
+ if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" {
+ switch dst := existing.(type) {
+ case []string:
+ if src, ok := property.([]string); ok {
+ dst = append(dst, src...)
+ (*p)[propertyName][productConfigProp] = dst
+ }
+ default:
+ // TODO(jingwen): Add support for more types.
+ }
+ } else {
+ (*p)[propertyName][productConfigProp] = property
+ }
}
var (
@@ -701,19 +715,10 @@
return v, true
}
-// productVariableValues uses reflection to convert a property struct for
-// product_variables and soong_config_variables to structs that can be generated
-// as select statements.
-func productVariableValues(
- fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
- if suffix != "" {
- suffix = "-" + suffix
- }
-
- // variableValues represent the product_variables or soong_config_variables
- // struct.
- variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
-
+func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value) {
+ // variableValues can either be a product_variables or
+ // soong_config_variables struct.
+ //
// Example of product_variables:
//
// product_variables: {
@@ -834,6 +839,20 @@
}
}
+// productVariableValues uses reflection to convert a property struct for
+// product_variables and soong_config_variables to structs that can be generated
+// as select statements.
+func productVariableValues(
+ fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties) {
+ if suffix != "" {
+ suffix = "-" + suffix
+ }
+
+ // variableValues represent the product_variables or soong_config_variables struct.
+ variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
+ productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues)
+}
+
func VariableMutator(mctx BottomUpMutatorContext) {
var module Module
var ok bool