xref: /aosp_15_r20/build/soong/android/path_properties.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
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