1// Copyright (C) 2019 The Android Open Source Project
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 sdk
16
17import (
18	"fmt"
19	"reflect"
20	"strings"
21
22	"android/soong/android"
23)
24
25type bpPropertySet struct {
26	properties map[string]interface{}
27	tags       map[string]android.BpPropertyTag
28	comments   map[string]string
29	order      []string
30}
31
32var _ android.BpPropertySet = (*bpPropertySet)(nil)
33
34func (s *bpPropertySet) init() {
35	s.properties = make(map[string]interface{})
36	s.tags = make(map[string]android.BpPropertyTag)
37}
38
39// Converts the given value, which is assumed to be a struct, to a
40// bpPropertySet.
41func convertToPropertySet(value reflect.Value) *bpPropertySet {
42	res := newPropertySet()
43	structType := value.Type()
44
45	for i := 0; i < structType.NumField(); i++ {
46		field := structType.Field(i)
47		fieldVal := value.Field(i)
48
49		switch fieldVal.Type().Kind() {
50		case reflect.Ptr:
51			if fieldVal.IsNil() {
52				continue // nil pointer means the property isn't set.
53			}
54			fieldVal = fieldVal.Elem()
55		case reflect.Slice:
56			if fieldVal.IsNil() {
57				continue // Ignore a nil slice (but not one with length zero).
58			}
59		}
60
61		if fieldVal.Type().Kind() == reflect.Struct {
62			fieldVal = fieldVal.Addr() // Avoid struct copy below.
63		}
64		res.AddProperty(strings.ToLower(field.Name), fieldVal.Interface())
65	}
66
67	return res
68}
69
70// Converts the given value to something that can be set in a property.
71func coercePropertyValue(value interface{}) interface{} {
72	val := reflect.ValueOf(value)
73	switch val.Kind() {
74	case reflect.Struct:
75		// convertToPropertySet requires an addressable struct, and this is probably
76		// a mistake.
77		panic(fmt.Sprintf("Value is a struct, not a pointer to one: %v", value))
78	case reflect.Ptr:
79		if _, ok := value.(*bpPropertySet); !ok {
80			derefValue := reflect.Indirect(val)
81			if derefValue.Kind() != reflect.Struct {
82				panic(fmt.Sprintf("A pointer must be to a struct, got: %v", value))
83			}
84			return convertToPropertySet(derefValue)
85		}
86	}
87	return value
88}
89
90// Merges the fields of the given property set into s.
91func (s *bpPropertySet) mergePropertySet(propSet *bpPropertySet) {
92	for _, name := range propSet.order {
93		if tag, ok := propSet.tags[name]; ok {
94			s.AddPropertyWithTag(name, propSet.properties[name], tag)
95		} else {
96			s.AddProperty(name, propSet.properties[name])
97		}
98	}
99}
100
101func (s *bpPropertySet) AddProperty(name string, value interface{}) {
102	value = coercePropertyValue(value)
103
104	if propSetValue, ok := value.(*bpPropertySet); ok {
105		if curValue, ok := s.properties[name]; ok {
106			if curSet, ok := curValue.(*bpPropertySet); ok {
107				curSet.mergePropertySet(propSetValue)
108				return
109			}
110			// If the current value isn't a property set we got conflicting types.
111			// Continue down to the check below to complain about it.
112		}
113	}
114
115	if s.properties[name] != nil {
116		panic(fmt.Sprintf("Property %q already exists in property set", name))
117	}
118
119	s.properties[name] = value
120	s.order = append(s.order, name)
121}
122
123func (s *bpPropertySet) AddPropertyWithTag(name string, value interface{}, tag android.BpPropertyTag) {
124	s.AddProperty(name, value)
125	s.tags[name] = tag
126}
127
128func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet {
129	s.AddProperty(name, newPropertySet())
130	return s.properties[name].(android.BpPropertySet)
131}
132
133func (s *bpPropertySet) getValue(name string) interface{} {
134	return s.properties[name]
135}
136
137func (s *bpPropertySet) getOptionalValue(name string) (interface{}, bool) {
138	value, ok := s.properties[name]
139	return value, ok
140}
141
142func (s *bpPropertySet) getTag(name string) interface{} {
143	return s.tags[name]
144}
145
146func (s *bpPropertySet) AddCommentForProperty(name, text string) {
147	if s.comments == nil {
148		s.comments = map[string]string{}
149	}
150	s.comments[name] = strings.TrimSpace(text)
151}
152
153func (s *bpPropertySet) transformContents(transformer bpPropertyTransformer) {
154	var newOrder []string
155	for _, name := range s.order {
156		value := s.properties[name]
157		tag := s.tags[name]
158		var newValue interface{}
159		var newTag android.BpPropertyTag
160		if propertySet, ok := value.(*bpPropertySet); ok {
161			var newPropertySet *bpPropertySet
162			newPropertySet, newTag = transformPropertySet(transformer, name, propertySet, tag)
163			if newPropertySet == nil {
164				newValue = nil
165			} else {
166				newValue = newPropertySet
167			}
168		} else {
169			newValue, newTag = transformer.transformProperty(name, value, tag)
170		}
171
172		if newValue == nil {
173			// Delete the property from the map and exclude it from the new order.
174			delete(s.properties, name)
175		} else {
176			// Update the property in the map and add the name to the new order list.
177			s.properties[name] = newValue
178			s.tags[name] = newTag
179			newOrder = append(newOrder, name)
180		}
181	}
182	s.order = newOrder
183}
184
185func transformPropertySet(transformer bpPropertyTransformer, name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
186	newPropertySet, newTag := transformer.transformPropertySetBeforeContents(name, propertySet, tag)
187	if newPropertySet != nil {
188		newPropertySet.transformContents(transformer)
189
190		newPropertySet, newTag = transformer.transformPropertySetAfterContents(name, newPropertySet, newTag)
191	}
192	return newPropertySet, newTag
193}
194
195func (s *bpPropertySet) setProperty(name string, value interface{}) {
196	if s.properties[name] == nil {
197		s.AddProperty(name, value)
198	} else {
199		s.properties[name] = value
200		s.tags[name] = nil
201	}
202}
203
204func (s *bpPropertySet) removeProperty(name string) {
205	delete(s.properties, name)
206	delete(s.tags, name)
207	_, s.order = android.RemoveFromList(name, s.order)
208}
209
210func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) {
211	if s.properties[name] != nil {
212		panic("Property %q already exists in property set")
213	}
214
215	// Add the name to the end of the order, to ensure it has necessary capacity
216	// and to handle the case when the position does not exist.
217	s.order = append(s.order, name)
218
219	// Search through the order for the item that matches supplied position. If
220	// found then insert the name of the new property after it.
221	for i, v := range s.order {
222		if v == position {
223			// Copy the items after the one where the new property should be inserted.
224			copy(s.order[i+2:], s.order[i+1:])
225			// Insert the item in the list.
226			s.order[i+1] = name
227		}
228	}
229
230	s.properties[name] = value
231}
232
233type bpModule struct {
234	*bpPropertySet
235	moduleType string
236}
237
238func (m *bpModule) ModuleType() string {
239	return m.moduleType
240}
241
242func (m *bpModule) Name() string {
243	name, hasName := m.getOptionalValue("name")
244	if hasName {
245		return name.(string)
246	} else {
247		return ""
248	}
249}
250
251var _ android.BpModule = (*bpModule)(nil)
252
253type bpPropertyTransformer interface {
254	// Transform the property set, returning the new property set/tag to insert back into the
255	// parent property set (or module if this is the top level property set).
256	//
257	// This will be called before transforming the properties in the supplied set.
258	//
259	// The name will be "" for the top level property set.
260	//
261	// Returning (nil, ...) will cause the property set to be removed.
262	transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
263
264	// Transform the property set, returning the new property set/tag to insert back into the
265	// parent property set (or module if this is the top level property set).
266	//
267	// This will be called after transforming the properties in the supplied set.
268	//
269	// The name will be "" for the top level property set.
270	//
271	// Returning (nil, ...) will cause the property set to be removed.
272	transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
273
274	// Transform a property, return the new value/tag to insert back into the property set.
275	//
276	// Returning (nil, ...) will cause the property to be removed.
277	transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag)
278}
279
280// Interface for transforming bpModule objects.
281type bpTransformer interface {
282	// Transform the module, returning the result.
283	//
284	// The method can either create a new module and return that, or modify the supplied module
285	// in place and return that.
286	//
287	// After this returns the transformer is applied to the contents of the returned module.
288	transformModule(module *bpModule) *bpModule
289
290	bpPropertyTransformer
291}
292
293type identityTransformation struct{}
294
295var _ bpTransformer = (*identityTransformation)(nil)
296
297func (t identityTransformation) transformModule(module *bpModule) *bpModule {
298	return module
299}
300
301func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
302	return propertySet, tag
303}
304
305func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
306	return propertySet, tag
307}
308
309func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
310	return value, tag
311}
312
313func (m *bpModule) deepCopy() *bpModule {
314	return m.transform(deepCopyTransformer)
315}
316
317func (m *bpModule) transform(transformer bpTransformer) *bpModule {
318	transformedModule := transformer.transformModule(m)
319	// Copy the contents of the returned property set into the module and then transform that.
320	transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
321	return transformedModule
322}
323
324type deepCopyTransformation struct {
325	identityTransformation
326}
327
328func (t deepCopyTransformation) transformModule(module *bpModule) *bpModule {
329	// Take a shallow copy of the module. Any mutable property values will be copied by the
330	// transformer.
331	moduleCopy := *module
332	return &moduleCopy
333}
334
335func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
336	// Create a shallow copy of the properties map. Any mutable property values will be copied by the
337	// transformer.
338	propertiesCopy := make(map[string]interface{})
339	for propertyName, value := range propertySet.properties {
340		propertiesCopy[propertyName] = value
341	}
342
343	// Ditto for tags map.
344	tagsCopy := make(map[string]android.BpPropertyTag)
345	for propertyName, propertyTag := range propertySet.tags {
346		tagsCopy[propertyName] = propertyTag
347	}
348
349	// Create a new property set.
350	return &bpPropertySet{
351		properties: propertiesCopy,
352		tags:       tagsCopy,
353		order:      append([]string(nil), propertySet.order...),
354	}, tag
355}
356
357func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
358	// Copy string slice, otherwise return value.
359	if values, ok := value.([]string); ok {
360		valuesCopy := make([]string, len(values))
361		copy(valuesCopy, values)
362		return valuesCopy, tag
363	}
364	return value, tag
365}
366
367var deepCopyTransformer bpTransformer = deepCopyTransformation{}
368
369// A .bp file
370type bpFile struct {
371	modules map[string]*bpModule
372	order   []*bpModule
373}
374
375// Add a module.
376//
377// The module must have had its "name" property set to a string value that
378// is unique within this file.
379func (f *bpFile) AddModule(module android.BpModule) {
380	m := module.(*bpModule)
381	moduleType := module.ModuleType()
382	name := m.Name()
383	hasName := true
384	if name == "" {
385		// Use a prefixed module type as the name instead just in case this is something like a package
386		// of namespace module which does not require a name.
387		name = "#" + moduleType
388		hasName = false
389	}
390
391	if f.modules[name] != nil {
392		if hasName {
393			panic(fmt.Sprintf("Module %q already exists in bp file", name))
394		} else {
395			panic(fmt.Sprintf("Unnamed module type %q already exists in bp file", moduleType))
396		}
397	}
398
399	f.modules[name] = m
400	f.order = append(f.order, m)
401}
402
403func (f *bpFile) newModule(moduleType string) *bpModule {
404	return newModule(moduleType)
405}
406
407func newModule(moduleType string) *bpModule {
408	module := &bpModule{
409		moduleType:    moduleType,
410		bpPropertySet: newPropertySet(),
411	}
412	return module
413}
414
415func newPropertySet() *bpPropertySet {
416	set := &bpPropertySet{}
417	set.init()
418	return set
419}
420