blob: 7d21b75bc74d2b7409fffe1c0a564213f72daa58 [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"
Jingwen Chen4ad40d92021-11-24 03:40:23 +000023 "sync"
Colin Cross9d34f352019-11-22 16:03:51 -080024
25 "github.com/google/blueprint"
26 "github.com/google/blueprint/parser"
27 "github.com/google/blueprint/proptools"
Liz Kammer72beb342022-02-03 08:42:10 -050028
29 "android/soong/starlark_fmt"
Colin Cross9d34f352019-11-22 16:03:51 -080030)
31
Liz Kammer432bd592020-12-16 12:42:02 -080032const conditionsDefault = "conditions_default"
33
Jingwen Chena47f28d2021-11-02 16:43:57 +000034var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
Colin Cross9d34f352019-11-22 16:03:51 -080035
36// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
37// result so each file is only parsed once.
38func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
39 scope := parser.NewScope(nil)
40 file, errs := parser.ParseAndEval(from, r, scope)
41
42 if len(errs) > 0 {
43 return nil, errs
44 }
45
46 mtDef := &SoongConfigDefinition{
47 ModuleTypes: make(map[string]*ModuleType),
48 variables: make(map[string]soongConfigVariable),
49 }
50
51 for _, def := range file.Defs {
52 switch def := def.(type) {
53 case *parser.Module:
54 newErrs := processImportModuleDef(mtDef, def)
55
56 if len(newErrs) > 0 {
57 errs = append(errs, newErrs...)
58 }
59
60 case *parser.Assignment:
61 // Already handled via Scope object
62 default:
63 panic("unknown definition type")
64 }
65 }
66
67 if len(errs) > 0 {
68 return nil, errs
69 }
70
71 for name, moduleType := range mtDef.ModuleTypes {
72 for _, varName := range moduleType.variableNames {
73 if v, ok := mtDef.variables[varName]; ok {
74 moduleType.Variables = append(moduleType.Variables, v)
75 } else {
76 return nil, []error{
77 fmt.Errorf("unknown variable %q in module type %q", varName, name),
78 }
79 }
80 }
81 }
82
83 return mtDef, nil
84}
85
86func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
87 switch def.Type {
88 case "soong_config_module_type":
89 return processModuleTypeDef(v, def)
90 case "soong_config_string_variable":
91 return processStringVariableDef(v, def)
92 case "soong_config_bool_variable":
93 return processBoolVariableDef(v, def)
94 default:
95 // Unknown module types will be handled when the file is parsed as a normal
96 // Android.bp file.
97 }
98
99 return nil
100}
101
102type ModuleTypeProperties struct {
103 // the name of the new module type. Unlike most modules, this name does not need to be unique,
104 // although only one module type with any name will be importable into an Android.bp file.
105 Name string
106
107 // the module type that this module type will extend.
108 Module_type string
109
110 // the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
111 // configuration variables from.
112 Config_namespace string
113
114 // the list of SOONG_CONFIG variables that this module type will read
115 Variables []string
116
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700117 // the list of boolean SOONG_CONFIG variables that this module type will read
118 Bool_variables []string
119
Dan Willemsenb0935db2020-03-23 19:42:18 -0700120 // the list of SOONG_CONFIG variables that this module type will read. The value will be
121 // inserted into the properties with %s substitution.
122 Value_variables []string
123
Colin Cross9d34f352019-11-22 16:03:51 -0800124 // the list of properties that this module type will extend.
125 Properties []string
126}
127
128func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
129
130 props := &ModuleTypeProperties{}
131
132 _, errs = proptools.UnpackProperties(def.Properties, props)
133 if len(errs) > 0 {
134 return errs
135 }
136
137 if props.Name == "" {
138 errs = append(errs, fmt.Errorf("name property must be set"))
139 }
140
141 if props.Config_namespace == "" {
142 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
143 }
144
145 if props.Module_type == "" {
146 errs = append(errs, fmt.Errorf("module_type property must be set"))
147 }
148
149 if len(errs) > 0 {
150 return errs
151 }
152
Liz Kammer432bd592020-12-16 12:42:02 -0800153 if mt, errs := newModuleType(props); len(errs) > 0 {
154 return errs
155 } else {
156 v.ModuleTypes[props.Name] = mt
Dan Willemsenb0935db2020-03-23 19:42:18 -0700157 }
158
Colin Cross9d34f352019-11-22 16:03:51 -0800159 return nil
160}
161
162type VariableProperties struct {
163 Name string
164}
165
166type StringVariableProperties struct {
167 Values []string
168}
169
170func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
171 stringProps := &StringVariableProperties{}
172
173 base, errs := processVariableDef(def, stringProps)
174 if len(errs) > 0 {
175 return errs
176 }
177
178 if len(stringProps.Values) == 0 {
179 return []error{fmt.Errorf("values property must be set")}
180 }
181
Liz Kammer72beb342022-02-03 08:42:10 -0500182 vals := make(map[string]bool, len(stringProps.Values))
Liz Kammer432bd592020-12-16 12:42:02 -0800183 for _, name := range stringProps.Values {
184 if err := checkVariableName(name); err != nil {
185 return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
Liz Kammer72beb342022-02-03 08:42:10 -0500186 } else if _, ok := vals[name]; ok {
187 return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
Liz Kammer432bd592020-12-16 12:42:02 -0800188 }
Liz Kammer72beb342022-02-03 08:42:10 -0500189 vals[name] = true
Liz Kammer432bd592020-12-16 12:42:02 -0800190 }
191
Colin Cross9d34f352019-11-22 16:03:51 -0800192 v.variables[base.variable] = &stringVariable{
193 baseVariable: base,
194 values: CanonicalizeToProperties(stringProps.Values),
195 }
196
197 return nil
198}
199
200func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
201 base, errs := processVariableDef(def)
202 if len(errs) > 0 {
203 return errs
204 }
205
206 v.variables[base.variable] = &boolVariable{
207 baseVariable: base,
208 }
209
210 return nil
211}
212
213func processVariableDef(def *parser.Module,
214 extraProps ...interface{}) (cond baseVariable, errs []error) {
215
216 props := &VariableProperties{}
217
218 allProps := append([]interface{}{props}, extraProps...)
219
220 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
221 if len(errs) > 0 {
222 return baseVariable{}, errs
223 }
224
225 if props.Name == "" {
226 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
227 }
228
229 return baseVariable{
230 variable: props.Name,
231 }, nil
232}
233
234type SoongConfigDefinition struct {
235 ModuleTypes map[string]*ModuleType
236
237 variables map[string]soongConfigVariable
238}
239
Jingwen Chen01812022021-11-19 14:29:43 +0000240// Bp2BuildSoongConfigDefinition keeps a global record of all soong config
241// string vars, bool vars and value vars created by every
242// soong_config_module_type in this build.
243type Bp2BuildSoongConfigDefinitions struct {
Liz Kammer72beb342022-02-03 08:42:10 -0500244 // varCache contains a cache of string variables namespace + property
245 // The same variable may be used in multiple module types (for example, if need support
246 // for cc_default and java_default), only need to process once
247 varCache map[string]bool
248
249 StringVars map[string][]string
Jingwen Chen01812022021-11-19 14:29:43 +0000250 BoolVars map[string]bool
251 ValueVars map[string]bool
252}
253
Jingwen Chen4ad40d92021-11-24 03:40:23 +0000254var bp2buildSoongConfigVarsLock sync.Mutex
255
Jingwen Chen01812022021-11-19 14:29:43 +0000256// SoongConfigVariablesForBp2build extracts information from a
257// SoongConfigDefinition that bp2build needs to generate constraint settings and
258// values for, in order to migrate soong_config_module_type usages to Bazel.
259func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition) {
Jingwen Chen4ad40d92021-11-24 03:40:23 +0000260 // In bp2build mode, this method is called concurrently in goroutines from
261 // loadhooks while parsing soong_config_module_type, so add a mutex to
262 // prevent concurrent map writes. See b/207572723
263 bp2buildSoongConfigVarsLock.Lock()
264 defer bp2buildSoongConfigVarsLock.Unlock()
265
Jingwen Chen01812022021-11-19 14:29:43 +0000266 if defs.StringVars == nil {
Liz Kammer72beb342022-02-03 08:42:10 -0500267 defs.StringVars = make(map[string][]string)
Jingwen Chen01812022021-11-19 14:29:43 +0000268 }
269 if defs.BoolVars == nil {
270 defs.BoolVars = make(map[string]bool)
271 }
272 if defs.ValueVars == nil {
273 defs.ValueVars = make(map[string]bool)
274 }
Liz Kammer72beb342022-02-03 08:42:10 -0500275 if defs.varCache == nil {
276 defs.varCache = make(map[string]bool)
277 }
Jingwen Chen01812022021-11-19 14:29:43 +0000278 for _, moduleType := range mtDef.ModuleTypes {
279 for _, v := range moduleType.Variables {
280 key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
Liz Kammer72beb342022-02-03 08:42:10 -0500281
282 // The same variable may be used in multiple module types (for example, if need support
283 // for cc_default and java_default), only need to process once
284 if _, keyInCache := defs.varCache[key]; keyInCache {
285 continue
286 } else {
287 defs.varCache[key] = true
288 }
289
Jingwen Chen01812022021-11-19 14:29:43 +0000290 if strVar, ok := v.(*stringVariable); ok {
Jingwen Chen01812022021-11-19 14:29:43 +0000291 for _, value := range strVar.values {
Liz Kammer72beb342022-02-03 08:42:10 -0500292 defs.StringVars[key] = append(defs.StringVars[key], value)
Jingwen Chen01812022021-11-19 14:29:43 +0000293 }
294 } else if _, ok := v.(*boolVariable); ok {
295 defs.BoolVars[key] = true
296 } else if _, ok := v.(*valueVariable); ok {
297 defs.ValueVars[key] = true
298 } else {
299 panic(fmt.Errorf("Unsupported variable type: %+v", v))
300 }
301 }
302 }
303}
304
305// This is a copy of the one available in soong/android/util.go, but depending
306// on the android package causes a cyclic dependency. A refactoring here is to
307// extract common utils out from android/utils.go for other packages like this.
308func sortedStringKeys(m interface{}) []string {
309 v := reflect.ValueOf(m)
310 if v.Kind() != reflect.Map {
311 panic(fmt.Sprintf("%#v is not a map", m))
312 }
313 keys := v.MapKeys()
314 s := make([]string, 0, len(keys))
315 for _, key := range keys {
316 s = append(s, key.String())
317 }
318 sort.Strings(s)
319 return s
320}
321
322// String emits the Soong config variable definitions as Starlark dictionaries.
323func (defs Bp2BuildSoongConfigDefinitions) String() string {
324 ret := ""
Liz Kammer72beb342022-02-03 08:42:10 -0500325 ret += "soong_config_bool_variables = "
326 ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
327 ret += "\n\n"
Jingwen Chen01812022021-11-19 14:29:43 +0000328
Liz Kammer72beb342022-02-03 08:42:10 -0500329 ret += "soong_config_value_variables = "
330 ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
331 ret += "\n\n"
Jingwen Chen01812022021-11-19 14:29:43 +0000332
Liz Kammer72beb342022-02-03 08:42:10 -0500333 ret += "soong_config_string_variables = "
334 ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
Jingwen Chen01812022021-11-19 14:29:43 +0000335
336 return ret
337}
338
Colin Cross9d34f352019-11-22 16:03:51 -0800339// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
340// property layout for the Soong config variables, with each possible value an interface{} that
341// contains a nil pointer to another newly constructed type that contains the affectable properties.
342// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
343//
344// For example, the acme_cc_defaults example above would
345// produce a reflect.Value whose type is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700346//
347// *struct {
348// Soong_config_variables struct {
349// Board struct {
350// Soc_a interface{}
351// Soc_b interface{}
352// }
353// }
354// }
355//
Colin Cross9d34f352019-11-22 16:03:51 -0800356// And whose value is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700357//
358// &{
359// Soong_config_variables: {
360// Board: {
361// Soc_a: (*struct{ Cflags []string })(nil),
362// Soc_b: (*struct{ Cflags []string })(nil),
363// },
364// },
365// }
Colin Cross9d34f352019-11-22 16:03:51 -0800366func CreateProperties(factory blueprint.ModuleFactory, moduleType *ModuleType) reflect.Value {
367 var fields []reflect.StructField
368
369 _, factoryProps := factory()
370 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
371 if affectablePropertiesType == nil {
372 return reflect.Value{}
373 }
374
375 for _, c := range moduleType.Variables {
376 fields = append(fields, reflect.StructField{
377 Name: proptools.FieldNameForProperty(c.variableProperty()),
378 Type: c.variableValuesType(),
379 })
380 }
381
382 typ := reflect.StructOf([]reflect.StructField{{
Jingwen Chena47f28d2021-11-02 16:43:57 +0000383 Name: SoongConfigProperty,
Colin Cross9d34f352019-11-22 16:03:51 -0800384 Type: reflect.StructOf(fields),
385 }})
386
387 props := reflect.New(typ)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000388 structConditions := props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800389
390 for i, c := range moduleType.Variables {
391 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
392 }
393
394 return props
395}
396
397// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
398// that exists in factoryProps.
399func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
400 affectableProperties = append([]string(nil), affectableProperties...)
401 sort.Strings(affectableProperties)
402
403 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
404 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
405 var fields []reflect.StructField
406
Colin Cross997f27a2021-03-05 17:25:41 -0800407 // Iterate while the list is non-empty so it can be modified in the loop.
Colin Cross9d34f352019-11-22 16:03:51 -0800408 for len(affectableProperties) > 0 {
409 p := affectableProperties[0]
410 if !strings.HasPrefix(affectableProperties[0], prefix) {
Colin Cross997f27a2021-03-05 17:25:41 -0800411 // The properties are sorted and recurse is always called with a prefix that matches
412 // the first property in the list, so if we've reached one that doesn't match the
413 // prefix we are done with this prefix.
Colin Cross9d34f352019-11-22 16:03:51 -0800414 break
415 }
Colin Cross9d34f352019-11-22 16:03:51 -0800416
417 nestedProperty := strings.TrimPrefix(p, prefix)
418 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
419 var nestedType reflect.Type
420 nestedPrefix := nestedProperty[:i+1]
421
Colin Cross997f27a2021-03-05 17:25:41 -0800422 // Recurse to handle the properties with the found prefix. This will return
423 // an updated affectableProperties with the handled entries removed from the front
424 // of the list, and the type that contains the handled entries. The type may be
425 // nil if none of the entries matched factoryProps.
Colin Cross9d34f352019-11-22 16:03:51 -0800426 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
427
428 if nestedType != nil {
429 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
430
431 fields = append(fields, reflect.StructField{
432 Name: nestedFieldName,
433 Type: nestedType,
434 })
435 }
436 } else {
437 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
438 if typ != nil {
439 fields = append(fields, reflect.StructField{
440 Name: proptools.FieldNameForProperty(nestedProperty),
441 Type: typ,
442 })
443 }
Colin Cross997f27a2021-03-05 17:25:41 -0800444 // The first element in the list has been handled, remove it from the list.
445 affectableProperties = affectableProperties[1:]
Colin Cross9d34f352019-11-22 16:03:51 -0800446 }
447 }
448
449 var typ reflect.Type
450 if len(fields) > 0 {
451 typ = reflect.StructOf(fields)
452 }
453 return affectableProperties, typ
454 }
455
456 affectableProperties, typ := recurse("", affectableProperties)
457 if len(affectableProperties) > 0 {
458 panic(fmt.Errorf("didn't handle all affectable properties"))
459 }
460
461 if typ != nil {
462 return reflect.PtrTo(typ)
463 }
464
465 return nil
466}
467
468func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
469 for _, ps := range psList {
470 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
471 return typ
472 }
473 }
474
475 return nil
476}
477
478func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
479 v := reflect.ValueOf(ps)
480 for len(property) > 0 {
481 if !v.IsValid() {
482 return nil
483 }
484
485 if v.Kind() == reflect.Interface {
486 if v.IsNil() {
487 return nil
488 } else {
489 v = v.Elem()
490 }
491 }
492
493 if v.Kind() == reflect.Ptr {
494 if v.IsNil() {
495 v = reflect.Zero(v.Type().Elem())
496 } else {
497 v = v.Elem()
498 }
499 }
500
501 if v.Kind() != reflect.Struct {
502 return nil
503 }
504
505 if index := strings.IndexRune(property, '.'); index >= 0 {
506 prefix := property[:index]
507 property = property[index+1:]
508
509 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
510 } else {
511 f := v.FieldByName(proptools.FieldNameForProperty(property))
512 if !f.IsValid() {
513 return nil
514 }
515 return f.Type()
516 }
517 }
518 return nil
519}
520
521// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
522// based on SoongConfig values.
Liz Kammerfe8853d2020-12-16 09:34:33 -0800523// Expects that props contains a struct field with name soong_config_variables. The fields within
Liz Kammer432bd592020-12-16 12:42:02 -0800524// soong_config_variables are expected to be in the same order as moduleType.Variables.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700525func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800526 var ret []interface{}
Jingwen Chena47f28d2021-11-02 16:43:57 +0000527 props = props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800528 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700529 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
530 return nil, err
531 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800532 ret = append(ret, ps)
533 }
534 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700535 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800536}
537
538type ModuleType struct {
539 BaseModuleType string
540 ConfigNamespace string
541 Variables []soongConfigVariable
542
543 affectableProperties []string
544 variableNames []string
545}
546
Liz Kammer432bd592020-12-16 12:42:02 -0800547func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
548 mt := &ModuleType{
549 affectableProperties: props.Properties,
550 ConfigNamespace: props.Config_namespace,
551 BaseModuleType: props.Module_type,
552 variableNames: props.Variables,
553 }
554
555 for _, name := range props.Bool_variables {
556 if err := checkVariableName(name); err != nil {
557 return nil, []error{fmt.Errorf("bool_variables %s", err)}
558 }
559
560 mt.Variables = append(mt.Variables, newBoolVariable(name))
561 }
562
563 for _, name := range props.Value_variables {
564 if err := checkVariableName(name); err != nil {
565 return nil, []error{fmt.Errorf("value_variables %s", err)}
566 }
567
568 mt.Variables = append(mt.Variables, &valueVariable{
569 baseVariable: baseVariable{
570 variable: name,
571 },
572 })
573 }
574
575 return mt, nil
576}
577
578func checkVariableName(name string) error {
579 if name == "" {
580 return fmt.Errorf("name must not be blank")
581 } else if name == conditionsDefault {
582 return fmt.Errorf("%q is reserved", conditionsDefault)
583 }
584 return nil
585}
586
Colin Cross9d34f352019-11-22 16:03:51 -0800587type soongConfigVariable interface {
588 // variableProperty returns the name of the variable.
589 variableProperty() string
590
591 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
592 variableValuesType() reflect.Type
593
594 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
595 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
596 // the zero value of the affectable properties type.
597 initializeProperties(v reflect.Value, typ reflect.Type)
598
599 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
600 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700601 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800602}
603
604type baseVariable struct {
605 variable string
606}
607
608func (c *baseVariable) variableProperty() string {
609 return CanonicalizeToProperty(c.variable)
610}
611
612type stringVariable struct {
613 baseVariable
614 values []string
615}
616
617func (s *stringVariable) variableValuesType() reflect.Type {
618 var fields []reflect.StructField
619
Liz Kammer432bd592020-12-16 12:42:02 -0800620 var values []string
621 values = append(values, s.values...)
622 values = append(values, conditionsDefault)
623 for _, v := range values {
Colin Cross9d34f352019-11-22 16:03:51 -0800624 fields = append(fields, reflect.StructField{
625 Name: proptools.FieldNameForProperty(v),
626 Type: emptyInterfaceType,
627 })
628 }
629
630 return reflect.StructOf(fields)
631}
632
Liz Kammer432bd592020-12-16 12:42:02 -0800633// initializeProperties initializes properties to zero value of typ for supported values and a final
634// conditions default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800635func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
636 for i := range s.values {
637 v.Field(i).Set(reflect.Zero(typ))
638 }
Liz Kammer432bd592020-12-16 12:42:02 -0800639 v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
Colin Cross9d34f352019-11-22 16:03:51 -0800640}
641
Liz Kammer432bd592020-12-16 12:42:02 -0800642// Extracts an interface from values containing the properties to apply based on config.
643// If config does not match a value with a non-nil property set, the default value will be returned.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700644func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800645 for j, v := range s.values {
Liz Kammer432bd592020-12-16 12:42:02 -0800646 f := values.Field(j)
Cole Faust3aa9dac2022-04-08 00:42:33 +0000647 if config.String(s.variable) == v && !f.Elem().IsNil() {
Liz Kammer432bd592020-12-16 12:42:02 -0800648 return f.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800649 }
650 }
Liz Kammer432bd592020-12-16 12:42:02 -0800651 // if we have reached this point, we have checked all valid values of string and either:
652 // * the value was not set
653 // * the value was set but that value was not specified in the Android.bp file
654 return values.Field(len(s.values)).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800655}
656
Liz Kammer432bd592020-12-16 12:42:02 -0800657// Struct to allow conditions set based on a boolean variable
Colin Cross9d34f352019-11-22 16:03:51 -0800658type boolVariable struct {
659 baseVariable
660}
661
Liz Kammer432bd592020-12-16 12:42:02 -0800662// newBoolVariable constructs a boolVariable with the given name
Liz Kammerfe8853d2020-12-16 09:34:33 -0800663func newBoolVariable(name string) *boolVariable {
664 return &boolVariable{
665 baseVariable{
666 variable: name,
667 },
668 }
669}
670
Colin Cross9d34f352019-11-22 16:03:51 -0800671func (b boolVariable) variableValuesType() reflect.Type {
672 return emptyInterfaceType
673}
674
Liz Kammer432bd592020-12-16 12:42:02 -0800675// initializeProperties initializes a property to zero value of typ with an additional conditions
676// default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800677func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800678 initializePropertiesWithDefault(v, typ)
Colin Cross9d34f352019-11-22 16:03:51 -0800679}
680
Liz Kammer432bd592020-12-16 12:42:02 -0800681// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
682// in typ, with an additional field for defaults of type typ. This should be used to initialize
683// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
684// one variable and a default.
685func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
686 sTyp := typ.Elem()
687 var fields []reflect.StructField
688 for i := 0; i < sTyp.NumField(); i++ {
689 fields = append(fields, sTyp.Field(i))
Colin Cross9d34f352019-11-22 16:03:51 -0800690 }
691
Liz Kammer432bd592020-12-16 12:42:02 -0800692 // create conditions_default field
693 nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
694 fields = append(fields, reflect.StructField{
695 Name: nestedFieldName,
696 Type: typ,
697 })
698
699 newTyp := reflect.PtrTo(reflect.StructOf(fields))
700 v.Set(reflect.Zero(newTyp))
701}
702
703// conditionsDefaultField extracts the conditions_default field from v. This is always the final
704// field if initialized with initializePropertiesWithDefault.
705func conditionsDefaultField(v reflect.Value) reflect.Value {
706 return v.Field(v.NumField() - 1)
707}
708
709// removeDefault removes the conditions_default field from values while retaining values from all
710// other fields. This allows
711func removeDefault(values reflect.Value) reflect.Value {
712 v := values.Elem().Elem()
713 s := conditionsDefaultField(v)
714 // if conditions_default field was not set, there will be no issues extending properties.
715 if !s.IsValid() {
716 return v
717 }
718
719 // If conditions_default field was set, it has the correct type for our property. Create a new
720 // reflect.Value of the conditions_default type and copy all fields (except for
721 // conditions_default) based on values to the result.
722 res := reflect.New(s.Type().Elem())
723 for i := 0; i < res.Type().Elem().NumField(); i++ {
724 val := v.Field(i)
725 res.Elem().Field(i).Set(val)
726 }
727
728 return res
729}
730
731// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
732// the module. If the value was not set, conditions_default interface will be returned; otherwise,
733// the interface in values, without conditions_default will be returned.
734func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
735 // If this variable was not referenced in the module, there are no properties to apply.
736 if values.Elem().IsZero() {
737 return nil, nil
738 }
739 if config.Bool(b.variable) {
740 values = removeDefault(values)
741 return values.Interface(), nil
742 }
743 v := values.Elem().Elem()
744 if f := conditionsDefaultField(v); f.IsValid() {
745 return f.Interface(), nil
746 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700747 return nil, nil
748}
749
Liz Kammer432bd592020-12-16 12:42:02 -0800750// Struct to allow conditions set based on a value variable, supporting string substitution.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700751type valueVariable struct {
752 baseVariable
753}
754
755func (s *valueVariable) variableValuesType() reflect.Type {
756 return emptyInterfaceType
757}
758
Liz Kammer432bd592020-12-16 12:42:02 -0800759// initializeProperties initializes a property to zero value of typ with an additional conditions
760// default field.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700761func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800762 initializePropertiesWithDefault(v, typ)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700763}
764
Liz Kammer432bd592020-12-16 12:42:02 -0800765// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
766// the module. If the variable was not set, conditions_default interface will be returned;
767// otherwise, the interface in values, without conditions_default will be returned with all
768// appropriate string substitutions based on variable being set.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700769func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer432bd592020-12-16 12:42:02 -0800770 // If this variable was not referenced in the module, there are no properties to apply.
771 if !values.IsValid() || values.Elem().IsZero() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700772 return nil, nil
773 }
Liz Kammer432bd592020-12-16 12:42:02 -0800774 if !config.IsSet(s.variable) {
775 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
776 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700777 configValue := config.String(s.variable)
778
Liz Kammer432bd592020-12-16 12:42:02 -0800779 values = removeDefault(values)
780 propStruct := values.Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800781 if !propStruct.IsValid() {
782 return nil, nil
783 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700784 for i := 0; i < propStruct.NumField(); i++ {
785 field := propStruct.Field(i)
786 kind := field.Kind()
787 if kind == reflect.Ptr {
788 if field.IsNil() {
789 continue
790 }
791 field = field.Elem()
792 }
793 switch kind {
794 case reflect.String:
795 err := printfIntoProperty(field, configValue)
796 if err != nil {
797 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
798 }
799 case reflect.Slice:
800 for j := 0; j < field.Len(); j++ {
801 err := printfIntoProperty(field.Index(j), configValue)
802 if err != nil {
803 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
804 }
805 }
806 case reflect.Bool:
807 // Nothing to do
808 default:
809 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
810 }
811 }
812
813 return values.Interface(), nil
814}
815
816func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
817 s := propertyValue.String()
818
819 count := strings.Count(s, "%")
820 if count == 0 {
821 return nil
822 }
823
824 if count > 1 {
825 return fmt.Errorf("value variable properties only support a single '%%'")
826 }
827
828 if !strings.Contains(s, "%s") {
829 return fmt.Errorf("unsupported %% in value variable property")
830 }
831
832 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
833
Colin Cross9d34f352019-11-22 16:03:51 -0800834 return nil
835}
836
837func CanonicalizeToProperty(v string) string {
838 return strings.Map(func(r rune) rune {
839 switch {
840 case r >= 'A' && r <= 'Z',
841 r >= 'a' && r <= 'z',
842 r >= '0' && r <= '9',
843 r == '_':
844 return r
845 default:
846 return '_'
847 }
848 }, v)
849}
850
851func CanonicalizeToProperties(values []string) []string {
852 ret := make([]string, len(values))
853 for i, v := range values {
854 ret[i] = CanonicalizeToProperty(v)
855 }
856 return ret
857}
858
859type emptyInterfaceStruct struct {
860 i interface{}
861}
862
863var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type