Merge "soong config: add value_variable substitution"
diff --git a/README.md b/README.md
index 3eac87b..8b028a8 100644
--- a/README.md
+++ b/README.md
@@ -421,6 +421,7 @@
config_namespace: "acme",
variables: ["board"],
bool_variables: ["feature"],
+ value_variables: ["width"],
properties: ["cflags", "srcs"],
}
@@ -431,8 +432,9 @@
```
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_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 @@
feature: {
cflags: ["-DFEATURE"],
},
+ width: {
+ cflags: ["-DWIDTH=%s"],
+ },
},
}
@@ -482,7 +488,7 @@
```
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
diff --git a/android/soong_config_modules.go b/android/soong_config_modules.go
index 6ce609a..619cf86 100644
--- a/android/soong_config_modules.go
+++ b/android/soong_config_modules.go
@@ -73,6 +73,9 @@
// feature: {
// cflags: ["-DFEATURE"],
// },
+// width: {
+// cflags: ["-DWIDTH=%s"],
+// },
// },
// }
//
@@ -90,6 +93,7 @@
// config_namespace: "acme",
// variables: ["board"],
// bool_variables: ["feature"],
+// value_variables: ["width"],
// properties: ["cflags", "srcs"],
// }
//
@@ -107,8 +111,9 @@
//
// 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 @@
// config_namespace: "acme",
// variables: ["board"],
// bool_variables: ["feature"],
+// value_variables: ["width"],
// properties: ["cflags", "srcs"],
// }
//
@@ -174,6 +180,9 @@
// feature: {
// cflags: ["-DFEATURE"],
// },
+// width: {
+// cflags: ["-DWIDTH=%s"],
+// },
// },
// }
//
@@ -192,6 +201,7 @@
//
// 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 @@
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)
}
})
diff --git a/android/soong_config_modules_test.go b/android/soong_config_modules_test.go
index 1cf060d..f905b1a 100644
--- a/android/soong_config_modules_test.go
+++ b/android/soong_config_modules_test.go
@@ -45,6 +45,7 @@
config_namespace: "acme",
variables: ["board", "feature1", "FEATURE3"],
bool_variables: ["feature2"],
+ value_variables: ["size"],
properties: ["cflags", "srcs"],
}
@@ -82,6 +83,9 @@
cflags: ["-DSOC_B"],
},
},
+ size: {
+ cflags: ["-DSIZE=%s"],
+ },
feature1: {
cflags: ["-DFEATURE1"],
},
@@ -101,6 +105,7 @@
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 @@
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)
}
}
diff --git a/android/soongconfig/modules.go b/android/soongconfig/modules.go
index 2d6063d..142a813 100644
--- a/android/soongconfig/modules.go
+++ b/android/soongconfig/modules.go
@@ -112,6 +112,10 @@
// 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 @@
})
}
+ 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 @@
// 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 @@
// 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) 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 @@
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
}