Merge "android:path attribute is respected for fields in a slice of struct"
This commit is contained in:
commit
4dc0702ce2
2 changed files with 89 additions and 35 deletions
|
@ -76,52 +76,73 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string {
|
||||||
var ret []string
|
var ret []string
|
||||||
|
|
||||||
for _, i := range pathPropertyIndexes {
|
for _, i := range pathPropertyIndexes {
|
||||||
// Turn an index into a field.
|
var values []reflect.Value
|
||||||
sv := fieldByIndex(v, i)
|
fieldsByIndex(v, i, &values)
|
||||||
if !sv.IsValid() {
|
for _, sv := range values {
|
||||||
// Skip properties inside a nil pointer.
|
if !sv.IsValid() {
|
||||||
continue
|
// Skip properties inside a nil pointer.
|
||||||
}
|
|
||||||
|
|
||||||
// If the field is a non-nil pointer step into it.
|
|
||||||
if sv.Kind() == reflect.Ptr {
|
|
||||||
if sv.IsNil() {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sv = sv.Elem()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collect paths from all strings and slices of strings.
|
// If the field is a non-nil pointer step into it.
|
||||||
switch sv.Kind() {
|
if sv.Kind() == reflect.Ptr {
|
||||||
case reflect.String:
|
if sv.IsNil() {
|
||||||
ret = append(ret, sv.String())
|
continue
|
||||||
case reflect.Slice:
|
}
|
||||||
ret = append(ret, sv.Interface().([]string)...)
|
sv = sv.Elem()
|
||||||
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()))
|
// 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
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
// fieldByIndex is like reflect.Value.FieldByIndex, but returns an invalid reflect.Value when
|
// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track
|
||||||
// traversing a nil pointer to a struct.
|
// nil pointers and it returns multiple values when there's slice of struct.
|
||||||
func fieldByIndex(v reflect.Value, index []int) reflect.Value {
|
func fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) {
|
||||||
|
// leaf case
|
||||||
if len(index) == 1 {
|
if len(index) == 1 {
|
||||||
return v.Field(index[0])
|
if isSliceOfStruct(v) {
|
||||||
}
|
for i := 0; i < v.Len(); i++ {
|
||||||
for _, x := range index {
|
*values = append(*values, v.Index(i).Field(index[0]))
|
||||||
if v.Kind() == reflect.Ptr {
|
|
||||||
if v.IsNil() {
|
|
||||||
return reflect.Value{}
|
|
||||||
}
|
}
|
||||||
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
|
var pathPropertyIndexesCache OncePer
|
||||||
|
|
|
@ -33,12 +33,21 @@ type pathDepsMutatorTestModule struct {
|
||||||
Foo string `android:"path"`
|
Foo string `android:"path"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// nested slices of struct
|
||||||
|
props3 struct {
|
||||||
|
X []struct {
|
||||||
|
Y []struct {
|
||||||
|
Z []string `android:"path"`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceDeps []string
|
sourceDeps []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func pathDepsMutatorTestModuleFactory() Module {
|
func pathDepsMutatorTestModuleFactory() Module {
|
||||||
module := &pathDepsMutatorTestModule{}
|
module := &pathDepsMutatorTestModule{}
|
||||||
module.AddProperties(&module.props, &module.props2)
|
module.AddProperties(&module.props, &module.props2, &module.props3)
|
||||||
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
|
InitAndroidArchModule(module, DeviceSupported, MultilibBoth)
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
@ -73,8 +82,20 @@ func TestPathDepsMutator(t *testing.T) {
|
||||||
bar: [":b"],
|
bar: [":b"],
|
||||||
baz: ":c{.bar}",
|
baz: ":c{.bar}",
|
||||||
qux: ":d",
|
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",
|
name: "arch variant",
|
||||||
|
@ -113,6 +134,18 @@ func TestPathDepsMutator(t *testing.T) {
|
||||||
filegroup {
|
filegroup {
|
||||||
name: "d",
|
name: "d",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
filegroup {
|
||||||
|
name: "x",
|
||||||
|
}
|
||||||
|
|
||||||
|
filegroup {
|
||||||
|
name: "y",
|
||||||
|
}
|
||||||
|
|
||||||
|
filegroup {
|
||||||
|
name: "z",
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
config := TestArchConfig(buildDir, nil, bp, nil)
|
config := TestArchConfig(buildDir, nil, bp, nil)
|
||||||
|
|
Loading…
Reference in a new issue