Make select statements work on path properties
Fixes: 329711542 Test: go test Change-Id: I71f489c26c535174e226e4a9ab449cc2b4bee83a
This commit is contained in:
@@ -16,9 +16,11 @@ package android
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/google/blueprint"
|
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/blueprint"
|
||||||
|
"github.com/google/blueprint/parser"
|
||||||
)
|
)
|
||||||
|
|
||||||
// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
|
// BaseModuleContext is the same as blueprint.BaseModuleContext except that Config() returns
|
||||||
@@ -214,6 +216,10 @@ type BaseModuleContext interface {
|
|||||||
// getMissingDependencies returns the list of missing dependencies.
|
// getMissingDependencies returns the list of missing dependencies.
|
||||||
// Calling this function prevents adding new dependencies.
|
// Calling this function prevents adding new dependencies.
|
||||||
getMissingDependencies() []string
|
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 {
|
type baseModuleContext struct {
|
||||||
@@ -564,3 +570,32 @@ func (b *baseModuleContext) GetPathString(skipFirst bool) string {
|
|||||||
}
|
}
|
||||||
return sb.String()
|
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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -21,7 +21,6 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/google/blueprint"
|
"github.com/google/blueprint"
|
||||||
"github.com/google/blueprint/parser"
|
|
||||||
"github.com/google/blueprint/proptools"
|
"github.com/google/blueprint/proptools"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -213,10 +212,6 @@ type ModuleContext interface {
|
|||||||
// GenerateAndroidBuildActions. If it is called then the struct will be written out and included in
|
// 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.
|
// the module-info.json generated by Make, and Make will not generate its own data for this module.
|
||||||
ModuleInfoJSON() *ModuleInfoJSON
|
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 {
|
type moduleContext struct {
|
||||||
@@ -719,32 +714,3 @@ func (m *moduleContext) HostRequiredModuleNames() []string {
|
|||||||
func (m *moduleContext) TargetRequiredModuleNames() []string {
|
func (m *moduleContext) TargetRequiredModuleNames() []string {
|
||||||
return m.module.TargetRequiredModuleNames()
|
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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -47,7 +47,7 @@ func addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) {
|
|||||||
// tagged with `android:"path"`.
|
// tagged with `android:"path"`.
|
||||||
var pathProperties []string
|
var pathProperties []string
|
||||||
for _, ps := range props {
|
for _, ps := range props {
|
||||||
pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...)
|
pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ctx, ps)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove duplicates to avoid multiple dependencies.
|
// Remove duplicates to avoid multiple dependencies.
|
||||||
@@ -64,7 +64,7 @@ func addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) {
|
|||||||
// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with
|
// 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
|
// android:"path" to extract all their values from a property struct, returning them as a single
|
||||||
// slice of strings.
|
// slice of strings.
|
||||||
func pathPropertiesForPropertyStruct(ps interface{}) []string {
|
func pathPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}) []string {
|
||||||
v := reflect.ValueOf(ps)
|
v := reflect.ValueOf(ps)
|
||||||
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct {
|
||||||
panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
|
panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type()))
|
||||||
@@ -106,6 +106,16 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string {
|
|||||||
ret = append(ret, sv.String())
|
ret = append(ret, sv.String())
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
ret = append(ret, sv.Interface().([]string)...)
|
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:
|
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`,
|
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()))
|
v.Type().FieldByIndex(i).Name, v.Type(), sv.Type()))
|
||||||
|
@@ -79,6 +79,36 @@ func TestSelects(t *testing.T) {
|
|||||||
my_bool: proptools.BoolPtr(true),
|
my_bool: proptools.BoolPtr(true),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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",
|
name: "Differing types",
|
||||||
bp: `
|
bp: `
|
||||||
@@ -233,6 +263,7 @@ type selectsTestProvider struct {
|
|||||||
my_bool *bool
|
my_bool *bool
|
||||||
my_string *string
|
my_string *string
|
||||||
my_string_list *[]string
|
my_string_list *[]string
|
||||||
|
my_paths *[]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *selectsTestProvider) String() string {
|
func (p *selectsTestProvider) String() string {
|
||||||
@@ -248,7 +279,8 @@ func (p *selectsTestProvider) String() string {
|
|||||||
my_bool: %v,
|
my_bool: %v,
|
||||||
my_string: %s,
|
my_string: %s,
|
||||||
my_string_list: %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]()
|
var selectsTestProviderKey = blueprint.NewProvider[selectsTestProvider]()
|
||||||
@@ -257,6 +289,7 @@ type selectsMockModuleProperties struct {
|
|||||||
My_bool proptools.Configurable[bool]
|
My_bool proptools.Configurable[bool]
|
||||||
My_string proptools.Configurable[string]
|
My_string proptools.Configurable[string]
|
||||||
My_string_list proptools.Configurable[[]string]
|
My_string_list proptools.Configurable[[]string]
|
||||||
|
My_paths proptools.Configurable[[]string] `android:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type selectsMockModule struct {
|
type selectsMockModule struct {
|
||||||
@@ -266,10 +299,11 @@ type selectsMockModule struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
|
func (p *selectsMockModule) GenerateAndroidBuildActions(ctx ModuleContext) {
|
||||||
SetProvider[selectsTestProvider](ctx, selectsTestProviderKey, selectsTestProvider{
|
SetProvider(ctx, selectsTestProviderKey, selectsTestProvider{
|
||||||
my_bool: p.properties.My_bool.Evaluate(ctx),
|
my_bool: p.properties.My_bool.Evaluate(ctx),
|
||||||
my_string: p.properties.My_string.Evaluate(ctx),
|
my_string: p.properties.My_string.Evaluate(ctx),
|
||||||
my_string_list: p.properties.My_string_list.Evaluate(ctx),
|
my_string_list: p.properties.My_string_list.Evaluate(ctx),
|
||||||
|
my_paths: p.properties.My_paths.Evaluate(ctx),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user