1// Copyright 2019 Google Inc. All rights reserved. 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package android 16 17import ( 18 "fmt" 19 "reflect" 20 21 "github.com/google/blueprint/proptools" 22) 23 24// This file implements support for automatically adding dependencies on any module referenced 25// with the ":module" module reference syntax in a property that is annotated with `android:"path"`. 26// The dependency is used by android.PathForModuleSrc to convert the module reference into the path 27// to the output file of the referenced module. 28 29func registerPathDepsMutator(ctx RegisterMutatorsContext) { 30 ctx.BottomUp("pathdeps", pathDepsMutator).Parallel() 31} 32 33// The pathDepsMutator automatically adds dependencies on any module that is listed with the 34// ":module" module reference syntax in a property that is tagged with `android:"path"`. 35func pathDepsMutator(ctx BottomUpMutatorContext) { 36 props := ctx.Module().base().generalProperties 37 addPathDepsForProps(ctx, props) 38} 39 40func addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) { 41 // Iterate through each property struct of the module extracting the contents of all properties 42 // tagged with `android:"path"`. 43 var pathProperties []string 44 for _, ps := range props { 45 pathProperties = append(pathProperties, pathPropertiesForPropertyStruct(ps)...) 46 } 47 48 // Remove duplicates to avoid multiple dependencies. 49 pathProperties = FirstUniqueStrings(pathProperties) 50 51 // Add dependencies to anything that is a module reference. 52 for _, s := range pathProperties { 53 if m, t := SrcIsModuleWithTag(s); m != "" { 54 ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(t), m) 55 } 56 } 57} 58 59// pathPropertiesForPropertyStruct uses the indexes of properties that are tagged with 60// android:"path" to extract all their values from a property struct, returning them as a single 61// slice of strings. 62func pathPropertiesForPropertyStruct(ps interface{}) []string { 63 v := reflect.ValueOf(ps) 64 if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { 65 panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type())) 66 } 67 68 // If the property struct is a nil pointer it can't have any paths set in it. 69 if v.IsNil() { 70 return nil 71 } 72 73 // v is now the reflect.Value for the concrete property struct. 74 v = v.Elem() 75 76 // Get or create the list of indexes of properties that are tagged with `android:"path"`. 77 pathPropertyIndexes := pathPropertyIndexesForPropertyStruct(ps) 78 79 var ret []string 80 81 for _, i := range pathPropertyIndexes { 82 var values []reflect.Value 83 fieldsByIndex(v, i, &values) 84 for _, sv := range values { 85 if !sv.IsValid() { 86 // Skip properties inside a nil pointer. 87 continue 88 } 89 90 // If the field is a non-nil pointer step into it. 91 if sv.Kind() == reflect.Ptr { 92 if sv.IsNil() { 93 continue 94 } 95 sv = sv.Elem() 96 } 97 98 // Collect paths from all strings and slices of strings. 99 switch sv.Kind() { 100 case reflect.String: 101 ret = append(ret, sv.String()) 102 case reflect.Slice: 103 ret = append(ret, sv.Interface().([]string)...) 104 default: 105 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`, 106 v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) 107 } 108 } 109 } 110 111 return ret 112} 113 114// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track 115// nil pointers and it returns multiple values when there's slice of struct. 116func fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) { 117 // leaf case 118 if len(index) == 1 { 119 if isSliceOfStruct(v) { 120 for i := 0; i < v.Len(); i++ { 121 *values = append(*values, v.Index(i).Field(index[0])) 122 } 123 } else { 124 // Dereference it if it's a pointer. 125 if v.Kind() == reflect.Ptr { 126 if v.IsNil() { 127 return 128 } 129 v = v.Elem() 130 } 131 *values = append(*values, v.Field(index[0])) 132 } 133 return 134 } 135 136 // recursion 137 if v.Kind() == reflect.Ptr { 138 // don't track nil pointer 139 if v.IsNil() { 140 return 141 } 142 v = v.Elem() 143 } else if isSliceOfStruct(v) { 144 // do the recursion for all elements 145 for i := 0; i < v.Len(); i++ { 146 fieldsByIndex(v.Index(i).Field(index[0]), index[1:], values) 147 } 148 return 149 } 150 fieldsByIndex(v.Field(index[0]), index[1:], values) 151 return 152} 153 154func isSliceOfStruct(v reflect.Value) bool { 155 return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct 156} 157 158var pathPropertyIndexesCache OncePer 159 160// pathPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in 161// property struct type that are tagged with `android:"path"`. Each index is a []int suitable for 162// passing to reflect.Value.FieldByIndex. The value is cached in a global cache by type. 163func pathPropertyIndexesForPropertyStruct(ps interface{}) [][]int { 164 key := NewCustomOnceKey(reflect.TypeOf(ps)) 165 return pathPropertyIndexesCache.Once(key, func() interface{} { 166 return proptools.PropertyIndexesWithTag(ps, "android", "path") 167 }).([][]int) 168} 169