xref: /aosp_15_r20/build/blueprint/proptools/repack.go (revision 1fa6dee971e1612fa5cc0aa5ca2d35a22e2c34a3)
1// Copyright 2024 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package proptools
16
17import (
18	"fmt"
19	"reflect"
20	"slices"
21
22	"github.com/google/blueprint/parser"
23)
24
25func RepackProperties(props []interface{}) (*parser.Map, error) {
26
27	var dereferencedProps []reflect.Value
28	for _, rawProp := range props {
29		propStruct := reflect.ValueOf(rawProp)
30		if !isStructPtr(propStruct.Type()) {
31			return nil, fmt.Errorf("properties must be *struct, got %s",
32				propStruct.Type())
33		}
34		propStruct = propStruct.Elem()
35		dereferencedProps = append(dereferencedProps, propStruct)
36	}
37
38	return repackStructs(dereferencedProps)
39}
40
41func repackStructs(props []reflect.Value) (*parser.Map, error) {
42	var allFieldNames []string
43	for _, prop := range props {
44		propType := prop.Type()
45		for i := 0; i < propType.NumField(); i++ {
46			field := propType.Field(i)
47			if !slices.Contains(allFieldNames, field.Name) {
48				allFieldNames = append(allFieldNames, field.Name)
49			}
50		}
51	}
52
53	result := &parser.Map{}
54
55	for _, fieldName := range allFieldNames {
56		var fields []reflect.Value
57		for _, prop := range props {
58			field := prop.FieldByName(fieldName)
59			if field.IsValid() {
60				fields = append(fields, field)
61			}
62		}
63		if err := assertFieldsEquivalent(fields); err != nil {
64			return nil, err
65		}
66
67		var expr parser.Expression
68		var field reflect.Value
69		for _, f := range fields {
70			if !isPropEmpty(f) {
71				field = f
72				break
73			}
74		}
75		if !field.IsValid() {
76			continue
77		}
78		if isStruct(field.Type()) && !isConfigurable(field.Type()) {
79			x, err := repackStructs(fields)
80			if err != nil {
81				return nil, err
82			}
83			if x != nil {
84				expr = x
85			}
86		} else {
87			x, err := fieldToExpr(field)
88			if err != nil {
89				return nil, err
90			}
91			if x != nil {
92				expr = *x
93			}
94		}
95
96		if expr != nil {
97			result.Properties = append(result.Properties, &parser.Property{
98				Name:  PropertyNameForField(fieldName),
99				Value: expr,
100			})
101		}
102	}
103
104	return result, nil
105}
106
107func fieldToExpr(field reflect.Value) (*parser.Expression, error) {
108	if IsConfigurable(field.Type()) {
109		return field.Interface().(configurableReflection).toExpression()
110	}
111	if field.Kind() == reflect.Pointer {
112		if field.IsNil() {
113			return nil, nil
114		}
115		field = field.Elem()
116	}
117	switch field.Kind() {
118	case reflect.String:
119		var result parser.Expression = &parser.String{Value: field.String()}
120		return &result, nil
121	case reflect.Bool:
122		var result parser.Expression = &parser.Bool{Value: field.Bool()}
123		return &result, nil
124	case reflect.Int, reflect.Int64:
125		var result parser.Expression = &parser.Int64{Value: field.Int()}
126		return &result, nil
127	case reflect.Slice:
128		var contents []parser.Expression
129		for i := 0; i < field.Len(); i++ {
130			inner, err := fieldToExpr(field.Index(i))
131			if err != nil {
132				return nil, err
133			}
134			contents = append(contents, *inner)
135		}
136		var result parser.Expression = &parser.List{Values: contents}
137		return &result, nil
138	case reflect.Struct:
139		var properties []*parser.Property
140		typ := field.Type()
141		for i := 0; i < typ.NumField(); i++ {
142			inner, err := fieldToExpr(field.Field(i))
143			if err != nil {
144				return nil, err
145			}
146			properties = append(properties, &parser.Property{
147				Name:  typ.Field(i).Name,
148				Value: *inner,
149			})
150		}
151		var result parser.Expression = &parser.Map{Properties: properties}
152		return &result, nil
153	default:
154		return nil, fmt.Errorf("Unhandled type: %s", field.Kind().String())
155	}
156}
157
158func isPropEmpty(value reflect.Value) bool {
159	switch value.Kind() {
160	case reflect.Pointer:
161		if value.IsNil() {
162			return true
163		}
164		return isPropEmpty(value.Elem())
165	case reflect.Struct:
166		if isConfigurable(value.Type()) {
167			return value.Interface().(configurableReflection).isEmpty()
168		}
169		for i := 0; i < value.NumField(); i++ {
170			if !isPropEmpty(value.Field(i)) {
171				return false
172			}
173		}
174		return true
175	default:
176		return false
177	}
178}
179
180func assertFieldsEquivalent(fields []reflect.Value) error {
181	var firstNonEmpty reflect.Value
182	var firstIndex int
183	for i, f := range fields {
184		if !isPropEmpty(f) {
185			firstNonEmpty = f
186			firstIndex = i
187			break
188		}
189	}
190	if !firstNonEmpty.IsValid() {
191		return nil
192	}
193	for i, f := range fields {
194		if i != firstIndex && !isPropEmpty(f) {
195			if err := assertTwoNonEmptyFieldsEquivalent(firstNonEmpty, f); err != nil {
196				return err
197			}
198		}
199	}
200	return nil
201}
202
203func assertTwoNonEmptyFieldsEquivalent(a, b reflect.Value) error {
204	aType := a.Type()
205	bType := b.Type()
206
207	if aType != bType {
208		return fmt.Errorf("fields must have the same type")
209	}
210
211	switch aType.Kind() {
212	case reflect.Pointer:
213		return assertTwoNonEmptyFieldsEquivalent(a.Elem(), b.Elem())
214	case reflect.String:
215		if a.String() != b.String() {
216			return fmt.Errorf("Conflicting fields in property structs had values %q and %q", a.String(), b.String())
217		}
218	case reflect.Bool:
219		if a.Bool() != b.Bool() {
220			return fmt.Errorf("Conflicting fields in property structs had values %t and %t", a.Bool(), b.Bool())
221		}
222	case reflect.Slice:
223		if a.Len() != b.Len() {
224			return fmt.Errorf("Conflicting fields in property structs had lengths %d and %d", a.Len(), b.Len())
225		}
226		for i := 0; i < a.Len(); i++ {
227			if err := assertTwoNonEmptyFieldsEquivalent(a.Index(i), b.Index(i)); err != nil {
228				return err
229			}
230		}
231	case reflect.Int:
232		if a.Int() != b.Int() {
233			return fmt.Errorf("Conflicting fields in property structs had values %d and %d", a.Int(), b.Int())
234		}
235	case reflect.Struct:
236		if isConfigurable(a.Type()) {
237			// We could properly check that two configurables are equivalent, but that's a lot more
238			// work for a case that I don't think should show up in practice.
239			return fmt.Errorf("Cannot handle two property structs with nonempty configurable properties")
240		}
241		// We don't care about checking if structs are equivalent, we'll check their individual
242		// fields when we recurse down.
243	default:
244		return fmt.Errorf("Unhandled kind: %s", aType.Kind().String())
245	}
246
247	return nil
248}
249