1*333d2b36SAndroid Build Coastguard Worker// Copyright 2019 Google Inc. All rights reserved. 2*333d2b36SAndroid Build Coastguard Worker// 3*333d2b36SAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*333d2b36SAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*333d2b36SAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*333d2b36SAndroid Build Coastguard Worker// 7*333d2b36SAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*333d2b36SAndroid Build Coastguard Worker// 9*333d2b36SAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*333d2b36SAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*333d2b36SAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*333d2b36SAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*333d2b36SAndroid Build Coastguard Worker// limitations under the License. 14*333d2b36SAndroid Build Coastguard Worker 15*333d2b36SAndroid Build Coastguard Workerpackage android 16*333d2b36SAndroid Build Coastguard Worker 17*333d2b36SAndroid Build Coastguard Workerimport ( 18*333d2b36SAndroid Build Coastguard Worker "fmt" 19*333d2b36SAndroid Build Coastguard Worker "reflect" 20*333d2b36SAndroid Build Coastguard Worker 21*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint" 22*333d2b36SAndroid Build Coastguard Worker "github.com/google/blueprint/proptools" 23*333d2b36SAndroid Build Coastguard Worker) 24*333d2b36SAndroid Build Coastguard Worker 25*333d2b36SAndroid Build Coastguard Worker// This file implements support for automatically adding dependencies on any module referenced 26*333d2b36SAndroid Build Coastguard Worker// with the ":module" module reference syntax in a property that is annotated with `android:"path"`. 27*333d2b36SAndroid Build Coastguard Worker// The dependency is used by android.PathForModuleSrc to convert the module reference into the path 28*333d2b36SAndroid Build Coastguard Worker// to the output file of the referenced module. 29*333d2b36SAndroid Build Coastguard Worker 30*333d2b36SAndroid Build Coastguard Workerfunc registerPathDepsMutator(ctx RegisterMutatorsContext) { 31*333d2b36SAndroid Build Coastguard Worker ctx.BottomUp("pathdeps", pathDepsMutator) 32*333d2b36SAndroid Build Coastguard Worker} 33*333d2b36SAndroid Build Coastguard Worker 34*333d2b36SAndroid Build Coastguard Worker// The pathDepsMutator automatically adds dependencies on any module that is listed with the 35*333d2b36SAndroid Build Coastguard Worker// ":module" module reference syntax in a property that is tagged with `android:"path"`. 36*333d2b36SAndroid Build Coastguard Workerfunc pathDepsMutator(ctx BottomUpMutatorContext) { 37*333d2b36SAndroid Build Coastguard Worker if _, ok := ctx.Module().(DefaultsModule); ok { 38*333d2b36SAndroid Build Coastguard Worker // Defaults modules shouldn't have dependencies added for path properties, they have already been 39*333d2b36SAndroid Build Coastguard Worker // squashed into the real modules. 40*333d2b36SAndroid Build Coastguard Worker return 41*333d2b36SAndroid Build Coastguard Worker } 42*333d2b36SAndroid Build Coastguard Worker if !ctx.Module().Enabled(ctx) { 43*333d2b36SAndroid Build Coastguard Worker return 44*333d2b36SAndroid Build Coastguard Worker } 45*333d2b36SAndroid Build Coastguard Worker props := ctx.Module().base().GetProperties() 46*333d2b36SAndroid Build Coastguard Worker addPathDepsForProps(ctx, props) 47*333d2b36SAndroid Build Coastguard Worker} 48*333d2b36SAndroid Build Coastguard Worker 49*333d2b36SAndroid Build Coastguard Workerfunc addPathDepsForProps(ctx BottomUpMutatorContext, props []interface{}) { 50*333d2b36SAndroid Build Coastguard Worker // Iterate through each property struct of the module extracting the contents of all properties 51*333d2b36SAndroid Build Coastguard Worker // tagged with `android:"path"` or one of the variant-specifying tags. 52*333d2b36SAndroid Build Coastguard Worker var pathProperties []string 53*333d2b36SAndroid Build Coastguard Worker var pathDeviceFirstProperties []string 54*333d2b36SAndroid Build Coastguard Worker var pathDeviceFirstPrefer32Properties []string 55*333d2b36SAndroid Build Coastguard Worker var pathDeviceCommonProperties []string 56*333d2b36SAndroid Build Coastguard Worker var pathCommonOsProperties []string 57*333d2b36SAndroid Build Coastguard Worker for _, ps := range props { 58*333d2b36SAndroid Build Coastguard Worker pathProperties = append(pathProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path")...) 59*333d2b36SAndroid Build Coastguard Worker pathDeviceFirstProperties = append(pathDeviceFirstProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first")...) 60*333d2b36SAndroid Build Coastguard Worker pathDeviceFirstPrefer32Properties = append(pathDeviceFirstPrefer32Properties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_first_prefer32")...) 61*333d2b36SAndroid Build Coastguard Worker pathDeviceCommonProperties = append(pathDeviceCommonProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_device_common")...) 62*333d2b36SAndroid Build Coastguard Worker pathCommonOsProperties = append(pathCommonOsProperties, taggedPropertiesForPropertyStruct(ctx, ps, "path_common_os")...) 63*333d2b36SAndroid Build Coastguard Worker } 64*333d2b36SAndroid Build Coastguard Worker 65*333d2b36SAndroid Build Coastguard Worker // Remove duplicates to avoid multiple dependencies. 66*333d2b36SAndroid Build Coastguard Worker pathProperties = FirstUniqueStrings(pathProperties) 67*333d2b36SAndroid Build Coastguard Worker pathDeviceFirstProperties = FirstUniqueStrings(pathDeviceFirstProperties) 68*333d2b36SAndroid Build Coastguard Worker pathDeviceFirstPrefer32Properties = FirstUniqueStrings(pathDeviceFirstPrefer32Properties) 69*333d2b36SAndroid Build Coastguard Worker pathDeviceCommonProperties = FirstUniqueStrings(pathDeviceCommonProperties) 70*333d2b36SAndroid Build Coastguard Worker pathCommonOsProperties = FirstUniqueStrings(pathCommonOsProperties) 71*333d2b36SAndroid Build Coastguard Worker 72*333d2b36SAndroid Build Coastguard Worker // Add dependencies to anything that is a module reference. 73*333d2b36SAndroid Build Coastguard Worker for _, s := range pathProperties { 74*333d2b36SAndroid Build Coastguard Worker if m, t := SrcIsModuleWithTag(s); m != "" { 75*333d2b36SAndroid Build Coastguard Worker ctx.AddDependency(ctx.Module(), sourceOrOutputDepTag(m, t), m) 76*333d2b36SAndroid Build Coastguard Worker } 77*333d2b36SAndroid Build Coastguard Worker } 78*333d2b36SAndroid Build Coastguard Worker // For properties tagged "path_device_first", use the first arch device variant when adding 79*333d2b36SAndroid Build Coastguard Worker // dependencies. This allows host modules to have some properties that add dependencies on 80*333d2b36SAndroid Build Coastguard Worker // device modules. 81*333d2b36SAndroid Build Coastguard Worker for _, s := range pathDeviceFirstProperties { 82*333d2b36SAndroid Build Coastguard Worker if m, t := SrcIsModuleWithTag(s); m != "" { 83*333d2b36SAndroid Build Coastguard Worker ctx.AddVariationDependencies(ctx.Config().AndroidFirstDeviceTarget.Variations(), sourceOrOutputDepTag(m, t), m) 84*333d2b36SAndroid Build Coastguard Worker } 85*333d2b36SAndroid Build Coastguard Worker } 86*333d2b36SAndroid Build Coastguard Worker // properties tagged path_device_first_prefer32 get the first 32 bit target if one is available, 87*333d2b36SAndroid Build Coastguard Worker // otherwise they use the first 64 bit target 88*333d2b36SAndroid Build Coastguard Worker if len(pathDeviceFirstPrefer32Properties) > 0 { 89*333d2b36SAndroid Build Coastguard Worker var targets []Target 90*333d2b36SAndroid Build Coastguard Worker if ctx.Config().IgnorePrefer32OnDevice() { 91*333d2b36SAndroid Build Coastguard Worker targets, _ = decodeMultilibTargets("first", ctx.Config().Targets[Android], false) 92*333d2b36SAndroid Build Coastguard Worker } else { 93*333d2b36SAndroid Build Coastguard Worker targets, _ = decodeMultilibTargets("first_prefer32", ctx.Config().Targets[Android], false) 94*333d2b36SAndroid Build Coastguard Worker } 95*333d2b36SAndroid Build Coastguard Worker if len(targets) == 0 { 96*333d2b36SAndroid Build Coastguard Worker ctx.ModuleErrorf("Could not find a first_prefer32 target") 97*333d2b36SAndroid Build Coastguard Worker } else { 98*333d2b36SAndroid Build Coastguard Worker for _, s := range pathDeviceFirstPrefer32Properties { 99*333d2b36SAndroid Build Coastguard Worker if m, t := SrcIsModuleWithTag(s); m != "" { 100*333d2b36SAndroid Build Coastguard Worker ctx.AddVariationDependencies(targets[0].Variations(), sourceOrOutputDepTag(m, t), m) 101*333d2b36SAndroid Build Coastguard Worker } 102*333d2b36SAndroid Build Coastguard Worker } 103*333d2b36SAndroid Build Coastguard Worker } 104*333d2b36SAndroid Build Coastguard Worker } 105*333d2b36SAndroid Build Coastguard Worker // properties tagged "path_device_common" get the device common variant 106*333d2b36SAndroid Build Coastguard Worker for _, s := range pathDeviceCommonProperties { 107*333d2b36SAndroid Build Coastguard Worker if m, t := SrcIsModuleWithTag(s); m != "" { 108*333d2b36SAndroid Build Coastguard Worker ctx.AddVariationDependencies(ctx.Config().AndroidCommonTarget.Variations(), sourceOrOutputDepTag(m, t), m) 109*333d2b36SAndroid Build Coastguard Worker } 110*333d2b36SAndroid Build Coastguard Worker } 111*333d2b36SAndroid Build Coastguard Worker // properties tagged "path_common_os" get the CommonOs variant 112*333d2b36SAndroid Build Coastguard Worker for _, s := range pathCommonOsProperties { 113*333d2b36SAndroid Build Coastguard Worker if m, t := SrcIsModuleWithTag(s); m != "" { 114*333d2b36SAndroid Build Coastguard Worker ctx.AddVariationDependencies([]blueprint.Variation{ 115*333d2b36SAndroid Build Coastguard Worker {Mutator: "os", Variation: "common_os"}, 116*333d2b36SAndroid Build Coastguard Worker {Mutator: "arch", Variation: ""}, 117*333d2b36SAndroid Build Coastguard Worker }, sourceOrOutputDepTag(m, t), m) 118*333d2b36SAndroid Build Coastguard Worker } 119*333d2b36SAndroid Build Coastguard Worker } 120*333d2b36SAndroid Build Coastguard Worker} 121*333d2b36SAndroid Build Coastguard Worker 122*333d2b36SAndroid Build Coastguard Worker// taggedPropertiesForPropertyStruct uses the indexes of properties that are tagged with 123*333d2b36SAndroid Build Coastguard Worker// android:"tagValue" to extract all their values from a property struct, returning them as a single 124*333d2b36SAndroid Build Coastguard Worker// slice of strings. 125*333d2b36SAndroid Build Coastguard Workerfunc taggedPropertiesForPropertyStruct(ctx BottomUpMutatorContext, ps interface{}, tagValue string) []string { 126*333d2b36SAndroid Build Coastguard Worker v := reflect.ValueOf(ps) 127*333d2b36SAndroid Build Coastguard Worker if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { 128*333d2b36SAndroid Build Coastguard Worker panic(fmt.Errorf("type %s is not a pointer to a struct", v.Type())) 129*333d2b36SAndroid Build Coastguard Worker } 130*333d2b36SAndroid Build Coastguard Worker 131*333d2b36SAndroid Build Coastguard Worker // If the property struct is a nil pointer it can't have any paths set in it. 132*333d2b36SAndroid Build Coastguard Worker if v.IsNil() { 133*333d2b36SAndroid Build Coastguard Worker return nil 134*333d2b36SAndroid Build Coastguard Worker } 135*333d2b36SAndroid Build Coastguard Worker 136*333d2b36SAndroid Build Coastguard Worker // v is now the reflect.Value for the concrete property struct. 137*333d2b36SAndroid Build Coastguard Worker v = v.Elem() 138*333d2b36SAndroid Build Coastguard Worker 139*333d2b36SAndroid Build Coastguard Worker // Get or create the list of indexes of properties that are tagged with `android:"path"`. 140*333d2b36SAndroid Build Coastguard Worker pathPropertyIndexes := taggedPropertyIndexesForPropertyStruct(ps, tagValue) 141*333d2b36SAndroid Build Coastguard Worker 142*333d2b36SAndroid Build Coastguard Worker var ret []string 143*333d2b36SAndroid Build Coastguard Worker 144*333d2b36SAndroid Build Coastguard Worker for _, i := range pathPropertyIndexes { 145*333d2b36SAndroid Build Coastguard Worker var values []reflect.Value 146*333d2b36SAndroid Build Coastguard Worker fieldsByIndex(v, i, &values) 147*333d2b36SAndroid Build Coastguard Worker for _, sv := range values { 148*333d2b36SAndroid Build Coastguard Worker if !sv.IsValid() { 149*333d2b36SAndroid Build Coastguard Worker // Skip properties inside a nil pointer. 150*333d2b36SAndroid Build Coastguard Worker continue 151*333d2b36SAndroid Build Coastguard Worker } 152*333d2b36SAndroid Build Coastguard Worker 153*333d2b36SAndroid Build Coastguard Worker // If the field is a non-nil pointer step into it. 154*333d2b36SAndroid Build Coastguard Worker if sv.Kind() == reflect.Ptr { 155*333d2b36SAndroid Build Coastguard Worker if sv.IsNil() { 156*333d2b36SAndroid Build Coastguard Worker continue 157*333d2b36SAndroid Build Coastguard Worker } 158*333d2b36SAndroid Build Coastguard Worker sv = sv.Elem() 159*333d2b36SAndroid Build Coastguard Worker } 160*333d2b36SAndroid Build Coastguard Worker 161*333d2b36SAndroid Build Coastguard Worker // Collect paths from all strings and slices of strings. 162*333d2b36SAndroid Build Coastguard Worker switch sv.Kind() { 163*333d2b36SAndroid Build Coastguard Worker case reflect.String: 164*333d2b36SAndroid Build Coastguard Worker ret = append(ret, sv.String()) 165*333d2b36SAndroid Build Coastguard Worker case reflect.Slice: 166*333d2b36SAndroid Build Coastguard Worker ret = append(ret, sv.Interface().([]string)...) 167*333d2b36SAndroid Build Coastguard Worker case reflect.Struct: 168*333d2b36SAndroid Build Coastguard Worker intf := sv.Interface() 169*333d2b36SAndroid Build Coastguard Worker if configurable, ok := intf.(proptools.Configurable[string]); ok { 170*333d2b36SAndroid Build Coastguard Worker ret = append(ret, configurable.GetOrDefault(ctx, "")) 171*333d2b36SAndroid Build Coastguard Worker } else if configurable, ok := intf.(proptools.Configurable[[]string]); ok { 172*333d2b36SAndroid Build Coastguard Worker ret = append(ret, configurable.GetOrDefault(ctx, nil)...) 173*333d2b36SAndroid Build Coastguard Worker } else { 174*333d2b36SAndroid Build Coastguard Worker 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`, 175*333d2b36SAndroid Build Coastguard Worker v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) 176*333d2b36SAndroid Build Coastguard Worker } 177*333d2b36SAndroid Build Coastguard Worker default: 178*333d2b36SAndroid Build Coastguard Worker 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`, 179*333d2b36SAndroid Build Coastguard Worker v.Type().FieldByIndex(i).Name, v.Type(), sv.Type())) 180*333d2b36SAndroid Build Coastguard Worker } 181*333d2b36SAndroid Build Coastguard Worker } 182*333d2b36SAndroid Build Coastguard Worker } 183*333d2b36SAndroid Build Coastguard Worker 184*333d2b36SAndroid Build Coastguard Worker return ret 185*333d2b36SAndroid Build Coastguard Worker} 186*333d2b36SAndroid Build Coastguard Worker 187*333d2b36SAndroid Build Coastguard Worker// fieldsByIndex is similar to reflect.Value.FieldByIndex, but is more robust: it doesn't track 188*333d2b36SAndroid Build Coastguard Worker// nil pointers and it returns multiple values when there's slice of struct. 189*333d2b36SAndroid Build Coastguard Workerfunc fieldsByIndex(v reflect.Value, index []int, values *[]reflect.Value) { 190*333d2b36SAndroid Build Coastguard Worker // leaf case 191*333d2b36SAndroid Build Coastguard Worker if len(index) == 1 { 192*333d2b36SAndroid Build Coastguard Worker if isSliceOfStruct(v) { 193*333d2b36SAndroid Build Coastguard Worker for i := 0; i < v.Len(); i++ { 194*333d2b36SAndroid Build Coastguard Worker *values = append(*values, v.Index(i).Field(index[0])) 195*333d2b36SAndroid Build Coastguard Worker } 196*333d2b36SAndroid Build Coastguard Worker } else { 197*333d2b36SAndroid Build Coastguard Worker // Dereference it if it's a pointer. 198*333d2b36SAndroid Build Coastguard Worker if v.Kind() == reflect.Ptr { 199*333d2b36SAndroid Build Coastguard Worker if v.IsNil() { 200*333d2b36SAndroid Build Coastguard Worker return 201*333d2b36SAndroid Build Coastguard Worker } 202*333d2b36SAndroid Build Coastguard Worker v = v.Elem() 203*333d2b36SAndroid Build Coastguard Worker } 204*333d2b36SAndroid Build Coastguard Worker *values = append(*values, v.Field(index[0])) 205*333d2b36SAndroid Build Coastguard Worker } 206*333d2b36SAndroid Build Coastguard Worker return 207*333d2b36SAndroid Build Coastguard Worker } 208*333d2b36SAndroid Build Coastguard Worker 209*333d2b36SAndroid Build Coastguard Worker // recursion 210*333d2b36SAndroid Build Coastguard Worker if v.Kind() == reflect.Ptr { 211*333d2b36SAndroid Build Coastguard Worker // don't track nil pointer 212*333d2b36SAndroid Build Coastguard Worker if v.IsNil() { 213*333d2b36SAndroid Build Coastguard Worker return 214*333d2b36SAndroid Build Coastguard Worker } 215*333d2b36SAndroid Build Coastguard Worker v = v.Elem() 216*333d2b36SAndroid Build Coastguard Worker } else if isSliceOfStruct(v) { 217*333d2b36SAndroid Build Coastguard Worker // do the recursion for all elements 218*333d2b36SAndroid Build Coastguard Worker for i := 0; i < v.Len(); i++ { 219*333d2b36SAndroid Build Coastguard Worker fieldsByIndex(v.Index(i).Field(index[0]), index[1:], values) 220*333d2b36SAndroid Build Coastguard Worker } 221*333d2b36SAndroid Build Coastguard Worker return 222*333d2b36SAndroid Build Coastguard Worker } 223*333d2b36SAndroid Build Coastguard Worker fieldsByIndex(v.Field(index[0]), index[1:], values) 224*333d2b36SAndroid Build Coastguard Worker return 225*333d2b36SAndroid Build Coastguard Worker} 226*333d2b36SAndroid Build Coastguard Worker 227*333d2b36SAndroid Build Coastguard Workerfunc isSliceOfStruct(v reflect.Value) bool { 228*333d2b36SAndroid Build Coastguard Worker return v.Kind() == reflect.Slice && v.Type().Elem().Kind() == reflect.Struct 229*333d2b36SAndroid Build Coastguard Worker} 230*333d2b36SAndroid Build Coastguard Worker 231*333d2b36SAndroid Build Coastguard Workervar pathPropertyIndexesCache OncePer 232*333d2b36SAndroid Build Coastguard Worker 233*333d2b36SAndroid Build Coastguard Worker// taggedPropertyIndexesForPropertyStruct returns a list of all of the indexes of properties in 234*333d2b36SAndroid Build Coastguard Worker// property struct type that are tagged with `android:"tagValue"`. Each index is a []int suitable 235*333d2b36SAndroid Build Coastguard Worker// for passing to reflect.Value.FieldByIndex. The value is cached in a global cache by type and 236*333d2b36SAndroid Build Coastguard Worker// tagValue. 237*333d2b36SAndroid Build Coastguard Workerfunc taggedPropertyIndexesForPropertyStruct(ps interface{}, tagValue string) [][]int { 238*333d2b36SAndroid Build Coastguard Worker type pathPropertyIndexesOnceKey struct { 239*333d2b36SAndroid Build Coastguard Worker propStructType reflect.Type 240*333d2b36SAndroid Build Coastguard Worker tagValue string 241*333d2b36SAndroid Build Coastguard Worker } 242*333d2b36SAndroid Build Coastguard Worker key := NewCustomOnceKey(pathPropertyIndexesOnceKey{ 243*333d2b36SAndroid Build Coastguard Worker propStructType: reflect.TypeOf(ps), 244*333d2b36SAndroid Build Coastguard Worker tagValue: tagValue, 245*333d2b36SAndroid Build Coastguard Worker }) 246*333d2b36SAndroid Build Coastguard Worker return pathPropertyIndexesCache.Once(key, func() interface{} { 247*333d2b36SAndroid Build Coastguard Worker return proptools.PropertyIndexesWithTag(ps, "android", tagValue) 248*333d2b36SAndroid Build Coastguard Worker }).([][]int) 249*333d2b36SAndroid Build Coastguard Worker} 250