xref: /aosp_15_r20/build/blueprint/bootstrap/bpdoc/bpdoc.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1*1fa6dee9SAndroid Build Coastguard Workerpackage bpdoc
2*1fa6dee9SAndroid Build Coastguard Worker
3*1fa6dee9SAndroid Build Coastguard Workerimport (
4*1fa6dee9SAndroid Build Coastguard Worker	"fmt"
5*1fa6dee9SAndroid Build Coastguard Worker	"html/template"
6*1fa6dee9SAndroid Build Coastguard Worker	"reflect"
7*1fa6dee9SAndroid Build Coastguard Worker	"sort"
8*1fa6dee9SAndroid Build Coastguard Worker	"strings"
9*1fa6dee9SAndroid Build Coastguard Worker
10*1fa6dee9SAndroid Build Coastguard Worker	"github.com/google/blueprint/proptools"
11*1fa6dee9SAndroid Build Coastguard Worker)
12*1fa6dee9SAndroid Build Coastguard Worker
13*1fa6dee9SAndroid Build Coastguard Worker// Package contains the information about a package relevant to generating documentation.
14*1fa6dee9SAndroid Build Coastguard Workertype Package struct {
15*1fa6dee9SAndroid Build Coastguard Worker	// Name is the name of the package.
16*1fa6dee9SAndroid Build Coastguard Worker	Name string
17*1fa6dee9SAndroid Build Coastguard Worker
18*1fa6dee9SAndroid Build Coastguard Worker	// Path is the full package path of the package as used in the primary builder.
19*1fa6dee9SAndroid Build Coastguard Worker	Path string
20*1fa6dee9SAndroid Build Coastguard Worker
21*1fa6dee9SAndroid Build Coastguard Worker	// Text is the contents of the package comment documenting the module types in the package.
22*1fa6dee9SAndroid Build Coastguard Worker	Text string
23*1fa6dee9SAndroid Build Coastguard Worker
24*1fa6dee9SAndroid Build Coastguard Worker	// ModuleTypes is a list of ModuleType objects that contain information about each module type that is
25*1fa6dee9SAndroid Build Coastguard Worker	// defined by the package.
26*1fa6dee9SAndroid Build Coastguard Worker	ModuleTypes []*ModuleType
27*1fa6dee9SAndroid Build Coastguard Worker}
28*1fa6dee9SAndroid Build Coastguard Worker
29*1fa6dee9SAndroid Build Coastguard Worker// ModuleType contains the information about a module type that is relevant to generating documentation.
30*1fa6dee9SAndroid Build Coastguard Workertype ModuleType struct {
31*1fa6dee9SAndroid Build Coastguard Worker	// Name is the string that will appear in Blueprints files when defining a new module of
32*1fa6dee9SAndroid Build Coastguard Worker	// this type.
33*1fa6dee9SAndroid Build Coastguard Worker	Name string
34*1fa6dee9SAndroid Build Coastguard Worker
35*1fa6dee9SAndroid Build Coastguard Worker	// PkgPath is the full package path of the package that contains the module type factory.
36*1fa6dee9SAndroid Build Coastguard Worker	PkgPath string
37*1fa6dee9SAndroid Build Coastguard Worker
38*1fa6dee9SAndroid Build Coastguard Worker	// Text is the contents of the comment documenting the module type.
39*1fa6dee9SAndroid Build Coastguard Worker	Text template.HTML
40*1fa6dee9SAndroid Build Coastguard Worker
41*1fa6dee9SAndroid Build Coastguard Worker	// PropertyStructs is a list of PropertyStruct objects that contain information about each
42*1fa6dee9SAndroid Build Coastguard Worker	// property struct that is used by the module type, containing all properties that are valid
43*1fa6dee9SAndroid Build Coastguard Worker	// for the module type.
44*1fa6dee9SAndroid Build Coastguard Worker	PropertyStructs []*PropertyStruct
45*1fa6dee9SAndroid Build Coastguard Worker}
46*1fa6dee9SAndroid Build Coastguard Worker
47*1fa6dee9SAndroid Build Coastguard Workertype PropertyStruct struct {
48*1fa6dee9SAndroid Build Coastguard Worker	Name       string
49*1fa6dee9SAndroid Build Coastguard Worker	Text       string
50*1fa6dee9SAndroid Build Coastguard Worker	Properties []Property
51*1fa6dee9SAndroid Build Coastguard Worker}
52*1fa6dee9SAndroid Build Coastguard Worker
53*1fa6dee9SAndroid Build Coastguard Workertype Property struct {
54*1fa6dee9SAndroid Build Coastguard Worker	Name       string
55*1fa6dee9SAndroid Build Coastguard Worker	OtherNames []string
56*1fa6dee9SAndroid Build Coastguard Worker	Type       string
57*1fa6dee9SAndroid Build Coastguard Worker	Tag        reflect.StructTag
58*1fa6dee9SAndroid Build Coastguard Worker	Text       template.HTML
59*1fa6dee9SAndroid Build Coastguard Worker	OtherTexts []template.HTML
60*1fa6dee9SAndroid Build Coastguard Worker	Properties []Property
61*1fa6dee9SAndroid Build Coastguard Worker	Default    string
62*1fa6dee9SAndroid Build Coastguard Worker	Anonymous  bool
63*1fa6dee9SAndroid Build Coastguard Worker}
64*1fa6dee9SAndroid Build Coastguard Worker
65*1fa6dee9SAndroid Build Coastguard Workerfunc AllPackages(pkgFiles map[string][]string, moduleTypeNameFactories map[string]reflect.Value,
66*1fa6dee9SAndroid Build Coastguard Worker	moduleTypeNamePropertyStructs map[string][]interface{}) ([]*Package, error) {
67*1fa6dee9SAndroid Build Coastguard Worker	// Read basic info from the files to construct a Reader instance.
68*1fa6dee9SAndroid Build Coastguard Worker	r := NewReader(pkgFiles)
69*1fa6dee9SAndroid Build Coastguard Worker
70*1fa6dee9SAndroid Build Coastguard Worker	pkgMap := map[string]*Package{}
71*1fa6dee9SAndroid Build Coastguard Worker	var pkgs []*Package
72*1fa6dee9SAndroid Build Coastguard Worker	// Scan through per-module-type property structs map.
73*1fa6dee9SAndroid Build Coastguard Worker	for mtName, propertyStructs := range moduleTypeNamePropertyStructs {
74*1fa6dee9SAndroid Build Coastguard Worker		// Construct ModuleType with the given info.
75*1fa6dee9SAndroid Build Coastguard Worker		mtInfo, err := assembleModuleTypeInfo(r, mtName, moduleTypeNameFactories[mtName], propertyStructs)
76*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
77*1fa6dee9SAndroid Build Coastguard Worker			return nil, err
78*1fa6dee9SAndroid Build Coastguard Worker		}
79*1fa6dee9SAndroid Build Coastguard Worker		// Some pruning work
80*1fa6dee9SAndroid Build Coastguard Worker		removeAnonymousProperties(mtInfo)
81*1fa6dee9SAndroid Build Coastguard Worker		removeEmptyPropertyStructs(mtInfo)
82*1fa6dee9SAndroid Build Coastguard Worker		collapseDuplicatePropertyStructs(mtInfo)
83*1fa6dee9SAndroid Build Coastguard Worker		collapseNestedPropertyStructs(mtInfo)
84*1fa6dee9SAndroid Build Coastguard Worker
85*1fa6dee9SAndroid Build Coastguard Worker		// Add the ModuleInfo to the corresponding Package map/slice entries.
86*1fa6dee9SAndroid Build Coastguard Worker		pkg := pkgMap[mtInfo.PkgPath]
87*1fa6dee9SAndroid Build Coastguard Worker		if pkg == nil {
88*1fa6dee9SAndroid Build Coastguard Worker			var err error
89*1fa6dee9SAndroid Build Coastguard Worker			pkg, err = r.Package(mtInfo.PkgPath)
90*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
91*1fa6dee9SAndroid Build Coastguard Worker				return nil, err
92*1fa6dee9SAndroid Build Coastguard Worker			}
93*1fa6dee9SAndroid Build Coastguard Worker			pkgMap[mtInfo.PkgPath] = pkg
94*1fa6dee9SAndroid Build Coastguard Worker			pkgs = append(pkgs, pkg)
95*1fa6dee9SAndroid Build Coastguard Worker		}
96*1fa6dee9SAndroid Build Coastguard Worker		pkg.ModuleTypes = append(pkg.ModuleTypes, mtInfo)
97*1fa6dee9SAndroid Build Coastguard Worker	}
98*1fa6dee9SAndroid Build Coastguard Worker
99*1fa6dee9SAndroid Build Coastguard Worker	// Sort ModuleTypes within each package.
100*1fa6dee9SAndroid Build Coastguard Worker	for _, pkg := range pkgs {
101*1fa6dee9SAndroid Build Coastguard Worker		sort.Slice(pkg.ModuleTypes, func(i, j int) bool { return pkg.ModuleTypes[i].Name < pkg.ModuleTypes[j].Name })
102*1fa6dee9SAndroid Build Coastguard Worker	}
103*1fa6dee9SAndroid Build Coastguard Worker	// Sort packages.
104*1fa6dee9SAndroid Build Coastguard Worker	sort.Slice(pkgs, func(i, j int) bool { return pkgs[i].Path < pkgs[j].Path })
105*1fa6dee9SAndroid Build Coastguard Worker
106*1fa6dee9SAndroid Build Coastguard Worker	return pkgs, nil
107*1fa6dee9SAndroid Build Coastguard Worker}
108*1fa6dee9SAndroid Build Coastguard Worker
109*1fa6dee9SAndroid Build Coastguard Workerfunc assembleModuleTypeInfo(r *Reader, name string, factory reflect.Value,
110*1fa6dee9SAndroid Build Coastguard Worker	propertyStructs []interface{}) (*ModuleType, error) {
111*1fa6dee9SAndroid Build Coastguard Worker
112*1fa6dee9SAndroid Build Coastguard Worker	mt, err := r.ModuleType(name, factory)
113*1fa6dee9SAndroid Build Coastguard Worker	if err != nil {
114*1fa6dee9SAndroid Build Coastguard Worker		return nil, err
115*1fa6dee9SAndroid Build Coastguard Worker	}
116*1fa6dee9SAndroid Build Coastguard Worker
117*1fa6dee9SAndroid Build Coastguard Worker	// Reader.ModuleType only fills basic information such as name and package path. Collect more info
118*1fa6dee9SAndroid Build Coastguard Worker	// from property struct data.
119*1fa6dee9SAndroid Build Coastguard Worker	for _, s := range propertyStructs {
120*1fa6dee9SAndroid Build Coastguard Worker		v := reflect.ValueOf(s).Elem()
121*1fa6dee9SAndroid Build Coastguard Worker		t := v.Type()
122*1fa6dee9SAndroid Build Coastguard Worker
123*1fa6dee9SAndroid Build Coastguard Worker		ps, err := r.PropertyStruct(t.PkgPath(), t.Name(), v)
124*1fa6dee9SAndroid Build Coastguard Worker
125*1fa6dee9SAndroid Build Coastguard Worker		if err != nil {
126*1fa6dee9SAndroid Build Coastguard Worker			return nil, err
127*1fa6dee9SAndroid Build Coastguard Worker		}
128*1fa6dee9SAndroid Build Coastguard Worker		ps.ExcludeByTag("blueprint", "mutated")
129*1fa6dee9SAndroid Build Coastguard Worker		for _, nestedProperty := range nestedPropertyStructs(v) {
130*1fa6dee9SAndroid Build Coastguard Worker			nestedName := nestedProperty.nestPoint
131*1fa6dee9SAndroid Build Coastguard Worker			nestedValue := nestedProperty.value
132*1fa6dee9SAndroid Build Coastguard Worker			nestedType := nestedValue.Type()
133*1fa6dee9SAndroid Build Coastguard Worker
134*1fa6dee9SAndroid Build Coastguard Worker			// Ignore property structs with unexported or unnamed types
135*1fa6dee9SAndroid Build Coastguard Worker			if nestedType.PkgPath() == "" {
136*1fa6dee9SAndroid Build Coastguard Worker				continue
137*1fa6dee9SAndroid Build Coastguard Worker			}
138*1fa6dee9SAndroid Build Coastguard Worker			nested, err := r.PropertyStruct(nestedType.PkgPath(), nestedType.Name(), nestedValue)
139*1fa6dee9SAndroid Build Coastguard Worker			if err != nil {
140*1fa6dee9SAndroid Build Coastguard Worker				return nil, err
141*1fa6dee9SAndroid Build Coastguard Worker			}
142*1fa6dee9SAndroid Build Coastguard Worker			nested.ExcludeByTag("blueprint", "mutated")
143*1fa6dee9SAndroid Build Coastguard Worker			if nestedName == "" {
144*1fa6dee9SAndroid Build Coastguard Worker				ps.Nest(nested)
145*1fa6dee9SAndroid Build Coastguard Worker			} else {
146*1fa6dee9SAndroid Build Coastguard Worker				nestPoint := ps.GetByName(nestedName)
147*1fa6dee9SAndroid Build Coastguard Worker				if nestPoint == nil {
148*1fa6dee9SAndroid Build Coastguard Worker					return nil, fmt.Errorf("nesting point %q not found", nestedName)
149*1fa6dee9SAndroid Build Coastguard Worker				}
150*1fa6dee9SAndroid Build Coastguard Worker				nestPoint.Nest(nested)
151*1fa6dee9SAndroid Build Coastguard Worker			}
152*1fa6dee9SAndroid Build Coastguard Worker
153*1fa6dee9SAndroid Build Coastguard Worker			if nestedProperty.anonymous {
154*1fa6dee9SAndroid Build Coastguard Worker				if nestedName != "" {
155*1fa6dee9SAndroid Build Coastguard Worker					nestedName += "."
156*1fa6dee9SAndroid Build Coastguard Worker				}
157*1fa6dee9SAndroid Build Coastguard Worker				nestedName += proptools.PropertyNameForField(nested.Name)
158*1fa6dee9SAndroid Build Coastguard Worker				nestedProp := ps.GetByName(nestedName)
159*1fa6dee9SAndroid Build Coastguard Worker				// Anonymous properties may have already been omitted, no need to ensure they are filtered later
160*1fa6dee9SAndroid Build Coastguard Worker				if nestedProp != nil {
161*1fa6dee9SAndroid Build Coastguard Worker					// Set property to anonymous to allow future filtering
162*1fa6dee9SAndroid Build Coastguard Worker					nestedProp.SetAnonymous()
163*1fa6dee9SAndroid Build Coastguard Worker				}
164*1fa6dee9SAndroid Build Coastguard Worker			}
165*1fa6dee9SAndroid Build Coastguard Worker		}
166*1fa6dee9SAndroid Build Coastguard Worker		mt.PropertyStructs = append(mt.PropertyStructs, ps)
167*1fa6dee9SAndroid Build Coastguard Worker	}
168*1fa6dee9SAndroid Build Coastguard Worker
169*1fa6dee9SAndroid Build Coastguard Worker	return mt, nil
170*1fa6dee9SAndroid Build Coastguard Worker}
171*1fa6dee9SAndroid Build Coastguard Worker
172*1fa6dee9SAndroid Build Coastguard Workertype nestedProperty struct {
173*1fa6dee9SAndroid Build Coastguard Worker	nestPoint string
174*1fa6dee9SAndroid Build Coastguard Worker	value     reflect.Value
175*1fa6dee9SAndroid Build Coastguard Worker	anonymous bool
176*1fa6dee9SAndroid Build Coastguard Worker}
177*1fa6dee9SAndroid Build Coastguard Worker
178*1fa6dee9SAndroid Build Coastguard Workerfunc nestedPropertyStructs(s reflect.Value) []nestedProperty {
179*1fa6dee9SAndroid Build Coastguard Worker	ret := make([]nestedProperty, 0)
180*1fa6dee9SAndroid Build Coastguard Worker	var walk func(structValue reflect.Value, prefix string)
181*1fa6dee9SAndroid Build Coastguard Worker	walk = func(structValue reflect.Value, prefix string) {
182*1fa6dee9SAndroid Build Coastguard Worker		var nestStruct func(field reflect.StructField, value reflect.Value, fieldName string)
183*1fa6dee9SAndroid Build Coastguard Worker		nestStruct = func(field reflect.StructField, value reflect.Value, fieldName string) {
184*1fa6dee9SAndroid Build Coastguard Worker			nestPoint := prefix
185*1fa6dee9SAndroid Build Coastguard Worker			if field.Anonymous {
186*1fa6dee9SAndroid Build Coastguard Worker				nestPoint = strings.TrimSuffix(nestPoint, ".")
187*1fa6dee9SAndroid Build Coastguard Worker			} else {
188*1fa6dee9SAndroid Build Coastguard Worker				nestPoint = nestPoint + proptools.PropertyNameForField(fieldName)
189*1fa6dee9SAndroid Build Coastguard Worker			}
190*1fa6dee9SAndroid Build Coastguard Worker			ret = append(ret, nestedProperty{nestPoint: nestPoint, value: value, anonymous: field.Anonymous})
191*1fa6dee9SAndroid Build Coastguard Worker			if nestPoint != "" {
192*1fa6dee9SAndroid Build Coastguard Worker				nestPoint += "."
193*1fa6dee9SAndroid Build Coastguard Worker			}
194*1fa6dee9SAndroid Build Coastguard Worker			walk(value, nestPoint)
195*1fa6dee9SAndroid Build Coastguard Worker		}
196*1fa6dee9SAndroid Build Coastguard Worker
197*1fa6dee9SAndroid Build Coastguard Worker		typ := structValue.Type()
198*1fa6dee9SAndroid Build Coastguard Worker		for i := 0; i < structValue.NumField(); i++ {
199*1fa6dee9SAndroid Build Coastguard Worker			field := typ.Field(i)
200*1fa6dee9SAndroid Build Coastguard Worker			if field.PkgPath != "" {
201*1fa6dee9SAndroid Build Coastguard Worker				// The field is not exported so just skip it.
202*1fa6dee9SAndroid Build Coastguard Worker				continue
203*1fa6dee9SAndroid Build Coastguard Worker			}
204*1fa6dee9SAndroid Build Coastguard Worker			if proptools.HasTag(field, "blueprint", "mutated") {
205*1fa6dee9SAndroid Build Coastguard Worker				continue
206*1fa6dee9SAndroid Build Coastguard Worker			}
207*1fa6dee9SAndroid Build Coastguard Worker			if proptools.IsConfigurable(field.Type) {
208*1fa6dee9SAndroid Build Coastguard Worker				// Don't recurse into configurable properties, they're structs but not property structs
209*1fa6dee9SAndroid Build Coastguard Worker				continue
210*1fa6dee9SAndroid Build Coastguard Worker			}
211*1fa6dee9SAndroid Build Coastguard Worker
212*1fa6dee9SAndroid Build Coastguard Worker			fieldValue := structValue.Field(i)
213*1fa6dee9SAndroid Build Coastguard Worker
214*1fa6dee9SAndroid Build Coastguard Worker			switch fieldValue.Kind() {
215*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Bool, reflect.String, reflect.Slice, reflect.Int, reflect.Uint:
216*1fa6dee9SAndroid Build Coastguard Worker				// Nothing
217*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Struct:
218*1fa6dee9SAndroid Build Coastguard Worker				nestStruct(field, fieldValue, field.Name)
219*1fa6dee9SAndroid Build Coastguard Worker			case reflect.Ptr, reflect.Interface:
220*1fa6dee9SAndroid Build Coastguard Worker
221*1fa6dee9SAndroid Build Coastguard Worker				if !fieldValue.IsNil() {
222*1fa6dee9SAndroid Build Coastguard Worker					// We leave the pointer intact and zero out the struct that's
223*1fa6dee9SAndroid Build Coastguard Worker					// pointed to.
224*1fa6dee9SAndroid Build Coastguard Worker					elem := fieldValue.Elem()
225*1fa6dee9SAndroid Build Coastguard Worker					if fieldValue.Kind() == reflect.Interface {
226*1fa6dee9SAndroid Build Coastguard Worker						if elem.Kind() != reflect.Ptr {
227*1fa6dee9SAndroid Build Coastguard Worker							panic(fmt.Errorf("can't get type of field %q: interface "+
228*1fa6dee9SAndroid Build Coastguard Worker								"refers to a non-pointer", field.Name))
229*1fa6dee9SAndroid Build Coastguard Worker						}
230*1fa6dee9SAndroid Build Coastguard Worker						elem = elem.Elem()
231*1fa6dee9SAndroid Build Coastguard Worker					}
232*1fa6dee9SAndroid Build Coastguard Worker					if elem.Kind() == reflect.Struct {
233*1fa6dee9SAndroid Build Coastguard Worker						nestStruct(field, elem, field.Name)
234*1fa6dee9SAndroid Build Coastguard Worker					}
235*1fa6dee9SAndroid Build Coastguard Worker				}
236*1fa6dee9SAndroid Build Coastguard Worker			default:
237*1fa6dee9SAndroid Build Coastguard Worker				panic(fmt.Errorf("unexpected kind for property struct field %q: %s",
238*1fa6dee9SAndroid Build Coastguard Worker					field.Name, fieldValue.Kind()))
239*1fa6dee9SAndroid Build Coastguard Worker			}
240*1fa6dee9SAndroid Build Coastguard Worker		}
241*1fa6dee9SAndroid Build Coastguard Worker	}
242*1fa6dee9SAndroid Build Coastguard Worker
243*1fa6dee9SAndroid Build Coastguard Worker	walk(s, "")
244*1fa6dee9SAndroid Build Coastguard Worker	return ret
245*1fa6dee9SAndroid Build Coastguard Worker}
246*1fa6dee9SAndroid Build Coastguard Worker
247*1fa6dee9SAndroid Build Coastguard Worker// Remove any property structs that have no exported fields
248*1fa6dee9SAndroid Build Coastguard Workerfunc removeEmptyPropertyStructs(mt *ModuleType) {
249*1fa6dee9SAndroid Build Coastguard Worker	for i := 0; i < len(mt.PropertyStructs); i++ {
250*1fa6dee9SAndroid Build Coastguard Worker		if len(mt.PropertyStructs[i].Properties) == 0 {
251*1fa6dee9SAndroid Build Coastguard Worker			mt.PropertyStructs = append(mt.PropertyStructs[:i], mt.PropertyStructs[i+1:]...)
252*1fa6dee9SAndroid Build Coastguard Worker			i--
253*1fa6dee9SAndroid Build Coastguard Worker		}
254*1fa6dee9SAndroid Build Coastguard Worker	}
255*1fa6dee9SAndroid Build Coastguard Worker}
256*1fa6dee9SAndroid Build Coastguard Worker
257*1fa6dee9SAndroid Build Coastguard Worker// Remove any property structs that are anonymous
258*1fa6dee9SAndroid Build Coastguard Workerfunc removeAnonymousProperties(mt *ModuleType) {
259*1fa6dee9SAndroid Build Coastguard Worker	var removeAnonymousProps func(props []Property) []Property
260*1fa6dee9SAndroid Build Coastguard Worker	removeAnonymousProps = func(props []Property) []Property {
261*1fa6dee9SAndroid Build Coastguard Worker		newProps := make([]Property, 0, len(props))
262*1fa6dee9SAndroid Build Coastguard Worker		for _, p := range props {
263*1fa6dee9SAndroid Build Coastguard Worker			if p.Anonymous {
264*1fa6dee9SAndroid Build Coastguard Worker				continue
265*1fa6dee9SAndroid Build Coastguard Worker			}
266*1fa6dee9SAndroid Build Coastguard Worker			if len(p.Properties) > 0 {
267*1fa6dee9SAndroid Build Coastguard Worker				p.Properties = removeAnonymousProps(p.Properties)
268*1fa6dee9SAndroid Build Coastguard Worker			}
269*1fa6dee9SAndroid Build Coastguard Worker			newProps = append(newProps, p)
270*1fa6dee9SAndroid Build Coastguard Worker		}
271*1fa6dee9SAndroid Build Coastguard Worker		return newProps
272*1fa6dee9SAndroid Build Coastguard Worker	}
273*1fa6dee9SAndroid Build Coastguard Worker	for _, ps := range mt.PropertyStructs {
274*1fa6dee9SAndroid Build Coastguard Worker		ps.Properties = removeAnonymousProps(ps.Properties)
275*1fa6dee9SAndroid Build Coastguard Worker	}
276*1fa6dee9SAndroid Build Coastguard Worker}
277*1fa6dee9SAndroid Build Coastguard Worker
278*1fa6dee9SAndroid Build Coastguard Worker// Squashes duplicates of the same property struct into single entries
279*1fa6dee9SAndroid Build Coastguard Workerfunc collapseDuplicatePropertyStructs(mt *ModuleType) {
280*1fa6dee9SAndroid Build Coastguard Worker	var collapsed []*PropertyStruct
281*1fa6dee9SAndroid Build Coastguard Worker
282*1fa6dee9SAndroid Build Coastguard WorkerpropertyStructLoop:
283*1fa6dee9SAndroid Build Coastguard Worker	for _, from := range mt.PropertyStructs {
284*1fa6dee9SAndroid Build Coastguard Worker		for _, to := range collapsed {
285*1fa6dee9SAndroid Build Coastguard Worker			if from.Name == to.Name {
286*1fa6dee9SAndroid Build Coastguard Worker				CollapseDuplicateProperties(&to.Properties, &from.Properties)
287*1fa6dee9SAndroid Build Coastguard Worker				continue propertyStructLoop
288*1fa6dee9SAndroid Build Coastguard Worker			}
289*1fa6dee9SAndroid Build Coastguard Worker		}
290*1fa6dee9SAndroid Build Coastguard Worker		collapsed = append(collapsed, from)
291*1fa6dee9SAndroid Build Coastguard Worker	}
292*1fa6dee9SAndroid Build Coastguard Worker	mt.PropertyStructs = collapsed
293*1fa6dee9SAndroid Build Coastguard Worker}
294*1fa6dee9SAndroid Build Coastguard Worker
295*1fa6dee9SAndroid Build Coastguard Workerfunc CollapseDuplicateProperties(to, from *[]Property) {
296*1fa6dee9SAndroid Build Coastguard WorkerpropertyLoop:
297*1fa6dee9SAndroid Build Coastguard Worker	for _, f := range *from {
298*1fa6dee9SAndroid Build Coastguard Worker		for i := range *to {
299*1fa6dee9SAndroid Build Coastguard Worker			t := &(*to)[i]
300*1fa6dee9SAndroid Build Coastguard Worker			if f.Name == t.Name {
301*1fa6dee9SAndroid Build Coastguard Worker				CollapseDuplicateProperties(&t.Properties, &f.Properties)
302*1fa6dee9SAndroid Build Coastguard Worker				continue propertyLoop
303*1fa6dee9SAndroid Build Coastguard Worker			}
304*1fa6dee9SAndroid Build Coastguard Worker		}
305*1fa6dee9SAndroid Build Coastguard Worker		*to = append(*to, f)
306*1fa6dee9SAndroid Build Coastguard Worker	}
307*1fa6dee9SAndroid Build Coastguard Worker}
308*1fa6dee9SAndroid Build Coastguard Worker
309*1fa6dee9SAndroid Build Coastguard Worker// Find all property structs that only contain structs, and move their children up one with
310*1fa6dee9SAndroid Build Coastguard Worker// a prefixed name
311*1fa6dee9SAndroid Build Coastguard Workerfunc collapseNestedPropertyStructs(mt *ModuleType) {
312*1fa6dee9SAndroid Build Coastguard Worker	for _, ps := range mt.PropertyStructs {
313*1fa6dee9SAndroid Build Coastguard Worker		collapseNestedProperties(&ps.Properties)
314*1fa6dee9SAndroid Build Coastguard Worker	}
315*1fa6dee9SAndroid Build Coastguard Worker}
316*1fa6dee9SAndroid Build Coastguard Worker
317*1fa6dee9SAndroid Build Coastguard Workerfunc collapseNestedProperties(p *[]Property) {
318*1fa6dee9SAndroid Build Coastguard Worker	var n []Property
319*1fa6dee9SAndroid Build Coastguard Worker
320*1fa6dee9SAndroid Build Coastguard Worker	for _, parent := range *p {
321*1fa6dee9SAndroid Build Coastguard Worker		var containsProperty bool
322*1fa6dee9SAndroid Build Coastguard Worker		for j := range parent.Properties {
323*1fa6dee9SAndroid Build Coastguard Worker			child := &parent.Properties[j]
324*1fa6dee9SAndroid Build Coastguard Worker			if len(child.Properties) > 0 {
325*1fa6dee9SAndroid Build Coastguard Worker				collapseNestedProperties(&child.Properties)
326*1fa6dee9SAndroid Build Coastguard Worker			} else {
327*1fa6dee9SAndroid Build Coastguard Worker				containsProperty = true
328*1fa6dee9SAndroid Build Coastguard Worker			}
329*1fa6dee9SAndroid Build Coastguard Worker		}
330*1fa6dee9SAndroid Build Coastguard Worker		if containsProperty || len(parent.Properties) == 0 {
331*1fa6dee9SAndroid Build Coastguard Worker			n = append(n, parent)
332*1fa6dee9SAndroid Build Coastguard Worker		} else {
333*1fa6dee9SAndroid Build Coastguard Worker			for j := range parent.Properties {
334*1fa6dee9SAndroid Build Coastguard Worker				child := parent.Properties[j]
335*1fa6dee9SAndroid Build Coastguard Worker				child.Name = parent.Name + "." + child.Name
336*1fa6dee9SAndroid Build Coastguard Worker				n = append(n, child)
337*1fa6dee9SAndroid Build Coastguard Worker			}
338*1fa6dee9SAndroid Build Coastguard Worker		}
339*1fa6dee9SAndroid Build Coastguard Worker	}
340*1fa6dee9SAndroid Build Coastguard Worker	*p = n
341*1fa6dee9SAndroid Build Coastguard Worker}
342