Add conditions_default for soong config variables.
Each variable can specify a conditions_default for properties to be used when the variable is not set, not set to a true value (for bools), or is set to a value that is not present in the module (for strings). Test: m nothing Test: go test soong tests Change-Id: I76ec026da2369b407f0f530f77760f530e7958fc
This commit is contained in:
@@ -26,6 +26,8 @@ import (
|
||||
"github.com/google/blueprint/proptools"
|
||||
)
|
||||
|
||||
const conditionsDefault = "conditions_default"
|
||||
|
||||
var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
|
||||
|
||||
// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
|
||||
@@ -145,32 +147,10 @@ func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []
|
||||
return errs
|
||||
}
|
||||
|
||||
mt := &ModuleType{
|
||||
affectableProperties: props.Properties,
|
||||
ConfigNamespace: props.Config_namespace,
|
||||
BaseModuleType: props.Module_type,
|
||||
variableNames: props.Variables,
|
||||
}
|
||||
v.ModuleTypes[props.Name] = mt
|
||||
|
||||
for _, name := range props.Bool_variables {
|
||||
if name == "" {
|
||||
return []error{fmt.Errorf("bool_variable name must not be blank")}
|
||||
}
|
||||
|
||||
mt.Variables = append(mt.Variables, newBoolVariable(name))
|
||||
}
|
||||
|
||||
for _, name := range props.Value_variables {
|
||||
if name == "" {
|
||||
return []error{fmt.Errorf("value_variables entry must not be blank")}
|
||||
}
|
||||
|
||||
mt.Variables = append(mt.Variables, &valueVariable{
|
||||
baseVariable: baseVariable{
|
||||
variable: name,
|
||||
},
|
||||
})
|
||||
if mt, errs := newModuleType(props); len(errs) > 0 {
|
||||
return errs
|
||||
} else {
|
||||
v.ModuleTypes[props.Name] = mt
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -196,6 +176,12 @@ func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (err
|
||||
return []error{fmt.Errorf("values property must be set")}
|
||||
}
|
||||
|
||||
for _, name := range stringProps.Values {
|
||||
if err := checkVariableName(name); err != nil {
|
||||
return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
|
||||
}
|
||||
}
|
||||
|
||||
v.variables[base.variable] = &stringVariable{
|
||||
baseVariable: base,
|
||||
values: CanonicalizeToProperties(stringProps.Values),
|
||||
@@ -417,8 +403,7 @@ func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.
|
||||
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
|
||||
// based on SoongConfig values.
|
||||
// Expects that props contains a struct field with name soong_config_variables. The fields within
|
||||
// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
|
||||
// props should be generated via CreateProperties.
|
||||
// soong_config_variables are expected to be in the same order as moduleType.Variables.
|
||||
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
|
||||
var ret []interface{}
|
||||
props = props.Elem().FieldByName(soongConfigProperty)
|
||||
@@ -441,6 +426,46 @@ type ModuleType struct {
|
||||
variableNames []string
|
||||
}
|
||||
|
||||
func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
|
||||
mt := &ModuleType{
|
||||
affectableProperties: props.Properties,
|
||||
ConfigNamespace: props.Config_namespace,
|
||||
BaseModuleType: props.Module_type,
|
||||
variableNames: props.Variables,
|
||||
}
|
||||
|
||||
for _, name := range props.Bool_variables {
|
||||
if err := checkVariableName(name); err != nil {
|
||||
return nil, []error{fmt.Errorf("bool_variables %s", err)}
|
||||
}
|
||||
|
||||
mt.Variables = append(mt.Variables, newBoolVariable(name))
|
||||
}
|
||||
|
||||
for _, name := range props.Value_variables {
|
||||
if err := checkVariableName(name); err != nil {
|
||||
return nil, []error{fmt.Errorf("value_variables %s", err)}
|
||||
}
|
||||
|
||||
mt.Variables = append(mt.Variables, &valueVariable{
|
||||
baseVariable: baseVariable{
|
||||
variable: name,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
return mt, nil
|
||||
}
|
||||
|
||||
func checkVariableName(name string) error {
|
||||
if name == "" {
|
||||
return fmt.Errorf("name must not be blank")
|
||||
} else if name == conditionsDefault {
|
||||
return fmt.Errorf("%q is reserved", conditionsDefault)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type soongConfigVariable interface {
|
||||
// variableProperty returns the name of the variable.
|
||||
variableProperty() string
|
||||
@@ -474,7 +499,10 @@ type stringVariable struct {
|
||||
func (s *stringVariable) variableValuesType() reflect.Type {
|
||||
var fields []reflect.StructField
|
||||
|
||||
for _, v := range s.values {
|
||||
var values []string
|
||||
values = append(values, s.values...)
|
||||
values = append(values, conditionsDefault)
|
||||
for _, v := range values {
|
||||
fields = append(fields, reflect.StructField{
|
||||
Name: proptools.FieldNameForProperty(v),
|
||||
Type: emptyInterfaceType,
|
||||
@@ -484,26 +512,36 @@ func (s *stringVariable) variableValuesType() reflect.Type {
|
||||
return reflect.StructOf(fields)
|
||||
}
|
||||
|
||||
// initializeProperties initializes properties to zero value of typ for supported values and a final
|
||||
// conditions default field.
|
||||
func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
||||
for i := range s.values {
|
||||
v.Field(i).Set(reflect.Zero(typ))
|
||||
}
|
||||
v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
|
||||
}
|
||||
|
||||
// Extracts an interface from values containing the properties to apply based on config.
|
||||
// If config does not match a value with a non-nil property set, the default value will be returned.
|
||||
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
||||
for j, v := range s.values {
|
||||
if config.String(s.variable) == v {
|
||||
return values.Field(j).Interface(), nil
|
||||
f := values.Field(j)
|
||||
if config.String(s.variable) == v && !f.Elem().IsNil() {
|
||||
return f.Interface(), nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
// if we have reached this point, we have checked all valid values of string and either:
|
||||
// * the value was not set
|
||||
// * the value was set but that value was not specified in the Android.bp file
|
||||
return values.Field(len(s.values)).Interface(), nil
|
||||
}
|
||||
|
||||
// Struct to allow conditions set based on a boolean variable
|
||||
type boolVariable struct {
|
||||
baseVariable
|
||||
}
|
||||
|
||||
// newBoolVariable constructs a boolVariable with the given name
|
||||
func newBoolVariable(name string) *boolVariable {
|
||||
return &boolVariable{
|
||||
baseVariable{
|
||||
@@ -516,18 +554,82 @@ func (b boolVariable) variableValuesType() reflect.Type {
|
||||
return emptyInterfaceType
|
||||
}
|
||||
|
||||
// initializeProperties initializes a property to zero value of typ with an additional conditions
|
||||
// default field.
|
||||
func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
||||
v.Set(reflect.Zero(typ))
|
||||
initializePropertiesWithDefault(v, typ)
|
||||
}
|
||||
|
||||
func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
||||
if config.Bool(b.variable) {
|
||||
return values.Interface(), nil
|
||||
// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
|
||||
// in typ, with an additional field for defaults of type typ. This should be used to initialize
|
||||
// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
|
||||
// one variable and a default.
|
||||
func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
|
||||
sTyp := typ.Elem()
|
||||
var fields []reflect.StructField
|
||||
for i := 0; i < sTyp.NumField(); i++ {
|
||||
fields = append(fields, sTyp.Field(i))
|
||||
}
|
||||
|
||||
// create conditions_default field
|
||||
nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
|
||||
fields = append(fields, reflect.StructField{
|
||||
Name: nestedFieldName,
|
||||
Type: typ,
|
||||
})
|
||||
|
||||
newTyp := reflect.PtrTo(reflect.StructOf(fields))
|
||||
v.Set(reflect.Zero(newTyp))
|
||||
}
|
||||
|
||||
// conditionsDefaultField extracts the conditions_default field from v. This is always the final
|
||||
// field if initialized with initializePropertiesWithDefault.
|
||||
func conditionsDefaultField(v reflect.Value) reflect.Value {
|
||||
return v.Field(v.NumField() - 1)
|
||||
}
|
||||
|
||||
// removeDefault removes the conditions_default field from values while retaining values from all
|
||||
// other fields. This allows
|
||||
func removeDefault(values reflect.Value) reflect.Value {
|
||||
v := values.Elem().Elem()
|
||||
s := conditionsDefaultField(v)
|
||||
// if conditions_default field was not set, there will be no issues extending properties.
|
||||
if !s.IsValid() {
|
||||
return v
|
||||
}
|
||||
|
||||
// If conditions_default field was set, it has the correct type for our property. Create a new
|
||||
// reflect.Value of the conditions_default type and copy all fields (except for
|
||||
// conditions_default) based on values to the result.
|
||||
res := reflect.New(s.Type().Elem())
|
||||
for i := 0; i < res.Type().Elem().NumField(); i++ {
|
||||
val := v.Field(i)
|
||||
res.Elem().Field(i).Set(val)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
|
||||
// the module. If the value was not set, conditions_default interface will be returned; otherwise,
|
||||
// the interface in values, without conditions_default will be returned.
|
||||
func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
||||
// If this variable was not referenced in the module, there are no properties to apply.
|
||||
if values.Elem().IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
if config.Bool(b.variable) {
|
||||
values = removeDefault(values)
|
||||
return values.Interface(), nil
|
||||
}
|
||||
v := values.Elem().Elem()
|
||||
if f := conditionsDefaultField(v); f.IsValid() {
|
||||
return f.Interface(), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Struct to allow conditions set based on a value variable, supporting string substitution.
|
||||
type valueVariable struct {
|
||||
baseVariable
|
||||
}
|
||||
@@ -536,17 +638,28 @@ func (s *valueVariable) variableValuesType() reflect.Type {
|
||||
return emptyInterfaceType
|
||||
}
|
||||
|
||||
// initializeProperties initializes a property to zero value of typ with an additional conditions
|
||||
// default field.
|
||||
func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
||||
v.Set(reflect.Zero(typ))
|
||||
initializePropertiesWithDefault(v, typ)
|
||||
}
|
||||
|
||||
// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
|
||||
// the module. If the variable was not set, conditions_default interface will be returned;
|
||||
// otherwise, the interface in values, without conditions_default will be returned with all
|
||||
// appropriate string substitutions based on variable being set.
|
||||
func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
|
||||
if !config.IsSet(s.variable) || !values.IsValid() {
|
||||
// If this variable was not referenced in the module, there are no properties to apply.
|
||||
if !values.IsValid() || values.Elem().IsZero() {
|
||||
return nil, nil
|
||||
}
|
||||
if !config.IsSet(s.variable) {
|
||||
return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
|
||||
}
|
||||
configValue := config.String(s.variable)
|
||||
|
||||
propStruct := values.Elem().Elem()
|
||||
values = removeDefault(values)
|
||||
propStruct := values.Elem()
|
||||
if !propStruct.IsValid() {
|
||||
return nil, nil
|
||||
}
|
||||
|
Reference in New Issue
Block a user