Merge "soong config: add value_variable substitution" am: 1062943c8a
Change-Id: Iccaf2543df343e4ca451aa775e00a8e45861a9f5
This commit is contained in:
commit
e2f34a9070
4 changed files with 131 additions and 15 deletions
12
README.md
12
README.md
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue