blob: 2d6063d79adaed3963b50d4d75e2e037436b6210 [file] [log] [blame]
Colin Cross9d34f352019-11-22 16:03:51 -08001// Copyright 2020 Google Inc. All rights reserved.
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 soongconfig
16
17import (
18 "fmt"
19 "io"
20 "reflect"
21 "sort"
22 "strings"
23
24 "github.com/google/blueprint"
25 "github.com/google/blueprint/parser"
26 "github.com/google/blueprint/proptools"
27)
28
29var soongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
30
31// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
32// result so each file is only parsed once.
33func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
34 scope := parser.NewScope(nil)
35 file, errs := parser.ParseAndEval(from, r, scope)
36
37 if len(errs) > 0 {
38 return nil, errs
39 }
40
41 mtDef := &SoongConfigDefinition{
42 ModuleTypes: make(map[string]*ModuleType),
43 variables: make(map[string]soongConfigVariable),
44 }
45
46 for _, def := range file.Defs {
47 switch def := def.(type) {
48 case *parser.Module:
49 newErrs := processImportModuleDef(mtDef, def)
50
51 if len(newErrs) > 0 {
52 errs = append(errs, newErrs...)
53 }
54
55 case *parser.Assignment:
56 // Already handled via Scope object
57 default:
58 panic("unknown definition type")
59 }
60 }
61
62 if len(errs) > 0 {
63 return nil, errs
64 }
65
66 for name, moduleType := range mtDef.ModuleTypes {
67 for _, varName := range moduleType.variableNames {
68 if v, ok := mtDef.variables[varName]; ok {
69 moduleType.Variables = append(moduleType.Variables, v)
70 } else {
71 return nil, []error{
72 fmt.Errorf("unknown variable %q in module type %q", varName, name),
73 }
74 }
75 }
76 }
77
78 return mtDef, nil
79}
80
81func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
82 switch def.Type {
83 case "soong_config_module_type":
84 return processModuleTypeDef(v, def)
85 case "soong_config_string_variable":
86 return processStringVariableDef(v, def)
87 case "soong_config_bool_variable":
88 return processBoolVariableDef(v, def)
89 default:
90 // Unknown module types will be handled when the file is parsed as a normal
91 // Android.bp file.
92 }
93
94 return nil
95}
96
97type ModuleTypeProperties struct {
98 // the name of the new module type. Unlike most modules, this name does not need to be unique,
99 // although only one module type with any name will be importable into an Android.bp file.
100 Name string
101
102 // the module type that this module type will extend.
103 Module_type string
104
105 // the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
106 // configuration variables from.
107 Config_namespace string
108
109 // the list of SOONG_CONFIG variables that this module type will read
110 Variables []string
111
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700112 // the list of boolean SOONG_CONFIG variables that this module type will read
113 Bool_variables []string
114
Colin Cross9d34f352019-11-22 16:03:51 -0800115 // the list of properties that this module type will extend.
116 Properties []string
117}
118
119func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
120
121 props := &ModuleTypeProperties{}
122
123 _, errs = proptools.UnpackProperties(def.Properties, props)
124 if len(errs) > 0 {
125 return errs
126 }
127
128 if props.Name == "" {
129 errs = append(errs, fmt.Errorf("name property must be set"))
130 }
131
132 if props.Config_namespace == "" {
133 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
134 }
135
136 if props.Module_type == "" {
137 errs = append(errs, fmt.Errorf("module_type property must be set"))
138 }
139
140 if len(errs) > 0 {
141 return errs
142 }
143
144 mt := &ModuleType{
145 affectableProperties: props.Properties,
146 ConfigNamespace: props.Config_namespace,
147 BaseModuleType: props.Module_type,
148 variableNames: props.Variables,
149 }
150 v.ModuleTypes[props.Name] = mt
151
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700152 for _, name := range props.Bool_variables {
153 if name == "" {
154 return []error{fmt.Errorf("bool_variable name must not be blank")}
155 }
156
157 mt.Variables = append(mt.Variables, &boolVariable{
158 baseVariable: baseVariable{
159 variable: name,
160 },
161 })
162 }
163
Colin Cross9d34f352019-11-22 16:03:51 -0800164 return nil
165}
166
167type VariableProperties struct {
168 Name string
169}
170
171type StringVariableProperties struct {
172 Values []string
173}
174
175func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
176 stringProps := &StringVariableProperties{}
177
178 base, errs := processVariableDef(def, stringProps)
179 if len(errs) > 0 {
180 return errs
181 }
182
183 if len(stringProps.Values) == 0 {
184 return []error{fmt.Errorf("values property must be set")}
185 }
186
187 v.variables[base.variable] = &stringVariable{
188 baseVariable: base,
189 values: CanonicalizeToProperties(stringProps.Values),
190 }
191
192 return nil
193}
194
195func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
196 base, errs := processVariableDef(def)
197 if len(errs) > 0 {
198 return errs
199 }
200
201 v.variables[base.variable] = &boolVariable{
202 baseVariable: base,
203 }
204
205 return nil
206}
207
208func processVariableDef(def *parser.Module,
209 extraProps ...interface{}) (cond baseVariable, errs []error) {
210
211 props := &VariableProperties{}
212
213 allProps := append([]interface{}{props}, extraProps...)
214
215 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
216 if len(errs) > 0 {
217 return baseVariable{}, errs
218 }
219
220 if props.Name == "" {
221 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
222 }
223
224 return baseVariable{
225 variable: props.Name,
226 }, nil
227}
228
229type SoongConfigDefinition struct {
230 ModuleTypes map[string]*ModuleType
231
232 variables map[string]soongConfigVariable
233}
234
235// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
236// property layout for the Soong config variables, with each possible value an interface{} that
237// contains a nil pointer to another newly constructed type that contains the affectable properties.
238// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
239//
240// For example, the acme_cc_defaults example above would
241// produce a reflect.Value whose type is:
242// *struct {
243// Soong_config_variables struct {
244// Board struct {
245// Soc_a interface{}
246// Soc_b interface{}
247// }
248// }
249// }
250// And whose value is:
251// &{
252// Soong_config_variables: {
253// Board: {
254// Soc_a: (*struct{ Cflags []string })(nil),
255// Soc_b: (*struct{ Cflags []string })(nil),
256// },
257// },
258// }
259func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
260 var fields []reflect.StructField
261
262 _, factoryProps := factory()
263 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
264 if affectablePropertiesType == nil {
265 return reflect.Value{}
266 }
267
268 for _, c := range moduleType.Variables {
269 fields = append(fields, reflect.StructField{
270 Name: proptools.FieldNameForProperty(c.variableProperty()),
271 Type: c.variableValuesType(),
272 })
273 }
274
275 typ := reflect.StructOf([]reflect.StructField{{
276 Name: soongConfigProperty,
277 Type: reflect.StructOf(fields),
278 }})
279
280 props := reflect.New(typ)
281 structConditions := props.Elem().FieldByName(soongConfigProperty)
282
283 for i, c := range moduleType.Variables {
284 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
285 }
286
287 return props
288}
289
290// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
291// that exists in factoryProps.
292func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
293 affectableProperties = append([]string(nil), affectableProperties...)
294 sort.Strings(affectableProperties)
295
296 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
297 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
298 var fields []reflect.StructField
299
300 for len(affectableProperties) > 0 {
301 p := affectableProperties[0]
302 if !strings.HasPrefix(affectableProperties[0], prefix) {
303 break
304 }
305 affectableProperties = affectableProperties[1:]
306
307 nestedProperty := strings.TrimPrefix(p, prefix)
308 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
309 var nestedType reflect.Type
310 nestedPrefix := nestedProperty[:i+1]
311
312 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
313
314 if nestedType != nil {
315 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
316
317 fields = append(fields, reflect.StructField{
318 Name: nestedFieldName,
319 Type: nestedType,
320 })
321 }
322 } else {
323 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
324 if typ != nil {
325 fields = append(fields, reflect.StructField{
326 Name: proptools.FieldNameForProperty(nestedProperty),
327 Type: typ,
328 })
329 }
330 }
331 }
332
333 var typ reflect.Type
334 if len(fields) > 0 {
335 typ = reflect.StructOf(fields)
336 }
337 return affectableProperties, typ
338 }
339
340 affectableProperties, typ := recurse("", affectableProperties)
341 if len(affectableProperties) > 0 {
342 panic(fmt.Errorf("didn't handle all affectable properties"))
343 }
344
345 if typ != nil {
346 return reflect.PtrTo(typ)
347 }
348
349 return nil
350}
351
352func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
353 for _, ps := range psList {
354 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
355 return typ
356 }
357 }
358
359 return nil
360}
361
362func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
363 v := reflect.ValueOf(ps)
364 for len(property) > 0 {
365 if !v.IsValid() {
366 return nil
367 }
368
369 if v.Kind() == reflect.Interface {
370 if v.IsNil() {
371 return nil
372 } else {
373 v = v.Elem()
374 }
375 }
376
377 if v.Kind() == reflect.Ptr {
378 if v.IsNil() {
379 v = reflect.Zero(v.Type().Elem())
380 } else {
381 v = v.Elem()
382 }
383 }
384
385 if v.Kind() != reflect.Struct {
386 return nil
387 }
388
389 if index := strings.IndexRune(property, '.'); index >= 0 {
390 prefix := property[:index]
391 property = property[index+1:]
392
393 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
394 } else {
395 f := v.FieldByName(proptools.FieldNameForProperty(property))
396 if !f.IsValid() {
397 return nil
398 }
399 return f.Type()
400 }
401 }
402 return nil
403}
404
405// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
406// based on SoongConfig values.
407func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) []interface{} {
408 var ret []interface{}
409 props = props.Elem().FieldByName(soongConfigProperty)
410 for i, c := range moduleType.Variables {
411 if ps := c.PropertiesToApply(config, props.Field(i)); ps != nil {
412 ret = append(ret, ps)
413 }
414 }
415 return ret
416}
417
418type ModuleType struct {
419 BaseModuleType string
420 ConfigNamespace string
421 Variables []soongConfigVariable
422
423 affectableProperties []string
424 variableNames []string
425}
426
427type soongConfigVariable interface {
428 // variableProperty returns the name of the variable.
429 variableProperty() string
430
431 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
432 variableValuesType() reflect.Type
433
434 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
435 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
436 // the zero value of the affectable properties type.
437 initializeProperties(v reflect.Value, typ reflect.Type)
438
439 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
440 // to the module.
441 PropertiesToApply(config SoongConfig, values reflect.Value) interface{}
442}
443
444type baseVariable struct {
445 variable string
446}
447
448func (c *baseVariable) variableProperty() string {
449 return CanonicalizeToProperty(c.variable)
450}
451
452type stringVariable struct {
453 baseVariable
454 values []string
455}
456
457func (s *stringVariable) variableValuesType() reflect.Type {
458 var fields []reflect.StructField
459
460 for _, v := range s.values {
461 fields = append(fields, reflect.StructField{
462 Name: proptools.FieldNameForProperty(v),
463 Type: emptyInterfaceType,
464 })
465 }
466
467 return reflect.StructOf(fields)
468}
469
470func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
471 for i := range s.values {
472 v.Field(i).Set(reflect.Zero(typ))
473 }
474}
475
476func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
477 for j, v := range s.values {
478 if config.String(s.variable) == v {
479 return values.Field(j).Interface()
480 }
481 }
482
483 return nil
484}
485
486type boolVariable struct {
487 baseVariable
488}
489
490func (b boolVariable) variableValuesType() reflect.Type {
491 return emptyInterfaceType
492}
493
494func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
495 v.Set(reflect.Zero(typ))
496}
497
498func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) interface{} {
499 if config.Bool(b.variable) {
500 return values.Interface()
501 }
502
503 return nil
504}
505
506func CanonicalizeToProperty(v string) string {
507 return strings.Map(func(r rune) rune {
508 switch {
509 case r >= 'A' && r <= 'Z',
510 r >= 'a' && r <= 'z',
511 r >= '0' && r <= '9',
512 r == '_':
513 return r
514 default:
515 return '_'
516 }
517 }, v)
518}
519
520func CanonicalizeToProperties(values []string) []string {
521 ret := make([]string, len(values))
522 for i, v := range values {
523 ret[i] = CanonicalizeToProperty(v)
524 }
525 return ret
526}
527
528type emptyInterfaceStruct struct {
529 i interface{}
530}
531
532var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type