Loading android/path_properties.go +54 −33 Original line number Diff line number Diff line Loading @@ -76,8 +76,9 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string { var ret []string for _, i := range pathPropertyIndexes { // Turn an index into a field. sv := fieldByIndex(v, i) var values []reflect.Value fieldsByIndex(v, i, &values) for _, sv := range values { if !sv.IsValid() { // Skip properties inside a nil pointer. continue Loading @@ -102,26 +103,46 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string { 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]) if isSliceOfStruct(v) { for i := 0; i < v.Len(); i++ { *values = append(*values, v.Index(i).Field(index[0])) } } else { *values = append(*values, v.Field(index[0])) } return } for _, x := range index { // recursion if v.Kind() == reflect.Ptr { // don't track nil pointer if v.IsNil() { return reflect.Value{} 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 } v = v.Field(x) fieldsByIndex(v.Field(index[0]), index[1:], values) return } return v func isSliceOfStruct(v reflect.Value) bool { return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct } var pathPropertyIndexesCache OncePer Loading android/path_properties_test.go +35 −2 Original line number Diff line number Diff line Loading @@ -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 } Loading Loading @@ -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", Loading Loading @@ -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) Loading Loading
android/path_properties.go +54 −33 Original line number Diff line number Diff line Loading @@ -76,8 +76,9 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string { var ret []string for _, i := range pathPropertyIndexes { // Turn an index into a field. sv := fieldByIndex(v, i) var values []reflect.Value fieldsByIndex(v, i, &values) for _, sv := range values { if !sv.IsValid() { // Skip properties inside a nil pointer. continue Loading @@ -102,26 +103,46 @@ func pathPropertiesForPropertyStruct(ps interface{}) []string { 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]) if isSliceOfStruct(v) { for i := 0; i < v.Len(); i++ { *values = append(*values, v.Index(i).Field(index[0])) } } else { *values = append(*values, v.Field(index[0])) } return } for _, x := range index { // recursion if v.Kind() == reflect.Ptr { // don't track nil pointer if v.IsNil() { return reflect.Value{} 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 } v = v.Field(x) fieldsByIndex(v.Field(index[0]), index[1:], values) return } return v func isSliceOfStruct(v reflect.Value) bool { return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct } var pathPropertyIndexesCache OncePer Loading
android/path_properties_test.go +35 −2 Original line number Diff line number Diff line Loading @@ -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 } Loading Loading @@ -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", Loading Loading @@ -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) Loading