blob: 9239ca95010e6f83eaa9b8c5588f5459a2023400 [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
Colin Cross9d34f352019-11-22 16:03:51 -080025 "github.com/google/blueprint/parser"
26 "github.com/google/blueprint/proptools"
Liz Kammer72beb342022-02-03 08:42:10 -050027
28 "android/soong/starlark_fmt"
Colin Cross9d34f352019-11-22 16:03:51 -080029)
30
Liz Kammer432bd592020-12-16 12:42:02 -080031const conditionsDefault = "conditions_default"
32
Jingwen Chena47f28d2021-11-02 16:43:57 +000033var SoongConfigProperty = proptools.FieldNameForProperty("soong_config_variables")
Colin Cross9d34f352019-11-22 16:03:51 -080034
35// loadSoongConfigModuleTypeDefinition loads module types from an Android.bp file. It caches the
36// result so each file is only parsed once.
37func Parse(r io.Reader, from string) (*SoongConfigDefinition, []error) {
38 scope := parser.NewScope(nil)
39 file, errs := parser.ParseAndEval(from, r, scope)
40
41 if len(errs) > 0 {
42 return nil, errs
43 }
44
45 mtDef := &SoongConfigDefinition{
46 ModuleTypes: make(map[string]*ModuleType),
47 variables: make(map[string]soongConfigVariable),
48 }
49
50 for _, def := range file.Defs {
51 switch def := def.(type) {
52 case *parser.Module:
53 newErrs := processImportModuleDef(mtDef, def)
54
55 if len(newErrs) > 0 {
56 errs = append(errs, newErrs...)
57 }
58
59 case *parser.Assignment:
60 // Already handled via Scope object
61 default:
62 panic("unknown definition type")
63 }
64 }
65
66 if len(errs) > 0 {
67 return nil, errs
68 }
69
70 for name, moduleType := range mtDef.ModuleTypes {
71 for _, varName := range moduleType.variableNames {
72 if v, ok := mtDef.variables[varName]; ok {
73 moduleType.Variables = append(moduleType.Variables, v)
74 } else {
75 return nil, []error{
76 fmt.Errorf("unknown variable %q in module type %q", varName, name),
77 }
78 }
79 }
80 }
81
82 return mtDef, nil
83}
84
85func processImportModuleDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
86 switch def.Type {
87 case "soong_config_module_type":
88 return processModuleTypeDef(v, def)
89 case "soong_config_string_variable":
90 return processStringVariableDef(v, def)
91 case "soong_config_bool_variable":
92 return processBoolVariableDef(v, def)
93 default:
94 // Unknown module types will be handled when the file is parsed as a normal
95 // Android.bp file.
96 }
97
98 return nil
99}
100
101type ModuleTypeProperties struct {
102 // the name of the new module type. Unlike most modules, this name does not need to be unique,
103 // although only one module type with any name will be importable into an Android.bp file.
104 Name string
105
106 // the module type that this module type will extend.
107 Module_type string
108
109 // the SOONG_CONFIG_NAMESPACE value from a BoardConfig.mk that this module type will read
110 // configuration variables from.
111 Config_namespace string
112
113 // the list of SOONG_CONFIG variables that this module type will read
114 Variables []string
115
Dan Willemsen2b8b89c2020-03-23 19:39:34 -0700116 // the list of boolean SOONG_CONFIG variables that this module type will read
117 Bool_variables []string
118
Dan Willemsenb0935db2020-03-23 19:42:18 -0700119 // the list of SOONG_CONFIG variables that this module type will read. The value will be
120 // inserted into the properties with %s substitution.
121 Value_variables []string
122
Colin Cross9d34f352019-11-22 16:03:51 -0800123 // the list of properties that this module type will extend.
124 Properties []string
125}
126
127func processModuleTypeDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
128
129 props := &ModuleTypeProperties{}
130
131 _, errs = proptools.UnpackProperties(def.Properties, props)
132 if len(errs) > 0 {
133 return errs
134 }
135
136 if props.Name == "" {
137 errs = append(errs, fmt.Errorf("name property must be set"))
138 }
139
140 if props.Config_namespace == "" {
141 errs = append(errs, fmt.Errorf("config_namespace property must be set"))
142 }
143
144 if props.Module_type == "" {
145 errs = append(errs, fmt.Errorf("module_type property must be set"))
146 }
147
148 if len(errs) > 0 {
149 return errs
150 }
151
Liz Kammer432bd592020-12-16 12:42:02 -0800152 if mt, errs := newModuleType(props); len(errs) > 0 {
153 return errs
154 } else {
155 v.ModuleTypes[props.Name] = mt
Dan Willemsenb0935db2020-03-23 19:42:18 -0700156 }
157
Colin Cross9d34f352019-11-22 16:03:51 -0800158 return nil
159}
160
161type VariableProperties struct {
162 Name string
163}
164
165type StringVariableProperties struct {
166 Values []string
167}
168
169func processStringVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
170 stringProps := &StringVariableProperties{}
171
172 base, errs := processVariableDef(def, stringProps)
173 if len(errs) > 0 {
174 return errs
175 }
176
177 if len(stringProps.Values) == 0 {
178 return []error{fmt.Errorf("values property must be set")}
179 }
180
Liz Kammer72beb342022-02-03 08:42:10 -0500181 vals := make(map[string]bool, len(stringProps.Values))
Liz Kammer432bd592020-12-16 12:42:02 -0800182 for _, name := range stringProps.Values {
183 if err := checkVariableName(name); err != nil {
184 return []error{fmt.Errorf("soong_config_string_variable: values property error %s", err)}
Liz Kammer72beb342022-02-03 08:42:10 -0500185 } else if _, ok := vals[name]; ok {
186 return []error{fmt.Errorf("soong_config_string_variable: values property error: duplicate value: %q", name)}
Liz Kammer432bd592020-12-16 12:42:02 -0800187 }
Liz Kammer72beb342022-02-03 08:42:10 -0500188 vals[name] = true
Liz Kammer432bd592020-12-16 12:42:02 -0800189 }
190
Colin Cross9d34f352019-11-22 16:03:51 -0800191 v.variables[base.variable] = &stringVariable{
192 baseVariable: base,
193 values: CanonicalizeToProperties(stringProps.Values),
194 }
195
196 return nil
197}
198
199func processBoolVariableDef(v *SoongConfigDefinition, def *parser.Module) (errs []error) {
200 base, errs := processVariableDef(def)
201 if len(errs) > 0 {
202 return errs
203 }
204
205 v.variables[base.variable] = &boolVariable{
206 baseVariable: base,
207 }
208
209 return nil
210}
211
212func processVariableDef(def *parser.Module,
213 extraProps ...interface{}) (cond baseVariable, errs []error) {
214
215 props := &VariableProperties{}
216
217 allProps := append([]interface{}{props}, extraProps...)
218
219 _, errs = proptools.UnpackProperties(def.Properties, allProps...)
220 if len(errs) > 0 {
221 return baseVariable{}, errs
222 }
223
224 if props.Name == "" {
225 return baseVariable{}, []error{fmt.Errorf("name property must be set")}
226 }
227
228 return baseVariable{
229 variable: props.Name,
230 }, nil
231}
232
233type SoongConfigDefinition struct {
234 ModuleTypes map[string]*ModuleType
235
236 variables map[string]soongConfigVariable
237}
238
Jingwen Chen01812022021-11-19 14:29:43 +0000239// Bp2BuildSoongConfigDefinition keeps a global record of all soong config
240// string vars, bool vars and value vars created by every
241// soong_config_module_type in this build.
242type Bp2BuildSoongConfigDefinitions struct {
Liz Kammer72beb342022-02-03 08:42:10 -0500243 // varCache contains a cache of string variables namespace + property
244 // The same variable may be used in multiple module types (for example, if need support
245 // for cc_default and java_default), only need to process once
246 varCache map[string]bool
247
248 StringVars map[string][]string
Jingwen Chen01812022021-11-19 14:29:43 +0000249 BoolVars map[string]bool
250 ValueVars map[string]bool
251}
252
Jingwen Chen4ad40d92021-11-24 03:40:23 +0000253var bp2buildSoongConfigVarsLock sync.Mutex
254
Jingwen Chen01812022021-11-19 14:29:43 +0000255// SoongConfigVariablesForBp2build extracts information from a
256// SoongConfigDefinition that bp2build needs to generate constraint settings and
257// values for, in order to migrate soong_config_module_type usages to Bazel.
258func (defs *Bp2BuildSoongConfigDefinitions) AddVars(mtDef SoongConfigDefinition) {
Jingwen Chen4ad40d92021-11-24 03:40:23 +0000259 // In bp2build mode, this method is called concurrently in goroutines from
260 // loadhooks while parsing soong_config_module_type, so add a mutex to
261 // prevent concurrent map writes. See b/207572723
262 bp2buildSoongConfigVarsLock.Lock()
263 defer bp2buildSoongConfigVarsLock.Unlock()
264
Jingwen Chen01812022021-11-19 14:29:43 +0000265 if defs.StringVars == nil {
Liz Kammer72beb342022-02-03 08:42:10 -0500266 defs.StringVars = make(map[string][]string)
Jingwen Chen01812022021-11-19 14:29:43 +0000267 }
268 if defs.BoolVars == nil {
269 defs.BoolVars = make(map[string]bool)
270 }
271 if defs.ValueVars == nil {
272 defs.ValueVars = make(map[string]bool)
273 }
Liz Kammer72beb342022-02-03 08:42:10 -0500274 if defs.varCache == nil {
275 defs.varCache = make(map[string]bool)
276 }
Jingwen Chen01812022021-11-19 14:29:43 +0000277 for _, moduleType := range mtDef.ModuleTypes {
278 for _, v := range moduleType.Variables {
279 key := strings.Join([]string{moduleType.ConfigNamespace, v.variableProperty()}, "__")
Liz Kammer72beb342022-02-03 08:42:10 -0500280
281 // The same variable may be used in multiple module types (for example, if need support
282 // for cc_default and java_default), only need to process once
283 if _, keyInCache := defs.varCache[key]; keyInCache {
284 continue
285 } else {
286 defs.varCache[key] = true
287 }
288
Jingwen Chen01812022021-11-19 14:29:43 +0000289 if strVar, ok := v.(*stringVariable); ok {
Jingwen Chen01812022021-11-19 14:29:43 +0000290 for _, value := range strVar.values {
Liz Kammer72beb342022-02-03 08:42:10 -0500291 defs.StringVars[key] = append(defs.StringVars[key], value)
Jingwen Chen01812022021-11-19 14:29:43 +0000292 }
293 } else if _, ok := v.(*boolVariable); ok {
294 defs.BoolVars[key] = true
295 } else if _, ok := v.(*valueVariable); ok {
296 defs.ValueVars[key] = true
297 } else {
298 panic(fmt.Errorf("Unsupported variable type: %+v", v))
299 }
300 }
301 }
302}
303
Jingwen Chen01812022021-11-19 14:29:43 +0000304// String emits the Soong config variable definitions as Starlark dictionaries.
305func (defs Bp2BuildSoongConfigDefinitions) String() string {
306 ret := ""
Liz Kammer72beb342022-02-03 08:42:10 -0500307 ret += "soong_config_bool_variables = "
308 ret += starlark_fmt.PrintBoolDict(defs.BoolVars, 0)
309 ret += "\n\n"
Jingwen Chen01812022021-11-19 14:29:43 +0000310
Liz Kammer72beb342022-02-03 08:42:10 -0500311 ret += "soong_config_value_variables = "
312 ret += starlark_fmt.PrintBoolDict(defs.ValueVars, 0)
313 ret += "\n\n"
Jingwen Chen01812022021-11-19 14:29:43 +0000314
Liz Kammer72beb342022-02-03 08:42:10 -0500315 ret += "soong_config_string_variables = "
316 ret += starlark_fmt.PrintStringListDict(defs.StringVars, 0)
Jingwen Chen01812022021-11-19 14:29:43 +0000317
318 return ret
319}
320
Colin Cross9d34f352019-11-22 16:03:51 -0800321// CreateProperties returns a reflect.Value of a newly constructed type that contains the desired
322// property layout for the Soong config variables, with each possible value an interface{} that
323// contains a nil pointer to another newly constructed type that contains the affectable properties.
324// The reflect.Value will be cloned for each call to the Soong config module type's factory method.
325//
326// For example, the acme_cc_defaults example above would
327// produce a reflect.Value whose type is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700328//
329// *struct {
330// Soong_config_variables struct {
331// Board struct {
332// Soc_a interface{}
333// Soc_b interface{}
334// }
335// }
336// }
337//
Colin Cross9d34f352019-11-22 16:03:51 -0800338// And whose value is:
Colin Crossd079e0b2022-08-16 10:27:33 -0700339//
340// &{
341// Soong_config_variables: {
342// Board: {
343// Soc_a: (*struct{ Cflags []string })(nil),
344// Soc_b: (*struct{ Cflags []string })(nil),
345// },
346// },
347// }
Paul Duffine8b47682023-01-09 15:42:57 +0000348func CreateProperties(factoryProps []interface{}, moduleType *ModuleType) reflect.Value {
Colin Cross9d34f352019-11-22 16:03:51 -0800349 var fields []reflect.StructField
350
Colin Cross9d34f352019-11-22 16:03:51 -0800351 affectablePropertiesType := createAffectablePropertiesType(moduleType.affectableProperties, factoryProps)
352 if affectablePropertiesType == nil {
353 return reflect.Value{}
354 }
355
356 for _, c := range moduleType.Variables {
357 fields = append(fields, reflect.StructField{
358 Name: proptools.FieldNameForProperty(c.variableProperty()),
359 Type: c.variableValuesType(),
360 })
361 }
362
363 typ := reflect.StructOf([]reflect.StructField{{
Jingwen Chena47f28d2021-11-02 16:43:57 +0000364 Name: SoongConfigProperty,
Colin Cross9d34f352019-11-22 16:03:51 -0800365 Type: reflect.StructOf(fields),
366 }})
367
368 props := reflect.New(typ)
Jingwen Chena47f28d2021-11-02 16:43:57 +0000369 structConditions := props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800370
371 for i, c := range moduleType.Variables {
372 c.initializeProperties(structConditions.Field(i), affectablePropertiesType)
373 }
374
375 return props
376}
377
378// createAffectablePropertiesType creates a reflect.Type of a struct that has a field for each affectable property
379// that exists in factoryProps.
380func createAffectablePropertiesType(affectableProperties []string, factoryProps []interface{}) reflect.Type {
381 affectableProperties = append([]string(nil), affectableProperties...)
382 sort.Strings(affectableProperties)
383
384 var recurse func(prefix string, aps []string) ([]string, reflect.Type)
385 recurse = func(prefix string, aps []string) ([]string, reflect.Type) {
386 var fields []reflect.StructField
387
Colin Cross997f27a2021-03-05 17:25:41 -0800388 // Iterate while the list is non-empty so it can be modified in the loop.
Colin Cross9d34f352019-11-22 16:03:51 -0800389 for len(affectableProperties) > 0 {
390 p := affectableProperties[0]
391 if !strings.HasPrefix(affectableProperties[0], prefix) {
Colin Cross997f27a2021-03-05 17:25:41 -0800392 // The properties are sorted and recurse is always called with a prefix that matches
393 // the first property in the list, so if we've reached one that doesn't match the
394 // prefix we are done with this prefix.
Colin Cross9d34f352019-11-22 16:03:51 -0800395 break
396 }
Colin Cross9d34f352019-11-22 16:03:51 -0800397
398 nestedProperty := strings.TrimPrefix(p, prefix)
399 if i := strings.IndexRune(nestedProperty, '.'); i >= 0 {
400 var nestedType reflect.Type
401 nestedPrefix := nestedProperty[:i+1]
402
Colin Cross997f27a2021-03-05 17:25:41 -0800403 // Recurse to handle the properties with the found prefix. This will return
404 // an updated affectableProperties with the handled entries removed from the front
405 // of the list, and the type that contains the handled entries. The type may be
406 // nil if none of the entries matched factoryProps.
Colin Cross9d34f352019-11-22 16:03:51 -0800407 affectableProperties, nestedType = recurse(prefix+nestedPrefix, affectableProperties)
408
409 if nestedType != nil {
410 nestedFieldName := proptools.FieldNameForProperty(strings.TrimSuffix(nestedPrefix, "."))
411
412 fields = append(fields, reflect.StructField{
413 Name: nestedFieldName,
414 Type: nestedType,
415 })
416 }
417 } else {
418 typ := typeForPropertyFromPropertyStructs(factoryProps, p)
419 if typ != nil {
420 fields = append(fields, reflect.StructField{
421 Name: proptools.FieldNameForProperty(nestedProperty),
422 Type: typ,
423 })
424 }
Colin Cross997f27a2021-03-05 17:25:41 -0800425 // The first element in the list has been handled, remove it from the list.
426 affectableProperties = affectableProperties[1:]
Colin Cross9d34f352019-11-22 16:03:51 -0800427 }
428 }
429
430 var typ reflect.Type
431 if len(fields) > 0 {
432 typ = reflect.StructOf(fields)
433 }
434 return affectableProperties, typ
435 }
436
437 affectableProperties, typ := recurse("", affectableProperties)
438 if len(affectableProperties) > 0 {
439 panic(fmt.Errorf("didn't handle all affectable properties"))
440 }
441
442 if typ != nil {
443 return reflect.PtrTo(typ)
444 }
445
446 return nil
447}
448
449func typeForPropertyFromPropertyStructs(psList []interface{}, property string) reflect.Type {
450 for _, ps := range psList {
451 if typ := typeForPropertyFromPropertyStruct(ps, property); typ != nil {
452 return typ
453 }
454 }
455
456 return nil
457}
458
459func typeForPropertyFromPropertyStruct(ps interface{}, property string) reflect.Type {
460 v := reflect.ValueOf(ps)
461 for len(property) > 0 {
462 if !v.IsValid() {
463 return nil
464 }
465
466 if v.Kind() == reflect.Interface {
467 if v.IsNil() {
468 return nil
469 } else {
470 v = v.Elem()
471 }
472 }
473
474 if v.Kind() == reflect.Ptr {
475 if v.IsNil() {
476 v = reflect.Zero(v.Type().Elem())
477 } else {
478 v = v.Elem()
479 }
480 }
481
482 if v.Kind() != reflect.Struct {
483 return nil
484 }
485
486 if index := strings.IndexRune(property, '.'); index >= 0 {
487 prefix := property[:index]
488 property = property[index+1:]
489
490 v = v.FieldByName(proptools.FieldNameForProperty(prefix))
491 } else {
492 f := v.FieldByName(proptools.FieldNameForProperty(property))
493 if !f.IsValid() {
494 return nil
495 }
496 return f.Type()
497 }
498 }
499 return nil
500}
501
502// PropertiesToApply returns the applicable properties from a ModuleType that should be applied
503// based on SoongConfig values.
Liz Kammerfe8853d2020-12-16 09:34:33 -0800504// Expects that props contains a struct field with name soong_config_variables. The fields within
Liz Kammer432bd592020-12-16 12:42:02 -0800505// soong_config_variables are expected to be in the same order as moduleType.Variables.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700506func PropertiesToApply(moduleType *ModuleType, props reflect.Value, config SoongConfig) ([]interface{}, error) {
Colin Cross9d34f352019-11-22 16:03:51 -0800507 var ret []interface{}
Jingwen Chena47f28d2021-11-02 16:43:57 +0000508 props = props.Elem().FieldByName(SoongConfigProperty)
Colin Cross9d34f352019-11-22 16:03:51 -0800509 for i, c := range moduleType.Variables {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700510 if ps, err := c.PropertiesToApply(config, props.Field(i)); err != nil {
511 return nil, err
512 } else if ps != nil {
Colin Cross9d34f352019-11-22 16:03:51 -0800513 ret = append(ret, ps)
514 }
515 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700516 return ret, nil
Colin Cross9d34f352019-11-22 16:03:51 -0800517}
518
519type ModuleType struct {
520 BaseModuleType string
521 ConfigNamespace string
522 Variables []soongConfigVariable
523
524 affectableProperties []string
525 variableNames []string
526}
527
Liz Kammer432bd592020-12-16 12:42:02 -0800528func newModuleType(props *ModuleTypeProperties) (*ModuleType, []error) {
529 mt := &ModuleType{
530 affectableProperties: props.Properties,
531 ConfigNamespace: props.Config_namespace,
532 BaseModuleType: props.Module_type,
533 variableNames: props.Variables,
534 }
535
536 for _, name := range props.Bool_variables {
537 if err := checkVariableName(name); err != nil {
538 return nil, []error{fmt.Errorf("bool_variables %s", err)}
539 }
540
541 mt.Variables = append(mt.Variables, newBoolVariable(name))
542 }
543
544 for _, name := range props.Value_variables {
545 if err := checkVariableName(name); err != nil {
546 return nil, []error{fmt.Errorf("value_variables %s", err)}
547 }
548
549 mt.Variables = append(mt.Variables, &valueVariable{
550 baseVariable: baseVariable{
551 variable: name,
552 },
553 })
554 }
555
556 return mt, nil
557}
558
559func checkVariableName(name string) error {
560 if name == "" {
561 return fmt.Errorf("name must not be blank")
562 } else if name == conditionsDefault {
563 return fmt.Errorf("%q is reserved", conditionsDefault)
564 }
565 return nil
566}
567
Colin Cross9d34f352019-11-22 16:03:51 -0800568type soongConfigVariable interface {
569 // variableProperty returns the name of the variable.
570 variableProperty() string
571
572 // conditionalValuesType returns a reflect.Type that contains an interface{} for each possible value.
573 variableValuesType() reflect.Type
574
575 // initializeProperties is passed a reflect.Value of the reflect.Type returned by conditionalValuesType and a
576 // reflect.Type of the affectable properties, and should initialize each interface{} in the reflect.Value with
577 // the zero value of the affectable properties type.
578 initializeProperties(v reflect.Value, typ reflect.Type)
579
580 // PropertiesToApply should return one of the interface{} values set by initializeProperties to be applied
581 // to the module.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700582 PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error)
Colin Cross9d34f352019-11-22 16:03:51 -0800583}
584
585type baseVariable struct {
586 variable string
587}
588
589func (c *baseVariable) variableProperty() string {
590 return CanonicalizeToProperty(c.variable)
591}
592
593type stringVariable struct {
594 baseVariable
595 values []string
596}
597
598func (s *stringVariable) variableValuesType() reflect.Type {
599 var fields []reflect.StructField
600
Liz Kammer432bd592020-12-16 12:42:02 -0800601 var values []string
602 values = append(values, s.values...)
603 values = append(values, conditionsDefault)
604 for _, v := range values {
Colin Cross9d34f352019-11-22 16:03:51 -0800605 fields = append(fields, reflect.StructField{
606 Name: proptools.FieldNameForProperty(v),
607 Type: emptyInterfaceType,
608 })
609 }
610
611 return reflect.StructOf(fields)
612}
613
Liz Kammer432bd592020-12-16 12:42:02 -0800614// initializeProperties initializes properties to zero value of typ for supported values and a final
615// conditions default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800616func (s *stringVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
617 for i := range s.values {
618 v.Field(i).Set(reflect.Zero(typ))
619 }
Liz Kammer432bd592020-12-16 12:42:02 -0800620 v.Field(len(s.values)).Set(reflect.Zero(typ)) // conditions default is the final value
Colin Cross9d34f352019-11-22 16:03:51 -0800621}
622
Liz Kammer432bd592020-12-16 12:42:02 -0800623// Extracts an interface from values containing the properties to apply based on config.
624// 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 -0700625func (s *stringVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Cole Faustb0a91332022-11-01 17:10:23 +0000626 configValue := config.String(s.variable)
627 if configValue != "" && !InList(configValue, s.values) {
628 return nil, fmt.Errorf("Soong config property %q must be one of %v, found %q", s.variable, s.values, configValue)
629 }
Colin Cross9d34f352019-11-22 16:03:51 -0800630 for j, v := range s.values {
Liz Kammer432bd592020-12-16 12:42:02 -0800631 f := values.Field(j)
Cole Faustb0a91332022-11-01 17:10:23 +0000632 if configValue == v && !f.Elem().IsNil() {
Liz Kammer432bd592020-12-16 12:42:02 -0800633 return f.Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800634 }
635 }
Liz Kammer432bd592020-12-16 12:42:02 -0800636 // if we have reached this point, we have checked all valid values of string and either:
637 // * the value was not set
638 // * the value was set but that value was not specified in the Android.bp file
639 return values.Field(len(s.values)).Interface(), nil
Colin Cross9d34f352019-11-22 16:03:51 -0800640}
641
Liz Kammer432bd592020-12-16 12:42:02 -0800642// Struct to allow conditions set based on a boolean variable
Colin Cross9d34f352019-11-22 16:03:51 -0800643type boolVariable struct {
644 baseVariable
645}
646
Liz Kammer432bd592020-12-16 12:42:02 -0800647// newBoolVariable constructs a boolVariable with the given name
Liz Kammerfe8853d2020-12-16 09:34:33 -0800648func newBoolVariable(name string) *boolVariable {
649 return &boolVariable{
650 baseVariable{
651 variable: name,
652 },
653 }
654}
655
Colin Cross9d34f352019-11-22 16:03:51 -0800656func (b boolVariable) variableValuesType() reflect.Type {
657 return emptyInterfaceType
658}
659
Liz Kammer432bd592020-12-16 12:42:02 -0800660// initializeProperties initializes a property to zero value of typ with an additional conditions
661// default field.
Colin Cross9d34f352019-11-22 16:03:51 -0800662func (b boolVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800663 initializePropertiesWithDefault(v, typ)
Colin Cross9d34f352019-11-22 16:03:51 -0800664}
665
Liz Kammer432bd592020-12-16 12:42:02 -0800666// initializePropertiesWithDefault, initialize with zero value, v to contain a field for each field
667// in typ, with an additional field for defaults of type typ. This should be used to initialize
668// boolVariable, valueVariable, or any future implementations of soongConfigVariable which support
669// one variable and a default.
670func initializePropertiesWithDefault(v reflect.Value, typ reflect.Type) {
671 sTyp := typ.Elem()
672 var fields []reflect.StructField
673 for i := 0; i < sTyp.NumField(); i++ {
674 fields = append(fields, sTyp.Field(i))
Colin Cross9d34f352019-11-22 16:03:51 -0800675 }
676
Liz Kammer432bd592020-12-16 12:42:02 -0800677 // create conditions_default field
678 nestedFieldName := proptools.FieldNameForProperty(conditionsDefault)
679 fields = append(fields, reflect.StructField{
680 Name: nestedFieldName,
681 Type: typ,
682 })
683
684 newTyp := reflect.PtrTo(reflect.StructOf(fields))
685 v.Set(reflect.Zero(newTyp))
686}
687
688// conditionsDefaultField extracts the conditions_default field from v. This is always the final
689// field if initialized with initializePropertiesWithDefault.
690func conditionsDefaultField(v reflect.Value) reflect.Value {
691 return v.Field(v.NumField() - 1)
692}
693
694// removeDefault removes the conditions_default field from values while retaining values from all
695// other fields. This allows
696func removeDefault(values reflect.Value) reflect.Value {
697 v := values.Elem().Elem()
698 s := conditionsDefaultField(v)
699 // if conditions_default field was not set, there will be no issues extending properties.
700 if !s.IsValid() {
701 return v
702 }
703
704 // If conditions_default field was set, it has the correct type for our property. Create a new
705 // reflect.Value of the conditions_default type and copy all fields (except for
706 // conditions_default) based on values to the result.
707 res := reflect.New(s.Type().Elem())
708 for i := 0; i < res.Type().Elem().NumField(); i++ {
709 val := v.Field(i)
710 res.Elem().Field(i).Set(val)
711 }
712
713 return res
714}
715
716// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
717// the module. If the value was not set, conditions_default interface will be returned; otherwise,
718// the interface in values, without conditions_default will be returned.
719func (b boolVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
720 // If this variable was not referenced in the module, there are no properties to apply.
721 if values.Elem().IsZero() {
722 return nil, nil
723 }
724 if config.Bool(b.variable) {
725 values = removeDefault(values)
726 return values.Interface(), nil
727 }
728 v := values.Elem().Elem()
729 if f := conditionsDefaultField(v); f.IsValid() {
730 return f.Interface(), nil
731 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700732 return nil, nil
733}
734
Liz Kammer432bd592020-12-16 12:42:02 -0800735// Struct to allow conditions set based on a value variable, supporting string substitution.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700736type valueVariable struct {
737 baseVariable
738}
739
740func (s *valueVariable) variableValuesType() reflect.Type {
741 return emptyInterfaceType
742}
743
Liz Kammer432bd592020-12-16 12:42:02 -0800744// initializeProperties initializes a property to zero value of typ with an additional conditions
745// default field.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700746func (s *valueVariable) initializeProperties(v reflect.Value, typ reflect.Type) {
Liz Kammer432bd592020-12-16 12:42:02 -0800747 initializePropertiesWithDefault(v, typ)
Dan Willemsenb0935db2020-03-23 19:42:18 -0700748}
749
Liz Kammer432bd592020-12-16 12:42:02 -0800750// PropertiesToApply returns an interface{} value based on initializeProperties to be applied to
751// the module. If the variable was not set, conditions_default interface will be returned;
752// otherwise, the interface in values, without conditions_default will be returned with all
753// appropriate string substitutions based on variable being set.
Dan Willemsenb0935db2020-03-23 19:42:18 -0700754func (s *valueVariable) PropertiesToApply(config SoongConfig, values reflect.Value) (interface{}, error) {
Liz Kammer432bd592020-12-16 12:42:02 -0800755 // If this variable was not referenced in the module, there are no properties to apply.
756 if !values.IsValid() || values.Elem().IsZero() {
Dan Willemsenb0935db2020-03-23 19:42:18 -0700757 return nil, nil
758 }
Liz Kammer432bd592020-12-16 12:42:02 -0800759 if !config.IsSet(s.variable) {
760 return conditionsDefaultField(values.Elem().Elem()).Interface(), nil
761 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700762 configValue := config.String(s.variable)
763
Liz Kammer432bd592020-12-16 12:42:02 -0800764 values = removeDefault(values)
765 propStruct := values.Elem()
Liz Kammer40ddfaa2020-12-16 10:59:00 -0800766 if !propStruct.IsValid() {
767 return nil, nil
768 }
Dan Willemsenb0935db2020-03-23 19:42:18 -0700769 for i := 0; i < propStruct.NumField(); i++ {
770 field := propStruct.Field(i)
771 kind := field.Kind()
772 if kind == reflect.Ptr {
773 if field.IsNil() {
774 continue
775 }
776 field = field.Elem()
777 }
778 switch kind {
779 case reflect.String:
780 err := printfIntoProperty(field, configValue)
781 if err != nil {
782 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
783 }
784 case reflect.Slice:
785 for j := 0; j < field.Len(); j++ {
786 err := printfIntoProperty(field.Index(j), configValue)
787 if err != nil {
788 return nil, fmt.Errorf("soong_config_variables.%s.%s: %s", s.variable, propStruct.Type().Field(i).Name, err)
789 }
790 }
791 case reflect.Bool:
792 // Nothing to do
793 default:
794 return nil, fmt.Errorf("soong_config_variables.%s.%s: unsupported property type %q", s.variable, propStruct.Type().Field(i).Name, kind)
795 }
796 }
797
798 return values.Interface(), nil
799}
800
801func printfIntoProperty(propertyValue reflect.Value, configValue string) error {
802 s := propertyValue.String()
803
804 count := strings.Count(s, "%")
805 if count == 0 {
806 return nil
807 }
808
809 if count > 1 {
810 return fmt.Errorf("value variable properties only support a single '%%'")
811 }
812
813 if !strings.Contains(s, "%s") {
814 return fmt.Errorf("unsupported %% in value variable property")
815 }
816
817 propertyValue.Set(reflect.ValueOf(fmt.Sprintf(s, configValue)))
818
Colin Cross9d34f352019-11-22 16:03:51 -0800819 return nil
820}
821
822func CanonicalizeToProperty(v string) string {
823 return strings.Map(func(r rune) rune {
824 switch {
825 case r >= 'A' && r <= 'Z',
826 r >= 'a' && r <= 'z',
827 r >= '0' && r <= '9',
828 r == '_':
829 return r
830 default:
831 return '_'
832 }
833 }, v)
834}
835
836func CanonicalizeToProperties(values []string) []string {
837 ret := make([]string, len(values))
838 for i, v := range values {
839 ret[i] = CanonicalizeToProperty(v)
840 }
841 return ret
842}
843
844type emptyInterfaceStruct struct {
845 i interface{}
846}
847
848var emptyInterfaceType = reflect.TypeOf(emptyInterfaceStruct{}).Field(0).Type
Cole Faustb0a91332022-11-01 17:10:23 +0000849
850// InList checks if the string belongs to the list
851func InList(s string, list []string) bool {
852 for _, s2 := range list {
853 if s2 == s {
854 return true
855 }
856 }
857 return false
858}