Merge "soong config: add value_variable substitution" am: 1062943c8a

Change-Id: Iccaf2543df343e4ca451aa775e00a8e45861a9f5
This commit is contained in:
Treehugger Robot 2020-04-09 20:35:34 +00:00 committed by Automerger Merge Worker
commit e2f34a9070
4 changed files with 131 additions and 15 deletions

View file

@ -421,6 +421,7 @@ soong_config_module_type {
config_namespace: "acme",
variables: ["board"],
bool_variables: ["feature"],
value_variables: ["width"],
properties: ["cflags", "srcs"],
}
@ -431,8 +432,9 @@ soong_config_string_variable {
```
This example describes a new `acme_cc_defaults` module type that extends the
`cc_defaults` module type, with two additional conditionals based on variables
`board` and `feature`, which can affect properties `cflags` and `srcs`.
`cc_defaults` module type, with three additional conditionals based on
variables `board`, `feature` and `width`, which can affect properties `cflags`
and `srcs`.
The values of the variables can be set from a product's `BoardConfig.mk` file:
```
@ -443,6 +445,7 @@ SOONG_CONFIG_acme += \
SOONG_CONFIG_acme_board := soc_a
SOONG_CONFIG_acme_feature := true
SOONG_CONFIG_acme_width := 200
```
The `acme_cc_defaults` module type can be used anywhere after the definition in
@ -471,6 +474,9 @@ acme_cc_defaults {
feature: {
cflags: ["-DFEATURE"],
},
width: {
cflags: ["-DWIDTH=%s"],
},
},
}
@ -482,7 +488,7 @@ cc_library {
```
With the `BoardConfig.mk` snippet above, libacme_foo would build with
cflags "-DGENERIC -DSOC_A -DFEATURE".
cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
`soong_config_module_type` modules will work best when used to wrap defaults
modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced

View file

@ -73,6 +73,9 @@ type soongConfigModuleTypeImportProperties struct {
// feature: {
// cflags: ["-DFEATURE"],
// },
// width: {
// cflags: ["-DWIDTH=%s"],
// },
// },
// }
//
@ -90,6 +93,7 @@ type soongConfigModuleTypeImportProperties struct {
// config_namespace: "acme",
// variables: ["board"],
// bool_variables: ["feature"],
// value_variables: ["width"],
// properties: ["cflags", "srcs"],
// }
//
@ -107,8 +111,9 @@ type soongConfigModuleTypeImportProperties struct {
//
// SOONG_CONFIG_acme_board := soc_a
// SOONG_CONFIG_acme_feature := true
// SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE -DWIDTH=200".
func soongConfigModuleTypeImportFactory() Module {
module := &soongConfigModuleTypeImport{}
@ -151,6 +156,7 @@ type soongConfigModuleTypeModule struct {
// config_namespace: "acme",
// variables: ["board"],
// bool_variables: ["feature"],
// value_variables: ["width"],
// properties: ["cflags", "srcs"],
// }
//
@ -174,6 +180,9 @@ type soongConfigModuleTypeModule struct {
// feature: {
// cflags: ["-DFEATURE"],
// },
// width: {
// cflags: ["-DWIDTH=%s"],
// },
// },
// }
//
@ -192,6 +201,7 @@ type soongConfigModuleTypeModule struct {
//
// SOONG_CONFIG_acme_board := soc_a
// SOONG_CONFIG_acme_feature := true
// SOONG_CONFIG_acme_width := 200
//
// Then libacme_foo would build with cflags "-DGENERIC -DSOC_A -DFEATURE".
func soongConfigModuleTypeFactory() Module {
@ -352,7 +362,12 @@ func soongConfigModuleFactory(factory blueprint.ModuleFactory,
AddLoadHook(module, func(ctx LoadHookContext) {
config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
for _, ps := range soongconfig.PropertiesToApply(moduleType, conditionalProps, config) {
newProps, err := soongconfig.PropertiesToApply(moduleType, conditionalProps, config)
if err != nil {
ctx.ModuleErrorf("%s", err)
return
}
for _, ps := range newProps {
ctx.AppendProperties(ps)
}
})

View file

@ -45,6 +45,7 @@ func TestSoongConfigModule(t *testing.T) {
config_namespace: "acme",
variables: ["board", "feature1", "FEATURE3"],
bool_variables: ["feature2"],
value_variables: ["size"],
properties: ["cflags", "srcs"],
}
@ -82,6 +83,9 @@ func TestSoongConfigModule(t *testing.T) {
cflags: ["-DSOC_B"],
},
},
size: {
cflags: ["-DSIZE=%s"],
},
feature1: {
cflags: ["-DFEATURE1"],
},
@ -101,6 +105,7 @@ func TestSoongConfigModule(t *testing.T) {
config.TestProductVariables.VendorVars = map[string]map[string]string{
"acme": map[string]string{
"board": "soc_a",
"size": "42",
"feature1": "true",
"feature2": "false",
// FEATURE3 unset
@ -121,7 +126,7 @@ func TestSoongConfigModule(t *testing.T) {
FailIfErrored(t, errs)
foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
if g, w := foo.props.Cflags, []string{"-DGENERIC", "-DSIZE=42", "-DSOC_A", "-DFEATURE1"}; !reflect.DeepEqual(g, w) {
t.Errorf("wanted foo cflags %q, got %q", w, g)
}
}

View file

@ -112,6 +112,10 @@ type ModuleTypeProperties struct {
// the list of boolean SOONG_CONFIG variables that this module type will read
Bool_variables []string
// the list of SOONG_CONFIG variables that this module type will read. The value will be
// inserted into the properties with %s substitution.
Value_variables []string
// the list of properties that this module type will extend.
Properties []string
}
@ -161,6 +165,18 @@ func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []
})
}
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,
},
})
}
return nil
}
@ -404,15 +420,17 @@ func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
// based on SoongConfig values.
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) []interface{} {
func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
var ret []interface{}
props = props.Elem().FieldByName(soongConfigProperty)
for i, c := range moduleType.Variables {
if ps := c.PropertiesToApply(config, props.Field(i)); ps != nil {
if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
return nil, err
} else if ps != nil {
ret = append(ret, ps)
}
}
return ret
return ret, nil
}
type ModuleType struct {
@ -438,7 +456,7 @@ type soongConfigVariable interface {
// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
// to the module.
PropertiesToApply(config SoongConfig, values reflect.Value) interface{}
PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
}
type baseVariable struct {
@ -473,14 +491,14 @@ func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type)
}
}
func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
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()
return values.Field(j).Interface(), nil
}
}
return nil
return nil, nil
}
type boolVariable struct {
@ -495,11 +513,83 @@ func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
v.Set(reflect.Zero(typ))
}
func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
if config.Bool(b.variable) {
return values.Interface()
return values.Interface(), nil
}
return nil, nil
}
type valueVariable struct {
baseVariable
}
func (s *valueVariable) variableValuesType() reflect.Type {
return emptyInterfaceType
}
func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
v.Set(reflect.Zero(typ))
}
func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
if !config.IsSet(s.variable) {
return nil, nil
}
configValue := config.String(s.variable)
propStruct := values.Elem().Elem()
for i := 0; i < propStruct.NumField(); i++ {
field := propStruct.Field(i)
kind := field.Kind()
if kind == reflect.Ptr {
if field.IsNil() {
continue
}
field = field.Elem()
}
switch kind {
case reflect.String:
err := printfIntoProperty(field, configValue)
if err != nil {
return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
}
case reflect.Slice:
for j := 0; j < field.Len(); j++ {
err := printfIntoProperty(field.Index(j), configValue)
if err != nil {
return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
}
}
case reflect.Bool:
// Nothing to do
default:
return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
}
}
return values.Interface(), nil
}
func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
s := propertyValue.String()
count := strings.Count(s, "%")
if count == 0 {
return nil
}
if count > 1 {
return fmt.Errorf("value variable properties only support a single '%%'")
}
if !strings.Contains(s, "%s") {
return fmt.Errorf("unsupported %% in value variable property")
}
propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
return nil
}