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