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
20	"android/soong/android"
21)
22
23type bpPropertySet struct {
24	properties map[string]interface{}
25	tags       map[string]android.BpPropertyTag
26	order      []string
27}
28
29var _ android.BpPropertySet = (*bpPropertySet)(nil)
30
31func (s *bpPropertySet) init() {
32	s.properties = make(map[string]interface{})
33	s.tags = make(map[string]android.BpPropertyTag)
34}
35
36func (s *bpPropertySet) AddProperty(name string, value interface{}) {
37	if s.properties[name] != nil {
38		panic(fmt.Sprintf("Property %q already exists in property set", name))
39	}
40
41	s.properties[name] = value
42	s.order = append(s.order, name)
43}
44
45func (s *bpPropertySet) AddPropertyWithTag(name string, value interface{}, tag android.BpPropertyTag) {
46	s.AddProperty(name, value)
47	s.tags[name] = tag
48}
49
50func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet {
51	set := newPropertySet()
52	s.AddProperty(name, set)
53	return set
54}
55
56func (s *bpPropertySet) getValue(name string) interface{} {
57	return s.properties[name]
58}
59
60func (s *bpPropertySet) getTag(name string) interface{} {
61	return s.tags[name]
62}
63
64func (s *bpPropertySet) transformContents(transformer bpPropertyTransformer) {
65	var newOrder []string
66	for _, name := range s.order {
67		value := s.properties[name]
68		tag := s.tags[name]
69		var newValue interface{}
70		var newTag android.BpPropertyTag
71		if propertySet, ok := value.(*bpPropertySet); ok {
72			var newPropertySet *bpPropertySet
73			newPropertySet, newTag = transformPropertySet(transformer, name, propertySet, tag)
74			if newPropertySet == nil {
75				newValue = nil
76			} else {
77				newValue = newPropertySet
78			}
79		} else {
80			newValue, newTag = transformer.transformProperty(name, value, tag)
81		}
82
83		if newValue == nil {
84			// Delete the property from the map and exclude it from the new order.
85			delete(s.properties, name)
86		} else {
87			// Update the property in the map and add the name to the new order list.
88			s.properties[name] = newValue
89			s.tags[name] = newTag
90			newOrder = append(newOrder, name)
91		}
92	}
93	s.order = newOrder
94}
95
96func transformPropertySet(transformer bpPropertyTransformer, name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
97	newPropertySet, newTag := transformer.transformPropertySetBeforeContents(name, propertySet, tag)
98	if newPropertySet != nil {
99		newPropertySet.transformContents(transformer)
100
101		newPropertySet, newTag = transformer.transformPropertySetAfterContents(name, newPropertySet, newTag)
102	}
103	return newPropertySet, newTag
104}
105
106func (s *bpPropertySet) setProperty(name string, value interface{}) {
107	if s.properties[name] == nil {
108		s.AddProperty(name, value)
109	} else {
110		s.properties[name] = value
111		s.tags[name] = nil
112	}
113}
114
115func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) {
116	if s.properties[name] != nil {
117		panic("Property %q already exists in property set")
118	}
119
120	// Add the name to the end of the order, to ensure it has necessary capacity
121	// and to handle the case when the position does not exist.
122	s.order = append(s.order, name)
123
124	// Search through the order for the item that matches supplied position. If
125	// found then insert the name of the new property after it.
126	for i, v := range s.order {
127		if v == position {
128			// Copy the items after the one where the new property should be inserted.
129			copy(s.order[i+2:], s.order[i+1:])
130			// Insert the item in the list.
131			s.order[i+1] = name
132		}
133	}
134
135	s.properties[name] = value
136}
137
138type bpModule struct {
139	*bpPropertySet
140	moduleType string
141}
142
143var _ android.BpModule = (*bpModule)(nil)
144
145type bpPropertyTransformer interface {
146	// Transform the property set, returning the new property set/tag to insert back into the
147	// parent property set (or module if this is the top level property set).
148	//
149	// This will be called before transforming the properties in the supplied set.
150	//
151	// The name will be "" for the top level property set.
152	//
153	// Returning (nil, ...) will cause the property set to be removed.
154	transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
155
156	// Transform the property set, returning the new property set/tag to insert back into the
157	// parent property set (or module if this is the top level property set).
158	//
159	// This will be called after transforming the properties in the supplied set.
160	//
161	// The name will be "" for the top level property set.
162	//
163	// Returning (nil, ...) will cause the property set to be removed.
164	transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
165
166	// Transform a property, return the new value/tag to insert back into the property set.
167	//
168	// Returning (nil, ...) will cause the property to be removed.
169	transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag)
170}
171
172// Interface for transforming bpModule objects.
173type bpTransformer interface {
174	// Transform the module, returning the result.
175	//
176	// The method can either create a new module and return that, or modify the supplied module
177	// in place and return that.
178	//
179	// After this returns the transformer is applied to the contents of the returned module.
180	transformModule(module *bpModule) *bpModule
181
182	bpPropertyTransformer
183}
184
185type identityTransformation struct{}
186
187var _ bpTransformer = (*identityTransformation)(nil)
188
189func (t identityTransformation) transformModule(module *bpModule) *bpModule {
190	return module
191}
192
193func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
194	return propertySet, tag
195}
196
197func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
198	return propertySet, tag
199}
200
201func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
202	return value, tag
203}
204
205func (m *bpModule) deepCopy() *bpModule {
206	return m.transform(deepCopyTransformer)
207}
208
209func (m *bpModule) transform(transformer bpTransformer) *bpModule {
210	transformedModule := transformer.transformModule(m)
211	// Copy the contents of the returned property set into the module and then transform that.
212	transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
213	return transformedModule
214}
215
216type deepCopyTransformation struct {
217	identityTransformation
218}
219
220func (t deepCopyTransformation) transformModule(module *bpModule) *bpModule {
221	// Take a shallow copy of the module. Any mutable property values will be copied by the
222	// transformer.
223	moduleCopy := *module
224	return &moduleCopy
225}
226
227func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
228	// Create a shallow copy of the properties map. Any mutable property values will be copied by the
229	// transformer.
230	propertiesCopy := make(map[string]interface{})
231	for propertyName, value := range propertySet.properties {
232		propertiesCopy[propertyName] = value
233	}
234
235	// Ditto for tags map.
236	tagsCopy := make(map[string]android.BpPropertyTag)
237	for propertyName, propertyTag := range propertySet.tags {
238		tagsCopy[propertyName] = propertyTag
239	}
240
241	// Create a new property set.
242	return &bpPropertySet{
243		properties: propertiesCopy,
244		tags:       tagsCopy,
245		order:      append([]string(nil), propertySet.order...),
246	}, tag
247}
248
249func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
250	// Copy string slice, otherwise return value.
251	if values, ok := value.([]string); ok {
252		valuesCopy := make([]string, len(values))
253		copy(valuesCopy, values)
254		return valuesCopy, tag
255	}
256	return value, tag
257}
258
259var deepCopyTransformer bpTransformer = deepCopyTransformation{}
260
261// A .bp file
262type bpFile struct {
263	modules map[string]*bpModule
264	order   []*bpModule
265}
266
267// Add a module.
268//
269// The module must have had its "name" property set to a string value that
270// is unique within this file.
271func (f *bpFile) AddModule(module android.BpModule) {
272	m := module.(*bpModule)
273	if name, ok := m.getValue("name").(string); ok {
274		if f.modules[name] != nil {
275			panic(fmt.Sprintf("Module %q already exists in bp file", name))
276		}
277
278		f.modules[name] = m
279		f.order = append(f.order, m)
280	} else {
281		panic("Module does not have a name property, or it is not a string")
282	}
283}
284
285func (f *bpFile) newModule(moduleType string) *bpModule {
286	return newModule(moduleType)
287}
288
289func newModule(moduleType string) *bpModule {
290	module := &bpModule{
291		moduleType:    moduleType,
292		bpPropertySet: newPropertySet(),
293	}
294	return module
295}
296
297func newPropertySet() *bpPropertySet {
298	set := &bpPropertySet{}
299	set.init()
300	return set
301}
302