blob: 9f3f804ef268970529886d7a0699e77a05526b51 [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
Liz Kammerfe8853d2020-12-16 09:34:33 -0800161 mt.Variables = append(mt.Variables, newBoolVariable(name))
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700162 }
163
Dan Willemsenb0935db2020-03-23 19:42:18 -0700164 for _, name := range props.Value_variables {
165 if name == "" {
166 return []error{fmt.Errorf("value_variables entry must not be blank")}
167 }
168
169 mt.Variables = append(mt.Variables, &valueVariable{
170 baseVariable: baseVariable{
171 variable: name,
172 },
173 })
174 }
175
Colin Cross9d34f352019-11-22 16:03:51 -0800176 return nil
177}
178
179type VariableProperties struct {
180 Name string
181}
182
183type StringVariableProperties struct {
184 Values []string
185}
186
187func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
188 stringProps := &StringVariableProperties{}
189
190 base, errs := processVariableDef(def, stringProps)
191 if len(errs) > 0 {
192 return errs
193 }
194
195 if len(stringProps.Values) == 0 {
196 return []error{fmt.Errorf("values property must be set")}
197 }
198
199 v.variables[base.variable] = &stringVariable{
200 baseVariable: base,
201 values: CanonicalizeToProperties(stringProps.Values),
202 }
203
204 return nil
205}
206
207func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
208 base, errs := processVariableDef(def)
209 if len(errs) > 0 {
210 return errs
211 }
212
213 v.variables[base.variable] = &boolVariable{
214 baseVariable: base,
215 }
216
217 return nil
218}
219
220func processVariableDef(def *parser.Module,
221 extraProps ...interface{}) (cond baseVariable, errs []error) {
222
223 props := &VariableProperties{}
224
225 allProps := append([]interface{}{props}, extraProps...)
226
227 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
228 if len(errs) > 0 {
229 return baseVariable{}, errs
230 }
231
232 if props.Name == "" {
233 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
234 }
235
236 return baseVariable{
237 variable: props.Name,
238 }, nil
239}
240
241type SoongConfigDefinition struct {
242 ModuleTypes map[string]*ModuleType
243
244 variables map[string]soongConfigVariable
245}
246
247// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
248// property layout for the Soong config variables, with each possible value an interface{} that
249// contains a nil pointer to another newly constructed type that contains the affectable properties.
250// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
251//
252// For example, the acme_cc_defaults example above would
253// produce a reflect.Value whose type is:
254// *struct {
255// Soong_config_variables struct {
256// Board struct {
257// Soc_a interface{}
258// Soc_b interface{}
259// }
260// }
261// }
262// And whose value is:
263// &{
264// Soong_config_variables: {
265// Board: {
266// Soc_a: (*struct{ Cflags []string })(nil),
267// Soc_b: (*struct{ Cflags []string })(nil),
268// },
269// },
270// }
271func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
272 var fields []reflect.StructField
273
274 _, factoryProps := factory()
275 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
276 if affectablePropertiesType == nil {
277 return reflect.Value{}
278 }
279
280 for _, c := range moduleType.Variables {
281 fields = append(fields, reflect.StructField{
282 Name: proptools.FieldNameForProperty(c.variableProperty()),
283 Type: c.variableValuesType(),
284 })
285 }
286
287 typ := reflect.StructOf([]reflect.StructField{{
288 Name: soongConfigProperty,
289 Type: reflect.StructOf(fields),
290 }})
291
292 props := reflect.New(typ)
293 structConditions := props.Elem().FieldByName(soongConfigProperty)
294
295 for i, c := range moduleType.Variables {
296 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
297 }
298
299 return props
300}
301
302// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
303// that exists in factoryProps.
304func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
305 affectableProperties = append([]string(nil), affectableProperties...)
306 sort.Strings(affectableProperties)
307
308 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
309 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
310 var fields []reflect.StructField
311
312 for len(affectableProperties) > 0 {
313 p := affectableProperties[0]
314 if !strings.HasPrefix(affectableProperties[0], prefix) {
315 break
316 }
317 affectableProperties = affectableProperties[1:]
318
319 nestedProperty := strings.TrimPrefix(p, prefix)
320 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
321 var nestedType reflect.Type
322 nestedPrefix := nestedProperty[:i+1]
323
324 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
325
326 if nestedType != nil {
327 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
328
329 fields = append(fields, reflect.StructField{
330 Name: nestedFieldName,
331 Type: nestedType,
332 })
333 }
334 } else {
335 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
336 if typ != nil {
337 fields = append(fields, reflect.StructField{
338 Name: proptools.FieldNameForProperty(nestedProperty),
339 Type: typ,
340 })
341 }
342 }
343 }
344
345 var typ reflect.Type
346 if len(fields) > 0 {
347 typ = reflect.StructOf(fields)
348 }
349 return affectableProperties, typ
350 }
351
352 affectableProperties, typ := recurse("", affectableProperties)
353 if len(affectableProperties) > 0 {
354 panic(fmt.Errorf("didn't handle all affectable properties"))
355 }
356
357 if typ != nil {
358 return reflect.PtrTo(typ)
359 }
360
361 return nil
362}
363
364func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
365 for _, ps := range psList {
366 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
367 return typ
368 }
369 }
370
371 return nil
372}
373
374func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
375 v := reflect.ValueOf(ps)
376 for len(property) > 0 {
377 if !v.IsValid() {
378 return nil
379 }
380
381 if v.Kind() == reflect.Interface {
382 if v.IsNil() {
383 return nil
384 } else {
385 v = v.Elem()
386 }
387 }
388
389 if v.Kind() == reflect.Ptr {
390 if v.IsNil() {
391 v = reflect.Zero(v.Type().Elem())
392 } else {
393 v = v.Elem()
394 }
395 }
396
397 if v.Kind() != reflect.Struct {
398 return nil
399 }
400
401 if index := strings.IndexRune(property, '.'); index >= 0 {
402 prefix := property[:index]
403 property = property[index+1:]
404
405 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
406 } else {
407 f := v.FieldByName(proptools.FieldNameForProperty(property))
408 if !f.IsValid() {
409 return nil
410 }
411 return f.Type()
412 }
413 }
414 return nil
415}
416
417// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
418// based on SoongConfig values.
Liz Kammerfe8853d2020-12-16 09:34:33 -0800419// Expects that props contains a struct field with name soong_config_variables. The fields within
420// soong_config_variables are expected to be in the same order as moduleType.Variables. In general,
421// props should be generated via CreateProperties.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700422func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800423 var ret []interface{}
424 props = props.Elem().FieldByName(soongConfigProperty)
425 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700426 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
427 return nil, err
428 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800429 ret = append(ret, ps)
430 }
431 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700432 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800433}
434
435type ModuleType struct {
436 BaseModuleType string
437 ConfigNamespace string
438 Variables []soongConfigVariable
439
440 affectableProperties []string
441 variableNames []string
442}
443
444type soongConfigVariable interface {
445 // variableProperty returns the name of the variable.
446 variableProperty() string
447
448 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
449 variableValuesType() reflect.Type
450
451 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
452 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
453 // the zero value of the affectable properties type.
454 initializeProperties(v reflect.Value, typ reflect.Type)
455
456 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
457 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700458 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800459}
460
461type baseVariable struct {
462 variable string
463}
464
465func (c *baseVariable) variableProperty() string {
466 return CanonicalizeToProperty(c.variable)
467}
468
469type stringVariable struct {
470 baseVariable
471 values []string
472}
473
474func (s *stringVariable) variableValuesType() reflect.Type {
475 var fields []reflect.StructField
476
477 for _, v := range s.values {
478 fields = append(fields, reflect.StructField{
479 Name: proptools.FieldNameForProperty(v),
480 Type: emptyInterfaceType,
481 })
482 }
483
484 return reflect.StructOf(fields)
485}
486
487func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
488 for i := range s.values {
489 v.Field(i).Set(reflect.Zero(typ))
490 }
491}
492
Dan Willemsenb0935db2020-03-23 19:42:18 -0700493func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800494 for j, v := range s.values {
495 if config.String(s.variable) == v {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700496 return values.Field(j).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800497 }
498 }
499
Dan Willemsenb0935db2020-03-23 19:42:18 -0700500 return nil, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800501}
502
503type boolVariable struct {
504 baseVariable
505}
506
Liz Kammerfe8853d2020-12-16 09:34:33 -0800507func newBoolVariable(name string) *boolVariable {
508 return &boolVariable{
509 baseVariable{
510 variable: name,
511 },
512 }
513}
514
Colin Cross9d34f352019-11-22 16:03:51 -0800515func (b boolVariable) variableValuesType() reflect.Type {
516 return emptyInterfaceType
517}
518
519func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
520 v.Set(reflect.Zero(typ))
521}
522
Dan Willemsenb0935db2020-03-23 19:42:18 -0700523func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800524 if config.Bool(b.variable) {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700525 return values.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800526 }
527
Dan Willemsenb0935db2020-03-23 19:42:18 -0700528 return nil, nil
529}
530
531type valueVariable struct {
532 baseVariable
533}
534
535func (s *valueVariable) variableValuesType() reflect.Type {
536 return emptyInterfaceType
537}
538
539func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
540 v.Set(reflect.Zero(typ))
541}
542
543func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800544 if !config.IsSet(s.variable) || !values.IsValid() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700545 return nil, nil
546 }
547 configValue := config.String(s.variable)
548
549 propStruct := values.Elem().Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800550 if !propStruct.IsValid() {
551 return nil, nil
552 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700553 for i := 0; i < propStruct.NumField(); i++ {
554 field := propStruct.Field(i)
555 kind := field.Kind()
556 if kind == reflect.Ptr {
557 if field.IsNil() {
558 continue
559 }
560 field = field.Elem()
561 }
562 switch kind {
563 case reflect.String:
564 err := printfIntoProperty(field, configValue)
565 if err != nil {
566 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
567 }
568 case reflect.Slice:
569 for j := 0; j < field.Len(); j++ {
570 err := printfIntoProperty(field.Index(j), configValue)
571 if err != nil {
572 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
573 }
574 }
575 case reflect.Bool:
576 // Nothing to do
577 default:
578 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
579 }
580 }
581
582 return values.Interface(), nil
583}
584
585func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
586 s := propertyValue.String()
587
588 count := strings.Count(s, "%")
589 if count == 0 {
590 return nil
591 }
592
593 if count > 1 {
594 return fmt.Errorf("value variable properties only support a single '%%'")
595 }
596
597 if !strings.Contains(s, "%s") {
598 return fmt.Errorf("unsupported %% in value variable property")
599 }
600
601 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
602
Colin Cross9d34f352019-11-22 16:03:51 -0800603 return nil
604}
605
606func CanonicalizeToProperty(v string) string {
607 return strings.Map(func(r rune) rune {
608 switch {
609 case r >= 'A' && r <= 'Z',
610 r >= 'a' && r <= 'z',
611 r >= '0' && r <= '9',
612 r == '_':
613 return r
614 default:
615 return '_'
616 }
617 }, v)
618}
619
620func CanonicalizeToProperties(values []string) []string {
621 ret := make([]string, len(values))
622 for i, v := range values {
623 ret[i] = CanonicalizeToProperty(v)
624 }
625 return ret
626}
627
628type emptyInterfaceStruct struct {
629 i interface{}
630}
631
632var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type