android:path attribute is respected for fields in a slice of struct

This change fixes a bug that android:path attribute is ignored when
annotated field is in a slice of struct.

blueprint now traverses fields whose type is slice of struct and
provides the index list in the case. Soong is modified so that it checks
whether the field being referenced is a slice of struct or not. If that
is the case, it gathers field values from each of the elements. If not,
it follows the original path.

Bug: 181018147
Test: m nothing
Change-Id: I220efb6feaa525a00939654459b2998e98e7ad56
This commit is contained in:
Jiyong Park 2021-02-24 01:22:57 +09:00
parent 0d2497088c
commit 66dd5c09e2
2 changed files with 89 additions and 35 deletions

View File

@ -76,52 +76,73 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string {
var ret []string
for _, i := range pathPropertyIndexes {
// Turn an index into a field.
sv := fieldByIndex(v, i)
if !sv.IsValid() {
// Skip properties inside a nil pointer.
continue
}
// If the field is a non-nil pointer step into it.
if sv.Kind() == reflect.Ptr {
if sv.IsNil() {
var values []reflect.Value
fieldsByIndex(v, i, &values)
for _, sv := range values {
if !sv.IsValid() {
// Skip properties inside a nil pointer.
continue
}
sv = sv.Elem()
}
// Collect paths from all strings and slices of strings.
switch sv.Kind() {
case reflect.String:
ret = append(ret, sv.String())
case reflect.Slice:
ret = append(ret, sv.Interface().([]string)...)
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()))
// If the field is a non-nil pointer step into it.
if sv.Kind() == reflect.Ptr {
if sv.IsNil() {
continue
}
sv = sv.Elem()
}
// Collect paths from all strings and slices of strings.
switch sv.Kind() {
case reflect.String:
ret = append(ret, sv.String())
case reflect.Slice:
ret = append(ret, sv.Interface().([]string)...)
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()))
}
}
}
return ret
}
// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when
// traversing a nil pointer to a struct.
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track
// nil pointers and it returns multiple values when there's slice of struct.
func fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) {
// leaf case
if len(index) == 1 {
return v.Field(index[0])
}
for _, x := range index {
if v.Kind() == reflect.Ptr {
if v.IsNil() {
return reflect.Value{}
if isSliceOfStruct(v) {
for i := 0; i < v.Len(); i++ {
*values = append(*values, v.Index(i).Field(index[0]))
}
v = v.Elem()
} else {
*values = append(*values, v.Field(index[0]))
}
v = v.Field(x)
return
}
return v
// recursion
if v.Kind() == reflect.Ptr {
// don't track nil pointer
if v.IsNil() {
return
}
v = v.Elem()
} else if isSliceOfStruct(v) {
// do the recursion for all elements
for i := 0; i < v.Len(); i++ {
fieldsByIndex(v.Index(i).Field(index[0]), index[1:], values)
}
return
}
fieldsByIndex(v.Field(index[0]), index[1:], values)
return
}
func isSliceOfStruct(v reflect.Value) bool {
return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct
}
var pathPropertyIndexesCache OncePer

View File

@ -33,12 +33,21 @@ type pathDepsMutatorTestModule struct {
Foo string `android:"path"`
}
// nested slices of struct
props3 struct {
X []struct {
Y []struct {
Z []string `android:"path"`
}
}
}
sourceDeps []string
}
func pathDepsMutatorTestModuleFactory() Module {
module := &pathDepsMutatorTestModule{}
module.AddProperties(&module.props, &module.props2)
module.AddProperties(&module.props, &module.props2, &module.props3)
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
return module
}
@ -73,8 +82,20 @@ func TestPathDepsMutator(t *testing.T) {
bar: [":b"],
baz: ":c{.bar}",
qux: ":d",
x: [
{
y: [
{
z: [":x", ":y"],
},
{
z: [":z"],
},
],
},
],
}`,
deps: []string{"a", "b", "c"},
deps: []string{"a", "b", "c", "x", "y", "z"},
},
{
name: "arch variant",
@ -113,6 +134,18 @@ func TestPathDepsMutator(t *testing.T) {
filegroup {
name: "d",
}
filegroup {
name: "x",
}
filegroup {
name: "y",
}
filegroup {
name: "z",
}
`
config := TestArchConfig(buildDir, nil, bp, nil)