xref: /aosp_15_r20/build/soong/sdk/build_release.go (revision 333d2b3687b3a337dbcca9d65000bca186795e39)
1*333d2b36SAndroid Build Coastguard Worker// Copyright (C) 2021 The Android Open Source Project
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 sdk
16*333d2b36SAndroid Build Coastguard Worker
17*333d2b36SAndroid Build Coastguard Workerimport (
18*333d2b36SAndroid Build Coastguard Worker	"fmt"
19*333d2b36SAndroid Build Coastguard Worker	"math"
20*333d2b36SAndroid Build Coastguard Worker	"reflect"
21*333d2b36SAndroid Build Coastguard Worker	"strings"
22*333d2b36SAndroid Build Coastguard Worker)
23*333d2b36SAndroid Build Coastguard Worker
24*333d2b36SAndroid Build Coastguard Worker// Supports customizing sdk snapshot output based on target build release.
25*333d2b36SAndroid Build Coastguard Worker
26*333d2b36SAndroid Build Coastguard Worker// buildRelease represents the version of a build system used to create a specific release.
27*333d2b36SAndroid Build Coastguard Worker//
28*333d2b36SAndroid Build Coastguard Worker// The name of the release, is the same as the code for the dessert release, e.g. S, Tiramisu, etc.
29*333d2b36SAndroid Build Coastguard Workertype buildRelease struct {
30*333d2b36SAndroid Build Coastguard Worker	// The name of the release, e.g. S, Tiramisu, etc.
31*333d2b36SAndroid Build Coastguard Worker	name string
32*333d2b36SAndroid Build Coastguard Worker
33*333d2b36SAndroid Build Coastguard Worker	// The index of this structure within the dessertBuildReleases list.
34*333d2b36SAndroid Build Coastguard Worker	//
35*333d2b36SAndroid Build Coastguard Worker	// The buildReleaseCurrent does not appear in the dessertBuildReleases list as it has an ordinal value
36*333d2b36SAndroid Build Coastguard Worker	// that is larger than the size of the dessertBuildReleases.
37*333d2b36SAndroid Build Coastguard Worker	ordinal int
38*333d2b36SAndroid Build Coastguard Worker}
39*333d2b36SAndroid Build Coastguard Worker
40*333d2b36SAndroid Build Coastguard Workerfunc (br *buildRelease) EarlierThan(other *buildRelease) bool {
41*333d2b36SAndroid Build Coastguard Worker	return br.ordinal < other.ordinal
42*333d2b36SAndroid Build Coastguard Worker}
43*333d2b36SAndroid Build Coastguard Worker
44*333d2b36SAndroid Build Coastguard Worker// String returns the name of the build release.
45*333d2b36SAndroid Build Coastguard Workerfunc (br *buildRelease) String() string {
46*333d2b36SAndroid Build Coastguard Worker	return br.name
47*333d2b36SAndroid Build Coastguard Worker}
48*333d2b36SAndroid Build Coastguard Worker
49*333d2b36SAndroid Build Coastguard Worker// buildReleaseSet represents a set of buildRelease objects.
50*333d2b36SAndroid Build Coastguard Workertype buildReleaseSet struct {
51*333d2b36SAndroid Build Coastguard Worker	// Set of *buildRelease represented as a map from *buildRelease to struct{}.
52*333d2b36SAndroid Build Coastguard Worker	contents map[*buildRelease]struct{}
53*333d2b36SAndroid Build Coastguard Worker}
54*333d2b36SAndroid Build Coastguard Worker
55*333d2b36SAndroid Build Coastguard Worker// addItem adds a build release to the set.
56*333d2b36SAndroid Build Coastguard Workerfunc (s *buildReleaseSet) addItem(release *buildRelease) {
57*333d2b36SAndroid Build Coastguard Worker	s.contents[release] = struct{}{}
58*333d2b36SAndroid Build Coastguard Worker}
59*333d2b36SAndroid Build Coastguard Worker
60*333d2b36SAndroid Build Coastguard Worker// addRange adds all the build releases from start (inclusive) to end (inclusive).
61*333d2b36SAndroid Build Coastguard Workerfunc (s *buildReleaseSet) addRange(start *buildRelease, end *buildRelease) {
62*333d2b36SAndroid Build Coastguard Worker	for i := start.ordinal; i <= end.ordinal; i += 1 {
63*333d2b36SAndroid Build Coastguard Worker		s.addItem(dessertBuildReleases[i])
64*333d2b36SAndroid Build Coastguard Worker	}
65*333d2b36SAndroid Build Coastguard Worker}
66*333d2b36SAndroid Build Coastguard Worker
67*333d2b36SAndroid Build Coastguard Worker// contains returns true if the set contains the specified build release.
68*333d2b36SAndroid Build Coastguard Workerfunc (s *buildReleaseSet) contains(release *buildRelease) bool {
69*333d2b36SAndroid Build Coastguard Worker	_, ok := s.contents[release]
70*333d2b36SAndroid Build Coastguard Worker	return ok
71*333d2b36SAndroid Build Coastguard Worker}
72*333d2b36SAndroid Build Coastguard Worker
73*333d2b36SAndroid Build Coastguard Worker// String returns a string representation of the set, sorted from earliest to latest release.
74*333d2b36SAndroid Build Coastguard Workerfunc (s *buildReleaseSet) String() string {
75*333d2b36SAndroid Build Coastguard Worker	list := []string{}
76*333d2b36SAndroid Build Coastguard Worker	addRelease := func(release *buildRelease) {
77*333d2b36SAndroid Build Coastguard Worker		if _, ok := s.contents[release]; ok {
78*333d2b36SAndroid Build Coastguard Worker			list = append(list, release.name)
79*333d2b36SAndroid Build Coastguard Worker		}
80*333d2b36SAndroid Build Coastguard Worker	}
81*333d2b36SAndroid Build Coastguard Worker	// Add the names of the build releases in this set in the order in which they were created.
82*333d2b36SAndroid Build Coastguard Worker	for _, release := range dessertBuildReleases {
83*333d2b36SAndroid Build Coastguard Worker		addRelease(release)
84*333d2b36SAndroid Build Coastguard Worker	}
85*333d2b36SAndroid Build Coastguard Worker	// Always add "current" to the list of names last if it is present in the set.
86*333d2b36SAndroid Build Coastguard Worker	addRelease(buildReleaseCurrent)
87*333d2b36SAndroid Build Coastguard Worker	return fmt.Sprintf("[%s]", strings.Join(list, ","))
88*333d2b36SAndroid Build Coastguard Worker}
89*333d2b36SAndroid Build Coastguard Worker
90*333d2b36SAndroid Build Coastguard Workervar (
91*333d2b36SAndroid Build Coastguard Worker	// nameToBuildRelease contains a map from name to build release.
92*333d2b36SAndroid Build Coastguard Worker	nameToBuildRelease = map[string]*buildRelease{}
93*333d2b36SAndroid Build Coastguard Worker
94*333d2b36SAndroid Build Coastguard Worker	// dessertBuildReleases lists all the available dessert build releases, i.e. excluding current.
95*333d2b36SAndroid Build Coastguard Worker	dessertBuildReleases = []*buildRelease{}
96*333d2b36SAndroid Build Coastguard Worker
97*333d2b36SAndroid Build Coastguard Worker	// allBuildReleaseSet is the set of all build releases.
98*333d2b36SAndroid Build Coastguard Worker	allBuildReleaseSet = &buildReleaseSet{contents: map[*buildRelease]struct{}{}}
99*333d2b36SAndroid Build Coastguard Worker
100*333d2b36SAndroid Build Coastguard Worker	// Add the dessert build releases from oldest to newest.
101*333d2b36SAndroid Build Coastguard Worker	buildReleaseS = initBuildRelease("S")
102*333d2b36SAndroid Build Coastguard Worker	buildReleaseT = initBuildRelease("Tiramisu")
103*333d2b36SAndroid Build Coastguard Worker	buildReleaseU = initBuildRelease("UpsideDownCake")
104*333d2b36SAndroid Build Coastguard Worker
105*333d2b36SAndroid Build Coastguard Worker	// Add the current build release which is always treated as being more recent than any other
106*333d2b36SAndroid Build Coastguard Worker	// build release, including those added in tests.
107*333d2b36SAndroid Build Coastguard Worker	buildReleaseCurrent = initBuildRelease("current")
108*333d2b36SAndroid Build Coastguard Worker)
109*333d2b36SAndroid Build Coastguard Worker
110*333d2b36SAndroid Build Coastguard Worker// initBuildRelease creates a new build release with the specified name.
111*333d2b36SAndroid Build Coastguard Workerfunc initBuildRelease(name string) *buildRelease {
112*333d2b36SAndroid Build Coastguard Worker	ordinal := len(dessertBuildReleases)
113*333d2b36SAndroid Build Coastguard Worker	if name == "current" {
114*333d2b36SAndroid Build Coastguard Worker		// The current build release is more recent than all other build releases, including those
115*333d2b36SAndroid Build Coastguard Worker		// created in tests so use the max int value. It cannot just rely on being created after all
116*333d2b36SAndroid Build Coastguard Worker		// the other build releases as some are created in tests which run after the current build
117*333d2b36SAndroid Build Coastguard Worker		// release has been created.
118*333d2b36SAndroid Build Coastguard Worker		ordinal = math.MaxInt
119*333d2b36SAndroid Build Coastguard Worker	}
120*333d2b36SAndroid Build Coastguard Worker	release := &buildRelease{name: name, ordinal: ordinal}
121*333d2b36SAndroid Build Coastguard Worker	nameToBuildRelease[name] = release
122*333d2b36SAndroid Build Coastguard Worker	allBuildReleaseSet.addItem(release)
123*333d2b36SAndroid Build Coastguard Worker	if name != "current" {
124*333d2b36SAndroid Build Coastguard Worker		// As the current build release has an ordinal value that does not correspond to its position
125*333d2b36SAndroid Build Coastguard Worker		// in the dessertBuildReleases list do not add it to the list.
126*333d2b36SAndroid Build Coastguard Worker		dessertBuildReleases = append(dessertBuildReleases, release)
127*333d2b36SAndroid Build Coastguard Worker	}
128*333d2b36SAndroid Build Coastguard Worker	return release
129*333d2b36SAndroid Build Coastguard Worker}
130*333d2b36SAndroid Build Coastguard Worker
131*333d2b36SAndroid Build Coastguard Worker// latestDessertBuildRelease returns the latest dessert release build name, i.e. the last dessert
132*333d2b36SAndroid Build Coastguard Worker// release added to the list, which does not include current.
133*333d2b36SAndroid Build Coastguard Workerfunc latestDessertBuildRelease() *buildRelease {
134*333d2b36SAndroid Build Coastguard Worker	return dessertBuildReleases[len(dessertBuildReleases)-1]
135*333d2b36SAndroid Build Coastguard Worker}
136*333d2b36SAndroid Build Coastguard Worker
137*333d2b36SAndroid Build Coastguard Worker// nameToRelease maps from build release name to the corresponding build release (if it exists) or
138*333d2b36SAndroid Build Coastguard Worker// the error if it does not.
139*333d2b36SAndroid Build Coastguard Workerfunc nameToRelease(name string) (*buildRelease, error) {
140*333d2b36SAndroid Build Coastguard Worker	if r, ok := nameToBuildRelease[name]; ok {
141*333d2b36SAndroid Build Coastguard Worker		return r, nil
142*333d2b36SAndroid Build Coastguard Worker	}
143*333d2b36SAndroid Build Coastguard Worker
144*333d2b36SAndroid Build Coastguard Worker	return nil, fmt.Errorf("unknown release %q, expected one of %s", name, allBuildReleaseSet)
145*333d2b36SAndroid Build Coastguard Worker}
146*333d2b36SAndroid Build Coastguard Worker
147*333d2b36SAndroid Build Coastguard Worker// parseBuildReleaseSet parses a build release set string specification into a build release set.
148*333d2b36SAndroid Build Coastguard Worker//
149*333d2b36SAndroid Build Coastguard Worker// The specification consists of one of the following:
150*333d2b36SAndroid Build Coastguard Worker// * a single build release name, e.g. S, T, etc.
151*333d2b36SAndroid Build Coastguard Worker// * a closed range (inclusive to inclusive), e.g. S-T
152*333d2b36SAndroid Build Coastguard Worker// * an open range, e.g. T+.
153*333d2b36SAndroid Build Coastguard Worker//
154*333d2b36SAndroid Build Coastguard Worker// This returns the set if the specification was valid or an error.
155*333d2b36SAndroid Build Coastguard Workerfunc parseBuildReleaseSet(specification string) (*buildReleaseSet, error) {
156*333d2b36SAndroid Build Coastguard Worker	set := &buildReleaseSet{contents: map[*buildRelease]struct{}{}}
157*333d2b36SAndroid Build Coastguard Worker
158*333d2b36SAndroid Build Coastguard Worker	if strings.HasSuffix(specification, "+") {
159*333d2b36SAndroid Build Coastguard Worker		rangeStart := strings.TrimSuffix(specification, "+")
160*333d2b36SAndroid Build Coastguard Worker		start, err := nameToRelease(rangeStart)
161*333d2b36SAndroid Build Coastguard Worker		if err != nil {
162*333d2b36SAndroid Build Coastguard Worker			return nil, err
163*333d2b36SAndroid Build Coastguard Worker		}
164*333d2b36SAndroid Build Coastguard Worker		end := latestDessertBuildRelease()
165*333d2b36SAndroid Build Coastguard Worker		set.addRange(start, end)
166*333d2b36SAndroid Build Coastguard Worker		// An open-ended range always includes the current release.
167*333d2b36SAndroid Build Coastguard Worker		set.addItem(buildReleaseCurrent)
168*333d2b36SAndroid Build Coastguard Worker	} else if strings.Contains(specification, "-") {
169*333d2b36SAndroid Build Coastguard Worker		limits := strings.SplitN(specification, "-", 2)
170*333d2b36SAndroid Build Coastguard Worker		start, err := nameToRelease(limits[0])
171*333d2b36SAndroid Build Coastguard Worker		if err != nil {
172*333d2b36SAndroid Build Coastguard Worker			return nil, err
173*333d2b36SAndroid Build Coastguard Worker		}
174*333d2b36SAndroid Build Coastguard Worker
175*333d2b36SAndroid Build Coastguard Worker		end, err := nameToRelease(limits[1])
176*333d2b36SAndroid Build Coastguard Worker		if err != nil {
177*333d2b36SAndroid Build Coastguard Worker			return nil, err
178*333d2b36SAndroid Build Coastguard Worker		}
179*333d2b36SAndroid Build Coastguard Worker
180*333d2b36SAndroid Build Coastguard Worker		if start.ordinal > end.ordinal {
181*333d2b36SAndroid Build Coastguard Worker			return nil, fmt.Errorf("invalid closed range, start release %q is later than end release %q", start.name, end.name)
182*333d2b36SAndroid Build Coastguard Worker		}
183*333d2b36SAndroid Build Coastguard Worker
184*333d2b36SAndroid Build Coastguard Worker		set.addRange(start, end)
185*333d2b36SAndroid Build Coastguard Worker	} else {
186*333d2b36SAndroid Build Coastguard Worker		release, err := nameToRelease(specification)
187*333d2b36SAndroid Build Coastguard Worker		if err != nil {
188*333d2b36SAndroid Build Coastguard Worker			return nil, err
189*333d2b36SAndroid Build Coastguard Worker		}
190*333d2b36SAndroid Build Coastguard Worker		set.addItem(release)
191*333d2b36SAndroid Build Coastguard Worker	}
192*333d2b36SAndroid Build Coastguard Worker
193*333d2b36SAndroid Build Coastguard Worker	return set, nil
194*333d2b36SAndroid Build Coastguard Worker}
195*333d2b36SAndroid Build Coastguard Worker
196*333d2b36SAndroid Build Coastguard Worker// Given a set of properties (struct value), set the value of a field within that struct (or one of
197*333d2b36SAndroid Build Coastguard Worker// its embedded structs) to its zero value.
198*333d2b36SAndroid Build Coastguard Workertype fieldPrunerFunc func(structValue reflect.Value)
199*333d2b36SAndroid Build Coastguard Worker
200*333d2b36SAndroid Build Coastguard Worker// A property that can be cleared by a propertyPruner.
201*333d2b36SAndroid Build Coastguard Workertype prunerProperty struct {
202*333d2b36SAndroid Build Coastguard Worker	// The name of the field for this property. It is a "."-separated path for fields in non-anonymous
203*333d2b36SAndroid Build Coastguard Worker	// sub-structs.
204*333d2b36SAndroid Build Coastguard Worker	name string
205*333d2b36SAndroid Build Coastguard Worker
206*333d2b36SAndroid Build Coastguard Worker	// Sets the associated field to its zero value.
207*333d2b36SAndroid Build Coastguard Worker	prunerFunc fieldPrunerFunc
208*333d2b36SAndroid Build Coastguard Worker}
209*333d2b36SAndroid Build Coastguard Worker
210*333d2b36SAndroid Build Coastguard Worker// propertyPruner provides support for pruning (i.e. setting to their zero value) properties from
211*333d2b36SAndroid Build Coastguard Worker// a properties structure.
212*333d2b36SAndroid Build Coastguard Workertype propertyPruner struct {
213*333d2b36SAndroid Build Coastguard Worker	// The properties that the pruner will clear.
214*333d2b36SAndroid Build Coastguard Worker	properties []prunerProperty
215*333d2b36SAndroid Build Coastguard Worker}
216*333d2b36SAndroid Build Coastguard Worker
217*333d2b36SAndroid Build Coastguard Worker// gatherFields recursively processes the supplied structure and a nested structures, selecting the
218*333d2b36SAndroid Build Coastguard Worker// fields that require pruning and populates the propertyPruner.properties with the information
219*333d2b36SAndroid Build Coastguard Worker// needed to prune those fields.
220*333d2b36SAndroid Build Coastguard Worker//
221*333d2b36SAndroid Build Coastguard Worker// containingStructAccessor is a func that if given an object will return a field whose value is
222*333d2b36SAndroid Build Coastguard Worker// of the supplied structType. It is nil on initial entry to this method but when this method is
223*333d2b36SAndroid Build Coastguard Worker// called recursively on a field that is a nested structure containingStructAccessor is set to a
224*333d2b36SAndroid Build Coastguard Worker// func that provides access to the field's value.
225*333d2b36SAndroid Build Coastguard Worker//
226*333d2b36SAndroid Build Coastguard Worker// namePrefix is the prefix to the fields that are being visited. It is "" on initial entry to this
227*333d2b36SAndroid Build Coastguard Worker// method but when this method is called recursively on a field that is a nested structure
228*333d2b36SAndroid Build Coastguard Worker// namePrefix is the result of appending the field name (plus a ".") to the previous name prefix.
229*333d2b36SAndroid Build Coastguard Worker// Unless the field is anonymous in which case it is passed through unchanged.
230*333d2b36SAndroid Build Coastguard Worker//
231*333d2b36SAndroid Build Coastguard Worker// selector is a func that will select whether the supplied field requires pruning or not. If it
232*333d2b36SAndroid Build Coastguard Worker// returns true then the field will be added to those to be pruned, otherwise it will not.
233*333d2b36SAndroid Build Coastguard Workerfunc (p *propertyPruner) gatherFields(structType reflect.Type, containingStructAccessor fieldAccessorFunc, namePrefix string, selector fieldSelectorFunc) {
234*333d2b36SAndroid Build Coastguard Worker	for f := 0; f < structType.NumField(); f++ {
235*333d2b36SAndroid Build Coastguard Worker		field := structType.Field(f)
236*333d2b36SAndroid Build Coastguard Worker		if field.PkgPath != "" {
237*333d2b36SAndroid Build Coastguard Worker			// Ignore unexported fields.
238*333d2b36SAndroid Build Coastguard Worker			continue
239*333d2b36SAndroid Build Coastguard Worker		}
240*333d2b36SAndroid Build Coastguard Worker
241*333d2b36SAndroid Build Coastguard Worker		// Save a copy of the field index for use in the function.
242*333d2b36SAndroid Build Coastguard Worker		fieldIndex := f
243*333d2b36SAndroid Build Coastguard Worker
244*333d2b36SAndroid Build Coastguard Worker		name := namePrefix + field.Name
245*333d2b36SAndroid Build Coastguard Worker
246*333d2b36SAndroid Build Coastguard Worker		fieldGetter := func(container reflect.Value) reflect.Value {
247*333d2b36SAndroid Build Coastguard Worker			if containingStructAccessor != nil {
248*333d2b36SAndroid Build Coastguard Worker				// This is an embedded structure so first access the field for the embedded
249*333d2b36SAndroid Build Coastguard Worker				// structure.
250*333d2b36SAndroid Build Coastguard Worker				container = containingStructAccessor(container)
251*333d2b36SAndroid Build Coastguard Worker			}
252*333d2b36SAndroid Build Coastguard Worker
253*333d2b36SAndroid Build Coastguard Worker			// Skip through interface and pointer values to find the structure.
254*333d2b36SAndroid Build Coastguard Worker			container = getStructValue(container)
255*333d2b36SAndroid Build Coastguard Worker
256*333d2b36SAndroid Build Coastguard Worker			defer func() {
257*333d2b36SAndroid Build Coastguard Worker				if r := recover(); r != nil {
258*333d2b36SAndroid Build Coastguard Worker					panic(fmt.Errorf("%s for fieldIndex %d of field %s of container %#v", r, fieldIndex, name, container.Interface()))
259*333d2b36SAndroid Build Coastguard Worker				}
260*333d2b36SAndroid Build Coastguard Worker			}()
261*333d2b36SAndroid Build Coastguard Worker
262*333d2b36SAndroid Build Coastguard Worker			// Return the field.
263*333d2b36SAndroid Build Coastguard Worker			return container.Field(fieldIndex)
264*333d2b36SAndroid Build Coastguard Worker		}
265*333d2b36SAndroid Build Coastguard Worker
266*333d2b36SAndroid Build Coastguard Worker		fieldType := field.Type
267*333d2b36SAndroid Build Coastguard Worker		if selector(name, field) {
268*333d2b36SAndroid Build Coastguard Worker			zeroValue := reflect.Zero(fieldType)
269*333d2b36SAndroid Build Coastguard Worker			fieldPruner := func(container reflect.Value) {
270*333d2b36SAndroid Build Coastguard Worker				if containingStructAccessor != nil {
271*333d2b36SAndroid Build Coastguard Worker					// This is an embedded structure so first access the field for the embedded
272*333d2b36SAndroid Build Coastguard Worker					// structure.
273*333d2b36SAndroid Build Coastguard Worker					container = containingStructAccessor(container)
274*333d2b36SAndroid Build Coastguard Worker				}
275*333d2b36SAndroid Build Coastguard Worker
276*333d2b36SAndroid Build Coastguard Worker				// Skip through interface and pointer values to find the structure.
277*333d2b36SAndroid Build Coastguard Worker				container = getStructValue(container)
278*333d2b36SAndroid Build Coastguard Worker
279*333d2b36SAndroid Build Coastguard Worker				defer func() {
280*333d2b36SAndroid Build Coastguard Worker					if r := recover(); r != nil {
281*333d2b36SAndroid Build Coastguard Worker						panic(fmt.Errorf("%s\n\tfor field (index %d, name %s)", r, fieldIndex, name))
282*333d2b36SAndroid Build Coastguard Worker					}
283*333d2b36SAndroid Build Coastguard Worker				}()
284*333d2b36SAndroid Build Coastguard Worker
285*333d2b36SAndroid Build Coastguard Worker				// Set the field.
286*333d2b36SAndroid Build Coastguard Worker				container.Field(fieldIndex).Set(zeroValue)
287*333d2b36SAndroid Build Coastguard Worker			}
288*333d2b36SAndroid Build Coastguard Worker
289*333d2b36SAndroid Build Coastguard Worker			property := prunerProperty{
290*333d2b36SAndroid Build Coastguard Worker				name,
291*333d2b36SAndroid Build Coastguard Worker				fieldPruner,
292*333d2b36SAndroid Build Coastguard Worker			}
293*333d2b36SAndroid Build Coastguard Worker			p.properties = append(p.properties, property)
294*333d2b36SAndroid Build Coastguard Worker		} else {
295*333d2b36SAndroid Build Coastguard Worker			switch fieldType.Kind() {
296*333d2b36SAndroid Build Coastguard Worker			case reflect.Struct:
297*333d2b36SAndroid Build Coastguard Worker				// Gather fields from the nested or embedded structure.
298*333d2b36SAndroid Build Coastguard Worker				var subNamePrefix string
299*333d2b36SAndroid Build Coastguard Worker				if field.Anonymous {
300*333d2b36SAndroid Build Coastguard Worker					subNamePrefix = namePrefix
301*333d2b36SAndroid Build Coastguard Worker				} else {
302*333d2b36SAndroid Build Coastguard Worker					subNamePrefix = name + "."
303*333d2b36SAndroid Build Coastguard Worker				}
304*333d2b36SAndroid Build Coastguard Worker				p.gatherFields(fieldType, fieldGetter, subNamePrefix, selector)
305*333d2b36SAndroid Build Coastguard Worker
306*333d2b36SAndroid Build Coastguard Worker			case reflect.Map:
307*333d2b36SAndroid Build Coastguard Worker				// Get the type of the values stored in the map.
308*333d2b36SAndroid Build Coastguard Worker				valueType := fieldType.Elem()
309*333d2b36SAndroid Build Coastguard Worker				// Skip over * types.
310*333d2b36SAndroid Build Coastguard Worker				if valueType.Kind() == reflect.Ptr {
311*333d2b36SAndroid Build Coastguard Worker					valueType = valueType.Elem()
312*333d2b36SAndroid Build Coastguard Worker				}
313*333d2b36SAndroid Build Coastguard Worker				if valueType.Kind() == reflect.Struct {
314*333d2b36SAndroid Build Coastguard Worker					// If this is not referenced by a pointer then it is an error as it is impossible to
315*333d2b36SAndroid Build Coastguard Worker					// modify a struct that is stored directly as a value in a map.
316*333d2b36SAndroid Build Coastguard Worker					if fieldType.Elem().Kind() != reflect.Ptr {
317*333d2b36SAndroid Build Coastguard Worker						panic(fmt.Errorf("Cannot prune struct %s stored by value in map %s, map values must"+
318*333d2b36SAndroid Build Coastguard Worker							" be pointers to structs",
319*333d2b36SAndroid Build Coastguard Worker							fieldType.Elem(), name))
320*333d2b36SAndroid Build Coastguard Worker					}
321*333d2b36SAndroid Build Coastguard Worker
322*333d2b36SAndroid Build Coastguard Worker					// Create a new pruner for the values of the map.
323*333d2b36SAndroid Build Coastguard Worker					valuePruner := newPropertyPrunerForStructType(valueType, selector)
324*333d2b36SAndroid Build Coastguard Worker
325*333d2b36SAndroid Build Coastguard Worker					// Create a new fieldPruner that will iterate over all the items in the map and call the
326*333d2b36SAndroid Build Coastguard Worker					// pruner on them.
327*333d2b36SAndroid Build Coastguard Worker					fieldPruner := func(container reflect.Value) {
328*333d2b36SAndroid Build Coastguard Worker						mapValue := fieldGetter(container)
329*333d2b36SAndroid Build Coastguard Worker
330*333d2b36SAndroid Build Coastguard Worker						for _, keyValue := range mapValue.MapKeys() {
331*333d2b36SAndroid Build Coastguard Worker							itemValue := mapValue.MapIndex(keyValue)
332*333d2b36SAndroid Build Coastguard Worker
333*333d2b36SAndroid Build Coastguard Worker							defer func() {
334*333d2b36SAndroid Build Coastguard Worker								if r := recover(); r != nil {
335*333d2b36SAndroid Build Coastguard Worker									panic(fmt.Errorf("%s\n\tfor key %q", r, keyValue))
336*333d2b36SAndroid Build Coastguard Worker								}
337*333d2b36SAndroid Build Coastguard Worker							}()
338*333d2b36SAndroid Build Coastguard Worker
339*333d2b36SAndroid Build Coastguard Worker							valuePruner.pruneProperties(itemValue.Interface())
340*333d2b36SAndroid Build Coastguard Worker						}
341*333d2b36SAndroid Build Coastguard Worker					}
342*333d2b36SAndroid Build Coastguard Worker
343*333d2b36SAndroid Build Coastguard Worker					// Add the map field pruner to the list of property pruners.
344*333d2b36SAndroid Build Coastguard Worker					property := prunerProperty{
345*333d2b36SAndroid Build Coastguard Worker						name + "[*]",
346*333d2b36SAndroid Build Coastguard Worker						fieldPruner,
347*333d2b36SAndroid Build Coastguard Worker					}
348*333d2b36SAndroid Build Coastguard Worker					p.properties = append(p.properties, property)
349*333d2b36SAndroid Build Coastguard Worker				}
350*333d2b36SAndroid Build Coastguard Worker			}
351*333d2b36SAndroid Build Coastguard Worker		}
352*333d2b36SAndroid Build Coastguard Worker	}
353*333d2b36SAndroid Build Coastguard Worker}
354*333d2b36SAndroid Build Coastguard Worker
355*333d2b36SAndroid Build Coastguard Worker// pruneProperties will prune (set to zero value) any properties in the struct referenced by the
356*333d2b36SAndroid Build Coastguard Worker// supplied struct pointer.
357*333d2b36SAndroid Build Coastguard Worker//
358*333d2b36SAndroid Build Coastguard Worker// The struct must be of the same type as was originally passed to newPropertyPruner to create this
359*333d2b36SAndroid Build Coastguard Worker// propertyPruner.
360*333d2b36SAndroid Build Coastguard Workerfunc (p *propertyPruner) pruneProperties(propertiesStruct interface{}) {
361*333d2b36SAndroid Build Coastguard Worker
362*333d2b36SAndroid Build Coastguard Worker	defer func() {
363*333d2b36SAndroid Build Coastguard Worker		if r := recover(); r != nil {
364*333d2b36SAndroid Build Coastguard Worker			panic(fmt.Errorf("%s\n\tof container %#v", r, propertiesStruct))
365*333d2b36SAndroid Build Coastguard Worker		}
366*333d2b36SAndroid Build Coastguard Worker	}()
367*333d2b36SAndroid Build Coastguard Worker
368*333d2b36SAndroid Build Coastguard Worker	structValue := reflect.ValueOf(propertiesStruct)
369*333d2b36SAndroid Build Coastguard Worker	for _, property := range p.properties {
370*333d2b36SAndroid Build Coastguard Worker		property.prunerFunc(structValue)
371*333d2b36SAndroid Build Coastguard Worker	}
372*333d2b36SAndroid Build Coastguard Worker}
373*333d2b36SAndroid Build Coastguard Worker
374*333d2b36SAndroid Build Coastguard Worker// fieldSelectorFunc is called to select whether a specific field should be pruned or not.
375*333d2b36SAndroid Build Coastguard Worker// name is the name of the field, including any prefixes from containing str
376*333d2b36SAndroid Build Coastguard Workertype fieldSelectorFunc func(name string, field reflect.StructField) bool
377*333d2b36SAndroid Build Coastguard Worker
378*333d2b36SAndroid Build Coastguard Worker// newPropertyPruner creates a new property pruner for the structure type for the supplied
379*333d2b36SAndroid Build Coastguard Worker// properties struct.
380*333d2b36SAndroid Build Coastguard Worker//
381*333d2b36SAndroid Build Coastguard Worker// The returned pruner can be used on any properties structure of the same type as the supplied set
382*333d2b36SAndroid Build Coastguard Worker// of properties.
383*333d2b36SAndroid Build Coastguard Workerfunc newPropertyPruner(propertiesStruct interface{}, selector fieldSelectorFunc) *propertyPruner {
384*333d2b36SAndroid Build Coastguard Worker	structType := getStructValue(reflect.ValueOf(propertiesStruct)).Type()
385*333d2b36SAndroid Build Coastguard Worker	return newPropertyPrunerForStructType(structType, selector)
386*333d2b36SAndroid Build Coastguard Worker}
387*333d2b36SAndroid Build Coastguard Worker
388*333d2b36SAndroid Build Coastguard Worker// newPropertyPruner creates a new property pruner for the supplied properties struct type.
389*333d2b36SAndroid Build Coastguard Worker//
390*333d2b36SAndroid Build Coastguard Worker// The returned pruner can be used on any properties structure of the supplied type.
391*333d2b36SAndroid Build Coastguard Workerfunc newPropertyPrunerForStructType(structType reflect.Type, selector fieldSelectorFunc) *propertyPruner {
392*333d2b36SAndroid Build Coastguard Worker	pruner := &propertyPruner{}
393*333d2b36SAndroid Build Coastguard Worker	pruner.gatherFields(structType, nil, "", selector)
394*333d2b36SAndroid Build Coastguard Worker	return pruner
395*333d2b36SAndroid Build Coastguard Worker}
396*333d2b36SAndroid Build Coastguard Worker
397*333d2b36SAndroid Build Coastguard Worker// newPropertyPrunerByBuildRelease creates a property pruner that will clear any properties in the
398*333d2b36SAndroid Build Coastguard Worker// structure which are not supported by the specified target build release.
399*333d2b36SAndroid Build Coastguard Worker//
400*333d2b36SAndroid Build Coastguard Worker// A property is pruned if its field has a tag of the form:
401*333d2b36SAndroid Build Coastguard Worker//
402*333d2b36SAndroid Build Coastguard Worker//	`supported_build_releases:"<build-release-set>"`
403*333d2b36SAndroid Build Coastguard Worker//
404*333d2b36SAndroid Build Coastguard Worker// and the resulting build release set does not contain the target build release. Properties that
405*333d2b36SAndroid Build Coastguard Worker// have no such tag are assumed to be supported by all releases.
406*333d2b36SAndroid Build Coastguard Workerfunc newPropertyPrunerByBuildRelease(propertiesStruct interface{}, targetBuildRelease *buildRelease) *propertyPruner {
407*333d2b36SAndroid Build Coastguard Worker	return newPropertyPruner(propertiesStruct, func(name string, field reflect.StructField) bool {
408*333d2b36SAndroid Build Coastguard Worker		if supportedBuildReleases, ok := field.Tag.Lookup("supported_build_releases"); ok {
409*333d2b36SAndroid Build Coastguard Worker			set, err := parseBuildReleaseSet(supportedBuildReleases)
410*333d2b36SAndroid Build Coastguard Worker			if err != nil {
411*333d2b36SAndroid Build Coastguard Worker				panic(fmt.Errorf("invalid `supported_build_releases` tag on %s of %T: %s", name, propertiesStruct, err))
412*333d2b36SAndroid Build Coastguard Worker			}
413*333d2b36SAndroid Build Coastguard Worker
414*333d2b36SAndroid Build Coastguard Worker			// If the field does not support tha target release then prune it.
415*333d2b36SAndroid Build Coastguard Worker			return !set.contains(targetBuildRelease)
416*333d2b36SAndroid Build Coastguard Worker
417*333d2b36SAndroid Build Coastguard Worker		} else {
418*333d2b36SAndroid Build Coastguard Worker			// Any untagged fields are assumed to be supported by all build releases so should never be
419*333d2b36SAndroid Build Coastguard Worker			// pruned.
420*333d2b36SAndroid Build Coastguard Worker			return false
421*333d2b36SAndroid Build Coastguard Worker		}
422*333d2b36SAndroid Build Coastguard Worker	})
423*333d2b36SAndroid Build Coastguard Worker}
424