Merge "Bp2build support for soong config variables + os" am: 069812158f am: 60fff115b1 am: 89aed7bcb4 am: 6234e1ff39

Original change: https://android-review.googlesource.com/c/platform/build/soong/+/2576792

Change-Id: If196edcde65b855b6a5d86375dd6e45f1a6f78a5
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Cole Faust
2023-05-11 00:29:37 +00:00
committed by Automerger Merge Worker
6 changed files with 350 additions and 230 deletions

View File

@@ -541,124 +541,102 @@ type ProductConfigContext interface {
Module() Module Module() Module
} }
// ProductConfigProperty contains the information for a single property (may be a struct) paired // ProductConfigOrSoongConfigProperty represents either a soong config variable + its value
// with the appropriate ProductConfigVariable. // or a product config variable. You can get both a ConfigurationAxis and a SelectKey from it
// for use in bazel attributes. ProductVariableProperties() will return a map from properties ->
// this interface -> property structs for use in bp2build converters
type ProductConfigOrSoongConfigProperty interface {
// Name of the product variable or soong config variable
Name() string
// AlwaysEmit returns true for soong config variables but false for product variables. This
// is intended to indicate if we need to always emit empty lists in the select statements.
AlwaysEmit() bool
// ConfigurationAxis returns the bazel.ConfigurationAxis that represents this variable. The
// configuration axis will change depending on the variable and whether it's arch/os variant
// as well.
ConfigurationAxis() bazel.ConfigurationAxis
// SelectKey returns a string that represents the key of a select branch, however, it is not
// actually the real label written out to the build file.
// this.ConfigurationAxis().SelectKey(this.SelectKey()) will give the actual label.
SelectKey() string
}
// ProductConfigProperty represents a product config variable, and if it is arch-variant or not.
type ProductConfigProperty struct { type ProductConfigProperty struct {
// The name of the product variable, e.g. "safestack", "malloc_not_svelte", // The name of the product variable, e.g. "safestack", "malloc_not_svelte",
// "board" // "board"
Name string name string
// Namespace of the variable, if this is a soong_config_module_type variable arch string
// e.g. "acme", "ANDROID", "vendor_name"
Namespace string
// 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
// keeps track of whether this product variable is nested under an arch variant
OuterAxis bazel.ConfigurationAxis
} }
func (p *ProductConfigProperty) AlwaysEmit() bool { func (p ProductConfigProperty) Name() string {
return p.Namespace != "" return p.name
} }
func (p *ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis { func (p ProductConfigProperty) AlwaysEmit() bool {
if p.Namespace == "" { return false
return bazel.ProductVariableConfigurationAxis(p.FullConfig, p.OuterAxis) }
func (p ProductConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
return bazel.ProductVariableConfigurationAxis(p.arch != "", p.name+"__"+p.arch)
}
func (p ProductConfigProperty) SelectKey() string {
if p.arch == "" {
return strings.ToLower(p.name)
} else { } else {
// Soong config variables can be uniquely identified by the namespace return strings.ToLower(p.name + "-" + p.arch)
// (e.g. acme, android) and the product variable name (e.g. board, size)
return bazel.ProductVariableConfigurationAxis(p.Namespace+"__"+p.Name, bazel.NoConfigAxis)
} }
} }
// SoongConfigProperty represents a soong config variable, its value if it's a string variable,
// and if it's dependent on the OS or not
type SoongConfigProperty struct {
name string
namespace string
// Can be an empty string for bool/value soong config variables
value string
// If there is a target: field inside a soong config property struct, the os that it selects
// on will be represented here.
os string
}
func (p SoongConfigProperty) Name() string {
return p.name
}
func (p SoongConfigProperty) AlwaysEmit() bool {
return true
}
func (p SoongConfigProperty) ConfigurationAxis() bazel.ConfigurationAxis {
return bazel.ProductVariableConfigurationAxis(false, p.namespace+"__"+p.name+"__"+p.os)
}
// SelectKey returns the literal string that represents this variable in a BUILD // SelectKey returns the literal string that represents this variable in a BUILD
// select statement. // select statement.
func (p *ProductConfigProperty) SelectKey() string { func (p SoongConfigProperty) SelectKey() string {
if p.Namespace == "" { // p.value being conditions_default can happen with or without a desired os. When not using
return strings.ToLower(p.FullConfig) // an os, we want to emit literally just //conditions:default in the select statement, but
} // when using an os, we want to emit namespace__name__conditions_default__os, so that
// the branch is only taken if the variable is not set, and we're on the desired os.
if p.FullConfig == bazel.ConditionsDefaultConfigKey { // ConfigurationAxis#SelectKey will map the conditions_default result of this function to
// //conditions:default.
if p.value == bazel.ConditionsDefaultConfigKey && p.os == "" {
return bazel.ConditionsDefaultConfigKey return bazel.ConditionsDefaultConfigKey
} }
value := p.FullConfig parts := []string{p.namespace, p.name}
if value == p.Name { if p.value != "" && p.value != bazel.ConditionsDefaultSelectKey {
value = "" parts = append(parts, p.value)
}
if p.os != "" {
parts = append(parts, p.os)
} }
// e.g. acme__feature1, android__board__soc_a // e.g. acme__feature1, android__board__soc_a, my_namespace__my_variables__my_value__my_os
selectKey := strings.ToLower(strings.Join([]string{p.Namespace, p.Name}, "__")) return strings.ToLower(strings.Join(parts, "__"))
if value != "" {
selectKey = strings.ToLower(strings.Join([]string{selectKey, value}, "__"))
}
return selectKey
} }
// ProductConfigProperties is a map of maps to group property values according // ProductConfigProperties is a map of maps to group property values according
@@ -674,7 +652,7 @@ func (p *ProductConfigProperty) SelectKey() string {
// //
// The value of the map is the interface{} representing the value of the // The value of the map is the interface{} representing the value of the
// property, like ["-DDEFINES"] for cflags. // property, like ["-DDEFINES"] for cflags.
type ProductConfigProperties map[string]map[ProductConfigProperty]interface{} type ProductConfigProperties map[string]map[ProductConfigOrSoongConfigProperty]interface{}
// ProductVariableProperties returns a ProductConfigProperties containing only the properties which // ProductVariableProperties returns a ProductConfigProperties containing only the properties which
// have been set for the given module. // have been set for the given module.
@@ -685,26 +663,10 @@ func ProductVariableProperties(ctx ArchVariantContext, module Module) ProductCon
if moduleBase.variableProperties != nil { if moduleBase.variableProperties != nil {
productVariablesProperty := proptools.FieldNameForProperty("product_variables") productVariablesProperty := proptools.FieldNameForProperty("product_variables")
productVariableValues( for /* axis */ _, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
productVariablesProperty,
moduleBase.variableProperties,
"",
"",
&productConfigProperties,
bazel.ConfigurationAxis{},
)
for axis, configToProps := range moduleBase.GetArchVariantProperties(ctx, moduleBase.variableProperties) {
for config, props := range configToProps { for config, props := range configToProps {
// GetArchVariantProperties is creating an instance of the requested type variableValues := reflect.ValueOf(props).Elem().FieldByName(productVariablesProperty)
// and productVariablesValues expects an interface, so no need to cast productConfigProperties.AddProductConfigProperties(variableValues, config)
productVariableValues(
productVariablesProperty,
props,
"",
config,
&productConfigProperties,
axis)
} }
} }
} }
@@ -712,13 +674,8 @@ func ProductVariableProperties(ctx ArchVariantContext, module Module) ProductCon
if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil { if m, ok := module.(Bazelable); ok && m.namespacedVariableProps() != nil {
for namespace, namespacedVariableProps := range m.namespacedVariableProps() { for namespace, namespacedVariableProps := range m.namespacedVariableProps() {
for _, namespacedVariableProp := range namespacedVariableProps { for _, namespacedVariableProp := range namespacedVariableProps {
productVariableValues( variableValues := reflect.ValueOf(namespacedVariableProp).Elem().FieldByName(soongconfig.SoongConfigProperty)
soongconfig.SoongConfigProperty, productConfigProperties.AddSoongConfigProperties(namespace, variableValues)
namespacedVariableProp,
namespace,
"",
&productConfigProperties,
bazel.NoConfigAxis)
} }
} }
} }
@@ -727,30 +684,49 @@ func ProductVariableProperties(ctx ArchVariantContext, module Module) ProductCon
} }
func (p *ProductConfigProperties) AddProductConfigProperty( func (p *ProductConfigProperties) AddProductConfigProperty(
propertyName, namespace, productVariableName, config string, property interface{}, outerAxis bazel.ConfigurationAxis) { propertyName, productVariableName, arch string, propertyValue interface{}) {
if (*p)[propertyName] == nil {
(*p)[propertyName] = make(map[ProductConfigProperty]interface{})
}
productConfigProp := ProductConfigProperty{ productConfigProp := ProductConfigProperty{
Namespace: namespace, // e.g. acme, android name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board
Name: productVariableName, // e.g. size, feature1, feature2, FEATURE3, board arch: arch, // e.g. "", x86, arm64
FullConfig: config, // e.g. size, feature1-x86, size__conditions_default
OuterAxis: outerAxis,
} }
if existing, ok := (*p)[propertyName][productConfigProp]; ok && namespace != "" { p.AddEitherProperty(propertyName, productConfigProp, propertyValue)
}
func (p *ProductConfigProperties) AddSoongConfigProperty(
propertyName, namespace, variableName, value, os string, propertyValue interface{}) {
soongConfigProp := SoongConfigProperty{
namespace: namespace,
name: variableName, // e.g. size, feature1, feature2, FEATURE3, board
value: value,
os: os, // e.g. android, linux_x86
}
p.AddEitherProperty(propertyName, soongConfigProp, propertyValue)
}
func (p *ProductConfigProperties) AddEitherProperty(
propertyName string, key ProductConfigOrSoongConfigProperty, propertyValue interface{}) {
if (*p)[propertyName] == nil {
(*p)[propertyName] = make(map[ProductConfigOrSoongConfigProperty]interface{})
}
if existing, ok := (*p)[propertyName][key]; ok {
switch dst := existing.(type) { switch dst := existing.(type) {
case []string: case []string:
if src, ok := property.([]string); ok { src, ok := propertyValue.([]string)
dst = append(dst, src...) if !ok {
(*p)[propertyName][productConfigProp] = dst panic("Conflicting types")
} }
dst = append(dst, src...)
(*p)[propertyName][key] = dst
default: default:
panic(fmt.Errorf("TODO: handle merging value %s", existing)) panic(fmt.Errorf("TODO: handle merging value %#v", existing))
} }
} else { } else {
(*p)[propertyName][productConfigProp] = property (*p)[propertyName][key] = propertyValue
} }
} }
@@ -787,10 +763,7 @@ func maybeExtractConfigVarProp(v reflect.Value) (reflect.Value, bool) {
return v, true return v, true
} }
func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(namespace, suffix string, variableValues reflect.Value, outerAxis bazel.ConfigurationAxis) { func (productConfigProperties *ProductConfigProperties) AddProductConfigProperties(variableValues reflect.Value, arch string) {
// variableValues can either be a product_variables or
// soong_config_variables struct.
//
// Example of product_variables: // Example of product_variables:
// //
// product_variables: { // product_variables: {
@@ -803,35 +776,7 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti
// ], // ],
// }, // },
// }, // },
//
// 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++ { for i := 0; i < variableValues.NumField(); i++ {
// e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc. // e.g. Platform_sdk_version, Unbundled_build, Malloc_not_svelte, etc.
productVariableName := variableValues.Type().Field(i).Name productVariableName := variableValues.Type().Field(i).Name
@@ -843,25 +788,78 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti
continue continue
} }
// 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++ { for j := 0; j < variableValue.NumField(); j++ {
property := variableValue.Field(j) property := variableValue.Field(j)
// e.g. Asflags, Cflags, Enabled, etc. // e.g. Asflags, Cflags, Enabled, etc.
propertyName := variableValue.Type().Field(j).Name propertyName := variableValue.Type().Field(j).Name
// config can also be "conditions_default". if property.Kind() != reflect.Interface {
config := proptools.PropertyNameForField(propertyName) productConfigProperties.AddProductConfigProperty(propertyName, productVariableName, arch, property.Interface())
}
}
}
}
func (productConfigProperties *ProductConfigProperties) AddSoongConfigProperties(namespace string, soongConfigVariablesStruct reflect.Value) {
//
// Example of soong_config_variables:
//
// soong_config_variables: {
// feature1: {
// conditions_default: {
// ...
// },
// cflags: ...
// },
// feature2: {
// cflags: ...
// conditions_default: {
// ...
// },
// },
// board: {
// soc_a: {
// ...
// },
// soc_b: {
// ...
// },
// soc_c: {},
// conditions_default: {
// ...
// },
// },
// }
for i := 0; i < soongConfigVariablesStruct.NumField(); i++ {
// e.g. feature1, feature2, board
variableName := soongConfigVariablesStruct.Type().Field(i).Name
variableStruct := soongConfigVariablesStruct.Field(i)
// Check if any properties were set for the module
if variableStruct.IsZero() {
// e.g. feature1: {}
continue
}
// Unlike product variables, config variables require a few more
// indirections to extract the struct from the reflect.Value.
if v, ok := maybeExtractConfigVarProp(variableStruct); ok {
variableStruct = v
}
for j := 0; j < variableStruct.NumField(); j++ {
propertyOrStruct := variableStruct.Field(j)
// propertyOrValueName can either be:
// - A property, like: Asflags, Cflags, Enabled, etc.
// - A soong config string variable's value, like soc_a, soc_b, soc_c in the example above
// - "conditions_default"
propertyOrValueName := variableStruct.Type().Field(j).Name
// If the property wasn't set, no need to pass it along // If the property wasn't set, no need to pass it along
if property.IsZero() { if propertyOrStruct.IsZero() {
continue continue
} }
if v, ok := maybeExtractConfigVarProp(property); ok { if v, ok := maybeExtractConfigVarProp(propertyOrStruct); ok {
// The field is a struct, which is used by: // The field is a struct, which is used by:
// 1) soong_config_string_variables // 1) soong_config_string_variables
// //
@@ -879,6 +877,9 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti
// cflags: ..., // cflags: ...,
// static_libs: ... // static_libs: ...
// } // }
//
// This means that propertyOrValueName is either conditions_default, or a soong
// config string variable's value.
field := v field := v
// Iterate over fields of this struct prop. // Iterate over fields of this struct prop.
for k := 0; k < field.NumField(); k++ { for k := 0; k < field.NumField(); k++ {
@@ -888,47 +889,59 @@ func (productConfigProperties *ProductConfigProperties) AddProductConfigProperti
if field.Field(k).IsZero() && namespace == "" { if field.Field(k).IsZero() && namespace == "" {
continue continue
} }
actualPropertyName := field.Type().Field(k).Name
productConfigProperties.AddProductConfigProperty( propertyName := field.Type().Field(k).Name
actualPropertyName, // e.g. cflags, static_libs if propertyName == "Target" {
namespace, // e.g. acme, android productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), field.Field(k))
productVariableName, // e.g. size, feature1, FEATURE2, board } else if propertyName == "Arch" || propertyName == "Multilib" {
config, panic("Arch/Multilib are not currently supported in soong config variable structs")
field.Field(k).Interface(), // e.g. ["-DDEFAULT"], ["foo", "bar"], } else {
outerAxis, productConfigProperties.AddSoongConfigProperty(propertyName, namespace, variableName, proptools.PropertyNameForField(propertyOrValueName), "", field.Field(k).Interface())
) }
} }
} else if property.Kind() != reflect.Interface { } else if propertyOrStruct.Kind() != reflect.Interface {
// If not an interface, then this is not a conditions_default or // If not an interface, then this is not a conditions_default or
// a struct prop. That is, this is a regular product variable, // a struct prop. That is, this is a bool/value config variable.
// or a bool/value config variable. if propertyOrValueName == "Target" {
config := productVariableName + suffix productConfigProperties.AddSoongConfigPropertiesFromTargetStruct(namespace, variableName, "", propertyOrStruct)
productConfigProperties.AddProductConfigProperty( } else if propertyOrValueName == "Arch" || propertyOrValueName == "Multilib" {
propertyName, panic("Arch/Multilib are not currently supported in soong config variable structs")
namespace, } else {
productVariableName, productConfigProperties.AddSoongConfigProperty(propertyOrValueName, namespace, variableName, "", "", propertyOrStruct.Interface())
config, }
property.Interface(),
outerAxis,
)
} }
} }
} }
} }
// productVariableValues uses reflection to convert a property struct for func (productConfigProperties *ProductConfigProperties) AddSoongConfigPropertiesFromTargetStruct(namespace, soongConfigVariableName string, soongConfigVariableValue string, targetStruct reflect.Value) {
// product_variables and soong_config_variables to structs that can be generated // targetStruct will be a struct with fields like "android", "host", "arm", "x86",
// as select statements. // "android_arm", etc. The values of each of those fields will be a regular property struct.
func productVariableValues( for i := 0; i < targetStruct.NumField(); i++ {
fieldName string, variableProps interface{}, namespace, suffix string, productConfigProperties *ProductConfigProperties, outerAxis bazel.ConfigurationAxis) { targetFieldName := targetStruct.Type().Field(i).Name
if suffix != "" { archOrOsSpecificStruct := targetStruct.Field(i)
suffix = "-" + suffix for j := 0; j < archOrOsSpecificStruct.NumField(); j++ {
} property := archOrOsSpecificStruct.Field(j)
// e.g. Asflags, Cflags, Enabled, etc.
propertyName := archOrOsSpecificStruct.Type().Field(j).Name
// variableValues represent the product_variables or soong_config_variables struct. if targetFieldName == "Android" {
variableValues := reflect.ValueOf(variableProps).Elem().FieldByName(fieldName) productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, "android", property.Interface())
productConfigProperties.AddProductConfigProperties(namespace, suffix, variableValues, outerAxis) } else if targetFieldName == "Host" {
for _, os := range osTypeList {
if os.Class == Host {
productConfigProperties.AddSoongConfigProperty(propertyName, namespace, soongConfigVariableName, soongConfigVariableValue, os.Name, property.Interface())
}
}
} else {
// One problem with supporting additional fields is that if multiple branches of
// "target" overlap, we don't want them to be in the same select statement (aka
// configuration axis). "android" and "host" are disjoint, so it's ok that we only
// have 2 axes right now. (soongConfigVariables and soongConfigVariablesPlusOs)
panic("TODO: support other target types in soong config variable structs: " + targetFieldName)
}
}
}
} }
func VariableMutator(mctx BottomUpMutatorContext) { func VariableMutator(mctx BottomUpMutatorContext) {

View File

@@ -292,8 +292,7 @@ func (ca ConfigurationAxis) SelectKey(config string) string {
case osArch: case osArch:
return platformOsArchMap[config] return platformOsArchMap[config]
case productVariables: case productVariables:
if strings.HasSuffix(config, ConditionsDefaultConfigKey) { if config == ConditionsDefaultConfigKey {
// e.g. "acme__feature1__conditions_default" or "android__board__conditions_default"
return ConditionsDefaultSelectKey return ConditionsDefaultSelectKey
} }
return fmt.Sprintf("%s:%s", productVariableBazelPackage, config) return fmt.Sprintf("%s:%s", productVariableBazelPackage, config)
@@ -325,11 +324,11 @@ var (
) )
// ProductVariableConfigurationAxis returns an axis for the given product variable // ProductVariableConfigurationAxis returns an axis for the given product variable
func ProductVariableConfigurationAxis(variable string, outerAxis ConfigurationAxis) ConfigurationAxis { func ProductVariableConfigurationAxis(archVariant bool, variable string) ConfigurationAxis {
return ConfigurationAxis{ return ConfigurationAxis{
configurationType: productVariables, configurationType: productVariables,
subType: variable, subType: variable,
outerAxisType: outerAxis.configurationType, archVariant: archVariant,
} }
} }
@@ -340,8 +339,8 @@ type ConfigurationAxis struct {
// some configuration types (e.g. productVariables) have multiple independent axes, subType helps // some configuration types (e.g. productVariables) have multiple independent axes, subType helps
// distinguish between them without needing to list all 17 product variables. // distinguish between them without needing to list all 17 product variables.
subType string subType string
// used to keep track of which product variables are arch variant
outerAxisType configurationType archVariant bool
} }
func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool { func (ca *ConfigurationAxis) less(other ConfigurationAxis) bool {

View File

@@ -334,7 +334,7 @@ func (la *LabelAttribute) Collapse() error {
if containsArch { if containsArch {
allProductVariablesAreArchVariant := true allProductVariablesAreArchVariant := true
for k := range la.ConfigurableValues { for k := range la.ConfigurableValues {
if k.configurationType == productVariables && k.outerAxisType != arch { if k.configurationType == productVariables && !k.archVariant {
allProductVariablesAreArchVariant = false allProductVariablesAreArchVariant = false
} }
} }

View File

@@ -248,13 +248,13 @@ func TestResolveExcludes(t *testing.T) {
OsArchConfigurationAxis: labelListSelectValues{ OsArchConfigurationAxis: labelListSelectValues{
"linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}), "linux_x86": makeLabelList([]string{"linux_x86_include"}, []string{}),
}, },
ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): labelListSelectValues{ ProductVariableConfigurationAxis(false, "product_with_defaults"): labelListSelectValues{
"a": makeLabelList([]string{}, []string{"not_in_value"}), "a": makeLabelList([]string{}, []string{"not_in_value"}),
"b": makeLabelList([]string{"b_val"}, []string{}), "b": makeLabelList([]string{"b_val"}, []string{}),
"c": makeLabelList([]string{"c_val"}, []string{}), "c": makeLabelList([]string{"c_val"}, []string{}),
ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}), ConditionsDefaultConfigKey: makeLabelList([]string{"c_val", "default", "default2", "all_exclude"}, []string{}),
}, },
ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): labelListSelectValues{ ProductVariableConfigurationAxis(false, "product_only_with_excludes"): labelListSelectValues{
"a": makeLabelList([]string{}, []string{"product_config_exclude"}), "a": makeLabelList([]string{}, []string{"product_config_exclude"}),
}, },
}, },
@@ -282,13 +282,13 @@ func TestResolveExcludes(t *testing.T) {
"linux_x86": makeLabels("linux_x86_include"), "linux_x86": makeLabels("linux_x86_include"),
ConditionsDefaultConfigKey: nilLabels, ConditionsDefaultConfigKey: nilLabels,
}, },
ProductVariableConfigurationAxis("product_with_defaults", NoConfigAxis): { ProductVariableConfigurationAxis(false, "product_with_defaults"): {
"a": nilLabels, "a": nilLabels,
"b": makeLabels("b_val"), "b": makeLabels("b_val"),
"c": makeLabels("c_val"), "c": makeLabels("c_val"),
ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"), ConditionsDefaultConfigKey: makeLabels("c_val", "default", "default2"),
}, },
ProductVariableConfigurationAxis("product_only_with_excludes", NoConfigAxis): { ProductVariableConfigurationAxis(false, "product_only_with_excludes"): {
"a": nilLabels, "a": nilLabels,
ConditionsDefaultConfigKey: makeLabels("product_config_exclude"), ConditionsDefaultConfigKey: makeLabels("product_config_exclude"),
}, },
@@ -679,7 +679,7 @@ func TestDeduplicateAxesFromBase(t *testing.T) {
OsArchConfigurationAxis: stringListSelectValues{ OsArchConfigurationAxis: stringListSelectValues{
"linux_x86": {"linux_x86_include"}, "linux_x86": {"linux_x86_include"},
}, },
ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{ ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
"a": []string{"not_in_value"}, "a": []string{"not_in_value"},
}, },
}, },
@@ -704,7 +704,7 @@ func TestDeduplicateAxesFromBase(t *testing.T) {
"linux": []string{"linux_include"}, "linux": []string{"linux_include"},
}, },
OsArchConfigurationAxis: stringListSelectValues{}, OsArchConfigurationAxis: stringListSelectValues{},
ProductVariableConfigurationAxis("a", NoConfigAxis): stringListSelectValues{ ProductVariableConfigurationAxis(false, "a"): stringListSelectValues{
"a": []string{"not_in_value"}, "a": []string{"not_in_value"},
}, },
} }

View File

@@ -1251,3 +1251,111 @@ cc_binary {
srcs = ["main.cc"], srcs = ["main.cc"],
)`}}) )`}})
} }
func TestSoongConfigModuleType_CombinedWithArchVariantProperties(t *testing.T) {
bp := `
soong_config_bool_variable {
name: "my_bool_variable",
}
soong_config_string_variable {
name: "my_string_variable",
values: [
"value1",
"value2",
],
}
soong_config_module_type {
name: "special_build_cc_defaults",
module_type: "cc_defaults",
config_namespace: "my_namespace",
bool_variables: ["my_bool_variable"],
variables: ["my_string_variable"],
properties: ["target.android.cflags", "cflags"],
}
special_build_cc_defaults {
name: "sample_cc_defaults",
target: {
android: {
cflags: ["-DFOO"],
},
},
soong_config_variables: {
my_bool_variable: {
target: {
android: {
cflags: ["-DBAR"],
},
},
conditions_default: {
target: {
android: {
cflags: ["-DBAZ"],
},
},
},
},
my_string_variable: {
value1: {
cflags: ["-DVALUE1_NOT_ANDROID"],
target: {
android: {
cflags: ["-DVALUE1"],
},
},
},
value2: {
target: {
android: {
cflags: ["-DVALUE2"],
},
},
},
conditions_default: {
target: {
android: {
cflags: ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
},
},
},
},
},
}
cc_binary {
name: "my_binary",
srcs: ["main.cc"],
defaults: ["sample_cc_defaults"],
}`
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{`cc_binary(
name = "my_binary",
copts = select({
"//build/bazel/platforms/os:android": ["-DFOO"],
"//conditions:default": [],
}) + select({
"//build/bazel/product_variables:my_namespace__my_bool_variable__android": ["-DBAR"],
"//build/bazel/product_variables:my_namespace__my_bool_variable__conditions_default__android": ["-DBAZ"],
"//conditions:default": [],
}) + select({
"//build/bazel/product_variables:my_namespace__my_string_variable__value1": ["-DVALUE1_NOT_ANDROID"],
"//conditions:default": [],
}) + select({
"//build/bazel/product_variables:my_namespace__my_string_variable__conditions_default__android": ["-DSTRING_VAR_CONDITIONS_DEFAULT"],
"//build/bazel/product_variables:my_namespace__my_string_variable__value1__android": ["-DVALUE1"],
"//build/bazel/product_variables:my_namespace__my_string_variable__value2__android": ["-DVALUE2"],
"//conditions:default": [],
}),
local_includes = ["."],
srcs = ["main.cc"],
target_compatible_with = ["//build/bazel/platforms/os:android"],
)`}})
}

View File

@@ -537,7 +537,7 @@ func (ca *compilerAttributes) convertProductVariables(ctx android.BazelConversio
if !ok { if !ok {
ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName)) ctx.ModuleErrorf("Could not convert product variable %s property", proptools.PropertyNameForField(propName))
} }
newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name) newFlags, _ := bazel.TryVariableSubstitutions(flags, productConfigProp.Name())
attr.SetSelectValue(productConfigProp.ConfigurationAxis(), productConfigProp.SelectKey(), newFlags) attr.SetSelectValue(productConfigProp.ConfigurationAxis(), productConfigProp.SelectKey(), newFlags)
} }
} }
@@ -1350,7 +1350,7 @@ func (la *linkerAttributes) convertProductVariables(ctx android.BazelConversionP
// Collect all the configurations that an include or exclude property exists for. // Collect all the configurations that an include or exclude property exists for.
// We want to iterate all configurations rather than either the include or exclude because, for a // We want to iterate all configurations rather than either the include or exclude because, for a
// particular configuration, we may have either only an include or an exclude to handle. // particular configuration, we may have either only an include or an exclude to handle.
productConfigProps := make(map[android.ProductConfigProperty]bool, len(props)+len(excludeProps)) productConfigProps := make(map[android.ProductConfigOrSoongConfigProperty]bool, len(props)+len(excludeProps))
for p := range props { for p := range props {
productConfigProps[p] = true productConfigProps[p] = true
} }