blob: d456911e2a50f13961b3603cc02f796d849cd1b0 [file] [log] [blame]
Paul Duffinb645ec82019-11-27 17:43:54 +00001// 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"
Martin Stjernholm191c25f2020-09-10 00:40:37 +010019 "reflect"
20 "strings"
Paul Duffinb645ec82019-11-27 17:43:54 +000021
22 "android/soong/android"
23)
24
25type bpPropertySet struct {
26 properties map[string]interface{}
Paul Duffin5b511a22020-01-15 14:23:52 +000027 tags map[string]android.BpPropertyTag
Paul Duffinb645ec82019-11-27 17:43:54 +000028 order []string
29}
30
31var _ android.BpPropertySet = (*bpPropertySet)(nil)
32
33func (s *bpPropertySet) init() {
34 s.properties = make(map[string]interface{})
Paul Duffin5b511a22020-01-15 14:23:52 +000035 s.tags = make(map[string]android.BpPropertyTag)
Paul Duffinb645ec82019-11-27 17:43:54 +000036}
37
Martin Stjernholm191c25f2020-09-10 00:40:37 +010038// Converts the given value, which is assumed to be a struct, to a
39// bpPropertySet.
40func convertToPropertySet(value reflect.Value) *bpPropertySet {
41 res := newPropertySet()
42 structType := value.Type()
43
44 for i := 0; i < structType.NumField(); i++ {
45 field := structType.Field(i)
46 fieldVal := value.Field(i)
47
48 switch fieldVal.Type().Kind() {
49 case reflect.Ptr:
50 if fieldVal.IsNil() {
51 continue // nil pointer means the property isn't set.
52 }
53 fieldVal = fieldVal.Elem()
54 case reflect.Slice:
55 if fieldVal.IsNil() {
56 continue // Ignore a nil slice (but not one with length zero).
57 }
58 }
59
60 if fieldVal.Type().Kind() == reflect.Struct {
61 fieldVal = fieldVal.Addr() // Avoid struct copy below.
62 }
63 res.AddProperty(strings.ToLower(field.Name), fieldVal.Interface())
64 }
65
66 return res
67}
68
69// Converts the given value to something that can be set in a property.
70func coercePropertyValue(value interface{}) interface{} {
71 val := reflect.ValueOf(value)
72 switch val.Kind() {
73 case reflect.Struct:
74 // convertToPropertySet requires an addressable struct, and this is probably
75 // a mistake.
76 panic(fmt.Sprintf("Value is a struct, not a pointer to one: %v", value))
77 case reflect.Ptr:
78 if _, ok := value.(*bpPropertySet); !ok {
79 derefValue := reflect.Indirect(val)
80 if derefValue.Kind() != reflect.Struct {
81 panic(fmt.Sprintf("A pointer must be to a struct, got: %v", value))
82 }
83 return convertToPropertySet(derefValue)
84 }
85 }
86 return value
87}
88
89// Merges the fields of the given property set into s.
90func (s *bpPropertySet) mergePropertySet(propSet *bpPropertySet) {
91 for _, name := range propSet.order {
92 if tag, ok := propSet.tags[name]; ok {
93 s.AddPropertyWithTag(name, propSet.properties[name], tag)
94 } else {
95 s.AddProperty(name, propSet.properties[name])
96 }
97 }
98}
99
Paul Duffinb645ec82019-11-27 17:43:54 +0000100func (s *bpPropertySet) AddProperty(name string, value interface{}) {
Martin Stjernholm191c25f2020-09-10 00:40:37 +0100101 value = coercePropertyValue(value)
102
103 if propSetValue, ok := value.(*bpPropertySet); ok {
104 if curValue, ok := s.properties[name]; ok {
105 if curSet, ok := curValue.(*bpPropertySet); ok {
106 curSet.mergePropertySet(propSetValue)
107 return
108 }
109 // If the current value isn't a property set we got conflicting types.
110 // Continue down to the check below to complain about it.
111 }
112 }
113
Paul Duffinb645ec82019-11-27 17:43:54 +0000114 if s.properties[name] != nil {
Paul Duffin109c2ad2020-03-02 16:29:11 +0000115 panic(fmt.Sprintf("Property %q already exists in property set", name))
Paul Duffinb645ec82019-11-27 17:43:54 +0000116 }
117
118 s.properties[name] = value
119 s.order = append(s.order, name)
120}
121
Paul Duffin5b511a22020-01-15 14:23:52 +0000122func (s *bpPropertySet) AddPropertyWithTag(name string, value interface{}, tag android.BpPropertyTag) {
123 s.AddProperty(name, value)
124 s.tags[name] = tag
125}
126
Paul Duffinb645ec82019-11-27 17:43:54 +0000127func (s *bpPropertySet) AddPropertySet(name string) android.BpPropertySet {
Martin Stjernholm191c25f2020-09-10 00:40:37 +0100128 s.AddProperty(name, newPropertySet())
129 return s.properties[name].(android.BpPropertySet)
Paul Duffinb645ec82019-11-27 17:43:54 +0000130}
131
132func (s *bpPropertySet) getValue(name string) interface{} {
133 return s.properties[name]
134}
135
Paul Duffin5b511a22020-01-15 14:23:52 +0000136func (s *bpPropertySet) getTag(name string) interface{} {
137 return s.tags[name]
138}
139
Paul Duffin047fdca2020-02-21 16:06:25 +0000140func (s *bpPropertySet) transformContents(transformer bpPropertyTransformer) {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000141 var newOrder []string
142 for _, name := range s.order {
143 value := s.properties[name]
Paul Duffin5b511a22020-01-15 14:23:52 +0000144 tag := s.tags[name]
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000145 var newValue interface{}
Paul Duffin5b511a22020-01-15 14:23:52 +0000146 var newTag android.BpPropertyTag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000147 if propertySet, ok := value.(*bpPropertySet); ok {
Paul Duffin047fdca2020-02-21 16:06:25 +0000148 var newPropertySet *bpPropertySet
149 newPropertySet, newTag = transformPropertySet(transformer, name, propertySet, tag)
150 if newPropertySet == nil {
151 newValue = nil
152 } else {
153 newValue = newPropertySet
154 }
Paul Duffincc72e982020-01-14 15:53:11 +0000155 } else {
Paul Duffin5b511a22020-01-15 14:23:52 +0000156 newValue, newTag = transformer.transformProperty(name, value, tag)
Paul Duffincc72e982020-01-14 15:53:11 +0000157 }
Paul Duffinb645ec82019-11-27 17:43:54 +0000158
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000159 if newValue == nil {
160 // Delete the property from the map and exclude it from the new order.
161 delete(s.properties, name)
162 } else {
163 // Update the property in the map and add the name to the new order list.
164 s.properties[name] = newValue
Paul Duffin5b511a22020-01-15 14:23:52 +0000165 s.tags[name] = newTag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000166 newOrder = append(newOrder, name)
167 }
Paul Duffinb645ec82019-11-27 17:43:54 +0000168 }
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000169 s.order = newOrder
Paul Duffinb645ec82019-11-27 17:43:54 +0000170}
171
Paul Duffin047fdca2020-02-21 16:06:25 +0000172func transformPropertySet(transformer bpPropertyTransformer, name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
Paul Duffin180a0062020-02-21 16:06:25 +0000173 newPropertySet, newTag := transformer.transformPropertySetBeforeContents(name, propertySet, tag)
Paul Duffin047fdca2020-02-21 16:06:25 +0000174 if newPropertySet != nil {
175 newPropertySet.transformContents(transformer)
Paul Duffin180a0062020-02-21 16:06:25 +0000176
177 newPropertySet, newTag = transformer.transformPropertySetAfterContents(name, newPropertySet, newTag)
Paul Duffin047fdca2020-02-21 16:06:25 +0000178 }
179 return newPropertySet, newTag
180}
181
Paul Duffinb645ec82019-11-27 17:43:54 +0000182func (s *bpPropertySet) setProperty(name string, value interface{}) {
183 if s.properties[name] == nil {
184 s.AddProperty(name, value)
185 } else {
186 s.properties[name] = value
Paul Duffin5b511a22020-01-15 14:23:52 +0000187 s.tags[name] = nil
Paul Duffinb645ec82019-11-27 17:43:54 +0000188 }
189}
190
Paul Duffin83ad9562021-05-10 23:49:04 +0100191func (s *bpPropertySet) removeProperty(name string) {
192 delete(s.properties, name)
193 delete(s.tags, name)
194 _, s.order = android.RemoveFromList(name, s.order)
195}
196
Paul Duffinb645ec82019-11-27 17:43:54 +0000197func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) {
198 if s.properties[name] != nil {
199 panic("Property %q already exists in property set")
200 }
201
202 // Add the name to the end of the order, to ensure it has necessary capacity
203 // and to handle the case when the position does not exist.
204 s.order = append(s.order, name)
205
206 // Search through the order for the item that matches supplied position. If
207 // found then insert the name of the new property after it.
208 for i, v := range s.order {
209 if v == position {
210 // Copy the items after the one where the new property should be inserted.
211 copy(s.order[i+2:], s.order[i+1:])
212 // Insert the item in the list.
213 s.order[i+1] = name
214 }
215 }
216
217 s.properties[name] = value
218}
219
220type bpModule struct {
Paul Duffincc72e982020-01-14 15:53:11 +0000221 *bpPropertySet
Paul Duffinb645ec82019-11-27 17:43:54 +0000222 moduleType string
223}
224
225var _ android.BpModule = (*bpModule)(nil)
226
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000227type bpPropertyTransformer interface {
Paul Duffin5b511a22020-01-15 14:23:52 +0000228 // Transform the property set, returning the new property set/tag to insert back into the
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000229 // parent property set (or module if this is the top level property set).
230 //
231 // This will be called before transforming the properties in the supplied set.
232 //
233 // The name will be "" for the top level property set.
234 //
Paul Duffin5b511a22020-01-15 14:23:52 +0000235 // Returning (nil, ...) will cause the property set to be removed.
Paul Duffin180a0062020-02-21 16:06:25 +0000236 transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
237
238 // Transform the property set, returning the new property set/tag to insert back into the
239 // parent property set (or module if this is the top level property set).
240 //
241 // This will be called after transforming the properties in the supplied set.
242 //
243 // The name will be "" for the top level property set.
244 //
245 // Returning (nil, ...) will cause the property set to be removed.
246 transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000247
Paul Duffin5b511a22020-01-15 14:23:52 +0000248 // Transform a property, return the new value/tag to insert back into the property set.
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000249 //
Paul Duffin5b511a22020-01-15 14:23:52 +0000250 // Returning (nil, ...) will cause the property to be removed.
251 transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag)
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000252}
253
254// Interface for transforming bpModule objects.
255type bpTransformer interface {
256 // Transform the module, returning the result.
257 //
258 // The method can either create a new module and return that, or modify the supplied module
259 // in place and return that.
260 //
261 // After this returns the transformer is applied to the contents of the returned module.
262 transformModule(module *bpModule) *bpModule
263
264 bpPropertyTransformer
265}
266
267type identityTransformation struct{}
268
269var _ bpTransformer = (*identityTransformation)(nil)
270
271func (t identityTransformation) transformModule(module *bpModule) *bpModule {
272 return module
273}
274
Paul Duffin180a0062020-02-21 16:06:25 +0000275func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
276 return propertySet, tag
277}
278
279func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
Paul Duffin5b511a22020-01-15 14:23:52 +0000280 return propertySet, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000281}
282
Paul Duffin5b511a22020-01-15 14:23:52 +0000283func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
284 return value, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000285}
286
Paul Duffincc72e982020-01-14 15:53:11 +0000287func (m *bpModule) deepCopy() *bpModule {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000288 return m.transform(deepCopyTransformer)
289}
290
291func (m *bpModule) transform(transformer bpTransformer) *bpModule {
292 transformedModule := transformer.transformModule(m)
293 // Copy the contents of the returned property set into the module and then transform that.
Paul Duffin047fdca2020-02-21 16:06:25 +0000294 transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000295 return transformedModule
296}
297
Paul Duffin180a0062020-02-21 16:06:25 +0000298type deepCopyTransformation struct {
299 identityTransformation
300}
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000301
302func (t deepCopyTransformation) transformModule(module *bpModule) *bpModule {
303 // Take a shallow copy of the module. Any mutable property values will be copied by the
304 // transformer.
305 moduleCopy := *module
306 return &moduleCopy
307}
308
Paul Duffin180a0062020-02-21 16:06:25 +0000309func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000310 // Create a shallow copy of the properties map. Any mutable property values will be copied by the
311 // transformer.
312 propertiesCopy := make(map[string]interface{})
Paul Duffin5b511a22020-01-15 14:23:52 +0000313 for propertyName, value := range propertySet.properties {
314 propertiesCopy[propertyName] = value
315 }
316
317 // Ditto for tags map.
318 tagsCopy := make(map[string]android.BpPropertyTag)
319 for propertyName, propertyTag := range propertySet.tags {
320 tagsCopy[propertyName] = propertyTag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000321 }
322
323 // Create a new property set.
324 return &bpPropertySet{
325 properties: propertiesCopy,
Paul Duffin5b511a22020-01-15 14:23:52 +0000326 tags: tagsCopy,
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000327 order: append([]string(nil), propertySet.order...),
Paul Duffin5b511a22020-01-15 14:23:52 +0000328 }, tag
Paul Duffinb645ec82019-11-27 17:43:54 +0000329}
330
Paul Duffin5b511a22020-01-15 14:23:52 +0000331func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000332 // Copy string slice, otherwise return value.
333 if values, ok := value.([]string); ok {
334 valuesCopy := make([]string, len(values))
335 copy(valuesCopy, values)
Paul Duffin5b511a22020-01-15 14:23:52 +0000336 return valuesCopy, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000337 }
Paul Duffin5b511a22020-01-15 14:23:52 +0000338 return value, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000339}
340
341var deepCopyTransformer bpTransformer = deepCopyTransformation{}
342
Paul Duffinb645ec82019-11-27 17:43:54 +0000343// A .bp file
344type bpFile struct {
345 modules map[string]*bpModule
346 order []*bpModule
347}
348
349// Add a module.
350//
351// The module must have had its "name" property set to a string value that
352// is unique within this file.
353func (f *bpFile) AddModule(module android.BpModule) {
354 m := module.(*bpModule)
355 if name, ok := m.getValue("name").(string); ok {
356 if f.modules[name] != nil {
357 panic(fmt.Sprintf("Module %q already exists in bp file", name))
358 }
359
360 f.modules[name] = m
361 f.order = append(f.order, m)
362 } else {
363 panic("Module does not have a name property, or it is not a string")
364 }
365}
366
367func (f *bpFile) newModule(moduleType string) *bpModule {
Paul Duffin047fdca2020-02-21 16:06:25 +0000368 return newModule(moduleType)
369}
370
371func newModule(moduleType string) *bpModule {
Paul Duffinb645ec82019-11-27 17:43:54 +0000372 module := &bpModule{
Paul Duffincc72e982020-01-14 15:53:11 +0000373 moduleType: moduleType,
Paul Duffin047fdca2020-02-21 16:06:25 +0000374 bpPropertySet: newPropertySet(),
Paul Duffinb645ec82019-11-27 17:43:54 +0000375 }
Paul Duffinb645ec82019-11-27 17:43:54 +0000376 return module
377}
Paul Duffin047fdca2020-02-21 16:06:25 +0000378
379func newPropertySet() *bpPropertySet {
380 set := &bpPropertySet{}
381 set.init()
382 return set
383}