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