|
@@ -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)
|
|
|
}
|
|
|
|
|
|
+// 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
|
|
|
}
|