Make select statements work on path properties
Fixes: 329711542
Test: go test
Change-Id: I71f489c26c535174e226e4a9ab449cc2b4bee83a
diff --git a/android/base_module_context.go b/android/base_module_context.go
index b9c1153..dd38a4e 100644
--- a/android/base_module_context.go
+++ b/android/base_module_context.go
@@ -16,9 +16,11 @@
import (
"fmt"
- "github.com/google/blueprint"
"regexp"
"strings"
+
+ "github.com/google/blueprint"
+ "github.com/google/blueprint/parser"
)
// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
@@ -214,6 +216,10 @@
// getMissingDependencies returns the list of missing dependencies.
// Calling this function prevents adding new dependencies.
getMissingDependencies() []string
+
+ // EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
+ // can be used to evaluate the final value of Configurable properties.
+ EvaluateConfiguration(parser.SelectType, string) (string, bool)
}
type baseModuleContext struct {
@@ -564,3 +570,32 @@
}
return sb.String()
}
+
+func (m *baseModuleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) {
+ switch ty {
+ case parser.SelectTypeReleaseVariable:
+ if v, ok := m.Config().productVariables.BuildFlags[condition]; ok {
+ return v, true
+ }
+ return "", false
+ case parser.SelectTypeProductVariable:
+ // TODO(b/323382414): Might add these on a case-by-case basis
+ m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects")
+ return "", false
+ case parser.SelectTypeSoongConfigVariable:
+ parts := strings.Split(condition, ":")
+ namespace := parts[0]
+ variable := parts[1]
+ if n, ok := m.Config().productVariables.VendorVars[namespace]; ok {
+ if v, ok := n[variable]; ok {
+ return v, true
+ }
+ }
+ return "", false
+ case parser.SelectTypeVariant:
+ m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects")
+ return "", false
+ default:
+ panic("Should be unreachable")
+ }
+}
diff --git a/android/module_context.go b/android/module_context.go
index 3fc5d01..1cab630 100644
--- a/android/module_context.go
+++ b/android/module_context.go
@@ -21,7 +21,6 @@
"strings"
"github.com/google/blueprint"
- "github.com/google/blueprint/parser"
"github.com/google/blueprint/proptools"
)
@@ -213,10 +212,6 @@
// GenerateAndroidBuildActions. If it is called then the struct will be written out and included in
// the module-info.json generated by Make, and Make will not generate its own data for this module.
ModuleInfoJSON() *ModuleInfoJSON
-
- // EvaluateConfiguration makes ModuleContext a valid proptools.ConfigurableEvaluator, so this context
- // can be used to evaluate the final value of Configurable properties.
- EvaluateConfiguration(parser.SelectType, string) (string, bool)
}
type moduleContext struct {
@@ -719,32 +714,3 @@
func (m *moduleContext) TargetRequiredModuleNames() []string {
return m.module.TargetRequiredModuleNames()
}
-
-func (m *moduleContext) EvaluateConfiguration(ty parser.SelectType, condition string) (string, bool) {
- switch ty {
- case parser.SelectTypeReleaseVariable:
- if v, ok := m.Config().productVariables.BuildFlags[condition]; ok {
- return v, true
- }
- return "", false
- case parser.SelectTypeProductVariable:
- // TODO: Might add these on a case-by-case basis
- m.ModuleErrorf("TODO(b/323382414): Product variables are not yet supported in selects")
- return "", false
- case parser.SelectTypeSoongConfigVariable:
- parts := strings.Split(condition, ":")
- namespace := parts[0]
- variable := parts[1]
- if n, ok := m.Config().productVariables.VendorVars[namespace]; ok {
- if v, ok := n[variable]; ok {
- return v, true
- }
- }
- return "", false
- case parser.SelectTypeVariant:
- m.ModuleErrorf("TODO(b/323382414): Variants are not yet supported in selects")
- return "", false
- default:
- panic("Should be unreachable")
- }
-}
diff --git a/android/path_properties.go b/android/path_properties.go
index bbfaa8c..ea92565 100644
--- a/android/path_properties.go
+++ b/android/path_properties.go
@@ -47,7 +47,7 @@
// tagged with `android:"path"`.
var pathProperties []string
for _, ps := range props {
- pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...)
+ pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
}
// Remove duplicates to avoid multiple dependencies.
@@ -64,7 +64,7 @@
// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with
// android:"path" to extract all their values from a property struct, returning them as a single
// slice of strings.
-func pathPropertiesForPropertyStruct(ps interface{}) []string {
+func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
v := reflect.ValueOf(ps)
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
@@ -106,6 +106,16 @@
ret = append(ret, sv.String())
case reflect.Slice:
ret = append(ret, sv.Interface().([]string)...)
+ case reflect.Struct:
+ intf := sv.Interface()
+ if configurable, ok := intf.(proptools.Configurable[string]); ok {
+ ret = append(ret, proptools.String(configurable.Evaluate(ctx)))
+ } else if configurable, ok := intf.(proptools.Configurable[[]string]); ok {
+ ret = append(ret, proptools.Slice(configurable.Evaluate(ctx))...)
+ } else {
+ panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
+ v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
+ }
default:
panic(fmt.Errorf(`field %s in type %s has tag android:"path" but is not a string or slice of strings, it is a %s`,
v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
diff --git a/android/selects_test.go b/android/selects_test.go
index dca3789..aa9c521 100644
--- a/android/selects_test.go
+++ b/android/selects_test.go
@@ -80,6 +80,36 @@
},
},
{
+ name: "basic paths",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
+ "a": ["foo.txt"],
+ "b": ["bar.txt"],
+ _: ["baz.txt"],
+ }),
+ }
+ `,
+ provider: selectsTestProvider{
+ my_paths: &[]string{"baz.txt"},
+ },
+ },
+ {
+ name: "paths with module references",
+ bp: `
+ my_module_type {
+ name: "foo",
+ my_paths: select(soong_config_variable("my_namespace", "my_variable"), {
+ "a": [":a"],
+ "b": [":b"],
+ _: [":c"],
+ }),
+ }
+ `,
+ expectedError: `"foo" depends on undefined module "c"`,
+ },
+ {
name: "Differing types",
bp: `
my_module_type {
@@ -233,6 +263,7 @@
my_bool *bool
my_string *string
my_string_list *[]string
+ my_paths *[]string
}
func (p *selectsTestProvider) String() string {
@@ -248,7 +279,8 @@
my_bool: %v,
my_string: %s,
my_string_list: %s,
-}`, myBoolStr, myStringStr, p.my_string_list)
+ my_paths: %s,
+}`, myBoolStr, myStringStr, p.my_string_list, p.my_paths)
}
var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
@@ -257,6 +289,7 @@
My_bool proptools.Configurable[bool]
My_string proptools.Configurable[string]
My_string_list proptools.Configurable[[]string]
+ My_paths proptools.Configurable[[]string] `android:"path"`
}
type selectsMockModule struct {
@@ -266,10 +299,11 @@
}
func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
- SetProvider[selectsTestProvider](ctx, selectsTestProviderKey, selectsTestProvider{
+ SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
my_bool: p.properties.My_bool.Evaluate(ctx),
my_string: p.properties.My_string.Evaluate(ctx),
my_string_list: p.properties.My_string_list.Evaluate(ctx),
+ my_paths: p.properties.My_paths.Evaluate(ctx),
})
}