Merge "soong config: add value_variable substitution" into rvc-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
a59774eb49
12
README.md
12
README.md
@@ -421,6 +421,7 @@ soong_config_module_type {
|
|||||||
config_namespace: "acme",
|
config_namespace: "acme",
|
||||||
variables: ["board"],
|
variables: ["board"],
|
||||||
bool_variables: ["feature"],
|
bool_variables: ["feature"],
|
||||||
|
value_variables: ["width"],
|
||||||
properties: ["cflags", "srcs"],
|
properties: ["cflags", "srcs"],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,8 +432,9 @@ soong_config_string_variable {
|
|||||||
```
|
```
|
||||||
|
|
||||||
This example describes a new `acme_cc_defaults` module type that extends the
|
This example describes a new `acme_cc_defaults` module type that extends the
|
||||||
`cc_defaults` module type, with two additional conditionals based on variables
|
`cc_defaults` module type, with three additional conditionals based on
|
||||||
`board` and `feature`, which can affect properties `cflags` and `srcs`.
|
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:
|
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_board := soc_a
|
||||||
SOONG_CONFIG_acme_feature := true
|
SOONG_CONFIG_acme_feature := true
|
||||||
|
SOONG_CONFIG_acme_width := 200
|
||||||
```
|
```
|
||||||
|
|
||||||
The `acme_cc_defaults` module type can be used anywhere after the definition in
|
The `acme_cc_defaults` module type can be used anywhere after the definition in
|
||||||
@@ -471,6 +474,9 @@ acme_cc_defaults {
|
|||||||
feature: {
|
feature: {
|
||||||
cflags: ["-DFEATURE"],
|
cflags: ["-DFEATURE"],
|
||||||
},
|
},
|
||||||
|
width: {
|
||||||
|
cflags: ["-DWIDTH=%s"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,7 +488,7 @@ cc_library {
|
|||||||
```
|
```
|
||||||
|
|
||||||
With the `BoardConfig.mk` snippet above, libacme_foo would build with
|
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
|
`soong_config_module_type` modules will work best when used to wrap defaults
|
||||||
modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
|
modules (`cc_defaults`, `java_defaults`, etc.), which can then be referenced
|
||||||
|
@@ -73,6 +73,9 @@ type soongConfigModuleTypeImportProperties struct {
|
|||||||
// feature: {
|
// feature: {
|
||||||
// cflags: ["-DFEATURE"],
|
// cflags: ["-DFEATURE"],
|
||||||
// },
|
// },
|
||||||
|
// width: {
|
||||||
|
// cflags: ["-DWIDTH=%s"],
|
||||||
|
// },
|
||||||
// },
|
// },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -90,6 +93,7 @@ type soongConfigModuleTypeImportProperties struct {
|
|||||||
// config_namespace: "acme",
|
// config_namespace: "acme",
|
||||||
// variables: ["board"],
|
// variables: ["board"],
|
||||||
// bool_variables: ["feature"],
|
// bool_variables: ["feature"],
|
||||||
|
// value_variables: ["width"],
|
||||||
// properties: ["cflags", "srcs"],
|
// properties: ["cflags", "srcs"],
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -107,8 +111,9 @@ type soongConfigModuleTypeImportProperties struct {
|
|||||||
//
|
//
|
||||||
// SOONG_CONFIG_acme_board := soc_a
|
// SOONG_CONFIG_acme_board := soc_a
|
||||||
// SOONG_CONFIG_acme_feature := true
|
// 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 {
|
func soongConfigModuleTypeImportFactory() Module {
|
||||||
module := &soongConfigModuleTypeImport{}
|
module := &soongConfigModuleTypeImport{}
|
||||||
|
|
||||||
@@ -151,6 +156,7 @@ type soongConfigModuleTypeModule struct {
|
|||||||
// config_namespace: "acme",
|
// config_namespace: "acme",
|
||||||
// variables: ["board"],
|
// variables: ["board"],
|
||||||
// bool_variables: ["feature"],
|
// bool_variables: ["feature"],
|
||||||
|
// value_variables: ["width"],
|
||||||
// properties: ["cflags", "srcs"],
|
// properties: ["cflags", "srcs"],
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -174,6 +180,9 @@ type soongConfigModuleTypeModule struct {
|
|||||||
// feature: {
|
// feature: {
|
||||||
// cflags: ["-DFEATURE"],
|
// cflags: ["-DFEATURE"],
|
||||||
// },
|
// },
|
||||||
|
// width: {
|
||||||
|
// cflags: ["-DWIDTH=%s"],
|
||||||
|
// },
|
||||||
// },
|
// },
|
||||||
// }
|
// }
|
||||||
//
|
//
|
||||||
@@ -192,6 +201,7 @@ type soongConfigModuleTypeModule struct {
|
|||||||
//
|
//
|
||||||
// SOONG_CONFIG_acme_board := soc_a
|
// SOONG_CONFIG_acme_board := soc_a
|
||||||
// SOONG_CONFIG_acme_feature := true
|
// 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".
|
||||||
func soongConfigModuleTypeFactory() Module {
|
func soongConfigModuleTypeFactory() Module {
|
||||||
@@ -352,7 +362,12 @@ func soongConfigModuleFactory(factory blueprint.ModuleFactory,
|
|||||||
|
|
||||||
AddLoadHook(module, func(ctx LoadHookContext) {
|
AddLoadHook(module, func(ctx LoadHookContext) {
|
||||||
config := ctx.Config().VendorConfig(moduleType.ConfigNamespace)
|
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)
|
ctx.AppendProperties(ps)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@@ -45,6 +45,7 @@ func TestSoongConfigModule(t *testing.T) {
|
|||||||
config_namespace: "acme",
|
config_namespace: "acme",
|
||||||
variables: ["board", "feature1", "FEATURE3"],
|
variables: ["board", "feature1", "FEATURE3"],
|
||||||
bool_variables: ["feature2"],
|
bool_variables: ["feature2"],
|
||||||
|
value_variables: ["size"],
|
||||||
properties: ["cflags", "srcs"],
|
properties: ["cflags", "srcs"],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,6 +83,9 @@ func TestSoongConfigModule(t *testing.T) {
|
|||||||
cflags: ["-DSOC_B"],
|
cflags: ["-DSOC_B"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
size: {
|
||||||
|
cflags: ["-DSIZE=%s"],
|
||||||
|
},
|
||||||
feature1: {
|
feature1: {
|
||||||
cflags: ["-DFEATURE1"],
|
cflags: ["-DFEATURE1"],
|
||||||
},
|
},
|
||||||
@@ -101,6 +105,7 @@ func TestSoongConfigModule(t *testing.T) {
|
|||||||
config.TestProductVariables.VendorVars = map[string]map[string]string{
|
config.TestProductVariables.VendorVars = map[string]map[string]string{
|
||||||
"acme": map[string]string{
|
"acme": map[string]string{
|
||||||
"board": "soc_a",
|
"board": "soc_a",
|
||||||
|
"size": "42",
|
||||||
"feature1": "true",
|
"feature1": "true",
|
||||||
"feature2": "false",
|
"feature2": "false",
|
||||||
// FEATURE3 unset
|
// FEATURE3 unset
|
||||||
@@ -121,7 +126,7 @@ func TestSoongConfigModule(t *testing.T) {
|
|||||||
FailIfErrored(t, errs)
|
FailIfErrored(t, errs)
|
||||||
|
|
||||||
foo := ctx.ModuleForTests("foo", "").Module().(*soongConfigTestModule)
|
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)
|
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
|
// the list of boolean SOONG_CONFIG variables that this module type will read
|
||||||
Bool_variables []string
|
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.
|
// the list of properties that this module type will extend.
|
||||||
Properties []string
|
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
|
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
|
// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
|
||||||
// based on SoongConfig values.
|
// 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{}
|
var ret []interface{}
|
||||||
props = props.Elem().FieldByName(soongConfigProperty)
|
props = props.Elem().FieldByName(soongConfigProperty)
|
||||||
for i, c := range moduleType.Variables {
|
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)
|
ret = append(ret, ps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return ret
|
return ret, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type ModuleType struct {
|
type ModuleType struct {
|
||||||
@@ -438,7 +456,7 @@ type soongConfigVariable interface {
|
|||||||
|
|
||||||
// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
|
// PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
|
||||||
// to the module.
|
// to the module.
|
||||||
PropertiesToApply(config SoongConfig, values reflect.Value) interface{}
|
PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type baseVariable struct {
|
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 {
|
for j, v := range s.values {
|
||||||
if config.String(s.variable) == v {
|
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 {
|
type boolVariable struct {
|
||||||
@@ -495,11 +513,83 @@ func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
|
|||||||
v.Set(reflect.Zero(typ))
|
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) {
|
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
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user