blob: 11ec8c6bd94f2edb2a48bf96c15a0ff1e873dc23 [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
191func (s *bpPropertySet) insertAfter(position string, name string, value interface{}) {
192 if s.properties[name] != nil {
193 panic("Property %q already exists in property set")
194 }
195
196 // Add the name to the end of the order, to ensure it has necessary capacity
197 // and to handle the case when the position does not exist.
198 s.order = append(s.order, name)
199
200 // Search through the order for the item that matches supplied position. If
201 // found then insert the name of the new property after it.
202 for i, v := range s.order {
203 if v == position {
204 // Copy the items after the one where the new property should be inserted.
205 copy(s.order[i+2:], s.order[i+1:])
206 // Insert the item in the list.
207 s.order[i+1] = name
208 }
209 }
210
211 s.properties[name] = value
212}
213
214type bpModule struct {
Paul Duffincc72e982020-01-14 15:53:11 +0000215 *bpPropertySet
Paul Duffinb645ec82019-11-27 17:43:54 +0000216 moduleType string
217}
218
219var _ android.BpModule = (*bpModule)(nil)
220
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000221type bpPropertyTransformer interface {
Paul Duffin5b511a22020-01-15 14:23:52 +0000222 // Transform the property set, returning the new property set/tag to insert back into the
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000223 // parent property set (or module if this is the top level property set).
224 //
225 // This will be called before transforming the properties in the supplied set.
226 //
227 // The name will be "" for the top level property set.
228 //
Paul Duffin5b511a22020-01-15 14:23:52 +0000229 // Returning (nil, ...) will cause the property set to be removed.
Paul Duffin180a0062020-02-21 16:06:25 +0000230 transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
231
232 // Transform the property set, returning the new property set/tag to insert back into the
233 // parent property set (or module if this is the top level property set).
234 //
235 // This will be called after transforming the properties in the supplied set.
236 //
237 // The name will be "" for the top level property set.
238 //
239 // Returning (nil, ...) will cause the property set to be removed.
240 transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag)
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000241
Paul Duffin5b511a22020-01-15 14:23:52 +0000242 // Transform a property, return the new value/tag to insert back into the property set.
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000243 //
Paul Duffin5b511a22020-01-15 14:23:52 +0000244 // Returning (nil, ...) will cause the property to be removed.
245 transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag)
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000246}
247
248// Interface for transforming bpModule objects.
249type bpTransformer interface {
250 // Transform the module, returning the result.
251 //
252 // The method can either create a new module and return that, or modify the supplied module
253 // in place and return that.
254 //
255 // After this returns the transformer is applied to the contents of the returned module.
256 transformModule(module *bpModule) *bpModule
257
258 bpPropertyTransformer
259}
260
261type identityTransformation struct{}
262
263var _ bpTransformer = (*identityTransformation)(nil)
264
265func (t identityTransformation) transformModule(module *bpModule) *bpModule {
266 return module
267}
268
Paul Duffin180a0062020-02-21 16:06:25 +0000269func (t identityTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
270 return propertySet, tag
271}
272
273func (t identityTransformation) transformPropertySetAfterContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
Paul Duffin5b511a22020-01-15 14:23:52 +0000274 return propertySet, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000275}
276
Paul Duffin5b511a22020-01-15 14:23:52 +0000277func (t identityTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
278 return value, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000279}
280
Paul Duffincc72e982020-01-14 15:53:11 +0000281func (m *bpModule) deepCopy() *bpModule {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000282 return m.transform(deepCopyTransformer)
283}
284
285func (m *bpModule) transform(transformer bpTransformer) *bpModule {
286 transformedModule := transformer.transformModule(m)
287 // Copy the contents of the returned property set into the module and then transform that.
Paul Duffin047fdca2020-02-21 16:06:25 +0000288 transformedModule.bpPropertySet, _ = transformPropertySet(transformer, "", transformedModule.bpPropertySet, nil)
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000289 return transformedModule
290}
291
Paul Duffin180a0062020-02-21 16:06:25 +0000292type deepCopyTransformation struct {
293 identityTransformation
294}
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000295
296func (t deepCopyTransformation) transformModule(module *bpModule) *bpModule {
297 // Take a shallow copy of the module. Any mutable property values will be copied by the
298 // transformer.
299 moduleCopy := *module
300 return &moduleCopy
301}
302
Paul Duffin180a0062020-02-21 16:06:25 +0000303func (t deepCopyTransformation) transformPropertySetBeforeContents(name string, propertySet *bpPropertySet, tag android.BpPropertyTag) (*bpPropertySet, android.BpPropertyTag) {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000304 // Create a shallow copy of the properties map. Any mutable property values will be copied by the
305 // transformer.
306 propertiesCopy := make(map[string]interface{})
Paul Duffin5b511a22020-01-15 14:23:52 +0000307 for propertyName, value := range propertySet.properties {
308 propertiesCopy[propertyName] = value
309 }
310
311 // Ditto for tags map.
312 tagsCopy := make(map[string]android.BpPropertyTag)
313 for propertyName, propertyTag := range propertySet.tags {
314 tagsCopy[propertyName] = propertyTag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000315 }
316
317 // Create a new property set.
318 return &bpPropertySet{
319 properties: propertiesCopy,
Paul Duffin5b511a22020-01-15 14:23:52 +0000320 tags: tagsCopy,
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000321 order: append([]string(nil), propertySet.order...),
Paul Duffin5b511a22020-01-15 14:23:52 +0000322 }, tag
Paul Duffinb645ec82019-11-27 17:43:54 +0000323}
324
Paul Duffin5b511a22020-01-15 14:23:52 +0000325func (t deepCopyTransformation) transformProperty(name string, value interface{}, tag android.BpPropertyTag) (interface{}, android.BpPropertyTag) {
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000326 // Copy string slice, otherwise return value.
327 if values, ok := value.([]string); ok {
328 valuesCopy := make([]string, len(values))
329 copy(valuesCopy, values)
Paul Duffin5b511a22020-01-15 14:23:52 +0000330 return valuesCopy, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000331 }
Paul Duffin5b511a22020-01-15 14:23:52 +0000332 return value, tag
Paul Duffinb4d9c1f2020-01-15 11:52:11 +0000333}
334
335var deepCopyTransformer bpTransformer = deepCopyTransformation{}
336
Paul Duffinb645ec82019-11-27 17:43:54 +0000337// A .bp file
338type bpFile struct {
339 modules map[string]*bpModule
340 order []*bpModule
341}
342
343// Add a module.
344//
345// The module must have had its "name" property set to a string value that
346// is unique within this file.
347func (f *bpFile) AddModule(module android.BpModule) {
348 m := module.(*bpModule)
349 if name, ok := m.getValue("name").(string); ok {
350 if f.modules[name] != nil {
351 panic(fmt.Sprintf("Module %q already exists in bp file", name))
352 }
353
354 f.modules[name] = m
355 f.order = append(f.order, m)
356 } else {
357 panic("Module does not have a name property, or it is not a string")
358 }
359}
360
361func (f *bpFile) newModule(moduleType string) *bpModule {
Paul Duffin047fdca2020-02-21 16:06:25 +0000362 return newModule(moduleType)
363}
364
365func newModule(moduleType string) *bpModule {
Paul Duffinb645ec82019-11-27 17:43:54 +0000366 module := &bpModule{
Paul Duffincc72e982020-01-14 15:53:11 +0000367 moduleType: moduleType,
Paul Duffin047fdca2020-02-21 16:06:25 +0000368 bpPropertySet: newPropertySet(),
Paul Duffinb645ec82019-11-27 17:43:54 +0000369 }
Paul Duffinb645ec82019-11-27 17:43:54 +0000370 return module
371}
Paul Duffin047fdca2020-02-21 16:06:25 +0000372
373func newPropertySet() *bpPropertySet {
374 set := &bpPropertySet{}
375 set.init()
376 return set
377}