bp2build: add support for soong_config_module_type.
Test: CI, go unit test Bug: 198556411 Change-Id: Idf862904d51d822f92af0c072341c31b7a02fc64
This commit is contained in:
@@ -15,6 +15,8 @@
|
||||
package android
|
||||
|
||||
import (
|
||||
"android/soong/android/soongconfig"
|
||||
"android/soong/bazel"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"runtime"
|
||||
@@ -487,13 +489,97 @@ type ProductConfigContext interface {
|
||||
// ProductConfigProperty contains the information for a single property (may be a struct) paired
|
||||
// with the appropriate ProductConfigVariable.
|
||||
type ProductConfigProperty struct {
|
||||
// The name of the product variable, e.g. "safestack", "malloc_not_svelte",
|
||||
// "board"
|
||||
ProductConfigVariable string
|
||||
FullConfig string
|
||||
Property interface{}
|
||||
|
||||
// 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
|
||||
|
||||
// Unique configuration to identify this product config property (i.e. a
|
||||
// primary key), as just using the product variable name is not sufficient.
|
||||
//
|
||||
// For product variables, this is the product variable name + optional
|
||||
// archvariant information. e.g.
|
||||
//
|
||||
// product_variables: {
|
||||
// foo: {
|
||||
// cflags: ["-Dfoo"],
|
||||
// },
|
||||
// },
|
||||
//
|
||||
// FullConfig would be "foo".
|
||||
//
|
||||
// target: {
|
||||
// android: {
|
||||
// product_variables: {
|
||||
// foo: {
|
||||
// cflags: ["-Dfoo-android"],
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
// },
|
||||
//
|
||||
// FullConfig would be "foo-android".
|
||||
//
|
||||
// For soong config variables, this is the namespace + product variable name
|
||||
// + value of the variable, if applicable. The value can also be
|
||||
// conditions_default.
|
||||
//
|
||||
// e.g.
|
||||
//
|
||||
// soong_config_variables: {
|
||||
// feature1: {
|
||||
// conditions_default: {
|
||||
// cflags: ["-DDEFAULT1"],
|
||||
// },
|
||||
// cflags: ["-DFEATURE1"],
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// where feature1 is created in the "acme" namespace, so FullConfig would be
|
||||
// "acme__feature1" and "acme__feature1__conditions_default".
|
||||
//
|
||||
// e.g.
|
||||
//
|
||||
// soong_config_variables: {
|
||||
// board: {
|
||||
// soc_a: {
|
||||
// cflags: ["-DSOC_A"],
|
||||
// },
|
||||
// soc_b: {
|
||||
// cflags: ["-DSOC_B"],
|
||||
// },
|
||||
// soc_c: {},
|
||||
// conditions_default: {
|
||||
// cflags: ["-DSOC_DEFAULT"]
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// where board is created in the "acme" namespace, so FullConfig would be
|
||||
// "acme__board__soc_a", "acme__board__soc_b", and
|
||||
// "acme__board__conditions_default"
|
||||
FullConfig string
|
||||
|
||||
// The actual property value: list, bool, string..
|
||||
Property interface{}
|
||||
}
|
||||
|
||||
// ProductConfigProperties is a map of property name to a slice of ProductConfigProperty such that
|
||||
// all it all product variable-specific versions of a property are easily accessed together
|
||||
func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
|
||||
if p.Namespace == "" {
|
||||
return bazel.ProductVariableConfigurationAxis(p.FullConfig)
|
||||
} 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)
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// ProductVariableProperties returns a ProductConfigProperties containing only the properties which
|
||||
@@ -504,36 +590,165 @@ func ProductVariableProperties(ctx BazelConversionPathContext) ProductConfigProp
|
||||
|
||||
productConfigProperties := ProductConfigProperties{}
|
||||
|
||||
if moduleBase.variableProperties == nil {
|
||||
return productConfigProperties
|
||||
if moduleBase.variableProperties != nil {
|
||||
productVariablesProperty := proptools.FieldNameForProperty("product_variables")
|
||||
productVariableValues(
|
||||
productVariablesProperty,
|
||||
moduleBase.variableProperties,
|
||||
"",
|
||||
"",
|
||||
&productConfigProperties)
|
||||
|
||||
for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
|
||||
for config, props := range configToProps {
|
||||
// GetArchVariantProperties is creating an instance of the requested type
|
||||
// and productVariablesValues expects an interface, so no need to cast
|
||||
productVariableValues(
|
||||
productVariablesProperty,
|
||||
props,
|
||||
"",
|
||||
config,
|
||||
&productConfigProperties)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
productVariableValues(moduleBase.variableProperties, "", &productConfigProperties)
|
||||
|
||||
for _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
|
||||
for config, props := range configToProps {
|
||||
// GetArchVariantProperties is creating an instance of the requested type
|
||||
// and productVariablesValues expects an interface, so no need to cast
|
||||
productVariableValues(props, config, &productConfigProperties)
|
||||
if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
|
||||
for namespace, namespacedVariableProp := range m.namespacedVariableProps() {
|
||||
productVariableValues(
|
||||
soongconfig.SoongConfigProperty,
|
||||
namespacedVariableProp,
|
||||
namespace,
|
||||
"",
|
||||
&productConfigProperties)
|
||||
}
|
||||
}
|
||||
|
||||
return productConfigProperties
|
||||
}
|
||||
|
||||
func productVariableValues(variableProps interface{}, suffix string, productConfigProperties *ProductConfigProperties) {
|
||||
func (p *ProductConfigProperties) AddProductConfigProperty(
|
||||
propertyName, namespace, productVariableName, config string, property interface{}) {
|
||||
if (*p)[propertyName] == nil {
|
||||
(*p)[propertyName] = make(map[string]ProductConfigProperty)
|
||||
}
|
||||
|
||||
// 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"]
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
conditionsDefaultField string = proptools.FieldNameForProperty(bazel.ConditionsDefaultConfigKey)
|
||||
)
|
||||
|
||||
// maybeExtractConfigVarProp attempts to read this value as a config var struct
|
||||
// wrapped by interfaces and ptrs. If it's not the right type, the second return
|
||||
// value is false.
|
||||
func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
|
||||
if v.Kind() == reflect.Interface {
|
||||
// The conditions_default value can be either
|
||||
// 1) an ptr to an interface of a struct (bool config variables and product variables)
|
||||
// 2) an interface of 1) (config variables with nested structs, like string vars)
|
||||
v = v.Elem()
|
||||
}
|
||||
if v.Kind() != reflect.Ptr {
|
||||
return v, false
|
||||
}
|
||||
v = reflect.Indirect(v)
|
||||
if v.Kind() == reflect.Interface {
|
||||
// Extract the struct from the interface
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
if !v.IsValid() {
|
||||
return v, false
|
||||
}
|
||||
|
||||
if v.Kind() != reflect.Struct {
|
||||
return v, false
|
||||
}
|
||||
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 := reflect.ValueOf(variableProps).Elem().FieldByName("Product_variables")
|
||||
|
||||
// variableValues represent the product_variables or soong_config_variables
|
||||
// struct.
|
||||
variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName)
|
||||
|
||||
// Example of product_variables:
|
||||
//
|
||||
// product_variables: {
|
||||
// malloc_not_svelte: {
|
||||
// shared_libs: ["malloc_not_svelte_shared_lib"],
|
||||
// whole_static_libs: ["malloc_not_svelte_whole_static_lib"],
|
||||
// exclude_static_libs: [
|
||||
// "malloc_not_svelte_static_lib_excludes",
|
||||
// "malloc_not_svelte_whole_static_lib_excludes",
|
||||
// ],
|
||||
// },
|
||||
// },
|
||||
//
|
||||
// Example of soong_config_variables:
|
||||
//
|
||||
// soong_config_variables: {
|
||||
// feature1: {
|
||||
// conditions_default: {
|
||||
// ...
|
||||
// },
|
||||
// cflags: ...
|
||||
// },
|
||||
// feature2: {
|
||||
// cflags: ...
|
||||
// conditions_default: {
|
||||
// ...
|
||||
// },
|
||||
// },
|
||||
// board: {
|
||||
// soc_a: {
|
||||
// ...
|
||||
// },
|
||||
// soc_a: {
|
||||
// ...
|
||||
// },
|
||||
// soc_c: {},
|
||||
// conditions_default: {
|
||||
// ...
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
for i := 0; i < variableValues.NumField(); i++ {
|
||||
// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
|
||||
productVariableName := variableValues.Type().Field(i).Name
|
||||
|
||||
variableValue := variableValues.Field(i)
|
||||
// Check if any properties were set for the module
|
||||
if variableValue.IsZero() {
|
||||
// e.g. feature1: {}, malloc_not_svelte: {}
|
||||
continue
|
||||
}
|
||||
// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
|
||||
productVariableName := variableValues.Type().Field(i).Name
|
||||
|
||||
// Unlike product variables, config variables require a few more
|
||||
// indirections to extract the struct from the reflect.Value.
|
||||
if v, ok := maybeExtractConfigVarProp(variableValue); ok {
|
||||
variableValue = v
|
||||
}
|
||||
|
||||
for j := 0; j < variableValue.NumField(); j++ {
|
||||
property := variableValue.Field(j)
|
||||
// If the property wasn't set, no need to pass it along
|
||||
@@ -543,14 +758,57 @@ func productVariableValues(variableProps interface{}, suffix string, productConf
|
||||
|
||||
// e.g. Asflags, Cflags, Enabled, etc.
|
||||
propertyName := variableValue.Type().Field(j).Name
|
||||
if (*productConfigProperties)[propertyName] == nil {
|
||||
(*productConfigProperties)[propertyName] = make(map[string]ProductConfigProperty)
|
||||
}
|
||||
config := productVariableName + suffix
|
||||
(*productConfigProperties)[propertyName][config] = ProductConfigProperty{
|
||||
ProductConfigVariable: productVariableName,
|
||||
FullConfig: config,
|
||||
Property: property.Interface(),
|
||||
|
||||
if v, ok := maybeExtractConfigVarProp(property); ok {
|
||||
// The field is a struct, which is used by:
|
||||
// 1) soong_config_string_variables
|
||||
//
|
||||
// soc_a: {
|
||||
// cflags: ...,
|
||||
// }
|
||||
//
|
||||
// soc_b: {
|
||||
// cflags: ...,
|
||||
// }
|
||||
//
|
||||
// 2) conditions_default structs for all soong config variable types.
|
||||
//
|
||||
// conditions_default: {
|
||||
// cflags: ...,
|
||||
// static_libs: ...
|
||||
// }
|
||||
field := v
|
||||
for k := 0; k < field.NumField(); k++ {
|
||||
// Iterate over fields of this struct prop.
|
||||
if field.Field(k).IsZero() {
|
||||
continue
|
||||
}
|
||||
productVariableValue := proptools.PropertyNameForField(propertyName)
|
||||
config := strings.Join([]string{namespace, productVariableName, productVariableValue}, "__")
|
||||
actualPropertyName := field.Type().Field(k).Name
|
||||
|
||||
productConfigProperties.AddProductConfigProperty(
|
||||
actualPropertyName, // e.g. cflags, static_libs
|
||||
namespace, // e.g. acme, android
|
||||
productVariableName, // e.g. size, feature1, FEATURE2, board
|
||||
config,
|
||||
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.
|
||||
config := productVariableName + suffix
|
||||
if namespace != "" {
|
||||
config = namespace + "__" + config
|
||||
}
|
||||
productConfigProperties.AddProductConfigProperty(
|
||||
propertyName,
|
||||
namespace,
|
||||
productVariableName,
|
||||
config,
|
||||
property.Interface(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user