blob: 142a81387697912ace76369ccdfe4f8b13401159 [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
Dan Willemsenb0935db2020-03-23 19:42:18 -0700115 // the list of SOONG_CONFIG variables that this module type will read. The value will be
116 // inserted into the properties with %s substitution.
117 Value_variables []string
118
Colin Cross9d34f352019-11-22 16:03:51 -0800119 // the list of properties that this module type will extend.
120 Properties []string
121}
122
123func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
124
125 props := &ModuleTypeProperties{}
126
127 _, errs = proptools.UnpackProperties(def.Properties, props)
128 if len(errs) > 0 {
129 return errs
130 }
131
132 if props.Name == "" {
133 errs = append(errs, fmt.Errorf("name property must be set"))
134 }
135
136 if props.Config_namespace == "" {
137 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
138 }
139
140 if props.Module_type == "" {
141 errs = append(errs, fmt.Errorf("module_type property must be set"))
142 }
143
144 if len(errs) > 0 {
145 return errs
146 }
147
148 mt := &ModuleType{
149 affectableProperties: props.Properties,
150 ConfigNamespace: props.Config_namespace,
151 BaseModuleType: props.Module_type,
152 variableNames: props.Variables,
153 }
154 v.ModuleTypes[props.Name] = mt
155
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700156 for _, name := range props.Bool_variables {
157 if name == "" {
158 return []error{fmt.Errorf("bool_variable name must not be blank")}
159 }
160
161 mt.Variables = append(mt.Variables, &boolVariable{
162 baseVariable: baseVariable{
163 variable: name,
164 },
165 })
166 }
167
Dan Willemsenb0935db2020-03-23 19:42:18 -0700168 for _, name := range props.Value_variables {
169 if name == "" {
170 return []error{fmt.Errorf("value_variables entry must not be blank")}
171 }
172
173 mt.Variables = append(mt.Variables, &valueVariable{
174 baseVariable: baseVariable{
175 variable: name,
176 },
177 })
178 }
179
Colin Cross9d34f352019-11-22 16:03:51 -0800180 return nil
181}
182
183type VariableProperties struct {
184 Name string
185}
186
187type StringVariableProperties struct {
188 Values []string
189}
190
191func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
192 stringProps := &StringVariableProperties{}
193
194 base, errs := processVariableDef(def, stringProps)
195 if len(errs) > 0 {
196 return errs
197 }
198
199 if len(stringProps.Values) == 0 {
200 return []error{fmt.Errorf("values property must be set")}
201 }
202
203 v.variables[base.variable] = &stringVariable{
204 baseVariable: base,
205 values: CanonicalizeToProperties(stringProps.Values),
206 }
207
208 return nil
209}
210
211func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
212 base, errs := processVariableDef(def)
213 if len(errs) > 0 {
214 return errs
215 }
216
217 v.variables[base.variable] = &boolVariable{
218 baseVariable: base,
219 }
220
221 return nil
222}
223
224func processVariableDef(def *parser.Module,
225 extraProps ...interface{}) (cond baseVariable, errs []error) {
226
227 props := &VariableProperties{}
228
229 allProps := append([]interface{}{props}, extraProps...)
230
231 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
232 if len(errs) > 0 {
233 return baseVariable{}, errs
234 }
235
236 if props.Name == "" {
237 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
238 }
239
240 return baseVariable{
241 variable: props.Name,
242 }, nil
243}
244
245type SoongConfigDefinition struct {
246 ModuleTypes map[string]*ModuleType
247
248 variables map[string]soongConfigVariable
249}
250
251// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
252// property layout for the Soong config variables, with each possible value an interface{} that
253// contains a nil pointer to another newly constructed type that contains the affectable properties.
254// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
255//
256// For example, the acme_cc_defaults example above would
257// produce a reflect.Value whose type is:
258// *struct {
259// Soong_config_variables struct {
260// Board struct {
261// Soc_a interface{}
262// Soc_b interface{}
263// }
264// }
265// }
266// And whose value is:
267// &{
268// Soong_config_variables: {
269// Board: {
270// Soc_a: (*struct{ Cflags []string })(nil),
271// Soc_b: (*struct{ Cflags []string })(nil),
272// },
273// },
274// }
275func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
276 var fields []reflect.StructField
277
278 _, factoryProps := factory()
279 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
280 if affectablePropertiesType == nil {
281 return reflect.Value{}
282 }
283
284 for _, c := range moduleType.Variables {
285 fields = append(fields, reflect.StructField{
286 Name: proptools.FieldNameForProperty(c.variableProperty()),
287 Type: c.variableValuesType(),
288 })
289 }
290
291 typ := reflect.StructOf([]reflect.StructField{{
292 Name: soongConfigProperty,
293 Type: reflect.StructOf(fields),
294 }})
295
296 props := reflect.New(typ)
297 structConditions := props.Elem().FieldByName(soongConfigProperty)
298
299 for i, c := range moduleType.Variables {
300 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
301 }
302
303 return props
304}
305
306// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
307// that exists in factoryProps.
308func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
309 affectableProperties = append([]string(nil), affectableProperties...)
310 sort.Strings(affectableProperties)
311
312 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
313 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
314 var fields []reflect.StructField
315
316 for len(affectableProperties) > 0 {
317 p := affectableProperties[0]
318 if !strings.HasPrefix(affectableProperties[0], prefix) {
319 break
320 }
321 affectableProperties = affectableProperties[1:]
322
323 nestedProperty := strings.TrimPrefix(p, prefix)
324 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
325 var nestedType reflect.Type
326 nestedPrefix := nestedProperty[:i+1]
327
328 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
329
330 if nestedType != nil {
331 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
332
333 fields = append(fields, reflect.StructField{
334 Name: nestedFieldName,
335 Type: nestedType,
336 })
337 }
338 } else {
339 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
340 if typ != nil {
341 fields = append(fields, reflect.StructField{
342 Name: proptools.FieldNameForProperty(nestedProperty),
343 Type: typ,
344 })
345 }
346 }
347 }
348
349 var typ reflect.Type
350 if len(fields) > 0 {
351 typ = reflect.StructOf(fields)
352 }
353 return affectableProperties, typ
354 }
355
356 affectableProperties, typ := recurse("", affectableProperties)
357 if len(affectableProperties) > 0 {
358 panic(fmt.Errorf("didn't handle all affectable properties"))
359 }
360
361 if typ != nil {
362 return reflect.PtrTo(typ)
363 }
364
365 return nil
366}
367
368func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
369 for _, ps := range psList {
370 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
371 return typ
372 }
373 }
374
375 return nil
376}
377
378func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
379 v := reflect.ValueOf(ps)
380 for len(property) > 0 {
381 if !v.IsValid() {
382 return nil
383 }
384
385 if v.Kind() == reflect.Interface {
386 if v.IsNil() {
387 return nil
388 } else {
389 v = v.Elem()
390 }
391 }
392
393 if v.Kind() == reflect.Ptr {
394 if v.IsNil() {
395 v = reflect.Zero(v.Type().Elem())
396 } else {
397 v = v.Elem()
398 }
399 }
400
401 if v.Kind() != reflect.Struct {
402 return nil
403 }
404
405 if index := strings.IndexRune(property, '.'); index >= 0 {
406 prefix := property[:index]
407 property = property[index+1:]
408
409 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
410 } else {
411 f := v.FieldByName(proptools.FieldNameForProperty(property))
412 if !f.IsValid() {
413 return nil
414 }
415 return f.Type()
416 }
417 }
418 return nil
419}
420
421// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
422// based on SoongConfig values.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700423func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800424 var ret []interface{}
425 props = props.Elem().FieldByName(soongConfigProperty)
426 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700427 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
428 return nil, err
429 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800430 ret = append(ret, ps)
431 }
432 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700433 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800434}
435
436type ModuleType struct {
437 BaseModuleType string
438 ConfigNamespace string
439 Variables []soongConfigVariable
440
441 affectableProperties []string
442 variableNames []string
443}
444
445type soongConfigVariable interface {
446 // variableProperty returns the name of the variable.
447 variableProperty() string
448
449 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
450 variableValuesType() reflect.Type
451
452 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
453 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
454 // the zero value of the affectable properties type.
455 initializeProperties(v reflect.Value, typ reflect.Type)
456
457 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
458 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700459 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800460}
461
462type baseVariable struct {
463 variable string
464}
465
466func (c *baseVariable) variableProperty() string {
467 return CanonicalizeToProperty(c.variable)
468}
469
470type stringVariable struct {
471 baseVariable
472 values []string
473}
474
475func (s *stringVariable) variableValuesType() reflect.Type {
476 var fields []reflect.StructField
477
478 for _, v := range s.values {
479 fields = append(fields, reflect.StructField{
480 Name: proptools.FieldNameForProperty(v),
481 Type: emptyInterfaceType,
482 })
483 }
484
485 return reflect.StructOf(fields)
486}
487
488func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
489 for i := range s.values {
490 v.Field(i).Set(reflect.Zero(typ))
491 }
492}
493
Dan Willemsenb0935db2020-03-23 19:42:18 -0700494func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800495 for j, v := range s.values {
496 if config.String(s.variable) == v {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700497 return values.Field(j).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800498 }
499 }
500
Dan Willemsenb0935db2020-03-23 19:42:18 -0700501 return nil, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800502}
503
504type boolVariable struct {
505 baseVariable
506}
507
508func (b boolVariable) variableValuesType() reflect.Type {
509 return emptyInterfaceType
510}
511
512func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
513 v.Set(reflect.Zero(typ))
514}
515
Dan Willemsenb0935db2020-03-23 19:42:18 -0700516func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800517 if config.Bool(b.variable) {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700518 return values.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800519 }
520
Dan Willemsenb0935db2020-03-23 19:42:18 -0700521 return nil, nil
522}
523
524type valueVariable struct {
525 baseVariable
526}
527
528func (s *valueVariable) variableValuesType() reflect.Type {
529 return emptyInterfaceType
530}
531
532func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
533 v.Set(reflect.Zero(typ))
534}
535
536func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
537 if !config.IsSet(s.variable) {
538 return nil, nil
539 }
540 configValue := config.String(s.variable)
541
542 propStruct := values.Elem().Elem()
543 for i := 0; i < propStruct.NumField(); i++ {
544 field := propStruct.Field(i)
545 kind := field.Kind()
546 if kind == reflect.Ptr {
547 if field.IsNil() {
548 continue
549 }
550 field = field.Elem()
551 }
552 switch kind {
553 case reflect.String:
554 err := printfIntoProperty(field, configValue)
555 if err != nil {
556 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
557 }
558 case reflect.Slice:
559 for j := 0; j < field.Len(); j++ {
560 err := printfIntoProperty(field.Index(j), configValue)
561 if err != nil {
562 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
563 }
564 }
565 case reflect.Bool:
566 // Nothing to do
567 default:
568 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
569 }
570 }
571
572 return values.Interface(), nil
573}
574
575func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
576 s := propertyValue.String()
577
578 count := strings.Count(s, "%")
579 if count == 0 {
580 return nil
581 }
582
583 if count > 1 {
584 return fmt.Errorf("value variable properties only support a single '%%'")
585 }
586
587 if !strings.Contains(s, "%s") {
588 return fmt.Errorf("unsupported %% in value variable property")
589 }
590
591 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
592
Colin Cross9d34f352019-11-22 16:03:51 -0800593 return nil
594}
595
596func CanonicalizeToProperty(v string) string {
597 return strings.Map(func(r rune) rune {
598 switch {
599 case r >= 'A' && r <= 'Z',
600 r >= 'a' && r <= 'z',
601 r >= '0' && r <= '9',
602 r == '_':
603 return r
604 default:
605 return '_'
606 }
607 }, v)
608}
609
610func CanonicalizeToProperties(values []string) []string {
611 ret := make([]string, len(values))
612 for i, v := range values {
613 ret[i] = CanonicalizeToProperty(v)
614 }
615 return ret
616}
617
618type emptyInterfaceStruct struct {
619 i interface{}
620}
621
622var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type